@topogram/cli 0.3.65 → 0.3.67
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 +21 -8
- package/src/adoption/reporting.js +1 -1
- package/src/agent-brief.js +7 -21
- package/src/agent-ops/query-builders/change-risk/review-packets.js +2 -2
- package/src/agent-ops/query-builders/common.js +2 -2
- package/src/agent-ops/query-builders/multi-agent.js +1 -1
- package/src/agent-ops/query-builders/workflow-presets-core.js +3 -2
- 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/copy.js +11 -6
- package/src/catalog/provenance.js +2 -1
- 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/check.js +3 -3
- package/src/cli/commands/doctor.js +2 -9
- package/src/cli/commands/generator-policy/runner.js +1 -1
- package/src/cli/commands/import/help.js +2 -2
- package/src/cli/commands/import/paths.js +3 -11
- package/src/cli/commands/import/plan.js +9 -1
- package/src/cli/commands/import/refresh.js +7 -6
- package/src/cli/commands/import/workspace.js +8 -5
- package/src/cli/commands/migrate.js +153 -0
- package/src/cli/commands/query/definitions.js +10 -10
- package/src/cli/commands/query/workspace.js +2 -6
- package/src/cli/commands/source.js +3 -12
- package/src/cli/commands/template/check.js +6 -5
- package/src/cli/commands/template-runner.js +6 -6
- 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/context/domain-page.js +1 -1
- package/src/generator/context/shared/maintained-boundary.js +2 -2
- package/src/generator/context/shared/metrics.js +2 -2
- package/src/generator/context/task-mode.js +2 -2
- package/src/generator/sdlc/doc-page.js +1 -1
- package/src/generator/surfaces/databases/lifecycle-shared.js +1 -1
- package/src/generator/surfaces/native/swiftui-templates/README.generated.md +1 -1
- package/src/import/core/context.js +5 -7
- package/src/import/core/runner/candidates.js +123 -3
- package/src/import/core/runner/reports.js +4 -3
- package/src/import/core/runner/ui-drafts.js +58 -2
- package/src/new-project/constants.js +1 -1
- package/src/new-project/create.js +9 -2
- package/src/new-project/project-files.js +16 -13
- package/src/new-project/template-resolution.js +6 -4
- package/src/new-project/template-snapshots.js +38 -8
- package/src/new-project/template-updates.js +1 -1
- package/src/project-config/index.js +27 -0
- package/src/sdlc/adopt.js +6 -5
- package/src/sdlc/paths.js +3 -5
- package/src/sdlc/scaffold.js +2 -1
- package/src/workflows/reconcile/adoption-plan/build.js +7 -3
- package/src/workflows/reconcile/adoption-plan/outputs.js +12 -2
- package/src/workflows/reconcile/adoption-plan/paths.js +1 -1
- package/src/workflows/reconcile/candidate-model.js +18 -2
- package/src/workflows/reconcile/impacts/adoption-plan.js +6 -2
- package/src/workflows/reconcile/impacts/indexes.js +5 -1
- package/src/workflows/reconcile/renderers.js +41 -6
- package/src/workflows/shared.js +5 -11
- package/src/workspace-paths.js +328 -0
|
@@ -0,0 +1,153 @@
|
|
|
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
|
+
DEFAULT_TOPO_FOLDER_NAME,
|
|
9
|
+
DEFAULT_WORKSPACE_PATH,
|
|
10
|
+
LEGACY_TOPOGRAM_FOLDER_NAME,
|
|
11
|
+
PROJECT_CONFIG_FILE
|
|
12
|
+
} from "../../workspace-paths.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string|null|undefined} inputPath
|
|
16
|
+
* @returns {string}
|
|
17
|
+
*/
|
|
18
|
+
function projectRootForMigration(inputPath) {
|
|
19
|
+
const absolute = path.resolve(inputPath || ".");
|
|
20
|
+
const base = path.basename(absolute);
|
|
21
|
+
if (base === DEFAULT_TOPO_FOLDER_NAME || base === LEGACY_TOPOGRAM_FOLDER_NAME) {
|
|
22
|
+
return path.dirname(absolute);
|
|
23
|
+
}
|
|
24
|
+
return absolute;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {string} projectRoot
|
|
29
|
+
* @returns {string[]}
|
|
30
|
+
*/
|
|
31
|
+
function caseCollisionEntries(projectRoot) {
|
|
32
|
+
if (!fs.existsSync(projectRoot) || !fs.statSync(projectRoot).isDirectory()) {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
return fs.readdirSync(projectRoot)
|
|
36
|
+
.filter((/** @type {string} */ entry) => entry.toLowerCase() === DEFAULT_TOPO_FOLDER_NAME && entry !== DEFAULT_TOPO_FOLDER_NAME);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {string} projectRoot
|
|
41
|
+
* @returns {{ write: boolean, path: string|null, before: any|null, after: any|null }}
|
|
42
|
+
*/
|
|
43
|
+
function plannedProjectConfigUpdate(projectRoot) {
|
|
44
|
+
const configPath = path.join(projectRoot, PROJECT_CONFIG_FILE);
|
|
45
|
+
if (!fs.existsSync(configPath)) {
|
|
46
|
+
return { write: false, path: null, before: null, after: null };
|
|
47
|
+
}
|
|
48
|
+
const before = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
49
|
+
const after = { ...before };
|
|
50
|
+
const currentWorkspace = before.workspace;
|
|
51
|
+
if (currentWorkspace == null || currentWorkspace === "./topogram" || currentWorkspace === "topogram") {
|
|
52
|
+
after.workspace = DEFAULT_WORKSPACE_PATH;
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
write: JSON.stringify(before) !== JSON.stringify(after),
|
|
56
|
+
path: configPath,
|
|
57
|
+
before,
|
|
58
|
+
after
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {string|null|undefined} inputPath
|
|
64
|
+
* @param {{ write?: boolean, json?: boolean }} [options]
|
|
65
|
+
* @returns {number}
|
|
66
|
+
*/
|
|
67
|
+
export function runMigrateCommand(inputPath, options = {}) {
|
|
68
|
+
const projectRoot = projectRootForMigration(inputPath);
|
|
69
|
+
const legacyPath = path.join(projectRoot, LEGACY_TOPOGRAM_FOLDER_NAME);
|
|
70
|
+
const topoPath = path.join(projectRoot, DEFAULT_TOPO_FOLDER_NAME);
|
|
71
|
+
const write = Boolean(options.write);
|
|
72
|
+
/** @type {Array<Record<string, any>>} */
|
|
73
|
+
const diagnostics = [];
|
|
74
|
+
/** @type {Array<Record<string, any>>} */
|
|
75
|
+
const actions = [];
|
|
76
|
+
|
|
77
|
+
if (fs.existsSync(legacyPath) && fs.lstatSync(legacyPath).isSymbolicLink()) {
|
|
78
|
+
diagnostics.push({ severity: "error", message: `Refusing to migrate symlinked ${LEGACY_TOPOGRAM_FOLDER_NAME}/ at ${legacyPath}.` });
|
|
79
|
+
}
|
|
80
|
+
const collisions = caseCollisionEntries(projectRoot);
|
|
81
|
+
if (collisions.length > 0) {
|
|
82
|
+
diagnostics.push({ severity: "error", message: `Refusing to migrate because case-conflicting topo path(s) exist: ${collisions.join(", ")}.` });
|
|
83
|
+
}
|
|
84
|
+
if (fs.existsSync(legacyPath) && fs.existsSync(topoPath)) {
|
|
85
|
+
diagnostics.push({ severity: "error", message: `Refusing to migrate because both ${LEGACY_TOPOGRAM_FOLDER_NAME}/ and ${DEFAULT_TOPO_FOLDER_NAME}/ exist.` });
|
|
86
|
+
}
|
|
87
|
+
if (!fs.existsSync(legacyPath) && !fs.existsSync(topoPath)) {
|
|
88
|
+
diagnostics.push({ severity: "error", message: `No ${LEGACY_TOPOGRAM_FOLDER_NAME}/ or ${DEFAULT_TOPO_FOLDER_NAME}/ workspace folder found at ${projectRoot}.` });
|
|
89
|
+
}
|
|
90
|
+
if (fs.existsSync(topoPath) && fs.statSync(topoPath).isDirectory() && fs.readdirSync(topoPath).length > 0 && fs.existsSync(legacyPath)) {
|
|
91
|
+
diagnostics.push({ severity: "error", message: `Refusing to overwrite non-empty ${DEFAULT_TOPO_FOLDER_NAME}/ at ${topoPath}.` });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (fs.existsSync(legacyPath) && diagnostics.length === 0) {
|
|
95
|
+
actions.push({
|
|
96
|
+
kind: "rename",
|
|
97
|
+
from: legacyPath,
|
|
98
|
+
to: topoPath
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const configUpdate = plannedProjectConfigUpdate(projectRoot);
|
|
102
|
+
if (configUpdate.write) {
|
|
103
|
+
actions.push({
|
|
104
|
+
kind: "update_config",
|
|
105
|
+
path: configUpdate.path,
|
|
106
|
+
workspace: DEFAULT_WORKSPACE_PATH
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const ok = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length === 0;
|
|
111
|
+
if (ok && write) {
|
|
112
|
+
for (const action of actions) {
|
|
113
|
+
if (action.kind === "rename") {
|
|
114
|
+
fs.renameSync(action.from, action.to);
|
|
115
|
+
}
|
|
116
|
+
if (action.kind === "update_config" && configUpdate.path && configUpdate.after) {
|
|
117
|
+
fs.writeFileSync(configUpdate.path, `${JSON.stringify(configUpdate.after, null, 2)}\n`, "utf8");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const payload = {
|
|
123
|
+
ok,
|
|
124
|
+
dryRun: !write,
|
|
125
|
+
projectRoot,
|
|
126
|
+
legacyPath,
|
|
127
|
+
topoPath,
|
|
128
|
+
actions,
|
|
129
|
+
diagnostics,
|
|
130
|
+
errors: diagnostics.filter((diagnostic) => diagnostic.severity === "error").map((diagnostic) => diagnostic.message)
|
|
131
|
+
};
|
|
132
|
+
if (options.json) {
|
|
133
|
+
console.log(stableStringify(payload));
|
|
134
|
+
} else if (payload.ok) {
|
|
135
|
+
console.log(write ? "Workspace folder migration complete." : "Workspace folder migration dry run.");
|
|
136
|
+
if (actions.length === 0) {
|
|
137
|
+
console.log("No changes needed.");
|
|
138
|
+
}
|
|
139
|
+
for (const action of actions) {
|
|
140
|
+
if (action.kind === "rename") {
|
|
141
|
+
console.log(`Rename: ${action.from} -> ${action.to}`);
|
|
142
|
+
}
|
|
143
|
+
if (action.kind === "update_config") {
|
|
144
|
+
console.log(`Update ${action.path}: workspace ${DEFAULT_WORKSPACE_PATH}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
for (const error of payload.errors) {
|
|
149
|
+
console.error(error);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return payload.ok ? 0 : 1;
|
|
153
|
+
}
|
|
@@ -40,7 +40,7 @@ export function queryDefinitions() {
|
|
|
40
40
|
selectors: ["capability", "workflow", "projection", "widget", "entity", "journey", "domain"],
|
|
41
41
|
args: ["[path]", "[selectors]", "[--json]"],
|
|
42
42
|
output: "context_slice",
|
|
43
|
-
example: "topogram query slice ./
|
|
43
|
+
example: "topogram query slice ./topo --widget widget_data_grid"
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
name: "verification-targets",
|
|
@@ -49,7 +49,7 @@ export function queryDefinitions() {
|
|
|
49
49
|
selectors: ["mode", "capability", "workflow", "projection", "widget", "entity", "journey", "from-topogram"],
|
|
50
50
|
args: ["[path]", "[selectors]", "[--from-topogram <path>]", "[--json]"],
|
|
51
51
|
output: "verification_targets",
|
|
52
|
-
example: "topogram query verification-targets ./
|
|
52
|
+
example: "topogram query verification-targets ./topo --widget widget_data_grid"
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
name: "widget-behavior",
|
|
@@ -58,7 +58,7 @@ export function queryDefinitions() {
|
|
|
58
58
|
selectors: ["projection", "widget"],
|
|
59
59
|
args: ["[path]", "[--projection <id>]", "[--widget <id>]", "[--json]"],
|
|
60
60
|
output: "widget_behavior_report",
|
|
61
|
-
example: "topogram query widget-behavior ./
|
|
61
|
+
example: "topogram query widget-behavior ./topo --projection proj_web_surface --widget widget_data_grid --json"
|
|
62
62
|
},
|
|
63
63
|
{
|
|
64
64
|
name: "change-plan",
|
|
@@ -67,7 +67,7 @@ export function queryDefinitions() {
|
|
|
67
67
|
selectors: ["mode", "capability", "workflow", "projection", "widget", "entity", "journey", "surface", "from-topogram"],
|
|
68
68
|
args: ["[path]", "[selectors]", "[--from-topogram <path>]", "[--json]"],
|
|
69
69
|
output: "change_plan_query",
|
|
70
|
-
example: "topogram query change-plan ./
|
|
70
|
+
example: "topogram query change-plan ./topo --widget widget_data_grid"
|
|
71
71
|
},
|
|
72
72
|
{
|
|
73
73
|
name: "review-packet",
|
|
@@ -76,7 +76,7 @@ export function queryDefinitions() {
|
|
|
76
76
|
selectors: ["mode", "capability", "workflow", "projection", "widget", "entity", "journey", "surface", "from-topogram"],
|
|
77
77
|
args: ["[path]", "[selectors]", "[--from-topogram <path>]", "[--json]"],
|
|
78
78
|
output: "review_packet_query",
|
|
79
|
-
example: "topogram query review-packet ./
|
|
79
|
+
example: "topogram query review-packet ./topo --widget widget_data_grid"
|
|
80
80
|
},
|
|
81
81
|
{
|
|
82
82
|
name: "resolved-workflow-context",
|
|
@@ -85,7 +85,7 @@ export function queryDefinitions() {
|
|
|
85
85
|
selectors: ["mode", "capability", "workflow", "projection", "widget", "entity", "journey", "surface", "provider", "preset", "from-topogram"],
|
|
86
86
|
args: ["[path]", "[--mode <id>]", "[selectors]", "[--from-topogram <path>]", "[--json]"],
|
|
87
87
|
output: "resolved_workflow_context_query",
|
|
88
|
-
example: "topogram query resolved-workflow-context ./
|
|
88
|
+
example: "topogram query resolved-workflow-context ./topo --mode modeling --widget widget_data_grid --json"
|
|
89
89
|
},
|
|
90
90
|
{
|
|
91
91
|
name: "single-agent-plan",
|
|
@@ -94,7 +94,7 @@ export function queryDefinitions() {
|
|
|
94
94
|
selectors: ["mode", "capability", "workflow", "projection", "widget", "entity", "journey", "surface", "from-topogram"],
|
|
95
95
|
args: ["[path]", "[--mode <id>]", "[selectors]", "[--from-topogram <path>]", "[--json]"],
|
|
96
96
|
output: "single_agent_plan_query",
|
|
97
|
-
example: "topogram query single-agent-plan ./
|
|
97
|
+
example: "topogram query single-agent-plan ./topo --mode modeling --widget widget_data_grid --json"
|
|
98
98
|
},
|
|
99
99
|
{
|
|
100
100
|
name: "risk-summary",
|
|
@@ -103,7 +103,7 @@ export function queryDefinitions() {
|
|
|
103
103
|
selectors: ["mode", "capability", "workflow", "projection", "widget", "entity", "journey", "surface", "from-topogram"],
|
|
104
104
|
args: ["[path]", "[selectors]", "[--from-topogram <path>]", "[--json]"],
|
|
105
105
|
output: "risk_summary_query",
|
|
106
|
-
example: "topogram query risk-summary ./
|
|
106
|
+
example: "topogram query risk-summary ./topo --widget widget_data_grid"
|
|
107
107
|
},
|
|
108
108
|
{
|
|
109
109
|
name: "proceed-decision",
|
|
@@ -112,7 +112,7 @@ export function queryDefinitions() {
|
|
|
112
112
|
selectors: ["mode", "capability", "workflow", "projection", "widget", "entity", "journey", "surface", "from-topogram"],
|
|
113
113
|
args: ["[path]", "[--mode <id>]", "[selectors]", "[--from-topogram <path>]", "[--json]"],
|
|
114
114
|
output: "proceed_decision_query",
|
|
115
|
-
example: "topogram query proceed-decision ./
|
|
115
|
+
example: "topogram query proceed-decision ./topo --mode verification"
|
|
116
116
|
},
|
|
117
117
|
{
|
|
118
118
|
name: "write-scope",
|
|
@@ -121,7 +121,7 @@ export function queryDefinitions() {
|
|
|
121
121
|
selectors: ["mode", "capability", "workflow", "projection", "widget", "entity", "journey", "from-topogram"],
|
|
122
122
|
args: ["[path]", "[selectors]", "[--from-topogram <path>]", "[--json]"],
|
|
123
123
|
output: "write_scope_query",
|
|
124
|
-
example: "topogram query write-scope ./
|
|
124
|
+
example: "topogram query write-scope ./topo --widget widget_data_grid"
|
|
125
125
|
}
|
|
126
126
|
];
|
|
127
127
|
}
|
|
@@ -6,6 +6,7 @@ import path from "node:path";
|
|
|
6
6
|
import { generateWorkspace } from "../../../generator.js";
|
|
7
7
|
import { formatValidationErrors } from "../../../validator.js";
|
|
8
8
|
import { buildChangePlanPayload } from "../../../agent-ops/query-builders.js";
|
|
9
|
+
import { resolveTopoRoot } from "../../../workspace-paths.js";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @typedef {Record<string, any>} AnyRecord
|
|
@@ -16,12 +17,7 @@ import { buildChangePlanPayload } from "../../../agent-ops/query-builders.js";
|
|
|
16
17
|
* @returns {string}
|
|
17
18
|
*/
|
|
18
19
|
export function normalizeTopogramPath(inputPath) {
|
|
19
|
-
|
|
20
|
-
if (path.basename(absolute) === "topogram") {
|
|
21
|
-
return absolute;
|
|
22
|
-
}
|
|
23
|
-
const candidate = path.join(absolute, "topogram");
|
|
24
|
-
return fs.existsSync(candidate) ? candidate : absolute;
|
|
20
|
+
return resolveTopoRoot(inputPath);
|
|
25
21
|
}
|
|
26
22
|
|
|
27
23
|
/**
|
|
@@ -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
|
/**
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
validateProjectConfig,
|
|
12
12
|
validateProjectOutputOwnership
|
|
13
13
|
} from "../../../project-config.js";
|
|
14
|
+
import { resolveTopoRoot } from "../../../workspace-paths.js";
|
|
14
15
|
import {
|
|
15
16
|
buildTemplateUpdatePlan,
|
|
16
17
|
createNewProject,
|
|
@@ -91,12 +92,12 @@ function diagnosticForTemplateCreateFailure(message, templateSpec, step) {
|
|
|
91
92
|
step
|
|
92
93
|
});
|
|
93
94
|
}
|
|
94
|
-
if (message.includes("is missing topogram/")) {
|
|
95
|
+
if (message.includes("is missing topo/") || message.includes("is missing topogram/")) {
|
|
95
96
|
return templateCheckDiagnostic({
|
|
96
97
|
code: "template_topogram_missing",
|
|
97
98
|
message,
|
|
98
|
-
path: localTemplatePath(templateSpec, "
|
|
99
|
-
suggestedFix: "Add a
|
|
99
|
+
path: localTemplatePath(templateSpec, "topo"),
|
|
100
|
+
suggestedFix: "Add a topo/ directory with the reusable Topogram source files.",
|
|
100
101
|
step
|
|
101
102
|
});
|
|
102
103
|
}
|
|
@@ -105,7 +106,7 @@ function diagnosticForTemplateCreateFailure(message, templateSpec, step) {
|
|
|
105
106
|
code: "template_project_config_missing",
|
|
106
107
|
message,
|
|
107
108
|
path: localTemplatePath(templateSpec, "topogram.project.json"),
|
|
108
|
-
suggestedFix: "Add topogram.project.json beside
|
|
109
|
+
suggestedFix: "Add topogram.project.json beside topo/ with outputs and topology.runtimes.",
|
|
109
110
|
step
|
|
110
111
|
});
|
|
111
112
|
}
|
|
@@ -320,7 +321,7 @@ export function buildTemplateCheckPayload(templateSpec) {
|
|
|
320
321
|
template: projectConfigInfo.config.template?.id || null
|
|
321
322
|
}));
|
|
322
323
|
|
|
323
|
-
const ast = parsePath(
|
|
324
|
+
const ast = parsePath(resolveTopoRoot(projectRoot));
|
|
324
325
|
const resolved = resolveWorkspace(ast);
|
|
325
326
|
const projectValidation = combineProjectValidationResults(
|
|
326
327
|
validateProjectConfig(projectConfigInfo.config, resolved.ok ? resolved.graph : null, { configDir: projectConfigInfo.configDir }),
|
|
@@ -77,7 +77,7 @@ export function runTemplateCommand(context) {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
if (command === "status") {
|
|
80
|
-
const projectConfigInfo = loadProjectConfig(inputPath || "./
|
|
80
|
+
const projectConfigInfo = loadProjectConfig(inputPath || "./topo");
|
|
81
81
|
if (!projectConfigInfo) {
|
|
82
82
|
throw new Error("Cannot inspect template status without topogram.project.json.");
|
|
83
83
|
}
|
|
@@ -108,7 +108,7 @@ export function runTemplateCommand(context) {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
if (command === "policy:init") {
|
|
111
|
-
const projectConfigInfo = loadProjectConfig(inputPath || "./
|
|
111
|
+
const projectConfigInfo = loadProjectConfig(inputPath || "./topo");
|
|
112
112
|
if (!projectConfigInfo) {
|
|
113
113
|
throw new Error("Cannot initialize template policy without topogram.project.json.");
|
|
114
114
|
}
|
|
@@ -131,7 +131,7 @@ export function runTemplateCommand(context) {
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
if (command === "policy:check") {
|
|
134
|
-
const payload = buildTemplatePolicyCheckPayload(inputPath || "./
|
|
134
|
+
const payload = buildTemplatePolicyCheckPayload(inputPath || "./topo");
|
|
135
135
|
if (json) {
|
|
136
136
|
console.log(stableStringify(payload));
|
|
137
137
|
} else {
|
|
@@ -141,7 +141,7 @@ export function runTemplateCommand(context) {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
if (command === "policy:explain") {
|
|
144
|
-
const payload = buildTemplatePolicyExplainPayload(inputPath || "./
|
|
144
|
+
const payload = buildTemplatePolicyExplainPayload(inputPath || "./topo");
|
|
145
145
|
if (json) {
|
|
146
146
|
console.log(stableStringify(payload));
|
|
147
147
|
} else {
|
|
@@ -151,7 +151,7 @@ export function runTemplateCommand(context) {
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
if (command === "policy:pin") {
|
|
154
|
-
const payload = buildTemplatePolicyPinPayload(inputPath || "./
|
|
154
|
+
const payload = buildTemplatePolicyPinPayload(inputPath || "./topo", commandArgs.templatePolicyPinSpec);
|
|
155
155
|
if (json) {
|
|
156
156
|
console.log(stableStringify(payload));
|
|
157
157
|
} else {
|
|
@@ -178,7 +178,7 @@ export function runTemplateCommand(context) {
|
|
|
178
178
|
if (command === "update") {
|
|
179
179
|
const payload = buildTemplateUpdateCliPayload({
|
|
180
180
|
args,
|
|
181
|
-
inputPath: inputPath || "./
|
|
181
|
+
inputPath: inputPath || "./topo",
|
|
182
182
|
templateIndex: args.indexOf("--template"),
|
|
183
183
|
templateName,
|
|
184
184
|
useLatestTemplate: args.includes("--latest"),
|
|
@@ -205,7 +205,7 @@ export function runTrustDiffCommand(inputPath, options = {}) {
|
|
|
205
205
|
* @returns {number}
|
|
206
206
|
*/
|
|
207
207
|
export function runTrustCommand(context) {
|
|
208
|
-
const inputPath = context.inputPath || "./
|
|
208
|
+
const inputPath = context.inputPath || "./topo";
|
|
209
209
|
if (context.commandArgs.trustCommand === "template") {
|
|
210
210
|
return runTrustTemplateCommand(inputPath, { force: Boolean(context.commandArgs.force) });
|
|
211
211
|
}
|
|
@@ -6,6 +6,7 @@ import path from "node:path";
|
|
|
6
6
|
import { stableStringify } from "../../format.js";
|
|
7
7
|
import { parsePath } from "../../parser.js";
|
|
8
8
|
import { formatValidationErrors, validateWorkspace } from "../../validator.js";
|
|
9
|
+
import { resolveTopoRoot } from "../../workspace-paths.js";
|
|
9
10
|
import { runWorkflow } from "../../workflows.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -13,7 +14,11 @@ import { runWorkflow } from "../../workflows.js";
|
|
|
13
14
|
* @returns {number}
|
|
14
15
|
*/
|
|
15
16
|
export function runValidateCommand(inputPath) {
|
|
16
|
-
const
|
|
17
|
+
const requestedPath = inputPath || ".";
|
|
18
|
+
const targetPath = fs.existsSync(requestedPath) && fs.statSync(requestedPath).isFile()
|
|
19
|
+
? requestedPath
|
|
20
|
+
: resolveTopoRoot(requestedPath);
|
|
21
|
+
const ast = parsePath(targetPath);
|
|
17
22
|
const result = validateWorkspace(ast);
|
|
18
23
|
if (!result.ok) {
|
|
19
24
|
console.error(formatValidationErrors(result));
|
package/src/cli/dispatcher.js
CHANGED
|
@@ -10,6 +10,7 @@ import { runGenerateAppCommand } from "./commands/generate.js";
|
|
|
10
10
|
import { runGeneratorCommand } from "./commands/generator.js";
|
|
11
11
|
import { runGeneratorPolicyCommand } from "./commands/generator-policy.js";
|
|
12
12
|
import { runImportCommand } from "./commands/import-runner.js";
|
|
13
|
+
import { runMigrateCommand } from "./commands/migrate.js";
|
|
13
14
|
import { runNewProjectCommand } from "./commands/new.js";
|
|
14
15
|
import { runPackageCommand } from "./commands/package.js";
|
|
15
16
|
import { runParseCommand, runResolveCommand } from "./commands/inspect.js";
|
|
@@ -145,7 +146,7 @@ export async function runCliDispatch(context) {
|
|
|
145
146
|
return 1;
|
|
146
147
|
}
|
|
147
148
|
|
|
148
|
-
if ((shouldCheck || shouldWidgetCheck || shouldWidgetBehavior || shouldAgentBrief || shouldValidate || commandArgs?.generatorPolicyCommand || commandArgs?.trustCommand ||
|
|
149
|
+
if ((shouldCheck || shouldWidgetCheck || shouldWidgetBehavior || shouldAgentBrief || shouldValidate || commandArgs?.generatorPolicyCommand || commandArgs?.trustCommand || commandArgs?.queryName || commandArgs?.workflowPresetCommand || generateTarget) && inputPath) {
|
|
149
150
|
inputPath = normalizeTopogramPath(inputPath);
|
|
150
151
|
}
|
|
151
152
|
const effectiveInputPath = commandInputPath(inputPath);
|
|
@@ -241,6 +242,10 @@ export async function runCliDispatch(context) {
|
|
|
241
242
|
return runPackageCommand({ commandArgs, inputPath, json: emitJson });
|
|
242
243
|
}
|
|
243
244
|
|
|
245
|
+
if (commandArgs?.migrateCommand) {
|
|
246
|
+
return runMigrateCommand(inputPath, { write: shouldWrite, json: emitJson });
|
|
247
|
+
}
|
|
248
|
+
|
|
244
249
|
if (commandArgs?.importCommand) {
|
|
245
250
|
return runImportCommand({
|
|
246
251
|
commandArgs,
|
package/src/cli/help.js
CHANGED
|
@@ -28,6 +28,7 @@ export function printUsage(options = {}) {
|
|
|
28
28
|
console.log(" or: topogram catalog doctor [--json] [--catalog <path-or-source>]");
|
|
29
29
|
console.log(" or: topogram catalog check <path-or-url> [--json]");
|
|
30
30
|
console.log(" or: topogram catalog copy <id> <target> [--version <version>] [--json] [--catalog <path-or-source>]");
|
|
31
|
+
console.log(" or: topogram migrate workspace-folder [path] [--dry-run|--write] [--json]");
|
|
31
32
|
console.log(" or: topogram package update-cli <version|--latest> [--json]");
|
|
32
33
|
console.log(" or: topogram import <app-path> --out <target> [--from <track[,track]>] [--json]");
|
|
33
34
|
console.log(" or: topogram import refresh [path] [--from <app-path>] [--dry-run] [--json]");
|
|
@@ -79,9 +80,9 @@ export function printUsage(options = {}) {
|
|
|
79
80
|
console.log(" topogram agent brief --json");
|
|
80
81
|
console.log(" topogram query list");
|
|
81
82
|
console.log(" topogram query show widget-behavior");
|
|
82
|
-
console.log(" topogram query widget-behavior ./
|
|
83
|
+
console.log(" topogram query widget-behavior ./topo --projection proj_web_surface --json");
|
|
83
84
|
console.log(" topogram emit ui-widget-contract --widget widget_data_grid --json");
|
|
84
|
-
console.log(" topogram emit widget-conformance-report ./
|
|
85
|
+
console.log(" topogram emit widget-conformance-report ./topo --projection proj_web_surface --json");
|
|
85
86
|
console.log(" topogram generator list");
|
|
86
87
|
console.log(" topogram generator show @topogram/generator-react-web");
|
|
87
88
|
console.log(" topogram generator check ./generator-package");
|
|
@@ -136,7 +137,7 @@ export function printUsage(options = {}) {
|
|
|
136
137
|
console.log(" topogram template update --check");
|
|
137
138
|
console.log(" topogram template update --apply");
|
|
138
139
|
console.log("");
|
|
139
|
-
console.log("Defaults: check/generate use ./
|
|
140
|
+
console.log("Defaults: check/generate use ./topo, and generate writes ./app.");
|
|
140
141
|
console.log("Default starter: hello-web from the catalog. Run `topogram template list` for catalog aliases.");
|
|
141
142
|
console.log("Generated app commands are emitted into the output package.json.");
|
|
142
143
|
console.log("Run `topogram help <command>` for command-specific help.");
|
|
@@ -231,13 +232,13 @@ export function printGenerateHelp() {
|
|
|
231
232
|
console.log("Usage: topogram generate [path] [--out <path>]");
|
|
232
233
|
console.log(" or: topogram generate app [path] [--out <path>]");
|
|
233
234
|
console.log("");
|
|
234
|
-
console.log("Defaults: path is ./
|
|
235
|
+
console.log("Defaults: path is ./topo and app generation writes ./app.");
|
|
235
236
|
console.log("Use `topogram emit <target>` for contracts, reports, snapshots, migration plans, and other artifacts.");
|
|
236
237
|
console.log("");
|
|
237
238
|
console.log("Examples:");
|
|
238
239
|
console.log(" topogram generate");
|
|
239
|
-
console.log(" topogram generate ./
|
|
240
|
-
console.log(" topogram generate app ./
|
|
240
|
+
console.log(" topogram generate ./topo --out ./app");
|
|
241
|
+
console.log(" topogram generate app ./topo --out ./app");
|
|
241
242
|
}
|
|
242
243
|
|
|
243
244
|
/**
|
|
@@ -249,7 +250,7 @@ export function printEmitHelp() {
|
|
|
249
250
|
console.log("");
|
|
250
251
|
console.log("Emits named contracts, reports, snapshots, migration plans, and other artifacts.");
|
|
251
252
|
console.log("");
|
|
252
|
-
console.log("Defaults: path is ./
|
|
253
|
+
console.log("Defaults: path is ./topo. Emit prints to stdout unless --write is passed. --write writes ./artifacts unless --out-dir is supplied.");
|
|
253
254
|
console.log("");
|
|
254
255
|
console.log("Common artifact targets:");
|
|
255
256
|
console.log(" ui-widget-contract");
|
|
@@ -268,10 +269,10 @@ export function printEmitHelp() {
|
|
|
268
269
|
console.log("");
|
|
269
270
|
console.log("Examples:");
|
|
270
271
|
console.log(" topogram emit ui-widget-contract --widget widget_data_grid --json");
|
|
271
|
-
console.log(" topogram emit widget-conformance-report ./
|
|
272
|
-
console.log(" topogram emit widget-behavior-report ./
|
|
273
|
-
console.log(" topogram emit db-schema-snapshot ./
|
|
274
|
-
console.log(" topogram emit sql-migration ./
|
|
272
|
+
console.log(" topogram emit widget-conformance-report ./topo --projection proj_web_surface --json");
|
|
273
|
+
console.log(" topogram emit widget-behavior-report ./topo --projection proj_web_surface --json");
|
|
274
|
+
console.log(" topogram emit db-schema-snapshot ./topo --projection proj_db_postgres --json");
|
|
275
|
+
console.log(" topogram emit sql-migration ./topo --projection proj_db_postgres --from-snapshot ./state/current.json");
|
|
275
276
|
console.log(" topogram emit ui-widget-contract --write --out-dir ./contracts");
|
|
276
277
|
}
|
|
277
278
|
|
|
@@ -284,13 +285,13 @@ export function printWidgetHelp() {
|
|
|
284
285
|
console.log("");
|
|
285
286
|
console.log("Checks projection widget_bindings usage against reusable widget contracts and behavior realizations.");
|
|
286
287
|
console.log("");
|
|
287
|
-
console.log("Defaults: path is ./
|
|
288
|
+
console.log("Defaults: path is ./topo.");
|
|
288
289
|
console.log("");
|
|
289
290
|
console.log("Examples:");
|
|
290
291
|
console.log(" topogram widget check");
|
|
291
292
|
console.log(" topogram widget check --projection proj_web_surface");
|
|
292
|
-
console.log(" topogram widget check ./
|
|
293
|
+
console.log(" topogram widget check ./topo --widget widget_data_grid --json");
|
|
293
294
|
console.log(" topogram widget behavior");
|
|
294
295
|
console.log(" topogram widget behavior --projection proj_web_surface");
|
|
295
|
-
console.log(" topogram widget behavior ./
|
|
296
|
+
console.log(" topogram widget behavior ./topo --widget widget_data_grid --json");
|
|
296
297
|
}
|
|
@@ -17,7 +17,7 @@ const RENAMED_GENERATE_TARGETS = new Map([
|
|
|
17
17
|
* @param {string} [fallback]
|
|
18
18
|
* @returns {string}
|
|
19
19
|
*/
|
|
20
|
-
function commandPath(args, index, fallback = "./
|
|
20
|
+
function commandPath(args, index, fallback = "./topo") {
|
|
21
21
|
const value = args[index];
|
|
22
22
|
return value && !value.startsWith("-") ? value : fallback;
|
|
23
23
|
}
|
package/src/cli/output-safety.js
CHANGED
|
@@ -5,6 +5,7 @@ import os from "node:os";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
|
|
7
7
|
import { outputOwnershipForPath } from "../project-config.js";
|
|
8
|
+
import { DEFAULT_TOPO_FOLDER_NAME } from "../workspace-paths.js";
|
|
8
9
|
|
|
9
10
|
export const GENERATED_OUTPUT_SENTINEL = ".topogram-generated.json";
|
|
10
11
|
|
|
@@ -103,5 +104,5 @@ export function topogramInputPathForGeneration(inputPath) {
|
|
|
103
104
|
if (isSameOrInside(REPO_ROOT, absolute)) {
|
|
104
105
|
return `./${path.relative(REPO_ROOT, absolute).replace(/\\/g, "/")}`;
|
|
105
106
|
}
|
|
106
|
-
return path.basename(absolute) ===
|
|
107
|
+
return path.basename(absolute) === DEFAULT_TOPO_FOLDER_NAME ? `./${DEFAULT_TOPO_FOLDER_NAME}` : ".";
|
|
107
108
|
}
|
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import path from "node:path";
|
|
3
|
+
import { resolveTopoRoot, resolveWorkspaceContext } from "../workspace-paths.js";
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* @param {string} inputPath
|
|
8
7
|
* @returns {string}
|
|
9
8
|
*/
|
|
10
9
|
export function normalizeTopogramPath(inputPath) {
|
|
11
|
-
|
|
12
|
-
if (path.basename(absolute) === "topogram") {
|
|
13
|
-
return absolute;
|
|
14
|
-
}
|
|
15
|
-
const candidate = path.join(absolute, "topogram");
|
|
16
|
-
return fs.existsSync(candidate) ? candidate : absolute;
|
|
10
|
+
return resolveTopoRoot(inputPath);
|
|
17
11
|
}
|
|
18
12
|
|
|
19
13
|
/**
|
|
@@ -21,9 +15,5 @@ export function normalizeTopogramPath(inputPath) {
|
|
|
21
15
|
* @returns {string}
|
|
22
16
|
*/
|
|
23
17
|
export function normalizeProjectRoot(inputPath) {
|
|
24
|
-
|
|
25
|
-
if (path.basename(absolute) === "topogram") {
|
|
26
|
-
return path.dirname(absolute);
|
|
27
|
-
}
|
|
28
|
-
return absolute;
|
|
18
|
+
return resolveWorkspaceContext(inputPath).projectRoot;
|
|
29
19
|
}
|
|
@@ -118,7 +118,7 @@ export function generateDomainPage(graph, options = {}) {
|
|
|
118
118
|
version: 1,
|
|
119
119
|
focus: { kind: "domain", id: domain.id },
|
|
120
120
|
output: {
|
|
121
|
-
path: `
|
|
121
|
+
path: `topo/docs-generated/domains/${domain.id}.md`,
|
|
122
122
|
contents: lines.join("\n") + "\n"
|
|
123
123
|
},
|
|
124
124
|
summary
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { resolveWorkspaceContext } from "../../../workspace-paths.js";
|
|
3
4
|
|
|
4
5
|
import { parseDocFile } from "../../../workspace-docs.js";
|
|
5
6
|
import {
|
|
@@ -49,8 +50,7 @@ export function relativePathFromGraph(graph, targetPath) {
|
|
|
49
50
|
* @returns {any}
|
|
50
51
|
*/
|
|
51
52
|
function workspaceRootFromGraph(graph) {
|
|
52
|
-
|
|
53
|
-
return path.basename(root) === "topogram" ? path.dirname(root) : root;
|
|
53
|
+
return resolveWorkspaceContext(graph.root).projectRoot;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
@@ -34,7 +34,7 @@ export function percentOf(part, whole) {
|
|
|
34
34
|
*/
|
|
35
35
|
export function buildDefaultWriteScope() {
|
|
36
36
|
return {
|
|
37
|
-
safe_to_edit: ["
|
|
37
|
+
safe_to_edit: ["topo/**", "candidates/**"],
|
|
38
38
|
generator_owned: ["artifacts/**", "apps/**"],
|
|
39
39
|
human_owned_review_required: ["examples/maintained/proof-app/**"],
|
|
40
40
|
out_of_bounds: [".git/**", "node_modules/**"]
|
|
@@ -54,7 +54,7 @@ export function buildMaintainedWriteScope(graph, maintainedFiles = []) {
|
|
|
54
54
|
...maintainedFiles,
|
|
55
55
|
"examples/maintained/proof-app/**"
|
|
56
56
|
]),
|
|
57
|
-
out_of_bounds: ["
|
|
57
|
+
out_of_bounds: ["topo/**"]
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
|