@topogram/cli 0.3.64 → 0.3.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/package.json +1 -1
  2. package/src/adoption/plan/index.js +716 -0
  3. package/src/adoption/plan.js +12 -703
  4. package/src/adoption/reporting.js +1 -1
  5. package/src/agent-brief.js +7 -21
  6. package/src/agent-ops/query-builders/auth.js +375 -0
  7. package/src/agent-ops/query-builders/change-risk/change-plan.js +123 -0
  8. package/src/agent-ops/query-builders/change-risk/import-plan.js +49 -0
  9. package/src/agent-ops/query-builders/change-risk/maintained.js +286 -0
  10. package/src/agent-ops/query-builders/change-risk/review-packets.js +123 -0
  11. package/src/agent-ops/query-builders/change-risk/risk.js +189 -0
  12. package/src/agent-ops/query-builders/change-risk.js +25 -0
  13. package/src/agent-ops/query-builders/common.js +149 -0
  14. package/src/agent-ops/query-builders/maintained-risk.js +539 -0
  15. package/src/agent-ops/query-builders/maintained-shared.js +120 -0
  16. package/src/agent-ops/query-builders/multi-agent.js +547 -0
  17. package/src/agent-ops/query-builders/projection-impacts.js +514 -0
  18. package/src/agent-ops/query-builders/work-packets.js +417 -0
  19. package/src/agent-ops/query-builders/workflow-context-shared.js +300 -0
  20. package/src/agent-ops/query-builders/workflow-context.js +398 -0
  21. package/src/agent-ops/query-builders/workflow-presets-core.js +677 -0
  22. package/src/agent-ops/query-builders/workflow-presets.js +341 -0
  23. package/src/agent-ops/query-builders.d.ts +26 -26
  24. package/src/agent-ops/query-builders.js +42 -5021
  25. package/src/archive/jsonl.js +2 -2
  26. package/src/archive/resolver-bridge.js +1 -1
  27. package/src/archive/unarchive.js +2 -1
  28. package/src/catalog/constants.js +10 -0
  29. package/src/catalog/copy.js +65 -0
  30. package/src/catalog/diagnostics.js +15 -0
  31. package/src/catalog/entries.js +42 -0
  32. package/src/catalog/files.js +67 -0
  33. package/src/catalog/provenance.js +123 -0
  34. package/src/catalog/source.js +150 -0
  35. package/src/catalog/validation.js +252 -0
  36. package/src/catalog.d.ts +2 -0
  37. package/src/catalog.js +18 -746
  38. package/src/cli/command-parsers/project.js +3 -0
  39. package/src/cli/command-parsers/shared.js +1 -1
  40. package/src/cli/commands/agent.js +2 -2
  41. package/src/cli/commands/catalog/check.js +31 -0
  42. package/src/cli/commands/catalog/copy.js +59 -0
  43. package/src/cli/commands/catalog/doctor.js +248 -0
  44. package/src/cli/commands/catalog/help.js +21 -0
  45. package/src/cli/commands/catalog/list.js +52 -0
  46. package/src/cli/commands/catalog/runner.js +92 -0
  47. package/src/cli/commands/catalog/shared.js +17 -0
  48. package/src/cli/commands/catalog/show.js +134 -0
  49. package/src/cli/commands/catalog.js +30 -615
  50. package/src/cli/commands/check.js +3 -3
  51. package/src/cli/commands/doctor.js +2 -9
  52. package/src/cli/commands/generator-policy/package-info.js +162 -0
  53. package/src/cli/commands/generator-policy/payloads.js +372 -0
  54. package/src/cli/commands/generator-policy/printers.js +159 -0
  55. package/src/cli/commands/generator-policy/runner.js +81 -0
  56. package/src/cli/commands/generator-policy/shared.js +39 -0
  57. package/src/cli/commands/generator-policy.js +15 -783
  58. package/src/cli/commands/import/adopt.js +170 -0
  59. package/src/cli/commands/import/check.js +91 -0
  60. package/src/cli/commands/import/diff.js +84 -0
  61. package/src/cli/commands/import/help.js +47 -0
  62. package/src/cli/commands/import/paths.js +269 -0
  63. package/src/cli/commands/import/plan.js +292 -0
  64. package/src/cli/commands/import/refresh.js +471 -0
  65. package/src/cli/commands/import/status-history.js +196 -0
  66. package/src/cli/commands/import/workspace.js +233 -0
  67. package/src/cli/commands/import.js +33 -1732
  68. package/src/cli/commands/migrate.js +153 -0
  69. package/src/cli/commands/package/constants.js +17 -0
  70. package/src/cli/commands/package/doctor.js +240 -0
  71. package/src/cli/commands/package/help.js +27 -0
  72. package/src/cli/commands/package/lockfile.js +135 -0
  73. package/src/cli/commands/package/npm.js +97 -0
  74. package/src/cli/commands/package/reporting.js +35 -0
  75. package/src/cli/commands/package/runner.js +33 -0
  76. package/src/cli/commands/package/shared.js +9 -0
  77. package/src/cli/commands/package/update-cli.js +252 -0
  78. package/src/cli/commands/package/versions.js +35 -0
  79. package/src/cli/commands/package.js +29 -813
  80. package/src/cli/commands/query/change-plan.js +68 -0
  81. package/src/cli/commands/query/definitions.js +202 -0
  82. package/src/cli/commands/query/import-adopt.js +121 -0
  83. package/src/cli/commands/query/runner/artifacts.js +102 -0
  84. package/src/cli/commands/query/runner/boundaries.js +211 -0
  85. package/src/cli/commands/query/runner/change.js +182 -0
  86. package/src/cli/commands/query/runner/import-adopt.js +111 -0
  87. package/src/cli/commands/query/runner/index.js +31 -0
  88. package/src/cli/commands/query/runner/output.js +12 -0
  89. package/src/cli/commands/query/runner/workflow.js +241 -0
  90. package/src/cli/commands/query/runner.js +3 -0
  91. package/src/cli/commands/query/workflow-context.js +5 -0
  92. package/src/cli/commands/query/workspace.js +270 -0
  93. package/src/cli/commands/query.js +9 -1300
  94. package/src/cli/commands/source.js +3 -12
  95. package/src/cli/commands/template/baseline.js +100 -0
  96. package/src/cli/commands/template/check.js +467 -0
  97. package/src/cli/commands/template/constants.js +8 -0
  98. package/src/cli/commands/template/diagnostics.js +26 -0
  99. package/src/cli/commands/template/help.js +28 -0
  100. package/src/cli/commands/template/lifecycle.js +404 -0
  101. package/src/cli/commands/template/list-show.js +287 -0
  102. package/src/cli/commands/template/policy.js +422 -0
  103. package/src/cli/commands/template/shared.js +127 -0
  104. package/src/cli/commands/template/updates.js +352 -0
  105. package/src/cli/commands/template-runner.js +6 -6
  106. package/src/cli/commands/template.js +41 -2143
  107. package/src/cli/commands/trust.js +1 -1
  108. package/src/cli/commands/workflow.js +6 -1
  109. package/src/cli/dispatcher.js +6 -1
  110. package/src/cli/help.js +15 -14
  111. package/src/cli/migration-guidance.js +1 -1
  112. package/src/cli/output-safety.js +2 -1
  113. package/src/cli/path-normalization.js +3 -13
  114. package/src/generator/api/contracts.js +497 -0
  115. package/src/generator/api/metadata.js +221 -0
  116. package/src/generator/api/openapi.js +559 -0
  117. package/src/generator/api/schema.js +124 -0
  118. package/src/generator/api/types.d.ts +98 -0
  119. package/src/generator/api.js +3 -1195
  120. package/src/generator/context/domain-page.js +1 -1
  121. package/src/generator/context/shared/domain-sdlc.js +282 -0
  122. package/src/generator/context/shared/maintained-boundary.js +665 -0
  123. package/src/generator/context/shared/metrics.js +85 -0
  124. package/src/generator/context/shared/primitives.js +64 -0
  125. package/src/generator/context/shared/relationships.js +453 -0
  126. package/src/generator/context/shared/summaries.js +263 -0
  127. package/src/generator/context/shared/types.d.ts +207 -0
  128. package/src/generator/context/shared.d.ts +42 -0
  129. package/src/generator/context/shared.js +80 -1390
  130. package/src/generator/context/slice/core.js +397 -0
  131. package/src/generator/context/slice/sdlc.js +417 -0
  132. package/src/generator/context/slice/ui-packets.js +183 -0
  133. package/src/generator/context/slice.js +2 -859
  134. package/src/generator/context/task-mode.js +2 -2
  135. package/src/generator/registry/index.js +507 -0
  136. package/src/generator/registry.js +18 -504
  137. package/src/generator/runtime/environment/index.js +666 -0
  138. package/src/generator/runtime/environment.js +4 -666
  139. package/src/generator/runtime/runtime-check/index.js +554 -0
  140. package/src/generator/runtime/runtime-check.js +4 -554
  141. package/src/generator/runtime/shared/index.js +572 -0
  142. package/src/generator/runtime/shared.js +19 -570
  143. package/src/generator/sdlc/doc-page.js +1 -1
  144. package/src/generator/shared.d.ts +2 -0
  145. package/src/generator/surfaces/databases/lifecycle-shared.js +1 -1
  146. package/src/generator/surfaces/native/swiftui-templates/README.generated.md +1 -1
  147. package/src/generator/surfaces/shared.d.ts +3 -0
  148. package/src/generator/widget-conformance/behavior-report.js +258 -0
  149. package/src/generator/widget-conformance/checks.js +371 -0
  150. package/src/generator/widget-conformance/projection-context.js +200 -0
  151. package/src/generator/widget-conformance/report.js +166 -0
  152. package/src/generator/widget-conformance/types.d.ts +121 -0
  153. package/src/generator/widget-conformance.js +3 -824
  154. package/src/import/core/context.d.ts +3 -0
  155. package/src/import/core/context.js +5 -7
  156. package/src/import/core/contracts.d.ts +1 -0
  157. package/src/import/core/registry.d.ts +4 -0
  158. package/src/import/core/runner/candidates.js +337 -0
  159. package/src/import/core/runner/options.js +22 -0
  160. package/src/import/core/runner/reports.js +51 -0
  161. package/src/import/core/runner/run.js +79 -0
  162. package/src/import/core/runner/tracks.js +150 -0
  163. package/src/import/core/runner/ui-drafts.js +393 -0
  164. package/src/import/core/runner.js +3 -698
  165. package/src/import/core/shared/api-routes.js +221 -0
  166. package/src/import/core/shared/candidates.js +97 -0
  167. package/src/import/core/shared/files.js +177 -0
  168. package/src/import/core/shared/next-app.js +389 -0
  169. package/src/import/core/shared/types.d.ts +51 -0
  170. package/src/import/core/shared/ui-routes.js +230 -0
  171. package/src/import/core/shared.js +60 -861
  172. package/src/new-project/constants.js +128 -0
  173. package/src/new-project/create.js +90 -0
  174. package/src/new-project/json.js +28 -0
  175. package/src/new-project/metadata.js +96 -0
  176. package/src/new-project/package-spec.js +161 -0
  177. package/src/new-project/project-files.js +351 -0
  178. package/src/new-project/template-policy.js +269 -0
  179. package/src/new-project/template-resolution.js +370 -0
  180. package/src/new-project/template-snapshots.js +442 -0
  181. package/src/new-project/template-updates.js +512 -0
  182. package/src/new-project/types.d.ts +83 -0
  183. package/src/new-project.js +6 -2277
  184. package/src/parser.d.ts +87 -1
  185. package/src/parser.js +118 -0
  186. package/src/policy/review-boundaries.d.ts +15 -0
  187. package/src/project-config/index.js +591 -0
  188. package/src/project-config.js +19 -561
  189. package/src/resolver/enrich/acceptance-criterion.js +2 -0
  190. package/src/resolver/enrich/bug.js +2 -0
  191. package/src/resolver/enrich/pitch.js +2 -0
  192. package/src/resolver/enrich/requirement.js +2 -0
  193. package/src/resolver/enrich/task.js +2 -0
  194. package/src/resolver/index.js +19 -2089
  195. package/src/resolver/normalize.js +384 -1
  196. package/src/resolver/plans.js +168 -0
  197. package/src/resolver/projections-api.js +494 -0
  198. package/src/resolver/projections-db.js +133 -0
  199. package/src/resolver/projections-ui.js +317 -0
  200. package/src/resolver/shapes.js +251 -0
  201. package/src/resolver/shared.js +278 -0
  202. package/src/resolver/widgets.js +132 -0
  203. package/src/sdlc/adopt.js +6 -5
  204. package/src/sdlc/paths.js +3 -5
  205. package/src/sdlc/scaffold.js +2 -1
  206. package/src/template-trust/constants.js +62 -0
  207. package/src/template-trust/content.js +258 -0
  208. package/src/template-trust/diff.js +92 -0
  209. package/src/template-trust/policy.js +61 -0
  210. package/src/template-trust/record.js +90 -0
  211. package/src/template-trust/status.js +182 -0
  212. package/src/template-trust.js +24 -687
  213. package/src/text-helpers.d.ts +1 -0
  214. package/src/topogram-types.d.ts +69 -0
  215. package/src/validator/common.js +488 -0
  216. package/src/validator/data-model.js +237 -0
  217. package/src/validator/docs.js +167 -0
  218. package/src/validator/expressions.js +146 -1
  219. package/src/validator/index.d.ts +23 -0
  220. package/src/validator/index.js +32 -3585
  221. package/src/validator/kinds.d.ts +41 -0
  222. package/src/validator/kinds.js +2 -0
  223. package/src/validator/model-helpers.js +46 -0
  224. package/src/validator/per-kind/acceptance-criterion.js +5 -0
  225. package/src/validator/per-kind/bug.js +6 -0
  226. package/src/validator/per-kind/domain.js +15 -2
  227. package/src/validator/per-kind/pitch.js +7 -0
  228. package/src/validator/per-kind/requirement.js +5 -0
  229. package/src/validator/per-kind/task.js +7 -0
  230. package/src/validator/per-kind/widget.js +14 -0
  231. package/src/validator/projections/api-http-async.js +410 -0
  232. package/src/validator/projections/api-http-authz.js +88 -0
  233. package/src/validator/projections/api-http-core.js +205 -0
  234. package/src/validator/projections/api-http-policies.js +339 -0
  235. package/src/validator/projections/api-http-responses.js +233 -0
  236. package/src/validator/projections/api-http.js +44 -0
  237. package/src/validator/projections/db.js +353 -0
  238. package/src/validator/projections/generator-defaults.js +45 -0
  239. package/src/validator/projections/helpers.js +87 -0
  240. package/src/validator/projections/ui-helpers.js +214 -0
  241. package/src/validator/projections/ui-navigation.js +344 -0
  242. package/src/validator/projections/ui-structure.js +364 -0
  243. package/src/validator/projections/ui-widgets.js +493 -0
  244. package/src/validator/projections/ui.js +46 -0
  245. package/src/validator/registry.js +48 -1
  246. package/src/validator/utils.d.ts +20 -0
  247. package/src/validator/utils.js +115 -12
  248. package/src/widget-behavior.d.ts +1 -0
  249. package/src/workflows/import-app/api/collect.js +221 -0
  250. package/src/workflows/import-app/api/openapi.js +257 -0
  251. package/src/workflows/import-app/api/routes.js +327 -0
  252. package/src/workflows/import-app/api/sources.js +22 -0
  253. package/src/workflows/import-app/api.js +2 -797
  254. package/src/workflows/reconcile/adoption-plan/build.js +212 -0
  255. package/src/workflows/reconcile/adoption-plan/dependencies.js +75 -0
  256. package/src/workflows/reconcile/adoption-plan/outputs.js +153 -0
  257. package/src/workflows/reconcile/adoption-plan/paths.js +58 -0
  258. package/src/workflows/reconcile/adoption-plan/projection-patches.js +177 -0
  259. package/src/workflows/reconcile/adoption-plan/reasons.js +107 -0
  260. package/src/workflows/reconcile/adoption-plan.js +30 -740
  261. package/src/workflows/reconcile/auth/closures.js +115 -0
  262. package/src/workflows/reconcile/auth/formatters.js +142 -0
  263. package/src/workflows/reconcile/auth/inference.js +330 -0
  264. package/src/workflows/reconcile/auth/roles.js +122 -0
  265. package/src/workflows/reconcile/auth.js +35 -690
  266. package/src/workflows/reconcile/bundle-core/index.js +600 -0
  267. package/src/workflows/reconcile/bundle-core.js +12 -598
  268. package/src/workflows/reconcile/candidate-model.js +18 -2
  269. package/src/workflows/reconcile/canonical-surface.js +1 -1
  270. package/src/workflows/reconcile/impacts/adoption-plan.js +196 -0
  271. package/src/workflows/reconcile/impacts/indexes.js +105 -0
  272. package/src/workflows/reconcile/impacts/patches.js +252 -0
  273. package/src/workflows/reconcile/impacts/reports.js +80 -0
  274. package/src/workflows/reconcile/impacts.js +14 -623
  275. package/src/workflows/reconcile/renderers.js +41 -6
  276. package/src/workflows/shared.js +5 -11
  277. package/src/workspace-docs.d.ts +29 -0
  278. package/src/workspace-paths.js +328 -0
@@ -0,0 +1,352 @@
1
+ // @ts-check
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+
6
+ import { stableStringify } from "../../../format.js";
7
+ import { loadProjectConfig } from "../../../project-config.js";
8
+ import {
9
+ applyTemplateUpdate,
10
+ applyTemplateUpdateFileAction,
11
+ buildTemplateUpdateCheck,
12
+ buildTemplateUpdatePlan,
13
+ buildTemplateUpdateStatus
14
+ } from "../../../new-project.js";
15
+ import { TEMPLATES_ROOT } from "./constants.js";
16
+ import { templateCheckDiagnostic } from "./diagnostics.js";
17
+ import { latestTemplateInfo, messageFromError, templateMetadataFromProjectConfig } from "./shared.js";
18
+
19
+ /**
20
+ * @param {any} plan
21
+ * @returns {void}
22
+ */
23
+ export function printTemplateUpdatePlan(plan) {
24
+ const isApply = plan.mode === "apply";
25
+ const isCheck = plan.mode === "check";
26
+ const isStatus = plan.mode === "status";
27
+ const isFileAction = ["accept-current", "accept-candidate", "delete-current"].includes(plan.mode);
28
+ if (isApply) {
29
+ console.log(plan.ok ? "Template update apply: complete" : "Template update apply: refused");
30
+ } else if (isStatus) {
31
+ console.log(plan.ok ? "Template update status: aligned" : "Template update status: action needed");
32
+ } else if (isCheck) {
33
+ console.log(plan.ok ? "Template update check: aligned" : "Template update check: out of date");
34
+ } else if (isFileAction) {
35
+ console.log(plan.ok ? `Template update ${plan.mode}: complete` : `Template update ${plan.mode}: refused`);
36
+ } else {
37
+ console.log(plan.ok ? "Template update plan: ready for review" : "Template update plan: incompatible");
38
+ }
39
+ console.log(`Current: ${plan.current?.id || "unknown"}@${plan.current?.version || "unknown"}`);
40
+ console.log(`Candidate: ${plan.candidate?.id || "unknown"}@${plan.candidate?.version || "unknown"}`);
41
+ console.log(`Writes: ${plan.writes ? "applied" : "none"}`);
42
+ if (plan.reportPath) {
43
+ console.log(`Report: ${plan.reportPath}`);
44
+ }
45
+ console.log(`Added: ${plan.summary.added}`);
46
+ console.log(`Changed: ${plan.summary.changed}`);
47
+ console.log(`Current-only: ${plan.summary.currentOnly}`);
48
+ console.log(`Unchanged: ${plan.summary.unchanged}`);
49
+ if (isApply || isStatus || isFileAction) {
50
+ const appliedCount = (plan.applied || []).length;
51
+ const acceptedCount = (plan.accepted || []).length;
52
+ const deletedCount = (plan.deleted || []).length;
53
+ const skippedCount = (plan.skipped || []).length;
54
+ const conflictCount = (plan.conflicts || []).length;
55
+ if (isApply && appliedCount === 0 && skippedCount === 0 && conflictCount === 0 && plan.files.length === 0) {
56
+ console.log("No changes to apply.");
57
+ }
58
+ if (isStatus && plan.files.length === 0 && conflictCount === 0 && skippedCount === 0 && (plan.diagnostics || []).length === 0) {
59
+ console.log("No template update action needed.");
60
+ }
61
+ if (isApply && appliedCount > 0) {
62
+ console.log(`Applied ${appliedCount} file(s).`);
63
+ }
64
+ if (isFileAction && appliedCount > 0) {
65
+ console.log(`Accepted candidate for ${appliedCount} file(s).`);
66
+ }
67
+ if (acceptedCount > 0) {
68
+ console.log(`Accepted current baseline for ${acceptedCount} file(s).`);
69
+ }
70
+ if (deletedCount > 0) {
71
+ console.log(`Deleted ${deletedCount} current-only file(s).`);
72
+ }
73
+ if (skippedCount > 0) {
74
+ console.log(`Skipped ${skippedCount} current-only file(s).`);
75
+ }
76
+ if (conflictCount > 0) {
77
+ console.log(`Refused due to ${conflictCount} conflict(s).`);
78
+ }
79
+ }
80
+ const diagnostics = Array.isArray(plan.diagnostics) ? plan.diagnostics : [];
81
+ for (const diagnostic of diagnostics) {
82
+ console.log(`[${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`);
83
+ if (diagnostic.path) {
84
+ console.log(` path: ${diagnostic.path}`);
85
+ }
86
+ if (diagnostic.suggestedFix) {
87
+ console.log(` fix: ${diagnostic.suggestedFix}`);
88
+ }
89
+ if (diagnostic.step) {
90
+ console.log(` step: ${diagnostic.step}`);
91
+ }
92
+ }
93
+ for (const conflict of plan.conflicts || []) {
94
+ console.log(`Conflict: ${conflict.path}`);
95
+ console.log(` reason: ${conflict.reason}`);
96
+ }
97
+ for (const applied of plan.applied || []) {
98
+ console.log(`Applied: ${applied.path}`);
99
+ }
100
+ for (const skipped of plan.skipped || []) {
101
+ console.log(`Skipped: ${skipped.path}`);
102
+ console.log(` reason: ${skipped.reason}`);
103
+ }
104
+ for (const accepted of plan.accepted || []) {
105
+ console.log(`Accepted current: ${accepted.path}`);
106
+ }
107
+ for (const deleted of plan.deleted || []) {
108
+ console.log(`Deleted: ${deleted.path}`);
109
+ }
110
+ for (const file of plan.files) {
111
+ console.log("");
112
+ console.log(`${file.kind.toUpperCase()}: ${file.path}`);
113
+ if (file.current) {
114
+ console.log(` current sha256: ${file.current.sha256}`);
115
+ console.log(` current size: ${file.current.size}`);
116
+ }
117
+ if (file.candidate) {
118
+ console.log(` candidate sha256: ${file.candidate.sha256}`);
119
+ console.log(` candidate size: ${file.candidate.size}`);
120
+ }
121
+ if (file.binary) {
122
+ console.log(" diff: binary file");
123
+ } else if (file.diffOmitted && !file.unifiedDiff) {
124
+ console.log(" diff: hash-only");
125
+ }
126
+ if (file.unifiedDiff) {
127
+ console.log(file.unifiedDiff.trimEnd());
128
+ }
129
+ }
130
+ if (plan.files.length === 0) {
131
+ console.log("No template-owned file changes found.");
132
+ }
133
+ if (!isApply && !isCheck && !isStatus && !isFileAction) {
134
+ console.log("");
135
+ console.log("This command did not write files. Review the plan before applying template updates.");
136
+ } else if (isCheck || isStatus) {
137
+ console.log("");
138
+ console.log("This command did not write files.");
139
+ }
140
+ }
141
+
142
+ /**
143
+ * @param {any} status
144
+ * @returns {any}
145
+ */
146
+ export function buildTemplateUpdateRecommendationPayload(status) {
147
+ /** @type {Array<{ action: string, command: string|null, reason: string, path: string|null }>} */
148
+ const recommendations = [];
149
+ /** @type {any[]} */
150
+ const diagnostics = Array.isArray(status.diagnostics)
151
+ ? status.diagnostics.map((/** @type {any} */ diagnostic) => diagnostic.code === "template_update_available"
152
+ ? { ...diagnostic, severity: "warning" }
153
+ : diagnostic)
154
+ : [];
155
+ const errorDiagnostics = diagnostics.filter((/** @type {any} */ diagnostic) => diagnostic.severity === "error");
156
+ const conflicts = Array.isArray(status.conflicts) ? status.conflicts : [];
157
+ const skipped = Array.isArray(status.skipped) ? status.skipped : [];
158
+ const files = Array.isArray(status.files) ? status.files : [];
159
+ const addedChanged = files.filter((/** @type {any} */ file) => file.kind === "added" || file.kind === "changed");
160
+
161
+ if (errorDiagnostics.length > 0) {
162
+ recommendations.push({
163
+ action: "resolve-errors",
164
+ command: "topogram template update --status",
165
+ reason: "Template policy, compatibility, baseline, or conflict errors must be resolved before applying candidate files.",
166
+ path: null
167
+ });
168
+ }
169
+ for (const conflict of conflicts) {
170
+ recommendations.push({
171
+ action: "review-conflict",
172
+ command: `topogram template update --accept-current ${conflict.path}`,
173
+ reason: "Local edits differ from the last trusted template-owned baseline. Accept current after review, or apply the candidate manually.",
174
+ path: conflict.path
175
+ });
176
+ }
177
+ if (addedChanged.length > 0 && conflicts.length === 0 && errorDiagnostics.length === 0) {
178
+ recommendations.push({
179
+ action: "apply-candidate",
180
+ command: "topogram template update --apply",
181
+ reason: `${addedChanged.length} added or changed candidate file(s) can be applied without local conflicts.`,
182
+ path: null
183
+ });
184
+ }
185
+ for (const item of skipped) {
186
+ recommendations.push({
187
+ action: "review-delete",
188
+ command: `topogram template update --delete-current ${item.path}`,
189
+ reason: "The candidate no longer owns this current file. Delete it only after review.",
190
+ path: item.path
191
+ });
192
+ }
193
+ if (files.length === 0 && errorDiagnostics.length === 0) {
194
+ recommendations.push({
195
+ action: "none",
196
+ command: null,
197
+ reason: "Current project files already match the candidate template.",
198
+ path: null
199
+ });
200
+ }
201
+ if (status.candidate?.id && status.candidate?.version && errorDiagnostics.length === 0) {
202
+ recommendations.push({
203
+ action: "pin-reviewed-version",
204
+ command: `topogram template policy pin ${status.candidate.id}@${status.candidate.version}`,
205
+ reason: "After reviewing or applying this candidate, pin the template version in project policy.",
206
+ path: null
207
+ });
208
+ }
209
+ return {
210
+ ...status,
211
+ ok: errorDiagnostics.length === 0,
212
+ mode: "recommend",
213
+ writes: false,
214
+ issues: errorDiagnostics.map((/** @type {any} */ diagnostic) => diagnostic.message),
215
+ diagnostics,
216
+ recommendations
217
+ };
218
+ }
219
+
220
+ /**
221
+ * @param {ReturnType<typeof buildTemplateUpdateRecommendationPayload>} payload
222
+ * @returns {void}
223
+ */
224
+ export function printTemplateUpdateRecommendation(payload) {
225
+ console.log(payload.ok ? "Template update recommendation: ready" : "Template update recommendation: blocked");
226
+ console.log(`Current: ${payload.current?.id || "unknown"}@${payload.current?.version || "unknown"}`);
227
+ console.log(`Candidate: ${payload.candidate?.id || "unknown"}@${payload.candidate?.version || "unknown"}`);
228
+ console.log(`Added: ${payload.summary.added}`);
229
+ console.log(`Changed: ${payload.summary.changed}`);
230
+ console.log(`Current-only: ${payload.summary.currentOnly}`);
231
+ console.log(`Conflicts: ${payload.conflicts.length}`);
232
+ if (payload.reportPath) {
233
+ console.log(`Report: ${payload.reportPath}`);
234
+ }
235
+ for (const diagnostic of payload.diagnostics || []) {
236
+ console.log(`[${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`);
237
+ if (diagnostic.path) {
238
+ console.log(` path: ${diagnostic.path}`);
239
+ }
240
+ if (diagnostic.suggestedFix) {
241
+ console.log(` fix: ${diagnostic.suggestedFix}`);
242
+ }
243
+ }
244
+ console.log("");
245
+ console.log("Recommended next steps:");
246
+ for (const recommendation of payload.recommendations) {
247
+ console.log(`- ${recommendation.reason}`);
248
+ if (recommendation.command) {
249
+ console.log(` ${recommendation.command}`);
250
+ }
251
+ }
252
+ }
253
+
254
+ /**
255
+ * @param {{ args: string[], inputPath: string, templateIndex: number, templateName: string|null|undefined, useLatestTemplate: boolean, outPath?: string|null }} options
256
+ * @returns {any}
257
+ */
258
+ export function buildTemplateUpdateCliPayload(options) {
259
+ const { args, inputPath, templateIndex, templateName, useLatestTemplate, outPath = null } = options;
260
+ const applyUpdate = args.includes("--apply");
261
+ const checkUpdate = args.includes("--check");
262
+ const planUpdate = args.includes("--plan");
263
+ const statusUpdate = args.includes("--status");
264
+ const recommendUpdate = args.includes("--recommend");
265
+ const acceptCurrentIndex = args.indexOf("--accept-current");
266
+ const acceptCandidateIndex = args.indexOf("--accept-candidate");
267
+ const deleteCurrentIndex = args.indexOf("--delete-current");
268
+ const acceptCurrentUpdate = acceptCurrentIndex >= 0;
269
+ const acceptCandidateUpdate = acceptCandidateIndex >= 0;
270
+ const deleteCurrentUpdate = deleteCurrentIndex >= 0;
271
+ const fileAction = acceptCurrentUpdate ? "accept-current" : acceptCandidateUpdate ? "accept-candidate" : deleteCurrentUpdate ? "delete-current" : null;
272
+ const fileActionIndex = acceptCurrentUpdate ? acceptCurrentIndex : acceptCandidateUpdate ? acceptCandidateIndex : deleteCurrentUpdate ? deleteCurrentIndex : -1;
273
+ const fileActionPath = fileActionIndex >= 0 ? args[fileActionIndex + 1] : null;
274
+ const updateModeCount = [applyUpdate, checkUpdate, planUpdate, statusUpdate, recommendUpdate, acceptCurrentUpdate, acceptCandidateUpdate, deleteCurrentUpdate].filter(Boolean).length;
275
+ if (updateModeCount > 1) {
276
+ throw new Error("Choose one template update mode or file adoption action.");
277
+ }
278
+ if (updateModeCount === 0) {
279
+ throw new Error("Template update requires `--status`, `--recommend`, `--plan`, `--check`, `--apply`, `--accept-current <file>`, `--accept-candidate <file>`, or `--delete-current <file>`.");
280
+ }
281
+ if (fileAction && (!fileActionPath || fileActionPath.startsWith("-"))) {
282
+ throw new Error(`Template update ${fileAction} requires a relative file path.`);
283
+ }
284
+ const projectConfigInfo = loadProjectConfig(inputPath);
285
+ if (!projectConfigInfo) {
286
+ throw new Error("Cannot update template without topogram.project.json.");
287
+ }
288
+ if (!projectConfigInfo.config.template?.id && !projectConfigInfo.config.template?.sourceSpec) {
289
+ throw new Error("Cannot update template because this project is detached from template metadata.");
290
+ }
291
+ const requestedTemplateName = templateIndex >= 0
292
+ ? templateName
293
+ : useLatestTemplate
294
+ ? latestTemplateInfo(templateMetadataFromProjectConfig(projectConfigInfo.config)).candidateSpec
295
+ : null;
296
+ if (useLatestTemplate && !requestedTemplateName) {
297
+ throw new Error("Cannot use --latest because the current template is not package-backed.");
298
+ }
299
+ let update;
300
+ try {
301
+ const updateOptions = {
302
+ projectRoot: projectConfigInfo.configDir,
303
+ projectConfig: projectConfigInfo.config,
304
+ templateName: requestedTemplateName,
305
+ templatesRoot: TEMPLATES_ROOT
306
+ };
307
+ update = fileAction
308
+ ? applyTemplateUpdateFileAction({ ...updateOptions, action: fileAction, filePath: fileActionPath || "" })
309
+ : recommendUpdate
310
+ ? buildTemplateUpdateRecommendationPayload(buildTemplateUpdateStatus(updateOptions))
311
+ : (applyUpdate ? applyTemplateUpdate : checkUpdate ? buildTemplateUpdateCheck : statusUpdate ? buildTemplateUpdateStatus : buildTemplateUpdatePlan)(updateOptions);
312
+ } catch (error) {
313
+ const message = messageFromError(error);
314
+ update = {
315
+ ok: false,
316
+ mode: fileAction || (applyUpdate ? "apply" : checkUpdate ? "check" : statusUpdate ? "status" : recommendUpdate ? "recommend" : "plan"),
317
+ writes: false,
318
+ current: {
319
+ id: typeof projectConfigInfo.config.template?.id === "string" ? projectConfigInfo.config.template.id : null,
320
+ version: typeof projectConfigInfo.config.template?.version === "string" ? projectConfigInfo.config.template.version : null
321
+ },
322
+ candidate: null,
323
+ compatible: false,
324
+ issues: [message],
325
+ diagnostics: [templateCheckDiagnostic({
326
+ code: "template_resolve_failed",
327
+ message,
328
+ path: templateIndex >= 0 && typeof templateName === "string" && path.isAbsolute(templateName) ? templateName : null,
329
+ suggestedFix: "Check the template path or package spec, and verify private registry authentication if this is a package template.",
330
+ step: "resolve-candidate"
331
+ })],
332
+ summary: { added: 0, changed: 0, currentOnly: 0, unchanged: 0 },
333
+ files: [],
334
+ applied: [],
335
+ skipped: [],
336
+ conflicts: [],
337
+ recommendations: recommendUpdate ? [{
338
+ action: "resolve-errors",
339
+ command: "topogram template update --status",
340
+ reason: "Resolve the candidate template before choosing an update action.",
341
+ path: null
342
+ }] : undefined
343
+ };
344
+ }
345
+ if (outPath) {
346
+ const reportPath = path.resolve(outPath);
347
+ fs.mkdirSync(path.dirname(reportPath), { recursive: true });
348
+ fs.writeFileSync(reportPath, `${stableStringify(update)}\n`, "utf8");
349
+ update.reportPath = reportPath;
350
+ }
351
+ return update;
352
+ }
@@ -77,7 +77,7 @@ export function runTemplateCommand(context) {
77
77
  }
78
78
 
79
79
  if (command === "status") {
80
- const projectConfigInfo = loadProjectConfig(inputPath || "./topogram");
80
+ const projectConfigInfo = loadProjectConfig(inputPath || "./topo");
81
81
  if (!projectConfigInfo) {
82
82
  throw new Error("Cannot inspect template status without topogram.project.json.");
83
83
  }
@@ -108,7 +108,7 @@ export function runTemplateCommand(context) {
108
108
  }
109
109
 
110
110
  if (command === "policy:init") {
111
- const projectConfigInfo = loadProjectConfig(inputPath || "./topogram");
111
+ const projectConfigInfo = loadProjectConfig(inputPath || "./topo");
112
112
  if (!projectConfigInfo) {
113
113
  throw new Error("Cannot initialize template policy without topogram.project.json.");
114
114
  }
@@ -131,7 +131,7 @@ export function runTemplateCommand(context) {
131
131
  }
132
132
 
133
133
  if (command === "policy:check") {
134
- const payload = buildTemplatePolicyCheckPayload(inputPath || "./topogram");
134
+ const payload = buildTemplatePolicyCheckPayload(inputPath || "./topo");
135
135
  if (json) {
136
136
  console.log(stableStringify(payload));
137
137
  } else {
@@ -141,7 +141,7 @@ export function runTemplateCommand(context) {
141
141
  }
142
142
 
143
143
  if (command === "policy:explain") {
144
- const payload = buildTemplatePolicyExplainPayload(inputPath || "./topogram");
144
+ const payload = buildTemplatePolicyExplainPayload(inputPath || "./topo");
145
145
  if (json) {
146
146
  console.log(stableStringify(payload));
147
147
  } else {
@@ -151,7 +151,7 @@ export function runTemplateCommand(context) {
151
151
  }
152
152
 
153
153
  if (command === "policy:pin") {
154
- const payload = buildTemplatePolicyPinPayload(inputPath || "./topogram", commandArgs.templatePolicyPinSpec);
154
+ const payload = buildTemplatePolicyPinPayload(inputPath || "./topo", commandArgs.templatePolicyPinSpec);
155
155
  if (json) {
156
156
  console.log(stableStringify(payload));
157
157
  } else {
@@ -178,7 +178,7 @@ export function runTemplateCommand(context) {
178
178
  if (command === "update") {
179
179
  const payload = buildTemplateUpdateCliPayload({
180
180
  args,
181
- inputPath: inputPath || "./topogram",
181
+ inputPath: inputPath || "./topo",
182
182
  templateIndex: args.indexOf("--template"),
183
183
  templateName,
184
184
  useLatestTemplate: args.includes("--latest"),