zelari-code 1.42.0 → 1.44.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/dist/cli/main.bundled.js +1278 -688
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/runHeadless.js +15 -0
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/toolRegistry.js +16 -0
- package/dist/cli/toolRegistry.js.map +1 -1
- package/dist/cli/tools/planTaskTools.js +236 -0
- package/dist/cli/tools/planTaskTools.js.map +1 -0
- package/dist/cli/workspace/index.js +2 -0
- package/dist/cli/workspace/index.js.map +1 -1
- package/dist/cli/workspace/planDriftCheck.js +217 -0
- package/dist/cli/workspace/planDriftCheck.js.map +1 -0
- package/dist/cli/workspace/planDriftCheck.test.js +146 -0
- package/dist/cli/workspace/planDriftCheck.test.js.map +1 -0
- package/dist/cli/workspace/planStore.js +213 -0
- package/dist/cli/workspace/planStore.js.map +1 -0
- package/dist/cli/workspace/planStore.test.js +295 -0
- package/dist/cli/workspace/planStore.test.js.map +1 -0
- package/dist/cli/workspace/postCouncilHook.js +7 -0
- package/dist/cli/workspace/postCouncilHook.js.map +1 -1
- package/dist/cli/workspace/stubs.js +14 -4
- package/dist/cli/workspace/stubs.js.map +1 -1
- package/dist/cli/workspace/workspaceSummary.js +7 -1
- package/dist/cli/workspace/workspaceSummary.js.map +1 -1
- package/package.json +2 -2
package/dist/cli/main.bundled.js
CHANGED
|
@@ -21063,6 +21063,12 @@ function isBrainMemberCostEvent(e) {
|
|
|
21063
21063
|
function isBrainCouncilModeEvent(e) {
|
|
21064
21064
|
return e.type === "council_mode";
|
|
21065
21065
|
}
|
|
21066
|
+
function isBrainTaskUpdateEvent(e) {
|
|
21067
|
+
return e.type === "task_update";
|
|
21068
|
+
}
|
|
21069
|
+
function isBrainTaskSnapshotEvent(e) {
|
|
21070
|
+
return e.type === "task_snapshot";
|
|
21071
|
+
}
|
|
21066
21072
|
function createBrainEvent(type, sessionId, data) {
|
|
21067
21073
|
return {
|
|
21068
21074
|
type,
|
|
@@ -28073,6 +28079,8 @@ __export(dist_exports, {
|
|
|
28073
28079
|
isBrainMessageStartEvent: () => isBrainMessageStartEvent,
|
|
28074
28080
|
isBrainQueueUpdateEvent: () => isBrainQueueUpdateEvent,
|
|
28075
28081
|
isBrainSessionCompactedEvent: () => isBrainSessionCompactedEvent,
|
|
28082
|
+
isBrainTaskSnapshotEvent: () => isBrainTaskSnapshotEvent,
|
|
28083
|
+
isBrainTaskUpdateEvent: () => isBrainTaskUpdateEvent,
|
|
28076
28084
|
isBrainThinkingDeltaEvent: () => isBrainThinkingDeltaEvent,
|
|
28077
28085
|
isBrainToolExecutionEndEvent: () => isBrainToolExecutionEndEvent,
|
|
28078
28086
|
isBrainToolExecutionStartEvent: () => isBrainToolExecutionStartEvent,
|
|
@@ -31026,6 +31034,771 @@ var init_todoTools = __esm({
|
|
|
31026
31034
|
}
|
|
31027
31035
|
});
|
|
31028
31036
|
|
|
31037
|
+
// src/cli/workspace/paths.ts
|
|
31038
|
+
import {
|
|
31039
|
+
mkdirSync as mkdirSync10,
|
|
31040
|
+
writeFileSync as writeFileSync12,
|
|
31041
|
+
existsSync as existsSync17,
|
|
31042
|
+
accessSync,
|
|
31043
|
+
constants,
|
|
31044
|
+
realpathSync
|
|
31045
|
+
} from "node:fs";
|
|
31046
|
+
import { join as join13, basename } from "node:path";
|
|
31047
|
+
import { homedir as homedir5 } from "node:os";
|
|
31048
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
31049
|
+
function resolveWorkspaceRoot(projectRoot = process.cwd()) {
|
|
31050
|
+
const candidates = [
|
|
31051
|
+
join13(projectRoot, ".zelari"),
|
|
31052
|
+
join13(homedir5(), ".zelari-code", "workspace", hashProject(projectRoot))
|
|
31053
|
+
];
|
|
31054
|
+
for (const candidate of candidates) {
|
|
31055
|
+
if (isWritableDir(projectRoot) || candidate !== candidates[0]) {
|
|
31056
|
+
ensureWorkspaceDir(candidate);
|
|
31057
|
+
return candidate;
|
|
31058
|
+
}
|
|
31059
|
+
}
|
|
31060
|
+
ensureWorkspaceDir(candidates[0]);
|
|
31061
|
+
return candidates[0];
|
|
31062
|
+
}
|
|
31063
|
+
function hashProject(projectPath) {
|
|
31064
|
+
return createHash4("sha1").update(realpathSync(projectPath)).digest("hex").slice(0, 12);
|
|
31065
|
+
}
|
|
31066
|
+
function isWritableDir(dir) {
|
|
31067
|
+
try {
|
|
31068
|
+
if (!existsSync17(dir)) return false;
|
|
31069
|
+
accessSync(dir, constants.W_OK);
|
|
31070
|
+
return true;
|
|
31071
|
+
} catch {
|
|
31072
|
+
return false;
|
|
31073
|
+
}
|
|
31074
|
+
}
|
|
31075
|
+
function ensureWorkspaceDir(workspaceDir) {
|
|
31076
|
+
mkdirSync10(workspaceDir, { recursive: true });
|
|
31077
|
+
if (workspaceDir.endsWith("/.zelari") && existsSync17(join13(workspaceDir, "..", ".git"))) {
|
|
31078
|
+
const gitignorePath = join13(workspaceDir, ".gitignore");
|
|
31079
|
+
if (!existsSync17(gitignorePath)) {
|
|
31080
|
+
writeFileSync12(gitignorePath, "*\n!.gitignore\n");
|
|
31081
|
+
}
|
|
31082
|
+
}
|
|
31083
|
+
}
|
|
31084
|
+
function workspaceFile(rootDir, kind) {
|
|
31085
|
+
switch (kind) {
|
|
31086
|
+
case "plan":
|
|
31087
|
+
return join13(rootDir, "plan.md");
|
|
31088
|
+
case "risks":
|
|
31089
|
+
return join13(rootDir, "risks.md");
|
|
31090
|
+
case "index":
|
|
31091
|
+
return join13(rootDir, "workspace.json");
|
|
31092
|
+
}
|
|
31093
|
+
}
|
|
31094
|
+
function workspaceArtifact(rootDir, subdir, slug) {
|
|
31095
|
+
return join13(rootDir, subdir, `${slug}.md`);
|
|
31096
|
+
}
|
|
31097
|
+
function projectName(projectRoot = process.cwd()) {
|
|
31098
|
+
return basename(realpathSync(projectRoot));
|
|
31099
|
+
}
|
|
31100
|
+
var init_paths2 = __esm({
|
|
31101
|
+
"src/cli/workspace/paths.ts"() {
|
|
31102
|
+
"use strict";
|
|
31103
|
+
}
|
|
31104
|
+
});
|
|
31105
|
+
|
|
31106
|
+
// src/cli/workspace/storage.ts
|
|
31107
|
+
var storage_exports = {};
|
|
31108
|
+
__export(storage_exports, {
|
|
31109
|
+
Storage: () => Storage,
|
|
31110
|
+
parseFrontmatter: () => parseFrontmatter,
|
|
31111
|
+
parseYaml: () => parseYaml,
|
|
31112
|
+
serializeFrontmatter: () => serializeFrontmatter,
|
|
31113
|
+
serializeYaml: () => serializeYaml,
|
|
31114
|
+
workspaceMutex: () => workspaceMutex
|
|
31115
|
+
});
|
|
31116
|
+
import {
|
|
31117
|
+
readFileSync as readFileSync16,
|
|
31118
|
+
writeFileSync as writeFileSync13,
|
|
31119
|
+
existsSync as existsSync18,
|
|
31120
|
+
mkdirSync as mkdirSync11,
|
|
31121
|
+
readdirSync as readdirSync4,
|
|
31122
|
+
renameSync as renameSync2
|
|
31123
|
+
} from "node:fs";
|
|
31124
|
+
import { dirname as dirname2, join as join14 } from "node:path";
|
|
31125
|
+
function parseFrontmatter(md) {
|
|
31126
|
+
const m = FRONTMATTER_RE.exec(md);
|
|
31127
|
+
if (!m) return { meta: {}, body: md };
|
|
31128
|
+
const meta3 = parseYaml(m[1]);
|
|
31129
|
+
const body = m[2];
|
|
31130
|
+
return { meta: meta3, body };
|
|
31131
|
+
}
|
|
31132
|
+
function serializeFrontmatter(meta3, body) {
|
|
31133
|
+
const yamlStr = serializeYaml(meta3);
|
|
31134
|
+
return `---
|
|
31135
|
+
${yamlStr}
|
|
31136
|
+
---
|
|
31137
|
+
${body}`;
|
|
31138
|
+
}
|
|
31139
|
+
function parseYaml(input) {
|
|
31140
|
+
const lines = input.split(/\r?\n/);
|
|
31141
|
+
const ctx = { lines, i: 0 };
|
|
31142
|
+
return parseNode(ctx, 0);
|
|
31143
|
+
}
|
|
31144
|
+
function parseNode(ctx, indent) {
|
|
31145
|
+
while (ctx.i < ctx.lines.length) {
|
|
31146
|
+
const line2 = ctx.lines[ctx.i];
|
|
31147
|
+
if (line2.trim() === "" || line2.trim().startsWith("#")) {
|
|
31148
|
+
ctx.i++;
|
|
31149
|
+
continue;
|
|
31150
|
+
}
|
|
31151
|
+
break;
|
|
31152
|
+
}
|
|
31153
|
+
if (ctx.i >= ctx.lines.length) return null;
|
|
31154
|
+
const line = ctx.lines[ctx.i];
|
|
31155
|
+
const lineIndent = countIndent(line);
|
|
31156
|
+
if (/^\s*-\s+/.test(line)) {
|
|
31157
|
+
return parseBlockSequence(ctx, indent);
|
|
31158
|
+
}
|
|
31159
|
+
if (/^\s*\[.*\]\s*$/.test(line)) {
|
|
31160
|
+
const flow = line.trim().replace(/^\[/, "").replace(/\]$/, "");
|
|
31161
|
+
return parseFlowSequence(flow);
|
|
31162
|
+
}
|
|
31163
|
+
if (/^\s*\{.*\}\s*$/.test(line)) {
|
|
31164
|
+
const flow = line.trim().replace(/^\{/, "").replace(/\}$/, "");
|
|
31165
|
+
return parseFlowMap(flow);
|
|
31166
|
+
}
|
|
31167
|
+
return parseBlockMap(ctx, indent);
|
|
31168
|
+
}
|
|
31169
|
+
function parseBlockMap(ctx, indent) {
|
|
31170
|
+
const out = {};
|
|
31171
|
+
while (ctx.i < ctx.lines.length) {
|
|
31172
|
+
const line = ctx.lines[ctx.i];
|
|
31173
|
+
if (line.trim() === "" || line.trim().startsWith("#")) {
|
|
31174
|
+
ctx.i++;
|
|
31175
|
+
continue;
|
|
31176
|
+
}
|
|
31177
|
+
const lineIndent = countIndent(line);
|
|
31178
|
+
if (lineIndent < indent) break;
|
|
31179
|
+
if (lineIndent > indent) {
|
|
31180
|
+
ctx.i++;
|
|
31181
|
+
continue;
|
|
31182
|
+
}
|
|
31183
|
+
const m = /^([^:]+):\s*(.*)$/.exec(line);
|
|
31184
|
+
if (!m) {
|
|
31185
|
+
ctx.i++;
|
|
31186
|
+
continue;
|
|
31187
|
+
}
|
|
31188
|
+
const key = m[1].trim();
|
|
31189
|
+
const valuePart = m[2].trim();
|
|
31190
|
+
if (valuePart === "" || valuePart === "|" || valuePart === ">") {
|
|
31191
|
+
ctx.i++;
|
|
31192
|
+
const nested = parseNode(ctx, indent + 2);
|
|
31193
|
+
out[key] = nested;
|
|
31194
|
+
} else {
|
|
31195
|
+
if (valuePart.startsWith("[")) {
|
|
31196
|
+
out[key] = parseFlowSequence(stripFlow(valuePart, "[", "]"));
|
|
31197
|
+
} else if (valuePart.startsWith("{")) {
|
|
31198
|
+
out[key] = parseFlowMap(stripFlow(valuePart, "{", "}"));
|
|
31199
|
+
} else {
|
|
31200
|
+
out[key] = parseScalar(valuePart);
|
|
31201
|
+
}
|
|
31202
|
+
ctx.i++;
|
|
31203
|
+
}
|
|
31204
|
+
}
|
|
31205
|
+
return out;
|
|
31206
|
+
}
|
|
31207
|
+
function parseBlockSequence(ctx, indent) {
|
|
31208
|
+
const out = [];
|
|
31209
|
+
while (ctx.i < ctx.lines.length) {
|
|
31210
|
+
const line = ctx.lines[ctx.i];
|
|
31211
|
+
if (line.trim() === "") {
|
|
31212
|
+
ctx.i++;
|
|
31213
|
+
continue;
|
|
31214
|
+
}
|
|
31215
|
+
const lineIndent = countIndent(line);
|
|
31216
|
+
if (lineIndent < indent) break;
|
|
31217
|
+
if (lineIndent > indent) break;
|
|
31218
|
+
const m = /^-\s*(.*)$/.exec(line);
|
|
31219
|
+
if (!m) break;
|
|
31220
|
+
const rest = m[1];
|
|
31221
|
+
if (rest === "") {
|
|
31222
|
+
ctx.i++;
|
|
31223
|
+
out.push(parseNode(ctx, indent + 2));
|
|
31224
|
+
} else if (rest.startsWith("[") || rest.startsWith("{")) {
|
|
31225
|
+
let buffer = rest;
|
|
31226
|
+
let depthSq = (buffer.match(/\[/g) || []).length - (buffer.match(/\]/g) || []).length;
|
|
31227
|
+
let depthCu = (buffer.match(/\{/g) || []).length - (buffer.match(/\}/g) || []).length;
|
|
31228
|
+
while ((depthSq > 0 || depthCu > 0) && ctx.i + 1 < ctx.lines.length) {
|
|
31229
|
+
ctx.i++;
|
|
31230
|
+
const next = ctx.lines[ctx.i].trim();
|
|
31231
|
+
buffer += " " + next;
|
|
31232
|
+
depthSq = (buffer.match(/\[/g) || []).length - (buffer.match(/\]/g) || []).length;
|
|
31233
|
+
depthCu = (buffer.match(/\{/g) || []).length - (buffer.match(/\}/g) || []).length;
|
|
31234
|
+
}
|
|
31235
|
+
if (buffer.startsWith("[")) {
|
|
31236
|
+
out.push(parseFlowSequence(buffer.slice(1).replace(/\]$/, "")));
|
|
31237
|
+
} else {
|
|
31238
|
+
out.push(parseFlowMap(buffer.slice(1).replace(/\}$/, "")));
|
|
31239
|
+
}
|
|
31240
|
+
ctx.i++;
|
|
31241
|
+
} else if (rest.includes(":")) {
|
|
31242
|
+
ctx.i++;
|
|
31243
|
+
const mapCtx = { lines: [" ".repeat(indent + 2) + rest, ...ctx.lines.slice(ctx.i)], i: 0 };
|
|
31244
|
+
const val = parseBlockMap(mapCtx, indent + 2);
|
|
31245
|
+
ctx.i += mapCtx.i - 1;
|
|
31246
|
+
out.push(val);
|
|
31247
|
+
} else {
|
|
31248
|
+
out.push(parseScalar(rest));
|
|
31249
|
+
ctx.i++;
|
|
31250
|
+
}
|
|
31251
|
+
}
|
|
31252
|
+
return out;
|
|
31253
|
+
}
|
|
31254
|
+
function parseFlowSequence(input) {
|
|
31255
|
+
const parts = splitFlow(input);
|
|
31256
|
+
return parts.map((p3) => {
|
|
31257
|
+
const trimmed = p3.trim();
|
|
31258
|
+
if (trimmed.startsWith("{")) {
|
|
31259
|
+
return parseFlowMap(stripFlow(trimmed, "{", "}"));
|
|
31260
|
+
}
|
|
31261
|
+
return parseScalar(trimmed);
|
|
31262
|
+
});
|
|
31263
|
+
}
|
|
31264
|
+
function stripFlow(s, open, close) {
|
|
31265
|
+
let out = s.trim();
|
|
31266
|
+
if (out.startsWith(open)) out = out.slice(1);
|
|
31267
|
+
if (out.endsWith(close)) out = out.slice(0, -1);
|
|
31268
|
+
return out;
|
|
31269
|
+
}
|
|
31270
|
+
function parseFlowMap(input) {
|
|
31271
|
+
const parts = splitFlow(input);
|
|
31272
|
+
const out = {};
|
|
31273
|
+
for (const p3 of parts) {
|
|
31274
|
+
const colonIdx = p3.indexOf(":");
|
|
31275
|
+
if (colonIdx < 0) continue;
|
|
31276
|
+
const key = p3.slice(0, colonIdx).trim();
|
|
31277
|
+
const value = p3.slice(colonIdx + 1).trim();
|
|
31278
|
+
out[key] = parseScalar(value);
|
|
31279
|
+
}
|
|
31280
|
+
return out;
|
|
31281
|
+
}
|
|
31282
|
+
function splitFlow(input) {
|
|
31283
|
+
const out = [];
|
|
31284
|
+
let depthSq = 0, depthCu = 0, depthQu = 0;
|
|
31285
|
+
let buffer = "";
|
|
31286
|
+
for (let i = 0; i < input.length; i++) {
|
|
31287
|
+
const c = input[i];
|
|
31288
|
+
if (c === '"' || c === "'") {
|
|
31289
|
+
depthQu = depthQu === 0 ? depthQu + 1 : 0;
|
|
31290
|
+
buffer += c;
|
|
31291
|
+
} else if (depthQu === 0) {
|
|
31292
|
+
if (c === "[") depthSq++;
|
|
31293
|
+
else if (c === "]") depthSq--;
|
|
31294
|
+
else if (c === "{") depthCu++;
|
|
31295
|
+
else if (c === "}") depthCu--;
|
|
31296
|
+
else if (c === "," && depthSq === 0 && depthCu === 0) {
|
|
31297
|
+
out.push(buffer);
|
|
31298
|
+
buffer = "";
|
|
31299
|
+
continue;
|
|
31300
|
+
}
|
|
31301
|
+
buffer += c;
|
|
31302
|
+
} else {
|
|
31303
|
+
buffer += c;
|
|
31304
|
+
}
|
|
31305
|
+
}
|
|
31306
|
+
if (buffer.trim()) out.push(buffer);
|
|
31307
|
+
return out;
|
|
31308
|
+
}
|
|
31309
|
+
function parseScalar(s) {
|
|
31310
|
+
if (s === "" || s === "null" || s === "~") return null;
|
|
31311
|
+
if (VALID_SCALARS.test(s)) return s.toLowerCase() === "true";
|
|
31312
|
+
if (/^-?\d+(\.\d+)?$/.test(s)) return Number(s);
|
|
31313
|
+
if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
|
|
31314
|
+
return s.slice(1, -1);
|
|
31315
|
+
}
|
|
31316
|
+
return s;
|
|
31317
|
+
}
|
|
31318
|
+
function serializeYaml(value, indent = 0) {
|
|
31319
|
+
if (value === null || value === void 0) return "";
|
|
31320
|
+
if (typeof value === "string") {
|
|
31321
|
+
if (/[:#\n\[\]\{\},&*!|>'"%@`]/.test(value)) {
|
|
31322
|
+
return JSON.stringify(value);
|
|
31323
|
+
}
|
|
31324
|
+
return value;
|
|
31325
|
+
}
|
|
31326
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
31327
|
+
if (Array.isArray(value)) {
|
|
31328
|
+
if (value.length === 0) return "[]";
|
|
31329
|
+
if (value.every((v) => v === null || typeof v !== "object")) {
|
|
31330
|
+
return `[${value.map(serializeScalarInline).join(", ")}]`;
|
|
31331
|
+
}
|
|
31332
|
+
return `[${value.map((v) => "{" + serializeInlineObject(v) + "}").join(", ")}]`;
|
|
31333
|
+
}
|
|
31334
|
+
if (typeof value === "object") {
|
|
31335
|
+
const entries = Object.entries(value).filter(([, v]) => v !== void 0);
|
|
31336
|
+
return entries.map(([k, v]) => {
|
|
31337
|
+
if (v === null || v === void 0) return `${k}:`;
|
|
31338
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
31339
|
+
return `${k}: ${serializeScalarInline(v)}`;
|
|
31340
|
+
}
|
|
31341
|
+
if (Array.isArray(v)) {
|
|
31342
|
+
if (v.length === 0) return `${k}: []`;
|
|
31343
|
+
if (v.every((x) => x === null || typeof x !== "object")) {
|
|
31344
|
+
return `${k}: [${v.map(serializeScalarInline).join(", ")}]`;
|
|
31345
|
+
}
|
|
31346
|
+
return `${k}: [${v.map((x) => "{" + serializeInlineObject(x) + "}").join(", ")}]`;
|
|
31347
|
+
}
|
|
31348
|
+
return `${k}:
|
|
31349
|
+
${serializeYaml(v, indent + 2)}`;
|
|
31350
|
+
}).map((line) => `${" ".repeat(indent)}${line}`).join("\n");
|
|
31351
|
+
}
|
|
31352
|
+
return String(value);
|
|
31353
|
+
}
|
|
31354
|
+
function serializeScalarInline(v) {
|
|
31355
|
+
if (typeof v === "string" && /[:#\n\[\]\{\},&*!|>'"%@`]/.test(v)) return JSON.stringify(v);
|
|
31356
|
+
return String(v);
|
|
31357
|
+
}
|
|
31358
|
+
function serializeInlineObject(v) {
|
|
31359
|
+
if (v === null || typeof v !== "object" || Array.isArray(v)) {
|
|
31360
|
+
return serializeScalarInline(v);
|
|
31361
|
+
}
|
|
31362
|
+
const entries = Object.entries(v).filter(([, val]) => val !== void 0);
|
|
31363
|
+
return entries.map(([k, val]) => `${k}: ${serializeYaml(val)}`).join(", ");
|
|
31364
|
+
}
|
|
31365
|
+
function countIndent(line) {
|
|
31366
|
+
let i = 0;
|
|
31367
|
+
while (i < line.length && line[i] === " ") i++;
|
|
31368
|
+
return i;
|
|
31369
|
+
}
|
|
31370
|
+
var FRONTMATTER_RE, VALID_SCALARS, Storage, KeyedMutex, workspaceMutex;
|
|
31371
|
+
var init_storage = __esm({
|
|
31372
|
+
"src/cli/workspace/storage.ts"() {
|
|
31373
|
+
"use strict";
|
|
31374
|
+
FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
31375
|
+
VALID_SCALARS = /^(true|false|null|~)$/i;
|
|
31376
|
+
Storage = class {
|
|
31377
|
+
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
31378
|
+
read(path53) {
|
|
31379
|
+
if (!existsSync18(path53)) {
|
|
31380
|
+
throw new Error(`File not found: ${path53}`);
|
|
31381
|
+
}
|
|
31382
|
+
const md = readFileSync16(path53, "utf8");
|
|
31383
|
+
return parseFrontmatter(md);
|
|
31384
|
+
}
|
|
31385
|
+
/** Read a Markdown file; returns null if not found. */
|
|
31386
|
+
readIfExists(path53) {
|
|
31387
|
+
if (!existsSync18(path53)) return null;
|
|
31388
|
+
return this.read(path53);
|
|
31389
|
+
}
|
|
31390
|
+
/**
|
|
31391
|
+
* Write a Markdown file atomically (tmp + rename). Creates parent dirs.
|
|
31392
|
+
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
31393
|
+
*/
|
|
31394
|
+
write(path53, meta3, body) {
|
|
31395
|
+
mkdirSync11(dirname2(path53), { recursive: true });
|
|
31396
|
+
const tmp = path53 + ".tmp-" + process.pid;
|
|
31397
|
+
const md = serializeFrontmatter(meta3, body);
|
|
31398
|
+
writeFileSync13(tmp, md, "utf8");
|
|
31399
|
+
renameSync2(tmp, path53);
|
|
31400
|
+
}
|
|
31401
|
+
/** List all .md files in a directory (non-recursive). */
|
|
31402
|
+
listMarkdown(dir) {
|
|
31403
|
+
if (!existsSync18(dir)) return [];
|
|
31404
|
+
return readdirSync4(dir).filter((f) => f.endsWith(".md") && !f.startsWith(".")).map((f) => join14(dir, f));
|
|
31405
|
+
}
|
|
31406
|
+
};
|
|
31407
|
+
KeyedMutex = class {
|
|
31408
|
+
chains = /* @__PURE__ */ new Map();
|
|
31409
|
+
async run(key, fn) {
|
|
31410
|
+
const prev2 = this.chains.get(key) ?? Promise.resolve();
|
|
31411
|
+
let release = () => {
|
|
31412
|
+
};
|
|
31413
|
+
const next = new Promise((resolve3) => {
|
|
31414
|
+
release = resolve3;
|
|
31415
|
+
});
|
|
31416
|
+
const chained = prev2.then(() => next);
|
|
31417
|
+
this.chains.set(key, chained);
|
|
31418
|
+
await prev2;
|
|
31419
|
+
try {
|
|
31420
|
+
return await fn();
|
|
31421
|
+
} finally {
|
|
31422
|
+
release();
|
|
31423
|
+
if (this.chains.get(key) === chained) {
|
|
31424
|
+
this.chains.delete(key);
|
|
31425
|
+
}
|
|
31426
|
+
}
|
|
31427
|
+
}
|
|
31428
|
+
};
|
|
31429
|
+
workspaceMutex = new KeyedMutex();
|
|
31430
|
+
}
|
|
31431
|
+
});
|
|
31432
|
+
|
|
31433
|
+
// src/cli/workspace/planStore.ts
|
|
31434
|
+
import {
|
|
31435
|
+
copyFileSync,
|
|
31436
|
+
existsSync as existsSync19,
|
|
31437
|
+
mkdirSync as mkdirSync12,
|
|
31438
|
+
readFileSync as readFileSync17,
|
|
31439
|
+
renameSync as renameSync3,
|
|
31440
|
+
writeFileSync as writeFileSync14
|
|
31441
|
+
} from "node:fs";
|
|
31442
|
+
import { dirname as dirname3, join as join15 } from "node:path";
|
|
31443
|
+
async function withPlanStore(projectRoot, fn) {
|
|
31444
|
+
const rootDir = resolveWorkspaceRoot(projectRoot);
|
|
31445
|
+
return workspaceMutex.run(`${rootDir}:plan`, () => {
|
|
31446
|
+
const handle = loadHandle(rootDir);
|
|
31447
|
+
const out = fn(handle);
|
|
31448
|
+
saveHandle(rootDir, handle);
|
|
31449
|
+
return out;
|
|
31450
|
+
});
|
|
31451
|
+
}
|
|
31452
|
+
function nextPlanTaskId(store4) {
|
|
31453
|
+
const maxExisting = store4.tasks.reduce((max, t) => {
|
|
31454
|
+
const m = /^t(\d+)$/.exec(t.id);
|
|
31455
|
+
return m ? Math.max(max, parseInt(m[1], 10)) : max;
|
|
31456
|
+
}, 0);
|
|
31457
|
+
store4.counter = Math.max(store4.counter, maxExisting) + 1;
|
|
31458
|
+
return `t${store4.counter}`;
|
|
31459
|
+
}
|
|
31460
|
+
function writePlanTaskArtifact(rootDir, task) {
|
|
31461
|
+
const path53 = join15(rootDir, "plan-tasks", `${task.id}.md`);
|
|
31462
|
+
mkdirSync12(dirname3(path53), { recursive: true });
|
|
31463
|
+
const meta3 = {
|
|
31464
|
+
kind: "task",
|
|
31465
|
+
id: task.id,
|
|
31466
|
+
name: task.title,
|
|
31467
|
+
phaseId: task.phaseId,
|
|
31468
|
+
status: task.status,
|
|
31469
|
+
priority: task.priority ?? "medium",
|
|
31470
|
+
updatedAt: task.updatedAt
|
|
31471
|
+
};
|
|
31472
|
+
const body = [
|
|
31473
|
+
`# Task ${task.id}: ${task.title}`,
|
|
31474
|
+
"",
|
|
31475
|
+
`- Status: **${task.status}**`,
|
|
31476
|
+
`- Priority: ${task.priority ?? "medium"}`,
|
|
31477
|
+
task.phaseId ? `- Phase: ${task.phaseId}` : null,
|
|
31478
|
+
task.agent ? `- Agent: ${task.agent}` : null,
|
|
31479
|
+
"",
|
|
31480
|
+
task.notes?.trim() ? task.notes.trim() : "_(no notes)_",
|
|
31481
|
+
""
|
|
31482
|
+
].filter((l) => l !== null).join("\n");
|
|
31483
|
+
new Storage().write(path53, meta3, body);
|
|
31484
|
+
}
|
|
31485
|
+
function loadHandle(rootDir) {
|
|
31486
|
+
const jsonPath = join15(rootDir, "plan.json");
|
|
31487
|
+
if (!existsSync19(jsonPath)) {
|
|
31488
|
+
return { rootDir, tasks: [], counter: 0, rootFields: {} };
|
|
31489
|
+
}
|
|
31490
|
+
let parsed;
|
|
31491
|
+
try {
|
|
31492
|
+
const raw = JSON.parse(readFileSync17(jsonPath, "utf8"));
|
|
31493
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
31494
|
+
throw new Error("root is not a JSON object");
|
|
31495
|
+
}
|
|
31496
|
+
parsed = raw;
|
|
31497
|
+
} catch {
|
|
31498
|
+
throw new PlanStoreError(
|
|
31499
|
+
`PLAN_CORRUPT: ${jsonPath} is not valid JSON \u2014 refusing to overwrite a possibly hand-edited or crashed write. Fix or remove the file (a .bak may exist), then retry.`,
|
|
31500
|
+
"PLAN_CORRUPT"
|
|
31501
|
+
);
|
|
31502
|
+
}
|
|
31503
|
+
const {
|
|
31504
|
+
tasks: rawTasks,
|
|
31505
|
+
counter,
|
|
31506
|
+
schemaVersion: _schemaVersion,
|
|
31507
|
+
...rootFields
|
|
31508
|
+
} = parsed;
|
|
31509
|
+
const tasks = (Array.isArray(rawTasks) ? rawTasks : []).map(normalizeTask).filter((t) => typeof t.id === "string" && t.id.length > 0);
|
|
31510
|
+
const numericCounter = typeof counter === "number" && Number.isFinite(counter) && counter >= 0 ? Math.floor(counter) : 0;
|
|
31511
|
+
return { rootDir, tasks, counter: numericCounter, rootFields };
|
|
31512
|
+
}
|
|
31513
|
+
function saveHandle(rootDir, handle) {
|
|
31514
|
+
if (handle.tasks.length > PLAN_MAX_TASKS) {
|
|
31515
|
+
throw new PlanStoreError(
|
|
31516
|
+
`PLAN_TOO_MANY_TASKS: plan.json would exceed ${PLAN_MAX_TASKS} tasks (${handle.tasks.length}) \u2014 cancel or complete tasks first.`,
|
|
31517
|
+
"PLAN_TOO_MANY_TASKS"
|
|
31518
|
+
);
|
|
31519
|
+
}
|
|
31520
|
+
const jsonPath = join15(rootDir, "plan.json");
|
|
31521
|
+
mkdirSync12(rootDir, { recursive: true });
|
|
31522
|
+
if (existsSync19(jsonPath)) {
|
|
31523
|
+
copyFileSync(jsonPath, `${jsonPath}.bak`);
|
|
31524
|
+
}
|
|
31525
|
+
const file2 = {
|
|
31526
|
+
...handle.rootFields,
|
|
31527
|
+
schemaVersion: PLAN_SCHEMA_VERSION,
|
|
31528
|
+
counter: handle.counter,
|
|
31529
|
+
tasks: handle.tasks
|
|
31530
|
+
};
|
|
31531
|
+
const tmp = `${jsonPath}.tmp-${process.pid}`;
|
|
31532
|
+
writeFileSync14(tmp, JSON.stringify(file2, null, 2) + "\n", "utf8");
|
|
31533
|
+
renameSync3(tmp, jsonPath);
|
|
31534
|
+
}
|
|
31535
|
+
function normalizeTask(raw) {
|
|
31536
|
+
const t = raw !== null && typeof raw === "object" ? { ...raw } : {};
|
|
31537
|
+
if (typeof t.id === "string") {
|
|
31538
|
+
t.id = t.id.trim().slice(0, 64);
|
|
31539
|
+
}
|
|
31540
|
+
const titleSource = firstString(t.title) ?? firstString(t.name) ?? firstString(t.description) ?? "";
|
|
31541
|
+
t.title = titleSource.slice(0, PLAN_TITLE_MAX);
|
|
31542
|
+
if (t.title && !firstString(t.name)) {
|
|
31543
|
+
t.name = t.title;
|
|
31544
|
+
}
|
|
31545
|
+
t.status = normalizeStatus(t.status);
|
|
31546
|
+
if (typeof t.notes === "string") {
|
|
31547
|
+
t.notes = t.notes.slice(0, PLAN_NOTES_MAX);
|
|
31548
|
+
}
|
|
31549
|
+
if (typeof t.phaseId === "string") {
|
|
31550
|
+
t.phaseId = t.phaseId.slice(0, PLAN_TAG_MAX);
|
|
31551
|
+
}
|
|
31552
|
+
if (typeof t.agent === "string") {
|
|
31553
|
+
t.agent = t.agent.slice(0, PLAN_TAG_MAX);
|
|
31554
|
+
}
|
|
31555
|
+
return t;
|
|
31556
|
+
}
|
|
31557
|
+
function normalizeStatus(raw) {
|
|
31558
|
+
const s = typeof raw === "string" ? raw.trim().toLowerCase() : "";
|
|
31559
|
+
switch (s) {
|
|
31560
|
+
case "in_progress":
|
|
31561
|
+
case "in-progress":
|
|
31562
|
+
case "doing":
|
|
31563
|
+
case "started":
|
|
31564
|
+
case "active":
|
|
31565
|
+
return "in_progress";
|
|
31566
|
+
case "completed":
|
|
31567
|
+
case "complete":
|
|
31568
|
+
case "done":
|
|
31569
|
+
case "finished":
|
|
31570
|
+
return "completed";
|
|
31571
|
+
case "cancelled":
|
|
31572
|
+
case "canceled":
|
|
31573
|
+
case "closed":
|
|
31574
|
+
return "cancelled";
|
|
31575
|
+
case "blocked":
|
|
31576
|
+
case "on-hold":
|
|
31577
|
+
case "on hold":
|
|
31578
|
+
return "blocked";
|
|
31579
|
+
default:
|
|
31580
|
+
return "pending";
|
|
31581
|
+
}
|
|
31582
|
+
}
|
|
31583
|
+
function firstString(v) {
|
|
31584
|
+
return typeof v === "string" && v.trim().length > 0 ? v : null;
|
|
31585
|
+
}
|
|
31586
|
+
var PLAN_SCHEMA_VERSION, PLAN_MAX_TASKS, PLAN_TITLE_MAX, PLAN_NOTES_MAX, PLAN_TAG_MAX, PlanStoreError;
|
|
31587
|
+
var init_planStore = __esm({
|
|
31588
|
+
"src/cli/workspace/planStore.ts"() {
|
|
31589
|
+
"use strict";
|
|
31590
|
+
init_paths2();
|
|
31591
|
+
init_storage();
|
|
31592
|
+
PLAN_SCHEMA_VERSION = 1;
|
|
31593
|
+
PLAN_MAX_TASKS = 100;
|
|
31594
|
+
PLAN_TITLE_MAX = 200;
|
|
31595
|
+
PLAN_NOTES_MAX = 2e3;
|
|
31596
|
+
PLAN_TAG_MAX = 64;
|
|
31597
|
+
PlanStoreError = class extends Error {
|
|
31598
|
+
constructor(message, code) {
|
|
31599
|
+
super(message);
|
|
31600
|
+
this.code = code;
|
|
31601
|
+
this.name = "PlanStoreError";
|
|
31602
|
+
}
|
|
31603
|
+
};
|
|
31604
|
+
}
|
|
31605
|
+
});
|
|
31606
|
+
|
|
31607
|
+
// src/cli/tools/planTaskTools.ts
|
|
31608
|
+
function taskSummaryLine(t) {
|
|
31609
|
+
return `- ${t.id}: ${t.title} (${t.status}${t.priority ? `, ${t.priority}` : ""})`;
|
|
31610
|
+
}
|
|
31611
|
+
function toTaskPayload(t) {
|
|
31612
|
+
return {
|
|
31613
|
+
id: t.id,
|
|
31614
|
+
title: t.title,
|
|
31615
|
+
status: t.status,
|
|
31616
|
+
phaseId: t.phaseId,
|
|
31617
|
+
priority: t.priority
|
|
31618
|
+
};
|
|
31619
|
+
}
|
|
31620
|
+
function safeEmit(sink, event) {
|
|
31621
|
+
if (!sink) return;
|
|
31622
|
+
try {
|
|
31623
|
+
sink(event);
|
|
31624
|
+
} catch {
|
|
31625
|
+
}
|
|
31626
|
+
}
|
|
31627
|
+
function createPlanTaskTools(opts) {
|
|
31628
|
+
const projectRoot = opts.projectRoot;
|
|
31629
|
+
const onTaskEvent = opts.onTaskEvent;
|
|
31630
|
+
const taskCreate = {
|
|
31631
|
+
name: "task_create",
|
|
31632
|
+
description: "Create a durable workspace task in .zelari/plan.json (multi-session, shared with the Desktop Live Tasks panel). Use for project work that must survive this session. For volatile per-session tracking use todo_write instead. Returns the assigned id (t<N>).",
|
|
31633
|
+
permissions: ["write"],
|
|
31634
|
+
timeoutMs: 5e3,
|
|
31635
|
+
inputSchema: CreateSchema,
|
|
31636
|
+
execute: async (input) => {
|
|
31637
|
+
try {
|
|
31638
|
+
const res = await withPlanStore(projectRoot, (store4) => {
|
|
31639
|
+
if (store4.tasks.length >= PLAN_MAX_TASKS) {
|
|
31640
|
+
return typedErr(
|
|
31641
|
+
`PLAN_TOO_MANY_TASKS: plan.json already holds ${store4.tasks.length} tasks (max ${PLAN_MAX_TASKS}).`
|
|
31642
|
+
);
|
|
31643
|
+
}
|
|
31644
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
31645
|
+
const id = nextPlanTaskId(store4);
|
|
31646
|
+
const task = {
|
|
31647
|
+
id,
|
|
31648
|
+
title: input.title.trim().slice(0, PLAN_TITLE_MAX),
|
|
31649
|
+
// council readers (buildPlanSummary) render `name` — keep the
|
|
31650
|
+
// alias in sync from creation, not just on reload.
|
|
31651
|
+
name: input.title.trim().slice(0, PLAN_TITLE_MAX),
|
|
31652
|
+
status: "pending",
|
|
31653
|
+
priority: input.priority,
|
|
31654
|
+
phaseId: input.phaseId?.trim().slice(0, 64),
|
|
31655
|
+
notes: input.notes?.trim().slice(0, PLAN_NOTES_MAX),
|
|
31656
|
+
createdAt: now,
|
|
31657
|
+
updatedAt: now
|
|
31658
|
+
};
|
|
31659
|
+
store4.tasks.push(task);
|
|
31660
|
+
writePlanTaskArtifact(store4.rootDir, task);
|
|
31661
|
+
return typedOk({ id, task });
|
|
31662
|
+
});
|
|
31663
|
+
if (res.ok) {
|
|
31664
|
+
safeEmit(onTaskEvent, {
|
|
31665
|
+
type: "task_update",
|
|
31666
|
+
source: "workspace_plan",
|
|
31667
|
+
task: toTaskPayload(res.value.task)
|
|
31668
|
+
});
|
|
31669
|
+
}
|
|
31670
|
+
return res;
|
|
31671
|
+
} catch (err) {
|
|
31672
|
+
return typedErr(planStoreErrorMessage(err, "task_create"));
|
|
31673
|
+
}
|
|
31674
|
+
}
|
|
31675
|
+
};
|
|
31676
|
+
const taskUpdate = {
|
|
31677
|
+
name: "task_update",
|
|
31678
|
+
description: "Update a durable workspace task in .zelari/plan.json (status, title, priority, phaseId, notes, or appendNote). Accepts council-created ids too (task_list shows them). Errors with PLAN_TASK_NOT_FOUND on unknown ids.",
|
|
31679
|
+
permissions: ["write"],
|
|
31680
|
+
timeoutMs: 5e3,
|
|
31681
|
+
inputSchema: UpdateSchema,
|
|
31682
|
+
execute: async (input) => {
|
|
31683
|
+
try {
|
|
31684
|
+
const res = await withPlanStore(projectRoot, (store4) => {
|
|
31685
|
+
const task = store4.tasks.find((t) => t.id === input.id);
|
|
31686
|
+
if (!task) {
|
|
31687
|
+
return typedErr(
|
|
31688
|
+
`PLAN_TASK_NOT_FOUND: no task with id "${input.id}" in .zelari/plan.json (call task_list for current ids).`
|
|
31689
|
+
);
|
|
31690
|
+
}
|
|
31691
|
+
if (input.title !== void 0) {
|
|
31692
|
+
task.title = input.title.trim().slice(0, PLAN_TITLE_MAX);
|
|
31693
|
+
task.name = task.title;
|
|
31694
|
+
}
|
|
31695
|
+
if (input.status !== void 0) task.status = input.status;
|
|
31696
|
+
if (input.priority !== void 0) task.priority = input.priority;
|
|
31697
|
+
if (input.phaseId !== void 0) task.phaseId = input.phaseId.slice(0, 64);
|
|
31698
|
+
if (input.notes !== void 0) {
|
|
31699
|
+
task.notes = input.notes.slice(0, PLAN_NOTES_MAX);
|
|
31700
|
+
}
|
|
31701
|
+
if (input.appendNote !== void 0) {
|
|
31702
|
+
const prev2 = typeof task.notes === "string" ? task.notes : "";
|
|
31703
|
+
const merged = prev2 ? `${prev2}
|
|
31704
|
+
${input.appendNote}` : input.appendNote;
|
|
31705
|
+
task.notes = merged.slice(-PLAN_NOTES_MAX);
|
|
31706
|
+
}
|
|
31707
|
+
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
31708
|
+
writePlanTaskArtifact(store4.rootDir, task);
|
|
31709
|
+
return typedOk({ task });
|
|
31710
|
+
});
|
|
31711
|
+
if (res.ok) {
|
|
31712
|
+
safeEmit(onTaskEvent, {
|
|
31713
|
+
type: "task_update",
|
|
31714
|
+
source: "workspace_plan",
|
|
31715
|
+
task: toTaskPayload(res.value.task)
|
|
31716
|
+
});
|
|
31717
|
+
}
|
|
31718
|
+
return res;
|
|
31719
|
+
} catch (err) {
|
|
31720
|
+
return typedErr(planStoreErrorMessage(err, "task_update"));
|
|
31721
|
+
}
|
|
31722
|
+
}
|
|
31723
|
+
};
|
|
31724
|
+
const taskList = {
|
|
31725
|
+
name: "task_list",
|
|
31726
|
+
description: "List durable workspace tasks from .zelari/plan.json (both t<N> agent tasks and council-created plan tasks). Optional status/phaseId filters. Use before task_update to discover ids.",
|
|
31727
|
+
permissions: ["read"],
|
|
31728
|
+
timeoutMs: 5e3,
|
|
31729
|
+
inputSchema: ListSchema,
|
|
31730
|
+
execute: async (input) => {
|
|
31731
|
+
try {
|
|
31732
|
+
let allPayloads = [];
|
|
31733
|
+
const res = await withPlanStore(projectRoot, (store4) => {
|
|
31734
|
+
allPayloads = store4.tasks.map(toTaskPayload);
|
|
31735
|
+
const filtered = store4.tasks.filter(
|
|
31736
|
+
(t) => (input.status === void 0 || t.status === input.status) && (input.phaseId === void 0 || t.phaseId === input.phaseId)
|
|
31737
|
+
);
|
|
31738
|
+
const done = store4.tasks.filter(
|
|
31739
|
+
(t) => t.status === "completed" || t.status === "cancelled"
|
|
31740
|
+
).length;
|
|
31741
|
+
const formatted = filtered.length === 0 ? "(no matching workspace tasks)" : filtered.map(taskSummaryLine).join("\n");
|
|
31742
|
+
return typedOk({
|
|
31743
|
+
tasks: filtered,
|
|
31744
|
+
total: store4.tasks.length,
|
|
31745
|
+
done,
|
|
31746
|
+
formatted: `${formatted}
|
|
31747
|
+
(done/total: ${done}/${store4.tasks.length})`
|
|
31748
|
+
});
|
|
31749
|
+
});
|
|
31750
|
+
if (res.ok) {
|
|
31751
|
+
safeEmit(onTaskEvent, {
|
|
31752
|
+
type: "task_snapshot",
|
|
31753
|
+
source: "workspace_plan",
|
|
31754
|
+
tasks: allPayloads
|
|
31755
|
+
});
|
|
31756
|
+
}
|
|
31757
|
+
return res;
|
|
31758
|
+
} catch (err) {
|
|
31759
|
+
return typedErr(planStoreErrorMessage(err, "task_list"));
|
|
31760
|
+
}
|
|
31761
|
+
}
|
|
31762
|
+
};
|
|
31763
|
+
return [taskCreate, taskUpdate, taskList];
|
|
31764
|
+
}
|
|
31765
|
+
function planStoreErrorMessage(err, tool) {
|
|
31766
|
+
if (err instanceof PlanStoreError) return err.message;
|
|
31767
|
+
return `[${tool}] ${err instanceof Error ? err.message : String(err)}`;
|
|
31768
|
+
}
|
|
31769
|
+
var StatusSchema2, PrioritySchema, CreateSchema, UpdateSchema, ListSchema;
|
|
31770
|
+
var init_planTaskTools = __esm({
|
|
31771
|
+
"src/cli/tools/planTaskTools.ts"() {
|
|
31772
|
+
"use strict";
|
|
31773
|
+
init_zod();
|
|
31774
|
+
init_toolTypes();
|
|
31775
|
+
init_planStore();
|
|
31776
|
+
StatusSchema2 = external_exports.enum(["pending", "in_progress", "completed", "cancelled", "blocked"]).describe(
|
|
31777
|
+
"blocked exists only here (session todos have no blocked); no rigid FSM \u2014 corrections like completed \u2192 in_progress are allowed"
|
|
31778
|
+
);
|
|
31779
|
+
PrioritySchema = external_exports.enum(["low", "medium", "high", "critical"]);
|
|
31780
|
+
CreateSchema = external_exports.object({
|
|
31781
|
+
title: external_exports.string().min(1).max(PLAN_TITLE_MAX).describe("Short task description"),
|
|
31782
|
+
priority: PrioritySchema.optional().describe("Default medium"),
|
|
31783
|
+
phaseId: external_exports.string().min(1).max(64).optional().describe("Existing plan phase id (see .zelari/plan.json phases)"),
|
|
31784
|
+
notes: external_exports.string().max(PLAN_NOTES_MAX).optional().describe("Optional context/acceptance notes")
|
|
31785
|
+
});
|
|
31786
|
+
UpdateSchema = external_exports.object({
|
|
31787
|
+
id: external_exports.string().min(1).max(64).describe("Task id from task_create/task_list"),
|
|
31788
|
+
status: StatusSchema2.optional(),
|
|
31789
|
+
title: external_exports.string().min(1).max(PLAN_TITLE_MAX).optional(),
|
|
31790
|
+
priority: PrioritySchema.optional(),
|
|
31791
|
+
phaseId: external_exports.string().min(1).max(64).optional(),
|
|
31792
|
+
notes: external_exports.string().max(PLAN_NOTES_MAX).optional(),
|
|
31793
|
+
appendNote: external_exports.string().max(PLAN_NOTES_MAX).optional().describe("Append to existing notes (kept within the size cap)")
|
|
31794
|
+
}).describe("At least one field besides id must be present");
|
|
31795
|
+
ListSchema = external_exports.object({
|
|
31796
|
+
status: StatusSchema2.optional().describe("Filter by status"),
|
|
31797
|
+
phaseId: external_exports.string().min(1).max(64).optional().describe("Filter by phase")
|
|
31798
|
+
});
|
|
31799
|
+
}
|
|
31800
|
+
});
|
|
31801
|
+
|
|
31029
31802
|
// src/cli/lsp/protocol.ts
|
|
31030
31803
|
function encodeMessage(message) {
|
|
31031
31804
|
const json2 = JSON.stringify(message);
|
|
@@ -31329,7 +32102,7 @@ var init_servers = __esm({
|
|
|
31329
32102
|
|
|
31330
32103
|
// src/cli/lsp/manager.ts
|
|
31331
32104
|
import { spawn as spawn4 } from "node:child_process";
|
|
31332
|
-
import { readFileSync as
|
|
32105
|
+
import { readFileSync as readFileSync18 } from "node:fs";
|
|
31333
32106
|
function processTransport(child) {
|
|
31334
32107
|
return {
|
|
31335
32108
|
send: (data) => {
|
|
@@ -31532,7 +32305,7 @@ var init_manager = __esm({
|
|
|
31532
32305
|
const uri = pathToUri(file2);
|
|
31533
32306
|
let text;
|
|
31534
32307
|
try {
|
|
31535
|
-
text =
|
|
32308
|
+
text = readFileSync18(file2, "utf8");
|
|
31536
32309
|
} catch {
|
|
31537
32310
|
text = "";
|
|
31538
32311
|
}
|
|
@@ -31854,13 +32627,13 @@ var init_store = __esm({
|
|
|
31854
32627
|
});
|
|
31855
32628
|
|
|
31856
32629
|
// src/cli/semantic/index.ts
|
|
31857
|
-
import { promises as fs12, existsSync as
|
|
31858
|
-
import { homedir as
|
|
32630
|
+
import { promises as fs12, existsSync as existsSync20, readFileSync as readFileSync19 } from "node:fs";
|
|
32631
|
+
import { homedir as homedir6 } from "node:os";
|
|
31859
32632
|
import path23 from "node:path";
|
|
31860
|
-
import { createHash as
|
|
32633
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
31861
32634
|
function getIndexPath(root) {
|
|
31862
|
-
const hash3 =
|
|
31863
|
-
return process.env.ZELARI_SEMANTIC_FILE ?? path23.join(
|
|
32635
|
+
const hash3 = createHash5("sha1").update(path23.resolve(root)).digest("hex").slice(0, 16);
|
|
32636
|
+
return process.env.ZELARI_SEMANTIC_FILE ?? path23.join(homedir6(), ".tmp", "zelari-code", "semantic", `${hash3}.json`);
|
|
31864
32637
|
}
|
|
31865
32638
|
async function collectSourceFiles(root, maxFiles = 1500) {
|
|
31866
32639
|
const out = [];
|
|
@@ -31938,9 +32711,9 @@ async function saveIndex(root, data) {
|
|
|
31938
32711
|
}
|
|
31939
32712
|
function loadIndex(root) {
|
|
31940
32713
|
const file2 = getIndexPath(root);
|
|
31941
|
-
if (!
|
|
32714
|
+
if (!existsSync20(file2)) return null;
|
|
31942
32715
|
try {
|
|
31943
|
-
const parsed = JSON.parse(
|
|
32716
|
+
const parsed = JSON.parse(readFileSync19(file2, "utf8"));
|
|
31944
32717
|
if (parsed && Array.isArray(parsed.chunks)) return parsed;
|
|
31945
32718
|
} catch {
|
|
31946
32719
|
}
|
|
@@ -32470,19 +33243,19 @@ __export(targets_exports, {
|
|
|
32470
33243
|
});
|
|
32471
33244
|
import {
|
|
32472
33245
|
chmodSync,
|
|
32473
|
-
existsSync as
|
|
32474
|
-
mkdirSync as
|
|
32475
|
-
readFileSync as
|
|
32476
|
-
writeFileSync as
|
|
33246
|
+
existsSync as existsSync21,
|
|
33247
|
+
mkdirSync as mkdirSync13,
|
|
33248
|
+
readFileSync as readFileSync20,
|
|
33249
|
+
writeFileSync as writeFileSync15
|
|
32477
33250
|
} from "node:fs";
|
|
32478
|
-
import { dirname as
|
|
32479
|
-
import { homedir as
|
|
33251
|
+
import { dirname as dirname4, join as join16 } from "node:path";
|
|
33252
|
+
import { homedir as homedir7 } from "node:os";
|
|
32480
33253
|
import { spawn as spawn5 } from "node:child_process";
|
|
32481
33254
|
function getSshTargetsPath() {
|
|
32482
|
-
return
|
|
33255
|
+
return join16(homedir7(), ".zelari-code", "ssh-targets.json");
|
|
32483
33256
|
}
|
|
32484
33257
|
function getSshSecretsPath() {
|
|
32485
|
-
return
|
|
33258
|
+
return join16(homedir7(), ".zelari-code", "ssh-secrets.json");
|
|
32486
33259
|
}
|
|
32487
33260
|
function normalizeAuth(auth) {
|
|
32488
33261
|
if (auth === "keyPath") return "keyPath";
|
|
@@ -32491,17 +33264,17 @@ function normalizeAuth(auth) {
|
|
|
32491
33264
|
}
|
|
32492
33265
|
function readSecrets() {
|
|
32493
33266
|
const path53 = getSshSecretsPath();
|
|
32494
|
-
if (!
|
|
33267
|
+
if (!existsSync21(path53)) return {};
|
|
32495
33268
|
try {
|
|
32496
|
-
return JSON.parse(
|
|
33269
|
+
return JSON.parse(readFileSync20(path53, "utf8"));
|
|
32497
33270
|
} catch {
|
|
32498
33271
|
return {};
|
|
32499
33272
|
}
|
|
32500
33273
|
}
|
|
32501
33274
|
function writeSecrets(data) {
|
|
32502
33275
|
const path53 = getSshSecretsPath();
|
|
32503
|
-
|
|
32504
|
-
|
|
33276
|
+
mkdirSync13(dirname4(path53), { recursive: true });
|
|
33277
|
+
writeFileSync15(path53, `${JSON.stringify(data, null, 2)}
|
|
32505
33278
|
`, "utf8");
|
|
32506
33279
|
try {
|
|
32507
33280
|
chmodSync(path53, 384);
|
|
@@ -32534,9 +33307,9 @@ function deleteSshPassword(id) {
|
|
|
32534
33307
|
}
|
|
32535
33308
|
function readStore2() {
|
|
32536
33309
|
const path53 = getSshTargetsPath();
|
|
32537
|
-
if (!
|
|
33310
|
+
if (!existsSync21(path53)) return [];
|
|
32538
33311
|
try {
|
|
32539
|
-
const parsed = JSON.parse(
|
|
33312
|
+
const parsed = JSON.parse(readFileSync20(path53, "utf8"));
|
|
32540
33313
|
const list = Array.isArray(parsed.targets) ? parsed.targets : [];
|
|
32541
33314
|
return list.filter(
|
|
32542
33315
|
(t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
|
|
@@ -32552,9 +33325,9 @@ function readStore2() {
|
|
|
32552
33325
|
}
|
|
32553
33326
|
function writeStore2(targets) {
|
|
32554
33327
|
const path53 = getSshTargetsPath();
|
|
32555
|
-
|
|
33328
|
+
mkdirSync13(dirname4(path53), { recursive: true });
|
|
32556
33329
|
const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
|
|
32557
|
-
|
|
33330
|
+
writeFileSync15(
|
|
32558
33331
|
path53,
|
|
32559
33332
|
`${JSON.stringify({ targets: clean }, null, 2)}
|
|
32560
33333
|
`,
|
|
@@ -32655,17 +33428,17 @@ function buildSshBaseArgs(target) {
|
|
|
32655
33428
|
return args;
|
|
32656
33429
|
}
|
|
32657
33430
|
function ensureAskpassHelper() {
|
|
32658
|
-
const dir =
|
|
32659
|
-
|
|
32660
|
-
const cjs =
|
|
32661
|
-
|
|
33431
|
+
const dir = join16(homedir7(), ".zelari-code", "ssh-helpers");
|
|
33432
|
+
mkdirSync13(dir, { recursive: true });
|
|
33433
|
+
const cjs = join16(dir, "askpass.cjs");
|
|
33434
|
+
writeFileSync15(
|
|
32662
33435
|
cjs,
|
|
32663
33436
|
"process.stdout.write(process.env.ZELARI_SSH_ASKPASS_PASS || '');\n",
|
|
32664
33437
|
"utf8"
|
|
32665
33438
|
);
|
|
32666
33439
|
if (process.platform === "win32") {
|
|
32667
|
-
const cmd =
|
|
32668
|
-
|
|
33440
|
+
const cmd = join16(dir, "askpass.cmd");
|
|
33441
|
+
writeFileSync15(
|
|
32669
33442
|
cmd,
|
|
32670
33443
|
`@echo off\r
|
|
32671
33444
|
node "%~dp0askpass.cjs"\r
|
|
@@ -32674,8 +33447,8 @@ node "%~dp0askpass.cjs"\r
|
|
|
32674
33447
|
);
|
|
32675
33448
|
return cmd;
|
|
32676
33449
|
}
|
|
32677
|
-
const sh =
|
|
32678
|
-
|
|
33450
|
+
const sh = join16(dir, "askpass.sh");
|
|
33451
|
+
writeFileSync15(
|
|
32679
33452
|
sh,
|
|
32680
33453
|
`#!/bin/sh
|
|
32681
33454
|
exec node "$(dirname "$0")/askpass.cjs"
|
|
@@ -32748,9 +33521,9 @@ function readSshPublicKey(keyOrPubPath) {
|
|
|
32748
33521
|
if (!raw) return { ok: false, error: "Empty path" };
|
|
32749
33522
|
const candidates = raw.endsWith(".pub") ? [raw] : [`${raw}.pub`, raw];
|
|
32750
33523
|
for (const p3 of candidates) {
|
|
32751
|
-
if (!
|
|
33524
|
+
if (!existsSync21(p3)) continue;
|
|
32752
33525
|
try {
|
|
32753
|
-
const content =
|
|
33526
|
+
const content = readFileSync20(p3, "utf8").trim();
|
|
32754
33527
|
if (!content) continue;
|
|
32755
33528
|
if (/BEGIN .*PRIVATE KEY/i.test(content)) {
|
|
32756
33529
|
return {
|
|
@@ -33320,11 +34093,11 @@ __export(folderTrust_exports, {
|
|
|
33320
34093
|
trustFolder: () => trustFolder,
|
|
33321
34094
|
untrustFolder: () => untrustFolder
|
|
33322
34095
|
});
|
|
33323
|
-
import { homedir as
|
|
33324
|
-
import { existsSync as
|
|
34096
|
+
import { homedir as homedir8 } from "node:os";
|
|
34097
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync14, readFileSync as readFileSync21, writeFileSync as writeFileSync16 } from "node:fs";
|
|
33325
34098
|
import path28 from "node:path";
|
|
33326
34099
|
function trustStorePath() {
|
|
33327
|
-
return _overrideStorePath ?? path28.join(
|
|
34100
|
+
return _overrideStorePath ?? path28.join(homedir8(), ".zelari-code", "trust.json");
|
|
33328
34101
|
}
|
|
33329
34102
|
function normalize(p3) {
|
|
33330
34103
|
const resolved = path28.resolve(p3);
|
|
@@ -33332,7 +34105,7 @@ function normalize(p3) {
|
|
|
33332
34105
|
}
|
|
33333
34106
|
function readStore3() {
|
|
33334
34107
|
try {
|
|
33335
|
-
const raw =
|
|
34108
|
+
const raw = readFileSync21(trustStorePath(), "utf8");
|
|
33336
34109
|
const parsed = JSON.parse(raw);
|
|
33337
34110
|
if (parsed && Array.isArray(parsed.folders)) return parsed;
|
|
33338
34111
|
return DEFAULT_STORE;
|
|
@@ -33343,8 +34116,8 @@ function readStore3() {
|
|
|
33343
34116
|
function writeStore3(store4) {
|
|
33344
34117
|
const p3 = trustStorePath();
|
|
33345
34118
|
try {
|
|
33346
|
-
|
|
33347
|
-
|
|
34119
|
+
mkdirSync14(path28.dirname(p3), { recursive: true });
|
|
34120
|
+
writeFileSync16(p3, JSON.stringify(store4, null, 2), "utf8");
|
|
33348
34121
|
} catch (err) {
|
|
33349
34122
|
throw new Error(
|
|
33350
34123
|
`failed to persist trust store ${p3}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -33399,7 +34172,7 @@ function getTrustStorePath() {
|
|
|
33399
34172
|
return trustStorePath();
|
|
33400
34173
|
}
|
|
33401
34174
|
function hasTrustStore() {
|
|
33402
|
-
return
|
|
34175
|
+
return existsSync22(trustStorePath());
|
|
33403
34176
|
}
|
|
33404
34177
|
function _setTrustStorePathForTests(p3) {
|
|
33405
34178
|
_overrideStorePath = p3;
|
|
@@ -33414,21 +34187,21 @@ var init_folderTrust = __esm({
|
|
|
33414
34187
|
});
|
|
33415
34188
|
|
|
33416
34189
|
// src/cli/safety/lifecycleHooks.ts
|
|
33417
|
-
import { homedir as
|
|
33418
|
-
import { join as
|
|
33419
|
-
import { readdirSync as
|
|
34190
|
+
import { homedir as homedir9 } from "node:os";
|
|
34191
|
+
import { join as join17 } from "node:path";
|
|
34192
|
+
import { readdirSync as readdirSync5, statSync as statSync3 } from "node:fs";
|
|
33420
34193
|
function globalHooksDir() {
|
|
33421
|
-
return
|
|
34194
|
+
return join17(homedir9(), ".zelari-code", "hooks");
|
|
33422
34195
|
}
|
|
33423
34196
|
function projectHooksDir(projectRoot) {
|
|
33424
|
-
return
|
|
34197
|
+
return join17(projectRoot, ".zelari", "hooks");
|
|
33425
34198
|
}
|
|
33426
34199
|
function fingerprintHookDirs(dirs) {
|
|
33427
34200
|
const parts = [];
|
|
33428
34201
|
for (const dir of dirs) {
|
|
33429
34202
|
let names;
|
|
33430
34203
|
try {
|
|
33431
|
-
names =
|
|
34204
|
+
names = readdirSync5(dir).filter((f) => f.endsWith(".json")).sort();
|
|
33432
34205
|
} catch {
|
|
33433
34206
|
parts.push(`${dir}:missing`);
|
|
33434
34207
|
continue;
|
|
@@ -33442,7 +34215,7 @@ function fingerprintHookDirs(dirs) {
|
|
|
33442
34215
|
continue;
|
|
33443
34216
|
}
|
|
33444
34217
|
for (const name of names) {
|
|
33445
|
-
const full =
|
|
34218
|
+
const full = join17(dir, name);
|
|
33446
34219
|
try {
|
|
33447
34220
|
const st = statSync3(full);
|
|
33448
34221
|
parts.push(`${full}:${st.mtimeMs}:${st.size}`);
|
|
@@ -33483,7 +34256,7 @@ var init_lifecycleHooks = __esm({
|
|
|
33483
34256
|
});
|
|
33484
34257
|
|
|
33485
34258
|
// src/cli/toolResultCache.ts
|
|
33486
|
-
import { createHash as
|
|
34259
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
33487
34260
|
import { promises as fs14 } from "node:fs";
|
|
33488
34261
|
import path29 from "node:path";
|
|
33489
34262
|
function isToolCacheEnabled() {
|
|
@@ -33496,7 +34269,7 @@ function resolveToolCacheTtlMs() {
|
|
|
33496
34269
|
return Number.isFinite(n) && n >= 0 ? n : TOOL_CACHE_DEFAULT_TTL_MS;
|
|
33497
34270
|
}
|
|
33498
34271
|
function hashKey(parts) {
|
|
33499
|
-
return
|
|
34272
|
+
return createHash6("sha256").update(JSON.stringify(parts), "utf8").digest("hex");
|
|
33500
34273
|
}
|
|
33501
34274
|
function resultBytes(result) {
|
|
33502
34275
|
try {
|
|
@@ -33786,6 +34559,11 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
33786
34559
|
const todoRead = enableTodos ? withPerm(createTodoReadTool()) : null;
|
|
33787
34560
|
if (todoWrite) registry4.register(todoWrite);
|
|
33788
34561
|
if (todoRead) registry4.register(todoRead);
|
|
34562
|
+
const enablePlanTasks = options.enablePlanTasks !== false && options.readOnly !== true && (profile === "full" || options.planMode === true) && profile !== "explore" && profile !== "verify" && profile !== "general";
|
|
34563
|
+
const planTaskToolsWrapped = (enablePlanTasks ? createPlanTaskTools({ projectRoot: root, onTaskEvent: options.onTaskEvent }) : []).map((t) => withPerm(t));
|
|
34564
|
+
for (const t of planTaskToolsWrapped) {
|
|
34565
|
+
registry4.register(t);
|
|
34566
|
+
}
|
|
33789
34567
|
const summary = [
|
|
33790
34568
|
safeReadFile,
|
|
33791
34569
|
safeGrepContent,
|
|
@@ -33798,7 +34576,8 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
33798
34576
|
...askUserTool ? [askUserTool] : [],
|
|
33799
34577
|
...skillTool ? [skillTool] : [],
|
|
33800
34578
|
...todoWrite ? [todoWrite] : [],
|
|
33801
|
-
...todoRead ? [todoRead] : []
|
|
34579
|
+
...todoRead ? [todoRead] : [],
|
|
34580
|
+
...planTaskToolsWrapped.length > 0 ? planTaskToolsWrapped : []
|
|
33802
34581
|
];
|
|
33803
34582
|
const tools = summary.map((t) => ({
|
|
33804
34583
|
name: t.name,
|
|
@@ -34147,6 +34926,7 @@ var init_toolRegistry = __esm({
|
|
|
34147
34926
|
init_askUser();
|
|
34148
34927
|
init_skillTool();
|
|
34149
34928
|
init_todoTools();
|
|
34929
|
+
init_planTaskTools();
|
|
34150
34930
|
init_tools2();
|
|
34151
34931
|
init_manager();
|
|
34152
34932
|
init_tools3();
|
|
@@ -34177,7 +34957,7 @@ var init_toolRegistry = __esm({
|
|
|
34177
34957
|
});
|
|
34178
34958
|
|
|
34179
34959
|
// src/cli/state/fileStateStore.ts
|
|
34180
|
-
import { createHash as
|
|
34960
|
+
import { createHash as createHash7, randomUUID as randomUUID2 } from "node:crypto";
|
|
34181
34961
|
import { promises as fs15 } from "node:fs";
|
|
34182
34962
|
import * as path30 from "node:path";
|
|
34183
34963
|
function shortId() {
|
|
@@ -34229,7 +35009,7 @@ async function getStateStore(projectRoot, env = process.env) {
|
|
|
34229
35009
|
}
|
|
34230
35010
|
}
|
|
34231
35011
|
function hashStablePrompt(stable) {
|
|
34232
|
-
return
|
|
35012
|
+
return createHash7("sha256").update(stable, "utf8").digest("hex").slice(0, 16);
|
|
34233
35013
|
}
|
|
34234
35014
|
var DEFAULT_MATERIALIZE_CHARS, FileDurableStateStore, NoopDurableStateStore;
|
|
34235
35015
|
var init_fileStateStore = __esm({
|
|
@@ -34942,8 +35722,8 @@ __export(conversationContext_exports, {
|
|
|
34942
35722
|
setHistory: () => setHistory,
|
|
34943
35723
|
setLastClarification: () => setLastClarification
|
|
34944
35724
|
});
|
|
34945
|
-
import { existsSync as
|
|
34946
|
-
import { join as
|
|
35725
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
35726
|
+
import { join as join19 } from "node:path";
|
|
34947
35727
|
function getHistory() {
|
|
34948
35728
|
return history;
|
|
34949
35729
|
}
|
|
@@ -34951,7 +35731,7 @@ function setHistory(messages) {
|
|
|
34951
35731
|
history = [...messages];
|
|
34952
35732
|
}
|
|
34953
35733
|
function compactInPlace(cwd = process.cwd()) {
|
|
34954
|
-
const durableStatePresent =
|
|
35734
|
+
const durableStatePresent = existsSync23(join19(cwd, ".zelari", "state", "HEAD.json"));
|
|
34955
35735
|
history = compactHistory(history, { durableStatePresent });
|
|
34956
35736
|
}
|
|
34957
35737
|
function appendMessages(msgs) {
|
|
@@ -35466,14 +36246,14 @@ var init_claudeProvider = __esm({
|
|
|
35466
36246
|
});
|
|
35467
36247
|
|
|
35468
36248
|
// src/cli/workspace/projectInstructions.ts
|
|
35469
|
-
import { existsSync as
|
|
35470
|
-
import { join as
|
|
36249
|
+
import { existsSync as existsSync24, readFileSync as readFileSync22 } from "node:fs";
|
|
36250
|
+
import { join as join20 } from "node:path";
|
|
35471
36251
|
function loadProjectInstructions(projectRoot = process.cwd(), maxChars = MAX_CHARS) {
|
|
35472
36252
|
for (const name of CANDIDATES) {
|
|
35473
|
-
const full =
|
|
35474
|
-
if (!
|
|
36253
|
+
const full = join20(projectRoot, name);
|
|
36254
|
+
if (!existsSync24(full)) continue;
|
|
35475
36255
|
try {
|
|
35476
|
-
let raw =
|
|
36256
|
+
let raw = readFileSync22(full, "utf8");
|
|
35477
36257
|
raw = raw.replace(/\r\n/g, "\n").trim();
|
|
35478
36258
|
if (!raw) continue;
|
|
35479
36259
|
if (raw.length <= maxChars) {
|
|
@@ -35508,75 +36288,6 @@ var init_projectInstructions = __esm({
|
|
|
35508
36288
|
}
|
|
35509
36289
|
});
|
|
35510
36290
|
|
|
35511
|
-
// src/cli/workspace/paths.ts
|
|
35512
|
-
import {
|
|
35513
|
-
mkdirSync as mkdirSync12,
|
|
35514
|
-
writeFileSync as writeFileSync14,
|
|
35515
|
-
existsSync as existsSync22,
|
|
35516
|
-
accessSync,
|
|
35517
|
-
constants,
|
|
35518
|
-
realpathSync
|
|
35519
|
-
} from "node:fs";
|
|
35520
|
-
import { join as join18, basename } from "node:path";
|
|
35521
|
-
import { homedir as homedir9 } from "node:os";
|
|
35522
|
-
import { createHash as createHash7 } from "node:crypto";
|
|
35523
|
-
function resolveWorkspaceRoot(projectRoot = process.cwd()) {
|
|
35524
|
-
const candidates = [
|
|
35525
|
-
join18(projectRoot, ".zelari"),
|
|
35526
|
-
join18(homedir9(), ".zelari-code", "workspace", hashProject(projectRoot))
|
|
35527
|
-
];
|
|
35528
|
-
for (const candidate of candidates) {
|
|
35529
|
-
if (isWritableDir(projectRoot) || candidate !== candidates[0]) {
|
|
35530
|
-
ensureWorkspaceDir(candidate);
|
|
35531
|
-
return candidate;
|
|
35532
|
-
}
|
|
35533
|
-
}
|
|
35534
|
-
ensureWorkspaceDir(candidates[0]);
|
|
35535
|
-
return candidates[0];
|
|
35536
|
-
}
|
|
35537
|
-
function hashProject(projectPath) {
|
|
35538
|
-
return createHash7("sha1").update(realpathSync(projectPath)).digest("hex").slice(0, 12);
|
|
35539
|
-
}
|
|
35540
|
-
function isWritableDir(dir) {
|
|
35541
|
-
try {
|
|
35542
|
-
if (!existsSync22(dir)) return false;
|
|
35543
|
-
accessSync(dir, constants.W_OK);
|
|
35544
|
-
return true;
|
|
35545
|
-
} catch {
|
|
35546
|
-
return false;
|
|
35547
|
-
}
|
|
35548
|
-
}
|
|
35549
|
-
function ensureWorkspaceDir(workspaceDir) {
|
|
35550
|
-
mkdirSync12(workspaceDir, { recursive: true });
|
|
35551
|
-
if (workspaceDir.endsWith("/.zelari") && existsSync22(join18(workspaceDir, "..", ".git"))) {
|
|
35552
|
-
const gitignorePath = join18(workspaceDir, ".gitignore");
|
|
35553
|
-
if (!existsSync22(gitignorePath)) {
|
|
35554
|
-
writeFileSync14(gitignorePath, "*\n!.gitignore\n");
|
|
35555
|
-
}
|
|
35556
|
-
}
|
|
35557
|
-
}
|
|
35558
|
-
function workspaceFile(rootDir, kind) {
|
|
35559
|
-
switch (kind) {
|
|
35560
|
-
case "plan":
|
|
35561
|
-
return join18(rootDir, "plan.md");
|
|
35562
|
-
case "risks":
|
|
35563
|
-
return join18(rootDir, "risks.md");
|
|
35564
|
-
case "index":
|
|
35565
|
-
return join18(rootDir, "workspace.json");
|
|
35566
|
-
}
|
|
35567
|
-
}
|
|
35568
|
-
function workspaceArtifact(rootDir, subdir, slug) {
|
|
35569
|
-
return join18(rootDir, subdir, `${slug}.md`);
|
|
35570
|
-
}
|
|
35571
|
-
function projectName(projectRoot = process.cwd()) {
|
|
35572
|
-
return basename(realpathSync(projectRoot));
|
|
35573
|
-
}
|
|
35574
|
-
var init_paths2 = __esm({
|
|
35575
|
-
"src/cli/workspace/paths.ts"() {
|
|
35576
|
-
"use strict";
|
|
35577
|
-
}
|
|
35578
|
-
});
|
|
35579
|
-
|
|
35580
36291
|
// src/cli/workspace/workspaceSummary.ts
|
|
35581
36292
|
var workspaceSummary_exports = {};
|
|
35582
36293
|
__export(workspaceSummary_exports, {
|
|
@@ -35586,8 +36297,8 @@ __export(workspaceSummary_exports, {
|
|
|
35586
36297
|
buildWorkspaceSummary: () => buildWorkspaceSummary,
|
|
35587
36298
|
buildZelariReadHint: () => buildZelariReadHint
|
|
35588
36299
|
});
|
|
35589
|
-
import { existsSync as
|
|
35590
|
-
import { join as
|
|
36300
|
+
import { existsSync as existsSync25, readFileSync as readFileSync23, readdirSync as readdirSync6, statSync as statSync4 } from "node:fs";
|
|
36301
|
+
import { join as join21, relative } from "node:path";
|
|
35591
36302
|
function buildWorkspaceSummary(projectRoot = process.cwd(), options = {}) {
|
|
35592
36303
|
const { maxEntries = 30, maxChars = 3500, maxDeps = 24, maxScripts = 16 } = options;
|
|
35593
36304
|
const name = safeProjectName(projectRoot);
|
|
@@ -35620,11 +36331,11 @@ function formatTaskLine(t) {
|
|
|
35620
36331
|
}
|
|
35621
36332
|
function buildPlanSummary(projectRoot = process.cwd(), options) {
|
|
35622
36333
|
const zelariRoot = resolveWorkspaceRoot(projectRoot);
|
|
35623
|
-
const planPath =
|
|
35624
|
-
if (!
|
|
36334
|
+
const planPath = join21(zelariRoot, "plan.json");
|
|
36335
|
+
if (!existsSync25(planPath)) return null;
|
|
35625
36336
|
let plan;
|
|
35626
36337
|
try {
|
|
35627
|
-
plan = JSON.parse(
|
|
36338
|
+
plan = JSON.parse(readFileSync23(planPath, "utf8"));
|
|
35628
36339
|
} catch {
|
|
35629
36340
|
return null;
|
|
35630
36341
|
}
|
|
@@ -35638,7 +36349,10 @@ function buildPlanSummary(projectRoot = process.cwd(), options) {
|
|
|
35638
36349
|
"# Plan ops (DRAFT \u2014 .zelari/plan.json)",
|
|
35639
36350
|
"_This is operational task list from design, not product law. Ground every change in the real source tree._"
|
|
35640
36351
|
];
|
|
35641
|
-
const open = tasks.filter((t) =>
|
|
36352
|
+
const open = tasks.filter((t) => {
|
|
36353
|
+
const s = t.status;
|
|
36354
|
+
return s !== "done" && s !== "completed" && s !== "cancelled";
|
|
36355
|
+
});
|
|
35642
36356
|
const done = tasks.length - open.length;
|
|
35643
36357
|
const userMessage = options?.userMessage?.trim();
|
|
35644
36358
|
let scopedOpen = open;
|
|
@@ -35760,8 +36474,8 @@ function pickNextTask(open) {
|
|
|
35760
36474
|
)[0];
|
|
35761
36475
|
}
|
|
35762
36476
|
function buildZelariReadHint(projectRoot = process.cwd()) {
|
|
35763
|
-
const planPath =
|
|
35764
|
-
if (!
|
|
36477
|
+
const planPath = join21(resolveWorkspaceRoot(projectRoot), "plan.json");
|
|
36478
|
+
if (!existsSync25(planPath)) return "";
|
|
35765
36479
|
return [
|
|
35766
36480
|
"# Council workspace detected (.zelari/) \u2014 DRAFT vault",
|
|
35767
36481
|
"`.zelari/plan.json` and `.zelari/docs/` hold **design hypotheses**, not verified product state.",
|
|
@@ -35776,10 +36490,10 @@ function safeProjectName(root) {
|
|
|
35776
36490
|
}
|
|
35777
36491
|
}
|
|
35778
36492
|
function readPackageJson(projectRoot) {
|
|
35779
|
-
const p3 =
|
|
35780
|
-
if (!
|
|
36493
|
+
const p3 = join21(projectRoot, "package.json");
|
|
36494
|
+
if (!existsSync25(p3)) return null;
|
|
35781
36495
|
try {
|
|
35782
|
-
return JSON.parse(
|
|
36496
|
+
return JSON.parse(readFileSync23(p3, "utf8"));
|
|
35783
36497
|
} catch {
|
|
35784
36498
|
return null;
|
|
35785
36499
|
}
|
|
@@ -35820,7 +36534,7 @@ function readBuildScripts(projectRoot, maxScripts = 16) {
|
|
|
35820
36534
|
function listShallow(projectRoot, maxEntries) {
|
|
35821
36535
|
const out = [];
|
|
35822
36536
|
try {
|
|
35823
|
-
const top =
|
|
36537
|
+
const top = readdirSync6(projectRoot, { withFileTypes: true }).filter(
|
|
35824
36538
|
(e) => !e.name.startsWith(".") && e.name !== "node_modules" && e.name !== "dist"
|
|
35825
36539
|
).sort((a, b) => a.name.localeCompare(b.name));
|
|
35826
36540
|
let count = 0;
|
|
@@ -35829,11 +36543,11 @@ function listShallow(projectRoot, maxEntries) {
|
|
|
35829
36543
|
out.push(`\u2026 (+${top.length - count} more)`);
|
|
35830
36544
|
break;
|
|
35831
36545
|
}
|
|
35832
|
-
const rel2 = relative(projectRoot,
|
|
36546
|
+
const rel2 = relative(projectRoot, join21(projectRoot, entry.name));
|
|
35833
36547
|
if (entry.isDirectory()) {
|
|
35834
36548
|
let inner = "";
|
|
35835
36549
|
try {
|
|
35836
|
-
const sub =
|
|
36550
|
+
const sub = readdirSync6(join21(projectRoot, entry.name), {
|
|
35837
36551
|
withFileTypes: true
|
|
35838
36552
|
}).filter((e) => !e.name.startsWith(".")).slice(0, 4).map((e) => e.name);
|
|
35839
36553
|
if (sub.length > 0)
|
|
@@ -35881,12 +36595,12 @@ var init_workspaceSummary = __esm({
|
|
|
35881
36595
|
});
|
|
35882
36596
|
|
|
35883
36597
|
// src/cli/workspace/buildLessonsSummary.ts
|
|
35884
|
-
import { existsSync as
|
|
35885
|
-
import { join as
|
|
36598
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
36599
|
+
import { join as join22 } from "node:path";
|
|
35886
36600
|
function buildLessonsSummary(projectRoot = process.cwd(), taskText) {
|
|
35887
36601
|
if (process.env["ZELARI_LESSONS"] === "0") return null;
|
|
35888
36602
|
const zelariRoot = resolveWorkspaceRoot(projectRoot);
|
|
35889
|
-
if (!
|
|
36603
|
+
if (!existsSync26(join22(zelariRoot, "lessons.jsonl"))) return null;
|
|
35890
36604
|
const lessons = recallLessons(zelariRoot, {
|
|
35891
36605
|
maxLessons: 5,
|
|
35892
36606
|
maxBytes: 2048,
|
|
@@ -35907,8 +36621,8 @@ var composeContext_exports = {};
|
|
|
35907
36621
|
__export(composeContext_exports, {
|
|
35908
36622
|
composeProjectContext: () => composeProjectContext
|
|
35909
36623
|
});
|
|
35910
|
-
import { existsSync as
|
|
35911
|
-
import { join as
|
|
36624
|
+
import { existsSync as existsSync27, readdirSync as readdirSync7, readFileSync as readFileSync24 } from "node:fs";
|
|
36625
|
+
import { join as join23 } from "node:path";
|
|
35912
36626
|
function cap2(text, max, label) {
|
|
35913
36627
|
if (!text || text.length <= max) return { text: text || "", truncated: false };
|
|
35914
36628
|
return {
|
|
@@ -35920,19 +36634,19 @@ function cap2(text, max, label) {
|
|
|
35920
36634
|
}
|
|
35921
36635
|
function buildDesignIndex(projectRoot, maxChars) {
|
|
35922
36636
|
const root = resolveWorkspaceRoot(projectRoot);
|
|
35923
|
-
if (!
|
|
36637
|
+
if (!existsSync27(root)) return "";
|
|
35924
36638
|
const lines = [
|
|
35925
36639
|
"# Design vault index (.zelari/) \u2014 HYPOTHESES only",
|
|
35926
36640
|
"Full design docs are NOT product source of truth. Open with list_files / read_file / searchDocuments if needed."
|
|
35927
36641
|
];
|
|
35928
|
-
const docsDir =
|
|
35929
|
-
if (
|
|
36642
|
+
const docsDir = join23(root, "docs");
|
|
36643
|
+
if (existsSync27(docsDir)) {
|
|
35930
36644
|
try {
|
|
35931
|
-
const docs =
|
|
36645
|
+
const docs = readdirSync7(docsDir).filter((n) => n.endsWith(".md")).slice(0, 12);
|
|
35932
36646
|
if (docs.length > 0) {
|
|
35933
36647
|
lines.push("", "## docs/ (titles only)");
|
|
35934
36648
|
for (const d of docs) lines.push(`- .zelari/docs/${d}`);
|
|
35935
|
-
if (
|
|
36649
|
+
if (readdirSync7(docsDir).filter((n) => n.endsWith(".md")).length > 12) {
|
|
35936
36650
|
lines.push("- \u2026 (more under .zelari/docs/)");
|
|
35937
36651
|
}
|
|
35938
36652
|
}
|
|
@@ -35940,14 +36654,14 @@ function buildDesignIndex(projectRoot, maxChars) {
|
|
|
35940
36654
|
}
|
|
35941
36655
|
}
|
|
35942
36656
|
for (const name of ["risks.md", "plan.json", "nfr-spec.json"]) {
|
|
35943
|
-
if (
|
|
36657
|
+
if (existsSync27(join23(root, name))) {
|
|
35944
36658
|
lines.push(`- .zelari/${name} present`);
|
|
35945
36659
|
}
|
|
35946
36660
|
}
|
|
35947
|
-
const decisionsDir =
|
|
35948
|
-
if (
|
|
36661
|
+
const decisionsDir = join23(root, "decisions");
|
|
36662
|
+
if (existsSync27(decisionsDir)) {
|
|
35949
36663
|
try {
|
|
35950
|
-
const n =
|
|
36664
|
+
const n = readdirSync7(decisionsDir).filter((f) => f.endsWith(".md")).length;
|
|
35951
36665
|
if (n > 0) lines.push(`- .zelari/decisions/ (${n} ADR file(s) \u2014 treat proposed as non-binding)`);
|
|
35952
36666
|
} catch {
|
|
35953
36667
|
}
|
|
@@ -36046,17 +36760,17 @@ function composeProjectContext(input) {
|
|
|
36046
36760
|
}
|
|
36047
36761
|
function readDurableHeadSync(projectRoot) {
|
|
36048
36762
|
try {
|
|
36049
|
-
const headPath =
|
|
36050
|
-
if (!
|
|
36051
|
-
const head = JSON.parse(
|
|
36763
|
+
const headPath = join23(projectRoot, ".zelari", "state", "HEAD.json");
|
|
36764
|
+
if (!existsSync27(headPath)) return "";
|
|
36765
|
+
const head = JSON.parse(readFileSync24(headPath, "utf8"));
|
|
36052
36766
|
if (!head?.id) return "";
|
|
36053
|
-
const metaPath =
|
|
36054
|
-
if (!
|
|
36055
|
-
const meta3 = JSON.parse(
|
|
36056
|
-
const discPath = meta3.artifactDir ?
|
|
36767
|
+
const metaPath = join23(projectRoot, ".zelari", "state", "commits", `${head.id}.json`);
|
|
36768
|
+
if (!existsSync27(metaPath)) return "";
|
|
36769
|
+
const meta3 = JSON.parse(readFileSync24(metaPath, "utf8"));
|
|
36770
|
+
const discPath = meta3.artifactDir ? join23(projectRoot, ".zelari", "state", meta3.artifactDir, "discoveries.json") : join23(projectRoot, ".zelari", "state", "artifacts", head.id, "discoveries.json");
|
|
36057
36771
|
let discoveries = [];
|
|
36058
|
-
if (
|
|
36059
|
-
discoveries = JSON.parse(
|
|
36772
|
+
if (existsSync27(discPath)) {
|
|
36773
|
+
discoveries = JSON.parse(readFileSync24(discPath, "utf8"));
|
|
36060
36774
|
}
|
|
36061
36775
|
const reusable = discoveries.filter((d) => d.reusable !== false);
|
|
36062
36776
|
const lines = [
|
|
@@ -36089,13 +36803,13 @@ var planDetect_exports = {};
|
|
|
36089
36803
|
__export(planDetect_exports, {
|
|
36090
36804
|
hasWorkspacePlan: () => hasWorkspacePlan
|
|
36091
36805
|
});
|
|
36092
|
-
import { existsSync as
|
|
36093
|
-
import { join as
|
|
36806
|
+
import { existsSync as existsSync28, readFileSync as readFileSync25 } from "node:fs";
|
|
36807
|
+
import { join as join24 } from "node:path";
|
|
36094
36808
|
function hasWorkspacePlan(projectRoot = process.cwd()) {
|
|
36095
|
-
const planPath =
|
|
36096
|
-
if (!
|
|
36809
|
+
const planPath = join24(resolveWorkspaceRoot(projectRoot), "plan.json");
|
|
36810
|
+
if (!existsSync28(planPath)) return false;
|
|
36097
36811
|
try {
|
|
36098
|
-
const parsed = JSON.parse(
|
|
36812
|
+
const parsed = JSON.parse(readFileSync25(planPath, "utf8"));
|
|
36099
36813
|
return Array.isArray(parsed.phases) && parsed.phases.length > 0;
|
|
36100
36814
|
} catch {
|
|
36101
36815
|
return false;
|
|
@@ -36144,333 +36858,6 @@ var init_loadDurableContext = __esm({
|
|
|
36144
36858
|
}
|
|
36145
36859
|
});
|
|
36146
36860
|
|
|
36147
|
-
// src/cli/workspace/storage.ts
|
|
36148
|
-
var storage_exports = {};
|
|
36149
|
-
__export(storage_exports, {
|
|
36150
|
-
Storage: () => Storage,
|
|
36151
|
-
parseFrontmatter: () => parseFrontmatter,
|
|
36152
|
-
parseYaml: () => parseYaml,
|
|
36153
|
-
serializeFrontmatter: () => serializeFrontmatter,
|
|
36154
|
-
serializeYaml: () => serializeYaml,
|
|
36155
|
-
workspaceMutex: () => workspaceMutex
|
|
36156
|
-
});
|
|
36157
|
-
import {
|
|
36158
|
-
readFileSync as readFileSync24,
|
|
36159
|
-
writeFileSync as writeFileSync15,
|
|
36160
|
-
existsSync as existsSync27,
|
|
36161
|
-
mkdirSync as mkdirSync13,
|
|
36162
|
-
readdirSync as readdirSync7,
|
|
36163
|
-
renameSync as renameSync2
|
|
36164
|
-
} from "node:fs";
|
|
36165
|
-
import { dirname as dirname4, join as join23 } from "node:path";
|
|
36166
|
-
function parseFrontmatter(md) {
|
|
36167
|
-
const m = FRONTMATTER_RE.exec(md);
|
|
36168
|
-
if (!m) return { meta: {}, body: md };
|
|
36169
|
-
const meta3 = parseYaml(m[1]);
|
|
36170
|
-
const body = m[2];
|
|
36171
|
-
return { meta: meta3, body };
|
|
36172
|
-
}
|
|
36173
|
-
function serializeFrontmatter(meta3, body) {
|
|
36174
|
-
const yamlStr = serializeYaml(meta3);
|
|
36175
|
-
return `---
|
|
36176
|
-
${yamlStr}
|
|
36177
|
-
---
|
|
36178
|
-
${body}`;
|
|
36179
|
-
}
|
|
36180
|
-
function parseYaml(input) {
|
|
36181
|
-
const lines = input.split(/\r?\n/);
|
|
36182
|
-
const ctx = { lines, i: 0 };
|
|
36183
|
-
return parseNode(ctx, 0);
|
|
36184
|
-
}
|
|
36185
|
-
function parseNode(ctx, indent) {
|
|
36186
|
-
while (ctx.i < ctx.lines.length) {
|
|
36187
|
-
const line2 = ctx.lines[ctx.i];
|
|
36188
|
-
if (line2.trim() === "" || line2.trim().startsWith("#")) {
|
|
36189
|
-
ctx.i++;
|
|
36190
|
-
continue;
|
|
36191
|
-
}
|
|
36192
|
-
break;
|
|
36193
|
-
}
|
|
36194
|
-
if (ctx.i >= ctx.lines.length) return null;
|
|
36195
|
-
const line = ctx.lines[ctx.i];
|
|
36196
|
-
const lineIndent = countIndent(line);
|
|
36197
|
-
if (/^\s*-\s+/.test(line)) {
|
|
36198
|
-
return parseBlockSequence(ctx, indent);
|
|
36199
|
-
}
|
|
36200
|
-
if (/^\s*\[.*\]\s*$/.test(line)) {
|
|
36201
|
-
const flow = line.trim().replace(/^\[/, "").replace(/\]$/, "");
|
|
36202
|
-
return parseFlowSequence(flow);
|
|
36203
|
-
}
|
|
36204
|
-
if (/^\s*\{.*\}\s*$/.test(line)) {
|
|
36205
|
-
const flow = line.trim().replace(/^\{/, "").replace(/\}$/, "");
|
|
36206
|
-
return parseFlowMap(flow);
|
|
36207
|
-
}
|
|
36208
|
-
return parseBlockMap(ctx, indent);
|
|
36209
|
-
}
|
|
36210
|
-
function parseBlockMap(ctx, indent) {
|
|
36211
|
-
const out = {};
|
|
36212
|
-
while (ctx.i < ctx.lines.length) {
|
|
36213
|
-
const line = ctx.lines[ctx.i];
|
|
36214
|
-
if (line.trim() === "" || line.trim().startsWith("#")) {
|
|
36215
|
-
ctx.i++;
|
|
36216
|
-
continue;
|
|
36217
|
-
}
|
|
36218
|
-
const lineIndent = countIndent(line);
|
|
36219
|
-
if (lineIndent < indent) break;
|
|
36220
|
-
if (lineIndent > indent) {
|
|
36221
|
-
ctx.i++;
|
|
36222
|
-
continue;
|
|
36223
|
-
}
|
|
36224
|
-
const m = /^([^:]+):\s*(.*)$/.exec(line);
|
|
36225
|
-
if (!m) {
|
|
36226
|
-
ctx.i++;
|
|
36227
|
-
continue;
|
|
36228
|
-
}
|
|
36229
|
-
const key = m[1].trim();
|
|
36230
|
-
const valuePart = m[2].trim();
|
|
36231
|
-
if (valuePart === "" || valuePart === "|" || valuePart === ">") {
|
|
36232
|
-
ctx.i++;
|
|
36233
|
-
const nested = parseNode(ctx, indent + 2);
|
|
36234
|
-
out[key] = nested;
|
|
36235
|
-
} else {
|
|
36236
|
-
if (valuePart.startsWith("[")) {
|
|
36237
|
-
out[key] = parseFlowSequence(stripFlow(valuePart, "[", "]"));
|
|
36238
|
-
} else if (valuePart.startsWith("{")) {
|
|
36239
|
-
out[key] = parseFlowMap(stripFlow(valuePart, "{", "}"));
|
|
36240
|
-
} else {
|
|
36241
|
-
out[key] = parseScalar(valuePart);
|
|
36242
|
-
}
|
|
36243
|
-
ctx.i++;
|
|
36244
|
-
}
|
|
36245
|
-
}
|
|
36246
|
-
return out;
|
|
36247
|
-
}
|
|
36248
|
-
function parseBlockSequence(ctx, indent) {
|
|
36249
|
-
const out = [];
|
|
36250
|
-
while (ctx.i < ctx.lines.length) {
|
|
36251
|
-
const line = ctx.lines[ctx.i];
|
|
36252
|
-
if (line.trim() === "") {
|
|
36253
|
-
ctx.i++;
|
|
36254
|
-
continue;
|
|
36255
|
-
}
|
|
36256
|
-
const lineIndent = countIndent(line);
|
|
36257
|
-
if (lineIndent < indent) break;
|
|
36258
|
-
if (lineIndent > indent) break;
|
|
36259
|
-
const m = /^-\s*(.*)$/.exec(line);
|
|
36260
|
-
if (!m) break;
|
|
36261
|
-
const rest = m[1];
|
|
36262
|
-
if (rest === "") {
|
|
36263
|
-
ctx.i++;
|
|
36264
|
-
out.push(parseNode(ctx, indent + 2));
|
|
36265
|
-
} else if (rest.startsWith("[") || rest.startsWith("{")) {
|
|
36266
|
-
let buffer = rest;
|
|
36267
|
-
let depthSq = (buffer.match(/\[/g) || []).length - (buffer.match(/\]/g) || []).length;
|
|
36268
|
-
let depthCu = (buffer.match(/\{/g) || []).length - (buffer.match(/\}/g) || []).length;
|
|
36269
|
-
while ((depthSq > 0 || depthCu > 0) && ctx.i + 1 < ctx.lines.length) {
|
|
36270
|
-
ctx.i++;
|
|
36271
|
-
const next = ctx.lines[ctx.i].trim();
|
|
36272
|
-
buffer += " " + next;
|
|
36273
|
-
depthSq = (buffer.match(/\[/g) || []).length - (buffer.match(/\]/g) || []).length;
|
|
36274
|
-
depthCu = (buffer.match(/\{/g) || []).length - (buffer.match(/\}/g) || []).length;
|
|
36275
|
-
}
|
|
36276
|
-
if (buffer.startsWith("[")) {
|
|
36277
|
-
out.push(parseFlowSequence(buffer.slice(1).replace(/\]$/, "")));
|
|
36278
|
-
} else {
|
|
36279
|
-
out.push(parseFlowMap(buffer.slice(1).replace(/\}$/, "")));
|
|
36280
|
-
}
|
|
36281
|
-
ctx.i++;
|
|
36282
|
-
} else if (rest.includes(":")) {
|
|
36283
|
-
ctx.i++;
|
|
36284
|
-
const mapCtx = { lines: [" ".repeat(indent + 2) + rest, ...ctx.lines.slice(ctx.i)], i: 0 };
|
|
36285
|
-
const val = parseBlockMap(mapCtx, indent + 2);
|
|
36286
|
-
ctx.i += mapCtx.i - 1;
|
|
36287
|
-
out.push(val);
|
|
36288
|
-
} else {
|
|
36289
|
-
out.push(parseScalar(rest));
|
|
36290
|
-
ctx.i++;
|
|
36291
|
-
}
|
|
36292
|
-
}
|
|
36293
|
-
return out;
|
|
36294
|
-
}
|
|
36295
|
-
function parseFlowSequence(input) {
|
|
36296
|
-
const parts = splitFlow(input);
|
|
36297
|
-
return parts.map((p3) => {
|
|
36298
|
-
const trimmed = p3.trim();
|
|
36299
|
-
if (trimmed.startsWith("{")) {
|
|
36300
|
-
return parseFlowMap(stripFlow(trimmed, "{", "}"));
|
|
36301
|
-
}
|
|
36302
|
-
return parseScalar(trimmed);
|
|
36303
|
-
});
|
|
36304
|
-
}
|
|
36305
|
-
function stripFlow(s, open, close) {
|
|
36306
|
-
let out = s.trim();
|
|
36307
|
-
if (out.startsWith(open)) out = out.slice(1);
|
|
36308
|
-
if (out.endsWith(close)) out = out.slice(0, -1);
|
|
36309
|
-
return out;
|
|
36310
|
-
}
|
|
36311
|
-
function parseFlowMap(input) {
|
|
36312
|
-
const parts = splitFlow(input);
|
|
36313
|
-
const out = {};
|
|
36314
|
-
for (const p3 of parts) {
|
|
36315
|
-
const colonIdx = p3.indexOf(":");
|
|
36316
|
-
if (colonIdx < 0) continue;
|
|
36317
|
-
const key = p3.slice(0, colonIdx).trim();
|
|
36318
|
-
const value = p3.slice(colonIdx + 1).trim();
|
|
36319
|
-
out[key] = parseScalar(value);
|
|
36320
|
-
}
|
|
36321
|
-
return out;
|
|
36322
|
-
}
|
|
36323
|
-
function splitFlow(input) {
|
|
36324
|
-
const out = [];
|
|
36325
|
-
let depthSq = 0, depthCu = 0, depthQu = 0;
|
|
36326
|
-
let buffer = "";
|
|
36327
|
-
for (let i = 0; i < input.length; i++) {
|
|
36328
|
-
const c = input[i];
|
|
36329
|
-
if (c === '"' || c === "'") {
|
|
36330
|
-
depthQu = depthQu === 0 ? depthQu + 1 : 0;
|
|
36331
|
-
buffer += c;
|
|
36332
|
-
} else if (depthQu === 0) {
|
|
36333
|
-
if (c === "[") depthSq++;
|
|
36334
|
-
else if (c === "]") depthSq--;
|
|
36335
|
-
else if (c === "{") depthCu++;
|
|
36336
|
-
else if (c === "}") depthCu--;
|
|
36337
|
-
else if (c === "," && depthSq === 0 && depthCu === 0) {
|
|
36338
|
-
out.push(buffer);
|
|
36339
|
-
buffer = "";
|
|
36340
|
-
continue;
|
|
36341
|
-
}
|
|
36342
|
-
buffer += c;
|
|
36343
|
-
} else {
|
|
36344
|
-
buffer += c;
|
|
36345
|
-
}
|
|
36346
|
-
}
|
|
36347
|
-
if (buffer.trim()) out.push(buffer);
|
|
36348
|
-
return out;
|
|
36349
|
-
}
|
|
36350
|
-
function parseScalar(s) {
|
|
36351
|
-
if (s === "" || s === "null" || s === "~") return null;
|
|
36352
|
-
if (VALID_SCALARS.test(s)) return s.toLowerCase() === "true";
|
|
36353
|
-
if (/^-?\d+(\.\d+)?$/.test(s)) return Number(s);
|
|
36354
|
-
if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
|
|
36355
|
-
return s.slice(1, -1);
|
|
36356
|
-
}
|
|
36357
|
-
return s;
|
|
36358
|
-
}
|
|
36359
|
-
function serializeYaml(value, indent = 0) {
|
|
36360
|
-
if (value === null || value === void 0) return "";
|
|
36361
|
-
if (typeof value === "string") {
|
|
36362
|
-
if (/[:#\n\[\]\{\},&*!|>'"%@`]/.test(value)) {
|
|
36363
|
-
return JSON.stringify(value);
|
|
36364
|
-
}
|
|
36365
|
-
return value;
|
|
36366
|
-
}
|
|
36367
|
-
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
36368
|
-
if (Array.isArray(value)) {
|
|
36369
|
-
if (value.length === 0) return "[]";
|
|
36370
|
-
if (value.every((v) => v === null || typeof v !== "object")) {
|
|
36371
|
-
return `[${value.map(serializeScalarInline).join(", ")}]`;
|
|
36372
|
-
}
|
|
36373
|
-
return `[${value.map((v) => "{" + serializeInlineObject(v) + "}").join(", ")}]`;
|
|
36374
|
-
}
|
|
36375
|
-
if (typeof value === "object") {
|
|
36376
|
-
const entries = Object.entries(value).filter(([, v]) => v !== void 0);
|
|
36377
|
-
return entries.map(([k, v]) => {
|
|
36378
|
-
if (v === null || v === void 0) return `${k}:`;
|
|
36379
|
-
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
36380
|
-
return `${k}: ${serializeScalarInline(v)}`;
|
|
36381
|
-
}
|
|
36382
|
-
if (Array.isArray(v)) {
|
|
36383
|
-
if (v.length === 0) return `${k}: []`;
|
|
36384
|
-
if (v.every((x) => x === null || typeof x !== "object")) {
|
|
36385
|
-
return `${k}: [${v.map(serializeScalarInline).join(", ")}]`;
|
|
36386
|
-
}
|
|
36387
|
-
return `${k}: [${v.map((x) => "{" + serializeInlineObject(x) + "}").join(", ")}]`;
|
|
36388
|
-
}
|
|
36389
|
-
return `${k}:
|
|
36390
|
-
${serializeYaml(v, indent + 2)}`;
|
|
36391
|
-
}).map((line) => `${" ".repeat(indent)}${line}`).join("\n");
|
|
36392
|
-
}
|
|
36393
|
-
return String(value);
|
|
36394
|
-
}
|
|
36395
|
-
function serializeScalarInline(v) {
|
|
36396
|
-
if (typeof v === "string" && /[:#\n\[\]\{\},&*!|>'"%@`]/.test(v)) return JSON.stringify(v);
|
|
36397
|
-
return String(v);
|
|
36398
|
-
}
|
|
36399
|
-
function serializeInlineObject(v) {
|
|
36400
|
-
if (v === null || typeof v !== "object" || Array.isArray(v)) {
|
|
36401
|
-
return serializeScalarInline(v);
|
|
36402
|
-
}
|
|
36403
|
-
const entries = Object.entries(v).filter(([, val]) => val !== void 0);
|
|
36404
|
-
return entries.map(([k, val]) => `${k}: ${serializeYaml(val)}`).join(", ");
|
|
36405
|
-
}
|
|
36406
|
-
function countIndent(line) {
|
|
36407
|
-
let i = 0;
|
|
36408
|
-
while (i < line.length && line[i] === " ") i++;
|
|
36409
|
-
return i;
|
|
36410
|
-
}
|
|
36411
|
-
var FRONTMATTER_RE, VALID_SCALARS, Storage, KeyedMutex, workspaceMutex;
|
|
36412
|
-
var init_storage = __esm({
|
|
36413
|
-
"src/cli/workspace/storage.ts"() {
|
|
36414
|
-
"use strict";
|
|
36415
|
-
FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
36416
|
-
VALID_SCALARS = /^(true|false|null|~)$/i;
|
|
36417
|
-
Storage = class {
|
|
36418
|
-
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
36419
|
-
read(path53) {
|
|
36420
|
-
if (!existsSync27(path53)) {
|
|
36421
|
-
throw new Error(`File not found: ${path53}`);
|
|
36422
|
-
}
|
|
36423
|
-
const md = readFileSync24(path53, "utf8");
|
|
36424
|
-
return parseFrontmatter(md);
|
|
36425
|
-
}
|
|
36426
|
-
/** Read a Markdown file; returns null if not found. */
|
|
36427
|
-
readIfExists(path53) {
|
|
36428
|
-
if (!existsSync27(path53)) return null;
|
|
36429
|
-
return this.read(path53);
|
|
36430
|
-
}
|
|
36431
|
-
/**
|
|
36432
|
-
* Write a Markdown file atomically (tmp + rename). Creates parent dirs.
|
|
36433
|
-
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
36434
|
-
*/
|
|
36435
|
-
write(path53, meta3, body) {
|
|
36436
|
-
mkdirSync13(dirname4(path53), { recursive: true });
|
|
36437
|
-
const tmp = path53 + ".tmp-" + process.pid;
|
|
36438
|
-
const md = serializeFrontmatter(meta3, body);
|
|
36439
|
-
writeFileSync15(tmp, md, "utf8");
|
|
36440
|
-
renameSync2(tmp, path53);
|
|
36441
|
-
}
|
|
36442
|
-
/** List all .md files in a directory (non-recursive). */
|
|
36443
|
-
listMarkdown(dir) {
|
|
36444
|
-
if (!existsSync27(dir)) return [];
|
|
36445
|
-
return readdirSync7(dir).filter((f) => f.endsWith(".md") && !f.startsWith(".")).map((f) => join23(dir, f));
|
|
36446
|
-
}
|
|
36447
|
-
};
|
|
36448
|
-
KeyedMutex = class {
|
|
36449
|
-
chains = /* @__PURE__ */ new Map();
|
|
36450
|
-
async run(key, fn) {
|
|
36451
|
-
const prev2 = this.chains.get(key) ?? Promise.resolve();
|
|
36452
|
-
let release = () => {
|
|
36453
|
-
};
|
|
36454
|
-
const next = new Promise((resolve3) => {
|
|
36455
|
-
release = resolve3;
|
|
36456
|
-
});
|
|
36457
|
-
const chained = prev2.then(() => next);
|
|
36458
|
-
this.chains.set(key, chained);
|
|
36459
|
-
await prev2;
|
|
36460
|
-
try {
|
|
36461
|
-
return await fn();
|
|
36462
|
-
} finally {
|
|
36463
|
-
release();
|
|
36464
|
-
if (this.chains.get(key) === chained) {
|
|
36465
|
-
this.chains.delete(key);
|
|
36466
|
-
}
|
|
36467
|
-
}
|
|
36468
|
-
}
|
|
36469
|
-
};
|
|
36470
|
-
workspaceMutex = new KeyedMutex();
|
|
36471
|
-
}
|
|
36472
|
-
});
|
|
36473
|
-
|
|
36474
36861
|
// src/cli/workspace/stubs.ts
|
|
36475
36862
|
var stubs_exports = {};
|
|
36476
36863
|
__export(stubs_exports, {
|
|
@@ -36481,14 +36868,14 @@ __export(stubs_exports, {
|
|
|
36481
36868
|
resolveWorkspaceRoot: () => resolveWorkspaceRoot
|
|
36482
36869
|
});
|
|
36483
36870
|
import {
|
|
36484
|
-
existsSync as
|
|
36871
|
+
existsSync as existsSync29,
|
|
36485
36872
|
readdirSync as readdirSync8,
|
|
36486
|
-
writeFileSync as
|
|
36487
|
-
readFileSync as
|
|
36488
|
-
mkdirSync as
|
|
36489
|
-
renameSync as
|
|
36873
|
+
writeFileSync as writeFileSync17,
|
|
36874
|
+
readFileSync as readFileSync26,
|
|
36875
|
+
mkdirSync as mkdirSync15,
|
|
36876
|
+
renameSync as renameSync4
|
|
36490
36877
|
} from "node:fs";
|
|
36491
|
-
import { join as
|
|
36878
|
+
import { join as join25, basename as basename2, dirname as dirname6, relative as relative2 } from "node:path";
|
|
36492
36879
|
function createWorkspaceContext(projectRoot = process.cwd()) {
|
|
36493
36880
|
const rootDir = resolveWorkspaceRoot(projectRoot);
|
|
36494
36881
|
return {
|
|
@@ -36498,19 +36885,21 @@ function createWorkspaceContext(projectRoot = process.cwd()) {
|
|
|
36498
36885
|
};
|
|
36499
36886
|
}
|
|
36500
36887
|
function planJsonPath(ctx) {
|
|
36501
|
-
return
|
|
36888
|
+
return join25(ctx.rootDir, "plan.json");
|
|
36502
36889
|
}
|
|
36503
36890
|
function readPlan(ctx) {
|
|
36504
36891
|
const jsonPath = planJsonPath(ctx);
|
|
36505
|
-
if (
|
|
36892
|
+
if (existsSync29(jsonPath)) {
|
|
36506
36893
|
try {
|
|
36507
36894
|
const parsed = JSON.parse(
|
|
36508
|
-
|
|
36895
|
+
readFileSync26(jsonPath, "utf8")
|
|
36509
36896
|
);
|
|
36897
|
+
const { phases, tasks, milestones, ...root } = parsed;
|
|
36510
36898
|
return {
|
|
36511
|
-
phases: Array.isArray(
|
|
36512
|
-
tasks: Array.isArray(
|
|
36513
|
-
milestones: Array.isArray(
|
|
36899
|
+
phases: Array.isArray(phases) ? phases : [],
|
|
36900
|
+
tasks: Array.isArray(tasks) ? tasks : [],
|
|
36901
|
+
milestones: Array.isArray(milestones) ? milestones : [],
|
|
36902
|
+
root
|
|
36514
36903
|
};
|
|
36515
36904
|
} catch {
|
|
36516
36905
|
}
|
|
@@ -36527,10 +36916,23 @@ function readPlan(ctx) {
|
|
|
36527
36916
|
}
|
|
36528
36917
|
function writePlan(ctx, summary) {
|
|
36529
36918
|
const jsonPath = planJsonPath(ctx);
|
|
36530
|
-
|
|
36919
|
+
mkdirSync15(dirname6(jsonPath), { recursive: true });
|
|
36531
36920
|
const tmp = jsonPath + ".tmp-" + process.pid;
|
|
36532
|
-
|
|
36533
|
-
|
|
36921
|
+
writeFileSync17(
|
|
36922
|
+
tmp,
|
|
36923
|
+
JSON.stringify(
|
|
36924
|
+
{
|
|
36925
|
+
...summary.root ?? {},
|
|
36926
|
+
phases: summary.phases,
|
|
36927
|
+
tasks: summary.tasks,
|
|
36928
|
+
milestones: summary.milestones
|
|
36929
|
+
},
|
|
36930
|
+
null,
|
|
36931
|
+
2
|
|
36932
|
+
),
|
|
36933
|
+
"utf8"
|
|
36934
|
+
);
|
|
36935
|
+
renameSync4(tmp, jsonPath);
|
|
36534
36936
|
const mdPath = workspaceFile(ctx.rootDir, "plan");
|
|
36535
36937
|
const summaryMeta = {
|
|
36536
36938
|
kind: "plan-summary",
|
|
@@ -36596,8 +36998,8 @@ function renderPlanBody(summary) {
|
|
|
36596
36998
|
return lines.join("\n");
|
|
36597
36999
|
}
|
|
36598
37000
|
function nextAdrId(ctx) {
|
|
36599
|
-
const decisionsDir =
|
|
36600
|
-
if (!
|
|
37001
|
+
const decisionsDir = join25(ctx.rootDir, "decisions");
|
|
37002
|
+
if (!existsSync29(decisionsDir)) return "001";
|
|
36601
37003
|
const existing = readdirSync8(decisionsDir).filter((f) => f.endsWith(".md")).map((f) => f.match(/^(\d+)-/)).filter((m) => !!m).map((m) => parseInt(m[1], 10));
|
|
36602
37004
|
const max = existing.length === 0 ? 0 : Math.max(...existing);
|
|
36603
37005
|
return String(max + 1).padStart(3, "0");
|
|
@@ -36639,7 +37041,7 @@ function addTaskRecord(ctx, summary, phaseId, t, options) {
|
|
|
36639
37041
|
status: "pending",
|
|
36640
37042
|
priority: t.priority
|
|
36641
37043
|
});
|
|
36642
|
-
const taskPath =
|
|
37044
|
+
const taskPath = join25(ctx.rootDir, "plan-tasks", `${id}.md`);
|
|
36643
37045
|
const meta3 = {
|
|
36644
37046
|
kind: "task",
|
|
36645
37047
|
id,
|
|
@@ -36681,7 +37083,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
36681
37083
|
dueDate: input.dueDate,
|
|
36682
37084
|
targetVersion: version2
|
|
36683
37085
|
});
|
|
36684
|
-
const path53 =
|
|
37086
|
+
const path53 = join25(ctx.rootDir, "milestones", `${id}.md`);
|
|
36685
37087
|
const meta3 = {
|
|
36686
37088
|
kind: "milestone",
|
|
36687
37089
|
id,
|
|
@@ -36979,8 +37381,8 @@ function createNfrSpecStub(ctx) {
|
|
|
36979
37381
|
},
|
|
36980
37382
|
planFeatureKeywords: Array.isArray(args["planFeatureKeywords"]) ? args["planFeatureKeywords"] : void 0
|
|
36981
37383
|
};
|
|
36982
|
-
const outPath =
|
|
36983
|
-
|
|
37384
|
+
const outPath = join25(ctx.rootDir, "nfr-spec.json");
|
|
37385
|
+
writeFileSync17(outPath, JSON.stringify(spec, null, 2), "utf8");
|
|
36984
37386
|
return `NFR spec written to nfr-spec.json (${targets.length} target(s)).`;
|
|
36985
37387
|
});
|
|
36986
37388
|
}
|
|
@@ -37043,18 +37445,18 @@ function searchDocumentsStub(ctx) {
|
|
|
37043
37445
|
(w) => w.length >= 3 && w !== "or" && w !== "and" && w !== "the"
|
|
37044
37446
|
);
|
|
37045
37447
|
const files = [
|
|
37046
|
-
...ctx.storage.listMarkdown(
|
|
37047
|
-
...ctx.storage.listMarkdown(
|
|
37048
|
-
...ctx.storage.listMarkdown(
|
|
37049
|
-
...ctx.storage.listMarkdown(
|
|
37050
|
-
...ctx.storage.listMarkdown(
|
|
37448
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "decisions")),
|
|
37449
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "docs")),
|
|
37450
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "reviews")),
|
|
37451
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "plan-tasks")),
|
|
37452
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "milestones")),
|
|
37051
37453
|
workspaceFile(ctx.rootDir, "plan"),
|
|
37052
37454
|
workspaceFile(ctx.rootDir, "risks")
|
|
37053
37455
|
];
|
|
37054
37456
|
const results = [];
|
|
37055
37457
|
for (const file2 of files) {
|
|
37056
|
-
if (!
|
|
37057
|
-
const raw =
|
|
37458
|
+
if (!existsSync29(file2)) continue;
|
|
37459
|
+
const raw = readFileSync26(file2, "utf8");
|
|
37058
37460
|
const content = raw.toLowerCase();
|
|
37059
37461
|
let idx = -1;
|
|
37060
37462
|
let matchLen = 0;
|
|
@@ -37105,9 +37507,9 @@ function linkDocumentsStub(ctx) {
|
|
|
37105
37507
|
const toId = args["toId"] ?? args["targetId"] ?? args["targetPathOrTitle"];
|
|
37106
37508
|
if (!fromId || !toId) return "linkDocuments requires fromId and toId.";
|
|
37107
37509
|
const allFiles = [
|
|
37108
|
-
...ctx.storage.listMarkdown(
|
|
37109
|
-
...ctx.storage.listMarkdown(
|
|
37110
|
-
...ctx.storage.listMarkdown(
|
|
37510
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "decisions")),
|
|
37511
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "docs")),
|
|
37512
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "reviews"))
|
|
37111
37513
|
];
|
|
37112
37514
|
const source = allFiles.find((f) => {
|
|
37113
37515
|
try {
|
|
@@ -37138,9 +37540,9 @@ function getDocumentBacklinksStub(ctx) {
|
|
|
37138
37540
|
const targetId = args["targetId"] ?? args["id"];
|
|
37139
37541
|
if (!targetId) return "getDocumentBacklinks requires targetId.";
|
|
37140
37542
|
const allFiles = [
|
|
37141
|
-
...ctx.storage.listMarkdown(
|
|
37142
|
-
...ctx.storage.listMarkdown(
|
|
37143
|
-
...ctx.storage.listMarkdown(
|
|
37543
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "decisions")),
|
|
37544
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "docs")),
|
|
37545
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "reviews"))
|
|
37144
37546
|
];
|
|
37145
37547
|
const backlinks = [];
|
|
37146
37548
|
for (const file2 of allFiles) {
|
|
@@ -37235,7 +37637,7 @@ __export(updater_exports, {
|
|
|
37235
37637
|
});
|
|
37236
37638
|
import { createRequire as createRequire2 } from "node:module";
|
|
37237
37639
|
import { spawn as spawn8 } from "node:child_process";
|
|
37238
|
-
import { existsSync as
|
|
37640
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
37239
37641
|
import path31 from "node:path";
|
|
37240
37642
|
import { fileURLToPath } from "node:url";
|
|
37241
37643
|
function resolveBundledNpmCli(execPath = process.execPath) {
|
|
@@ -37248,7 +37650,7 @@ function resolveBundledNpmCli(execPath = process.execPath) {
|
|
|
37248
37650
|
];
|
|
37249
37651
|
for (const candidate of candidates) {
|
|
37250
37652
|
try {
|
|
37251
|
-
if (
|
|
37653
|
+
if (existsSync30(candidate)) return candidate;
|
|
37252
37654
|
} catch {
|
|
37253
37655
|
}
|
|
37254
37656
|
}
|
|
@@ -37548,23 +37950,23 @@ var init_mcpClient = __esm({
|
|
|
37548
37950
|
|
|
37549
37951
|
// src/cli/mcp/mcpConfigIo.ts
|
|
37550
37952
|
import {
|
|
37551
|
-
existsSync as
|
|
37552
|
-
mkdirSync as
|
|
37553
|
-
readFileSync as
|
|
37554
|
-
writeFileSync as
|
|
37953
|
+
existsSync as existsSync31,
|
|
37954
|
+
mkdirSync as mkdirSync16,
|
|
37955
|
+
readFileSync as readFileSync27,
|
|
37956
|
+
writeFileSync as writeFileSync18
|
|
37555
37957
|
} from "node:fs";
|
|
37556
|
-
import { dirname as
|
|
37958
|
+
import { dirname as dirname7, join as join26 } from "node:path";
|
|
37557
37959
|
import { homedir as homedir10 } from "node:os";
|
|
37558
37960
|
function getUserMcpPath() {
|
|
37559
|
-
return
|
|
37961
|
+
return join26(homedir10(), ".zelari-code", "mcp.json");
|
|
37560
37962
|
}
|
|
37561
37963
|
function getProjectMcpPath(projectRoot) {
|
|
37562
|
-
return
|
|
37964
|
+
return join26(projectRoot, ".zelari", "mcp.json");
|
|
37563
37965
|
}
|
|
37564
37966
|
function readFile2(path53) {
|
|
37565
|
-
if (!
|
|
37967
|
+
if (!existsSync31(path53)) return {};
|
|
37566
37968
|
try {
|
|
37567
|
-
const parsed = JSON.parse(
|
|
37969
|
+
const parsed = JSON.parse(readFileSync27(path53, "utf8"));
|
|
37568
37970
|
const out = {};
|
|
37569
37971
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
37570
37972
|
if (!cfg || typeof cfg.command !== "string" || !cfg.command.trim()) continue;
|
|
@@ -37581,9 +37983,9 @@ function readFile2(path53) {
|
|
|
37581
37983
|
}
|
|
37582
37984
|
}
|
|
37583
37985
|
function writeFile(path53, servers) {
|
|
37584
|
-
|
|
37986
|
+
mkdirSync16(dirname7(path53), { recursive: true });
|
|
37585
37987
|
const body = { mcpServers: servers };
|
|
37586
|
-
|
|
37988
|
+
writeFileSync18(path53, `${JSON.stringify(body, null, 2)}
|
|
37587
37989
|
`, "utf8");
|
|
37588
37990
|
}
|
|
37589
37991
|
function listMcpServers(projectRoot) {
|
|
@@ -37792,22 +38194,22 @@ __export(mcpManager_exports, {
|
|
|
37792
38194
|
readMcpConfig: () => readMcpConfig,
|
|
37793
38195
|
registerMcpTools: () => registerMcpTools
|
|
37794
38196
|
});
|
|
37795
|
-
import { existsSync as
|
|
37796
|
-
import { join as
|
|
38197
|
+
import { existsSync as existsSync32, readFileSync as readFileSync28 } from "node:fs";
|
|
38198
|
+
import { join as join27 } from "node:path";
|
|
37797
38199
|
import { homedir as homedir11 } from "node:os";
|
|
37798
38200
|
function readMcpConfig(projectRoot = process.cwd(), opts) {
|
|
37799
38201
|
const merged = {};
|
|
37800
38202
|
const paths = [];
|
|
37801
38203
|
if (process.env["ZELARI_MCP_USER"] !== "0") {
|
|
37802
|
-
paths.push(
|
|
38204
|
+
paths.push(join27(homedir11(), ".zelari-code", "mcp.json"));
|
|
37803
38205
|
}
|
|
37804
38206
|
if (!opts?.skipProjectMcp) {
|
|
37805
|
-
paths.push(
|
|
38207
|
+
paths.push(join27(projectRoot, ".zelari", "mcp.json"));
|
|
37806
38208
|
}
|
|
37807
38209
|
for (const p3 of paths) {
|
|
37808
|
-
if (!
|
|
38210
|
+
if (!existsSync32(p3)) continue;
|
|
37809
38211
|
try {
|
|
37810
|
-
const parsed = JSON.parse(
|
|
38212
|
+
const parsed = JSON.parse(readFileSync28(p3, "utf8"));
|
|
37811
38213
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
37812
38214
|
if (!cfg || typeof cfg.command !== "string" || cfg.command.length === 0) continue;
|
|
37813
38215
|
merged[name] = cfg;
|
|
@@ -37821,7 +38223,7 @@ async function ensureLoaded(projectRoot) {
|
|
|
37821
38223
|
if (state2.loaded) return;
|
|
37822
38224
|
state2.loaded = true;
|
|
37823
38225
|
const trusted = isFolderTrusted(projectRoot);
|
|
37824
|
-
if (!trusted &&
|
|
38226
|
+
if (!trusted && existsSync32(join27(projectRoot, ".zelari", "mcp.json"))) {
|
|
37825
38227
|
state2.warnings.push(
|
|
37826
38228
|
"[mcp] project .zelari/mcp.json ignored \u2014 folder not trusted (run /trust or `zelari-code --trust` to enable project MCP)"
|
|
37827
38229
|
);
|
|
@@ -38046,13 +38448,13 @@ __export(agentsMd_exports, {
|
|
|
38046
38448
|
serializeAgentsMd: () => serializeAgentsMd,
|
|
38047
38449
|
updateAgentsMd: () => updateAgentsMd
|
|
38048
38450
|
});
|
|
38049
|
-
import { existsSync as
|
|
38451
|
+
import { existsSync as existsSync33, readFileSync as readFileSync29, writeFileSync as writeFileSync19 } from "node:fs";
|
|
38050
38452
|
import { createHash as createHash8 } from "node:crypto";
|
|
38051
|
-
import { join as
|
|
38453
|
+
import { join as join28 } from "node:path";
|
|
38052
38454
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
38053
38455
|
async function readPackageJson2(projectRoot) {
|
|
38054
|
-
const path53 =
|
|
38055
|
-
if (!
|
|
38456
|
+
const path53 = join28(projectRoot, "package.json");
|
|
38457
|
+
if (!existsSync33(path53)) return null;
|
|
38056
38458
|
try {
|
|
38057
38459
|
return JSON.parse(await readFile3(path53, "utf8"));
|
|
38058
38460
|
} catch {
|
|
@@ -38076,8 +38478,8 @@ async function genTechStack(ctx) {
|
|
|
38076
38478
|
].join("\n");
|
|
38077
38479
|
}
|
|
38078
38480
|
async function genDecisions(ctx) {
|
|
38079
|
-
const decisionsDir =
|
|
38080
|
-
if (!
|
|
38481
|
+
const decisionsDir = join28(ctx.rootDir, "decisions");
|
|
38482
|
+
if (!existsSync33(decisionsDir)) return "_No ADRs yet._";
|
|
38081
38483
|
const files = ctx.storage.listMarkdown(decisionsDir).sort();
|
|
38082
38484
|
const accepted = [];
|
|
38083
38485
|
const proposed = [];
|
|
@@ -38102,9 +38504,9 @@ async function genDecisions(ctx) {
|
|
|
38102
38504
|
}
|
|
38103
38505
|
async function genConventions(ctx) {
|
|
38104
38506
|
const lines = [];
|
|
38105
|
-
const claudeMd =
|
|
38106
|
-
if (
|
|
38107
|
-
const content =
|
|
38507
|
+
const claudeMd = join28(ctx.projectRoot, "CLAUDE.MD");
|
|
38508
|
+
if (existsSync33(claudeMd)) {
|
|
38509
|
+
const content = readFileSync29(claudeMd, "utf8");
|
|
38108
38510
|
const match = content.match(/## Architecture rules[\s\S]+?(?=\n## |\n*$)/);
|
|
38109
38511
|
if (match) {
|
|
38110
38512
|
lines.push('<!-- Extracted from CLAUDE.MD "Architecture rules" -->');
|
|
@@ -38136,9 +38538,9 @@ async function genBuild(ctx) {
|
|
|
38136
38538
|
].join("\n");
|
|
38137
38539
|
}
|
|
38138
38540
|
async function genOpenQuestions(ctx) {
|
|
38139
|
-
const path53 =
|
|
38140
|
-
if (!
|
|
38141
|
-
const content =
|
|
38541
|
+
const path53 = join28(ctx.rootDir, "risks.md");
|
|
38542
|
+
if (!existsSync33(path53)) return "_No open questions._";
|
|
38543
|
+
const content = readFileSync29(path53, "utf8");
|
|
38142
38544
|
const lines = content.split("\n");
|
|
38143
38545
|
const questions = [];
|
|
38144
38546
|
let currentTitle = "";
|
|
@@ -38212,9 +38614,9 @@ function titleCase(id) {
|
|
|
38212
38614
|
return id.split("-").map((w) => w[0].toUpperCase() + w.slice(1)).join(" ");
|
|
38213
38615
|
}
|
|
38214
38616
|
async function updateAgentsMd(ctx, projectRoot) {
|
|
38215
|
-
const agentsPath =
|
|
38216
|
-
if (
|
|
38217
|
-
const content =
|
|
38617
|
+
const agentsPath = join28(projectRoot, "AGENTS.MD");
|
|
38618
|
+
if (existsSync33(agentsPath)) {
|
|
38619
|
+
const content = readFileSync29(agentsPath, "utf8");
|
|
38218
38620
|
const hasAnyMarker = AUTO_SECTIONS.some((id) => content.includes(MARKER_OPEN(id)));
|
|
38219
38621
|
if (!hasAnyMarker) {
|
|
38220
38622
|
return {
|
|
@@ -38229,8 +38631,8 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
38229
38631
|
newSections.set(id, await GENERATORS[id](ctx));
|
|
38230
38632
|
}
|
|
38231
38633
|
let manualContent = "";
|
|
38232
|
-
if (
|
|
38233
|
-
const { manualBlocks } = parseAgentsMd(
|
|
38634
|
+
if (existsSync33(agentsPath)) {
|
|
38635
|
+
const { manualBlocks } = parseAgentsMd(readFileSync29(agentsPath, "utf8"));
|
|
38234
38636
|
manualContent = manualBlocks.after;
|
|
38235
38637
|
} else {
|
|
38236
38638
|
const projectName2 = projectName(projectRoot);
|
|
@@ -38246,7 +38648,7 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
38246
38648
|
""
|
|
38247
38649
|
].join("\n");
|
|
38248
38650
|
}
|
|
38249
|
-
const oldContent =
|
|
38651
|
+
const oldContent = existsSync33(agentsPath) ? readFileSync29(agentsPath, "utf8") : "";
|
|
38250
38652
|
const { sections: oldSections } = parseAgentsMd(oldContent);
|
|
38251
38653
|
const changedSections = [];
|
|
38252
38654
|
for (const id of AUTO_SECTIONS) {
|
|
@@ -38258,7 +38660,7 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
38258
38660
|
return { changed: false, sections: [] };
|
|
38259
38661
|
}
|
|
38260
38662
|
const newContent = serializeAgentsMd(manualContent, newSections);
|
|
38261
|
-
|
|
38663
|
+
writeFileSync19(agentsPath, newContent, "utf8");
|
|
38262
38664
|
return { changed: true, sections: changedSections };
|
|
38263
38665
|
}
|
|
38264
38666
|
function hash2(s) {
|
|
@@ -38382,10 +38784,181 @@ var init_completeDesign = __esm({
|
|
|
38382
38784
|
}
|
|
38383
38785
|
});
|
|
38384
38786
|
|
|
38787
|
+
// src/cli/workspace/planDriftCheck.ts
|
|
38788
|
+
import { existsSync as existsSync34, readFileSync as readFileSync30, readdirSync as readdirSync9, statSync as statSync5, writeFileSync as writeFileSync20 } from "node:fs";
|
|
38789
|
+
import { join as join29 } from "node:path";
|
|
38790
|
+
function findCanonicalDoc(rootDir) {
|
|
38791
|
+
const docsDir = join29(rootDir, "docs");
|
|
38792
|
+
if (!existsSync34(docsDir)) return null;
|
|
38793
|
+
const candidates = readdirSync9(docsDir).filter((f) => /^plan-canonical.*\.md$/i.test(f)).map((f) => ({ f, mtime: statSync5(join29(docsDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
38794
|
+
return candidates.length > 0 ? candidates[0].f : null;
|
|
38795
|
+
}
|
|
38796
|
+
function parseCanonicalDoc(text) {
|
|
38797
|
+
const activePhases = /* @__PURE__ */ new Set();
|
|
38798
|
+
const blockedPrefixes = /* @__PURE__ */ new Set();
|
|
38799
|
+
const headingPhase = /^#{2,6}[^\n]*?`([a-z0-9][a-z0-9-]*)`/gm;
|
|
38800
|
+
for (const m of text.matchAll(headingPhase)) activePhases.add(m[1]);
|
|
38801
|
+
const blocked = /`([a-z0-9][a-z0-9-]*)-\*`/g;
|
|
38802
|
+
for (const m of text.matchAll(blocked)) blockedPrefixes.add(`${m[1]}-`);
|
|
38803
|
+
return { activePhases, blockedPrefixes };
|
|
38804
|
+
}
|
|
38805
|
+
function normalizeTitle(value) {
|
|
38806
|
+
if (typeof value !== "string") return "";
|
|
38807
|
+
return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
38808
|
+
}
|
|
38809
|
+
function versionKey(value) {
|
|
38810
|
+
if (typeof value !== "string") return "";
|
|
38811
|
+
const m = value.toLowerCase().match(/v?\d+(?:\.\d+)*/);
|
|
38812
|
+
return m ? m[0].replace(/^v/, "") : value.trim().toLowerCase();
|
|
38813
|
+
}
|
|
38814
|
+
function firstString2(v) {
|
|
38815
|
+
return typeof v === "string" && v.trim().length > 0 ? v : null;
|
|
38816
|
+
}
|
|
38817
|
+
function readFileSyncSafe(path53) {
|
|
38818
|
+
try {
|
|
38819
|
+
return readFileSync30(path53, "utf8");
|
|
38820
|
+
} catch {
|
|
38821
|
+
return null;
|
|
38822
|
+
}
|
|
38823
|
+
}
|
|
38824
|
+
async function runPlanDriftCheck(rootDir) {
|
|
38825
|
+
if (process.env["ZELARI_DRIFT_CHECK"] === "0") {
|
|
38826
|
+
return { ran: false, reason: "ZELARI_DRIFT_CHECK=0 (disabled)" };
|
|
38827
|
+
}
|
|
38828
|
+
const planPath = join29(rootDir, "plan.json");
|
|
38829
|
+
if (!existsSync34(planPath)) {
|
|
38830
|
+
return { ran: false, reason: ".zelari/plan.json missing (not design-phase)" };
|
|
38831
|
+
}
|
|
38832
|
+
let plan;
|
|
38833
|
+
try {
|
|
38834
|
+
plan = JSON.parse(readFileSync30(planPath, "utf8"));
|
|
38835
|
+
} catch {
|
|
38836
|
+
return { ran: false, reason: ".zelari/plan.json corrupt" };
|
|
38837
|
+
}
|
|
38838
|
+
const findings = [];
|
|
38839
|
+
const phases = Array.isArray(plan.phases) ? plan.phases : [];
|
|
38840
|
+
const tasks = Array.isArray(plan.tasks) ? plan.tasks : [];
|
|
38841
|
+
const milestones = Array.isArray(plan.milestones) ? plan.milestones : [];
|
|
38842
|
+
const phaseIds = new Set(phases.map((p3) => typeof p3.id === "string" ? p3.id : "").filter(Boolean));
|
|
38843
|
+
const canonicalName = findCanonicalDoc(rootDir);
|
|
38844
|
+
const canonicalText = canonicalName ? readFileSyncSafe(join29(rootDir, "docs", canonicalName)) : null;
|
|
38845
|
+
if (canonicalText !== null) {
|
|
38846
|
+
const { activePhases, blockedPrefixes } = parseCanonicalDoc(canonicalText);
|
|
38847
|
+
for (const id of activePhases) {
|
|
38848
|
+
if (!phaseIds.has(id)) {
|
|
38849
|
+
findings.push({
|
|
38850
|
+
code: "CANONICAL_PHASE_MISSING",
|
|
38851
|
+
severity: "error",
|
|
38852
|
+
message: `canonical phase \`${id}\` (${canonicalName}) is missing from plan.json`
|
|
38853
|
+
});
|
|
38854
|
+
}
|
|
38855
|
+
}
|
|
38856
|
+
for (const id of phaseIds) {
|
|
38857
|
+
const hit = [...blockedPrefixes].find((p3) => id.startsWith(p3));
|
|
38858
|
+
if (hit) {
|
|
38859
|
+
findings.push({
|
|
38860
|
+
code: "PHASE_IN_CANONICAL_BLOCKLIST",
|
|
38861
|
+
severity: "error",
|
|
38862
|
+
message: `phase \`${id}\` matches canonical duplicate/descope prefix \`${hit}*\``
|
|
38863
|
+
});
|
|
38864
|
+
}
|
|
38865
|
+
}
|
|
38866
|
+
for (const t of tasks) {
|
|
38867
|
+
const id = typeof t.id === "string" ? t.id : "";
|
|
38868
|
+
const hit = id ? [...blockedPrefixes].find((p3) => id.startsWith(p3)) : void 0;
|
|
38869
|
+
if (hit) {
|
|
38870
|
+
findings.push({
|
|
38871
|
+
code: "TASK_IN_CANONICAL_BLOCKLIST",
|
|
38872
|
+
severity: "error",
|
|
38873
|
+
message: `task \`${id}\` matches canonical duplicate/descope prefix \`${hit}*\``
|
|
38874
|
+
});
|
|
38875
|
+
}
|
|
38876
|
+
}
|
|
38877
|
+
}
|
|
38878
|
+
const byVersion = /* @__PURE__ */ new Map();
|
|
38879
|
+
for (const m of milestones) {
|
|
38880
|
+
const key = versionKey(m.targetVersion);
|
|
38881
|
+
if (!key) continue;
|
|
38882
|
+
const id = typeof m.id === "string" ? m.id : "(no id)";
|
|
38883
|
+
byVersion.set(key, [...byVersion.get(key) ?? [], id]);
|
|
38884
|
+
}
|
|
38885
|
+
for (const [version2, ids] of byVersion) {
|
|
38886
|
+
if (ids.length > 1) {
|
|
38887
|
+
findings.push({
|
|
38888
|
+
code: "DUPLICATE_MILESTONE",
|
|
38889
|
+
severity: "error",
|
|
38890
|
+
message: `${ids.length} milestones target ${version2}: ${ids.join(", ")} \u2014 keep exactly one canonical`
|
|
38891
|
+
});
|
|
38892
|
+
}
|
|
38893
|
+
}
|
|
38894
|
+
const byTitle = /* @__PURE__ */ new Map();
|
|
38895
|
+
for (const t of tasks) {
|
|
38896
|
+
const title = normalizeTitle(
|
|
38897
|
+
firstString2(t.title) ?? firstString2(t.name) ?? firstString2(t.description)
|
|
38898
|
+
);
|
|
38899
|
+
const id = typeof t.id === "string" ? t.id : "(no id)";
|
|
38900
|
+
if (title) byTitle.set(title, [...byTitle.get(title) ?? [], id]);
|
|
38901
|
+
const phaseId = firstString2(t.phaseId);
|
|
38902
|
+
if (phaseId && !phaseIds.has(phaseId)) {
|
|
38903
|
+
findings.push({
|
|
38904
|
+
code: "TASK_IN_UNKNOWN_PHASE",
|
|
38905
|
+
severity: "warning",
|
|
38906
|
+
message: `task \`${id}\` references unknown phaseId \`${phaseId}\``
|
|
38907
|
+
});
|
|
38908
|
+
}
|
|
38909
|
+
}
|
|
38910
|
+
for (const [title, ids] of byTitle) {
|
|
38911
|
+
if (ids.length > 1) {
|
|
38912
|
+
findings.push({
|
|
38913
|
+
code: "DUPLICATE_TASK_TITLE",
|
|
38914
|
+
severity: "warning",
|
|
38915
|
+
message: `duplicate task title "${title.slice(0, 60)}": ${ids.join(", ")}`
|
|
38916
|
+
});
|
|
38917
|
+
}
|
|
38918
|
+
}
|
|
38919
|
+
const ok = findings.every((f) => f.severity !== "error");
|
|
38920
|
+
const canonicalParsed = canonicalName !== null && canonicalText !== null;
|
|
38921
|
+
let reportPath;
|
|
38922
|
+
try {
|
|
38923
|
+
reportPath = join29(rootDir, "drift-report.json");
|
|
38924
|
+
writeFileSync20(
|
|
38925
|
+
reportPath,
|
|
38926
|
+
JSON.stringify(
|
|
38927
|
+
{
|
|
38928
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
38929
|
+
ok,
|
|
38930
|
+
canonicalDoc: canonicalName ?? null,
|
|
38931
|
+
counts: { phases: phases.length, tasks: tasks.length, milestones: milestones.length },
|
|
38932
|
+
findings
|
|
38933
|
+
},
|
|
38934
|
+
null,
|
|
38935
|
+
2
|
|
38936
|
+
) + "\n",
|
|
38937
|
+
"utf8"
|
|
38938
|
+
);
|
|
38939
|
+
} catch {
|
|
38940
|
+
reportPath = void 0;
|
|
38941
|
+
}
|
|
38942
|
+
return {
|
|
38943
|
+
ran: true,
|
|
38944
|
+
ok,
|
|
38945
|
+
findings,
|
|
38946
|
+
...canonicalParsed ? { canonicalDoc: canonicalName } : {
|
|
38947
|
+
reason: canonicalName ? `canonical doc ${canonicalName} unreadable (structural checks only)` : "no canonical doc (structural checks only)"
|
|
38948
|
+
},
|
|
38949
|
+
...reportPath ? { reportPath } : {}
|
|
38950
|
+
};
|
|
38951
|
+
}
|
|
38952
|
+
var init_planDriftCheck = __esm({
|
|
38953
|
+
"src/cli/workspace/planDriftCheck.ts"() {
|
|
38954
|
+
"use strict";
|
|
38955
|
+
}
|
|
38956
|
+
});
|
|
38957
|
+
|
|
38385
38958
|
// src/cli/workspace/projectSmoke.ts
|
|
38386
38959
|
import { spawn as spawn10 } from "node:child_process";
|
|
38387
|
-
import { existsSync as
|
|
38388
|
-
import { join as
|
|
38960
|
+
import { existsSync as existsSync35, readFileSync as readFileSync31 } from "node:fs";
|
|
38961
|
+
import { join as join30 } from "node:path";
|
|
38389
38962
|
function pickSmokeScript(scripts) {
|
|
38390
38963
|
if (!scripts) return null;
|
|
38391
38964
|
for (const name of SMOKE_SCRIPT_PRIORITY) {
|
|
@@ -38397,13 +38970,13 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS2) {
|
|
|
38397
38970
|
if (process.env["ZELARI_SMOKE"] === "0") {
|
|
38398
38971
|
return { ran: false, reason: "ZELARI_SMOKE=0 (disabled)" };
|
|
38399
38972
|
}
|
|
38400
|
-
const pkgPath =
|
|
38401
|
-
if (!
|
|
38973
|
+
const pkgPath = join30(projectRoot, "package.json");
|
|
38974
|
+
if (!existsSync35(pkgPath)) {
|
|
38402
38975
|
return { ran: false, reason: "no package.json (skipped)" };
|
|
38403
38976
|
}
|
|
38404
38977
|
let scripts = {};
|
|
38405
38978
|
try {
|
|
38406
|
-
const pkg = JSON.parse(
|
|
38979
|
+
const pkg = JSON.parse(readFileSync31(pkgPath, "utf8"));
|
|
38407
38980
|
scripts = pkg.scripts ?? {};
|
|
38408
38981
|
} catch {
|
|
38409
38982
|
return { ran: false, reason: "package.json unreadable (skipped)" };
|
|
@@ -38491,8 +39064,8 @@ __export(postCouncilHook_exports, {
|
|
|
38491
39064
|
runPostCouncilHook: () => runPostCouncilHook
|
|
38492
39065
|
});
|
|
38493
39066
|
import { spawn as spawn11 } from "node:child_process";
|
|
38494
|
-
import { existsSync as
|
|
38495
|
-
import { join as
|
|
39067
|
+
import { existsSync as existsSync36, readFileSync as readFileSync32 } from "node:fs";
|
|
39068
|
+
import { join as join31 } from "node:path";
|
|
38496
39069
|
async function runCompleteDesignPostProcessor(ctx, options) {
|
|
38497
39070
|
if (options?.runMode === "implementation") {
|
|
38498
39071
|
return {
|
|
@@ -38503,9 +39076,9 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
38503
39076
|
if (process.env["ZELARI_COMPLETE_DESIGN"] === "0") {
|
|
38504
39077
|
return { ran: false, reason: "ZELARI_COMPLETE_DESIGN=0 (disabled)" };
|
|
38505
39078
|
}
|
|
38506
|
-
const planJsonPath2 =
|
|
38507
|
-
const scriptPath =
|
|
38508
|
-
if (!
|
|
39079
|
+
const planJsonPath2 = join31(ctx.rootDir, "plan.json");
|
|
39080
|
+
const scriptPath = join31(ctx.projectRoot, "complete-design.mjs");
|
|
39081
|
+
if (!existsSync36(planJsonPath2)) {
|
|
38509
39082
|
return {
|
|
38510
39083
|
ran: false,
|
|
38511
39084
|
reason: ".zelari/plan.json missing (not design-phase)"
|
|
@@ -38513,7 +39086,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
38513
39086
|
}
|
|
38514
39087
|
let phaseCount = 0;
|
|
38515
39088
|
try {
|
|
38516
|
-
const parsed = JSON.parse(
|
|
39089
|
+
const parsed = JSON.parse(readFileSync32(planJsonPath2, "utf8"));
|
|
38517
39090
|
phaseCount = Array.isArray(parsed.phases) ? parsed.phases.length : 0;
|
|
38518
39091
|
} catch {
|
|
38519
39092
|
return { ran: false, reason: ".zelari/plan.json corrupt" };
|
|
@@ -38521,7 +39094,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
38521
39094
|
if (phaseCount === 0) {
|
|
38522
39095
|
return { ran: false, reason: ".zelari/plan.json has no phases" };
|
|
38523
39096
|
}
|
|
38524
|
-
if (!
|
|
39097
|
+
if (!existsSync36(scriptPath)) {
|
|
38525
39098
|
try {
|
|
38526
39099
|
const builtin = await runBuiltinCompleteDesign(ctx);
|
|
38527
39100
|
return {
|
|
@@ -38624,6 +39197,7 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
38624
39197
|
}
|
|
38625
39198
|
}
|
|
38626
39199
|
const completeDesign = await runCompleteDesignPostProcessor(ctx, options);
|
|
39200
|
+
const driftCheck = await runPlanDriftCheck(ctx.rootDir);
|
|
38627
39201
|
let verification = await runImplementationVerificationHook(ctx, options);
|
|
38628
39202
|
let autofix = { ran: false };
|
|
38629
39203
|
if (process.env["ZELARI_VERIFY_AUTOFIX"] !== "0" && verification.ran && verification.ok === false && verification.report) {
|
|
@@ -38715,7 +39289,7 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
38715
39289
|
}
|
|
38716
39290
|
}
|
|
38717
39291
|
return {
|
|
38718
|
-
ran: agentsMdResult.ran || completeDesign.ran || verification.ran || lessons.ran || smoke.ran || completionHook.ran,
|
|
39292
|
+
ran: agentsMdResult.ran || completeDesign.ran || driftCheck.ran || verification.ran || lessons.ran || smoke.ran || completionHook.ran,
|
|
38719
39293
|
changed: agentsMdResult.changed,
|
|
38720
39294
|
sections: agentsMdResult.sections,
|
|
38721
39295
|
...agentsMdResult.reason ? { reason: agentsMdResult.reason } : {},
|
|
@@ -38724,7 +39298,8 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
38724
39298
|
autofix,
|
|
38725
39299
|
lessons,
|
|
38726
39300
|
smoke,
|
|
38727
|
-
completion: completionHook
|
|
39301
|
+
completion: completionHook,
|
|
39302
|
+
driftCheck
|
|
38728
39303
|
};
|
|
38729
39304
|
}
|
|
38730
39305
|
var init_postCouncilHook = __esm({
|
|
@@ -38733,6 +39308,7 @@ var init_postCouncilHook = __esm({
|
|
|
38733
39308
|
init_council();
|
|
38734
39309
|
init_agentsMd();
|
|
38735
39310
|
init_completeDesign();
|
|
39311
|
+
init_planDriftCheck();
|
|
38736
39312
|
init_projectSmoke();
|
|
38737
39313
|
}
|
|
38738
39314
|
});
|
|
@@ -38744,10 +39320,10 @@ __export(councilFeedback_exports, {
|
|
|
38744
39320
|
});
|
|
38745
39321
|
import {
|
|
38746
39322
|
promises as fs16,
|
|
38747
|
-
existsSync as
|
|
38748
|
-
readFileSync as
|
|
38749
|
-
writeFileSync as
|
|
38750
|
-
mkdirSync as
|
|
39323
|
+
existsSync as existsSync37,
|
|
39324
|
+
readFileSync as readFileSync33,
|
|
39325
|
+
writeFileSync as writeFileSync21,
|
|
39326
|
+
mkdirSync as mkdirSync17
|
|
38751
39327
|
} from "node:fs";
|
|
38752
39328
|
import path32 from "node:path";
|
|
38753
39329
|
import os9 from "node:os";
|
|
@@ -38853,9 +39429,9 @@ var init_councilFeedback = __esm({
|
|
|
38853
39429
|
}
|
|
38854
39430
|
// --- persistence ---------------------------------------------------------
|
|
38855
39431
|
load() {
|
|
38856
|
-
if (!
|
|
39432
|
+
if (!existsSync37(this.file)) return;
|
|
38857
39433
|
try {
|
|
38858
|
-
const raw =
|
|
39434
|
+
const raw = readFileSync33(this.file, "utf-8");
|
|
38859
39435
|
const parsed = JSON.parse(raw);
|
|
38860
39436
|
if (parsed && Array.isArray(parsed.entries)) {
|
|
38861
39437
|
this.entries = parsed.entries.filter(
|
|
@@ -38866,8 +39442,8 @@ var init_councilFeedback = __esm({
|
|
|
38866
39442
|
}
|
|
38867
39443
|
}
|
|
38868
39444
|
save() {
|
|
38869
|
-
|
|
38870
|
-
|
|
39445
|
+
mkdirSync17(path32.dirname(this.file), { recursive: true });
|
|
39446
|
+
writeFileSync21(
|
|
38871
39447
|
this.file,
|
|
38872
39448
|
JSON.stringify({ entries: this.entries }, null, 2),
|
|
38873
39449
|
{ encoding: "utf-8", mode: 384 }
|
|
@@ -41293,7 +41869,7 @@ __export(executor_exports, {
|
|
|
41293
41869
|
resolveNodeTimeoutMs: () => resolveNodeTimeoutMs,
|
|
41294
41870
|
thoroughnessForKind: () => thoroughnessForKind
|
|
41295
41871
|
});
|
|
41296
|
-
import { existsSync as
|
|
41872
|
+
import { existsSync as existsSync38 } from "node:fs";
|
|
41297
41873
|
import path40 from "node:path";
|
|
41298
41874
|
function resolveMaxParallel(env = process.env) {
|
|
41299
41875
|
const raw = env.ZELARI_KRAKEN_MAX_PARALLEL;
|
|
@@ -41352,7 +41928,7 @@ function isWorldModelGateEnabled(cwd, env = process.env, checksExists = defaultC
|
|
|
41352
41928
|
}
|
|
41353
41929
|
function defaultChecksExists(cwd) {
|
|
41354
41930
|
try {
|
|
41355
|
-
return
|
|
41931
|
+
return existsSync38(path40.join(cwd, ".zelari", "world", "checks.json"));
|
|
41356
41932
|
} catch {
|
|
41357
41933
|
return false;
|
|
41358
41934
|
}
|
|
@@ -42319,8 +42895,8 @@ __export(prereqChecks_exports, {
|
|
|
42319
42895
|
runPrereqChecks: () => runPrereqChecks
|
|
42320
42896
|
});
|
|
42321
42897
|
import { execSync, spawnSync as spawnSync2 } from "node:child_process";
|
|
42322
|
-
import { existsSync as
|
|
42323
|
-
import { dirname as
|
|
42898
|
+
import { existsSync as existsSync39 } from "node:fs";
|
|
42899
|
+
import { dirname as dirname8 } from "node:path";
|
|
42324
42900
|
function isWslBashPath2(p3) {
|
|
42325
42901
|
if (!p3 || typeof p3 !== "string") return false;
|
|
42326
42902
|
const n = p3.replace(/\//g, "\\").toLowerCase();
|
|
@@ -42426,7 +43002,7 @@ function resolveAgentShellSync() {
|
|
|
42426
43002
|
function agentProbeEnv() {
|
|
42427
43003
|
const env = { ...process.env };
|
|
42428
43004
|
try {
|
|
42429
|
-
const nodeDir =
|
|
43005
|
+
const nodeDir = dirname8(process.execPath);
|
|
42430
43006
|
if (!nodeDir) return env;
|
|
42431
43007
|
const sep2 = process.platform === "win32" ? ";" : ":";
|
|
42432
43008
|
const current = env.PATH ?? env.Path ?? "";
|
|
@@ -42443,7 +43019,7 @@ function agentProbeEnv() {
|
|
|
42443
43019
|
}
|
|
42444
43020
|
function existsSyncSafe2(p3) {
|
|
42445
43021
|
try {
|
|
42446
|
-
return
|
|
43022
|
+
return existsSync39(p3);
|
|
42447
43023
|
} catch {
|
|
42448
43024
|
return false;
|
|
42449
43025
|
}
|
|
@@ -42690,7 +43266,7 @@ var init_prereqChecks = __esm({
|
|
|
42690
43266
|
});
|
|
42691
43267
|
|
|
42692
43268
|
// src/cli/plugins/prefs.ts
|
|
42693
|
-
import { existsSync as
|
|
43269
|
+
import { existsSync as existsSync40, readFileSync as readFileSync34, writeFileSync as writeFileSync22, mkdirSync as mkdirSync18 } from "node:fs";
|
|
42694
43270
|
import path43 from "node:path";
|
|
42695
43271
|
import os10 from "node:os";
|
|
42696
43272
|
function getPluginPrefsPath() {
|
|
@@ -42699,8 +43275,8 @@ function getPluginPrefsPath() {
|
|
|
42699
43275
|
function getPluginPrefs() {
|
|
42700
43276
|
const file2 = getPluginPrefsPath();
|
|
42701
43277
|
try {
|
|
42702
|
-
if (!
|
|
42703
|
-
const raw =
|
|
43278
|
+
if (!existsSync40(file2)) return { ...DEFAULTS2, dontAskAgain: {} };
|
|
43279
|
+
const raw = readFileSync34(file2, "utf-8");
|
|
42704
43280
|
const parsed = JSON.parse(raw);
|
|
42705
43281
|
if (parsed && typeof parsed === "object" && parsed.dontAskAgain && typeof parsed.dontAskAgain === "object") {
|
|
42706
43282
|
const clean = {};
|
|
@@ -42715,8 +43291,8 @@ function getPluginPrefs() {
|
|
|
42715
43291
|
}
|
|
42716
43292
|
function writePluginPrefs(prefs) {
|
|
42717
43293
|
const file2 = getPluginPrefsPath();
|
|
42718
|
-
|
|
42719
|
-
|
|
43294
|
+
mkdirSync18(path43.dirname(file2), { recursive: true });
|
|
43295
|
+
writeFileSync22(file2, JSON.stringify(prefs, null, 2), {
|
|
42720
43296
|
encoding: "utf-8",
|
|
42721
43297
|
mode: 384
|
|
42722
43298
|
});
|
|
@@ -42751,7 +43327,7 @@ __export(registry_exports, {
|
|
|
42751
43327
|
findPlugin: () => findPlugin,
|
|
42752
43328
|
isBinaryOnPath: () => isBinaryOnPath
|
|
42753
43329
|
});
|
|
42754
|
-
import { existsSync as
|
|
43330
|
+
import { existsSync as existsSync41 } from "node:fs";
|
|
42755
43331
|
import path44 from "node:path";
|
|
42756
43332
|
function detectLocalBin(bin) {
|
|
42757
43333
|
return (cwd) => {
|
|
@@ -42768,7 +43344,7 @@ function isBinaryOnPath(bin, opts = {}) {
|
|
|
42768
43344
|
return false;
|
|
42769
43345
|
}
|
|
42770
43346
|
const platform = opts.platform ?? process.platform;
|
|
42771
|
-
const exists = opts.exists ??
|
|
43347
|
+
const exists = opts.exists ?? existsSync41;
|
|
42772
43348
|
const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
|
|
42773
43349
|
const pathMod = platform === "win32" ? path44.win32 : path44.posix;
|
|
42774
43350
|
const sep2 = platform === "win32" ? ";" : ":";
|
|
@@ -43502,7 +44078,7 @@ __export(atMentions_exports, {
|
|
|
43502
44078
|
extractAtMentions: () => extractAtMentions,
|
|
43503
44079
|
hasAtMentions: () => hasAtMentions
|
|
43504
44080
|
});
|
|
43505
|
-
import { existsSync as
|
|
44081
|
+
import { existsSync as existsSync44, readFileSync as readFileSync36, statSync as statSync8 } from "node:fs";
|
|
43506
44082
|
import { basename as basename3, isAbsolute as isAbsolute2, relative as relative3, resolve, sep } from "node:path";
|
|
43507
44083
|
function isImagePath(abs) {
|
|
43508
44084
|
const ext = abs.split(".").pop()?.toLowerCase() ?? "";
|
|
@@ -43556,7 +44132,7 @@ function resolveMention(token, cwd) {
|
|
|
43556
44132
|
note: "outside project root \u2014 skipped"
|
|
43557
44133
|
};
|
|
43558
44134
|
}
|
|
43559
|
-
if (!
|
|
44135
|
+
if (!existsSync44(abs)) {
|
|
43560
44136
|
return {
|
|
43561
44137
|
raw: token,
|
|
43562
44138
|
path: token,
|
|
@@ -43567,7 +44143,7 @@ function resolveMention(token, cwd) {
|
|
|
43567
44143
|
}
|
|
43568
44144
|
let st;
|
|
43569
44145
|
try {
|
|
43570
|
-
st =
|
|
44146
|
+
st = statSync8(abs);
|
|
43571
44147
|
} catch {
|
|
43572
44148
|
return {
|
|
43573
44149
|
raw: token,
|
|
@@ -43608,7 +44184,7 @@ function resolveMention(token, cwd) {
|
|
|
43608
44184
|
note: `image too large (${Math.round(st.size / 1024)} KB) \u2014 path only`
|
|
43609
44185
|
};
|
|
43610
44186
|
}
|
|
43611
|
-
const dataBase64 =
|
|
44187
|
+
const dataBase64 = readFileSync36(abs).toString("base64");
|
|
43612
44188
|
return {
|
|
43613
44189
|
raw: token,
|
|
43614
44190
|
path: rel2,
|
|
@@ -43619,7 +44195,7 @@ function resolveMention(token, cwd) {
|
|
|
43619
44195
|
};
|
|
43620
44196
|
}
|
|
43621
44197
|
try {
|
|
43622
|
-
const buf =
|
|
44198
|
+
const buf = readFileSync36(abs);
|
|
43623
44199
|
const head = buf.subarray(0, 800).toString("utf8");
|
|
43624
44200
|
if (!isProbablyText(abs, head)) {
|
|
43625
44201
|
return {
|
|
@@ -44116,14 +44692,14 @@ var init_skillCategories = __esm({
|
|
|
44116
44692
|
|
|
44117
44693
|
// src/cli/skillConfigIo.ts
|
|
44118
44694
|
import {
|
|
44119
|
-
existsSync as
|
|
44120
|
-
mkdirSync as
|
|
44121
|
-
readdirSync as
|
|
44122
|
-
readFileSync as
|
|
44695
|
+
existsSync as existsSync46,
|
|
44696
|
+
mkdirSync as mkdirSync21,
|
|
44697
|
+
readdirSync as readdirSync10,
|
|
44698
|
+
readFileSync as readFileSync38,
|
|
44123
44699
|
rmSync as rmSync4,
|
|
44124
|
-
writeFileSync as
|
|
44700
|
+
writeFileSync as writeFileSync24
|
|
44125
44701
|
} from "node:fs";
|
|
44126
|
-
import { dirname as
|
|
44702
|
+
import { dirname as dirname10, join as join37 } from "node:path";
|
|
44127
44703
|
import { homedir as homedir12 } from "node:os";
|
|
44128
44704
|
function ensureBuiltinSkillsLoadedSync() {
|
|
44129
44705
|
if (builtinsLoaded) return;
|
|
@@ -44135,13 +44711,13 @@ function ensureBuiltinSkillsLoadedSync() {
|
|
|
44135
44711
|
}
|
|
44136
44712
|
}
|
|
44137
44713
|
function getUserSkillsDir() {
|
|
44138
|
-
return
|
|
44714
|
+
return join37(homedir12(), ".zelari-code", "skills");
|
|
44139
44715
|
}
|
|
44140
44716
|
function getProjectSkillsDir(projectRoot) {
|
|
44141
|
-
return
|
|
44717
|
+
return join37(projectRoot, ".zelari", "skills");
|
|
44142
44718
|
}
|
|
44143
44719
|
function skillFilePath(dir, name) {
|
|
44144
|
-
return
|
|
44720
|
+
return join37(dir, name, "SKILL.md");
|
|
44145
44721
|
}
|
|
44146
44722
|
function classifyScope(skillPath, projectRoot) {
|
|
44147
44723
|
const userDir = getUserSkillsDir().replace(/\\/g, "/");
|
|
@@ -44190,18 +44766,18 @@ function entryFromBuiltin(skill) {
|
|
|
44190
44766
|
};
|
|
44191
44767
|
}
|
|
44192
44768
|
function scanSkillsDir(dir, projectRoot, seen, out) {
|
|
44193
|
-
if (!
|
|
44769
|
+
if (!existsSync46(dir)) return;
|
|
44194
44770
|
let entries;
|
|
44195
44771
|
try {
|
|
44196
|
-
entries =
|
|
44772
|
+
entries = readdirSync10(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
44197
44773
|
} catch {
|
|
44198
44774
|
return;
|
|
44199
44775
|
}
|
|
44200
44776
|
for (const entry of entries) {
|
|
44201
44777
|
const skillPath = skillFilePath(dir, entry);
|
|
44202
|
-
if (!
|
|
44778
|
+
if (!existsSync46(skillPath)) continue;
|
|
44203
44779
|
try {
|
|
44204
|
-
const parsed = parseSkillMd(
|
|
44780
|
+
const parsed = parseSkillMd(readFileSync38(skillPath, "utf8"), skillPath);
|
|
44205
44781
|
if (!parsed) continue;
|
|
44206
44782
|
if (seen.has(parsed.name)) continue;
|
|
44207
44783
|
seen.add(parsed.name);
|
|
@@ -44217,9 +44793,9 @@ function listSkillsSnapshot(projectRoot) {
|
|
|
44217
44793
|
const skills = [];
|
|
44218
44794
|
const seen = /* @__PURE__ */ new Set();
|
|
44219
44795
|
if (root) {
|
|
44220
|
-
scanSkillsDir(
|
|
44221
|
-
scanSkillsDir(
|
|
44222
|
-
scanSkillsDir(
|
|
44796
|
+
scanSkillsDir(join37(root, ".zelari", "skills"), root, seen, skills);
|
|
44797
|
+
scanSkillsDir(join37(root, ".claude", "skills"), root, seen, skills);
|
|
44798
|
+
scanSkillsDir(join37(root, ".opencode", "skills"), root, seen, skills);
|
|
44223
44799
|
}
|
|
44224
44800
|
scanSkillsDir(userSkillsDir, root, seen, skills);
|
|
44225
44801
|
for (const s of listCodingSkills()) {
|
|
@@ -44298,8 +44874,8 @@ function upsertSkill(opts) {
|
|
|
44298
44874
|
if (!parsed) {
|
|
44299
44875
|
return { ok: false, error: "Generated SKILL.md failed validation" };
|
|
44300
44876
|
}
|
|
44301
|
-
|
|
44302
|
-
|
|
44877
|
+
mkdirSync21(dirname10(path53), { recursive: true });
|
|
44878
|
+
writeFileSync24(path53, content, "utf8");
|
|
44303
44879
|
return { ok: true, path: path53 };
|
|
44304
44880
|
}
|
|
44305
44881
|
function removeSkill(opts) {
|
|
@@ -44317,9 +44893,9 @@ function removeSkill(opts) {
|
|
|
44317
44893
|
}
|
|
44318
44894
|
dir = getProjectSkillsDir(root);
|
|
44319
44895
|
}
|
|
44320
|
-
const skillDir =
|
|
44896
|
+
const skillDir = join37(dir, name);
|
|
44321
44897
|
const path53 = skillFilePath(dir, name);
|
|
44322
|
-
if (!
|
|
44898
|
+
if (!existsSync46(path53) && !existsSync46(skillDir)) {
|
|
44323
44899
|
return { ok: false, error: `Skill "${name}" not found in ${dir}` };
|
|
44324
44900
|
}
|
|
44325
44901
|
try {
|
|
@@ -44624,36 +45200,36 @@ var init_permissionCli = __esm({
|
|
|
44624
45200
|
|
|
44625
45201
|
// src/cli/companion/config.ts
|
|
44626
45202
|
import {
|
|
44627
|
-
existsSync as
|
|
44628
|
-
mkdirSync as
|
|
44629
|
-
readFileSync as
|
|
44630
|
-
writeFileSync as
|
|
45203
|
+
existsSync as existsSync47,
|
|
45204
|
+
mkdirSync as mkdirSync22,
|
|
45205
|
+
readFileSync as readFileSync39,
|
|
45206
|
+
writeFileSync as writeFileSync25
|
|
44631
45207
|
} from "node:fs";
|
|
44632
|
-
import { join as
|
|
45208
|
+
import { join as join38 } from "node:path";
|
|
44633
45209
|
import { homedir as homedir13 } from "node:os";
|
|
44634
45210
|
import { createHash as createHash9, randomBytes as randomBytes5, timingSafeEqual } from "node:crypto";
|
|
44635
45211
|
function getZelariHome() {
|
|
44636
|
-
return
|
|
45212
|
+
return join38(homedir13(), ".zelari-code");
|
|
44637
45213
|
}
|
|
44638
45214
|
function getCompanionConfigPath() {
|
|
44639
|
-
return
|
|
45215
|
+
return join38(getZelariHome(), "companion.json");
|
|
44640
45216
|
}
|
|
44641
45217
|
function getCompanionTokenPath() {
|
|
44642
|
-
return
|
|
45218
|
+
return join38(getZelariHome(), "companion.token");
|
|
44643
45219
|
}
|
|
44644
45220
|
function ensureHome() {
|
|
44645
45221
|
const home = getZelariHome();
|
|
44646
|
-
if (!
|
|
44647
|
-
|
|
45222
|
+
if (!existsSync47(home)) {
|
|
45223
|
+
mkdirSync22(home, { recursive: true });
|
|
44648
45224
|
}
|
|
44649
45225
|
}
|
|
44650
45226
|
function loadCompanionConfig() {
|
|
44651
45227
|
const path53 = getCompanionConfigPath();
|
|
44652
|
-
if (!
|
|
45228
|
+
if (!existsSync47(path53)) {
|
|
44653
45229
|
return { projects: [] };
|
|
44654
45230
|
}
|
|
44655
45231
|
try {
|
|
44656
|
-
const raw = JSON.parse(
|
|
45232
|
+
const raw = JSON.parse(readFileSync39(path53, "utf8"));
|
|
44657
45233
|
const projects = Array.isArray(raw.projects) ? raw.projects.filter(
|
|
44658
45234
|
(p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
|
|
44659
45235
|
).map((p3) => ({
|
|
@@ -44672,7 +45248,7 @@ function loadCompanionConfig() {
|
|
|
44672
45248
|
}
|
|
44673
45249
|
function saveCompanionConfig(cfg) {
|
|
44674
45250
|
ensureHome();
|
|
44675
|
-
|
|
45251
|
+
writeFileSync25(
|
|
44676
45252
|
getCompanionConfigPath(),
|
|
44677
45253
|
JSON.stringify(
|
|
44678
45254
|
{
|
|
@@ -44692,12 +45268,12 @@ function loadOrCreateToken(explicit) {
|
|
|
44692
45268
|
}
|
|
44693
45269
|
ensureHome();
|
|
44694
45270
|
const path53 = getCompanionTokenPath();
|
|
44695
|
-
if (
|
|
44696
|
-
const t =
|
|
45271
|
+
if (existsSync47(path53)) {
|
|
45272
|
+
const t = readFileSync39(path53, "utf8").trim();
|
|
44697
45273
|
if (t) return { token: t, created: false };
|
|
44698
45274
|
}
|
|
44699
45275
|
const token = randomBytes5(24).toString("base64url");
|
|
44700
|
-
|
|
45276
|
+
writeFileSync25(path53, token + "\n", "utf8");
|
|
44701
45277
|
try {
|
|
44702
45278
|
const fs31 = __require("node:fs");
|
|
44703
45279
|
fs31.chmodSync?.(path53, 384);
|
|
@@ -44786,8 +45362,8 @@ var init_config = __esm({
|
|
|
44786
45362
|
import { spawn as spawn13 } from "node:child_process";
|
|
44787
45363
|
import { createInterface as createInterface2 } from "node:readline";
|
|
44788
45364
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
44789
|
-
import { writeFileSync as
|
|
44790
|
-
import { join as
|
|
45365
|
+
import { writeFileSync as writeFileSync26, unlinkSync as unlinkSync3 } from "node:fs";
|
|
45366
|
+
import { join as join39 } from "node:path";
|
|
44791
45367
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
44792
45368
|
var RunManager;
|
|
44793
45369
|
var init_runManager = __esm({
|
|
@@ -44891,9 +45467,9 @@ var init_runManager = __esm({
|
|
|
44891
45467
|
}
|
|
44892
45468
|
let historyFile;
|
|
44893
45469
|
if (args.history && Array.isArray(args.history) && args.history.length > 0) {
|
|
44894
|
-
historyFile =
|
|
45470
|
+
historyFile = join39(tmpdir3(), `zelari-companion-hist-${id}.json`);
|
|
44895
45471
|
try {
|
|
44896
|
-
|
|
45472
|
+
writeFileSync26(historyFile, JSON.stringify(args.history), "utf8");
|
|
44897
45473
|
argv.push("--history-file", historyFile);
|
|
44898
45474
|
} catch {
|
|
44899
45475
|
historyFile = void 0;
|
|
@@ -45018,7 +45594,7 @@ __export(serve_exports, {
|
|
|
45018
45594
|
runCompanionServe: () => runCompanionServe
|
|
45019
45595
|
});
|
|
45020
45596
|
import { createServer as createServer3 } from "node:http";
|
|
45021
|
-
import { existsSync as
|
|
45597
|
+
import { existsSync as existsSync48 } from "node:fs";
|
|
45022
45598
|
import { resolve as resolve2 } from "node:path";
|
|
45023
45599
|
function readBody(req, max = 2e6) {
|
|
45024
45600
|
return new Promise((resolveBody, reject) => {
|
|
@@ -45066,7 +45642,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
45066
45642
|
let projects = mergeProjects(fileCfg, opts.projects ?? []);
|
|
45067
45643
|
projects = projects.filter((p3) => {
|
|
45068
45644
|
const abs = resolve2(p3.path);
|
|
45069
|
-
if (!
|
|
45645
|
+
if (!existsSync48(abs)) {
|
|
45070
45646
|
process.stderr.write(
|
|
45071
45647
|
`[zelari-code serve] skip missing project path: ${p3.path}
|
|
45072
45648
|
`
|
|
@@ -45388,7 +45964,7 @@ __export(doctor_exports, {
|
|
|
45388
45964
|
runDoctor: () => runDoctor
|
|
45389
45965
|
});
|
|
45390
45966
|
import { execSync as execSync2 } from "node:child_process";
|
|
45391
|
-
import { existsSync as
|
|
45967
|
+
import { existsSync as existsSync49, readFileSync as readFileSync40, readlinkSync, statSync as statSync9 } from "node:fs";
|
|
45392
45968
|
import { createRequire as createRequire3 } from "node:module";
|
|
45393
45969
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
45394
45970
|
import path51 from "node:path";
|
|
@@ -45396,9 +45972,9 @@ function findPackageRoot(start) {
|
|
|
45396
45972
|
let dir = start;
|
|
45397
45973
|
for (let i = 0; i < 6; i += 1) {
|
|
45398
45974
|
const candidate = path51.join(dir, "package.json");
|
|
45399
|
-
if (
|
|
45975
|
+
if (existsSync49(candidate)) {
|
|
45400
45976
|
try {
|
|
45401
|
-
const pkg = JSON.parse(
|
|
45977
|
+
const pkg = JSON.parse(readFileSync40(candidate, "utf8"));
|
|
45402
45978
|
if (pkg.name === "zelari-code") return dir;
|
|
45403
45979
|
} catch {
|
|
45404
45980
|
}
|
|
@@ -45422,7 +45998,7 @@ function tryExec(cmd) {
|
|
|
45422
45998
|
function readPackageJson3() {
|
|
45423
45999
|
try {
|
|
45424
46000
|
const pkgPath = path51.join(packageRoot, "package.json");
|
|
45425
|
-
return JSON.parse(
|
|
46001
|
+
return JSON.parse(readFileSync40(pkgPath, "utf8"));
|
|
45426
46002
|
} catch {
|
|
45427
46003
|
return null;
|
|
45428
46004
|
}
|
|
@@ -45438,16 +46014,16 @@ function checkShim(pkgName) {
|
|
|
45438
46014
|
const isWin = process.platform === "win32";
|
|
45439
46015
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
45440
46016
|
const shimPath = path51.join(prefix, shimName);
|
|
45441
|
-
if (!
|
|
46017
|
+
if (!existsSync49(shimPath)) {
|
|
45442
46018
|
return FAIL(
|
|
45443
46019
|
`shim not found at ${shimPath}
|
|
45444
46020
|
fix: npm install -g ${pkgName}@latest --force`
|
|
45445
46021
|
);
|
|
45446
46022
|
}
|
|
45447
46023
|
try {
|
|
45448
|
-
const st =
|
|
46024
|
+
const st = statSync9(shimPath);
|
|
45449
46025
|
if (isWin) {
|
|
45450
|
-
const content =
|
|
46026
|
+
const content = readFileSync40(shimPath, "utf8");
|
|
45451
46027
|
if (content.includes(`${pkgName}\\bin\\`) || content.includes(`${pkgName}/bin/`)) {
|
|
45452
46028
|
return OK(`shim OK at ${shimPath} (${st.size} bytes)`);
|
|
45453
46029
|
}
|
|
@@ -45506,14 +46082,14 @@ function checkNode(pkg) {
|
|
|
45506
46082
|
}
|
|
45507
46083
|
function checkBundle() {
|
|
45508
46084
|
const bundle = path51.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
45509
|
-
if (!
|
|
46085
|
+
if (!existsSync49(bundle)) {
|
|
45510
46086
|
return FAIL(
|
|
45511
46087
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
45512
46088
|
fix: npm run build:cli (then reinstall or run via tsx)`
|
|
45513
46089
|
);
|
|
45514
46090
|
}
|
|
45515
46091
|
try {
|
|
45516
|
-
const st =
|
|
46092
|
+
const st = statSync9(bundle);
|
|
45517
46093
|
return OK(`bundle OK (${(st.size / 1024 / 1024).toFixed(2)} MB)`);
|
|
45518
46094
|
} catch (err) {
|
|
45519
46095
|
return FAIL(
|
|
@@ -45887,7 +46463,7 @@ __export(inspect_exports, {
|
|
|
45887
46463
|
runInspect: () => runInspect
|
|
45888
46464
|
});
|
|
45889
46465
|
import path52 from "node:path";
|
|
45890
|
-
import { existsSync as
|
|
46466
|
+
import { existsSync as existsSync50, readFileSync as readFileSync41, readdirSync as readdirSync11 } from "node:fs";
|
|
45891
46467
|
import { homedir as homedir14 } from "node:os";
|
|
45892
46468
|
async function collectInspectReport(cwd = process.cwd()) {
|
|
45893
46469
|
ensureBuiltinSkillsLoadedSync();
|
|
@@ -45920,11 +46496,11 @@ async function collectInspectReport(cwd = process.cwd()) {
|
|
|
45920
46496
|
folders: listTrustedFolders()
|
|
45921
46497
|
},
|
|
45922
46498
|
configSources: [
|
|
45923
|
-
{ path: userMcpPath, exists:
|
|
45924
|
-
{ path: projectMcpPath, exists:
|
|
45925
|
-
{ path: path52.join(homedir14(), ".zelari-code", "provider.json"), exists:
|
|
45926
|
-
{ path: path52.join(cwd, ".zelari", "AGENTS.md"), exists:
|
|
45927
|
-
{ path: path52.join(cwd, "AGENTS.md"), exists:
|
|
46499
|
+
{ path: userMcpPath, exists: existsSync50(userMcpPath) },
|
|
46500
|
+
{ path: projectMcpPath, exists: existsSync50(projectMcpPath) },
|
|
46501
|
+
{ path: path52.join(homedir14(), ".zelari-code", "provider.json"), exists: existsSync50(path52.join(homedir14(), ".zelari-code", "provider.json")) },
|
|
46502
|
+
{ path: path52.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync50(path52.join(cwd, ".zelari", "AGENTS.md")) },
|
|
46503
|
+
{ path: path52.join(cwd, "AGENTS.md"), exists: existsSync50(path52.join(cwd, "AGENTS.md")) }
|
|
45928
46504
|
],
|
|
45929
46505
|
skills: {
|
|
45930
46506
|
total: snap.skills.length,
|
|
@@ -45937,7 +46513,7 @@ async function collectInspectReport(cwd = process.cwd()) {
|
|
|
45937
46513
|
user: mcp.servers.filter((s) => s.scope === "user").map((s) => ({ name: s.name, enabled: s.enabled !== false })),
|
|
45938
46514
|
project: mcp.servers.filter((s) => s.scope === "project").map((s) => ({ name: s.name, enabled: s.enabled !== false })),
|
|
45939
46515
|
projectTrusted,
|
|
45940
|
-
projectConfigExists:
|
|
46516
|
+
projectConfigExists: existsSync50(projectMcpPath)
|
|
45941
46517
|
},
|
|
45942
46518
|
hooks: {
|
|
45943
46519
|
global: {
|
|
@@ -45957,7 +46533,7 @@ async function collectInspectReport(cwd = process.cwd()) {
|
|
|
45957
46533
|
}
|
|
45958
46534
|
function listJsonFiles(dir) {
|
|
45959
46535
|
try {
|
|
45960
|
-
return
|
|
46536
|
+
return readdirSync11(dir).filter((f) => f.endsWith(".json")).sort();
|
|
45961
46537
|
} catch {
|
|
45962
46538
|
return [];
|
|
45963
46539
|
}
|
|
@@ -45969,9 +46545,9 @@ function findAgentsMd(cwd) {
|
|
|
45969
46545
|
];
|
|
45970
46546
|
const found = [];
|
|
45971
46547
|
for (const c of candidates) {
|
|
45972
|
-
if (
|
|
46548
|
+
if (existsSync50(c)) {
|
|
45973
46549
|
try {
|
|
45974
|
-
const text =
|
|
46550
|
+
const text = readFileSync41(c, "utf8");
|
|
45975
46551
|
found.push(`${c} (${text.length} bytes)`);
|
|
45976
46552
|
} catch {
|
|
45977
46553
|
found.push(`${c} (unreadable)`);
|
|
@@ -53165,7 +53741,7 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
53165
53741
|
}
|
|
53166
53742
|
|
|
53167
53743
|
// src/cli/branchManager.ts
|
|
53168
|
-
import { promises as fs26, existsSync as
|
|
53744
|
+
import { promises as fs26, existsSync as existsSync42, readFileSync as readFileSync35, writeFileSync as writeFileSync23, mkdirSync as mkdirSync19, statSync as statSync6, rmSync as rmSync3 } from "node:fs";
|
|
53169
53745
|
import path46 from "node:path";
|
|
53170
53746
|
import os12 from "node:os";
|
|
53171
53747
|
var META_FILENAME = "meta.json";
|
|
@@ -53187,11 +53763,11 @@ function sessionsPathFor(name, baseDir) {
|
|
|
53187
53763
|
}
|
|
53188
53764
|
function readBranchMeta(name, baseDir) {
|
|
53189
53765
|
const metaPath = metaPathFor(name, baseDir);
|
|
53190
|
-
if (!
|
|
53766
|
+
if (!existsSync42(metaPath)) {
|
|
53191
53767
|
throw new BranchNotFoundError(`Branch "${name}" not found`);
|
|
53192
53768
|
}
|
|
53193
53769
|
try {
|
|
53194
|
-
const raw =
|
|
53770
|
+
const raw = readFileSync35(metaPath, "utf-8");
|
|
53195
53771
|
const parsed = JSON.parse(raw);
|
|
53196
53772
|
if (!parsed || typeof parsed !== "object" || typeof parsed.name !== "string" || typeof parsed.createdAt !== "number" || typeof parsed.fromSessionId !== "string") {
|
|
53197
53773
|
throw new BranchCorruptError(`Branch "${name}" meta.json is malformed`);
|
|
@@ -53208,8 +53784,8 @@ function readBranchMeta(name, baseDir) {
|
|
|
53208
53784
|
}
|
|
53209
53785
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
53210
53786
|
const metaPath = metaPathFor(name, baseDir);
|
|
53211
|
-
|
|
53212
|
-
|
|
53787
|
+
mkdirSync19(path46.dirname(metaPath), { recursive: true });
|
|
53788
|
+
writeFileSync23(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
53213
53789
|
}
|
|
53214
53790
|
async function countSessions(name, baseDir) {
|
|
53215
53791
|
const sessionsPath = sessionsPathFor(name, baseDir);
|
|
@@ -53247,7 +53823,7 @@ var SessionNotFoundError = class extends Error {
|
|
|
53247
53823
|
};
|
|
53248
53824
|
function branchExists(name, baseDir = getBranchesBaseDir()) {
|
|
53249
53825
|
const bp = branchPathFor(name, baseDir);
|
|
53250
|
-
return
|
|
53826
|
+
return existsSync42(bp) && existsSync42(metaPathFor(name, baseDir));
|
|
53251
53827
|
}
|
|
53252
53828
|
async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
|
|
53253
53829
|
if (!name || name.trim().length === 0) {
|
|
@@ -53260,12 +53836,12 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
53260
53836
|
throw new BranchAlreadyExistsError(name);
|
|
53261
53837
|
}
|
|
53262
53838
|
const sourcePath = path46.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
53263
|
-
if (!
|
|
53839
|
+
if (!existsSync42(sourcePath)) {
|
|
53264
53840
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
53265
53841
|
}
|
|
53266
53842
|
const branchPath = branchPathFor(name, baseDir);
|
|
53267
53843
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
53268
|
-
|
|
53844
|
+
mkdirSync19(branchSessionsPath, { recursive: true });
|
|
53269
53845
|
const destPath = path46.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
53270
53846
|
await fs26.copyFile(sourcePath, destPath);
|
|
53271
53847
|
const meta3 = {
|
|
@@ -53293,7 +53869,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
|
53293
53869
|
const results = [];
|
|
53294
53870
|
for (const entry of entries) {
|
|
53295
53871
|
const metaPath = metaPathFor(entry, baseDir);
|
|
53296
|
-
if (!
|
|
53872
|
+
if (!existsSync42(metaPath)) continue;
|
|
53297
53873
|
try {
|
|
53298
53874
|
const meta3 = readBranchMeta(entry, baseDir);
|
|
53299
53875
|
const sessionCount = await countSessions(entry, baseDir);
|
|
@@ -53484,7 +54060,7 @@ import path48 from "node:path";
|
|
|
53484
54060
|
import os13 from "node:os";
|
|
53485
54061
|
|
|
53486
54062
|
// src/cli/skillHistory.ts
|
|
53487
|
-
import { promises as fs28, existsSync as
|
|
54063
|
+
import { promises as fs28, existsSync as existsSync43, statSync as statSync7, renameSync as renameSync5, appendFileSync as appendFileSync4, mkdirSync as mkdirSync20 } from "node:fs";
|
|
53488
54064
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
53489
54065
|
async function readSkillHistory(file2) {
|
|
53490
54066
|
let raw = "";
|
|
@@ -54812,9 +55388,9 @@ function ContinueKey({ onContinue }) {
|
|
|
54812
55388
|
init_providerConfig();
|
|
54813
55389
|
|
|
54814
55390
|
// src/cli/wizard/firstRun.ts
|
|
54815
|
-
import { existsSync as
|
|
55391
|
+
import { existsSync as existsSync45 } from "node:fs";
|
|
54816
55392
|
function shouldRunWizard(input) {
|
|
54817
|
-
const exists = input.exists ??
|
|
55393
|
+
const exists = input.exists ?? existsSync45;
|
|
54818
55394
|
if (input.hasResetConfigFlag) {
|
|
54819
55395
|
return { shouldRun: true, reason: "--reset-config flag forced wizard" };
|
|
54820
55396
|
}
|
|
@@ -55099,7 +55675,7 @@ init_keyStore();
|
|
|
55099
55675
|
init_providerConfig();
|
|
55100
55676
|
init_openai_compatible();
|
|
55101
55677
|
init_phase();
|
|
55102
|
-
import { readFileSync as
|
|
55678
|
+
import { readFileSync as readFileSync37 } from "node:fs";
|
|
55103
55679
|
function parseHeadlessFlags(argv) {
|
|
55104
55680
|
if (!argv.includes("--headless")) {
|
|
55105
55681
|
return { options: null };
|
|
@@ -55172,7 +55748,7 @@ function parseHeadlessFlags(argv) {
|
|
|
55172
55748
|
let raw = null;
|
|
55173
55749
|
if (arg === "--history-file") {
|
|
55174
55750
|
try {
|
|
55175
|
-
raw =
|
|
55751
|
+
raw = readFileSync37(next, "utf-8");
|
|
55176
55752
|
} catch {
|
|
55177
55753
|
raw = null;
|
|
55178
55754
|
}
|
|
@@ -55659,6 +56235,20 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
55659
56235
|
const sessionId = crypto.randomUUID();
|
|
55660
56236
|
const { registry: toolRegistry } = createBuiltinToolRegistry({
|
|
55661
56237
|
planMode: planModeFromOpts(opts),
|
|
56238
|
+
// ADR-0018 3b: upgrade plan-task domain events to first-class NDJSON
|
|
56239
|
+
// BrainEvents. Rust envelopes every stdout line with runId/conversationId,
|
|
56240
|
+
// so task events ride the same multiplexed channel as the rest.
|
|
56241
|
+
onTaskEvent: (ev) => {
|
|
56242
|
+
if (opts.output !== "json") return;
|
|
56243
|
+
emitEvent({
|
|
56244
|
+
type: ev.type,
|
|
56245
|
+
id: crypto.randomUUID(),
|
|
56246
|
+
ts: Date.now(),
|
|
56247
|
+
sessionId,
|
|
56248
|
+
source: ev.source,
|
|
56249
|
+
...ev.type === "task_update" ? { task: ev.task } : { tasks: ev.tasks }
|
|
56250
|
+
});
|
|
56251
|
+
},
|
|
55662
56252
|
permissionPolicy: {
|
|
55663
56253
|
read: "allow",
|
|
55664
56254
|
write: "allow",
|