@topogram/cli 0.3.64 → 0.3.66
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/adoption/plan/index.js +716 -0
- package/src/adoption/plan.js +12 -703
- package/src/adoption/reporting.js +1 -1
- package/src/agent-brief.js +7 -21
- 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 +677 -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/archive/jsonl.js +2 -2
- package/src/archive/resolver-bridge.js +1 -1
- package/src/archive/unarchive.js +2 -1
- package/src/catalog/constants.js +10 -0
- package/src/catalog/copy.js +65 -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 +123 -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/command-parsers/project.js +3 -0
- package/src/cli/command-parsers/shared.js +1 -1
- package/src/cli/commands/agent.js +2 -2
- 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/check.js +3 -3
- package/src/cli/commands/doctor.js +2 -9
- 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 +269 -0
- package/src/cli/commands/import/plan.js +292 -0
- package/src/cli/commands/import/refresh.js +471 -0
- package/src/cli/commands/import/status-history.js +196 -0
- package/src/cli/commands/import/workspace.js +233 -0
- package/src/cli/commands/import.js +33 -1732
- package/src/cli/commands/migrate.js +153 -0
- 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 +270 -0
- package/src/cli/commands/query.js +9 -1300
- package/src/cli/commands/source.js +3 -12
- package/src/cli/commands/template/baseline.js +100 -0
- package/src/cli/commands/template/check.js +467 -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-runner.js +6 -6
- package/src/cli/commands/template.js +41 -2143
- package/src/cli/commands/trust.js +1 -1
- package/src/cli/commands/workflow.js +6 -1
- package/src/cli/dispatcher.js +6 -1
- package/src/cli/help.js +15 -14
- package/src/cli/migration-guidance.js +1 -1
- package/src/cli/output-safety.js +2 -1
- package/src/cli/path-normalization.js +3 -13
- 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/domain-page.js +1 -1
- 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/context/task-mode.js +2 -2
- 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/sdlc/doc-page.js +1 -1
- package/src/generator/shared.d.ts +2 -0
- package/src/generator/surfaces/databases/lifecycle-shared.js +1 -1
- package/src/generator/surfaces/native/swiftui-templates/README.generated.md +1 -1
- 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/context.js +5 -7
- 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 +337 -0
- package/src/import/core/runner/options.js +22 -0
- package/src/import/core/runner/reports.js +51 -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 +393 -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 +90 -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 +351 -0
- package/src/new-project/template-policy.js +269 -0
- package/src/new-project/template-resolution.js +370 -0
- package/src/new-project/template-snapshots.js +442 -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 +591 -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/sdlc/adopt.js +6 -5
- package/src/sdlc/paths.js +3 -5
- package/src/sdlc/scaffold.js +2 -1
- 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 +212 -0
- package/src/workflows/reconcile/adoption-plan/dependencies.js +75 -0
- package/src/workflows/reconcile/adoption-plan/outputs.js +153 -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/candidate-model.js +18 -2
- package/src/workflows/reconcile/canonical-surface.js +1 -1
- package/src/workflows/reconcile/impacts/adoption-plan.js +196 -0
- package/src/workflows/reconcile/impacts/indexes.js +105 -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/workflows/reconcile/renderers.js +41 -6
- package/src/workflows/shared.js +5 -11
- package/src/workspace-docs.d.ts +29 -0
- package/src/workspace-paths.js +328 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
import fs from "node:fs";
|
|
4
3
|
import path from "node:path";
|
|
5
4
|
|
|
6
5
|
import { stableStringify } from "../../format.js";
|
|
@@ -18,6 +17,7 @@ import {
|
|
|
18
17
|
localTemplatePackageStatus
|
|
19
18
|
} from "./package.js";
|
|
20
19
|
import { buildTemplateOwnedBaselineStatus } from "./template.js";
|
|
20
|
+
import { resolveTopoRoot, resolveWorkspaceContext } from "../../workspace-paths.js";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* @typedef {Record<string, any>} AnyRecord
|
|
@@ -43,12 +43,7 @@ export function printSourceHelp() {
|
|
|
43
43
|
* @returns {string}
|
|
44
44
|
*/
|
|
45
45
|
function normalizeTopogramPath(inputPath) {
|
|
46
|
-
|
|
47
|
-
if (path.basename(absolute) === "topogram") {
|
|
48
|
-
return absolute;
|
|
49
|
-
}
|
|
50
|
-
const candidate = path.join(absolute, "topogram");
|
|
51
|
-
return fs.existsSync(candidate) ? candidate : absolute;
|
|
46
|
+
return resolveTopoRoot(inputPath);
|
|
52
47
|
}
|
|
53
48
|
|
|
54
49
|
/**
|
|
@@ -56,11 +51,7 @@ function normalizeTopogramPath(inputPath) {
|
|
|
56
51
|
* @returns {string}
|
|
57
52
|
*/
|
|
58
53
|
export function normalizeProjectRoot(inputPath) {
|
|
59
|
-
|
|
60
|
-
if (path.basename(absolute) === "topogram") {
|
|
61
|
-
return path.dirname(absolute);
|
|
62
|
-
}
|
|
63
|
-
return absolute;
|
|
54
|
+
return resolveWorkspaceContext(inputPath).projectRoot;
|
|
64
55
|
}
|
|
65
56
|
|
|
66
57
|
/**
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import crypto from "node:crypto";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
|
|
7
|
+
import { stableStringify } from "../../../format.js";
|
|
8
|
+
import { TEMPLATE_FILES_MANIFEST } from "./constants.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {string} filePath
|
|
12
|
+
* @returns {{ sha256: string, size: number }}
|
|
13
|
+
*/
|
|
14
|
+
function projectFileHash(filePath) {
|
|
15
|
+
const bytes = fs.readFileSync(filePath);
|
|
16
|
+
return {
|
|
17
|
+
sha256: crypto.createHash("sha256").update(bytes).digest("hex"),
|
|
18
|
+
size: bytes.length
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} projectRoot
|
|
24
|
+
* @param {string} relativePath
|
|
25
|
+
* @returns {{ sha256: string, size: number }}
|
|
26
|
+
*/
|
|
27
|
+
function templateBaselineFileHash(projectRoot, relativePath) {
|
|
28
|
+
const filePath = path.join(projectRoot, relativePath);
|
|
29
|
+
if (relativePath === "topogram.project.json") {
|
|
30
|
+
const content = `${stableStringify(JSON.parse(fs.readFileSync(filePath, "utf8")))}\n`;
|
|
31
|
+
return {
|
|
32
|
+
sha256: crypto.createHash("sha256").update(content).digest("hex"),
|
|
33
|
+
size: Buffer.byteLength(content)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return projectFileHash(filePath);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {string} projectRoot
|
|
41
|
+
* @returns {{ exists: boolean, path: string, status: "missing"|"clean"|"changed", state: "missing"|"matches-template"|"diverged", meaning: "no-template-baseline"|"matches-template-baseline"|"local-project-owns-changes", changedAllowed: boolean, localOwnership: boolean, blocksCheck: boolean, blocksGenerate: boolean, nextCommand: string|null, content: { changed: string[], added: string[], removed: string[] }, trustedFiles: number }}
|
|
42
|
+
*/
|
|
43
|
+
export function buildTemplateOwnedBaselineStatus(projectRoot) {
|
|
44
|
+
const manifestPath = path.join(projectRoot, TEMPLATE_FILES_MANIFEST);
|
|
45
|
+
if (!fs.existsSync(manifestPath)) {
|
|
46
|
+
return {
|
|
47
|
+
exists: false,
|
|
48
|
+
path: manifestPath,
|
|
49
|
+
status: "missing",
|
|
50
|
+
state: "missing",
|
|
51
|
+
meaning: "no-template-baseline",
|
|
52
|
+
changedAllowed: true,
|
|
53
|
+
localOwnership: false,
|
|
54
|
+
blocksCheck: false,
|
|
55
|
+
blocksGenerate: false,
|
|
56
|
+
nextCommand: null,
|
|
57
|
+
content: { changed: [], added: [], removed: [] },
|
|
58
|
+
trustedFiles: 0
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
62
|
+
const trustedFiles = Array.isArray(manifest.files) ? manifest.files : [];
|
|
63
|
+
const changed = [];
|
|
64
|
+
const removed = [];
|
|
65
|
+
for (const file of trustedFiles) {
|
|
66
|
+
const relativePath = String(file.path || "");
|
|
67
|
+
if (!relativePath) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const absolutePath = path.join(projectRoot, relativePath);
|
|
71
|
+
if (!fs.existsSync(absolutePath)) {
|
|
72
|
+
removed.push(relativePath);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const current = templateBaselineFileHash(projectRoot, relativePath);
|
|
76
|
+
if (current.sha256 !== file.sha256 || current.size !== file.size) {
|
|
77
|
+
changed.push(relativePath);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const status = changed.length || removed.length ? "changed" : "clean";
|
|
81
|
+
const diverged = status === "changed";
|
|
82
|
+
return {
|
|
83
|
+
exists: true,
|
|
84
|
+
path: manifestPath,
|
|
85
|
+
status,
|
|
86
|
+
state: diverged ? "diverged" : "matches-template",
|
|
87
|
+
meaning: diverged ? "local-project-owns-changes" : "matches-template-baseline",
|
|
88
|
+
changedAllowed: true,
|
|
89
|
+
localOwnership: diverged,
|
|
90
|
+
blocksCheck: false,
|
|
91
|
+
blocksGenerate: false,
|
|
92
|
+
nextCommand: diverged ? "topogram template update --check" : null,
|
|
93
|
+
content: {
|
|
94
|
+
changed: changed.sort((a, b) => a.localeCompare(b)),
|
|
95
|
+
added: [],
|
|
96
|
+
removed: removed.sort((a, b) => a.localeCompare(b))
|
|
97
|
+
},
|
|
98
|
+
trustedFiles: trustedFiles.length
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
|
|
7
|
+
import { parsePath } from "../../../parser.js";
|
|
8
|
+
import { resolveWorkspace } from "../../../resolver.js";
|
|
9
|
+
import {
|
|
10
|
+
loadProjectConfig,
|
|
11
|
+
validateProjectConfig,
|
|
12
|
+
validateProjectOutputOwnership
|
|
13
|
+
} from "../../../project-config.js";
|
|
14
|
+
import { resolveTopoRoot } from "../../../workspace-paths.js";
|
|
15
|
+
import {
|
|
16
|
+
buildTemplateUpdatePlan,
|
|
17
|
+
createNewProject,
|
|
18
|
+
loadTemplatePolicy,
|
|
19
|
+
resolveTemplate,
|
|
20
|
+
templatePolicyDiagnosticsForTemplate
|
|
21
|
+
} from "../../../new-project.js";
|
|
22
|
+
import {
|
|
23
|
+
getTemplateTrustStatus,
|
|
24
|
+
implementationRequiresTrust,
|
|
25
|
+
TEMPLATE_TRUST_FILE,
|
|
26
|
+
templateTrustRecoveryGuidance,
|
|
27
|
+
validateProjectImplementationTrust
|
|
28
|
+
} from "../../../template-trust.js";
|
|
29
|
+
import { runNpmForPackageUpdate } from "../package.js";
|
|
30
|
+
import { ENGINE_ROOT, TEMPLATES_ROOT } from "./constants.js";
|
|
31
|
+
import { combineProjectValidationResults, messageFromError } from "./shared.js";
|
|
32
|
+
import { templateCheckDiagnostic } from "./diagnostics.js";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {Object} TemplateCheckDiagnostic
|
|
36
|
+
* @property {string} code
|
|
37
|
+
* @property {"error"|"warning"} severity
|
|
38
|
+
* @property {string} message
|
|
39
|
+
* @property {string|null} path
|
|
40
|
+
* @property {string|null} suggestedFix
|
|
41
|
+
* @property {string|null} step
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {string} templateSpec
|
|
46
|
+
* @param {string} relativePath
|
|
47
|
+
* @returns {string|null}
|
|
48
|
+
*/
|
|
49
|
+
function localTemplatePath(templateSpec, relativePath) {
|
|
50
|
+
if (
|
|
51
|
+
templateSpec === "." ||
|
|
52
|
+
templateSpec.startsWith("./") ||
|
|
53
|
+
templateSpec.startsWith("../") ||
|
|
54
|
+
path.isAbsolute(templateSpec)
|
|
55
|
+
) {
|
|
56
|
+
return path.join(path.resolve(templateSpec), relativePath);
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @param {string} message
|
|
63
|
+
* @param {string} templateSpec
|
|
64
|
+
* @param {string} step
|
|
65
|
+
* @returns {TemplateCheckDiagnostic}
|
|
66
|
+
*/
|
|
67
|
+
function diagnosticForTemplateCreateFailure(message, templateSpec, step) {
|
|
68
|
+
if (message.includes("is missing topogram-template.json")) {
|
|
69
|
+
return templateCheckDiagnostic({
|
|
70
|
+
code: "template_manifest_missing",
|
|
71
|
+
message,
|
|
72
|
+
path: localTemplatePath(templateSpec, "topogram-template.json"),
|
|
73
|
+
suggestedFix: "Add topogram-template.json with id, version, kind, and topogramVersion.",
|
|
74
|
+
step
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (message.includes("contains implementation/") && message.includes("includesExecutableImplementation: true")) {
|
|
78
|
+
return templateCheckDiagnostic({
|
|
79
|
+
code: "template_implementation_undeclared",
|
|
80
|
+
message,
|
|
81
|
+
path: localTemplatePath(templateSpec, "topogram-template.json"),
|
|
82
|
+
suggestedFix: "Set includesExecutableImplementation to true after reviewing implementation/, or remove implementation/.",
|
|
83
|
+
step
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (message.includes("is missing required string field") || message.includes("topogram-template.json")) {
|
|
87
|
+
return templateCheckDiagnostic({
|
|
88
|
+
code: "template_manifest_invalid",
|
|
89
|
+
message,
|
|
90
|
+
path: localTemplatePath(templateSpec, "topogram-template.json"),
|
|
91
|
+
suggestedFix: "Fix topogram-template.json so it matches the template manifest schema.",
|
|
92
|
+
step
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (message.includes("is missing topo/") || message.includes("is missing topogram/")) {
|
|
96
|
+
return templateCheckDiagnostic({
|
|
97
|
+
code: "template_topogram_missing",
|
|
98
|
+
message,
|
|
99
|
+
path: localTemplatePath(templateSpec, "topo"),
|
|
100
|
+
suggestedFix: "Add a topo/ directory with the reusable Topogram source files.",
|
|
101
|
+
step
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
if (message.includes("is missing topogram.project.json")) {
|
|
105
|
+
return templateCheckDiagnostic({
|
|
106
|
+
code: "template_project_config_missing",
|
|
107
|
+
message,
|
|
108
|
+
path: localTemplatePath(templateSpec, "topogram.project.json"),
|
|
109
|
+
suggestedFix: "Add topogram.project.json beside topo/ with outputs and topology.runtimes.",
|
|
110
|
+
step
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
if (message.includes("is missing implementation/")) {
|
|
114
|
+
return templateCheckDiagnostic({
|
|
115
|
+
code: "template_implementation_missing",
|
|
116
|
+
message,
|
|
117
|
+
path: localTemplatePath(templateSpec, "implementation"),
|
|
118
|
+
suggestedFix: "Add implementation/ or set includesExecutableImplementation to false.",
|
|
119
|
+
step
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
if (message.includes("unsupported symlink")) {
|
|
123
|
+
return templateCheckDiagnostic({
|
|
124
|
+
code: "template_symlink_unsupported",
|
|
125
|
+
message,
|
|
126
|
+
path: path.isAbsolute(templateSpec) ? templateSpec : null,
|
|
127
|
+
suggestedFix: "Replace template symlinks with real files or directories, then rerun `topogram new` or `topogram template check`.",
|
|
128
|
+
step
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return templateCheckDiagnostic({
|
|
132
|
+
code: "template_create_failed",
|
|
133
|
+
message,
|
|
134
|
+
path: path.isAbsolute(templateSpec) ? templateSpec : null,
|
|
135
|
+
suggestedFix: "Fix the template pack so topogram new can create a starter from it.",
|
|
136
|
+
step
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @param {{ message: string, loc?: any }} error
|
|
142
|
+
* @param {string} step
|
|
143
|
+
* @param {string|null} configPath
|
|
144
|
+
* @returns {TemplateCheckDiagnostic}
|
|
145
|
+
*/
|
|
146
|
+
function diagnosticForStarterCheckFailure(error, step, configPath) {
|
|
147
|
+
const locFile = typeof error?.loc?.file === "string" ? error.loc.file : null;
|
|
148
|
+
const isTrust = error.message.includes(TEMPLATE_TRUST_FILE) ||
|
|
149
|
+
error.message.includes("unsupported symlink") ||
|
|
150
|
+
error.message.includes("must be under implementation/");
|
|
151
|
+
return templateCheckDiagnostic({
|
|
152
|
+
code: isTrust ? "template_trust_invalid" : "starter_check_failed",
|
|
153
|
+
message: error.message,
|
|
154
|
+
path: locFile || configPath,
|
|
155
|
+
suggestedFix: isTrust
|
|
156
|
+
? templateTrustRecoveryGuidance(error.message)
|
|
157
|
+
: "Fix the generated Topogram source or topogram.project.json so topogram check passes.",
|
|
158
|
+
step
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* @param {string} name
|
|
164
|
+
* @param {boolean} ok
|
|
165
|
+
* @param {Record<string, any>} [details]
|
|
166
|
+
* @param {TemplateCheckDiagnostic[]} [diagnostics]
|
|
167
|
+
* @returns {{ name: string, ok: boolean, details: Record<string, any>, diagnostics: TemplateCheckDiagnostic[] }}
|
|
168
|
+
*/
|
|
169
|
+
function templateCheckStep(name, ok, details = {}, diagnostics = []) {
|
|
170
|
+
return { name, ok, details, diagnostics };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @param {string} projectRoot
|
|
175
|
+
* @returns {string[]}
|
|
176
|
+
*/
|
|
177
|
+
function templateCheckGeneratorDependencies(projectRoot) {
|
|
178
|
+
const packagePath = path.join(projectRoot, "package.json");
|
|
179
|
+
if (!fs.existsSync(packagePath)) {
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
const pkg = JSON.parse(fs.readFileSync(packagePath, "utf8"));
|
|
183
|
+
const dependencies = {
|
|
184
|
+
...(pkg.dependencies || {}),
|
|
185
|
+
...(pkg.devDependencies || {})
|
|
186
|
+
};
|
|
187
|
+
return Object.keys(dependencies).filter((name) =>
|
|
188
|
+
name.includes("topogram-generator") || name.startsWith("@topogram/generator-")
|
|
189
|
+
).sort();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* @param {string} projectRoot
|
|
194
|
+
* @param {string[]} dependencies
|
|
195
|
+
* @returns {TemplateCheckDiagnostic|null}
|
|
196
|
+
*/
|
|
197
|
+
function installTemplateCheckGeneratorDependencies(projectRoot, dependencies) {
|
|
198
|
+
if (dependencies.length === 0) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
const result = runNpmForPackageUpdate(["install", "--ignore-scripts"], projectRoot);
|
|
202
|
+
if (result.status === 0) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
const output = `${result.stdout || ""}\n${result.stderr || ""}`.trim();
|
|
206
|
+
return templateCheckDiagnostic({
|
|
207
|
+
code: "template_generator_dependencies_install_failed",
|
|
208
|
+
message: `Failed to install package-backed generator dependencies: ${dependencies.join(", ")}.`,
|
|
209
|
+
path: path.join(projectRoot, "package.json"),
|
|
210
|
+
suggestedFix: `Run npm install before checking this package-backed generator template.${output ? ` ${output.split(/\r?\n/).slice(-3).join(" ")}` : ""}`,
|
|
211
|
+
step: "generator-dependencies"
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* @param {string} templateSpec
|
|
217
|
+
* @returns {{ ok: boolean, templateSpec: string, projectRoot: string|null, steps: Array<{ name: string, ok: boolean, details: Record<string, any>, diagnostics: TemplateCheckDiagnostic[] }>, diagnostics: TemplateCheckDiagnostic[], errors: string[] }}
|
|
218
|
+
*/
|
|
219
|
+
export function buildTemplateCheckPayload(templateSpec) {
|
|
220
|
+
if (!templateSpec) {
|
|
221
|
+
throw new Error("topogram template check requires <template-spec-or-path>.");
|
|
222
|
+
}
|
|
223
|
+
const runRoot = fs.mkdtempSync(path.join(os.tmpdir(), "topogram-template-check-"));
|
|
224
|
+
const projectRoot = path.join(runRoot, "starter");
|
|
225
|
+
/** @type {Array<{ name: string, ok: boolean, details: Record<string, any>, diagnostics: TemplateCheckDiagnostic[] }>} */
|
|
226
|
+
const steps = [];
|
|
227
|
+
/** @type {TemplateCheckDiagnostic[]} */
|
|
228
|
+
const diagnostics = [];
|
|
229
|
+
try {
|
|
230
|
+
const callerPolicyInfo = loadTemplatePolicy(process.cwd());
|
|
231
|
+
if (callerPolicyInfo.exists) {
|
|
232
|
+
const resolvedTemplate = resolveTemplate(templateSpec, TEMPLATES_ROOT);
|
|
233
|
+
const policyDiagnostics = templatePolicyDiagnosticsForTemplate(callerPolicyInfo, resolvedTemplate, "template-check-policy");
|
|
234
|
+
if (policyDiagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
235
|
+
const stepDiagnostics = policyDiagnostics.map((diagnostic) => templateCheckDiagnostic(diagnostic));
|
|
236
|
+
diagnostics.push(...stepDiagnostics);
|
|
237
|
+
steps.push(templateCheckStep("template-policy", false, {
|
|
238
|
+
path: callerPolicyInfo.path
|
|
239
|
+
}, stepDiagnostics));
|
|
240
|
+
return {
|
|
241
|
+
ok: false,
|
|
242
|
+
templateSpec,
|
|
243
|
+
projectRoot: null,
|
|
244
|
+
steps,
|
|
245
|
+
diagnostics,
|
|
246
|
+
errors: diagnostics.map((diagnostic) => diagnostic.message)
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
const created = createNewProject({
|
|
251
|
+
targetPath: projectRoot,
|
|
252
|
+
templateName: templateSpec,
|
|
253
|
+
engineRoot: ENGINE_ROOT,
|
|
254
|
+
templatesRoot: TEMPLATES_ROOT
|
|
255
|
+
});
|
|
256
|
+
steps.push(templateCheckStep("create-starter", true, {
|
|
257
|
+
template: created.templateName,
|
|
258
|
+
warnings: created.warnings.length
|
|
259
|
+
}));
|
|
260
|
+
const generatorDependencies = templateCheckGeneratorDependencies(projectRoot);
|
|
261
|
+
const installDiagnostic = installTemplateCheckGeneratorDependencies(projectRoot, generatorDependencies);
|
|
262
|
+
if (installDiagnostic) {
|
|
263
|
+
diagnostics.push(installDiagnostic);
|
|
264
|
+
steps.push(templateCheckStep("generator-dependencies", false, {
|
|
265
|
+
dependencies: generatorDependencies
|
|
266
|
+
}, [installDiagnostic]));
|
|
267
|
+
return {
|
|
268
|
+
ok: false,
|
|
269
|
+
templateSpec,
|
|
270
|
+
projectRoot,
|
|
271
|
+
steps,
|
|
272
|
+
diagnostics,
|
|
273
|
+
errors: diagnostics.map((diagnostic) => diagnostic.message)
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
if (generatorDependencies.length > 0) {
|
|
277
|
+
steps.push(templateCheckStep("generator-dependencies", true, {
|
|
278
|
+
dependencies: generatorDependencies
|
|
279
|
+
}));
|
|
280
|
+
}
|
|
281
|
+
} catch (error) {
|
|
282
|
+
const stepDiagnostics = [
|
|
283
|
+
diagnosticForTemplateCreateFailure(messageFromError(error), templateSpec, "create-starter")
|
|
284
|
+
];
|
|
285
|
+
diagnostics.push(...stepDiagnostics);
|
|
286
|
+
steps.push(templateCheckStep("create-starter", false, {}, stepDiagnostics));
|
|
287
|
+
return {
|
|
288
|
+
ok: false,
|
|
289
|
+
templateSpec,
|
|
290
|
+
projectRoot: null,
|
|
291
|
+
steps,
|
|
292
|
+
diagnostics,
|
|
293
|
+
errors: diagnostics.map((diagnostic) => diagnostic.message)
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const projectConfigInfo = loadProjectConfig(projectRoot);
|
|
298
|
+
if (!projectConfigInfo) {
|
|
299
|
+
const stepDiagnostics = [
|
|
300
|
+
templateCheckDiagnostic({
|
|
301
|
+
code: "starter_project_config_missing",
|
|
302
|
+
message: "Generated starter is missing topogram.project.json.",
|
|
303
|
+
path: path.join(projectRoot, "topogram.project.json"),
|
|
304
|
+
suggestedFix: "Ensure the template includes topogram.project.json at its root.",
|
|
305
|
+
step: "project-config"
|
|
306
|
+
})
|
|
307
|
+
];
|
|
308
|
+
diagnostics.push(...stepDiagnostics);
|
|
309
|
+
steps.push(templateCheckStep("project-config", false, {}, stepDiagnostics));
|
|
310
|
+
return {
|
|
311
|
+
ok: false,
|
|
312
|
+
templateSpec,
|
|
313
|
+
projectRoot,
|
|
314
|
+
steps,
|
|
315
|
+
diagnostics,
|
|
316
|
+
errors: diagnostics.map((diagnostic) => diagnostic.message)
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
steps.push(templateCheckStep("project-config", true, {
|
|
320
|
+
path: projectConfigInfo.configPath,
|
|
321
|
+
template: projectConfigInfo.config.template?.id || null
|
|
322
|
+
}));
|
|
323
|
+
|
|
324
|
+
const ast = parsePath(resolveTopoRoot(projectRoot));
|
|
325
|
+
const resolved = resolveWorkspace(ast);
|
|
326
|
+
const projectValidation = combineProjectValidationResults(
|
|
327
|
+
validateProjectConfig(projectConfigInfo.config, resolved.ok ? resolved.graph : null, { configDir: projectConfigInfo.configDir }),
|
|
328
|
+
validateProjectOutputOwnership(projectConfigInfo),
|
|
329
|
+
validateProjectImplementationTrust(projectConfigInfo)
|
|
330
|
+
);
|
|
331
|
+
const starterCheckOk = resolved.ok && projectValidation.ok;
|
|
332
|
+
const starterDiagnostics = [
|
|
333
|
+
...(resolved.ok ? [] : resolved.validation.errors),
|
|
334
|
+
...projectValidation.errors
|
|
335
|
+
].map((error) => diagnosticForStarterCheckFailure(error, "starter-check", projectConfigInfo.configPath));
|
|
336
|
+
steps.push(templateCheckStep("starter-check", starterCheckOk, {
|
|
337
|
+
files: ast.files.length,
|
|
338
|
+
statements: ast.files.flatMap((/** @type {{ statements: any[] }} */ file) => file.statements).length
|
|
339
|
+
}, starterDiagnostics));
|
|
340
|
+
if (!starterCheckOk) {
|
|
341
|
+
diagnostics.push(...starterDiagnostics);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const implementationInfo = projectConfigInfo.config.implementation
|
|
345
|
+
? {
|
|
346
|
+
config: projectConfigInfo.config.implementation,
|
|
347
|
+
configPath: projectConfigInfo.configPath,
|
|
348
|
+
configDir: projectConfigInfo.configDir
|
|
349
|
+
}
|
|
350
|
+
: null;
|
|
351
|
+
if (implementationInfo && implementationRequiresTrust(implementationInfo, projectConfigInfo.config)) {
|
|
352
|
+
const trustStatus = getTemplateTrustStatus(implementationInfo, projectConfigInfo.config);
|
|
353
|
+
const trustDiagnostics = trustStatus.issues.map((issue) => templateCheckDiagnostic({
|
|
354
|
+
code: "template_trust_invalid",
|
|
355
|
+
message: issue,
|
|
356
|
+
path: trustStatus.trustPath,
|
|
357
|
+
suggestedFix: templateTrustRecoveryGuidance(issue),
|
|
358
|
+
step: "executable-implementation-trust"
|
|
359
|
+
}));
|
|
360
|
+
steps.push(templateCheckStep("executable-implementation-trust", trustStatus.ok, {
|
|
361
|
+
requiresTrust: true,
|
|
362
|
+
trustPath: trustStatus.trustPath,
|
|
363
|
+
trustedFiles: trustStatus.trustRecord?.content?.files?.length || 0
|
|
364
|
+
}, trustDiagnostics));
|
|
365
|
+
if (!trustStatus.ok) {
|
|
366
|
+
diagnostics.push(...trustDiagnostics);
|
|
367
|
+
}
|
|
368
|
+
} else {
|
|
369
|
+
steps.push(templateCheckStep("executable-implementation-trust", true, {
|
|
370
|
+
requiresTrust: false
|
|
371
|
+
}));
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
try {
|
|
375
|
+
const updatePlan = buildTemplateUpdatePlan({
|
|
376
|
+
projectRoot,
|
|
377
|
+
projectConfig: projectConfigInfo.config,
|
|
378
|
+
templateName: null,
|
|
379
|
+
templatesRoot: TEMPLATES_ROOT
|
|
380
|
+
});
|
|
381
|
+
steps.push(templateCheckStep("template-update-plan", updatePlan.ok, {
|
|
382
|
+
writes: updatePlan.writes,
|
|
383
|
+
added: updatePlan.summary.added,
|
|
384
|
+
changed: updatePlan.summary.changed,
|
|
385
|
+
currentOnly: updatePlan.summary.currentOnly
|
|
386
|
+
}));
|
|
387
|
+
if (!updatePlan.ok) {
|
|
388
|
+
const stepDiagnostics = updatePlan.issues.map((issue) => templateCheckDiagnostic({
|
|
389
|
+
code: "template_update_plan_failed",
|
|
390
|
+
message: issue,
|
|
391
|
+
path: projectConfigInfo.configPath,
|
|
392
|
+
suggestedFix: "Fix template metadata so a no-write update plan can be produced.",
|
|
393
|
+
step: "template-update-plan"
|
|
394
|
+
}));
|
|
395
|
+
steps[steps.length - 1].diagnostics.push(...stepDiagnostics);
|
|
396
|
+
diagnostics.push(...stepDiagnostics);
|
|
397
|
+
}
|
|
398
|
+
} catch (error) {
|
|
399
|
+
const stepDiagnostics = [
|
|
400
|
+
templateCheckDiagnostic({
|
|
401
|
+
code: "template_update_plan_failed",
|
|
402
|
+
message: messageFromError(error),
|
|
403
|
+
path: projectConfigInfo.configPath,
|
|
404
|
+
suggestedFix: "Fix template metadata so a no-write update plan can be produced.",
|
|
405
|
+
step: "template-update-plan"
|
|
406
|
+
})
|
|
407
|
+
];
|
|
408
|
+
diagnostics.push(...stepDiagnostics);
|
|
409
|
+
steps.push(templateCheckStep("template-update-plan", false, {}, stepDiagnostics));
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return {
|
|
413
|
+
ok: steps.every((step) => step.ok),
|
|
414
|
+
templateSpec,
|
|
415
|
+
projectRoot,
|
|
416
|
+
steps,
|
|
417
|
+
diagnostics,
|
|
418
|
+
errors: diagnostics.map((diagnostic) => diagnostic.message)
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* @param {Record<string, any>} details
|
|
424
|
+
* @returns {string[]}
|
|
425
|
+
*/
|
|
426
|
+
function formatTemplateCheckDetails(details) {
|
|
427
|
+
return Object.entries(details)
|
|
428
|
+
.filter(([, value]) => value !== undefined && value !== null)
|
|
429
|
+
.map(([key, value]) => ` ${key}: ${typeof value === "object" ? JSON.stringify(value) : String(value)}`);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* @param {ReturnType<typeof buildTemplateCheckPayload>} payload
|
|
434
|
+
* @returns {void}
|
|
435
|
+
*/
|
|
436
|
+
export function printTemplateCheckPayload(payload) {
|
|
437
|
+
console.log(payload.ok ? "Template check passed" : "Template check failed");
|
|
438
|
+
console.log(`Template spec: ${payload.templateSpec}`);
|
|
439
|
+
if (payload.projectRoot) {
|
|
440
|
+
console.log(`Temp starter: ${payload.projectRoot}`);
|
|
441
|
+
}
|
|
442
|
+
for (const step of payload.steps) {
|
|
443
|
+
console.log(`${step.ok ? "PASS" : "FAIL"} ${step.name}`);
|
|
444
|
+
for (const detail of formatTemplateCheckDetails(step.details)) {
|
|
445
|
+
console.log(detail);
|
|
446
|
+
}
|
|
447
|
+
for (const diagnostic of step.diagnostics) {
|
|
448
|
+
console.log(` [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`);
|
|
449
|
+
if (diagnostic.path) {
|
|
450
|
+
console.log(` path: ${diagnostic.path}`);
|
|
451
|
+
}
|
|
452
|
+
if (diagnostic.suggestedFix) {
|
|
453
|
+
console.log(` fix: ${diagnostic.suggestedFix}`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
const stepDiagnostics = new Set(payload.steps.flatMap((step) => step.diagnostics));
|
|
458
|
+
for (const diagnostic of payload.diagnostics.filter((item) => !stepDiagnostics.has(item))) {
|
|
459
|
+
console.log(`[${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`);
|
|
460
|
+
if (diagnostic.path) {
|
|
461
|
+
console.log(` path: ${diagnostic.path}`);
|
|
462
|
+
}
|
|
463
|
+
if (diagnostic.suggestedFix) {
|
|
464
|
+
console.log(` fix: ${diagnostic.suggestedFix}`);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
export const TEMPLATE_FILES_MANIFEST = ".topogram-template-files.json";
|
|
6
|
+
export const TEMPLATE_POLICY_FILE = "topogram.template-policy.json";
|
|
7
|
+
export const ENGINE_ROOT = decodeURIComponent(new URL("../../../../", import.meta.url).pathname);
|
|
8
|
+
export const TEMPLATES_ROOT = path.join(ENGINE_ROOT, "templates");
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} TemplateCheckDiagnostic
|
|
5
|
+
* @property {string} code
|
|
6
|
+
* @property {"error"|"warning"} severity
|
|
7
|
+
* @property {string} message
|
|
8
|
+
* @property {string|null} path
|
|
9
|
+
* @property {string|null} suggestedFix
|
|
10
|
+
* @property {string|null} step
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {Record<string, any>} input
|
|
15
|
+
* @returns {TemplateCheckDiagnostic}
|
|
16
|
+
*/
|
|
17
|
+
export function templateCheckDiagnostic(input) {
|
|
18
|
+
return {
|
|
19
|
+
code: String(input.code || "template_check_failed"),
|
|
20
|
+
severity: input.severity === "warning" ? "warning" : "error",
|
|
21
|
+
message: String(input.message || "Template check failed."),
|
|
22
|
+
path: typeof input.path === "string" ? input.path : null,
|
|
23
|
+
suggestedFix: typeof input.suggestedFix === "string" ? input.suggestedFix : null,
|
|
24
|
+
step: typeof input.step === "string" ? input.step : null
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @returns {void}
|
|
5
|
+
*/
|
|
6
|
+
export function printTemplateHelp() {
|
|
7
|
+
console.log("Usage: topogram template list [--json] [--catalog <path-or-source>]");
|
|
8
|
+
console.log(" or: topogram template explain [path] [--json]");
|
|
9
|
+
console.log(" or: topogram template status [path] [--latest] [--json]");
|
|
10
|
+
console.log(" or: topogram template detach [path] [--dry-run] [--remove-policy] [--json]");
|
|
11
|
+
console.log(" or: topogram template check <template-spec-or-path> [--json]");
|
|
12
|
+
console.log(" or: topogram template policy init [path] [--json]");
|
|
13
|
+
console.log(" or: topogram template policy check [path] [--json]");
|
|
14
|
+
console.log(" or: topogram template policy explain [path] [--json]");
|
|
15
|
+
console.log(" or: topogram template policy pin <template-id@version> [path] [--json]");
|
|
16
|
+
console.log(" or: topogram template update [path] --status|--recommend|--plan|--check|--apply [--template <spec>|--latest] [--json] [--out <path>]");
|
|
17
|
+
console.log("");
|
|
18
|
+
console.log("Template commands inspect catalog-backed starters, project provenance, trust policy, and update plans.");
|
|
19
|
+
console.log("");
|
|
20
|
+
console.log("Examples:");
|
|
21
|
+
console.log(" topogram template list");
|
|
22
|
+
console.log(" topogram template explain");
|
|
23
|
+
console.log(" topogram template status");
|
|
24
|
+
console.log(" topogram template status --latest");
|
|
25
|
+
console.log(" topogram template policy check");
|
|
26
|
+
console.log(" topogram template check ./local-template");
|
|
27
|
+
console.log(" topogram template update --recommend");
|
|
28
|
+
}
|