@topogram/cli 0.3.62 → 0.3.64
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.d.ts +6 -0
- package/src/adoption/reporting.d.ts +10 -0
- package/src/adoption/review-groups.d.ts +6 -0
- package/src/agent-brief.d.ts +3 -0
- package/src/agent-brief.js +495 -0
- package/src/agent-ops/query-builders.d.ts +26 -0
- package/src/archive/archive.d.ts +2 -0
- package/src/archive/compact.d.ts +1 -0
- package/src/archive/unarchive.d.ts +1 -0
- package/src/catalog.d.ts +10 -0
- package/src/catalog.js +62 -66
- package/src/cli/catalog-alias.d.ts +1 -0
- package/src/cli/command-parser.js +38 -0
- package/src/cli/command-parsers/core.js +102 -0
- package/src/cli/command-parsers/generator.js +39 -0
- package/src/cli/command-parsers/import.js +44 -0
- package/src/cli/command-parsers/legacy-workflow.js +21 -0
- package/src/cli/command-parsers/project.js +47 -0
- package/src/cli/command-parsers/sdlc.js +47 -0
- package/src/cli/command-parsers/shared.js +51 -0
- package/src/cli/command-parsers/template.js +48 -0
- package/src/cli/commands/agent.js +47 -0
- package/src/cli/commands/catalog.js +617 -0
- package/src/cli/commands/check.js +268 -0
- package/src/cli/commands/doctor.js +268 -0
- package/src/cli/commands/emit.js +149 -0
- package/src/cli/commands/generate.js +96 -0
- package/src/cli/commands/generator-policy.js +785 -0
- package/src/cli/commands/generator.js +443 -0
- package/src/cli/commands/import-runner.js +157 -0
- package/src/cli/commands/import.js +1734 -0
- package/src/cli/commands/inspect.js +55 -0
- package/src/cli/commands/new.js +94 -0
- package/src/cli/commands/package.js +815 -0
- package/src/cli/commands/query.js +1302 -0
- package/src/cli/commands/release-rollout.js +257 -0
- package/src/cli/commands/release-shared.js +528 -0
- package/src/cli/commands/release-status.js +429 -0
- package/src/cli/commands/release.js +107 -0
- package/src/cli/commands/sdlc.js +168 -0
- package/src/cli/commands/setup.js +76 -0
- package/src/cli/commands/source.js +291 -0
- package/src/cli/commands/template-runner.js +198 -0
- package/src/cli/commands/template.js +2145 -0
- package/src/cli/commands/trust.js +219 -0
- package/src/cli/commands/version.js +40 -0
- package/src/cli/commands/widget.js +168 -0
- package/src/cli/commands/workflow.js +63 -0
- package/src/cli/dispatcher.js +392 -0
- package/src/cli/help-dispatch.js +188 -0
- package/src/cli/help.js +296 -0
- package/src/cli/migration-guidance.js +59 -0
- package/src/cli/options.js +96 -0
- package/src/cli/output-safety.js +107 -0
- package/src/cli/path-normalization.js +29 -0
- package/src/cli.js +47 -11711
- package/src/example-implementation.d.ts +2 -0
- package/src/format.d.ts +1 -0
- package/src/generator/check.d.ts +1 -0
- package/src/generator/context/bundle.d.ts +1 -0
- package/src/generator/context/shared.d.ts +2 -0
- package/src/generator/native/parity-bundle.js +2 -1
- package/src/generator/surfaces/web/html-escape.js +22 -0
- package/src/generator/surfaces/web/react.js +10 -8
- package/src/generator/surfaces/web/sveltekit.js +7 -5
- package/src/generator/surfaces/web/vanilla.js +8 -4
- package/src/generator.d.ts +2 -0
- package/src/github-client.js +520 -0
- package/src/import/core/shared.js +20 -62
- package/src/import/extractors/api/flutter-dio.js +4 -8
- package/src/import/extractors/api/react-native-repository.js +4 -8
- package/src/import/index.d.ts +4 -0
- package/src/import/provenance.d.ts +4 -0
- package/src/new-project.js +100 -11
- package/src/npm-safety.js +79 -0
- package/src/parser.d.ts +1 -0
- package/src/path-helpers.d.ts +1 -0
- package/src/path-helpers.js +20 -0
- package/src/project-config.js +1 -0
- package/src/reconcile/docs.d.ts +8 -0
- package/src/reconcile/journeys.d.ts +1 -0
- package/src/resolver.d.ts +1 -0
- package/src/runtime-support.js +29 -0
- package/src/sdlc/adopt.d.ts +1 -0
- package/src/sdlc/check.d.ts +1 -0
- package/src/sdlc/explain.d.ts +1 -0
- package/src/sdlc/release.d.ts +1 -0
- package/src/sdlc/scaffold.d.ts +1 -0
- package/src/sdlc/transition.d.ts +1 -0
- package/src/text-helpers.d.ts +6 -0
- package/src/text-helpers.js +245 -0
- package/src/topogram-config.js +306 -0
- package/src/validator.d.ts +2 -0
- package/src/workflows/adoption/index.js +26 -0
- package/src/workflows/docs-generate.js +262 -0
- package/src/workflows/docs-scan.js +703 -0
- package/src/workflows/docs.js +15 -0
- package/src/workflows/import-app/api.js +799 -0
- package/src/workflows/import-app/db.js +538 -0
- package/src/workflows/import-app/index.js +30 -0
- package/src/workflows/import-app/shared.js +218 -0
- package/src/workflows/import-app/ui.js +443 -0
- package/src/workflows/import-app/workflow.js +159 -0
- package/src/workflows/reconcile/adoption-plan.js +742 -0
- package/src/workflows/reconcile/auth.js +692 -0
- package/src/workflows/reconcile/bundle-core.js +600 -0
- package/src/workflows/reconcile/bundle-shared.js +75 -0
- package/src/workflows/reconcile/candidate-model.js +477 -0
- package/src/workflows/reconcile/canonical-surface.js +264 -0
- package/src/workflows/reconcile/gap-report.js +333 -0
- package/src/workflows/reconcile/ids.js +6 -0
- package/src/workflows/reconcile/impacts.js +625 -0
- package/src/workflows/reconcile/index.js +7 -0
- package/src/workflows/reconcile/renderers.js +461 -0
- package/src/workflows/reconcile/summary.js +90 -0
- package/src/workflows/reconcile/workflow.js +309 -0
- package/src/workflows/shared.js +189 -0
- package/src/workflows/types.d.ts +93 -0
- package/src/workflows.d.ts +1 -0
- package/src/workflows.js +10 -7652
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @returns {void}
|
|
5
|
+
*/
|
|
6
|
+
export function printSetupHelp() {
|
|
7
|
+
console.log("Usage: topogram setup package-auth|catalog-auth");
|
|
8
|
+
console.log("");
|
|
9
|
+
console.log("Prints setup guidance for public Topogram packages, private package auth, and catalog access. This command does not write credentials.");
|
|
10
|
+
console.log("");
|
|
11
|
+
console.log("Commands:");
|
|
12
|
+
console.log(" topogram setup package-auth");
|
|
13
|
+
console.log(" topogram setup catalog-auth");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @returns {void}
|
|
18
|
+
*/
|
|
19
|
+
export function printPackageAuthSetup() {
|
|
20
|
+
console.log("Topogram package auth setup");
|
|
21
|
+
console.log("");
|
|
22
|
+
console.log("Public Topogram CLI, generator, template, and starter packages are published to npmjs.");
|
|
23
|
+
console.log("");
|
|
24
|
+
console.log("Local public-package setup:");
|
|
25
|
+
console.log(" npm install --save-dev @topogram/cli");
|
|
26
|
+
console.log("");
|
|
27
|
+
console.log("Private template or generator packages may still require registry-specific npm auth.");
|
|
28
|
+
console.log("Run `topogram doctor` after setup.");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @returns {void}
|
|
33
|
+
*/
|
|
34
|
+
export function printCatalogAuthSetup() {
|
|
35
|
+
console.log("Topogram catalog auth setup");
|
|
36
|
+
console.log("");
|
|
37
|
+
console.log("The default Topogram catalog is public and does not require GitHub auth.");
|
|
38
|
+
console.log("Restricted GitHub catalog reads prefer GITHUB_TOKEN or GH_TOKEN. Local `gh auth login` is a fallback when no token env var is set.");
|
|
39
|
+
console.log("");
|
|
40
|
+
console.log("Restricted catalog local setup:");
|
|
41
|
+
console.log(" export GITHUB_TOKEN=<token-with-repo-read>");
|
|
42
|
+
console.log(" topogram catalog list");
|
|
43
|
+
console.log("");
|
|
44
|
+
console.log("Local fallback without token env:");
|
|
45
|
+
console.log(" gh auth login");
|
|
46
|
+
console.log(" topogram catalog list");
|
|
47
|
+
console.log("");
|
|
48
|
+
console.log("Restricted catalog GitHub Actions setup:");
|
|
49
|
+
console.log(" permissions:");
|
|
50
|
+
console.log(" contents: read");
|
|
51
|
+
console.log(" env:");
|
|
52
|
+
console.log(" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}");
|
|
53
|
+
console.log("");
|
|
54
|
+
console.log("For restricted catalog repositories, grant the workflow token or PAT read access.");
|
|
55
|
+
console.log("Run `topogram catalog doctor` after setup.");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @param {string[]} args
|
|
60
|
+
* @returns {number|null}
|
|
61
|
+
*/
|
|
62
|
+
export function handleSetupCommand(args) {
|
|
63
|
+
if (args[0] !== "setup") {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
if (args[1] === "package-auth") {
|
|
67
|
+
printPackageAuthSetup();
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
70
|
+
if (args[1] === "catalog-auth") {
|
|
71
|
+
printCatalogAuthSetup();
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
printSetupHelp();
|
|
75
|
+
return args[1] ? 1 : 0;
|
|
76
|
+
}
|
|
@@ -0,0 +1,291 @@
|
|
|
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 {
|
|
8
|
+
buildTopogramSourceStatus,
|
|
9
|
+
TOPOGRAM_SOURCE_FILE
|
|
10
|
+
} from "../../catalog.js";
|
|
11
|
+
import { loadProjectConfig } from "../../project-config.js";
|
|
12
|
+
import {
|
|
13
|
+
getTemplateTrustStatus,
|
|
14
|
+
TEMPLATE_TRUST_FILE
|
|
15
|
+
} from "../../template-trust.js";
|
|
16
|
+
import {
|
|
17
|
+
checkTemplatePackageStatus,
|
|
18
|
+
localTemplatePackageStatus
|
|
19
|
+
} from "./package.js";
|
|
20
|
+
import { buildTemplateOwnedBaselineStatus } from "./template.js";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {Record<string, any>} AnyRecord
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @returns {void}
|
|
28
|
+
*/
|
|
29
|
+
export function printSourceHelp() {
|
|
30
|
+
console.log("Usage: topogram source status [path] [--local|--remote] [--json]");
|
|
31
|
+
console.log("");
|
|
32
|
+
console.log("Reports source provenance, template attachment state, and whether local edits affect template-owned files.");
|
|
33
|
+
console.log("");
|
|
34
|
+
console.log("Examples:");
|
|
35
|
+
console.log(" topogram source status");
|
|
36
|
+
console.log(" topogram source status --local");
|
|
37
|
+
console.log(" topogram source status --remote");
|
|
38
|
+
console.log(" topogram source status --json");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {string} inputPath
|
|
43
|
+
* @returns {string}
|
|
44
|
+
*/
|
|
45
|
+
function normalizeTopogramPath(inputPath) {
|
|
46
|
+
const absolute = path.resolve(inputPath);
|
|
47
|
+
if (path.basename(absolute) === "topogram") {
|
|
48
|
+
return absolute;
|
|
49
|
+
}
|
|
50
|
+
const candidate = path.join(absolute, "topogram");
|
|
51
|
+
return fs.existsSync(candidate) ? candidate : absolute;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {string} inputPath
|
|
56
|
+
* @returns {string}
|
|
57
|
+
*/
|
|
58
|
+
export function normalizeProjectRoot(inputPath) {
|
|
59
|
+
const absolute = path.resolve(inputPath);
|
|
60
|
+
if (path.basename(absolute) === "topogram") {
|
|
61
|
+
return path.dirname(absolute);
|
|
62
|
+
}
|
|
63
|
+
return absolute;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param {string} projectRoot
|
|
68
|
+
* @param {{ local?: boolean }} [options]
|
|
69
|
+
* @returns {ReturnType<typeof buildTopogramSourceStatus> & { project: AnyRecord }}
|
|
70
|
+
*/
|
|
71
|
+
export function buildProjectSourceStatus(projectRoot, options = {}) {
|
|
72
|
+
const resolvedRoot = normalizeProjectRoot(projectRoot);
|
|
73
|
+
const sourceStatus = buildTopogramSourceStatus(resolvedRoot);
|
|
74
|
+
const projectConfigInfo = loadProjectConfig(normalizeTopogramPath(resolvedRoot));
|
|
75
|
+
const template = projectConfigInfo?.config?.template || null;
|
|
76
|
+
const baseline = buildTemplateOwnedBaselineStatus(resolvedRoot);
|
|
77
|
+
/** @type {AnyRecord} */
|
|
78
|
+
let trust = {
|
|
79
|
+
requiresTrust: false,
|
|
80
|
+
ok: true,
|
|
81
|
+
status: "not-required",
|
|
82
|
+
path: path.join(resolvedRoot, TEMPLATE_TRUST_FILE),
|
|
83
|
+
template: null,
|
|
84
|
+
implementation: null,
|
|
85
|
+
content: { trustedDigest: null, currentDigest: null, changed: [], added: [], removed: [] },
|
|
86
|
+
issues: []
|
|
87
|
+
};
|
|
88
|
+
if (projectConfigInfo?.config?.implementation) {
|
|
89
|
+
const trustStatus = getTemplateTrustStatus({
|
|
90
|
+
config: projectConfigInfo.config.implementation,
|
|
91
|
+
configPath: projectConfigInfo.configPath,
|
|
92
|
+
configDir: projectConfigInfo.configDir
|
|
93
|
+
}, projectConfigInfo.config);
|
|
94
|
+
trust = {
|
|
95
|
+
requiresTrust: trustStatus.requiresTrust,
|
|
96
|
+
ok: trustStatus.ok,
|
|
97
|
+
status: trustStatus.requiresTrust ? (trustStatus.ok ? "trusted" : "review-required") : "not-required",
|
|
98
|
+
path: trustStatus.trustPath,
|
|
99
|
+
template: trustStatus.template,
|
|
100
|
+
implementation: trustStatus.implementation,
|
|
101
|
+
content: trustStatus.content,
|
|
102
|
+
issues: trustStatus.issues
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const packageStatus = template?.source === "package" && template.sourceSpec
|
|
106
|
+
? (options.local ? localTemplatePackageStatus(template.sourceSpec) : checkTemplatePackageStatus(template.sourceSpec))
|
|
107
|
+
: null;
|
|
108
|
+
const projectDiagnostics = [];
|
|
109
|
+
if (!projectConfigInfo) {
|
|
110
|
+
projectDiagnostics.push({
|
|
111
|
+
code: "project_config_missing",
|
|
112
|
+
severity: "warning",
|
|
113
|
+
message: "topogram.project.json was not found.",
|
|
114
|
+
path: path.join(resolvedRoot, "topogram.project.json"),
|
|
115
|
+
suggestedFix: "Run `topogram check` from a Topogram project root."
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
...sourceStatus,
|
|
120
|
+
project: {
|
|
121
|
+
root: resolvedRoot,
|
|
122
|
+
config: projectConfigInfo
|
|
123
|
+
? { exists: true, path: projectConfigInfo.configPath }
|
|
124
|
+
: { exists: false, path: path.join(resolvedRoot, "topogram.project.json") },
|
|
125
|
+
catalog: template?.catalog || sourceStatus.source?.catalog || null,
|
|
126
|
+
template: template
|
|
127
|
+
? {
|
|
128
|
+
id: template.id || null,
|
|
129
|
+
version: template.version || null,
|
|
130
|
+
requested: template.requested || null,
|
|
131
|
+
source: template.source || null,
|
|
132
|
+
sourceSpec: template.sourceSpec || null,
|
|
133
|
+
includesExecutableImplementation: typeof template.includesExecutableImplementation === "boolean"
|
|
134
|
+
? template.includesExecutableImplementation
|
|
135
|
+
: null
|
|
136
|
+
}
|
|
137
|
+
: null,
|
|
138
|
+
package: packageStatus,
|
|
139
|
+
packageChecks: {
|
|
140
|
+
mode: options.local ? "local" : "remote",
|
|
141
|
+
skipped: Boolean(options.local),
|
|
142
|
+
reason: options.local ? "Package registry checks were skipped because --local was used." : null
|
|
143
|
+
},
|
|
144
|
+
trust,
|
|
145
|
+
templateBaseline: baseline,
|
|
146
|
+
diagnostics: projectDiagnostics
|
|
147
|
+
},
|
|
148
|
+
diagnostics: [...sourceStatus.diagnostics, ...projectDiagnostics]
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* @param {ReturnType<typeof buildProjectSourceStatus>} payload
|
|
154
|
+
* @returns {void}
|
|
155
|
+
*/
|
|
156
|
+
export function printTopogramSourceStatus(payload) {
|
|
157
|
+
if (payload.project?.package && payload.project?.packageChecks?.mode === "remote") {
|
|
158
|
+
console.log("Package checks: remote. Use --local to skip registry access.");
|
|
159
|
+
} else if (payload.project?.package && payload.project?.packageChecks?.mode === "local") {
|
|
160
|
+
console.log("Package checks: local. Registry access skipped.");
|
|
161
|
+
}
|
|
162
|
+
if (!payload.exists) {
|
|
163
|
+
console.log("Topogram source status: no provenance");
|
|
164
|
+
console.log(`Expected: ${payload.path}`);
|
|
165
|
+
console.log(`${TOPOGRAM_SOURCE_FILE} was not found. This workspace may not have been copied from a catalog topogram entry.`);
|
|
166
|
+
} else {
|
|
167
|
+
console.log(`Topogram source status: ${payload.status}`);
|
|
168
|
+
console.log(`File: ${payload.path}`);
|
|
169
|
+
if (payload.source?.catalog?.id) {
|
|
170
|
+
console.log(`Catalog: ${payload.source.catalog.id}${payload.source.catalog.source ? ` from ${payload.source.catalog.source}` : ""}`);
|
|
171
|
+
}
|
|
172
|
+
if (payload.source?.package?.spec) {
|
|
173
|
+
console.log(`Package: ${payload.source.package.spec}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (payload.project?.config?.exists) {
|
|
177
|
+
console.log(`Project config: ${payload.project.config.path}`);
|
|
178
|
+
}
|
|
179
|
+
if (payload.project?.catalog?.id) {
|
|
180
|
+
console.log(`Project catalog: ${payload.project.catalog.id}${payload.project.catalog.source ? ` from ${payload.project.catalog.source}` : ""}`);
|
|
181
|
+
}
|
|
182
|
+
if (payload.project?.template?.id) {
|
|
183
|
+
console.log("Template attachment: attached");
|
|
184
|
+
console.log(`Template: ${payload.project.template.id}@${payload.project.template.version || "unknown"}`);
|
|
185
|
+
console.log(`Template source: ${payload.project.template.sourceSpec || payload.project.template.source || "unknown"}`);
|
|
186
|
+
console.log(`Executable implementation: ${payload.project.template.includesExecutableImplementation ? "yes" : "no"}`);
|
|
187
|
+
} else if (payload.project?.config?.exists) {
|
|
188
|
+
console.log("Template attachment: detached");
|
|
189
|
+
console.log("Template ownership: project-owned");
|
|
190
|
+
}
|
|
191
|
+
if (payload.project?.package?.package) {
|
|
192
|
+
const packageStatus = payload.project.package;
|
|
193
|
+
if (packageStatus.checked === false) {
|
|
194
|
+
console.log(`Template package: ${packageStatus.packageSpec} (not checked, local mode)`);
|
|
195
|
+
} else {
|
|
196
|
+
const currentLabel = packageStatus.current === null ? "unknown" : (packageStatus.current ? "current" : "update available");
|
|
197
|
+
console.log(`Template package: ${packageStatus.packageSpec} (${packageStatus.ok ? "reachable" : "unreachable"}, ${currentLabel})`);
|
|
198
|
+
if (packageStatus.latestVersion) {
|
|
199
|
+
console.log(`Latest template package version: ${packageStatus.latestVersion}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (payload.project?.trust) {
|
|
204
|
+
console.log(`Implementation trust: ${payload.project.trust.status}`);
|
|
205
|
+
if (payload.project.trust.content.trustedDigest) {
|
|
206
|
+
console.log(`Trusted digest: ${payload.project.trust.content.trustedDigest}`);
|
|
207
|
+
}
|
|
208
|
+
if (payload.project.trust.content.currentDigest) {
|
|
209
|
+
console.log(`Current digest: ${payload.project.trust.content.currentDigest}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (payload.project?.templateBaseline) {
|
|
213
|
+
const baseline = payload.project.templateBaseline;
|
|
214
|
+
const blockLabel = baseline.blocksCheck || baseline.blocksGenerate
|
|
215
|
+
? "may block workflow"
|
|
216
|
+
: "does not block check/generate";
|
|
217
|
+
console.log(`Template baseline: ${baseline.state} (${baseline.trustedFiles} file(s), ${blockLabel})`);
|
|
218
|
+
console.log(`Template baseline meaning: ${baseline.meaning}`);
|
|
219
|
+
if (baseline.localOwnership) {
|
|
220
|
+
console.log("Template baseline ownership: local project owns these changes");
|
|
221
|
+
}
|
|
222
|
+
console.log(`Template baseline changed: ${baseline.content.changed.length}`);
|
|
223
|
+
console.log(`Template baseline removed: ${baseline.content.removed.length}`);
|
|
224
|
+
}
|
|
225
|
+
for (const kind of ["changed", "added", "removed"]) {
|
|
226
|
+
const files = payload.content[kind] || [];
|
|
227
|
+
console.log(`${kind[0].toUpperCase()}${kind.slice(1)}: ${files.length}`);
|
|
228
|
+
for (const file of files) {
|
|
229
|
+
console.log(`- ${file}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
for (const diagnostic of payload.diagnostics) {
|
|
233
|
+
const label = diagnostic.severity === "warning" ? "Warning" : "Error";
|
|
234
|
+
console.log(`${label}: ${diagnostic.message}`);
|
|
235
|
+
}
|
|
236
|
+
if (payload.project?.package?.diagnostics?.length) {
|
|
237
|
+
for (const diagnostic of payload.project.package.diagnostics) {
|
|
238
|
+
const label = diagnostic.severity === "warning" ? "Warning" : "Error";
|
|
239
|
+
console.log(`${label}: ${diagnostic.message}`);
|
|
240
|
+
if (diagnostic.suggestedFix) {
|
|
241
|
+
console.log(`Fix: ${diagnostic.suggestedFix}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (payload.project?.trust?.issues?.length) {
|
|
246
|
+
for (const issue of payload.project.trust.issues) {
|
|
247
|
+
console.log(`Issue: ${issue}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
console.log("");
|
|
251
|
+
console.log(`${TOPOGRAM_SOURCE_FILE} records catalog-copy provenance only. Local edits are allowed.`);
|
|
252
|
+
console.log("Template attachment controls update tracking. Detaching makes the project fully owned by this workspace.");
|
|
253
|
+
console.log("Template baseline drift does not block `topogram check` or `topogram generate`.");
|
|
254
|
+
console.log("Implementation trust is separate and can block check/generate when review is required.");
|
|
255
|
+
if (payload.project?.trust?.status === "review-required") {
|
|
256
|
+
console.log("Next: review implementation changes, then run `topogram trust status` or `topogram trust template`.");
|
|
257
|
+
} else if (payload.exists && payload.status === "changed") {
|
|
258
|
+
console.log("Next: review the listed files, then run `topogram check` and `topogram generate` when ready.");
|
|
259
|
+
} else if (payload.project?.templateBaseline?.state === "diverged") {
|
|
260
|
+
console.log("Next: local template-derived changes are owned by this project. Run `topogram template update --check` only when reviewing upstream template changes.");
|
|
261
|
+
} else if (!payload.exists) {
|
|
262
|
+
console.log("Next: use `topogram catalog copy <id> <target>` for pure topogram provenance, or continue with template/project provenance above.");
|
|
263
|
+
} else {
|
|
264
|
+
console.log("Next: run `topogram check` or `topogram generate`.");
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* @param {{
|
|
270
|
+
* commandArgs: Record<string, any>,
|
|
271
|
+
* inputPath: string|null|undefined,
|
|
272
|
+
* args: string[],
|
|
273
|
+
* json: boolean
|
|
274
|
+
* }} context
|
|
275
|
+
* @returns {number}
|
|
276
|
+
*/
|
|
277
|
+
export function runSourceCommand(context) {
|
|
278
|
+
if (context.commandArgs.sourceCommand !== "status") {
|
|
279
|
+
throw new Error(`Unknown source command '${context.commandArgs.sourceCommand}'`);
|
|
280
|
+
}
|
|
281
|
+
const sourceStatusRemote = context.args.includes("--remote");
|
|
282
|
+
const payload = buildProjectSourceStatus(normalizeProjectRoot(context.inputPath || "."), {
|
|
283
|
+
local: context.args.includes("--local") && !sourceStatusRemote
|
|
284
|
+
});
|
|
285
|
+
if (context.json) {
|
|
286
|
+
console.log(stableStringify(payload));
|
|
287
|
+
} else {
|
|
288
|
+
printTopogramSourceStatus(payload);
|
|
289
|
+
}
|
|
290
|
+
return 0;
|
|
291
|
+
}
|
|
@@ -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
|
+
}
|