youmd 0.10.0 → 0.10.2
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/dist/commands/brain.d.ts +25 -0
- package/dist/commands/brain.d.ts.map +1 -0
- package/dist/commands/brain.js +683 -0
- package/dist/commands/brain.js.map +1 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +4 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/machine.d.ts +3 -0
- package/dist/commands/machine.d.ts.map +1 -1
- package/dist/commands/machine.js +73 -4
- package/dist/commands/machine.js.map +1 -1
- package/dist/commands/orchestrate.d.ts.map +1 -1
- package/dist/commands/orchestrate.js +4 -3
- package/dist/commands/orchestrate.js.map +1 -1
- package/dist/commands/project.d.ts.map +1 -1
- package/dist/commands/project.js +24 -0
- package/dist/commands/project.js.map +1 -1
- package/dist/commands/remote.d.ts.map +1 -1
- package/dist/commands/remote.js +42 -13
- package/dist/commands/remote.js.map +1 -1
- package/dist/commands/skill.d.ts.map +1 -1
- package/dist/commands/skill.js +2 -0
- package/dist/commands/skill.js.map +1 -1
- package/dist/commands/tasks.d.ts +23 -3
- package/dist/commands/tasks.d.ts.map +1 -1
- package/dist/commands/tasks.js +179 -49
- package/dist/commands/tasks.js.map +1 -1
- package/dist/generated/capability-registry.d.ts +1096 -0
- package/dist/generated/capability-registry.d.ts.map +1 -0
- package/dist/generated/capability-registry.js +1204 -0
- package/dist/generated/capability-registry.js.map +1 -0
- package/dist/index.js +42 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/api.d.ts +84 -0
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +26 -0
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/brain/adapters.d.ts +34 -0
- package/dist/lib/brain/adapters.d.ts.map +1 -0
- package/dist/lib/brain/adapters.js +362 -0
- package/dist/lib/brain/adapters.js.map +1 -0
- package/dist/lib/brain/apply-control-plane.d.ts +17 -0
- package/dist/lib/brain/apply-control-plane.d.ts.map +1 -0
- package/dist/lib/brain/apply-control-plane.js +117 -0
- package/dist/lib/brain/apply-control-plane.js.map +1 -0
- package/dist/lib/brain/cache.d.ts +25 -0
- package/dist/lib/brain/cache.d.ts.map +1 -0
- package/dist/lib/brain/cache.js +99 -0
- package/dist/lib/brain/cache.js.map +1 -0
- package/dist/lib/brain/canonical.d.ts +5 -0
- package/dist/lib/brain/canonical.d.ts.map +1 -0
- package/dist/lib/brain/canonical.js +38 -0
- package/dist/lib/brain/canonical.js.map +1 -0
- package/dist/lib/brain/discovery.d.ts +9 -0
- package/dist/lib/brain/discovery.d.ts.map +1 -0
- package/dist/lib/brain/discovery.js +560 -0
- package/dist/lib/brain/discovery.js.map +1 -0
- package/dist/lib/brain/index.d.ts +55 -0
- package/dist/lib/brain/index.d.ts.map +1 -0
- package/dist/lib/brain/index.js +384 -0
- package/dist/lib/brain/index.js.map +1 -0
- package/dist/lib/brain/proposal.d.ts +179 -0
- package/dist/lib/brain/proposal.d.ts.map +1 -0
- package/dist/lib/brain/proposal.js +694 -0
- package/dist/lib/brain/proposal.js.map +1 -0
- package/dist/lib/brain/render.d.ts +33 -0
- package/dist/lib/brain/render.d.ts.map +1 -0
- package/dist/lib/brain/render.js +515 -0
- package/dist/lib/brain/render.js.map +1 -0
- package/dist/lib/brain/safe-file.d.ts +110 -0
- package/dist/lib/brain/safe-file.d.ts.map +1 -0
- package/dist/lib/brain/safe-file.js +564 -0
- package/dist/lib/brain/safe-file.js.map +1 -0
- package/dist/lib/brain/safety.d.ts +8 -0
- package/dist/lib/brain/safety.d.ts.map +1 -0
- package/dist/lib/brain/safety.js +42 -0
- package/dist/lib/brain/safety.js.map +1 -0
- package/dist/lib/brain/staged-apply.d.ts +57 -0
- package/dist/lib/brain/staged-apply.d.ts.map +1 -0
- package/dist/lib/brain/staged-apply.js +336 -0
- package/dist/lib/brain/staged-apply.js.map +1 -0
- package/dist/lib/brain/store.d.ts +26 -0
- package/dist/lib/brain/store.d.ts.map +1 -0
- package/dist/lib/brain/store.js +504 -0
- package/dist/lib/brain/store.js.map +1 -0
- package/dist/lib/brain/types.d.ts +160 -0
- package/dist/lib/brain/types.d.ts.map +1 -0
- package/dist/lib/brain/types.js +24 -0
- package/dist/lib/brain/types.js.map +1 -0
- package/dist/lib/config.d.ts +22 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +70 -14
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/daemon.d.ts +27 -0
- package/dist/lib/daemon.d.ts.map +1 -1
- package/dist/lib/daemon.js +57 -0
- package/dist/lib/daemon.js.map +1 -1
- package/dist/lib/machine-identity.d.ts +16 -0
- package/dist/lib/machine-identity.d.ts.map +1 -0
- package/dist/lib/machine-identity.js +101 -0
- package/dist/lib/machine-identity.js.map +1 -0
- package/dist/lib/orchestrator/loop.d.ts +1 -0
- package/dist/lib/orchestrator/loop.d.ts.map +1 -1
- package/dist/lib/orchestrator/loop.js +16 -0
- package/dist/lib/orchestrator/loop.js.map +1 -1
- package/dist/lib/orchestrator/tools.d.ts +11 -0
- package/dist/lib/orchestrator/tools.d.ts.map +1 -1
- package/dist/lib/orchestrator/tools.js +143 -0
- package/dist/lib/orchestrator/tools.js.map +1 -1
- package/dist/lib/project-strategy.d.ts +38 -0
- package/dist/lib/project-strategy.d.ts.map +1 -1
- package/dist/lib/project-strategy.js +91 -0
- package/dist/lib/project-strategy.js.map +1 -1
- package/dist/lib/projectContext.d.ts +6 -4
- package/dist/lib/projectContext.d.ts.map +1 -1
- package/dist/lib/projectContext.js +19 -17
- package/dist/lib/projectContext.js.map +1 -1
- package/dist/lib/tasksFile.d.ts +27 -32
- package/dist/lib/tasksFile.d.ts.map +1 -1
- package/dist/lib/tasksFile.js +359 -163
- package/dist/lib/tasksFile.js.map +1 -1
- package/dist/mcp/registry.d.ts +8 -2
- package/dist/mcp/registry.d.ts.map +1 -1
- package/dist/mcp/registry.js +189 -2
- package/dist/mcp/registry.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +3 -2
- package/dist/mcp/server.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,683 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.validateBrainMapPaths = validateBrainMapPaths;
|
|
40
|
+
exports.brainCommand = brainCommand;
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const os = __importStar(require("os"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const render_1 = require("../lib/render");
|
|
46
|
+
const brain_1 = require("../lib/brain");
|
|
47
|
+
const apply_control_plane_1 = require("../lib/brain/apply-control-plane");
|
|
48
|
+
const ACCENT = chalk_1.default.hex("#C46A3A");
|
|
49
|
+
const DIM = chalk_1.default.dim;
|
|
50
|
+
class BrainProposalInputError extends Error {
|
|
51
|
+
constructor() {
|
|
52
|
+
super(...arguments);
|
|
53
|
+
this.code = "PROJECT_BRAIN_PROPOSAL_INPUT_INVALID";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
class BrainMapError extends Error {
|
|
57
|
+
constructor(code, safeMessage) {
|
|
58
|
+
super(safeMessage);
|
|
59
|
+
this.code = code;
|
|
60
|
+
this.safeMessage = safeMessage;
|
|
61
|
+
this.name = "BrainMapError";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function expandHome(value) {
|
|
65
|
+
if (value === "~")
|
|
66
|
+
return os.homedir();
|
|
67
|
+
if (value.startsWith(`~${path.sep}`) || value.startsWith("~/"))
|
|
68
|
+
return path.join(os.homedir(), value.slice(2));
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
function canonicalPath(value) {
|
|
72
|
+
const absolute = path.resolve(expandHome(value));
|
|
73
|
+
const missing = [];
|
|
74
|
+
let existing = absolute;
|
|
75
|
+
while (!fs.existsSync(existing)) {
|
|
76
|
+
const parent = path.dirname(existing);
|
|
77
|
+
if (parent === existing)
|
|
78
|
+
break;
|
|
79
|
+
missing.unshift(path.basename(existing));
|
|
80
|
+
existing = parent;
|
|
81
|
+
}
|
|
82
|
+
const canonicalExisting = fs.existsSync(existing) ? fs.realpathSync(existing) : existing;
|
|
83
|
+
return path.resolve(canonicalExisting, ...missing);
|
|
84
|
+
}
|
|
85
|
+
function pathsOverlap(left, right) {
|
|
86
|
+
const relative = path.relative(left, right);
|
|
87
|
+
return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== "..");
|
|
88
|
+
}
|
|
89
|
+
/** Validate before scanning so a map can never write into a source repository. */
|
|
90
|
+
function validateBrainMapPaths(rootsValue, outValue) {
|
|
91
|
+
const roots = commaList(rootsValue)?.map(canonicalPath);
|
|
92
|
+
if (!roots?.length || !outValue?.trim()) {
|
|
93
|
+
throw new BrainMapError("PROJECT_BRAIN_MAP_OPTIONS_INVALID", "Brain mapping requires explicit --roots and --out values; nothing was written.");
|
|
94
|
+
}
|
|
95
|
+
const outDir = canonicalPath(outValue.trim());
|
|
96
|
+
for (const root of roots) {
|
|
97
|
+
if (pathsOverlap(root, outDir) || pathsOverlap(outDir, root)) {
|
|
98
|
+
throw new BrainMapError("PROJECT_BRAIN_MAP_OPTIONS_INVALID", "The brain map output must be disjoint from every scanned root; nothing was written.");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return { roots: [...new Set(roots)].sort((a, b) => a.localeCompare(b)), outDir };
|
|
102
|
+
}
|
|
103
|
+
function commaList(value) {
|
|
104
|
+
const items = value
|
|
105
|
+
?.split(",")
|
|
106
|
+
.map((item) => item.trim())
|
|
107
|
+
.filter(Boolean);
|
|
108
|
+
return items?.length ? items : undefined;
|
|
109
|
+
}
|
|
110
|
+
function isOpaqueHostedId(value) {
|
|
111
|
+
return typeof value === "string" && /^[A-Za-z0-9_-]{10,128}$/.test(value);
|
|
112
|
+
}
|
|
113
|
+
function asRecord(value) {
|
|
114
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
115
|
+
? value
|
|
116
|
+
: {};
|
|
117
|
+
}
|
|
118
|
+
function readinessReady(value) {
|
|
119
|
+
const record = asRecord(value);
|
|
120
|
+
const readiness = asRecord(record.readiness);
|
|
121
|
+
if (typeof readiness.ready === "boolean")
|
|
122
|
+
return readiness.ready;
|
|
123
|
+
if (typeof record.valid === "boolean")
|
|
124
|
+
return record.valid;
|
|
125
|
+
if (typeof record.ok === "boolean")
|
|
126
|
+
return record.ok;
|
|
127
|
+
if (record.kind === "project-brain-batch" && typeof readiness.status === "string") {
|
|
128
|
+
return !["cancelled", "unavailable"].includes(readiness.status);
|
|
129
|
+
}
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
function safeFailure(error) {
|
|
133
|
+
if (error instanceof BrainProposalInputError) {
|
|
134
|
+
return { code: error.code, message: "The proposal input is invalid or unsafe; nothing was changed." };
|
|
135
|
+
}
|
|
136
|
+
if (error instanceof brain_1.BrainSafeFileError) {
|
|
137
|
+
return { code: error.code, message: "The guarded Project Brain file operation failed safely; inspect the local apply journal before retrying." };
|
|
138
|
+
}
|
|
139
|
+
if (error instanceof brain_1.BrainStagedApplyError) {
|
|
140
|
+
return { code: error.code, message: "The staged Project Brain apply is unavailable, stale, unsafe, or already consumed; nothing was changed." };
|
|
141
|
+
}
|
|
142
|
+
if (error instanceof apply_control_plane_1.BrainApplyControlPlaneError) {
|
|
143
|
+
const message = error.phase === "claim"
|
|
144
|
+
? "The server did not issue the exact guarded-apply claim; no repository file was changed."
|
|
145
|
+
: error.phase === "applying"
|
|
146
|
+
? "The encrypted rollback journal was prepared, but the server did not accept applying; the repository file was not changed."
|
|
147
|
+
: error.phase === "applied"
|
|
148
|
+
? "The local patch was applied, but the server did not accept the applied receipt; inspect the local apply journal before continuing."
|
|
149
|
+
: "The server did not accept the rollback-failure lifecycle receipt; the artifact remains frozen for manual recovery.";
|
|
150
|
+
return { code: error.code, message };
|
|
151
|
+
}
|
|
152
|
+
if (error instanceof BrainMapError) {
|
|
153
|
+
return { code: error.code, message: error.safeMessage };
|
|
154
|
+
}
|
|
155
|
+
if (error instanceof brain_1.BrainSafetyError) {
|
|
156
|
+
return {
|
|
157
|
+
code: "PROJECT_BRAIN_PERSISTENCE_REFUSED",
|
|
158
|
+
message: "The project brain safety gate rejected the payload; nothing was persisted.",
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
code: "PROJECT_BRAIN_INTERNAL",
|
|
163
|
+
message: "The project brain operation failed; no repository state was changed.",
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function readSafeJsonFile(fileValue, maxBytes = 1024 * 1024) {
|
|
167
|
+
const resolved = canonicalPath(fileValue);
|
|
168
|
+
let stat;
|
|
169
|
+
try {
|
|
170
|
+
stat = fs.lstatSync(resolved);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
throw new BrainProposalInputError("proposal input is unavailable");
|
|
174
|
+
}
|
|
175
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > maxBytes)
|
|
176
|
+
throw new BrainProposalInputError("proposal input is unsafe");
|
|
177
|
+
const content = fs.readFileSync(resolved, "utf8");
|
|
178
|
+
try {
|
|
179
|
+
return { path: resolved, value: JSON.parse(content), content };
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
throw new BrainProposalInputError("proposal input is not JSON");
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function readProposalCandidate(fileValue) {
|
|
186
|
+
const resolved = canonicalPath(fileValue);
|
|
187
|
+
let stat;
|
|
188
|
+
try {
|
|
189
|
+
stat = fs.lstatSync(resolved);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 1024 * 1024)
|
|
195
|
+
throw new BrainProposalInputError("proposal validation input is unsafe");
|
|
196
|
+
try {
|
|
197
|
+
const value = JSON.parse(fs.readFileSync(resolved, "utf8"));
|
|
198
|
+
return asRecord(value).kind === "project-brain-proposal" ? value : null;
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function proposalGenerationResult(type, generated) {
|
|
205
|
+
return {
|
|
206
|
+
schemaVersion: "you-md/project-brain-proposal-generation/v1",
|
|
207
|
+
kind: "project-brain-proposal-generation",
|
|
208
|
+
proposalType: type,
|
|
209
|
+
readiness: {
|
|
210
|
+
ready: generated.proposals.length > 0 && generated.errors.length === 0,
|
|
211
|
+
status: generated.proposals.length > 0 && generated.errors.length === 0 ? "ready" : "unavailable",
|
|
212
|
+
reason: generated.proposals.length > 0 ? undefined : "no_valid_proposal",
|
|
213
|
+
},
|
|
214
|
+
counts: { proposals: generated.proposals.length, errors: generated.errors.length },
|
|
215
|
+
proposals: generated.proposals,
|
|
216
|
+
errors: generated.errors,
|
|
217
|
+
applyAvailable: false,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function resultLabel(subcommand, value) {
|
|
221
|
+
const result = asRecord(value);
|
|
222
|
+
if (result.kind === "project-brain-batch") {
|
|
223
|
+
const count = Array.isArray(result.workspaces) ? result.workspaces.length : 0;
|
|
224
|
+
return `${subcommand} · ${count} workspace${count === 1 ? "" : "s"}`;
|
|
225
|
+
}
|
|
226
|
+
if (result.kind === "project-brain-map") {
|
|
227
|
+
const counts = asRecord(result.counts);
|
|
228
|
+
return `${subcommand} · ${counts.projects ?? 0} project${counts.projects === 1 ? "" : "s"}`;
|
|
229
|
+
}
|
|
230
|
+
const scan = asRecord(result.scan);
|
|
231
|
+
const scanRevision = asRecord(scan.revision);
|
|
232
|
+
const revision = typeof result.revision === "string"
|
|
233
|
+
? result.revision
|
|
234
|
+
: typeof asRecord(result.brain).revision === "string"
|
|
235
|
+
? String(asRecord(result.brain).revision)
|
|
236
|
+
: typeof scanRevision.digest === "string"
|
|
237
|
+
? scanRevision.digest
|
|
238
|
+
: "";
|
|
239
|
+
const project = asRecord(result.project);
|
|
240
|
+
const projectName = typeof project.slug === "string"
|
|
241
|
+
? project.slug
|
|
242
|
+
: typeof project.name === "string"
|
|
243
|
+
? project.name
|
|
244
|
+
: typeof result.projectSlug === "string"
|
|
245
|
+
? result.projectSlug
|
|
246
|
+
: "";
|
|
247
|
+
return [subcommand, projectName, revision ? revision.slice(0, 12) : ""]
|
|
248
|
+
.filter(Boolean)
|
|
249
|
+
.join(" · ");
|
|
250
|
+
}
|
|
251
|
+
function printBatchHuman(value) {
|
|
252
|
+
const result = asRecord(value);
|
|
253
|
+
const readiness = asRecord(result.readiness);
|
|
254
|
+
const workspaces = Array.isArray(result.workspaces) ? result.workspaces : [];
|
|
255
|
+
const status = typeof readiness.status === "string" ? readiness.status : "unavailable";
|
|
256
|
+
const failed = ["cancelled", "unavailable"].includes(status);
|
|
257
|
+
console.log("");
|
|
258
|
+
console.log(" " + chalk_1.default.bold("project brain") + DIM(` -- ${workspaces.length} workspace${workspaces.length === 1 ? "" : "s"}`));
|
|
259
|
+
console.log("");
|
|
260
|
+
console.log(` ${failed ? ACCENT("×") : status === "degraded" ? ACCENT("!") : chalk_1.default.green("✓")} ${status}`);
|
|
261
|
+
console.log(` ${DIM([
|
|
262
|
+
`total=${readiness.total ?? workspaces.length}`,
|
|
263
|
+
`ready=${readiness.ready ?? 0}`,
|
|
264
|
+
`degraded=${readiness.degraded ?? 0}`,
|
|
265
|
+
`unavailable=${readiness.unavailable ?? 0}`,
|
|
266
|
+
].join(" "))}`);
|
|
267
|
+
console.log("");
|
|
268
|
+
for (const item of workspaces) {
|
|
269
|
+
const workspace = asRecord(item);
|
|
270
|
+
const workspaceReadiness = asRecord(workspace.readiness);
|
|
271
|
+
const workspaceStatus = typeof workspaceReadiness.status === "string" ? workspaceReadiness.status : "unavailable";
|
|
272
|
+
const projectRef = typeof workspace.projectRef === "string" ? workspace.projectRef : "project:unknown";
|
|
273
|
+
const workspaceRef = typeof workspace.workspaceRef === "string" ? workspace.workspaceRef : "workspace:unknown";
|
|
274
|
+
const revision = typeof workspace.revision === "string" ? workspace.revision : "";
|
|
275
|
+
const persistence = asRecord(workspace.persistence);
|
|
276
|
+
const persisted = Object.keys(persistence).length
|
|
277
|
+
? persistence.written === true ? "written" : "unchanged"
|
|
278
|
+
: "memory-only";
|
|
279
|
+
console.log(` ${workspaceStatus === "ready" ? chalk_1.default.green("✓") : ACCENT("!")} ${projectRef.replace(/^project:/, "")}`);
|
|
280
|
+
console.log(` ${DIM(`${workspaceRef.slice(0, 28)}… ${revision.slice(0, 20)}… ${persisted}`)}`);
|
|
281
|
+
}
|
|
282
|
+
if (Array.isArray(result.diagnostics) && result.diagnostics.length) {
|
|
283
|
+
console.log("");
|
|
284
|
+
console.log(` ${ACCENT(`${result.diagnostics.length} diagnostic(s)`)}`);
|
|
285
|
+
}
|
|
286
|
+
console.log("");
|
|
287
|
+
}
|
|
288
|
+
function printHumanResult(subcommand, value) {
|
|
289
|
+
const result = asRecord(value);
|
|
290
|
+
if (result.kind === "project-brain-batch") {
|
|
291
|
+
printBatchHuman(value);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
const scan = asRecord(result.scan);
|
|
295
|
+
const ready = readinessReady(value);
|
|
296
|
+
const label = resultLabel(subcommand, value);
|
|
297
|
+
console.log("");
|
|
298
|
+
console.log(" " + chalk_1.default.bold("project brain") + DIM(` -- ${label || subcommand}`));
|
|
299
|
+
console.log("");
|
|
300
|
+
const readiness = asRecord(result.readiness);
|
|
301
|
+
const status = typeof readiness.status === "string"
|
|
302
|
+
? readiness.status
|
|
303
|
+
: ready === false ? "not ready" : ready === true ? "ready" : "complete";
|
|
304
|
+
console.log(` ${ready === false ? ACCENT("×") : chalk_1.default.green("✓")} ${status}`);
|
|
305
|
+
const reason = typeof readiness.reason === "string" ? readiness.reason : undefined;
|
|
306
|
+
if (reason)
|
|
307
|
+
console.log(` ${DIM(reason)}`);
|
|
308
|
+
const revision = typeof result.revision === "string"
|
|
309
|
+
? result.revision
|
|
310
|
+
: typeof asRecord(scan.revision).digest === "string"
|
|
311
|
+
? String(asRecord(scan.revision).digest)
|
|
312
|
+
: undefined;
|
|
313
|
+
if (revision)
|
|
314
|
+
console.log(` ${DIM("revision")} ${revision}`);
|
|
315
|
+
const outputPath = typeof result.path === "string"
|
|
316
|
+
? result.path
|
|
317
|
+
: typeof result.outputPath === "string"
|
|
318
|
+
? result.outputPath
|
|
319
|
+
: undefined;
|
|
320
|
+
if (outputPath)
|
|
321
|
+
console.log(` ${DIM("path")} ${outputPath}`);
|
|
322
|
+
const counts = asRecord(result.counts);
|
|
323
|
+
if (Object.keys(counts).length) {
|
|
324
|
+
const summary = Object.entries(counts)
|
|
325
|
+
.filter(([, count]) => typeof count === "number")
|
|
326
|
+
.map(([name, count]) => `${name}=${count}`)
|
|
327
|
+
.join(" ");
|
|
328
|
+
if (summary)
|
|
329
|
+
console.log(` ${DIM(summary)}`);
|
|
330
|
+
}
|
|
331
|
+
if (Array.isArray(scan.projects))
|
|
332
|
+
console.log(` ${DIM(`${scan.projects.length} project(s) indexed`)}`);
|
|
333
|
+
if (Array.isArray(result.diagnostics) && result.diagnostics.length) {
|
|
334
|
+
console.log(` ${ACCENT(`${result.diagnostics.length} diagnostic(s)`)}`);
|
|
335
|
+
}
|
|
336
|
+
console.log("");
|
|
337
|
+
}
|
|
338
|
+
function printHelp() {
|
|
339
|
+
console.log("");
|
|
340
|
+
console.log(" " + chalk_1.default.bold("you brain") + DIM(" -- provenance-aware project truth catalog"));
|
|
341
|
+
console.log("");
|
|
342
|
+
console.log(" " + ACCENT("Commands"));
|
|
343
|
+
console.log(" " + chalk_1.default.cyan("scan") + DIM(" scan declared roots and atomically update the local brain"));
|
|
344
|
+
console.log(" " + chalk_1.default.cyan("map") + DIM(" render a portable project-brain catalog into a separate output repo"));
|
|
345
|
+
console.log(" " + chalk_1.default.cyan("adopt") + DIM(" propose a reviewable .you-project descriptor without writing it"));
|
|
346
|
+
console.log(" " + chalk_1.default.cyan("propose") + DIM(" import an overrides/notes JSON patch as an exact proposal"));
|
|
347
|
+
console.log(" " + chalk_1.default.cyan("stage") + DIM(" stage exact proposal/approval/root bindings for one native apply id"));
|
|
348
|
+
console.log(" " + chalk_1.default.cyan("apply") + DIM(" apply an approved, explicitly granted local artifact patch"));
|
|
349
|
+
console.log(" " + chalk_1.default.cyan("recover") + DIM(" recover an interrupted local apply from encrypted rollback"));
|
|
350
|
+
console.log(" " + chalk_1.default.cyan("validate") + DIM(" validate a manifest, snapshot, or current project brain"));
|
|
351
|
+
console.log(" " + chalk_1.default.cyan("show") + DIM(" read the latest or selected project brain snapshot"));
|
|
352
|
+
console.log("");
|
|
353
|
+
console.log(" " + DIM("Examples:"));
|
|
354
|
+
console.log(" " + chalk_1.default.cyan("you brain scan --roots ~/Desktop/CODE_2025,~/Desktop/CODE_YOU"));
|
|
355
|
+
console.log(" " + chalk_1.default.cyan("you brain map --roots ~/code/youmd,~/code/hubify --out ~/brain-repo"));
|
|
356
|
+
console.log(" " + chalk_1.default.cyan("you brain adopt --project legacy-app --json"));
|
|
357
|
+
console.log(" " + chalk_1.default.cyan("you brain propose --path overrides/legacy-app.json --json"));
|
|
358
|
+
console.log(" " + chalk_1.default.cyan("you brain stage --path proposal.json --proposal-id <hosted-id> --apply-id <id> --approval approval.json --root . --json"));
|
|
359
|
+
console.log(" " + chalk_1.default.cyan("you brain apply --path proposal.json --proposal-id <hosted-id> --apply-id <correlation-id> --approval approval.json --root . --json"));
|
|
360
|
+
console.log(" " + chalk_1.default.cyan("you brain recover --proposal-ref proposal:brain/... --project app --root . --json"));
|
|
361
|
+
console.log(" " + chalk_1.default.cyan("you brain validate --project youmd --json"));
|
|
362
|
+
console.log(" " + chalk_1.default.cyan("you brain show --project youmd --json"));
|
|
363
|
+
console.log("");
|
|
364
|
+
}
|
|
365
|
+
async function brainCommand(subcommand, args = [], options = {}) {
|
|
366
|
+
const command = subcommand?.trim().toLowerCase();
|
|
367
|
+
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
368
|
+
printHelp();
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (!new Set(["scan", "map", "adopt", "propose", "stage", "apply", "recover", "validate", "show"]).has(command)) {
|
|
372
|
+
console.error(ACCENT(`unknown brain command: ${subcommand}`));
|
|
373
|
+
printHelp();
|
|
374
|
+
process.exitCode = 1;
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
const spinner = options.json ? null : new render_1.BrailleSpinner(command === "scan"
|
|
378
|
+
? "indexing project truth without touching its owners"
|
|
379
|
+
: command === "map"
|
|
380
|
+
? "rendering a portable map from repository-owned truth"
|
|
381
|
+
: command === "adopt" || command === "propose"
|
|
382
|
+
? "drafting an exact proposal without applying it"
|
|
383
|
+
: command === "stage" || command === "apply" || command === "recover"
|
|
384
|
+
? "checking grants, journal, and encrypted rollback boundaries"
|
|
385
|
+
: command === "validate"
|
|
386
|
+
? "checking provenance and revision integrity"
|
|
387
|
+
: "opening the latest trusted project snapshot");
|
|
388
|
+
spinner?.start();
|
|
389
|
+
try {
|
|
390
|
+
let result;
|
|
391
|
+
if (command === "scan") {
|
|
392
|
+
const roots = commaList(options.roots);
|
|
393
|
+
result = roots
|
|
394
|
+
? await (0, brain_1.scanAllProjectBrains)({ roots, persist: options.persist !== false })
|
|
395
|
+
: await (0, brain_1.scanProjectBrain)({ project: options.project, persist: options.persist !== false });
|
|
396
|
+
}
|
|
397
|
+
else if (command === "map") {
|
|
398
|
+
const { roots, outDir } = validateBrainMapPaths(options.roots, options.out);
|
|
399
|
+
const batch = await (0, brain_1.scanAllProjectBrains)({ roots, persist: false });
|
|
400
|
+
if (batch.workspaces.length === 0 ||
|
|
401
|
+
batch.readiness.cancelled ||
|
|
402
|
+
["cancelled", "unavailable"].includes(batch.readiness.status)) {
|
|
403
|
+
throw new BrainMapError("PROJECT_BRAIN_MAP_SCAN_UNAVAILABLE", "The project brain scan was unavailable, so no portable output was written.");
|
|
404
|
+
}
|
|
405
|
+
const rendered = (0, brain_1.renderPortableProjectBrainBatch)(batch);
|
|
406
|
+
const write = (0, brain_1.writePortableProjectBrainOutput)(rendered, outDir);
|
|
407
|
+
const projects = batch.workspaces
|
|
408
|
+
.map((workspace) => ({
|
|
409
|
+
projectRef: workspace.projectRef,
|
|
410
|
+
workspaceRef: workspace.workspaceRef,
|
|
411
|
+
revision: workspace.revision,
|
|
412
|
+
}))
|
|
413
|
+
.sort((a, b) => a.projectRef.localeCompare(b.projectRef)
|
|
414
|
+
|| a.workspaceRef.localeCompare(b.workspaceRef)
|
|
415
|
+
|| a.revision.localeCompare(b.revision));
|
|
416
|
+
result = {
|
|
417
|
+
schemaVersion: "you-md/project-brain-map/v1",
|
|
418
|
+
kind: "project-brain-map",
|
|
419
|
+
revision: rendered.revision,
|
|
420
|
+
readiness: {
|
|
421
|
+
...batch.readiness,
|
|
422
|
+
ready: !batch.readiness.cancelled && batch.readiness.unavailable === 0,
|
|
423
|
+
},
|
|
424
|
+
counts: {
|
|
425
|
+
projects: new Set(projects.map((project) => project.projectRef)).size,
|
|
426
|
+
workspaces: projects.length,
|
|
427
|
+
written: write.written.length,
|
|
428
|
+
unchanged: write.unchanged.length,
|
|
429
|
+
pruned: write.pruned.length,
|
|
430
|
+
},
|
|
431
|
+
projects,
|
|
432
|
+
files: write,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
else if (command === "adopt") {
|
|
436
|
+
const scanned = await (0, brain_1.scanProjectBrain)({ project: options.project || args[0], persist: false });
|
|
437
|
+
const generated = (0, brain_1.proposeProjectBrainAdoption)(scanned.scan, {
|
|
438
|
+
projectRef: options.project ? `project:${options.project.replace(/^project:/, "")}` : undefined,
|
|
439
|
+
name: options.name,
|
|
440
|
+
rationale: options.rationale,
|
|
441
|
+
});
|
|
442
|
+
result = proposalGenerationResult("descriptor_adoption", generated);
|
|
443
|
+
}
|
|
444
|
+
else if (command === "propose") {
|
|
445
|
+
const requestedPath = options.path || args[0];
|
|
446
|
+
if (!requestedPath)
|
|
447
|
+
throw new BrainProposalInputError("proposal input path is required");
|
|
448
|
+
const source = readSafeJsonFile(requestedPath, 256 * 1024);
|
|
449
|
+
const sourcePath = path.relative(process.cwd(), source.path).split(path.sep).join("/");
|
|
450
|
+
const scanned = await (0, brain_1.scanProjectBrain)({ project: options.project, persist: false });
|
|
451
|
+
const generated = (0, brain_1.importProjectBrainOverrides)(scanned.scan, [{ sourcePath, content: source.content }]);
|
|
452
|
+
result = proposalGenerationResult("personal_override", generated);
|
|
453
|
+
}
|
|
454
|
+
else if (command === "stage") {
|
|
455
|
+
const proposalPath = options.path || args[0];
|
|
456
|
+
if (!proposalPath || !options.approval || !options.root
|
|
457
|
+
|| !isOpaqueHostedId(options.proposalId) || !isOpaqueHostedId(options.applyId)) {
|
|
458
|
+
throw new BrainProposalInputError("staging requires exact proposal, approval, root, proposal id, and apply id inputs");
|
|
459
|
+
}
|
|
460
|
+
const proposalInput = readSafeJsonFile(proposalPath);
|
|
461
|
+
const proposalValidation = (0, brain_1.validateProjectBrainProposal)(proposalInput.value);
|
|
462
|
+
if (!proposalValidation.valid || !proposalValidation.proposal)
|
|
463
|
+
throw new BrainProposalInputError("proposal is invalid");
|
|
464
|
+
const approvalInput = readSafeJsonFile(options.approval, 256 * 1024);
|
|
465
|
+
const staged = (0, brain_1.stageProjectBrainApply)({
|
|
466
|
+
brainRoot: options.brainRoot,
|
|
467
|
+
proposalId: options.proposalId,
|
|
468
|
+
applyId: options.applyId,
|
|
469
|
+
root: canonicalPath(options.root),
|
|
470
|
+
proposalPath: proposalInput.path,
|
|
471
|
+
proposalContent: proposalInput.content,
|
|
472
|
+
proposal: proposalValidation.proposal,
|
|
473
|
+
approvalPath: approvalInput.path,
|
|
474
|
+
approvalContent: approvalInput.content,
|
|
475
|
+
approval: approvalInput.value,
|
|
476
|
+
});
|
|
477
|
+
result = {
|
|
478
|
+
schemaVersion: staged.schemaVersion,
|
|
479
|
+
kind: staged.kind,
|
|
480
|
+
status: staged.state,
|
|
481
|
+
proposalId: staged.proposalId,
|
|
482
|
+
applyId: staged.applyId,
|
|
483
|
+
proposalRef: staged.proposalRef,
|
|
484
|
+
rootPathHash: staged.rootPathHash,
|
|
485
|
+
proposalContentHash: staged.proposalContentHash,
|
|
486
|
+
approvalContentHash: staged.approvalContentHash,
|
|
487
|
+
createdAt: staged.createdAt,
|
|
488
|
+
expiresAt: staged.expiresAt,
|
|
489
|
+
secretValuesExposed: false,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
else if (command === "apply") {
|
|
493
|
+
let proposalPath = options.path || args[0];
|
|
494
|
+
let approvalPath = options.approval;
|
|
495
|
+
let rootValue = options.root;
|
|
496
|
+
let stagedResolution;
|
|
497
|
+
if (options.applyId && !proposalPath && !approvalPath && !rootValue) {
|
|
498
|
+
if (!isOpaqueHostedId(options.proposalId) || !isOpaqueHostedId(options.applyId)) {
|
|
499
|
+
throw new BrainProposalInputError("hosted proposal or apply correlation id is invalid");
|
|
500
|
+
}
|
|
501
|
+
stagedResolution = (0, brain_1.consumeProjectBrainStagedApply)({
|
|
502
|
+
brainRoot: options.brainRoot,
|
|
503
|
+
proposalId: options.proposalId,
|
|
504
|
+
applyId: options.applyId,
|
|
505
|
+
});
|
|
506
|
+
proposalPath = stagedResolution.proposalPath;
|
|
507
|
+
approvalPath = stagedResolution.approvalPath;
|
|
508
|
+
rootValue = stagedResolution.root;
|
|
509
|
+
}
|
|
510
|
+
else if (options.applyId && (!proposalPath || !approvalPath || !rootValue)) {
|
|
511
|
+
throw new BrainProposalInputError("hosted apply correlation requires explicit proposal, approval, and repository root inputs");
|
|
512
|
+
}
|
|
513
|
+
if (!proposalPath || !approvalPath || !options.proposalId) {
|
|
514
|
+
throw new BrainProposalInputError("proposal, hosted proposal id, and approval paths are required");
|
|
515
|
+
}
|
|
516
|
+
if (!isOpaqueHostedId(options.proposalId) || (options.applyId !== undefined && !isOpaqueHostedId(options.applyId))) {
|
|
517
|
+
throw new BrainProposalInputError("hosted proposal or apply correlation id is invalid");
|
|
518
|
+
}
|
|
519
|
+
const proposalInput = readSafeJsonFile(proposalPath);
|
|
520
|
+
if (stagedResolution && `sha256:${(0, brain_1.sha256)(proposalInput.content)}` !== stagedResolution.proposalContentHash) {
|
|
521
|
+
throw new brain_1.BrainStagedApplyError("PROJECT_BRAIN_STAGE_STALE_HASH", "proposal changed after staged consumption");
|
|
522
|
+
}
|
|
523
|
+
const proposalValidation = (0, brain_1.validateProjectBrainProposal)(proposalInput.value);
|
|
524
|
+
if (!proposalValidation.valid || !proposalValidation.proposal)
|
|
525
|
+
throw new BrainProposalInputError("proposal is invalid");
|
|
526
|
+
const approvalInput = readSafeJsonFile(approvalPath, 256 * 1024);
|
|
527
|
+
if (stagedResolution && `sha256:${(0, brain_1.sha256)(approvalInput.content)}` !== stagedResolution.approvalContentHash) {
|
|
528
|
+
throw new brain_1.BrainStagedApplyError("PROJECT_BRAIN_STAGE_STALE_HASH", "approval changed after staged consumption");
|
|
529
|
+
}
|
|
530
|
+
const root = canonicalPath(rootValue || process.cwd());
|
|
531
|
+
const scanned = await (0, brain_1.scanProjectBrain)({
|
|
532
|
+
roots: [root],
|
|
533
|
+
project: proposalValidation.proposal.projectRef.replace(/^project:/, ""),
|
|
534
|
+
persist: false,
|
|
535
|
+
});
|
|
536
|
+
const proposal = proposalValidation.proposal;
|
|
537
|
+
const project = scanned.scan.projects.find((candidate) => candidate.projectRef === proposal.projectRef);
|
|
538
|
+
const artifact = project?.artifacts.find((candidate) => candidate.artifactRef === proposal.target.artifactRef);
|
|
539
|
+
const capability = artifact?.writePolicy === "managed_write" || artifact?.writePolicy === "task_write"
|
|
540
|
+
? artifact.writePolicy
|
|
541
|
+
: null;
|
|
542
|
+
if (!capability || !proposal.target.path)
|
|
543
|
+
throw new BrainProposalInputError("proposal target lacks a local write grant");
|
|
544
|
+
const device = (0, brain_1.readProjectBrainTrustedDeviceIdentity)();
|
|
545
|
+
const grantCredential = options.applyId
|
|
546
|
+
? (0, brain_1.deriveProjectBrainApplyCredential)(options.proposalId, options.applyId)
|
|
547
|
+
: undefined;
|
|
548
|
+
const claimInput = {
|
|
549
|
+
proposalId: options.proposalId,
|
|
550
|
+
claimNonce: (0, apply_control_plane_1.newProjectBrainApplyClaimNonce)(options.applyId),
|
|
551
|
+
deviceId: device.deviceId,
|
|
552
|
+
machineRef: proposal.machineRef,
|
|
553
|
+
workspaceRef: proposal.workspaceRef,
|
|
554
|
+
baseRevision: proposal.baseRevision,
|
|
555
|
+
baseContentHash: proposal.target.baseContentHash,
|
|
556
|
+
patchDigest: proposal.patchDigest,
|
|
557
|
+
targetPath: proposal.target.path,
|
|
558
|
+
capability,
|
|
559
|
+
secretValuesExposed: false,
|
|
560
|
+
};
|
|
561
|
+
const claim = grantCredential
|
|
562
|
+
? await (0, apply_control_plane_1.claimBoundProjectBrainApply)(claimInput, { grantCredential })
|
|
563
|
+
: await (0, apply_control_plane_1.claimBoundProjectBrainApply)(claimInput);
|
|
564
|
+
let rollbackCiphertextHash;
|
|
565
|
+
let localResult;
|
|
566
|
+
try {
|
|
567
|
+
localResult = await (0, brain_1.applyProjectBrainSafeFile)({
|
|
568
|
+
root,
|
|
569
|
+
brainRoot: options.brainRoot,
|
|
570
|
+
proposal,
|
|
571
|
+
approval: approvalInput.value,
|
|
572
|
+
scan: scanned.scan,
|
|
573
|
+
onPrepared: async (evidence) => {
|
|
574
|
+
rollbackCiphertextHash = evidence.rollbackCiphertextHash;
|
|
575
|
+
await (0, apply_control_plane_1.recordBoundProjectBrainApplyEvent)({
|
|
576
|
+
attemptId: claim.attemptId,
|
|
577
|
+
grantCredential: claim.grantCredential,
|
|
578
|
+
status: "applying",
|
|
579
|
+
rollbackCiphertextHash: evidence.rollbackCiphertextHash,
|
|
580
|
+
secretValuesExposed: false,
|
|
581
|
+
}, claim.applyAttemptRef);
|
|
582
|
+
},
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
catch (error) {
|
|
586
|
+
if (error instanceof brain_1.BrainSafeFileError && error.code === "PROJECT_BRAIN_ROLLBACK_FAILED" && rollbackCiphertextHash) {
|
|
587
|
+
await (0, apply_control_plane_1.recordBoundProjectBrainApplyEvent)({
|
|
588
|
+
attemptId: claim.attemptId,
|
|
589
|
+
grantCredential: claim.grantCredential,
|
|
590
|
+
status: "rollback_failed",
|
|
591
|
+
rollbackCiphertextHash,
|
|
592
|
+
failureCode: "rollback_failed",
|
|
593
|
+
secretValuesExposed: false,
|
|
594
|
+
}, claim.applyAttemptRef);
|
|
595
|
+
await (0, apply_control_plane_1.recordBoundProjectBrainApplyEvent)({
|
|
596
|
+
attemptId: claim.attemptId,
|
|
597
|
+
grantCredential: claim.grantCredential,
|
|
598
|
+
status: "manual_recovery_required",
|
|
599
|
+
rollbackCiphertextHash,
|
|
600
|
+
failureCode: "manual_recovery_required",
|
|
601
|
+
secretValuesExposed: false,
|
|
602
|
+
}, claim.applyAttemptRef);
|
|
603
|
+
}
|
|
604
|
+
throw error;
|
|
605
|
+
}
|
|
606
|
+
await (0, apply_control_plane_1.recordBoundProjectBrainApplyEvent)({
|
|
607
|
+
attemptId: claim.attemptId,
|
|
608
|
+
grantCredential: claim.grantCredential,
|
|
609
|
+
status: "applied",
|
|
610
|
+
resultContentHash: localResult.afterHash,
|
|
611
|
+
rollbackCiphertextHash: localResult.rollbackCiphertextHash,
|
|
612
|
+
secretValuesExposed: false,
|
|
613
|
+
}, claim.applyAttemptRef);
|
|
614
|
+
result = {
|
|
615
|
+
...localResult,
|
|
616
|
+
serverClaim: {
|
|
617
|
+
status: "applied",
|
|
618
|
+
proposalId: options.proposalId,
|
|
619
|
+
applyId: options.applyId,
|
|
620
|
+
applyAttemptRef: claim.applyAttemptRef,
|
|
621
|
+
grantRef: claim.grant.grantRef,
|
|
622
|
+
applyingReported: true,
|
|
623
|
+
appliedReported: true,
|
|
624
|
+
secretValuesExposed: false,
|
|
625
|
+
},
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
else if (command === "recover") {
|
|
629
|
+
const proposalRef = options.proposalRef || args[0];
|
|
630
|
+
if (!proposalRef || !options.project)
|
|
631
|
+
throw new BrainProposalInputError("proposalRef and project are required for recovery");
|
|
632
|
+
const root = canonicalPath(options.root || process.cwd());
|
|
633
|
+
const stateRoot = options.brainRoot ? canonicalPath(options.brainRoot) : (0, brain_1.defaultBrainRoot)();
|
|
634
|
+
// Safe existence check also prevents recovery from accepting a fabricated ref.
|
|
635
|
+
readSafeJsonFile((0, brain_1.projectBrainApplyJournalPath)(stateRoot, proposalRef), 1024 * 1024);
|
|
636
|
+
const scanned = await (0, brain_1.scanProjectBrain)({ roots: [root], project: options.project, persist: false });
|
|
637
|
+
result = (0, brain_1.recoverProjectBrainSafeFile)({ root, brainRoot: stateRoot, proposalRef, scan: scanned.scan });
|
|
638
|
+
}
|
|
639
|
+
else if (command === "validate") {
|
|
640
|
+
const requestedPath = options.path || args[0];
|
|
641
|
+
if (requestedPath) {
|
|
642
|
+
const proposal = readProposalCandidate(requestedPath);
|
|
643
|
+
result = proposal
|
|
644
|
+
? (0, brain_1.validateProjectBrainProposal)(proposal)
|
|
645
|
+
: await (0, brain_1.validateProjectBrain)({ path: requestedPath, project: options.project });
|
|
646
|
+
}
|
|
647
|
+
else
|
|
648
|
+
result = await (0, brain_1.validateProjectBrain)({ project: options.project });
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
result = await (0, brain_1.showProjectBrain)({
|
|
652
|
+
project: options.project || args[0],
|
|
653
|
+
workspace: options.workspace,
|
|
654
|
+
revision: options.revision,
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
if (spinner)
|
|
658
|
+
spinner.stop(resultLabel(command, result));
|
|
659
|
+
if (options.json)
|
|
660
|
+
console.log(JSON.stringify(result, null, 2));
|
|
661
|
+
else
|
|
662
|
+
printHumanResult(command, result);
|
|
663
|
+
if (readinessReady(result) === false)
|
|
664
|
+
process.exitCode = 1;
|
|
665
|
+
}
|
|
666
|
+
catch (error) {
|
|
667
|
+
const failure = safeFailure(error);
|
|
668
|
+
if (spinner)
|
|
669
|
+
spinner.fail(failure.message);
|
|
670
|
+
if (options.json) {
|
|
671
|
+
console.log(JSON.stringify({
|
|
672
|
+
schemaVersion: "you-md/project-brain-error/v1",
|
|
673
|
+
command,
|
|
674
|
+
error: failure,
|
|
675
|
+
}, null, 2));
|
|
676
|
+
}
|
|
677
|
+
else {
|
|
678
|
+
console.error(` ${ACCENT("brain command failed")} ${DIM(failure.message)}`);
|
|
679
|
+
}
|
|
680
|
+
process.exitCode = 1;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
//# sourceMappingURL=brain.js.map
|