@topogram/cli 0.3.63 → 0.3.65

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 (344) hide show
  1. package/package.json +1 -1
  2. package/src/adoption/plan/index.js +703 -0
  3. package/src/adoption/plan.d.ts +6 -0
  4. package/src/adoption/plan.js +12 -703
  5. package/src/adoption/reporting.d.ts +10 -0
  6. package/src/adoption/review-groups.d.ts +6 -0
  7. package/src/agent-brief.d.ts +3 -0
  8. package/src/agent-brief.js +495 -0
  9. package/src/agent-ops/query-builders/auth.js +375 -0
  10. package/src/agent-ops/query-builders/change-risk/change-plan.js +123 -0
  11. package/src/agent-ops/query-builders/change-risk/import-plan.js +49 -0
  12. package/src/agent-ops/query-builders/change-risk/maintained.js +286 -0
  13. package/src/agent-ops/query-builders/change-risk/review-packets.js +123 -0
  14. package/src/agent-ops/query-builders/change-risk/risk.js +189 -0
  15. package/src/agent-ops/query-builders/change-risk.js +25 -0
  16. package/src/agent-ops/query-builders/common.js +149 -0
  17. package/src/agent-ops/query-builders/maintained-risk.js +539 -0
  18. package/src/agent-ops/query-builders/maintained-shared.js +120 -0
  19. package/src/agent-ops/query-builders/multi-agent.js +547 -0
  20. package/src/agent-ops/query-builders/projection-impacts.js +514 -0
  21. package/src/agent-ops/query-builders/work-packets.js +417 -0
  22. package/src/agent-ops/query-builders/workflow-context-shared.js +300 -0
  23. package/src/agent-ops/query-builders/workflow-context.js +398 -0
  24. package/src/agent-ops/query-builders/workflow-presets-core.js +676 -0
  25. package/src/agent-ops/query-builders/workflow-presets.js +341 -0
  26. package/src/agent-ops/query-builders.d.ts +26 -0
  27. package/src/agent-ops/query-builders.js +42 -5021
  28. package/src/archive/archive.d.ts +2 -0
  29. package/src/archive/compact.d.ts +1 -0
  30. package/src/archive/unarchive.d.ts +1 -0
  31. package/src/catalog/constants.js +10 -0
  32. package/src/catalog/copy.js +60 -0
  33. package/src/catalog/diagnostics.js +15 -0
  34. package/src/catalog/entries.js +42 -0
  35. package/src/catalog/files.js +67 -0
  36. package/src/catalog/provenance.js +122 -0
  37. package/src/catalog/source.js +150 -0
  38. package/src/catalog/validation.js +252 -0
  39. package/src/catalog.d.ts +12 -0
  40. package/src/catalog.js +18 -750
  41. package/src/cli/catalog-alias.d.ts +1 -0
  42. package/src/cli/command-parser.js +38 -0
  43. package/src/cli/command-parsers/core.js +102 -0
  44. package/src/cli/command-parsers/generator.js +39 -0
  45. package/src/cli/command-parsers/import.js +44 -0
  46. package/src/cli/command-parsers/legacy-workflow.js +21 -0
  47. package/src/cli/command-parsers/project.js +47 -0
  48. package/src/cli/command-parsers/sdlc.js +47 -0
  49. package/src/cli/command-parsers/shared.js +51 -0
  50. package/src/cli/command-parsers/template.js +48 -0
  51. package/src/cli/commands/agent.js +47 -0
  52. package/src/cli/commands/catalog/check.js +31 -0
  53. package/src/cli/commands/catalog/copy.js +59 -0
  54. package/src/cli/commands/catalog/doctor.js +248 -0
  55. package/src/cli/commands/catalog/help.js +21 -0
  56. package/src/cli/commands/catalog/list.js +52 -0
  57. package/src/cli/commands/catalog/runner.js +92 -0
  58. package/src/cli/commands/catalog/shared.js +17 -0
  59. package/src/cli/commands/catalog/show.js +134 -0
  60. package/src/cli/commands/catalog.js +32 -0
  61. package/src/cli/commands/check.js +268 -0
  62. package/src/cli/commands/doctor.js +268 -0
  63. package/src/cli/commands/emit.js +149 -0
  64. package/src/cli/commands/generate.js +96 -0
  65. package/src/cli/commands/generator-policy/package-info.js +162 -0
  66. package/src/cli/commands/generator-policy/payloads.js +372 -0
  67. package/src/cli/commands/generator-policy/printers.js +159 -0
  68. package/src/cli/commands/generator-policy/runner.js +81 -0
  69. package/src/cli/commands/generator-policy/shared.js +39 -0
  70. package/src/cli/commands/generator-policy.js +17 -0
  71. package/src/cli/commands/generator.js +443 -0
  72. package/src/cli/commands/import/adopt.js +170 -0
  73. package/src/cli/commands/import/check.js +91 -0
  74. package/src/cli/commands/import/diff.js +84 -0
  75. package/src/cli/commands/import/help.js +47 -0
  76. package/src/cli/commands/import/paths.js +277 -0
  77. package/src/cli/commands/import/plan.js +284 -0
  78. package/src/cli/commands/import/refresh.js +470 -0
  79. package/src/cli/commands/import/status-history.js +196 -0
  80. package/src/cli/commands/import/workspace.js +230 -0
  81. package/src/cli/commands/import-runner.js +157 -0
  82. package/src/cli/commands/import.js +35 -0
  83. package/src/cli/commands/inspect.js +55 -0
  84. package/src/cli/commands/new.js +94 -0
  85. package/src/cli/commands/package/constants.js +17 -0
  86. package/src/cli/commands/package/doctor.js +240 -0
  87. package/src/cli/commands/package/help.js +27 -0
  88. package/src/cli/commands/package/lockfile.js +135 -0
  89. package/src/cli/commands/package/npm.js +97 -0
  90. package/src/cli/commands/package/reporting.js +35 -0
  91. package/src/cli/commands/package/runner.js +33 -0
  92. package/src/cli/commands/package/shared.js +9 -0
  93. package/src/cli/commands/package/update-cli.js +252 -0
  94. package/src/cli/commands/package/versions.js +35 -0
  95. package/src/cli/commands/package.js +31 -0
  96. package/src/cli/commands/query/change-plan.js +68 -0
  97. package/src/cli/commands/query/definitions.js +202 -0
  98. package/src/cli/commands/query/import-adopt.js +121 -0
  99. package/src/cli/commands/query/runner/artifacts.js +102 -0
  100. package/src/cli/commands/query/runner/boundaries.js +211 -0
  101. package/src/cli/commands/query/runner/change.js +182 -0
  102. package/src/cli/commands/query/runner/import-adopt.js +111 -0
  103. package/src/cli/commands/query/runner/index.js +31 -0
  104. package/src/cli/commands/query/runner/output.js +12 -0
  105. package/src/cli/commands/query/runner/workflow.js +241 -0
  106. package/src/cli/commands/query/runner.js +3 -0
  107. package/src/cli/commands/query/workflow-context.js +5 -0
  108. package/src/cli/commands/query/workspace.js +274 -0
  109. package/src/cli/commands/query.js +11 -0
  110. package/src/cli/commands/release-rollout.js +257 -0
  111. package/src/cli/commands/release-shared.js +528 -0
  112. package/src/cli/commands/release-status.js +429 -0
  113. package/src/cli/commands/release.js +107 -0
  114. package/src/cli/commands/sdlc.js +168 -0
  115. package/src/cli/commands/setup.js +76 -0
  116. package/src/cli/commands/source.js +291 -0
  117. package/src/cli/commands/template/baseline.js +100 -0
  118. package/src/cli/commands/template/check.js +466 -0
  119. package/src/cli/commands/template/constants.js +8 -0
  120. package/src/cli/commands/template/diagnostics.js +26 -0
  121. package/src/cli/commands/template/help.js +28 -0
  122. package/src/cli/commands/template/lifecycle.js +404 -0
  123. package/src/cli/commands/template/list-show.js +287 -0
  124. package/src/cli/commands/template/policy.js +422 -0
  125. package/src/cli/commands/template/shared.js +127 -0
  126. package/src/cli/commands/template/updates.js +352 -0
  127. package/src/cli/commands/template-runner.js +198 -0
  128. package/src/cli/commands/template.js +43 -0
  129. package/src/cli/commands/trust.js +219 -0
  130. package/src/cli/commands/version.js +40 -0
  131. package/src/cli/commands/widget.js +168 -0
  132. package/src/cli/commands/workflow.js +63 -0
  133. package/src/cli/dispatcher.js +392 -0
  134. package/src/cli/help-dispatch.js +188 -0
  135. package/src/cli/help.js +296 -0
  136. package/src/cli/migration-guidance.js +59 -0
  137. package/src/cli/options.js +96 -0
  138. package/src/cli/output-safety.js +107 -0
  139. package/src/cli/path-normalization.js +29 -0
  140. package/src/cli.js +47 -11711
  141. package/src/example-implementation.d.ts +2 -0
  142. package/src/format.d.ts +1 -0
  143. package/src/generator/api/contracts.js +497 -0
  144. package/src/generator/api/metadata.js +221 -0
  145. package/src/generator/api/openapi.js +559 -0
  146. package/src/generator/api/schema.js +124 -0
  147. package/src/generator/api/types.d.ts +98 -0
  148. package/src/generator/api.js +3 -1195
  149. package/src/generator/check.d.ts +1 -0
  150. package/src/generator/context/bundle.d.ts +1 -0
  151. package/src/generator/context/shared/domain-sdlc.js +282 -0
  152. package/src/generator/context/shared/maintained-boundary.js +665 -0
  153. package/src/generator/context/shared/metrics.js +85 -0
  154. package/src/generator/context/shared/primitives.js +64 -0
  155. package/src/generator/context/shared/relationships.js +453 -0
  156. package/src/generator/context/shared/summaries.js +263 -0
  157. package/src/generator/context/shared/types.d.ts +207 -0
  158. package/src/generator/context/shared.d.ts +44 -0
  159. package/src/generator/context/shared.js +80 -1390
  160. package/src/generator/context/slice/core.js +397 -0
  161. package/src/generator/context/slice/sdlc.js +417 -0
  162. package/src/generator/context/slice/ui-packets.js +183 -0
  163. package/src/generator/context/slice.js +2 -859
  164. package/src/generator/native/parity-bundle.js +2 -1
  165. package/src/generator/registry/index.js +507 -0
  166. package/src/generator/registry.js +18 -504
  167. package/src/generator/runtime/environment/index.js +666 -0
  168. package/src/generator/runtime/environment.js +4 -666
  169. package/src/generator/runtime/runtime-check/index.js +554 -0
  170. package/src/generator/runtime/runtime-check.js +4 -554
  171. package/src/generator/runtime/shared/index.js +572 -0
  172. package/src/generator/runtime/shared.js +19 -570
  173. package/src/generator/shared.d.ts +2 -0
  174. package/src/generator/surfaces/shared.d.ts +3 -0
  175. package/src/generator/surfaces/web/html-escape.js +22 -0
  176. package/src/generator/surfaces/web/react.js +10 -8
  177. package/src/generator/surfaces/web/sveltekit.js +7 -5
  178. package/src/generator/surfaces/web/vanilla.js +8 -4
  179. package/src/generator/widget-conformance/behavior-report.js +258 -0
  180. package/src/generator/widget-conformance/checks.js +371 -0
  181. package/src/generator/widget-conformance/projection-context.js +200 -0
  182. package/src/generator/widget-conformance/report.js +166 -0
  183. package/src/generator/widget-conformance/types.d.ts +121 -0
  184. package/src/generator/widget-conformance.js +3 -824
  185. package/src/generator.d.ts +2 -0
  186. package/src/github-client.js +520 -0
  187. package/src/import/core/context.d.ts +3 -0
  188. package/src/import/core/contracts.d.ts +1 -0
  189. package/src/import/core/registry.d.ts +4 -0
  190. package/src/import/core/runner/candidates.js +217 -0
  191. package/src/import/core/runner/options.js +22 -0
  192. package/src/import/core/runner/reports.js +50 -0
  193. package/src/import/core/runner/run.js +79 -0
  194. package/src/import/core/runner/tracks.js +150 -0
  195. package/src/import/core/runner/ui-drafts.js +337 -0
  196. package/src/import/core/runner.js +3 -698
  197. package/src/import/core/shared/api-routes.js +221 -0
  198. package/src/import/core/shared/candidates.js +97 -0
  199. package/src/import/core/shared/files.js +177 -0
  200. package/src/import/core/shared/next-app.js +389 -0
  201. package/src/import/core/shared/types.d.ts +51 -0
  202. package/src/import/core/shared/ui-routes.js +230 -0
  203. package/src/import/core/shared.js +67 -910
  204. package/src/import/extractors/api/flutter-dio.js +4 -8
  205. package/src/import/extractors/api/react-native-repository.js +4 -8
  206. package/src/import/index.d.ts +4 -0
  207. package/src/import/provenance.d.ts +4 -0
  208. package/src/new-project/constants.js +128 -0
  209. package/src/new-project/create.js +83 -0
  210. package/src/new-project/json.js +28 -0
  211. package/src/new-project/metadata.js +96 -0
  212. package/src/new-project/package-spec.js +161 -0
  213. package/src/new-project/project-files.js +348 -0
  214. package/src/new-project/template-policy.js +269 -0
  215. package/src/new-project/template-resolution.js +368 -0
  216. package/src/new-project/template-snapshots.js +430 -0
  217. package/src/new-project/template-updates.js +512 -0
  218. package/src/new-project/types.d.ts +83 -0
  219. package/src/new-project.js +6 -2188
  220. package/src/npm-safety.js +79 -0
  221. package/src/parser.d.ts +87 -0
  222. package/src/parser.js +118 -0
  223. package/src/path-helpers.d.ts +1 -0
  224. package/src/path-helpers.js +20 -0
  225. package/src/policy/review-boundaries.d.ts +15 -0
  226. package/src/project-config/index.js +564 -0
  227. package/src/project-config.js +19 -560
  228. package/src/reconcile/docs.d.ts +8 -0
  229. package/src/reconcile/journeys.d.ts +1 -0
  230. package/src/resolver/enrich/acceptance-criterion.js +2 -0
  231. package/src/resolver/enrich/bug.js +2 -0
  232. package/src/resolver/enrich/pitch.js +2 -0
  233. package/src/resolver/enrich/requirement.js +2 -0
  234. package/src/resolver/enrich/task.js +2 -0
  235. package/src/resolver/index.js +19 -2089
  236. package/src/resolver/normalize.js +384 -1
  237. package/src/resolver/plans.js +168 -0
  238. package/src/resolver/projections-api.js +494 -0
  239. package/src/resolver/projections-db.js +133 -0
  240. package/src/resolver/projections-ui.js +317 -0
  241. package/src/resolver/shapes.js +251 -0
  242. package/src/resolver/shared.js +278 -0
  243. package/src/resolver/widgets.js +132 -0
  244. package/src/resolver.d.ts +1 -0
  245. package/src/runtime-support.js +29 -0
  246. package/src/sdlc/adopt.d.ts +1 -0
  247. package/src/sdlc/check.d.ts +1 -0
  248. package/src/sdlc/explain.d.ts +1 -0
  249. package/src/sdlc/release.d.ts +1 -0
  250. package/src/sdlc/scaffold.d.ts +1 -0
  251. package/src/sdlc/transition.d.ts +1 -0
  252. package/src/template-trust/constants.js +62 -0
  253. package/src/template-trust/content.js +258 -0
  254. package/src/template-trust/diff.js +92 -0
  255. package/src/template-trust/policy.js +61 -0
  256. package/src/template-trust/record.js +90 -0
  257. package/src/template-trust/status.js +182 -0
  258. package/src/template-trust.js +24 -687
  259. package/src/text-helpers.d.ts +7 -0
  260. package/src/text-helpers.js +245 -0
  261. package/src/topogram-config.js +306 -0
  262. package/src/topogram-types.d.ts +69 -0
  263. package/src/validator/common.js +488 -0
  264. package/src/validator/data-model.js +237 -0
  265. package/src/validator/docs.js +167 -0
  266. package/src/validator/expressions.js +146 -1
  267. package/src/validator/index.d.ts +23 -0
  268. package/src/validator/index.js +32 -3585
  269. package/src/validator/kinds.d.ts +41 -0
  270. package/src/validator/kinds.js +2 -0
  271. package/src/validator/model-helpers.js +46 -0
  272. package/src/validator/per-kind/acceptance-criterion.js +5 -0
  273. package/src/validator/per-kind/bug.js +6 -0
  274. package/src/validator/per-kind/domain.js +15 -2
  275. package/src/validator/per-kind/pitch.js +7 -0
  276. package/src/validator/per-kind/requirement.js +5 -0
  277. package/src/validator/per-kind/task.js +7 -0
  278. package/src/validator/per-kind/widget.js +14 -0
  279. package/src/validator/projections/api-http-async.js +410 -0
  280. package/src/validator/projections/api-http-authz.js +88 -0
  281. package/src/validator/projections/api-http-core.js +205 -0
  282. package/src/validator/projections/api-http-policies.js +339 -0
  283. package/src/validator/projections/api-http-responses.js +233 -0
  284. package/src/validator/projections/api-http.js +44 -0
  285. package/src/validator/projections/db.js +353 -0
  286. package/src/validator/projections/generator-defaults.js +45 -0
  287. package/src/validator/projections/helpers.js +87 -0
  288. package/src/validator/projections/ui-helpers.js +214 -0
  289. package/src/validator/projections/ui-navigation.js +344 -0
  290. package/src/validator/projections/ui-structure.js +364 -0
  291. package/src/validator/projections/ui-widgets.js +493 -0
  292. package/src/validator/projections/ui.js +46 -0
  293. package/src/validator/registry.js +48 -1
  294. package/src/validator/utils.d.ts +20 -0
  295. package/src/validator/utils.js +115 -12
  296. package/src/validator.d.ts +2 -0
  297. package/src/widget-behavior.d.ts +1 -0
  298. package/src/workflows/adoption/index.js +26 -0
  299. package/src/workflows/docs-generate.js +262 -0
  300. package/src/workflows/docs-scan.js +703 -0
  301. package/src/workflows/docs.js +15 -0
  302. package/src/workflows/import-app/api/collect.js +221 -0
  303. package/src/workflows/import-app/api/openapi.js +257 -0
  304. package/src/workflows/import-app/api/routes.js +327 -0
  305. package/src/workflows/import-app/api/sources.js +22 -0
  306. package/src/workflows/import-app/api.js +4 -0
  307. package/src/workflows/import-app/db.js +538 -0
  308. package/src/workflows/import-app/index.js +30 -0
  309. package/src/workflows/import-app/shared.js +218 -0
  310. package/src/workflows/import-app/ui.js +443 -0
  311. package/src/workflows/import-app/workflow.js +159 -0
  312. package/src/workflows/reconcile/adoption-plan/build.js +208 -0
  313. package/src/workflows/reconcile/adoption-plan/dependencies.js +75 -0
  314. package/src/workflows/reconcile/adoption-plan/outputs.js +143 -0
  315. package/src/workflows/reconcile/adoption-plan/paths.js +58 -0
  316. package/src/workflows/reconcile/adoption-plan/projection-patches.js +177 -0
  317. package/src/workflows/reconcile/adoption-plan/reasons.js +107 -0
  318. package/src/workflows/reconcile/adoption-plan.js +32 -0
  319. package/src/workflows/reconcile/auth/closures.js +115 -0
  320. package/src/workflows/reconcile/auth/formatters.js +142 -0
  321. package/src/workflows/reconcile/auth/inference.js +330 -0
  322. package/src/workflows/reconcile/auth/roles.js +122 -0
  323. package/src/workflows/reconcile/auth.js +37 -0
  324. package/src/workflows/reconcile/bundle-core/index.js +600 -0
  325. package/src/workflows/reconcile/bundle-core.js +14 -0
  326. package/src/workflows/reconcile/bundle-shared.js +75 -0
  327. package/src/workflows/reconcile/candidate-model.js +477 -0
  328. package/src/workflows/reconcile/canonical-surface.js +264 -0
  329. package/src/workflows/reconcile/gap-report.js +333 -0
  330. package/src/workflows/reconcile/ids.js +6 -0
  331. package/src/workflows/reconcile/impacts/adoption-plan.js +192 -0
  332. package/src/workflows/reconcile/impacts/indexes.js +101 -0
  333. package/src/workflows/reconcile/impacts/patches.js +252 -0
  334. package/src/workflows/reconcile/impacts/reports.js +80 -0
  335. package/src/workflows/reconcile/impacts.js +16 -0
  336. package/src/workflows/reconcile/index.js +7 -0
  337. package/src/workflows/reconcile/renderers.js +461 -0
  338. package/src/workflows/reconcile/summary.js +90 -0
  339. package/src/workflows/reconcile/workflow.js +309 -0
  340. package/src/workflows/shared.js +189 -0
  341. package/src/workflows/types.d.ts +93 -0
  342. package/src/workflows.d.ts +1 -0
  343. package/src/workflows.js +10 -7652
  344. package/src/workspace-docs.d.ts +29 -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
+ }
@@ -0,0 +1,198 @@
1
+ // @ts-check
2
+
3
+ import path from "node:path";
4
+
5
+ import { stableStringify } from "../../format.js";
6
+ import { loadProjectConfig } from "../../project-config.js";
7
+ import { writeTemplatePolicyForProject } from "../../new-project.js";
8
+ import {
9
+ buildTemplateCheckPayload,
10
+ buildTemplateDetachPayload,
11
+ buildTemplateExplainPayload,
12
+ buildTemplateListPayload,
13
+ buildTemplatePolicyCheckPayload,
14
+ buildTemplatePolicyExplainPayload,
15
+ buildTemplatePolicyPinPayload,
16
+ buildTemplateShowPayload,
17
+ buildTemplateStatusPayload,
18
+ buildTemplateUpdateCliPayload,
19
+ printTemplateCheckPayload,
20
+ printTemplateDetachPayload,
21
+ printTemplateExplain,
22
+ printTemplateHelp,
23
+ printTemplateList,
24
+ printTemplatePolicyCheckPayload,
25
+ printTemplatePolicyExplainPayload,
26
+ printTemplatePolicyPinPayload,
27
+ printTemplateShow,
28
+ printTemplateStatus,
29
+ printTemplateUpdatePlan,
30
+ printTemplateUpdateRecommendation
31
+ } from "./template.js";
32
+
33
+ /**
34
+ * @param {{ commandArgs: Record<string, any>, inputPath: string|null|undefined, args: string[], catalogSource?: string|null, templateName?: string|null, outPath?: string|null, json?: boolean }} context
35
+ * @returns {number}
36
+ */
37
+ export function runTemplateCommand(context) {
38
+ const { commandArgs, inputPath, args, catalogSource = null, templateName = null, outPath = null, json = false } = context;
39
+ const command = commandArgs.templateCommand;
40
+ if (command === "list") {
41
+ const payload = buildTemplateListPayload({ catalogSource });
42
+ if (json) {
43
+ console.log(stableStringify(payload));
44
+ } else {
45
+ printTemplateList(payload);
46
+ }
47
+ return 0;
48
+ }
49
+
50
+ if (command === "show") {
51
+ if (!inputPath) {
52
+ console.error("Missing required <id>.");
53
+ printTemplateHelp();
54
+ return 1;
55
+ }
56
+ const payload = buildTemplateShowPayload(inputPath, catalogSource);
57
+ if (json) {
58
+ console.log(stableStringify(payload));
59
+ } else {
60
+ printTemplateShow(payload);
61
+ }
62
+ return payload.ok ? 0 : 1;
63
+ }
64
+
65
+ if (command === "explain") {
66
+ const projectConfigInfo = loadProjectConfig(inputPath || ".");
67
+ if (!projectConfigInfo) {
68
+ throw new Error("Cannot explain template lifecycle without topogram.project.json.");
69
+ }
70
+ const payload = buildTemplateExplainPayload(projectConfigInfo);
71
+ if (json) {
72
+ console.log(stableStringify(payload));
73
+ } else {
74
+ printTemplateExplain(payload);
75
+ }
76
+ return payload.ok ? 0 : 1;
77
+ }
78
+
79
+ if (command === "status") {
80
+ const projectConfigInfo = loadProjectConfig(inputPath || "./topogram");
81
+ if (!projectConfigInfo) {
82
+ throw new Error("Cannot inspect template status without topogram.project.json.");
83
+ }
84
+ const payload = buildTemplateStatusPayload(projectConfigInfo, { latest: args.includes("--latest") });
85
+ if (json) {
86
+ console.log(stableStringify(payload));
87
+ } else {
88
+ printTemplateStatus(payload);
89
+ }
90
+ return payload.ok ? 0 : 1;
91
+ }
92
+
93
+ if (command === "detach") {
94
+ const projectConfigInfo = loadProjectConfig(inputPath || ".");
95
+ if (!projectConfigInfo) {
96
+ throw new Error("Cannot detach template metadata without topogram.project.json.");
97
+ }
98
+ const payload = buildTemplateDetachPayload(projectConfigInfo, {
99
+ dryRun: args.includes("--dry-run"),
100
+ removePolicy: args.includes("--remove-policy")
101
+ });
102
+ if (json) {
103
+ console.log(stableStringify(payload));
104
+ } else {
105
+ printTemplateDetachPayload(payload);
106
+ }
107
+ return payload.ok ? 0 : 1;
108
+ }
109
+
110
+ if (command === "policy:init") {
111
+ const projectConfigInfo = loadProjectConfig(inputPath || "./topogram");
112
+ if (!projectConfigInfo) {
113
+ throw new Error("Cannot initialize template policy without topogram.project.json.");
114
+ }
115
+ const policy = writeTemplatePolicyForProject(projectConfigInfo.configDir, projectConfigInfo.config);
116
+ const payload = {
117
+ ok: true,
118
+ path: path.join(projectConfigInfo.configDir, "topogram.template-policy.json"),
119
+ policy,
120
+ diagnostics: [],
121
+ errors: []
122
+ };
123
+ if (json) {
124
+ console.log(stableStringify(payload));
125
+ } else {
126
+ console.log(`Wrote template policy: ${payload.path}`);
127
+ console.log(`Allowed template ids: ${policy.allowedTemplateIds.join(", ") || "(any)"}`);
128
+ console.log(`Allowed sources: ${policy.allowedSources.join(", ") || "(any)"}`);
129
+ }
130
+ return 0;
131
+ }
132
+
133
+ if (command === "policy:check") {
134
+ const payload = buildTemplatePolicyCheckPayload(inputPath || "./topogram");
135
+ if (json) {
136
+ console.log(stableStringify(payload));
137
+ } else {
138
+ printTemplatePolicyCheckPayload(payload);
139
+ }
140
+ return payload.ok ? 0 : 1;
141
+ }
142
+
143
+ if (command === "policy:explain") {
144
+ const payload = buildTemplatePolicyExplainPayload(inputPath || "./topogram");
145
+ if (json) {
146
+ console.log(stableStringify(payload));
147
+ } else {
148
+ printTemplatePolicyExplainPayload(payload);
149
+ }
150
+ return payload.ok ? 0 : 1;
151
+ }
152
+
153
+ if (command === "policy:pin") {
154
+ const payload = buildTemplatePolicyPinPayload(inputPath || "./topogram", commandArgs.templatePolicyPinSpec);
155
+ if (json) {
156
+ console.log(stableStringify(payload));
157
+ } else {
158
+ printTemplatePolicyPinPayload(payload);
159
+ }
160
+ return payload.ok ? 0 : 1;
161
+ }
162
+
163
+ if (command === "check") {
164
+ if (!inputPath) {
165
+ console.error("Missing required <template-spec-or-path>.");
166
+ printTemplateHelp();
167
+ return 1;
168
+ }
169
+ const payload = buildTemplateCheckPayload(inputPath);
170
+ if (json) {
171
+ console.log(stableStringify(payload));
172
+ } else {
173
+ printTemplateCheckPayload(payload);
174
+ }
175
+ return payload.ok ? 0 : 1;
176
+ }
177
+
178
+ if (command === "update") {
179
+ const payload = buildTemplateUpdateCliPayload({
180
+ args,
181
+ inputPath: inputPath || "./topogram",
182
+ templateIndex: args.indexOf("--template"),
183
+ templateName,
184
+ useLatestTemplate: args.includes("--latest"),
185
+ outPath
186
+ });
187
+ if (json) {
188
+ console.log(stableStringify(payload));
189
+ } else if (args.includes("--recommend")) {
190
+ printTemplateUpdateRecommendation(payload);
191
+ } else {
192
+ printTemplateUpdatePlan(payload);
193
+ }
194
+ return payload.ok ? 0 : 1;
195
+ }
196
+
197
+ throw new Error(`Unknown template command '${command}'`);
198
+ }
@@ -0,0 +1,43 @@
1
+ // @ts-check
2
+
3
+ export { printTemplateHelp } from "./template/help.js";
4
+ export {
5
+ buildTemplateListPayload,
6
+ buildTemplateShowPayload,
7
+ printTemplateList,
8
+ printTemplateShow
9
+ } from "./template/list-show.js";
10
+ export {
11
+ latestTemplateInfo,
12
+ templateMetadataFromProjectConfig
13
+ } from "./template/shared.js";
14
+ export {
15
+ buildTemplateStatusPayload,
16
+ printTemplateStatus,
17
+ buildTemplateExplainPayload,
18
+ printTemplateExplain,
19
+ buildTemplateDetachPayload,
20
+ printTemplateDetachPayload
21
+ } from "./template/lifecycle.js";
22
+ export {
23
+ printTemplateUpdatePlan,
24
+ buildTemplateUpdateRecommendationPayload,
25
+ printTemplateUpdateRecommendation,
26
+ buildTemplateUpdateCliPayload
27
+ } from "./template/updates.js";
28
+ export {
29
+ templateCheckDiagnostic
30
+ } from "./template/diagnostics.js";
31
+ export {
32
+ buildTemplateCheckPayload,
33
+ printTemplateCheckPayload
34
+ } from "./template/check.js";
35
+ export {
36
+ buildTemplatePolicyCheckPayload,
37
+ printTemplatePolicyCheckPayload,
38
+ buildTemplatePolicyExplainPayload,
39
+ printTemplatePolicyExplainPayload,
40
+ buildTemplatePolicyPinPayload,
41
+ printTemplatePolicyPinPayload
42
+ } from "./template/policy.js";
43
+ export { buildTemplateOwnedBaselineStatus } from "./template/baseline.js";