@topogram/cli 0.3.64 → 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.
- package/package.json +1 -1
- package/src/adoption/plan/index.js +703 -0
- package/src/adoption/plan.js +12 -703
- package/src/agent-ops/query-builders/auth.js +375 -0
- package/src/agent-ops/query-builders/change-risk/change-plan.js +123 -0
- package/src/agent-ops/query-builders/change-risk/import-plan.js +49 -0
- package/src/agent-ops/query-builders/change-risk/maintained.js +286 -0
- package/src/agent-ops/query-builders/change-risk/review-packets.js +123 -0
- package/src/agent-ops/query-builders/change-risk/risk.js +189 -0
- package/src/agent-ops/query-builders/change-risk.js +25 -0
- package/src/agent-ops/query-builders/common.js +149 -0
- package/src/agent-ops/query-builders/maintained-risk.js +539 -0
- package/src/agent-ops/query-builders/maintained-shared.js +120 -0
- package/src/agent-ops/query-builders/multi-agent.js +547 -0
- package/src/agent-ops/query-builders/projection-impacts.js +514 -0
- package/src/agent-ops/query-builders/work-packets.js +417 -0
- package/src/agent-ops/query-builders/workflow-context-shared.js +300 -0
- package/src/agent-ops/query-builders/workflow-context.js +398 -0
- package/src/agent-ops/query-builders/workflow-presets-core.js +676 -0
- package/src/agent-ops/query-builders/workflow-presets.js +341 -0
- package/src/agent-ops/query-builders.d.ts +26 -26
- package/src/agent-ops/query-builders.js +42 -5021
- package/src/catalog/constants.js +10 -0
- package/src/catalog/copy.js +60 -0
- package/src/catalog/diagnostics.js +15 -0
- package/src/catalog/entries.js +42 -0
- package/src/catalog/files.js +67 -0
- package/src/catalog/provenance.js +122 -0
- package/src/catalog/source.js +150 -0
- package/src/catalog/validation.js +252 -0
- package/src/catalog.d.ts +2 -0
- package/src/catalog.js +18 -746
- package/src/cli/commands/catalog/check.js +31 -0
- package/src/cli/commands/catalog/copy.js +59 -0
- package/src/cli/commands/catalog/doctor.js +248 -0
- package/src/cli/commands/catalog/help.js +21 -0
- package/src/cli/commands/catalog/list.js +52 -0
- package/src/cli/commands/catalog/runner.js +92 -0
- package/src/cli/commands/catalog/shared.js +17 -0
- package/src/cli/commands/catalog/show.js +134 -0
- package/src/cli/commands/catalog.js +30 -615
- package/src/cli/commands/generator-policy/package-info.js +162 -0
- package/src/cli/commands/generator-policy/payloads.js +372 -0
- package/src/cli/commands/generator-policy/printers.js +159 -0
- package/src/cli/commands/generator-policy/runner.js +81 -0
- package/src/cli/commands/generator-policy/shared.js +39 -0
- package/src/cli/commands/generator-policy.js +15 -783
- package/src/cli/commands/import/adopt.js +170 -0
- package/src/cli/commands/import/check.js +91 -0
- package/src/cli/commands/import/diff.js +84 -0
- package/src/cli/commands/import/help.js +47 -0
- package/src/cli/commands/import/paths.js +277 -0
- package/src/cli/commands/import/plan.js +284 -0
- package/src/cli/commands/import/refresh.js +470 -0
- package/src/cli/commands/import/status-history.js +196 -0
- package/src/cli/commands/import/workspace.js +230 -0
- package/src/cli/commands/import.js +33 -1732
- package/src/cli/commands/package/constants.js +17 -0
- package/src/cli/commands/package/doctor.js +240 -0
- package/src/cli/commands/package/help.js +27 -0
- package/src/cli/commands/package/lockfile.js +135 -0
- package/src/cli/commands/package/npm.js +97 -0
- package/src/cli/commands/package/reporting.js +35 -0
- package/src/cli/commands/package/runner.js +33 -0
- package/src/cli/commands/package/shared.js +9 -0
- package/src/cli/commands/package/update-cli.js +252 -0
- package/src/cli/commands/package/versions.js +35 -0
- package/src/cli/commands/package.js +29 -813
- package/src/cli/commands/query/change-plan.js +68 -0
- package/src/cli/commands/query/definitions.js +202 -0
- package/src/cli/commands/query/import-adopt.js +121 -0
- package/src/cli/commands/query/runner/artifacts.js +102 -0
- package/src/cli/commands/query/runner/boundaries.js +211 -0
- package/src/cli/commands/query/runner/change.js +182 -0
- package/src/cli/commands/query/runner/import-adopt.js +111 -0
- package/src/cli/commands/query/runner/index.js +31 -0
- package/src/cli/commands/query/runner/output.js +12 -0
- package/src/cli/commands/query/runner/workflow.js +241 -0
- package/src/cli/commands/query/runner.js +3 -0
- package/src/cli/commands/query/workflow-context.js +5 -0
- package/src/cli/commands/query/workspace.js +274 -0
- package/src/cli/commands/query.js +9 -1300
- package/src/cli/commands/template/baseline.js +100 -0
- package/src/cli/commands/template/check.js +466 -0
- package/src/cli/commands/template/constants.js +8 -0
- package/src/cli/commands/template/diagnostics.js +26 -0
- package/src/cli/commands/template/help.js +28 -0
- package/src/cli/commands/template/lifecycle.js +404 -0
- package/src/cli/commands/template/list-show.js +287 -0
- package/src/cli/commands/template/policy.js +422 -0
- package/src/cli/commands/template/shared.js +127 -0
- package/src/cli/commands/template/updates.js +352 -0
- package/src/cli/commands/template.js +41 -2143
- package/src/generator/api/contracts.js +497 -0
- package/src/generator/api/metadata.js +221 -0
- package/src/generator/api/openapi.js +559 -0
- package/src/generator/api/schema.js +124 -0
- package/src/generator/api/types.d.ts +98 -0
- package/src/generator/api.js +3 -1195
- package/src/generator/context/shared/domain-sdlc.js +282 -0
- package/src/generator/context/shared/maintained-boundary.js +665 -0
- package/src/generator/context/shared/metrics.js +85 -0
- package/src/generator/context/shared/primitives.js +64 -0
- package/src/generator/context/shared/relationships.js +453 -0
- package/src/generator/context/shared/summaries.js +263 -0
- package/src/generator/context/shared/types.d.ts +207 -0
- package/src/generator/context/shared.d.ts +42 -0
- package/src/generator/context/shared.js +80 -1390
- package/src/generator/context/slice/core.js +397 -0
- package/src/generator/context/slice/sdlc.js +417 -0
- package/src/generator/context/slice/ui-packets.js +183 -0
- package/src/generator/context/slice.js +2 -859
- package/src/generator/registry/index.js +507 -0
- package/src/generator/registry.js +18 -504
- package/src/generator/runtime/environment/index.js +666 -0
- package/src/generator/runtime/environment.js +4 -666
- package/src/generator/runtime/runtime-check/index.js +554 -0
- package/src/generator/runtime/runtime-check.js +4 -554
- package/src/generator/runtime/shared/index.js +572 -0
- package/src/generator/runtime/shared.js +19 -570
- package/src/generator/shared.d.ts +2 -0
- package/src/generator/surfaces/shared.d.ts +3 -0
- package/src/generator/widget-conformance/behavior-report.js +258 -0
- package/src/generator/widget-conformance/checks.js +371 -0
- package/src/generator/widget-conformance/projection-context.js +200 -0
- package/src/generator/widget-conformance/report.js +166 -0
- package/src/generator/widget-conformance/types.d.ts +121 -0
- package/src/generator/widget-conformance.js +3 -824
- package/src/import/core/context.d.ts +3 -0
- package/src/import/core/contracts.d.ts +1 -0
- package/src/import/core/registry.d.ts +4 -0
- package/src/import/core/runner/candidates.js +217 -0
- package/src/import/core/runner/options.js +22 -0
- package/src/import/core/runner/reports.js +50 -0
- package/src/import/core/runner/run.js +79 -0
- package/src/import/core/runner/tracks.js +150 -0
- package/src/import/core/runner/ui-drafts.js +337 -0
- package/src/import/core/runner.js +3 -698
- package/src/import/core/shared/api-routes.js +221 -0
- package/src/import/core/shared/candidates.js +97 -0
- package/src/import/core/shared/files.js +177 -0
- package/src/import/core/shared/next-app.js +389 -0
- package/src/import/core/shared/types.d.ts +51 -0
- package/src/import/core/shared/ui-routes.js +230 -0
- package/src/import/core/shared.js +60 -861
- package/src/new-project/constants.js +128 -0
- package/src/new-project/create.js +83 -0
- package/src/new-project/json.js +28 -0
- package/src/new-project/metadata.js +96 -0
- package/src/new-project/package-spec.js +161 -0
- package/src/new-project/project-files.js +348 -0
- package/src/new-project/template-policy.js +269 -0
- package/src/new-project/template-resolution.js +368 -0
- package/src/new-project/template-snapshots.js +430 -0
- package/src/new-project/template-updates.js +512 -0
- package/src/new-project/types.d.ts +83 -0
- package/src/new-project.js +6 -2277
- package/src/parser.d.ts +87 -1
- package/src/parser.js +118 -0
- package/src/policy/review-boundaries.d.ts +15 -0
- package/src/project-config/index.js +564 -0
- package/src/project-config.js +19 -561
- package/src/resolver/enrich/acceptance-criterion.js +2 -0
- package/src/resolver/enrich/bug.js +2 -0
- package/src/resolver/enrich/pitch.js +2 -0
- package/src/resolver/enrich/requirement.js +2 -0
- package/src/resolver/enrich/task.js +2 -0
- package/src/resolver/index.js +19 -2089
- package/src/resolver/normalize.js +384 -1
- package/src/resolver/plans.js +168 -0
- package/src/resolver/projections-api.js +494 -0
- package/src/resolver/projections-db.js +133 -0
- package/src/resolver/projections-ui.js +317 -0
- package/src/resolver/shapes.js +251 -0
- package/src/resolver/shared.js +278 -0
- package/src/resolver/widgets.js +132 -0
- package/src/template-trust/constants.js +62 -0
- package/src/template-trust/content.js +258 -0
- package/src/template-trust/diff.js +92 -0
- package/src/template-trust/policy.js +61 -0
- package/src/template-trust/record.js +90 -0
- package/src/template-trust/status.js +182 -0
- package/src/template-trust.js +24 -687
- package/src/text-helpers.d.ts +1 -0
- package/src/topogram-types.d.ts +69 -0
- package/src/validator/common.js +488 -0
- package/src/validator/data-model.js +237 -0
- package/src/validator/docs.js +167 -0
- package/src/validator/expressions.js +146 -1
- package/src/validator/index.d.ts +23 -0
- package/src/validator/index.js +32 -3585
- package/src/validator/kinds.d.ts +41 -0
- package/src/validator/kinds.js +2 -0
- package/src/validator/model-helpers.js +46 -0
- package/src/validator/per-kind/acceptance-criterion.js +5 -0
- package/src/validator/per-kind/bug.js +6 -0
- package/src/validator/per-kind/domain.js +15 -2
- package/src/validator/per-kind/pitch.js +7 -0
- package/src/validator/per-kind/requirement.js +5 -0
- package/src/validator/per-kind/task.js +7 -0
- package/src/validator/per-kind/widget.js +14 -0
- package/src/validator/projections/api-http-async.js +410 -0
- package/src/validator/projections/api-http-authz.js +88 -0
- package/src/validator/projections/api-http-core.js +205 -0
- package/src/validator/projections/api-http-policies.js +339 -0
- package/src/validator/projections/api-http-responses.js +233 -0
- package/src/validator/projections/api-http.js +44 -0
- package/src/validator/projections/db.js +353 -0
- package/src/validator/projections/generator-defaults.js +45 -0
- package/src/validator/projections/helpers.js +87 -0
- package/src/validator/projections/ui-helpers.js +214 -0
- package/src/validator/projections/ui-navigation.js +344 -0
- package/src/validator/projections/ui-structure.js +364 -0
- package/src/validator/projections/ui-widgets.js +493 -0
- package/src/validator/projections/ui.js +46 -0
- package/src/validator/registry.js +48 -1
- package/src/validator/utils.d.ts +20 -0
- package/src/validator/utils.js +115 -12
- package/src/widget-behavior.d.ts +1 -0
- package/src/workflows/import-app/api/collect.js +221 -0
- package/src/workflows/import-app/api/openapi.js +257 -0
- package/src/workflows/import-app/api/routes.js +327 -0
- package/src/workflows/import-app/api/sources.js +22 -0
- package/src/workflows/import-app/api.js +2 -797
- package/src/workflows/reconcile/adoption-plan/build.js +208 -0
- package/src/workflows/reconcile/adoption-plan/dependencies.js +75 -0
- package/src/workflows/reconcile/adoption-plan/outputs.js +143 -0
- package/src/workflows/reconcile/adoption-plan/paths.js +58 -0
- package/src/workflows/reconcile/adoption-plan/projection-patches.js +177 -0
- package/src/workflows/reconcile/adoption-plan/reasons.js +107 -0
- package/src/workflows/reconcile/adoption-plan.js +30 -740
- package/src/workflows/reconcile/auth/closures.js +115 -0
- package/src/workflows/reconcile/auth/formatters.js +142 -0
- package/src/workflows/reconcile/auth/inference.js +330 -0
- package/src/workflows/reconcile/auth/roles.js +122 -0
- package/src/workflows/reconcile/auth.js +35 -690
- package/src/workflows/reconcile/bundle-core/index.js +600 -0
- package/src/workflows/reconcile/bundle-core.js +12 -598
- package/src/workflows/reconcile/canonical-surface.js +1 -1
- package/src/workflows/reconcile/impacts/adoption-plan.js +192 -0
- package/src/workflows/reconcile/impacts/indexes.js +101 -0
- package/src/workflows/reconcile/impacts/patches.js +252 -0
- package/src/workflows/reconcile/impacts/reports.js +80 -0
- package/src/workflows/reconcile/impacts.js +14 -623
- 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
|
+
}
|