Forge
csharp4405de34
1using System.IO;
2using CascadeIDE.Features.Workspace;
3
4namespace CascadeIDE.Services;
5
6internal static class DocsTemplatesCatalogResolver
7{
8 public sealed record TemplateEntry(
9 string Id,
10 string Title,
11 string Kind,
12 string Source,
13 string? RepoPath,
14 string? KnowledgeRootId,
15 string? KnowledgeFilePath);
16
17 private sealed class DocsTemplatesCatalogRoot
18 {
19 public RepositoryDocsTemplatesToml? DocsTemplates { get; set; }
20 }
21
22 public static IReadOnlyList<TemplateEntry> ResolveTemplatesFromWorkspaceToml(
23 RepositoryWorkspaceToml? workspaceToml,
24 string workspaceRoot)
25 {
26 var list = new List<TemplateEntry>();
27
28 // 1) catalog file
29 var catalogPath = workspaceToml?.Workspace?.DocsTemplates?.CatalogPath;
30 if (!string.IsNullOrWhiteSpace(catalogPath))
31 {
32 var abs = WorkspaceAdrMapResolver.TryResolveAbsoluteDocPath(workspaceRoot, catalogPath!);
33 if (!string.IsNullOrWhiteSpace(abs) && File.Exists(abs))
34 {
35 try
36 {
37 var raw = File.ReadAllText(abs);
38 var parsed = CascadeTomlSerializer.Deserialize<DocsTemplatesCatalogRoot>(raw);
39 if (parsed?.DocsTemplates?.Template is { Count: > 0 } templates)
40 Append(list, templates);
41 }
42 catch
43 {
44 // ignore
45 }
46 }
47 }
48
49 // 2) inline
50 if (workspaceToml?.Workspace?.DocsTemplates?.Template is { Count: > 0 } inline)
51 Append(list, inline);
52
53 // 3) fallback built-ins (repo)
54 if (list.Count == 0)
55 {
56 list.Add(new TemplateEntry("feature.v1", "Feature doc", "feature_doc", "repo", "docs/templates/feature.md", null, null));
57 list.Add(new TemplateEntry("module.v1", "Module doc", "module_doc", "repo", "docs/templates/module.md", null, null));
58 list.Add(new TemplateEntry("adr-mini.v1", "ADR mini", "adr", "repo", "docs/templates/adr-mini.md", null, null));
59 list.Add(new TemplateEntry("runbook.v1", "Runbook", "runbook", "repo", "docs/templates/runbook.md", null, null));
60 }
61
62 // De-dup by id (later wins).
63 var dict = new Dictionary<string, TemplateEntry>(StringComparer.OrdinalIgnoreCase);
64 foreach (var t in list)
65 dict[t.Id] = t;
66
67 return dict.Values.OrderBy(x => x.Id, StringComparer.Ordinal).ToList();
68 }
69
70 private static void Append(List<TemplateEntry> dst, List<DocsTemplateToml> templates)
71 {
72 foreach (var t in templates)
73 {
74 var id = (t.Id ?? "").Trim();
75 if (id.Length == 0)
76 continue;
77
78 var title = (t.Title ?? id).Trim();
79 var kind = (t.Kind ?? "").Trim();
80 var source = (t.Source ?? "repo").Trim().ToLowerInvariant();
81
82 if (source == "knowledge")
83 {
84 var rootId = (t.KnowledgeRootId ?? "").Trim();
85 var file = (t.FilePath ?? "").Trim();
86 if (file.Length == 0)
87 continue;
88
89 dst.Add(new TemplateEntry(
90 id,
91 title,
92 kind,
93 "knowledge",
94 null,
95 rootId.Length == 0 ? null : rootId,
96 file));
97 continue;
98 }
99
100 var path = (t.Path ?? "").Trim();
101 if (path.Length == 0)
102 continue;
103
104 dst.Add(new TemplateEntry(id, title, kind, "repo", path, null, null));
105 }
106 }
107}
108
109
View only · write via MCP/CIDE