@documentation-robotics/cli 0.1.12 → 0.2.0
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/README.md +39 -1
- package/dist/analyzers/verify-engine.js +22 -19
- package/dist/analyzers/verify-engine.js.map +2 -2
- package/dist/cli.js +101 -27
- package/dist/cli.js.map +3 -3
- package/dist/coding-agents/agents/claude-code.js +3 -1
- package/dist/coding-agents/agents/claude-code.js.map +2 -2
- package/dist/coding-agents/claude-code-client.js +2 -1
- package/dist/coding-agents/claude-code-client.js.map +2 -2
- package/dist/coding-agents/copilot-client.js +17 -12
- package/dist/coding-agents/copilot-client.js.map +2 -2
- package/dist/coding-agents/default-permissions.js +122 -0
- package/dist/coding-agents/default-permissions.js.map +7 -0
- package/dist/commands/add.js +10 -6
- package/dist/commands/add.js.map +2 -2
- package/dist/commands/analyzer.js +18 -10
- package/dist/commands/analyzer.js.map +2 -2
- package/dist/commands/audit.js +6 -6
- package/dist/commands/audit.js.map +2 -2
- package/dist/commands/changeset.js +238 -145
- package/dist/commands/changeset.js.map +2 -2
- package/dist/commands/delete.js +45 -18
- package/dist/commands/delete.js.map +3 -3
- package/dist/commands/element.js +3 -2
- package/dist/commands/element.js.map +2 -2
- package/dist/commands/farm.js +657 -0
- package/dist/commands/farm.js.map +7 -0
- package/dist/commands/init.js +11 -1
- package/dist/commands/init.js.map +2 -2
- package/dist/commands/mcp.js +107 -0
- package/dist/commands/mcp.js.map +7 -0
- package/dist/commands/relationship.js +53 -30
- package/dist/commands/relationship.js.map +2 -2
- package/dist/commands/reports.js +1 -1
- package/dist/commands/reports.js.map +2 -2
- package/dist/commands/update.js +19 -6
- package/dist/commands/update.js.map +2 -2
- package/dist/commands/validate.js +41 -3
- package/dist/commands/validate.js.map +2 -2
- package/dist/commands/version.js +2 -2
- package/dist/commands/version.js.map +2 -2
- package/dist/config.js +83 -0
- package/dist/config.js.map +7 -0
- package/dist/core/annotation-store.js +138 -0
- package/dist/core/annotation-store.js.map +7 -0
- package/dist/core/farm-manifest.js +134 -0
- package/dist/core/farm-manifest.js.map +7 -0
- package/dist/core/farm-sync-engine.js +346 -0
- package/dist/core/farm-sync-engine.js.map +7 -0
- package/dist/core/farm-sync-state.js +159 -0
- package/dist/core/farm-sync-state.js.map +7 -0
- package/dist/core/manifest.js +6 -3
- package/dist/core/manifest.js.map +2 -2
- package/dist/core/migration-registry.js +55 -1
- package/dist/core/migration-registry.js.map +2 -2
- package/dist/core/model.js +177 -16
- package/dist/core/model.js.map +3 -3
- package/dist/core/virtual-projection.js +1 -0
- package/dist/core/virtual-projection.js.map +2 -2
- package/dist/generated/node-types.js +1 -1
- package/dist/generated/node-types.js.map +2 -2
- package/dist/generated/relationship-index.js +45 -9
- package/dist/generated/relationship-index.js.map +2 -2
- package/dist/integrations/claude_code/docs/MCP_SERVER.md +55 -0
- package/dist/integrations/claude_code/docs/README.md +4 -0
- package/dist/mcp/api-key-manager.js +98 -0
- package/dist/mcp/api-key-manager.js.map +7 -0
- package/dist/mcp/http-transport.js +176 -0
- package/dist/mcp/http-transport.js.map +7 -0
- package/dist/mcp/resource-registry.js +97 -0
- package/dist/mcp/resource-registry.js.map +7 -0
- package/dist/mcp/tool-registry.js +44 -0
- package/dist/mcp/tool-registry.js.map +7 -0
- package/dist/mcp/tools/annotation-create.js +39 -0
- package/dist/mcp/tools/annotation-create.js.map +7 -0
- package/dist/mcp/tools/annotation-delete.js +29 -0
- package/dist/mcp/tools/annotation-delete.js.map +7 -0
- package/dist/mcp/tools/annotation-get.js +29 -0
- package/dist/mcp/tools/annotation-get.js.map +7 -0
- package/dist/mcp/tools/annotation-list.js +24 -0
- package/dist/mcp/tools/annotation-list.js.map +7 -0
- package/dist/mcp/tools/annotation-reply.js +34 -0
- package/dist/mcp/tools/annotation-reply.js.map +7 -0
- package/dist/mcp/tools/annotation-shared.js +10 -0
- package/dist/mcp/tools/annotation-shared.js.map +7 -0
- package/dist/mcp/tools/annotation-update.js +39 -0
- package/dist/mcp/tools/annotation-update.js.map +7 -0
- package/dist/mcp/tools/changeset-list.js +33 -0
- package/dist/mcp/tools/changeset-list.js.map +7 -0
- package/dist/mcp/tools/changeset-show.js +39 -0
- package/dist/mcp/tools/changeset-show.js.map +7 -0
- package/dist/mcp/tools/chat-launch.js +50 -0
- package/dist/mcp/tools/chat-launch.js.map +7 -0
- package/dist/mcp/tools/chat-status.js +26 -0
- package/dist/mcp/tools/chat-status.js.map +7 -0
- package/dist/mcp/tools/index.js +47 -0
- package/dist/mcp/tools/index.js.map +7 -0
- package/dist/mcp/tools/model-add.js +106 -0
- package/dist/mcp/tools/model-add.js.map +7 -0
- package/dist/mcp/tools/model-delete.js +99 -0
- package/dist/mcp/tools/model-delete.js.map +7 -0
- package/dist/mcp/tools/model-export.js +72 -0
- package/dist/mcp/tools/model-export.js.map +7 -0
- package/dist/mcp/tools/model-info.js +51 -0
- package/dist/mcp/tools/model-info.js.map +7 -0
- package/dist/mcp/tools/model-list.js +53 -0
- package/dist/mcp/tools/model-list.js.map +7 -0
- package/dist/mcp/tools/model-reload.js +34 -0
- package/dist/mcp/tools/model-reload.js.map +7 -0
- package/dist/mcp/tools/model-search.js +88 -0
- package/dist/mcp/tools/model-search.js.map +7 -0
- package/dist/mcp/tools/model-show.js +41 -0
- package/dist/mcp/tools/model-show.js.map +7 -0
- package/dist/mcp/tools/model-stats.js +22 -0
- package/dist/mcp/tools/model-stats.js.map +7 -0
- package/dist/mcp/tools/model-trace.js +55 -0
- package/dist/mcp/tools/model-trace.js.map +7 -0
- package/dist/mcp/tools/model-update.js +105 -0
- package/dist/mcp/tools/model-update.js.map +7 -0
- package/dist/mcp/tools/model-validate.js +78 -0
- package/dist/mcp/tools/model-validate.js.map +7 -0
- package/dist/mcp/tools/shared.js +103 -0
- package/dist/mcp/tools/shared.js.map +7 -0
- package/dist/reports/model-layer-report-generator.js +5 -3
- package/dist/reports/model-layer-report-generator.js.map +2 -2
- package/dist/reports/model-readme-generator.js +116 -0
- package/dist/reports/model-readme-generator.js.map +7 -0
- package/dist/reports/model-report-data.js +37 -1
- package/dist/reports/model-report-data.js.map +2 -2
- package/dist/reports/model-report-orchestrator.js +29 -6
- package/dist/reports/model-report-orchestrator.js.map +2 -2
- package/dist/reports/model-report-utils.js +9 -0
- package/dist/reports/model-report-utils.js.map +7 -0
- package/dist/schemas/bundled/analyzers/cbm.json +2 -2
- package/dist/schemas/bundled/analyzers/manifest.json +2 -2
- package/dist/schemas/bundled/api.json +1 -1
- package/dist/schemas/bundled/apm.json +1 -1
- package/dist/schemas/bundled/application.json +12 -1
- package/dist/schemas/bundled/base.json +1 -1
- package/dist/schemas/bundled/business.json +1 -1
- package/dist/schemas/bundled/data-model.json +38 -2
- package/dist/schemas/bundled/data-store.json +1 -1
- package/dist/schemas/bundled/manifest.json +4 -4
- package/dist/schemas/bundled/motivation.json +1 -1
- package/dist/schemas/bundled/navigation.json +1 -1
- package/dist/schemas/bundled/product.json +1 -1
- package/dist/schemas/bundled/security.json +1 -1
- package/dist/schemas/bundled/technology.json +1 -1
- package/dist/schemas/bundled/testing.json +1 -1
- package/dist/schemas/bundled/ux.json +1 -1
- package/dist/server/server.js +45 -7
- package/dist/server/server.js.map +2 -2
- package/dist/telemetry/config.js +2 -40
- package/dist/telemetry/config.js.map +2 -2
- package/dist/telemetry/index.js +1 -1
- package/dist/types/index.js.map +2 -2
- package/dist/utils/ansi-suppressor.js +25 -0
- package/dist/utils/ansi-suppressor.js.map +7 -0
- package/dist/utils/color-utils.js +34 -0
- package/dist/utils/color-utils.js.map +7 -0
- package/dist/utils/element-reference-shape.js +15 -1
- package/dist/utils/element-reference-shape.js.map +3 -3
- package/dist/utils/errors.js +69 -10
- package/dist/utils/errors.js.map +2 -2
- package/dist/utils/globals.js +17 -1
- package/dist/utils/globals.js.map +2 -2
- package/dist/utils/project-paths.js +24 -2
- package/dist/utils/project-paths.js.map +2 -2
- package/dist/utils/reference-path-parser.js +49 -0
- package/dist/utils/reference-path-parser.js.map +7 -0
- package/dist/utils/source-reference.js +5 -0
- package/dist/utils/source-reference.js.map +2 -2
- package/dist/utils/spec-version.js +10 -2
- package/dist/utils/spec-version.js.map +2 -2
- package/dist/validators/composed-reference-validator.js +262 -0
- package/dist/validators/composed-reference-validator.js.map +7 -0
- package/dist/validators/reference-validator.js +59 -8
- package/dist/validators/reference-validator.js.map +3 -3
- package/dist/validators/relationship-schema-validator.js +30 -2
- package/dist/validators/relationship-schema-validator.js.map +3 -3
- package/dist/viewer/README.md +6 -6
- package/dist/viewer/assets/main-B7AOQXXX.js +81 -0
- package/dist/viewer/assets/main-vdrFrG7q.css +1 -0
- package/dist/viewer/index.html +2 -2
- package/dist/viewer/manifest.json +10 -10
- package/dist/viewer/version.json +1 -1
- package/package.json +6 -3
- package/dist/viewer/assets/main-BRPZyc36.css +0 -1
- package/dist/viewer/assets/main-BrpLargo.js +0 -81
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
import ansis from "ansis";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { rm } from "fs/promises";
|
|
4
|
+
import { FarmManifest } from "../core/farm-manifest.js";
|
|
5
|
+
import { findFarmRoot } from "../utils/project-paths.js";
|
|
6
|
+
import { fileExists } from "../utils/file-io.js";
|
|
7
|
+
import { execSync, execFileSync } from "child_process";
|
|
8
|
+
import * as prompts from "@clack/prompts";
|
|
9
|
+
import { getErrorMessage, handleSuccess, handleInfo } from "../utils/errors.js";
|
|
10
|
+
import { isJson } from "../utils/globals.js";
|
|
11
|
+
import { isTelemetryEnabled, startSpan, endSpan } from "../telemetry/index.js";
|
|
12
|
+
import { Model } from "../core/model.js";
|
|
13
|
+
import { ComposedReferenceValidator } from "../validators/composed-reference-validator.js";
|
|
14
|
+
import { ValidationFormatter } from "../validators/validation-formatter.js";
|
|
15
|
+
import { FarmSyncEngine } from "../core/farm-sync-engine.js";
|
|
16
|
+
function isGitRepository(cwd) {
|
|
17
|
+
try {
|
|
18
|
+
return execSync("git rev-parse --git-dir", { cwd, stdio: "pipe" }), !0;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
const errorMsg = getErrorMessage(error);
|
|
21
|
+
return errorMsg.toLowerCase().includes("not a git repository") || process.env.DEBUG && console.warn(`Warning: Unexpected error checking git repository at ${cwd}: ${errorMsg}`), !1;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function farmInitCommand(options) {
|
|
25
|
+
const span = isTelemetryEnabled ? startSpan("farm.init") : null;
|
|
26
|
+
try {
|
|
27
|
+
const farmPath = process.cwd(), farmYamlPath = path.join(farmPath, "farm.yaml"), useJson = options.format === "json" || isJson();
|
|
28
|
+
if (await fileExists(farmYamlPath)) {
|
|
29
|
+
const message = "Farm already initialized in this directory";
|
|
30
|
+
useJson ? console.log(JSON.stringify({ status: "error", code: 1, message })) : console.error(ansis.red(`Error: ${message}`)), span && endSpan(span), process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
let farmName = options.name;
|
|
33
|
+
if (!farmName && process.stdin.isTTY) {
|
|
34
|
+
const result = await prompts.text({
|
|
35
|
+
message: "Farm name",
|
|
36
|
+
placeholder: "My Architecture Farm"
|
|
37
|
+
});
|
|
38
|
+
typeof result == "string" && (farmName = result);
|
|
39
|
+
}
|
|
40
|
+
farmName || (farmName = "Architecture Farm"), await FarmManifest.create(farmName, {
|
|
41
|
+
platform_view: options.platformView || !1
|
|
42
|
+
}).save(farmYamlPath);
|
|
43
|
+
try {
|
|
44
|
+
execSync("git init", { cwd: farmPath, stdio: useJson ? "pipe" : "inherit" }), execSync("git config user.email 'dr-farm@localhost'", {
|
|
45
|
+
cwd: farmPath,
|
|
46
|
+
stdio: "pipe"
|
|
47
|
+
}), execSync("git config user.name 'DR Farm'", {
|
|
48
|
+
cwd: farmPath,
|
|
49
|
+
stdio: "pipe"
|
|
50
|
+
}), execSync("git add farm.yaml", { cwd: farmPath, stdio: "pipe" }), execSync("git commit -m 'Initialize farm manifest'", {
|
|
51
|
+
cwd: farmPath,
|
|
52
|
+
stdio: useJson ? "pipe" : "inherit"
|
|
53
|
+
});
|
|
54
|
+
} catch (gitError) {
|
|
55
|
+
throw new Error(`Failed to initialize farm git repository: ${getErrorMessage(gitError)}`);
|
|
56
|
+
}
|
|
57
|
+
if (useJson)
|
|
58
|
+
console.log(JSON.stringify({ status: "ok", farmPath, farmName, platform_view: options.platformView || !1 }));
|
|
59
|
+
else {
|
|
60
|
+
const platformViewNote = options.platformView ? " (platform-view enabled)" : "";
|
|
61
|
+
handleSuccess(`Farm initialized: ${ansis.bold(farmName)}${platformViewNote}
|
|
62
|
+
Location: ${farmPath}`);
|
|
63
|
+
}
|
|
64
|
+
span && endSpan(span);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
options.format === "json" || isJson() ? console.log(JSON.stringify({
|
|
67
|
+
status: "error",
|
|
68
|
+
code: 1,
|
|
69
|
+
message: getErrorMessage(error)
|
|
70
|
+
})) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), span && endSpan(span), process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function farmAddCommand(name, options) {
|
|
74
|
+
const span = isTelemetryEnabled ? startSpan("farm.add", { "project.name": name }) : null, useJson = options.format === "json" || isJson();
|
|
75
|
+
try {
|
|
76
|
+
const farmRoot = await findFarmRoot();
|
|
77
|
+
if (!farmRoot)
|
|
78
|
+
throw new Error("Not in a farm directory. Run 'dr farm init' first.");
|
|
79
|
+
const farmYamlPath = path.join(farmRoot, "farm.yaml"), manifest = await FarmManifest.load(farmYamlPath);
|
|
80
|
+
if (manifest.getProject(name))
|
|
81
|
+
throw new Error(`Project '${name}' already exists in this farm`);
|
|
82
|
+
let codebasePath = options.codebase || name;
|
|
83
|
+
if (options.remote) {
|
|
84
|
+
const codebaseFullPath = path.join(farmRoot, codebasePath);
|
|
85
|
+
if (await fileExists(codebaseFullPath))
|
|
86
|
+
throw new Error(`Codebase directory '${codebasePath}' already exists`);
|
|
87
|
+
useJson || handleInfo(`Cloning ${options.remote} to ${codebasePath}...`);
|
|
88
|
+
try {
|
|
89
|
+
execFileSync("git", ["clone", options.remote, codebaseFullPath], {
|
|
90
|
+
stdio: useJson ? "pipe" : "inherit"
|
|
91
|
+
});
|
|
92
|
+
} catch (error) {
|
|
93
|
+
throw new Error(`Failed to clone repository: ${getErrorMessage(error)}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const modelFolder = `${name}-model`, modelFullPath = path.join(farmRoot, modelFolder);
|
|
97
|
+
if (await fileExists(modelFullPath) || (await Model.init(modelFullPath, {
|
|
98
|
+
name,
|
|
99
|
+
version: "0.1.0",
|
|
100
|
+
description: void 0,
|
|
101
|
+
author: void 0,
|
|
102
|
+
specVersion: (await import("../utils/spec-version.js")).getCliBundledSpecVersion(),
|
|
103
|
+
created: (/* @__PURE__ */ new Date()).toISOString()
|
|
104
|
+
}, { lazyLoad: !1 }), useJson || handleInfo(`Created and initialized model folder: ${modelFolder}`)), manifest.addProject(name, {
|
|
105
|
+
name,
|
|
106
|
+
source: codebasePath,
|
|
107
|
+
model: modelFolder,
|
|
108
|
+
remote: options.remote
|
|
109
|
+
}), await manifest.save(farmYamlPath), isGitRepository(farmRoot))
|
|
110
|
+
try {
|
|
111
|
+
execSync("git add farm.yaml", { cwd: farmRoot, stdio: "pipe" }), execFileSync("git", ["commit", "-m", `Add project: ${name}`], {
|
|
112
|
+
cwd: farmRoot,
|
|
113
|
+
stdio: useJson ? "pipe" : "inherit"
|
|
114
|
+
});
|
|
115
|
+
} catch (commitError) {
|
|
116
|
+
const errorMsg = getErrorMessage(commitError);
|
|
117
|
+
if (!errorMsg.includes("nothing to commit"))
|
|
118
|
+
throw new Error(`Failed to commit farm changes: ${errorMsg}`);
|
|
119
|
+
}
|
|
120
|
+
useJson ? console.log(JSON.stringify({
|
|
121
|
+
status: "ok",
|
|
122
|
+
project: name,
|
|
123
|
+
source: codebasePath,
|
|
124
|
+
model: modelFolder
|
|
125
|
+
})) : handleSuccess(`Project added to farm: ${ansis.bold(name)}
|
|
126
|
+
Codebase: ${codebasePath}
|
|
127
|
+
Model: ${modelFolder}`), span && endSpan(span);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
useJson ? console.log(JSON.stringify({
|
|
130
|
+
status: "error",
|
|
131
|
+
code: 1,
|
|
132
|
+
message: getErrorMessage(error)
|
|
133
|
+
})) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), span && endSpan(span), process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async function farmRemoveCommand(name, options) {
|
|
137
|
+
const span = isTelemetryEnabled ? startSpan("farm.remove", { "project.name": name }) : null, useJson = options.format === "json" || isJson();
|
|
138
|
+
try {
|
|
139
|
+
const farmRoot = await findFarmRoot();
|
|
140
|
+
if (!farmRoot)
|
|
141
|
+
throw new Error("Not in a farm directory.");
|
|
142
|
+
const farmYamlPath = path.join(farmRoot, "farm.yaml"), manifest = await FarmManifest.load(farmYamlPath), project = manifest.getProject(name);
|
|
143
|
+
if (!project)
|
|
144
|
+
throw new Error(`Project '${name}' not found in farm`);
|
|
145
|
+
if (options.deleteModel) {
|
|
146
|
+
const modelFullPath = path.resolve(path.join(farmRoot, project.model)), farmRootResolved = path.resolve(farmRoot);
|
|
147
|
+
if (!modelFullPath.startsWith(farmRootResolved + path.sep))
|
|
148
|
+
throw new Error(`Invalid model path: would escape farm root. Path: ${project.model}`);
|
|
149
|
+
if (await fileExists(modelFullPath))
|
|
150
|
+
try {
|
|
151
|
+
await rm(modelFullPath, { recursive: !0, force: !1 }), useJson || handleInfo(`Deleted model folder: ${project.model}`);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
throw new Error(`Failed to delete model folder: ${getErrorMessage(error)}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (manifest.removeProject(name), await manifest.save(farmYamlPath), isGitRepository(farmRoot))
|
|
157
|
+
try {
|
|
158
|
+
execSync("git add farm.yaml", { cwd: farmRoot, stdio: "pipe" }), execFileSync("git", ["commit", "-m", `Remove project: ${name}`], {
|
|
159
|
+
cwd: farmRoot,
|
|
160
|
+
stdio: useJson ? "pipe" : "inherit"
|
|
161
|
+
});
|
|
162
|
+
} catch (commitError) {
|
|
163
|
+
const errorMsg = getErrorMessage(commitError);
|
|
164
|
+
if (!errorMsg.includes("nothing to commit"))
|
|
165
|
+
throw new Error(`Failed to commit farm changes: ${errorMsg}`);
|
|
166
|
+
}
|
|
167
|
+
useJson ? console.log(JSON.stringify({
|
|
168
|
+
status: "ok",
|
|
169
|
+
project: name,
|
|
170
|
+
modelDeleted: options.deleteModel ?? !1
|
|
171
|
+
})) : handleSuccess(`Project removed from farm: ${ansis.bold(name)}`), span && endSpan(span);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
useJson ? console.log(JSON.stringify({
|
|
174
|
+
status: "error",
|
|
175
|
+
code: 1,
|
|
176
|
+
message: getErrorMessage(error)
|
|
177
|
+
})) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), span && endSpan(span), process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async function farmStatusCommand(options) {
|
|
181
|
+
const span = isTelemetryEnabled ? startSpan("farm.status") : null;
|
|
182
|
+
try {
|
|
183
|
+
const farmRoot = await findFarmRoot();
|
|
184
|
+
if (!farmRoot)
|
|
185
|
+
throw new Error("Not in a farm directory.");
|
|
186
|
+
const farmYamlPath = path.join(farmRoot, "farm.yaml"), manifest = await FarmManifest.load(farmYamlPath), projects = manifest.getAllProjects(), engine = new FarmSyncEngine(farmRoot), projectsWithStatus = await Promise.all(projects.map(async (p) => {
|
|
187
|
+
try {
|
|
188
|
+
const syncStatePath = path.join(farmRoot, p.model, ".farm-sync.yaml"), syncState = await (await import("../core/farm-sync-state.js")).FarmSyncState.loadOrCreate(syncStatePath, p.name), currentCommit = await engine.getCurrentCommit(p.source), lastSyncCommit = syncState.lastSyncCommit, hasPendingChanges = !!(lastSyncCommit && lastSyncCommit !== currentCommit);
|
|
189
|
+
return {
|
|
190
|
+
name: p.name,
|
|
191
|
+
source: p.source,
|
|
192
|
+
model: p.model,
|
|
193
|
+
remote: p.remote,
|
|
194
|
+
last_sync_commit: lastSyncCommit,
|
|
195
|
+
current_commit: currentCommit,
|
|
196
|
+
has_pending_changes: hasPendingChanges
|
|
197
|
+
};
|
|
198
|
+
} catch (error) {
|
|
199
|
+
const errorMsg = getErrorMessage(error);
|
|
200
|
+
return !errorMsg.includes("not found") && !errorMsg.includes("does not exist") && !errorMsg.includes("ENOENT") && console.error(`Warning: Error reading sync state for ${p.name}: ${errorMsg}`), {
|
|
201
|
+
name: p.name,
|
|
202
|
+
source: p.source,
|
|
203
|
+
model: p.model,
|
|
204
|
+
remote: p.remote,
|
|
205
|
+
last_sync_commit: void 0,
|
|
206
|
+
current_commit: void 0,
|
|
207
|
+
has_pending_changes: !1
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
}));
|
|
211
|
+
options.format === "json" || isJson() ? console.log(JSON.stringify({
|
|
212
|
+
status: "ok",
|
|
213
|
+
farm: {
|
|
214
|
+
name: manifest.name,
|
|
215
|
+
path: farmRoot,
|
|
216
|
+
created: manifest.created,
|
|
217
|
+
modified: manifest.modified,
|
|
218
|
+
platform_view: manifest.platform_view
|
|
219
|
+
},
|
|
220
|
+
projects: projectsWithStatus,
|
|
221
|
+
project_count: projects.length
|
|
222
|
+
})) : (console.log(ansis.bold(`Farm: ${manifest.name}`)), console.log(`Location: ${farmRoot}`), manifest.platform_view && console.log(`Platform-View: ${ansis.green("enabled")}`), console.log(`Projects: ${projects.length}
|
|
223
|
+
`), projects.length === 0 ? console.log(ansis.dim("No projects registered yet.")) : projectsWithStatus.forEach((project) => {
|
|
224
|
+
console.log(` ${ansis.cyan(project.name)}`), console.log(` Codebase: ${project.source}`), console.log(` Model: ${project.model}`), project.remote && console.log(` Remote: ${project.remote}`), project.has_pending_changes ? console.log(` Status: ${ansis.yellow("\u26A0 Pending changes")}`) : project.last_sync_commit ? console.log(` Status: ${ansis.green("\u2713 Up to date")}`) : console.log(` Status: ${ansis.dim("Not synced yet")}`);
|
|
225
|
+
})), span && endSpan(span);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
options.format === "json" || isJson() ? console.log(JSON.stringify({
|
|
228
|
+
status: "error",
|
|
229
|
+
code: 2,
|
|
230
|
+
message: getErrorMessage(error)
|
|
231
|
+
})) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), span && endSpan(span), process.exit(2);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async function farmValidateCommand(options) {
|
|
235
|
+
const span = isTelemetryEnabled ? startSpan("farm.validate") : null;
|
|
236
|
+
try {
|
|
237
|
+
const farmRoot = await findFarmRoot();
|
|
238
|
+
if (!farmRoot)
|
|
239
|
+
throw new Error("Not in a farm directory. Run 'dr farm init' first.");
|
|
240
|
+
const farmYamlPath = path.join(farmRoot, "farm.yaml"), farmManifest = await FarmManifest.load(farmYamlPath), useJson = options.format === "json" || isJson();
|
|
241
|
+
let projectsToValidate = farmManifest.getAllProjects(), isPlatformViewValidation = !1;
|
|
242
|
+
if (options.project) {
|
|
243
|
+
const project = farmManifest.getProject(options.project);
|
|
244
|
+
if (!project)
|
|
245
|
+
throw new Error(`Project '${options.project}' not found in farm`);
|
|
246
|
+
projectsToValidate = [project], farmManifest.isPlatformViewEnabled() && options.project === "platform-view" ? isPlatformViewValidation = !0 : options.project === "platform-view" && !farmManifest.isPlatformViewEnabled() && !useJson && !options.quiet && handleInfo("Note: Validating platform-view project, but platform-view is not enabled on this farm");
|
|
247
|
+
}
|
|
248
|
+
if (projectsToValidate.length === 0)
|
|
249
|
+
throw new Error("No projects found in farm");
|
|
250
|
+
const composedValidator = await ComposedReferenceValidator.fromFarm(farmRoot);
|
|
251
|
+
if (useJson)
|
|
252
|
+
console.log(JSON.stringify({ status: "validating", projects: projectsToValidate.length, platform_view: isPlatformViewValidation }));
|
|
253
|
+
else if (!options.quiet)
|
|
254
|
+
if (options.project) {
|
|
255
|
+
const platformViewNote = isPlatformViewValidation ? " (platform-view aggregation)" : "";
|
|
256
|
+
handleInfo(`Validating project '${options.project}'${platformViewNote} with farm-aware references...`);
|
|
257
|
+
} else
|
|
258
|
+
handleInfo(`Validating ${projectsToValidate.length} farm project(s) with farm-aware references...`);
|
|
259
|
+
const allResults = [], reportData = [];
|
|
260
|
+
for (const project of projectsToValidate) {
|
|
261
|
+
const modelPath = path.join(farmRoot, project.model);
|
|
262
|
+
if (!await fileExists(modelPath)) {
|
|
263
|
+
const error = `Model folder not found for project '${project.name}' at ${modelPath}`;
|
|
264
|
+
useJson ? console.log(JSON.stringify({
|
|
265
|
+
status: "error",
|
|
266
|
+
code: 2,
|
|
267
|
+
project: project.name,
|
|
268
|
+
message: error
|
|
269
|
+
})) : console.error(ansis.red(`Error: ${error}`)), span && endSpan(span), process.exit(2);
|
|
270
|
+
}
|
|
271
|
+
let drModelPath = modelPath;
|
|
272
|
+
const manifestPath = path.join(modelPath, "manifest.yaml");
|
|
273
|
+
await fileExists(manifestPath) && (drModelPath = manifestPath);
|
|
274
|
+
const model = await Model.load(drModelPath);
|
|
275
|
+
let platformViewInfo;
|
|
276
|
+
if (isPlatformViewValidation) {
|
|
277
|
+
const declaredModels = model.manifest.models || {}, declaredModelNames = Object.keys(declaredModels);
|
|
278
|
+
if (platformViewInfo = {
|
|
279
|
+
declared_models: declaredModelNames,
|
|
280
|
+
missing_models: []
|
|
281
|
+
}, declaredModelNames.length === 0)
|
|
282
|
+
!useJson && !options.quiet && handleInfo(" Platform-view project has no declared external models");
|
|
283
|
+
else {
|
|
284
|
+
!useJson && !options.quiet && handleInfo(` Platform-view is declaring external models: ${declaredModelNames.join(", ")}`);
|
|
285
|
+
const missingModels = [];
|
|
286
|
+
for (const declaredModelName of declaredModelNames)
|
|
287
|
+
farmManifest.getProject(declaredModelName) || missingModels.push(declaredModelName);
|
|
288
|
+
platformViewInfo.missing_models = missingModels, missingModels.length > 0 && (useJson || console.error(ansis.yellow(` \u26A0 Warning: The following declared external models are not registered in the farm: ${missingModels.join(", ")}`)));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const result = await composedValidator.validateModel(model);
|
|
292
|
+
allResults.push({ project: project.name, result });
|
|
293
|
+
const formatted = ValidationFormatter.format(result, model, {
|
|
294
|
+
verbose: options.verbose,
|
|
295
|
+
quiet: options.quiet
|
|
296
|
+
}), reportItem = {
|
|
297
|
+
project: project.name,
|
|
298
|
+
valid: result.isValid(),
|
|
299
|
+
errors: result.errors.map((e) => ({
|
|
300
|
+
message: e.message,
|
|
301
|
+
layer: e.layer,
|
|
302
|
+
elementId: e.elementId,
|
|
303
|
+
location: e.location
|
|
304
|
+
})),
|
|
305
|
+
warnings: result.warnings.map((w) => ({
|
|
306
|
+
message: w.message,
|
|
307
|
+
layer: w.layer,
|
|
308
|
+
elementId: w.elementId,
|
|
309
|
+
location: w.location
|
|
310
|
+
})),
|
|
311
|
+
formatted
|
|
312
|
+
};
|
|
313
|
+
if (platformViewInfo && (reportItem.platform_view_info = platformViewInfo), reportData.push(reportItem), options.quiet || (console.log(ansis.bold(`
|
|
314
|
+
Project: ${project.name}`)), console.log(formatted)), !result.isValid() && options.project)
|
|
315
|
+
throw new Error(`Validation failed for project '${project.name}'`);
|
|
316
|
+
}
|
|
317
|
+
const allValid = allResults.every((r) => r.result.isValid());
|
|
318
|
+
if (options.output) {
|
|
319
|
+
const outputExt = path.extname(options.output).toLowerCase();
|
|
320
|
+
if (outputExt === ".json") {
|
|
321
|
+
const jsonReport = {
|
|
322
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
323
|
+
farm_root: farmRoot,
|
|
324
|
+
all_valid: allValid,
|
|
325
|
+
projects: reportData.map((r) => {
|
|
326
|
+
const projectData = {
|
|
327
|
+
project: r.project,
|
|
328
|
+
valid: r.valid,
|
|
329
|
+
errors: r.errors,
|
|
330
|
+
warnings: r.warnings
|
|
331
|
+
};
|
|
332
|
+
return r.platform_view_info && (projectData.platform_view_info = r.platform_view_info), projectData;
|
|
333
|
+
})
|
|
334
|
+
};
|
|
335
|
+
await (await import("fs/promises")).writeFile(options.output, JSON.stringify(jsonReport, null, 2), "utf-8");
|
|
336
|
+
} else if (outputExt === ".md") {
|
|
337
|
+
let mdContent = `# Farm Validation Report
|
|
338
|
+
|
|
339
|
+
`;
|
|
340
|
+
mdContent += `**Date**: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
341
|
+
`, mdContent += `**Farm Root**: ${farmRoot}
|
|
342
|
+
`, mdContent += `**Overall Status**: ${allValid ? "\u2705 Valid" : "\u274C Invalid"}
|
|
343
|
+
|
|
344
|
+
`, mdContent += `## Projects
|
|
345
|
+
|
|
346
|
+
`;
|
|
347
|
+
for (const r of reportData) {
|
|
348
|
+
if (mdContent += `### ${r.project}
|
|
349
|
+
|
|
350
|
+
`, mdContent += `**Status**: ${r.valid ? "\u2705 Valid" : "\u274C Invalid"}
|
|
351
|
+
|
|
352
|
+
`, r.errors.length > 0) {
|
|
353
|
+
mdContent += `#### Errors (${r.errors.length})
|
|
354
|
+
|
|
355
|
+
`;
|
|
356
|
+
for (const err of r.errors)
|
|
357
|
+
mdContent += `- ${err.message}
|
|
358
|
+
`;
|
|
359
|
+
mdContent += `
|
|
360
|
+
`;
|
|
361
|
+
}
|
|
362
|
+
if (r.warnings.length > 0) {
|
|
363
|
+
mdContent += `#### Warnings (${r.warnings.length})
|
|
364
|
+
|
|
365
|
+
`;
|
|
366
|
+
for (const warn of r.warnings)
|
|
367
|
+
mdContent += `- ${warn.message}
|
|
368
|
+
`;
|
|
369
|
+
mdContent += `
|
|
370
|
+
`;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
await (await import("fs/promises")).writeFile(options.output, mdContent, "utf-8");
|
|
374
|
+
} else
|
|
375
|
+
throw new Error(`Unsupported output format: ${outputExt}. Use .json or .md`);
|
|
376
|
+
options.quiet || handleInfo(`Report written to ${options.output}`);
|
|
377
|
+
}
|
|
378
|
+
useJson && console.log(JSON.stringify({
|
|
379
|
+
status: "ok",
|
|
380
|
+
projects: allResults.map((r) => ({
|
|
381
|
+
name: r.project,
|
|
382
|
+
valid: r.result.isValid(),
|
|
383
|
+
errors: r.result.errors.length,
|
|
384
|
+
warnings: r.result.warnings.length
|
|
385
|
+
})),
|
|
386
|
+
all_valid: allValid,
|
|
387
|
+
output: options.output ? path.resolve(options.output) : void 0
|
|
388
|
+
})), allValid || (span && endSpan(span), process.exit(1)), options.quiet || handleSuccess(options.project ? `Project '${options.project}' validated successfully` : "All farm projects validated successfully"), span && endSpan(span);
|
|
389
|
+
} catch (error) {
|
|
390
|
+
options.format === "json" || isJson() ? console.log(JSON.stringify({
|
|
391
|
+
status: "error",
|
|
392
|
+
code: 2,
|
|
393
|
+
message: getErrorMessage(error)
|
|
394
|
+
})) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), span && endSpan(span), process.exit(2);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
async function farmPullCommand(options) {
|
|
398
|
+
const span = isTelemetryEnabled ? startSpan("farm.pull") : null;
|
|
399
|
+
try {
|
|
400
|
+
const farmRoot = await findFarmRoot();
|
|
401
|
+
if (!farmRoot)
|
|
402
|
+
throw new Error("Not in a farm directory. Run 'dr farm init' first.");
|
|
403
|
+
const farmYamlPath = path.join(farmRoot, "farm.yaml"), manifest = await FarmManifest.load(farmYamlPath);
|
|
404
|
+
let projectsToPull = manifest.getAllProjects();
|
|
405
|
+
if (options.project) {
|
|
406
|
+
const project = manifest.getProject(options.project);
|
|
407
|
+
if (!project)
|
|
408
|
+
throw new Error(`Project '${options.project}' not found in farm`);
|
|
409
|
+
projectsToPull = [project];
|
|
410
|
+
}
|
|
411
|
+
if (projectsToPull.length === 0)
|
|
412
|
+
throw new Error("No projects found in farm");
|
|
413
|
+
const useJson = options.format === "json" || isJson();
|
|
414
|
+
!useJson && !options.verbose && handleInfo(`Pulling ${projectsToPull.length} project(s)...`);
|
|
415
|
+
const engine = new FarmSyncEngine(farmRoot), results = [];
|
|
416
|
+
for (const project of projectsToPull)
|
|
417
|
+
try {
|
|
418
|
+
const commit = await engine.pullCodebase(project.source);
|
|
419
|
+
results.push({
|
|
420
|
+
project: project.name,
|
|
421
|
+
status: "success",
|
|
422
|
+
commit
|
|
423
|
+
}), options.verbose && !useJson && handleInfo(` \u2713 ${project.name}: ${commit.substring(0, 8)}`);
|
|
424
|
+
} catch (error) {
|
|
425
|
+
results.push({
|
|
426
|
+
project: project.name,
|
|
427
|
+
status: "error",
|
|
428
|
+
message: getErrorMessage(error)
|
|
429
|
+
}), useJson || console.error(ansis.red(` \u2717 ${project.name}: ${getErrorMessage(error)}`));
|
|
430
|
+
}
|
|
431
|
+
const allSucceeded = results.every((r) => r.status === "success");
|
|
432
|
+
useJson ? console.log(JSON.stringify({
|
|
433
|
+
status: allSucceeded ? "ok" : "partial",
|
|
434
|
+
code: allSucceeded ? 0 : 1,
|
|
435
|
+
projects: results,
|
|
436
|
+
pulled: results.filter((r) => r.status === "success").length,
|
|
437
|
+
failed: results.filter((r) => r.status === "error").length
|
|
438
|
+
})) : allSucceeded ? handleSuccess(`Pulled ${projectsToPull.length} project(s) successfully`) : console.error(ansis.red("Pull failed for some projects")), span && endSpan(span), allSucceeded || process.exit(1);
|
|
439
|
+
} catch (error) {
|
|
440
|
+
options.format === "json" || isJson() ? console.log(JSON.stringify({
|
|
441
|
+
status: "error",
|
|
442
|
+
code: 2,
|
|
443
|
+
message: getErrorMessage(error)
|
|
444
|
+
})) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), span && endSpan(span), process.exit(2);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
async function farmSyncCommand(options) {
|
|
448
|
+
const span = isTelemetryEnabled ? startSpan("farm.sync") : null;
|
|
449
|
+
try {
|
|
450
|
+
const farmRoot = await findFarmRoot();
|
|
451
|
+
if (!farmRoot)
|
|
452
|
+
throw new Error("Not in a farm directory. Run 'dr farm init' first.");
|
|
453
|
+
const farmYamlPath = path.join(farmRoot, "farm.yaml"), manifest = await FarmManifest.load(farmYamlPath);
|
|
454
|
+
let projectsToSync = manifest.getAllProjects();
|
|
455
|
+
if (options.project) {
|
|
456
|
+
const project = manifest.getProject(options.project);
|
|
457
|
+
if (!project)
|
|
458
|
+
throw new Error(`Project '${options.project}' not found in farm`);
|
|
459
|
+
projectsToSync = [project];
|
|
460
|
+
}
|
|
461
|
+
if (projectsToSync.length === 0)
|
|
462
|
+
throw new Error("No projects found in farm");
|
|
463
|
+
const concurrency = options.concurrency ? Math.max(1, parseInt(options.concurrency, 10)) : 1;
|
|
464
|
+
if (isNaN(concurrency))
|
|
465
|
+
throw new Error("Concurrency must be a positive number");
|
|
466
|
+
if (options.autoCommit && concurrency > 1)
|
|
467
|
+
throw new Error("Cannot use --auto-commit with --concurrency > 1: concurrent git operations would cause race conditions. Use --concurrency 1 for auto-commit, or remove --auto-commit for parallel processing.");
|
|
468
|
+
const useJson = options.format === "json" || isJson();
|
|
469
|
+
!useJson && !options.verbose && handleInfo(`Syncing ${projectsToSync.length} project(s) (concurrency: ${concurrency})...`);
|
|
470
|
+
const allResults = [], processSyncProject = async (project) => {
|
|
471
|
+
try {
|
|
472
|
+
const modelPath = path.join(farmRoot, project.model);
|
|
473
|
+
if (!await fileExists(modelPath))
|
|
474
|
+
throw new Error(`Model folder not found for project '${project.name}' at ${modelPath}`);
|
|
475
|
+
let drModelPath = modelPath;
|
|
476
|
+
const manifestPath = path.join(modelPath, "manifest.yaml");
|
|
477
|
+
await fileExists(manifestPath) && (drModelPath = manifestPath);
|
|
478
|
+
try {
|
|
479
|
+
const model = await Model.load(drModelPath), result = await new FarmSyncEngine(farmRoot, model).syncProject(project, {
|
|
480
|
+
verbose: options.verbose,
|
|
481
|
+
dryRun: options.dryRun,
|
|
482
|
+
force: options.force
|
|
483
|
+
}), resultEntry = {
|
|
484
|
+
project: project.name,
|
|
485
|
+
status: result.status === "failed" ? "error" : result.status,
|
|
486
|
+
change_count: result.changeCount,
|
|
487
|
+
changeset_id: result.changesetId,
|
|
488
|
+
files_changed: result.filesChanged,
|
|
489
|
+
ambiguities: result.ambiguities.length,
|
|
490
|
+
commits_before: result.commitsBefore,
|
|
491
|
+
commits_after: result.commitsAfter
|
|
492
|
+
};
|
|
493
|
+
if (options.autoCommit && result.changesetId && result.changeCount > 0 && !options.dryRun)
|
|
494
|
+
try {
|
|
495
|
+
const projectModelPath = path.join(farmRoot, project.model), commitResult = await new (await import("../core/staging-area.js")).StagingAreaManager(projectModelPath, model).commit(model, result.changesetId);
|
|
496
|
+
if (resultEntry.auto_committed = !0, resultEntry.committed_changes = commitResult.committed, isGitRepository(farmRoot))
|
|
497
|
+
try {
|
|
498
|
+
execFileSync("git", ["add", project.model], { cwd: farmRoot, stdio: "pipe" }), execFileSync("git", ["commit", "-m", `Sync: ${result.changesetId} - ${commitResult.committed} change(s)`], { cwd: farmRoot, stdio: "pipe" });
|
|
499
|
+
} catch (gitCommitError) {
|
|
500
|
+
if (!getErrorMessage(gitCommitError).includes("nothing to commit"))
|
|
501
|
+
throw gitCommitError;
|
|
502
|
+
}
|
|
503
|
+
options.verbose && !useJson && handleInfo(` Auto-committed: ${commitResult.committed} change(s)`);
|
|
504
|
+
} catch (commitError) {
|
|
505
|
+
resultEntry.auto_committed = !1, resultEntry.commit_error = getErrorMessage(commitError), resultEntry.status = "error", useJson || handleInfo(` Auto-commit failed: ${getErrorMessage(commitError)}`);
|
|
506
|
+
}
|
|
507
|
+
if (allResults.push(resultEntry), options.verbose && !useJson) {
|
|
508
|
+
handleInfo(`
|
|
509
|
+
Project: ${project.name}`);
|
|
510
|
+
const statusEmoji = result.status === "success" ? "\u2713" : result.status === "partial" ? "\u26A0" : "\u2717", statusText = result.status === "success" ? "Success" : result.status === "partial" ? "Partial" : "Failed";
|
|
511
|
+
handleInfo(` Status: ${statusEmoji} ${statusText}`), handleInfo(` Commits: ${result.commitsBefore}...${result.commitsAfter}`), handleInfo(` Files changed: +${result.filesChanged.added.length} ~${result.filesChanged.modified.length} -${result.filesChanged.deleted.length}`), handleInfo(` Staged changes: ${result.changeCount}`), result.changesetId && handleInfo(` Changeset: ${result.changesetId}`), result.ambiguities.length > 0 && handleInfo(` Ambiguities: ${result.ambiguities.length} (flagged for review)`);
|
|
512
|
+
for (const note of result.notes)
|
|
513
|
+
console.log(` ${note}`);
|
|
514
|
+
}
|
|
515
|
+
} catch (error) {
|
|
516
|
+
allResults.push({
|
|
517
|
+
project: project.name,
|
|
518
|
+
status: "error",
|
|
519
|
+
message: getErrorMessage(error)
|
|
520
|
+
}), useJson || console.error(ansis.red(`\u2717 ${project.name}: ${getErrorMessage(error)}`));
|
|
521
|
+
}
|
|
522
|
+
} catch (error) {
|
|
523
|
+
allResults.push({
|
|
524
|
+
project: project.name,
|
|
525
|
+
status: "error",
|
|
526
|
+
message: getErrorMessage(error)
|
|
527
|
+
}), useJson || console.error(ansis.red(`\u2717 ${project.name}: ${getErrorMessage(error)}`));
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
if (concurrency === 1)
|
|
531
|
+
for (const project of projectsToSync)
|
|
532
|
+
await processSyncProject(project);
|
|
533
|
+
else {
|
|
534
|
+
const queue = [...projectsToSync], active = [];
|
|
535
|
+
for (; queue.length > 0 || active.length > 0; ) {
|
|
536
|
+
for (; active.length < concurrency && queue.length > 0; ) {
|
|
537
|
+
const project = queue.shift(), wrapped = processSyncProject(project).then(() => {
|
|
538
|
+
const idx = active.indexOf(wrapped);
|
|
539
|
+
idx !== -1 && active.splice(idx, 1);
|
|
540
|
+
});
|
|
541
|
+
active.push(wrapped);
|
|
542
|
+
}
|
|
543
|
+
active.length > 0 && await Promise.race(active);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
const allSucceeded = allResults.every((r) => r.status === "success" || r.status === "partial");
|
|
547
|
+
if (useJson)
|
|
548
|
+
console.log(JSON.stringify({
|
|
549
|
+
status: allSucceeded ? "ok" : "error",
|
|
550
|
+
code: allSucceeded ? 0 : 1,
|
|
551
|
+
projects: allResults,
|
|
552
|
+
synced: allResults.filter((r) => r.status === "success").length,
|
|
553
|
+
failed: allResults.filter((r) => r.status === "error").length,
|
|
554
|
+
auto_committed: options.autoCommit
|
|
555
|
+
}));
|
|
556
|
+
else {
|
|
557
|
+
const synced = allResults.filter((r) => r.status === "success").length;
|
|
558
|
+
allSucceeded && handleSuccess(`Synced ${synced}/${projectsToSync.length} project(s)`);
|
|
559
|
+
}
|
|
560
|
+
if (options.output) {
|
|
561
|
+
const { writeFile } = await import("../utils/file-io.js");
|
|
562
|
+
path.extname(options.output).toLowerCase() === ".json" && await writeFile(options.output, JSON.stringify({
|
|
563
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
564
|
+
farm_root: farmRoot,
|
|
565
|
+
projects: allResults
|
|
566
|
+
}, null, 2));
|
|
567
|
+
}
|
|
568
|
+
span && endSpan(span), allSucceeded || process.exit(1);
|
|
569
|
+
} catch (error) {
|
|
570
|
+
options.format === "json" || isJson() ? console.log(JSON.stringify({
|
|
571
|
+
status: "error",
|
|
572
|
+
code: 2,
|
|
573
|
+
message: getErrorMessage(error)
|
|
574
|
+
})) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), span && endSpan(span), process.exit(2);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
function farmCommands(program) {
|
|
578
|
+
const farmGroup = program.command("farm").description("Manage model farms");
|
|
579
|
+
farmGroup.command("init").description("Initialize a new farm").option("--name <name>", "Farm name").option("--platform-view", "Enable platform-view support for cross-model aggregation").option("--format <format>", "Output format: text, json", "text").addHelpText("after", `
|
|
580
|
+
Examples:
|
|
581
|
+
$ dr farm init
|
|
582
|
+
$ dr farm init --name "My Architecture Farm"
|
|
583
|
+
$ dr farm init --platform-view
|
|
584
|
+
$ dr farm init --format json
|
|
585
|
+
|
|
586
|
+
Notes:
|
|
587
|
+
When using --platform-view, create a project named exactly "platform-view" to serve as
|
|
588
|
+
the aggregation hub. When validating this project with 'dr farm validate --project platform-view',
|
|
589
|
+
the CLI will automatically resolve cross-model references from all farm projects.`).action(async (options) => {
|
|
590
|
+
await farmInitCommand(options);
|
|
591
|
+
}), farmGroup.command("add <name>").description("Add a project to the farm").option("--remote <url>", "Git remote URL to clone").option("--codebase <path>", "Path to codebase folder (defaults to project name)").option("--format <format>", "Output format: text, json", "text").addHelpText("after", `
|
|
592
|
+
Examples:
|
|
593
|
+
$ dr farm add my-service
|
|
594
|
+
$ dr farm add my-service --remote https://github.com/org/my-service.git
|
|
595
|
+
$ dr farm add my-service --codebase ./services/my-service
|
|
596
|
+
$ dr farm add my-service --format json`).action(async (name, options) => {
|
|
597
|
+
await farmAddCommand(name, options);
|
|
598
|
+
}), farmGroup.command("remove <name>").description("Remove a project from the farm").option("--delete-model", "Also delete the model folder").option("--format <format>", "Output format: text, json", "text").addHelpText("after", `
|
|
599
|
+
Examples:
|
|
600
|
+
$ dr farm remove my-service
|
|
601
|
+
$ dr farm remove my-service --delete-model
|
|
602
|
+
$ dr farm remove my-service --format json`).action(async (name, options) => {
|
|
603
|
+
await farmRemoveCommand(name, options);
|
|
604
|
+
}), farmGroup.command("status").description("Show farm status and registered projects").option("--format <format>", "Output format: text, json", "text").addHelpText("after", `
|
|
605
|
+
Examples:
|
|
606
|
+
$ dr farm status
|
|
607
|
+
$ dr farm status --format json`).action(async (options) => {
|
|
608
|
+
await farmStatusCommand(options);
|
|
609
|
+
}), farmGroup.command("validate").description("Validate farm projects using composed scope (cross-model references)").option("--project <name>", "Validate only a specific project (default: all)").option("--verbose", "Show verbose validation output").option("--quiet", "Suppress success messages").option("--output <path>", "Export validation report to file (JSON/Markdown)").option("--format <format>", "Output format: text, json", "text").addHelpText("after", `
|
|
610
|
+
Examples:
|
|
611
|
+
$ dr farm validate
|
|
612
|
+
$ dr farm validate --project my-service
|
|
613
|
+
$ dr farm validate --verbose
|
|
614
|
+
$ dr farm validate --format json
|
|
615
|
+
$ dr farm validate --output report.json
|
|
616
|
+
|
|
617
|
+
Notes:
|
|
618
|
+
For platform-view aggregation: Use 'dr farm validate --project platform-view' to validate
|
|
619
|
+
a platform-view project. The project name must be exactly 'platform-view' and the farm
|
|
620
|
+
must have platform-view enabled (set during 'dr farm init --platform-view').`).action(async (options) => {
|
|
621
|
+
await farmValidateCommand(options);
|
|
622
|
+
}), farmGroup.command("pull").description("Pull latest changes from remote for farm projects").option("--project <name>", "Pull only a specific project (default: all)").option("--verbose", "Show verbose output").option("--format <format>", "Output format: text, json", "text").addHelpText("after", `
|
|
623
|
+
Examples:
|
|
624
|
+
$ dr farm pull
|
|
625
|
+
$ dr farm pull --project my-service
|
|
626
|
+
$ dr farm pull --verbose
|
|
627
|
+
$ dr farm pull --format json`).action(async (options) => {
|
|
628
|
+
await farmPullCommand(options);
|
|
629
|
+
}), farmGroup.command("sync").description("Sync farm projects with their models (generates staged changesets)").option("--project <name>", "Sync only a specific project (default: all)").option("--verbose", "Show verbose output").option("--dry-run", "Preview without creating changesets").option("--force", "Proceed despite ambiguous file-to-element mappings").option("--output <path>", "Export sync report to file (JSON)").option("--format <format>", "Output format: text, json", "text").option("--auto-commit", "Automatically commit changes (bypasses staged review)").option("--concurrency <n>", "Process multiple projects in parallel (default: 1)", "1").addHelpText("after", `
|
|
630
|
+
Examples:
|
|
631
|
+
$ dr farm sync
|
|
632
|
+
$ dr farm sync --project my-service
|
|
633
|
+
$ dr farm sync --verbose --output sync-report.json
|
|
634
|
+
$ dr farm sync --dry-run
|
|
635
|
+
$ dr farm sync --format json --auto-commit
|
|
636
|
+
$ dr farm sync --concurrency 4
|
|
637
|
+
|
|
638
|
+
Automation (cron/CI):
|
|
639
|
+
$ dr farm sync --format json --auto-commit --output sync-report.json
|
|
640
|
+
$ dr farm sync --format json --concurrency 4 --output sync-report.json
|
|
641
|
+
|
|
642
|
+
Note: --auto-commit and --concurrency > 1 cannot be used together (race condition risk).
|
|
643
|
+
Use --concurrency 1 (default) with --auto-commit for automated syncs.`).action(async (options) => {
|
|
644
|
+
await farmSyncCommand(options);
|
|
645
|
+
}), farmGroup.showSuggestionAfterError();
|
|
646
|
+
}
|
|
647
|
+
export {
|
|
648
|
+
farmAddCommand,
|
|
649
|
+
farmCommands,
|
|
650
|
+
farmInitCommand,
|
|
651
|
+
farmPullCommand,
|
|
652
|
+
farmRemoveCommand,
|
|
653
|
+
farmStatusCommand,
|
|
654
|
+
farmSyncCommand,
|
|
655
|
+
farmValidateCommand
|
|
656
|
+
};
|
|
657
|
+
//# sourceMappingURL=farm.js.map
|