- Journal.DevTool: C# TUI dev tool (SDT) using Spectre.Console - Phosphor green (#00FF41) terminal aesthetic with amber/red accents - Grouped build target menu driven by devtool.json config - Topological dependency resolver for DependsOn chains - Live stdout/stderr streaming per target step - Interactive environment variable editor (dropdown + free-text) - Toolchain management screen: Python venv create/install/upgrade, Node npm install - Workspace switcher: reads sdt-workspace.json, hot-switches between projects - Outputs as 'sdt' executable (net10.0) - devtool.json: project config for SDT - All build targets: sidecar, web, webgateway, tauri, tauri-nsis, all (virtual) - Dev targets: run-gateway - Test targets: test (smoke), gate (migration) - Cache targets: nuget-export, nuget-import - Toolchains: Python 3.14 (cpu/gpu/nlp profiles) + Node/npm (Journal.App) - Env vars: AI provider, log level, NLP backend, path overrides - justfile: just command runner recipes wrapping existing scripts - Correct dependency ordering (sidecar before tauri, web before webgateway) - OS-aware runtime detection (win-x64 / linux-x64) - Recipes: sidecar, web, webgateway, tauri, tauri-nsis, all, run, dev-app, test, gate, build, nuget-export/import, sdt - Journal.slnx: added Journal.DevTool project
53 lines
1.8 KiB
C#
53 lines
1.8 KiB
C#
using System.Text.Json;
|
|
|
|
namespace Sdt.Config;
|
|
|
|
public static class WorkspaceLoader
|
|
{
|
|
private const string FileName = "sdt-workspace.json";
|
|
|
|
private static readonly JsonSerializerOptions JsonOptions = new()
|
|
{
|
|
PropertyNameCaseInsensitive = true,
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
AllowTrailingCommas = true,
|
|
ReadCommentHandling = JsonCommentHandling.Skip,
|
|
};
|
|
|
|
/// <summary>
|
|
/// Walks up from <paramref name="startDir"/> (or CWD) to find sdt-workspace.json.
|
|
/// Returns null if not found.
|
|
/// </summary>
|
|
public static (WorkspaceConfig Config, string WorkspaceRoot)? FindAndLoad(string? startDir = null)
|
|
{
|
|
var dir = new DirectoryInfo(startDir ?? Directory.GetCurrentDirectory());
|
|
while (dir is not null)
|
|
{
|
|
var candidate = Path.Combine(dir.FullName, FileName);
|
|
if (File.Exists(candidate))
|
|
{
|
|
try
|
|
{
|
|
var json = File.ReadAllText(candidate);
|
|
var config = JsonSerializer.Deserialize<WorkspaceConfig>(json, JsonOptions)
|
|
?? throw new InvalidOperationException($"{FileName} deserialized to null.");
|
|
return (config, dir.FullName);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Failed to parse {FileName} at {candidate}: {ex.Message}", ex);
|
|
}
|
|
}
|
|
dir = dir.Parent!;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resolves the absolute project root for a workspace project entry.
|
|
/// </summary>
|
|
public static string ResolveProjectRoot(string workspaceRoot, WorkspaceProject project)
|
|
=> Path.GetFullPath(Path.Combine(workspaceRoot, project.Path));
|
|
}
|