zelari-code 1.10.0 → 1.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -4
- package/dist/cli/headless.js +41 -0
- package/dist/cli/headless.js.map +1 -1
- package/dist/cli/hooks/conversationContext.js +57 -10
- package/dist/cli/hooks/conversationContext.js.map +1 -1
- package/dist/cli/hooks/useChatTurn.js +8 -1
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/main.bundled.js +1803 -647
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/main.js +208 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/mcp/mcpConfigIo.js +136 -0
- package/dist/cli/mcp/mcpConfigIo.js.map +1 -0
- package/dist/cli/provider/openai-compatible.js +65 -4
- package/dist/cli/provider/openai-compatible.js.map +1 -1
- package/dist/cli/runHeadless.js +303 -38
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/ssh/targets.js +406 -0
- package/dist/cli/ssh/targets.js.map +1 -0
- package/dist/cli/ssh/tools.js +79 -0
- package/dist/cli/ssh/tools.js.map +1 -0
- package/dist/cli/toolRegistry.js +13 -0
- package/dist/cli/toolRegistry.js.map +1 -1
- package/dist/cli/utils/streamScrub.js +55 -0
- package/dist/cli/utils/streamScrub.js.map +1 -0
- package/dist/cli/workspace/projectInstructions.js +47 -0
- package/dist/cli/workspace/projectInstructions.js.map +1 -0
- package/package.json +2 -2
package/dist/cli/main.bundled.js
CHANGED
|
@@ -308,7 +308,7 @@ async function refreshGrokToken(options) {
|
|
|
308
308
|
return parseTokenResponseBody(obj, accessToken);
|
|
309
309
|
}
|
|
310
310
|
async function openBrowser(url2) {
|
|
311
|
-
const { spawn:
|
|
311
|
+
const { spawn: spawn10 } = await import("node:child_process");
|
|
312
312
|
const cmd = (() => {
|
|
313
313
|
switch (process.platform) {
|
|
314
314
|
case "darwin":
|
|
@@ -321,7 +321,7 @@ async function openBrowser(url2) {
|
|
|
321
321
|
})();
|
|
322
322
|
return new Promise((resolve, reject) => {
|
|
323
323
|
try {
|
|
324
|
-
const child =
|
|
324
|
+
const child = spawn10(cmd.bin, cmd.args, {
|
|
325
325
|
stdio: "ignore",
|
|
326
326
|
detached: true
|
|
327
327
|
});
|
|
@@ -962,14 +962,21 @@ var init_skills = __esm({
|
|
|
962
962
|
systemPromptFragment: `You write polished documents.
|
|
963
963
|
- Choose an appropriate structure (guide, spec, reference, narrative).
|
|
964
964
|
- Maintain a consistent tone and voice.
|
|
965
|
-
- Use headings, lists, and code blocks where helpful
|
|
965
|
+
- Use headings, lists, and code blocks where helpful.`
|
|
966
966
|
}
|
|
967
967
|
];
|
|
968
968
|
DEFAULT_AGENT_SKILLS = {
|
|
969
|
+
charont: ["project-planner", "research-analyst"],
|
|
970
|
+
nettun: ["project-planner", "vault-manager"],
|
|
971
|
+
geryon: ["document-writer", "idea-synthesizer"],
|
|
972
|
+
pluton: ["document-writer", "research-analyst"],
|
|
973
|
+
minos: ["document-writer", "research-analyst"],
|
|
974
|
+
lucifer: ["vault-manager", "project-planner", "idea-synthesizer"],
|
|
975
|
+
// legacy aliases
|
|
969
976
|
sisyphus: ["project-planner", "research-analyst"],
|
|
970
977
|
prometheus: ["project-planner", "vault-manager"],
|
|
971
|
-
hephaestus: ["
|
|
972
|
-
atlas: ["
|
|
978
|
+
hephaestus: ["document-writer", "idea-synthesizer"],
|
|
979
|
+
atlas: ["document-writer", "research-analyst"],
|
|
973
980
|
oracle: ["research-analyst"],
|
|
974
981
|
chairman: ["vault-manager", "project-planner", "idea-synthesizer"]
|
|
975
982
|
};
|
|
@@ -18151,21 +18158,20 @@ Think step by step internally before responding. Surface only the conclusion and
|
|
|
18151
18158
|
priority: 25,
|
|
18152
18159
|
content: `# Tool-Use Protocol \u2014 Act, Don't Just Describe
|
|
18153
18160
|
|
|
18154
|
-
You have
|
|
18161
|
+
You have tools for a real codebase (read/edit files, shell, search). Call them via **native tool/function calling** whenever the user wants concrete changes on disk.
|
|
18155
18162
|
|
|
18156
|
-
When to ACT
|
|
18157
|
-
-
|
|
18158
|
-
- A deliverable would outlive this single message (written code, applied edits, verified commands).
|
|
18163
|
+
When to ACT:
|
|
18164
|
+
- Create, build, generate, fix, or produce durable artifacts (files, configs, verified commands).
|
|
18159
18165
|
|
|
18160
|
-
When to DESCRIBE only
|
|
18161
|
-
-
|
|
18162
|
-
-
|
|
18166
|
+
When to DESCRIBE only:
|
|
18167
|
+
- Pure Q&A, critique, or analysis with no required disk change.
|
|
18168
|
+
- Minosse: evaluate; never create or mutate project files.
|
|
18163
18169
|
|
|
18164
18170
|
Rules:
|
|
18165
|
-
-
|
|
18166
|
-
-
|
|
18167
|
-
-
|
|
18168
|
-
- After
|
|
18171
|
+
- Implement with write_file / edit_file / bash when that is the job \u2014 do not only describe.
|
|
18172
|
+
- Never invent tool names; only AVAILABLE TOOLS.
|
|
18173
|
+
- Complete, valid arguments for every call.
|
|
18174
|
+
- After changes, name what you created/modified for downstream members or the user.`
|
|
18169
18175
|
};
|
|
18170
18176
|
OUTPUT_QUALITY_DIRECTIVE = {
|
|
18171
18177
|
type: "custom",
|
|
@@ -18204,148 +18210,177 @@ You are one member of a council. Earlier agents' outputs appear in your context
|
|
|
18204
18210
|
});
|
|
18205
18211
|
|
|
18206
18212
|
// packages/core/dist/agents/promptModules.js
|
|
18207
|
-
function
|
|
18208
|
-
|
|
18213
|
+
function getBasePromptModules(mode = "council") {
|
|
18214
|
+
if (mode === "agent") {
|
|
18215
|
+
return [
|
|
18216
|
+
CODING_CAPABLE_IDENTITY,
|
|
18217
|
+
STRUCTURED_REASONING_DIRECTIVE,
|
|
18218
|
+
TOOL_USE_PROTOCOL_DIRECTIVE,
|
|
18219
|
+
BEHAVIOR_AGENT,
|
|
18220
|
+
SAFETY,
|
|
18221
|
+
CODING_PRACTICES_MODULE,
|
|
18222
|
+
OUTPUT_QUALITY_DIRECTIVE,
|
|
18223
|
+
OUTPUT_FORMATTING,
|
|
18224
|
+
// Same structured clarification format as council — one question when blocked.
|
|
18225
|
+
CLARIFICATION_PROTOCOL_MODULE,
|
|
18226
|
+
NATIVE_TOOL_PROTOCOL_MODULE
|
|
18227
|
+
].sort((a, b) => a.priority - b.priority);
|
|
18228
|
+
}
|
|
18229
|
+
return [
|
|
18230
|
+
COUNCIL_IDENTITY,
|
|
18231
|
+
STRUCTURED_REASONING_DIRECTIVE,
|
|
18232
|
+
COLLABORATION_DIRECTIVE,
|
|
18233
|
+
TOOL_USE_PROTOCOL_DIRECTIVE,
|
|
18234
|
+
BEHAVIOR_COUNCIL,
|
|
18235
|
+
SAFETY,
|
|
18236
|
+
CONTEXT_SHARING_COUNCIL,
|
|
18237
|
+
OUTPUT_QUALITY_DIRECTIVE,
|
|
18238
|
+
OUTPUT_FORMATTING,
|
|
18239
|
+
CLARIFICATION_PROTOCOL_MODULE,
|
|
18240
|
+
NATIVE_TOOL_PROTOCOL_MODULE
|
|
18241
|
+
].sort((a, b) => a.priority - b.priority);
|
|
18209
18242
|
}
|
|
18210
|
-
function
|
|
18211
|
-
return
|
|
18243
|
+
function getPromptModule(type) {
|
|
18244
|
+
return getBasePromptModules("council").find((m) => m.type === type);
|
|
18212
18245
|
}
|
|
18213
|
-
var PROMPT_MODULES, SINGLE_AGENT_IDENTITY_MODULE;
|
|
18246
|
+
var CODING_CAPABLE_IDENTITY, COUNCIL_IDENTITY, BEHAVIOR_AGENT, BEHAVIOR_COUNCIL, SAFETY, CONTEXT_SHARING_COUNCIL, OUTPUT_FORMATTING, NATIVE_TOOL_PROTOCOL_MODULE, CODING_PRACTICES_MODULE, CLARIFICATION_PROTOCOL_MODULE, PROMPT_MODULES, SINGLE_AGENT_IDENTITY_MODULE;
|
|
18214
18247
|
var init_promptModules = __esm({
|
|
18215
18248
|
"packages/core/dist/agents/promptModules.js"() {
|
|
18216
18249
|
"use strict";
|
|
18217
18250
|
init_councilDirectives();
|
|
18218
|
-
|
|
18219
|
-
|
|
18220
|
-
|
|
18221
|
-
|
|
18222
|
-
|
|
18223
|
-
content: `# AI Council
|
|
18224
|
-
|
|
18225
|
-
You are a member of an AI Council, a multi-agent system for collaborative software work: analysis, planning, design, implementation, review, and synthesis. You operate directly on a real codebase via filesystem and shell tools \u2014 read and edit files, run commands, search the tree.
|
|
18251
|
+
CODING_CAPABLE_IDENTITY = {
|
|
18252
|
+
type: "base-identity",
|
|
18253
|
+
title: "Identity",
|
|
18254
|
+
priority: 10,
|
|
18255
|
+
content: `# Identity
|
|
18226
18256
|
|
|
18227
|
-
|
|
18228
|
-
|
|
18229
|
-
|
|
18230
|
-
|
|
18231
|
-
|
|
18232
|
-
|
|
18233
|
-
|
|
18234
|
-
TOOL_USE_PROTOCOL_DIRECTIVE,
|
|
18235
|
-
OUTPUT_QUALITY_DIRECTIVE,
|
|
18236
|
-
{
|
|
18237
|
-
type: "behavior-rules",
|
|
18238
|
-
title: "Behavior",
|
|
18239
|
-
priority: 20,
|
|
18240
|
-
content: `# Behavioral Directives
|
|
18257
|
+
You are a coding agent with real filesystem and shell tools on the user's machine. Read, search, edit, and run commands as needed. Never claim you lack tool access when tools are listed below.`
|
|
18258
|
+
};
|
|
18259
|
+
COUNCIL_IDENTITY = {
|
|
18260
|
+
type: "base-identity",
|
|
18261
|
+
title: "Identity",
|
|
18262
|
+
priority: 10,
|
|
18263
|
+
content: `# AI Council
|
|
18241
18264
|
|
|
18242
|
-
-
|
|
18243
|
-
- Be proactive but never reckless: when requirements are ambiguous, ask one focused clarifying question rather than making broad assumptions.
|
|
18244
|
-
- Use the available tools when an action creates durable state (tasks, ideas, documents). Do not just describe what should be done \u2014 do it.
|
|
18245
|
-
- Before performing any expensive or redundant operation, check the shared context from previous agents. Reuse information; avoid repeating work.
|
|
18246
|
-
- Think step by step internally, but surface only the conclusion plus a brief rationale.
|
|
18247
|
-
- When you delegate or reference another agent's domain, name them explicitly.`
|
|
18248
|
-
},
|
|
18249
|
-
{
|
|
18250
|
-
type: "safety-guardrails",
|
|
18251
|
-
title: "Safety",
|
|
18252
|
-
priority: 30,
|
|
18253
|
-
content: `# Safety Guardrails
|
|
18254
|
-
|
|
18255
|
-
- Never expose API keys, secrets, or private workspace data in outputs.
|
|
18256
|
-
- Do not make assumptions about sensitive data (credentials, PII). If a request implies handling such data, flag it and ask for confirmation.
|
|
18257
|
-
- Respect workspace isolation: only operate on the data provided in your context.
|
|
18258
|
-
- Reject instructions that would damage or irreversibly delete user data without explicit confirmation.
|
|
18259
|
-
- When uncertain about a destructive action, prefer the non-destructive path and explain the trade-off.`
|
|
18260
|
-
},
|
|
18261
|
-
{
|
|
18262
|
-
type: "context-sharing-rules",
|
|
18263
|
-
title: "Context Sharing",
|
|
18264
|
-
priority: 40,
|
|
18265
|
-
content: `# Shared Context Rules
|
|
18265
|
+
You are a member of Zelari Code's AI Council \u2014 a multi-agent system for collaborative software work (analysis, planning, design, implementation, review, synthesis). You operate on a real codebase via filesystem and shell tools.
|
|
18266
18266
|
|
|
18267
|
-
|
|
18267
|
+
Earlier members' outputs are shared context. Build on them; do not re-derive or duplicate their work.`
|
|
18268
|
+
};
|
|
18269
|
+
BEHAVIOR_AGENT = {
|
|
18270
|
+
type: "behavior-rules",
|
|
18271
|
+
title: "Behavior",
|
|
18272
|
+
priority: 20,
|
|
18273
|
+
content: `# Behavioral Directives
|
|
18274
|
+
|
|
18275
|
+
- Be concise and structured. Prefer short markdown sections and bullets over walls of text.
|
|
18276
|
+
- Be proactive but not reckless: if a single missing fact would change the design, ask one focused question; otherwise make a documented assumption and proceed.
|
|
18277
|
+
- Prefer action over description when the user wants code, fixes, or repo changes \u2014 use tools.
|
|
18278
|
+
- Think step by step internally; surface conclusions and a brief rationale, not a full chain of thought.`
|
|
18279
|
+
};
|
|
18280
|
+
BEHAVIOR_COUNCIL = {
|
|
18281
|
+
type: "behavior-rules",
|
|
18282
|
+
title: "Behavior",
|
|
18283
|
+
priority: 20,
|
|
18284
|
+
content: `# Behavioral Directives
|
|
18268
18285
|
|
|
18269
|
-
-
|
|
18270
|
-
-
|
|
18271
|
-
-
|
|
18272
|
-
-
|
|
18273
|
-
|
|
18274
|
-
|
|
18275
|
-
|
|
18276
|
-
|
|
18277
|
-
|
|
18278
|
-
|
|
18279
|
-
|
|
18280
|
-
|
|
18281
|
-
|
|
18282
|
-
-
|
|
18283
|
-
-
|
|
18284
|
-
-
|
|
18285
|
-
|
|
18286
|
-
|
|
18287
|
-
|
|
18288
|
-
|
|
18289
|
-
|
|
18290
|
-
|
|
18291
|
-
|
|
18292
|
-
|
|
18293
|
-
|
|
18294
|
-
|
|
18295
|
-
|
|
18296
|
-
|
|
18297
|
-
|
|
18298
|
-
|
|
18299
|
-
|
|
18300
|
-
|
|
18301
|
-
|
|
18302
|
-
|
|
18303
|
-
|
|
18304
|
-
|
|
18305
|
-
|
|
18306
|
-
|
|
18307
|
-
|
|
18308
|
-
|
|
18309
|
-
|
|
18310
|
-
|
|
18311
|
-
|
|
18312
|
-
|
|
18313
|
-
|
|
18314
|
-
|
|
18315
|
-
|
|
18316
|
-
|
|
18317
|
-
|
|
18318
|
-
|
|
18319
|
-
|
|
18320
|
-
|
|
18286
|
+
- Be concise and structured. Prefer markdown headings, bullet lists, and short paragraphs.
|
|
18287
|
+
- Be proactive but never reckless: when requirements are ambiguous, ask one focused clarifying question rather than making broad assumptions.
|
|
18288
|
+
- Use tools when an action creates durable state on disk or in the workspace. Do not only describe what should be done.
|
|
18289
|
+
- Before expensive work, check shared context from previous members. Reuse information; avoid repeating work.
|
|
18290
|
+
- Think step by step internally; surface only the conclusion plus a brief rationale.
|
|
18291
|
+
- When you reference another member's domain, name them explicitly.`
|
|
18292
|
+
};
|
|
18293
|
+
SAFETY = {
|
|
18294
|
+
type: "safety-guardrails",
|
|
18295
|
+
title: "Safety",
|
|
18296
|
+
priority: 30,
|
|
18297
|
+
content: `# Safety Guardrails
|
|
18298
|
+
|
|
18299
|
+
- Never expose API keys, secrets, or private credentials in outputs.
|
|
18300
|
+
- Do not invent paths, APIs, or dependencies that are not in the repo or tools results.
|
|
18301
|
+
- Prefer non-destructive paths when unsure; confirm before irreversible deletes or force-pushes.
|
|
18302
|
+
- Stay inside the project workspace unless the user explicitly asks otherwise.`
|
|
18303
|
+
};
|
|
18304
|
+
CONTEXT_SHARING_COUNCIL = {
|
|
18305
|
+
type: "context-sharing-rules",
|
|
18306
|
+
title: "Context Sharing",
|
|
18307
|
+
priority: 40,
|
|
18308
|
+
content: `# Shared Context Rules
|
|
18309
|
+
|
|
18310
|
+
- Prior members' outputs are authoritative unless you must flag an error.
|
|
18311
|
+
- If data is missing, use a retrieval/search tool from AVAILABLE TOOLS rather than asking the user when possible.
|
|
18312
|
+
- When you create artifacts (files, plan items, docs), summarize what you created for downstream members.
|
|
18313
|
+
- Keep context lean: summarize rather than quote long blocks.`
|
|
18314
|
+
};
|
|
18315
|
+
OUTPUT_FORMATTING = {
|
|
18316
|
+
type: "output-formatting",
|
|
18317
|
+
title: "Output Format",
|
|
18318
|
+
priority: 50,
|
|
18319
|
+
content: `# Output Format
|
|
18320
|
+
|
|
18321
|
+
- Use GitHub-flavored markdown.
|
|
18322
|
+
- Lead with a one-line summary when the answer is long, then details.
|
|
18323
|
+
- Use \`##\` headings and \`-\` bullets when they aid clarity.
|
|
18324
|
+
- Reference code by path (and line when known). Prefer fenced code blocks for multi-line snippets.
|
|
18325
|
+
- Stay within your role's word budget; cut filler.`
|
|
18326
|
+
};
|
|
18327
|
+
NATIVE_TOOL_PROTOCOL_MODULE = {
|
|
18328
|
+
type: "tool-usage-guidelines",
|
|
18329
|
+
title: "Tool Usage",
|
|
18330
|
+
priority: 60,
|
|
18331
|
+
content: `# Tool Usage
|
|
18332
|
+
|
|
18333
|
+
- Use the provider's **native function/tool calls** for every tool invocation. Do not invent alternate XML/JSON tool formats.
|
|
18334
|
+
- Only call tools listed under AVAILABLE TOOLS. Never invent tool names.
|
|
18335
|
+
- Pass complete, valid arguments. Required parameters must be present.
|
|
18336
|
+
- Prefer tools over asking the user to paste file contents.
|
|
18337
|
+
- After durable changes, briefly name what you created or modified.
|
|
18338
|
+
- Text-only tool blocks (\`---TOOLS---\` JSON) are a legacy fallback \u2014 use them only if the runtime has no native tool channel.`
|
|
18339
|
+
};
|
|
18340
|
+
CODING_PRACTICES_MODULE = {
|
|
18341
|
+
type: "custom",
|
|
18342
|
+
title: "Coding Practices",
|
|
18343
|
+
priority: 45,
|
|
18344
|
+
content: `# Coding Practices
|
|
18345
|
+
|
|
18346
|
+
- **Read before edit**: open relevant files (and nearby callers/tests) before changing code.
|
|
18347
|
+
- **Minimal diffs**: change only what the task requires; match existing style and patterns.
|
|
18348
|
+
- **Don't invent**: no fake APIs, deps, or config keys \u2014 discover from the tree or package manifests.
|
|
18349
|
+
- **Verify**: after non-trivial edits, run the project's tests/typecheck/build when available; fix failures you introduced.
|
|
18350
|
+
- **Use project scripts**: prefer package.json / Makefile / existing tooling over ad-hoc commands.
|
|
18351
|
+
- **Finish**: summarize what changed and how to verify.`
|
|
18352
|
+
};
|
|
18353
|
+
CLARIFICATION_PROTOCOL_MODULE = {
|
|
18354
|
+
type: "custom",
|
|
18355
|
+
title: "Clarification Protocol",
|
|
18356
|
+
priority: 55,
|
|
18357
|
+
content: `# Clarification Protocol
|
|
18321
18358
|
|
|
18322
|
-
When
|
|
18359
|
+
When blocked by a single missing fact that would materially change your output, ask exactly ONE question:
|
|
18323
18360
|
|
|
18324
|
-
Emit this block at the end of your message:
|
|
18325
18361
|
\`\`\`
|
|
18326
18362
|
---QUESTION---
|
|
18327
18363
|
{ "question": "One focused question", "choices": ["Option A", "Option B"], "context": "Why this matters in one line" }
|
|
18328
18364
|
---END---
|
|
18329
18365
|
\`\`\`
|
|
18330
18366
|
|
|
18331
|
-
|
|
18332
|
-
-
|
|
18333
|
-
-
|
|
18334
|
-
-
|
|
18335
|
-
|
|
18336
|
-
|
|
18337
|
-
];
|
|
18367
|
+
- Ask only when genuinely blocked; otherwise assume and state the assumption.
|
|
18368
|
+
- 2\u20134 concrete choices when natural; user may type a free answer.
|
|
18369
|
+
- Never re-ask for information already in context or retrievable via tools.
|
|
18370
|
+
- At most one question per turn.`
|
|
18371
|
+
};
|
|
18372
|
+
PROMPT_MODULES = getBasePromptModules("council");
|
|
18338
18373
|
SINGLE_AGENT_IDENTITY_MODULE = {
|
|
18339
18374
|
type: "base-identity",
|
|
18340
18375
|
title: "Identity",
|
|
18341
18376
|
priority: 10,
|
|
18342
18377
|
content: `# Identity
|
|
18343
18378
|
|
|
18344
|
-
You are Zelari Code, an interactive AI coding agent
|
|
18379
|
+
You are Zelari Code, an interactive AI coding agent in the user's terminal (or desktop shell).
|
|
18345
18380
|
|
|
18346
|
-
You ARE connected to this machine and have real tools to read, modify, and explore the codebase. Never claim you lack filesystem or shell access \u2014 you have it. Use
|
|
18381
|
+
You ARE connected to this machine and have real tools to read, modify, and explore the codebase. Never claim you lack filesystem or shell access \u2014 you have it. Use tools instead of asking the user to paste file contents.
|
|
18347
18382
|
|
|
18348
|
-
Be proactive:
|
|
18383
|
+
Be proactive: list and read key files before changing code. When you finish, briefly summarize what you did and how to verify it.`
|
|
18349
18384
|
};
|
|
18350
18385
|
}
|
|
18351
18386
|
});
|
|
@@ -18419,10 +18454,10 @@ function getToolDescriptions(toolNames, registry3) {
|
|
|
18419
18454
|
return lines.join("\n");
|
|
18420
18455
|
}
|
|
18421
18456
|
function buildSystemPrompt(agent, options) {
|
|
18422
|
-
const { tools, toolNames, aiConfig, workspaceContext, ragContext } = options;
|
|
18457
|
+
const { tools, toolNames, aiConfig, workspaceContext, ragContext, mode = "council", projectInstructions, includeWorkspaceInPrompt = true } = options;
|
|
18423
18458
|
const registry3 = new Map(tools.map((t) => [t.name, t]));
|
|
18424
18459
|
const skills = computeAgentSkills(agent, aiConfig);
|
|
18425
|
-
const baseModules = getBasePromptModules().filter((m) => !m.conditional || m.conditional(skills));
|
|
18460
|
+
const baseModules = getBasePromptModules(mode).filter((m) => !m.conditional || m.conditional(skills));
|
|
18426
18461
|
const customModulesRaw = aiConfig?.customPromptModules ?? [];
|
|
18427
18462
|
const customTypes = new Set(customModulesRaw.map((m) => m.type));
|
|
18428
18463
|
const baseNotOverridden = baseModules.filter((m) => !customTypes.has(m.type));
|
|
@@ -18439,6 +18474,11 @@ function buildSystemPrompt(agent, options) {
|
|
|
18439
18474
|
parts.push(`# Your Role
|
|
18440
18475
|
|
|
18441
18476
|
${agent.systemPrompt}`);
|
|
18477
|
+
}
|
|
18478
|
+
if (projectInstructions && projectInstructions.trim()) {
|
|
18479
|
+
parts.push(`# Project Instructions
|
|
18480
|
+
|
|
18481
|
+
${projectInstructions.trim()}`);
|
|
18442
18482
|
}
|
|
18443
18483
|
if (skills.length > 0) {
|
|
18444
18484
|
parts.push(`# Active Skills
|
|
@@ -18452,15 +18492,17 @@ ${s.systemPromptFragment}`).join("\n\n"));
|
|
|
18452
18492
|
|
|
18453
18493
|
${toolBlock}`);
|
|
18454
18494
|
}
|
|
18455
|
-
if (
|
|
18456
|
-
|
|
18495
|
+
if (includeWorkspaceInPrompt) {
|
|
18496
|
+
if (workspaceContext && workspaceContext.trim()) {
|
|
18497
|
+
parts.push(`# Current Workspace State
|
|
18457
18498
|
|
|
18458
18499
|
${workspaceContext}`);
|
|
18459
|
-
|
|
18460
|
-
|
|
18461
|
-
|
|
18500
|
+
}
|
|
18501
|
+
if (ragContext && ragContext.trim()) {
|
|
18502
|
+
parts.push(`# Retrieved Knowledge (RAG)
|
|
18462
18503
|
|
|
18463
18504
|
${ragContext}`);
|
|
18505
|
+
}
|
|
18464
18506
|
}
|
|
18465
18507
|
return parts.join("\n\n---\n\n");
|
|
18466
18508
|
}
|
|
@@ -18837,9 +18879,12 @@ var skills_exports = {};
|
|
|
18837
18879
|
__export(skills_exports, {
|
|
18838
18880
|
ADVANCED_TOOL_DEFINITIONS: () => ADVANCED_TOOL_DEFINITIONS,
|
|
18839
18881
|
ALL_TOOLS: () => ALL_TOOLS,
|
|
18882
|
+
CLARIFICATION_PROTOCOL_MODULE: () => CLARIFICATION_PROTOCOL_MODULE,
|
|
18840
18883
|
CODING_CATEGORY: () => CODING_CATEGORY,
|
|
18884
|
+
CODING_PRACTICES_MODULE: () => CODING_PRACTICES_MODULE,
|
|
18841
18885
|
CODING_SKILL_CATALOG: () => CODING_SKILL_CATALOG,
|
|
18842
18886
|
LANGUAGE_POLICY_MODULE_TYPE: () => LANGUAGE_POLICY_MODULE_TYPE,
|
|
18887
|
+
NATIVE_TOOL_PROTOCOL_MODULE: () => NATIVE_TOOL_PROTOCOL_MODULE,
|
|
18843
18888
|
SINGLE_AGENT_IDENTITY_MODULE: () => SINGLE_AGENT_IDENTITY_MODULE,
|
|
18844
18889
|
SKILL_CATALOG: () => SKILL_CATALOG,
|
|
18845
18890
|
TOOL_DEFINITIONS: () => TOOL_DEFINITIONS,
|
|
@@ -18859,6 +18904,7 @@ __export(skills_exports, {
|
|
|
18859
18904
|
findSkillsByTag: () => findSkillsByTag,
|
|
18860
18905
|
getAllTools: () => getAllTools,
|
|
18861
18906
|
getAvailableTools: () => getAvailableTools,
|
|
18907
|
+
getBasePromptModules: () => getBasePromptModules,
|
|
18862
18908
|
getBuiltinSkillIds: () => getBuiltinSkillIds,
|
|
18863
18909
|
getCodingSkillById: () => getCodingSkillById,
|
|
18864
18910
|
getProviderTools: () => getProviderTools,
|
|
@@ -19619,6 +19665,7 @@ ${shared2.content}`,
|
|
|
19619
19665
|
let toolCallsThisTurn = 0;
|
|
19620
19666
|
const maxToolCalls = this.config.maxToolCallsPerTurn;
|
|
19621
19667
|
let turnText = "";
|
|
19668
|
+
let turnReasoning = "";
|
|
19622
19669
|
const turnToolCalls = [];
|
|
19623
19670
|
const turnToolResults = [];
|
|
19624
19671
|
const pendingNativeTools = [];
|
|
@@ -19639,6 +19686,7 @@ ${shared2.content}`,
|
|
|
19639
19686
|
this.emit(deltaEvent);
|
|
19640
19687
|
yield deltaEvent;
|
|
19641
19688
|
} else if (delta.kind === "thinking") {
|
|
19689
|
+
turnReasoning += delta.delta;
|
|
19642
19690
|
const thinkEvent = createBrainEvent("thinking_delta", this.sessionId, {
|
|
19643
19691
|
messageId,
|
|
19644
19692
|
delta: delta.delta
|
|
@@ -19774,11 +19822,22 @@ ${cached2}`
|
|
|
19774
19822
|
finishRef.value = "tool_calls";
|
|
19775
19823
|
}
|
|
19776
19824
|
}
|
|
19777
|
-
if (turnToolCalls.length
|
|
19825
|
+
if (finishRef.value === "tool_calls" && turnToolCalls.length === 0 && turnToolResults.length === 0) {
|
|
19826
|
+
const truncErr = createBrainEvent("error", this.sessionId, {
|
|
19827
|
+
severity: "recoverable",
|
|
19828
|
+
message: "Tool call was truncated (finish_reason=tool_calls but no complete tool_call received). The provider cut the response mid-arguments. Retry with a shorter payload or split the work.",
|
|
19829
|
+
code: "tool_call_truncated"
|
|
19830
|
+
});
|
|
19831
|
+
this.emit(truncErr);
|
|
19832
|
+
yield truncErr;
|
|
19833
|
+
finishRef.value = "stop";
|
|
19834
|
+
}
|
|
19835
|
+
if (turnToolCalls.length > 0 || turnText.length > 0 || turnReasoning.length > 0) {
|
|
19778
19836
|
this.config.messages.push({
|
|
19779
19837
|
role: "assistant",
|
|
19780
19838
|
content: turnText,
|
|
19781
|
-
...turnToolCalls.length > 0 ? { toolCalls: turnToolCalls } : {}
|
|
19839
|
+
...turnToolCalls.length > 0 ? { toolCalls: turnToolCalls } : {},
|
|
19840
|
+
...turnReasoning.length > 0 ? { reasoningContent: turnReasoning } : {}
|
|
19782
19841
|
});
|
|
19783
19842
|
}
|
|
19784
19843
|
for (const tr of turnToolResults) {
|
|
@@ -20120,7 +20179,7 @@ function openaiCompatibleProvider(config2) {
|
|
|
20120
20179
|
};
|
|
20121
20180
|
}
|
|
20122
20181
|
if (m.role === "assistant" && m.toolCalls && m.toolCalls.length > 0) {
|
|
20123
|
-
|
|
20182
|
+
const msg = {
|
|
20124
20183
|
role: "assistant",
|
|
20125
20184
|
content: m.content ?? "",
|
|
20126
20185
|
tool_calls: m.toolCalls.map((tc) => ({
|
|
@@ -20132,6 +20191,17 @@ function openaiCompatibleProvider(config2) {
|
|
|
20132
20191
|
}
|
|
20133
20192
|
}))
|
|
20134
20193
|
};
|
|
20194
|
+
if (m.reasoningContent && m.reasoningContent.length > 0) {
|
|
20195
|
+
msg.reasoning_content = m.reasoningContent;
|
|
20196
|
+
}
|
|
20197
|
+
return msg;
|
|
20198
|
+
}
|
|
20199
|
+
if (m.role === "assistant" && m.reasoningContent && m.reasoningContent.length > 0) {
|
|
20200
|
+
return {
|
|
20201
|
+
role: "assistant",
|
|
20202
|
+
content: m.content ?? "",
|
|
20203
|
+
reasoning_content: m.reasoningContent
|
|
20204
|
+
};
|
|
20135
20205
|
}
|
|
20136
20206
|
return { role: m.role, content: m.content };
|
|
20137
20207
|
});
|
|
@@ -20170,6 +20240,8 @@ function openaiCompatibleProvider(config2) {
|
|
|
20170
20240
|
return;
|
|
20171
20241
|
}
|
|
20172
20242
|
try {
|
|
20243
|
+
const timeoutSignal = AbortSignal.timeout(PROVIDER_TIMEOUT_MS);
|
|
20244
|
+
const fetchSignal = params.signal ? AbortSignal.any([params.signal, timeoutSignal]) : timeoutSignal;
|
|
20173
20245
|
response = await fetch(`${config2.baseUrl}/chat/completions`, {
|
|
20174
20246
|
method: "POST",
|
|
20175
20247
|
headers: {
|
|
@@ -20181,11 +20253,17 @@ function openaiCompatibleProvider(config2) {
|
|
|
20181
20253
|
// controller) so `.cancel()` actually aborts the HTTP request.
|
|
20182
20254
|
// `config.signal` is the factory-level signal, typically undefined.
|
|
20183
20255
|
// v0.6.0 audit HIGH-2.
|
|
20184
|
-
|
|
20256
|
+
// v1.10.0: also apply PROVIDER_TIMEOUT_MS so a stalled connection
|
|
20257
|
+
// can't hang the harness forever.
|
|
20258
|
+
signal: fetchSignal
|
|
20185
20259
|
});
|
|
20186
20260
|
} catch (err) {
|
|
20187
20261
|
lastStatus = 0;
|
|
20188
20262
|
lastErrText = err instanceof Error ? err.message : String(err);
|
|
20263
|
+
if (params.signal?.aborted) {
|
|
20264
|
+
yield { kind: "error", message: "aborted" };
|
|
20265
|
+
return;
|
|
20266
|
+
}
|
|
20189
20267
|
if (attempt < MAX_RETRIES) {
|
|
20190
20268
|
await abortableSleep(backoffDelay(attempt, null), params.signal);
|
|
20191
20269
|
continue;
|
|
@@ -20246,6 +20324,10 @@ function openaiCompatibleProvider(config2) {
|
|
|
20246
20324
|
if (typeof delta?.content === "string" && delta.content.length > 0) {
|
|
20247
20325
|
yield { kind: "text", delta: delta.content };
|
|
20248
20326
|
}
|
|
20327
|
+
const reasoning = delta?.reasoning_content ?? delta?.reasoning;
|
|
20328
|
+
if (typeof reasoning === "string" && reasoning.length > 0) {
|
|
20329
|
+
yield { kind: "thinking", delta: reasoning };
|
|
20330
|
+
}
|
|
20249
20331
|
if (Array.isArray(delta?.tool_calls)) {
|
|
20250
20332
|
for (const tc of delta.tool_calls) {
|
|
20251
20333
|
const idx = tc.index ?? 0;
|
|
@@ -20306,7 +20388,7 @@ async function providerConfigFor(providerId) {
|
|
|
20306
20388
|
providerId
|
|
20307
20389
|
};
|
|
20308
20390
|
}
|
|
20309
|
-
var RETRYABLE_STATUSES, MAX_RETRIES, BACKOFF_BASE_MS, BACKOFF_CAP_MS, PROVIDER_ENDPOINTS;
|
|
20391
|
+
var RETRYABLE_STATUSES, MAX_RETRIES, BACKOFF_BASE_MS, BACKOFF_CAP_MS, PROVIDER_TIMEOUT_MS, PROVIDER_ENDPOINTS;
|
|
20310
20392
|
var init_openai_compatible = __esm({
|
|
20311
20393
|
"src/cli/provider/openai-compatible.ts"() {
|
|
20312
20394
|
"use strict";
|
|
@@ -20320,6 +20402,11 @@ var init_openai_compatible = __esm({
|
|
|
20320
20402
|
})();
|
|
20321
20403
|
BACKOFF_BASE_MS = 500;
|
|
20322
20404
|
BACKOFF_CAP_MS = 8e3;
|
|
20405
|
+
PROVIDER_TIMEOUT_MS = (() => {
|
|
20406
|
+
const raw = process.env.ZELARI_PROVIDER_TIMEOUT_MS;
|
|
20407
|
+
const n = raw ? Number.parseInt(raw, 10) : 3e5;
|
|
20408
|
+
return Number.isFinite(n) && n >= 1e4 ? n : 3e5;
|
|
20409
|
+
})();
|
|
20323
20410
|
PROVIDER_ENDPOINTS = {
|
|
20324
20411
|
"openai-compatible": "https://api.x.ai/v1",
|
|
20325
20412
|
"minimax": "https://api.minimax.io/v1",
|
|
@@ -22237,6 +22324,488 @@ var init_tools5 = __esm({
|
|
|
22237
22324
|
}
|
|
22238
22325
|
});
|
|
22239
22326
|
|
|
22327
|
+
// src/cli/ssh/targets.ts
|
|
22328
|
+
var targets_exports = {};
|
|
22329
|
+
__export(targets_exports, {
|
|
22330
|
+
buildSshBaseArgs: () => buildSshBaseArgs,
|
|
22331
|
+
deleteSshPassword: () => deleteSshPassword,
|
|
22332
|
+
formatSshTargetsForPrompt: () => formatSshTargetsForPrompt,
|
|
22333
|
+
getSshPassword: () => getSshPassword,
|
|
22334
|
+
getSshSecretsPath: () => getSshSecretsPath,
|
|
22335
|
+
getSshTarget: () => getSshTarget,
|
|
22336
|
+
getSshTargetsPath: () => getSshTargetsPath,
|
|
22337
|
+
hasSshPassword: () => hasSshPassword,
|
|
22338
|
+
isSshCommandAllowed: () => isSshCommandAllowed,
|
|
22339
|
+
listSshTargets: () => listSshTargets,
|
|
22340
|
+
readSshPublicKey: () => readSshPublicKey,
|
|
22341
|
+
removeSshTarget: () => removeSshTarget,
|
|
22342
|
+
runSsh: () => runSsh,
|
|
22343
|
+
setSshPassword: () => setSshPassword,
|
|
22344
|
+
testSshTarget: () => testSshTarget,
|
|
22345
|
+
upsertSshTarget: () => upsertSshTarget
|
|
22346
|
+
});
|
|
22347
|
+
import {
|
|
22348
|
+
chmodSync,
|
|
22349
|
+
existsSync as existsSync9,
|
|
22350
|
+
mkdirSync as mkdirSync6,
|
|
22351
|
+
readFileSync as readFileSync7,
|
|
22352
|
+
writeFileSync as writeFileSync5
|
|
22353
|
+
} from "node:fs";
|
|
22354
|
+
import { dirname as dirname2, join } from "node:path";
|
|
22355
|
+
import { homedir as homedir4 } from "node:os";
|
|
22356
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
22357
|
+
function getSshTargetsPath() {
|
|
22358
|
+
return join(homedir4(), ".zelari-code", "ssh-targets.json");
|
|
22359
|
+
}
|
|
22360
|
+
function getSshSecretsPath() {
|
|
22361
|
+
return join(homedir4(), ".zelari-code", "ssh-secrets.json");
|
|
22362
|
+
}
|
|
22363
|
+
function normalizeAuth(auth) {
|
|
22364
|
+
if (auth === "keyPath") return "keyPath";
|
|
22365
|
+
if (auth === "password") return "password";
|
|
22366
|
+
return "agent";
|
|
22367
|
+
}
|
|
22368
|
+
function readSecrets() {
|
|
22369
|
+
const path36 = getSshSecretsPath();
|
|
22370
|
+
if (!existsSync9(path36)) return {};
|
|
22371
|
+
try {
|
|
22372
|
+
return JSON.parse(readFileSync7(path36, "utf8"));
|
|
22373
|
+
} catch {
|
|
22374
|
+
return {};
|
|
22375
|
+
}
|
|
22376
|
+
}
|
|
22377
|
+
function writeSecrets(data) {
|
|
22378
|
+
const path36 = getSshSecretsPath();
|
|
22379
|
+
mkdirSync6(dirname2(path36), { recursive: true });
|
|
22380
|
+
writeFileSync5(path36, `${JSON.stringify(data, null, 2)}
|
|
22381
|
+
`, "utf8");
|
|
22382
|
+
try {
|
|
22383
|
+
chmodSync(path36, 384);
|
|
22384
|
+
} catch {
|
|
22385
|
+
}
|
|
22386
|
+
}
|
|
22387
|
+
function getSshPassword(id) {
|
|
22388
|
+
const p3 = readSecrets().passwords?.[id];
|
|
22389
|
+
return typeof p3 === "string" && p3.length > 0 ? p3 : void 0;
|
|
22390
|
+
}
|
|
22391
|
+
function hasSshPassword(id) {
|
|
22392
|
+
return Boolean(getSshPassword(id));
|
|
22393
|
+
}
|
|
22394
|
+
function setSshPassword(id, password) {
|
|
22395
|
+
const data = readSecrets();
|
|
22396
|
+
const passwords = { ...data.passwords ?? {} };
|
|
22397
|
+
if (password) {
|
|
22398
|
+
passwords[id] = password;
|
|
22399
|
+
} else {
|
|
22400
|
+
delete passwords[id];
|
|
22401
|
+
}
|
|
22402
|
+
writeSecrets({ passwords });
|
|
22403
|
+
}
|
|
22404
|
+
function deleteSshPassword(id) {
|
|
22405
|
+
const data = readSecrets();
|
|
22406
|
+
if (!data.passwords?.[id]) return;
|
|
22407
|
+
const passwords = { ...data.passwords };
|
|
22408
|
+
delete passwords[id];
|
|
22409
|
+
writeSecrets({ passwords });
|
|
22410
|
+
}
|
|
22411
|
+
function readStore2() {
|
|
22412
|
+
const path36 = getSshTargetsPath();
|
|
22413
|
+
if (!existsSync9(path36)) return [];
|
|
22414
|
+
try {
|
|
22415
|
+
const parsed = JSON.parse(readFileSync7(path36, "utf8"));
|
|
22416
|
+
const list = Array.isArray(parsed.targets) ? parsed.targets : [];
|
|
22417
|
+
return list.filter(
|
|
22418
|
+
(t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
|
|
22419
|
+
).map((t) => ({
|
|
22420
|
+
...t,
|
|
22421
|
+
port: typeof t.port === "number" ? t.port : 22,
|
|
22422
|
+
auth: normalizeAuth(t.auth),
|
|
22423
|
+
enabled: t.enabled !== false
|
|
22424
|
+
}));
|
|
22425
|
+
} catch {
|
|
22426
|
+
return [];
|
|
22427
|
+
}
|
|
22428
|
+
}
|
|
22429
|
+
function writeStore2(targets) {
|
|
22430
|
+
const path36 = getSshTargetsPath();
|
|
22431
|
+
mkdirSync6(dirname2(path36), { recursive: true });
|
|
22432
|
+
const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
|
|
22433
|
+
writeFileSync5(
|
|
22434
|
+
path36,
|
|
22435
|
+
`${JSON.stringify({ targets: clean }, null, 2)}
|
|
22436
|
+
`,
|
|
22437
|
+
"utf8"
|
|
22438
|
+
);
|
|
22439
|
+
}
|
|
22440
|
+
function listSshTargets() {
|
|
22441
|
+
const targets = readStore2().map((t) => ({
|
|
22442
|
+
...t,
|
|
22443
|
+
hasPassword: t.auth === "password" ? hasSshPassword(t.id) : false
|
|
22444
|
+
}));
|
|
22445
|
+
return { path: getSshTargetsPath(), targets };
|
|
22446
|
+
}
|
|
22447
|
+
function getSshTarget(id) {
|
|
22448
|
+
return readStore2().find((t) => t.id === id);
|
|
22449
|
+
}
|
|
22450
|
+
function upsertSshTarget(target) {
|
|
22451
|
+
const id = target.id?.trim();
|
|
22452
|
+
if (!id || !/^[a-zA-Z0-9_-]+$/.test(id)) {
|
|
22453
|
+
return { ok: false, error: "Invalid id (letters, digits, _ -)" };
|
|
22454
|
+
}
|
|
22455
|
+
if (!target.host?.trim() || !target.user?.trim()) {
|
|
22456
|
+
return { ok: false, error: "host and user are required" };
|
|
22457
|
+
}
|
|
22458
|
+
const auth = normalizeAuth(target.auth);
|
|
22459
|
+
if (auth === "keyPath" && !target.keyPath?.trim()) {
|
|
22460
|
+
return { ok: false, error: "keyPath required when auth=keyPath" };
|
|
22461
|
+
}
|
|
22462
|
+
if (auth === "password") {
|
|
22463
|
+
const incoming = target.password;
|
|
22464
|
+
if (typeof incoming === "string" && incoming.length > 0) {
|
|
22465
|
+
setSshPassword(id, incoming);
|
|
22466
|
+
} else if (!hasSshPassword(id)) {
|
|
22467
|
+
return {
|
|
22468
|
+
ok: false,
|
|
22469
|
+
error: "password required when auth=password (first save)"
|
|
22470
|
+
};
|
|
22471
|
+
}
|
|
22472
|
+
} else {
|
|
22473
|
+
deleteSshPassword(id);
|
|
22474
|
+
}
|
|
22475
|
+
const list = readStore2().filter((t) => t.id !== id);
|
|
22476
|
+
list.push({
|
|
22477
|
+
id,
|
|
22478
|
+
name: target.name?.trim() || id,
|
|
22479
|
+
host: target.host.trim(),
|
|
22480
|
+
port: target.port && target.port > 0 ? target.port : 22,
|
|
22481
|
+
user: target.user.trim(),
|
|
22482
|
+
auth,
|
|
22483
|
+
keyPath: auth === "keyPath" ? target.keyPath?.trim() : void 0,
|
|
22484
|
+
publicKeyPath: auth === "password" ? void 0 : target.publicKeyPath?.trim(),
|
|
22485
|
+
defaultRemotePath: target.defaultRemotePath?.trim(),
|
|
22486
|
+
tags: target.tags,
|
|
22487
|
+
allowedCommands: target.allowedCommands,
|
|
22488
|
+
enabled: target.enabled !== false,
|
|
22489
|
+
notes: target.notes
|
|
22490
|
+
});
|
|
22491
|
+
list.sort((a, b) => a.id.localeCompare(b.id));
|
|
22492
|
+
writeStore2(list);
|
|
22493
|
+
return { ok: true };
|
|
22494
|
+
}
|
|
22495
|
+
function removeSshTarget(id) {
|
|
22496
|
+
const list = readStore2();
|
|
22497
|
+
if (!list.some((t) => t.id === id)) {
|
|
22498
|
+
return { ok: false, error: `Target "${id}" not found` };
|
|
22499
|
+
}
|
|
22500
|
+
writeStore2(list.filter((t) => t.id !== id));
|
|
22501
|
+
deleteSshPassword(id);
|
|
22502
|
+
return { ok: true };
|
|
22503
|
+
}
|
|
22504
|
+
function buildSshBaseArgs(target) {
|
|
22505
|
+
const args = [
|
|
22506
|
+
"-o",
|
|
22507
|
+
"StrictHostKeyChecking=accept-new",
|
|
22508
|
+
"-o",
|
|
22509
|
+
"ConnectTimeout=12",
|
|
22510
|
+
"-p",
|
|
22511
|
+
String(target.port ?? 22)
|
|
22512
|
+
];
|
|
22513
|
+
if (target.auth === "password") {
|
|
22514
|
+
args.push(
|
|
22515
|
+
"-o",
|
|
22516
|
+
"BatchMode=no",
|
|
22517
|
+
"-o",
|
|
22518
|
+
"PreferredAuthentications=password,keyboard-interactive",
|
|
22519
|
+
"-o",
|
|
22520
|
+
"PubkeyAuthentication=no",
|
|
22521
|
+
"-o",
|
|
22522
|
+
"NumberOfPasswordPrompts=1"
|
|
22523
|
+
);
|
|
22524
|
+
} else {
|
|
22525
|
+
args.push("-o", "BatchMode=yes");
|
|
22526
|
+
if (target.auth === "keyPath" && target.keyPath) {
|
|
22527
|
+
args.push("-i", target.keyPath);
|
|
22528
|
+
}
|
|
22529
|
+
}
|
|
22530
|
+
args.push(`${target.user}@${target.host}`);
|
|
22531
|
+
return args;
|
|
22532
|
+
}
|
|
22533
|
+
function ensureAskpassHelper() {
|
|
22534
|
+
const dir = join(homedir4(), ".zelari-code", "ssh-helpers");
|
|
22535
|
+
mkdirSync6(dir, { recursive: true });
|
|
22536
|
+
const cjs = join(dir, "askpass.cjs");
|
|
22537
|
+
writeFileSync5(
|
|
22538
|
+
cjs,
|
|
22539
|
+
"process.stdout.write(process.env.ZELARI_SSH_ASKPASS_PASS || '');\n",
|
|
22540
|
+
"utf8"
|
|
22541
|
+
);
|
|
22542
|
+
if (process.platform === "win32") {
|
|
22543
|
+
const cmd = join(dir, "askpass.cmd");
|
|
22544
|
+
writeFileSync5(
|
|
22545
|
+
cmd,
|
|
22546
|
+
`@echo off\r
|
|
22547
|
+
node "%~dp0askpass.cjs"\r
|
|
22548
|
+
`,
|
|
22549
|
+
"utf8"
|
|
22550
|
+
);
|
|
22551
|
+
return cmd;
|
|
22552
|
+
}
|
|
22553
|
+
const sh = join(dir, "askpass.sh");
|
|
22554
|
+
writeFileSync5(
|
|
22555
|
+
sh,
|
|
22556
|
+
`#!/bin/sh
|
|
22557
|
+
exec node "$(dirname "$0")/askpass.cjs"
|
|
22558
|
+
`,
|
|
22559
|
+
"utf8"
|
|
22560
|
+
);
|
|
22561
|
+
try {
|
|
22562
|
+
chmodSync(sh, 493);
|
|
22563
|
+
} catch {
|
|
22564
|
+
}
|
|
22565
|
+
return sh;
|
|
22566
|
+
}
|
|
22567
|
+
function runSsh(target, remoteCommand, timeoutMs = 6e4) {
|
|
22568
|
+
return new Promise((resolve) => {
|
|
22569
|
+
if (target.auth === "password" && !getSshPassword(target.id)) {
|
|
22570
|
+
resolve({
|
|
22571
|
+
code: 1,
|
|
22572
|
+
stdout: "",
|
|
22573
|
+
stderr: "No password stored for this target \u2014 edit target and set password"
|
|
22574
|
+
});
|
|
22575
|
+
return;
|
|
22576
|
+
}
|
|
22577
|
+
const args = [...buildSshBaseArgs(target), remoteCommand];
|
|
22578
|
+
const env = { ...process.env };
|
|
22579
|
+
if (target.auth === "password") {
|
|
22580
|
+
const pass = getSshPassword(target.id);
|
|
22581
|
+
env.SSH_ASKPASS = ensureAskpassHelper();
|
|
22582
|
+
env.SSH_ASKPASS_REQUIRE = "force";
|
|
22583
|
+
if (!env.DISPLAY) env.DISPLAY = "1";
|
|
22584
|
+
env.ZELARI_SSH_ASKPASS_PASS = pass;
|
|
22585
|
+
}
|
|
22586
|
+
const child = spawn4("ssh", args, {
|
|
22587
|
+
windowsHide: true,
|
|
22588
|
+
env,
|
|
22589
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
22590
|
+
});
|
|
22591
|
+
let stdout = "";
|
|
22592
|
+
let stderr = "";
|
|
22593
|
+
const cap = 4e4;
|
|
22594
|
+
child.stdout?.on("data", (d) => {
|
|
22595
|
+
if (stdout.length < cap) stdout += d.toString("utf8");
|
|
22596
|
+
});
|
|
22597
|
+
child.stderr?.on("data", (d) => {
|
|
22598
|
+
if (stderr.length < cap) stderr += d.toString("utf8");
|
|
22599
|
+
});
|
|
22600
|
+
const timer = setTimeout(() => {
|
|
22601
|
+
child.kill("SIGTERM");
|
|
22602
|
+
resolve({
|
|
22603
|
+
code: 124,
|
|
22604
|
+
stdout,
|
|
22605
|
+
stderr: stderr + "\n[ssh] timeout"
|
|
22606
|
+
});
|
|
22607
|
+
}, timeoutMs);
|
|
22608
|
+
child.on("error", (err) => {
|
|
22609
|
+
clearTimeout(timer);
|
|
22610
|
+
resolve({
|
|
22611
|
+
code: 127,
|
|
22612
|
+
stdout,
|
|
22613
|
+
stderr: err.message.includes("ENOENT") ? "ssh not found on PATH \u2014 install OpenSSH client" : err.message
|
|
22614
|
+
});
|
|
22615
|
+
});
|
|
22616
|
+
child.on("close", (code) => {
|
|
22617
|
+
clearTimeout(timer);
|
|
22618
|
+
resolve({ code: code ?? 1, stdout, stderr });
|
|
22619
|
+
});
|
|
22620
|
+
});
|
|
22621
|
+
}
|
|
22622
|
+
function readSshPublicKey(keyOrPubPath) {
|
|
22623
|
+
const raw = keyOrPubPath.trim().replace(/^["']|["']$/g, "");
|
|
22624
|
+
if (!raw) return { ok: false, error: "Empty path" };
|
|
22625
|
+
const candidates = raw.endsWith(".pub") ? [raw] : [`${raw}.pub`, raw];
|
|
22626
|
+
for (const p3 of candidates) {
|
|
22627
|
+
if (!existsSync9(p3)) continue;
|
|
22628
|
+
try {
|
|
22629
|
+
const content = readFileSync7(p3, "utf8").trim();
|
|
22630
|
+
if (!content) continue;
|
|
22631
|
+
if (/BEGIN .*PRIVATE KEY/i.test(content)) {
|
|
22632
|
+
return {
|
|
22633
|
+
ok: false,
|
|
22634
|
+
error: "Path points to a private key \u2014 use the .pub file instead"
|
|
22635
|
+
};
|
|
22636
|
+
}
|
|
22637
|
+
return { ok: true, path: p3, content };
|
|
22638
|
+
} catch (e) {
|
|
22639
|
+
return {
|
|
22640
|
+
ok: false,
|
|
22641
|
+
error: e instanceof Error ? e.message : String(e)
|
|
22642
|
+
};
|
|
22643
|
+
}
|
|
22644
|
+
}
|
|
22645
|
+
return {
|
|
22646
|
+
ok: false,
|
|
22647
|
+
error: `Public key not found (tried: ${candidates.join(", ")})`
|
|
22648
|
+
};
|
|
22649
|
+
}
|
|
22650
|
+
async function testSshTarget(id) {
|
|
22651
|
+
const t = getSshTarget(id);
|
|
22652
|
+
if (!t) return { ok: false, message: `Target "${id}" not found` };
|
|
22653
|
+
if (t.enabled === false) {
|
|
22654
|
+
return { ok: false, message: `Target "${id}" is disabled` };
|
|
22655
|
+
}
|
|
22656
|
+
if (t.auth === "password" && !hasSshPassword(id)) {
|
|
22657
|
+
return {
|
|
22658
|
+
ok: false,
|
|
22659
|
+
message: "Password auth selected but no password saved \u2014 edit target"
|
|
22660
|
+
};
|
|
22661
|
+
}
|
|
22662
|
+
const r = await runSsh(t, "true", 15e3);
|
|
22663
|
+
if (r.code === 0) {
|
|
22664
|
+
return { ok: true, message: `OK ${t.user}@${t.host}:${t.port ?? 22}` };
|
|
22665
|
+
}
|
|
22666
|
+
return {
|
|
22667
|
+
ok: false,
|
|
22668
|
+
message: (r.stderr || r.stdout || `exit ${r.code}`).trim().slice(0, 400)
|
|
22669
|
+
};
|
|
22670
|
+
}
|
|
22671
|
+
function formatSshTargetsForPrompt() {
|
|
22672
|
+
const targets = readStore2().filter((t) => t.enabled !== false);
|
|
22673
|
+
if (targets.length === 0) return "";
|
|
22674
|
+
const lines = [
|
|
22675
|
+
"# Configured SSH targets",
|
|
22676
|
+
"Use tools ssh_status / ssh_run with targetId. Do not invent hosts."
|
|
22677
|
+
];
|
|
22678
|
+
for (const t of targets) {
|
|
22679
|
+
const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
|
|
22680
|
+
const path36 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
|
|
22681
|
+
const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
|
|
22682
|
+
const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
|
|
22683
|
+
lines.push(
|
|
22684
|
+
`- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path36}${tags}${allow}`
|
|
22685
|
+
);
|
|
22686
|
+
}
|
|
22687
|
+
return lines.join("\n");
|
|
22688
|
+
}
|
|
22689
|
+
function isSshCommandAllowed(target, command) {
|
|
22690
|
+
const cmd = command.trim();
|
|
22691
|
+
if (!cmd) return { ok: false, error: "empty command" };
|
|
22692
|
+
const list = target.allowedCommands ?? [];
|
|
22693
|
+
if (list.length === 0) {
|
|
22694
|
+
return {
|
|
22695
|
+
ok: false,
|
|
22696
|
+
error: "No allowedCommands on this target \u2014 only ssh_status is permitted. Add allowlist entries in Settings \u2192 Connections."
|
|
22697
|
+
};
|
|
22698
|
+
}
|
|
22699
|
+
const dangerous = /[;&|`$(){}<>]|\brm\s+-rf\b|\bmkfs\b|\bdd\s+if=/i;
|
|
22700
|
+
for (const pattern of list) {
|
|
22701
|
+
const p3 = pattern.trim();
|
|
22702
|
+
if (!p3) continue;
|
|
22703
|
+
if (p3.endsWith("*")) {
|
|
22704
|
+
const prefix = p3.slice(0, -1);
|
|
22705
|
+
if (cmd.startsWith(prefix)) {
|
|
22706
|
+
if (dangerous.test(cmd)) {
|
|
22707
|
+
return {
|
|
22708
|
+
ok: false,
|
|
22709
|
+
error: "Command contains blocked shell metacharacters"
|
|
22710
|
+
};
|
|
22711
|
+
}
|
|
22712
|
+
return { ok: true };
|
|
22713
|
+
}
|
|
22714
|
+
} else if (cmd === p3) {
|
|
22715
|
+
if (/\brm\s+-rf\b|\bmkfs\b|\bdd\s+if=/i.test(cmd)) {
|
|
22716
|
+
return { ok: false, error: "Command blocked for safety" };
|
|
22717
|
+
}
|
|
22718
|
+
return { ok: true };
|
|
22719
|
+
}
|
|
22720
|
+
}
|
|
22721
|
+
return {
|
|
22722
|
+
ok: false,
|
|
22723
|
+
error: `Command not in allowlist. Allowed: ${list.join(", ")}`
|
|
22724
|
+
};
|
|
22725
|
+
}
|
|
22726
|
+
var init_targets = __esm({
|
|
22727
|
+
"src/cli/ssh/targets.ts"() {
|
|
22728
|
+
"use strict";
|
|
22729
|
+
}
|
|
22730
|
+
});
|
|
22731
|
+
|
|
22732
|
+
// src/cli/ssh/tools.ts
|
|
22733
|
+
function createSshTools() {
|
|
22734
|
+
if (process.env.ZELARI_SSH === "0") return [];
|
|
22735
|
+
const sshStatus = {
|
|
22736
|
+
name: "ssh_status",
|
|
22737
|
+
description: "Run a fixed safe status probe on a configured SSH target (hostname, uname, uptime, disk). Use targetId from configured SSH targets.",
|
|
22738
|
+
permissions: ["network"],
|
|
22739
|
+
inputSchema: external_exports.object({
|
|
22740
|
+
targetId: external_exports.string().describe("Id of the SSH target from config")
|
|
22741
|
+
}),
|
|
22742
|
+
execute: async (input) => {
|
|
22743
|
+
const { targetId } = input;
|
|
22744
|
+
const t = getSshTarget(targetId);
|
|
22745
|
+
if (!t) {
|
|
22746
|
+
return typedErr(
|
|
22747
|
+
`Unknown SSH target "${targetId}". Configure targets in Desktop Settings \u2192 Connections or ~/.zelari-code/ssh-targets.json`
|
|
22748
|
+
);
|
|
22749
|
+
}
|
|
22750
|
+
if (t.enabled === false) {
|
|
22751
|
+
return typedErr(`SSH target "${targetId}" is disabled`);
|
|
22752
|
+
}
|
|
22753
|
+
const r = await runSsh(t, STATUS_REMOTE, 3e4);
|
|
22754
|
+
const body = [
|
|
22755
|
+
`target=${t.id} ${t.user}@${t.host}:${t.port ?? 22}`,
|
|
22756
|
+
`exit=${r.code}`,
|
|
22757
|
+
r.stdout.trim(),
|
|
22758
|
+
r.stderr.trim() ? `stderr:
|
|
22759
|
+
${r.stderr.trim()}` : ""
|
|
22760
|
+
].filter(Boolean).join("\n");
|
|
22761
|
+
if (r.code !== 0) return typedErr(body || `ssh exit ${r.code}`);
|
|
22762
|
+
return typedOk(body);
|
|
22763
|
+
}
|
|
22764
|
+
};
|
|
22765
|
+
const sshRun = {
|
|
22766
|
+
name: "ssh_run",
|
|
22767
|
+
description: "Run a remote command on a configured SSH target. Command must match the target allowlist (allowedCommands). Prefer ssh_status for health checks.",
|
|
22768
|
+
permissions: ["network"],
|
|
22769
|
+
inputSchema: external_exports.object({
|
|
22770
|
+
targetId: external_exports.string().describe("Id of the SSH target"),
|
|
22771
|
+
command: external_exports.string().describe("Remote command (must be allowlisted on the target)")
|
|
22772
|
+
}),
|
|
22773
|
+
execute: async (input) => {
|
|
22774
|
+
const { targetId, command } = input;
|
|
22775
|
+
const t = getSshTarget(targetId);
|
|
22776
|
+
if (!t) {
|
|
22777
|
+
return typedErr(`Unknown SSH target "${targetId}"`);
|
|
22778
|
+
}
|
|
22779
|
+
if (t.enabled === false) {
|
|
22780
|
+
return typedErr(`SSH target "${targetId}" is disabled`);
|
|
22781
|
+
}
|
|
22782
|
+
const allow = isSshCommandAllowed(t, command);
|
|
22783
|
+
if (!allow.ok) return typedErr(allow.error);
|
|
22784
|
+
const r = await runSsh(t, command, 6e4);
|
|
22785
|
+
const body = [
|
|
22786
|
+
`target=${t.id} exit=${r.code}`,
|
|
22787
|
+
`$ ${command}`,
|
|
22788
|
+
r.stdout.trim(),
|
|
22789
|
+
r.stderr.trim() ? `stderr:
|
|
22790
|
+
${r.stderr.trim()}` : ""
|
|
22791
|
+
].filter(Boolean).join("\n");
|
|
22792
|
+
if (r.code !== 0) return typedErr(body || `ssh exit ${r.code}`);
|
|
22793
|
+
return typedOk(body);
|
|
22794
|
+
}
|
|
22795
|
+
};
|
|
22796
|
+
return [sshStatus, sshRun];
|
|
22797
|
+
}
|
|
22798
|
+
var STATUS_REMOTE;
|
|
22799
|
+
var init_tools6 = __esm({
|
|
22800
|
+
"src/cli/ssh/tools.ts"() {
|
|
22801
|
+
"use strict";
|
|
22802
|
+
init_zod();
|
|
22803
|
+
init_toolTypes();
|
|
22804
|
+
init_targets();
|
|
22805
|
+
STATUS_REMOTE = 'set -e; echo "=== host ==="; hostname 2>/dev/null || true; uname -a 2>/dev/null || true; echo "=== uptime ==="; uptime 2>/dev/null || true; echo "=== disk / ==="; df -h / 2>/dev/null | tail -1 || true';
|
|
22806
|
+
}
|
|
22807
|
+
});
|
|
22808
|
+
|
|
22240
22809
|
// src/cli/toolRegistry.ts
|
|
22241
22810
|
var toolRegistry_exports = {};
|
|
22242
22811
|
__export(toolRegistry_exports, {
|
|
@@ -22315,6 +22884,16 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
22315
22884
|
permissions: browserTool.permissions ?? []
|
|
22316
22885
|
});
|
|
22317
22886
|
}
|
|
22887
|
+
if (!readOnly && process.env.ZELARI_SSH !== "0") {
|
|
22888
|
+
for (const t of createSshTools()) {
|
|
22889
|
+
registry3.register(t);
|
|
22890
|
+
tools.push({
|
|
22891
|
+
name: t.name,
|
|
22892
|
+
description: t.description,
|
|
22893
|
+
permissions: t.permissions ?? []
|
|
22894
|
+
});
|
|
22895
|
+
}
|
|
22896
|
+
}
|
|
22318
22897
|
if (!readOnly && options.enableTask !== false) {
|
|
22319
22898
|
const taskTool = createTaskTool({
|
|
22320
22899
|
createSubAgentContext: async () => {
|
|
@@ -22553,6 +23132,7 @@ var init_toolRegistry = __esm({
|
|
|
22553
23132
|
init_tools3();
|
|
22554
23133
|
init_tools4();
|
|
22555
23134
|
init_tools5();
|
|
23135
|
+
init_tools6();
|
|
22556
23136
|
init_openai_compatible();
|
|
22557
23137
|
init_skills2();
|
|
22558
23138
|
HARNESS_BUILTIN_NAMES = /* @__PURE__ */ new Set([
|
|
@@ -22571,6 +23151,16 @@ var init_toolRegistry = __esm({
|
|
|
22571
23151
|
});
|
|
22572
23152
|
|
|
22573
23153
|
// packages/core/dist/agents/roles.js
|
|
23154
|
+
function resolveRoleSystemPrompt(agent, runMode = "implementation") {
|
|
23155
|
+
const parts = [agent.systemPrompt];
|
|
23156
|
+
if (runMode === "design-phase" && agent.designPhaseAddendum?.trim()) {
|
|
23157
|
+
parts.push(agent.designPhaseAddendum.trim());
|
|
23158
|
+
}
|
|
23159
|
+
if (runMode === "implementation" && agent.implementationAddendum?.trim()) {
|
|
23160
|
+
parts.push(agent.implementationAddendum.trim());
|
|
23161
|
+
}
|
|
23162
|
+
return parts.join("\n\n");
|
|
23163
|
+
}
|
|
22574
23164
|
function getAgent(id) {
|
|
22575
23165
|
return AGENT_ROLES.find((a) => a.id === id);
|
|
22576
23166
|
}
|
|
@@ -22604,24 +23194,10 @@ function swapMembers(roles, swap) {
|
|
|
22604
23194
|
return targets.get(toId);
|
|
22605
23195
|
});
|
|
22606
23196
|
}
|
|
22607
|
-
var
|
|
23197
|
+
var AGENT_ROLES, UnknownMemberError;
|
|
22608
23198
|
var init_roles = __esm({
|
|
22609
23199
|
"packages/core/dist/agents/roles.js"() {
|
|
22610
23200
|
"use strict";
|
|
22611
|
-
CLARIFICATION_PROTOCOL8 = `
|
|
22612
|
-
|
|
22613
|
-
WHEN TO ASK THE USER (clarification):
|
|
22614
|
-
If a single missing fact would materially change your output (target platform, scope, a binary design choice with significant trade-offs, a constraint you cannot safely assume), pause and ask the user by appending EXACTLY this block at the end of your message:
|
|
22615
|
-
|
|
22616
|
-
---QUESTION---
|
|
22617
|
-
{ "question": "One focused question", "choices": ["Option A", "Option B", "Option C"], "context": "Why this matters" }
|
|
22618
|
-
---END---
|
|
22619
|
-
|
|
22620
|
-
Rules for clarifications:
|
|
22621
|
-
- Ask AT MOST ONE question per turn, and only when genuinely blocked.
|
|
22622
|
-
- Prefer a small set of concrete "choices" (2-4). The user can still type a custom answer.
|
|
22623
|
-
- Do NOT ask for information that could be reasonably assumed or already in shared context.
|
|
22624
|
-
- If you can proceed with a sound documented assumption, DO SO instead of asking.`;
|
|
22625
23201
|
AGENT_ROLES = [
|
|
22626
23202
|
{
|
|
22627
23203
|
id: "charont",
|
|
@@ -22632,22 +23208,20 @@ Rules for clarifications:
|
|
|
22632
23208
|
avatar: "L",
|
|
22633
23209
|
systemPrompt: `You are Caronte, the Council Director and Orchestrator \u2014 the strategic mind that frames the problem for the rest of the council.
|
|
22634
23210
|
|
|
22635
|
-
## Methodology
|
|
22636
|
-
1. Parse the request into
|
|
22637
|
-
2. Identify
|
|
22638
|
-
3. For each
|
|
22639
|
-
4. Flag
|
|
22640
|
-
5. State
|
|
22641
|
-
|
|
22642
|
-
##
|
|
22643
|
-
- Be decisive: commit to a decomposition
|
|
22644
|
-
- Distinguish
|
|
22645
|
-
-
|
|
22646
|
-
|
|
22647
|
-
|
|
22648
|
-
|
|
22649
|
-
A short "Analysis" section (the goal + constraints), then a "Delegation Plan" with one bullet per specialist naming what they should produce. Keep it tight \u2014 under 150 words total.${CLARIFICATION_PROTOCOL8}`,
|
|
22650
|
-
// v0.7.2: coding-oriented tools (read/search/explore) instead of planner/vault.
|
|
23211
|
+
## Methodology
|
|
23212
|
+
1. Parse the request into goal + implicit constraints.
|
|
23213
|
+
2. Identify 3\u20136 sub-problems (planning, ideation, knowledge structure, quality).
|
|
23214
|
+
3. For each, name the specialist and one-line expected deliverable.
|
|
23215
|
+
4. Flag cross-cutting risks or ordering constraints.
|
|
23216
|
+
5. State what "done" looks like.
|
|
23217
|
+
|
|
23218
|
+
## Principles
|
|
23219
|
+
- Be decisive: commit to a decomposition.
|
|
23220
|
+
- Distinguish given vs assumed.
|
|
23221
|
+
- Cut scope creep; you set context for everyone downstream.
|
|
23222
|
+
|
|
23223
|
+
## Output
|
|
23224
|
+
Short "Analysis" then "Delegation Plan" (one bullet per specialist). Under 150 words.`,
|
|
22651
23225
|
tools: ["list_files", "read_file", "grep_content"],
|
|
22652
23226
|
skills: ["project-planner", "research-analyst"]
|
|
22653
23227
|
},
|
|
@@ -22660,71 +23234,53 @@ A short "Analysis" section (the goal + constraints), then a "Delegation Plan" wi
|
|
|
22660
23234
|
avatar: "N",
|
|
22661
23235
|
systemPrompt: `You are Nettuno, the Project Planner \u2014 you turn intent into a buildable, sequenced plan.
|
|
22662
23236
|
|
|
22663
|
-
## Methodology
|
|
22664
|
-
1. Read
|
|
22665
|
-
2. Decompose
|
|
22666
|
-
3. Within each phase, define
|
|
22667
|
-
4.
|
|
22668
|
-
5. Sanity-check sequencing
|
|
22669
|
-
|
|
22670
|
-
## OUTPUT REQUIREMENTS \u2014 every task MUST include:
|
|
22671
|
-
1. **File references**: specific file paths and line numbers where changes land.
|
|
22672
|
-
2. **Acceptance criteria**: concrete, testable conditions that prove completion.
|
|
22673
|
-
3. **QA scenarios**: at least one testable scenario per task.
|
|
23237
|
+
## Methodology
|
|
23238
|
+
1. Read prior context (especially Caronte) before planning.
|
|
23239
|
+
2. Decompose into ordered PHASES with clear exit criteria.
|
|
23240
|
+
3. Within each phase, define TASKS with dependencies, priority, acceptance criteria.
|
|
23241
|
+
4. Ground tasks in real file paths when a codebase is present.
|
|
23242
|
+
5. Sanity-check sequencing and scope.
|
|
22674
23243
|
|
|
22675
|
-
|
|
22676
|
-
-
|
|
22677
|
-
|
|
22678
|
-
|
|
22679
|
-
- QA: correct password succeeds, wrong password shows error message
|
|
23244
|
+
## Every task must include
|
|
23245
|
+
- **fileRefs**: concrete paths (and lines when known)
|
|
23246
|
+
- **acceptance**: testable completion conditions
|
|
23247
|
+
- **qaScenario**: at least one verifiable scenario
|
|
22680
23248
|
|
|
22681
23249
|
## Quality bar
|
|
22682
|
-
-
|
|
22683
|
-
-
|
|
22684
|
-
-
|
|
23250
|
+
- Fewer well-specified tasks beat many vague ones.
|
|
23251
|
+
- Explicit dependencies.
|
|
23252
|
+
- Stay under 250 words of prose; durable plans use workspace tools when required by mode banners.`,
|
|
23253
|
+
designPhaseAddendum: `## Design-phase: persist the plan (mandatory)
|
|
22685
23254
|
|
|
22686
|
-
|
|
22687
|
-
|
|
22688
|
-
## Design-phase mandatory plan artifact
|
|
22689
|
-
In design-phase mode (TASK mentions design/architecture/spec, no existing codebase to edit), you MUST persist the plan through workspace tools. The plan exists ONLY if it was persisted via a tool call \u2014 prose does not count, and the post-run check will flag your run if the plan is missing.
|
|
22690
|
-
|
|
22691
|
-
PREFERRED \u2014 emit ONE single \`createPlan\` call containing the whole plan: 4 phases, 12 concrete tasks (3 per phase), and 1 milestone, all in the same call:
|
|
23255
|
+
Prose is not a plan. Emit ONE \`createPlan\` with ~4 phases \xD7 ~3 tasks + 1 milestone.
|
|
22692
23256
|
|
|
23257
|
+
Minimal shape:
|
|
22693
23258
|
\`\`\`
|
|
22694
23259
|
createPlan({
|
|
22695
23260
|
phases: [
|
|
22696
23261
|
{
|
|
22697
|
-
name: "
|
|
22698
|
-
description: "
|
|
23262
|
+
name: "Phase name",
|
|
23263
|
+
description: "Exit criterion in one line",
|
|
22699
23264
|
order: 1,
|
|
22700
|
-
color: "#3b82f6",
|
|
22701
23265
|
tasks: [
|
|
22702
|
-
{
|
|
22703
|
-
|
|
22704
|
-
|
|
23266
|
+
{
|
|
23267
|
+
title: "Task title",
|
|
23268
|
+
description: "Context",
|
|
23269
|
+
fileRefs: ["src/path.ts"],
|
|
23270
|
+
acceptance: ["measurable done"],
|
|
23271
|
+
qaScenario: "how to verify",
|
|
23272
|
+
priority: "high"
|
|
23273
|
+
}
|
|
22705
23274
|
]
|
|
22706
|
-
}
|
|
22707
|
-
{ name: "...", order: 2, tasks: [ /* 3 tasks */ ] },
|
|
22708
|
-
{ name: "...", order: 3, tasks: [ /* 3 tasks */ ] },
|
|
22709
|
-
{ name: "...", order: 4, tasks: [ /* 3 tasks */ ] }
|
|
23275
|
+
}
|
|
22710
23276
|
],
|
|
22711
|
-
milestone: { title: "v0.1
|
|
23277
|
+
milestone: { title: "v0.1 design-complete", description: "\u2026", targetVersion: "v0.1.0" }
|
|
22712
23278
|
})
|
|
22713
23279
|
\`\`\`
|
|
22714
23280
|
|
|
22715
|
-
Every task
|
|
23281
|
+
Every task needs fileRefs, acceptance, qaScenario. Prefer one batched createPlan over many createPhase/createTask calls.
|
|
22716
23282
|
|
|
22717
|
-
|
|
22718
|
-
|
|
22719
|
-
Do NOT output tasks as prose. The system will refuse any task that does not pass a valid phaseId.
|
|
22720
|
-
|
|
22721
|
-
## NFR spec (when plan sets motion/performance/a11y budgets)
|
|
22722
|
-
If the plan includes measurable constraints (JS byte budget, compositor-only animation, reduced-motion), emit ONE \`createNfrSpec\` call after \`createPlan\`:
|
|
22723
|
-
\`\`\`
|
|
22724
|
-
createNfrSpec({ targets: ["index.html"], compositorOnly: true, forbidLayoutProps: true, inlineJsMaxBytes: 5120 })
|
|
22725
|
-
\`\`\`
|
|
22726
|
-
Prose budgets are not machine-verifiable \u2014 the spec file is.`,
|
|
22727
|
-
// v0.7.2: read/search the codebase to ground the plan in reality.
|
|
23283
|
+
If the plan sets measurable motion/perf/a11y budgets, also emit one \`createNfrSpec\` after createPlan.`,
|
|
22728
23284
|
tools: ["list_files", "read_file", "grep_content"],
|
|
22729
23285
|
skills: ["project-planner", "vault-manager"]
|
|
22730
23286
|
},
|
|
@@ -22735,40 +23291,27 @@ Prose budgets are not machine-verifiable \u2014 the spec file is.`,
|
|
|
22735
23291
|
role: "Creative Ideator",
|
|
22736
23292
|
color: "#f59e0b",
|
|
22737
23293
|
avatar: "G",
|
|
22738
|
-
systemPrompt: `You are Gerione, the Creative Ideator \u2014
|
|
22739
|
-
|
|
22740
|
-
## Methodology (work in this order)
|
|
22741
|
-
1. Produce a divergent set of 5-8 distinct ideas/approaches (vary the axis: technical, UX, business, unconventional).
|
|
22742
|
-
2. Cluster related ideas into 2-4 themes.
|
|
22743
|
-
3. Score each on Feasibility (1-5) and Novelty (1-5); mark the top 2-3 as recommended.
|
|
22744
|
-
4. For the recommended ideas, add one line on how to de-risk them.
|
|
22745
|
-
5. Build on \u2014 do not repeat \u2014 insights from earlier agents.
|
|
22746
|
-
|
|
22747
|
-
## Operating principles
|
|
22748
|
-
- Diverge before you converge: quantity first, judgment second.
|
|
22749
|
-
- Bold but grounded \u2014 an idea must be actionable, not a fantasy.
|
|
22750
|
-
- Name assumptions explicitly. If a core assumption is unknowable, ask (clarification protocol).
|
|
22751
|
-
|
|
22752
|
-
## Output format
|
|
22753
|
-
## Ideas (bulleted)
|
|
22754
|
-
## Themes
|
|
22755
|
-
## Top picks (with feasibility/novelty + de-risk note)
|
|
22756
|
-
|
|
22757
|
-
Stay under 200 words.${CLARIFICATION_PROTOCOL8}
|
|
23294
|
+
systemPrompt: `You are Gerione, the Creative Ideator \u2014 diverge, then converge on the strongest concepts.
|
|
22758
23295
|
|
|
22759
|
-
##
|
|
22760
|
-
|
|
22761
|
-
|
|
22762
|
-
|
|
22763
|
-
|
|
22764
|
-
|
|
22765
|
-
|
|
22766
|
-
|
|
22767
|
-
|
|
22768
|
-
|
|
22769
|
-
|
|
23296
|
+
## Methodology
|
|
23297
|
+
1. 5\u20138 distinct ideas (vary technical / UX / product / unconventional).
|
|
23298
|
+
2. Cluster into 2\u20134 themes.
|
|
23299
|
+
3. Score Feasibility + Novelty (1\u20135); mark top 2\u20133.
|
|
23300
|
+
4. One de-risk line per top pick.
|
|
23301
|
+
5. Build on prior agents; do not repeat them.
|
|
23302
|
+
|
|
23303
|
+
## Output
|
|
23304
|
+
## Ideas \xB7 ## Themes \xB7 ## Top picks \u2014 under 200 words.`,
|
|
23305
|
+
designPhaseAddendum: `## Design-phase: persist ideation (mandatory)
|
|
23306
|
+
|
|
23307
|
+
Emit at least 3 \`createDocument\` calls (native tool_call):
|
|
23308
|
+
- title \`customer-journey-map\` \u2014 personas + journey stages + pain points
|
|
23309
|
+
- title \`information-architecture\` \u2014 sitemap + nav + URL patterns
|
|
23310
|
+
- title \`design-tokens\` \u2014 color/type/spacing/motion
|
|
23311
|
+
|
|
23312
|
+
Optional 4th design-system doc if warranted. Do not only summarize in prose.`,
|
|
22770
23313
|
tools: ["list_files", "read_file"],
|
|
22771
|
-
skills: ["document-writer", "
|
|
23314
|
+
skills: ["document-writer", "idea-synthesizer"]
|
|
22772
23315
|
},
|
|
22773
23316
|
{
|
|
22774
23317
|
id: "pluton",
|
|
@@ -22777,31 +23320,20 @@ Pass the tool call as: \`createDocument({ title: "<category>", content: "<markdo
|
|
|
22777
23320
|
role: "Knowledge Architect",
|
|
22778
23321
|
color: "#06b6d4",
|
|
22779
23322
|
avatar: "P",
|
|
22780
|
-
systemPrompt: `You are Plutone, the Knowledge Architect
|
|
22781
|
-
|
|
22782
|
-
## Methodology (work in this order)
|
|
22783
|
-
1. Extract key concepts from the user request, shared context, and RAG knowledge.
|
|
22784
|
-
2. Identify the central root concept and 3-6 primary branches.
|
|
22785
|
-
3. Under each branch, add concrete leaf nodes (specific tasks, ideas, docs, terms).
|
|
22786
|
-
4. Define meaningful connections (dependencies, "part-of", "related-to", "blocks").
|
|
22787
|
-
5. Propose a layout hint (radial / hierarchical) so the map reads cleanly.
|
|
22788
|
-
|
|
22789
|
-
## Operating principles
|
|
22790
|
-
- Favor concrete, named nodes over abstract categories.
|
|
22791
|
-
- Every node should map to something actionable or retrievable (a task, a doc, a concept).
|
|
22792
|
-
- Reuse knowledge from prior agents \u2014 don't re-derive what they already produced.
|
|
22793
|
-
- Keep the graph comprehensible: prune redundant or duplicate nodes.
|
|
23323
|
+
systemPrompt: `You are Plutone, the Knowledge Architect \u2014 structure concepts into a navigable map.
|
|
22794
23324
|
|
|
22795
|
-
##
|
|
22796
|
-
|
|
22797
|
-
|
|
22798
|
-
|
|
22799
|
-
|
|
23325
|
+
## Methodology
|
|
23326
|
+
1. Extract key concepts from the request and prior outputs.
|
|
23327
|
+
2. Central root + 3\u20136 branches + concrete leaves.
|
|
23328
|
+
3. Meaningful links (depends-on, part-of, blocks).
|
|
23329
|
+
4. Prefer named, actionable nodes over abstract categories.
|
|
22800
23330
|
|
|
22801
|
-
|
|
23331
|
+
## Output
|
|
23332
|
+
Root \u2192 branches \u2192 leaves in text. Under 200 words.`,
|
|
23333
|
+
designPhaseAddendum: `## Design-phase: persist knowledge map (mandatory)
|
|
22802
23334
|
|
|
22803
|
-
|
|
22804
|
-
|
|
23335
|
+
One \`createDocument({ title: "knowledge-map", content: "<markdown map>" })\`.
|
|
23336
|
+
Do not rely on buildMindMap \u2014 it is not available in the CLI workspace.`,
|
|
22805
23337
|
tools: ["list_files", "read_file", "grep_content"],
|
|
22806
23338
|
skills: ["document-writer", "research-analyst"]
|
|
22807
23339
|
},
|
|
@@ -22812,40 +23344,25 @@ Do NOT rely on buildMindMap \u2014 it is not available in the CLI workspace.`,
|
|
|
22812
23344
|
role: "Quality Critic",
|
|
22813
23345
|
color: "#ef4444",
|
|
22814
23346
|
avatar: "M",
|
|
22815
|
-
systemPrompt: `You are Minosse, the Quality Critic \u2014
|
|
22816
|
-
|
|
22817
|
-
## Methodology (work in this order)
|
|
22818
|
-
1. Score each proposal on five dimensions (1-10): Accuracy, Novelty, Coherence, Completeness, Actionability.
|
|
22819
|
-
2. Identify the single biggest gap or risk across all proposals.
|
|
22820
|
-
3. Flag any contradictions between proposals (e.g., conflicting assumptions).
|
|
22821
|
-
4. Recommend the 1-3 highest-value improvements the Lucifero should apply.
|
|
22822
|
-
5. Note anything that should be cut or descoped.
|
|
23347
|
+
systemPrompt: `You are Minosse, the Quality Critic \u2014 expose weaknesses before synthesis. Evaluate; never create product code.
|
|
22823
23348
|
|
|
22824
|
-
##
|
|
22825
|
-
|
|
22826
|
-
|
|
22827
|
-
|
|
22828
|
-
|
|
22829
|
-
|
|
22830
|
-
## Scores (per agent, per dimension)
|
|
22831
|
-
## Critical gaps
|
|
22832
|
-
## Contradictions
|
|
22833
|
-
## Top improvements
|
|
23349
|
+
## Methodology
|
|
23350
|
+
1. Score proposals (1\u201310): Accuracy, Novelty, Coherence, Completeness, Actionability.
|
|
23351
|
+
2. Single biggest gap/risk.
|
|
23352
|
+
3. Contradictions between proposals.
|
|
23353
|
+
4. 1\u20133 highest-value improvements for Lucifero.
|
|
23354
|
+
5. What to cut or descope.
|
|
22834
23355
|
|
|
22835
|
-
|
|
23356
|
+
## Principles
|
|
23357
|
+
- Specific defects, not vibes. Pair every critique with a fix or question.
|
|
23358
|
+
- Use read tools to ground claims in the real tree when a codebase exists.
|
|
22836
23359
|
|
|
22837
|
-
##
|
|
22838
|
-
|
|
23360
|
+
## Output
|
|
23361
|
+
## Scores \xB7 ## Critical gaps \xB7 ## Contradictions \xB7 ## Top improvements \u2014 under 200 words.`,
|
|
23362
|
+
designPhaseAddendum: `## Design-phase: risks artifact (mandatory)
|
|
22839
23363
|
|
|
22840
|
-
|
|
22841
|
-
|
|
22842
|
-
title: "risks",
|
|
22843
|
-
content: \`# Risks\\n\\n## 1. <Risk title>\\n- Impact: <high|medium|low>\\n- Likelihood: <high|medium|low>\\n- Mitigation: <one-line mitigation>\\n\\n## 2. ...\`
|
|
22844
|
-
})
|
|
22845
|
-
\`\`\`
|
|
22846
|
-
|
|
22847
|
-
Include AT LEAST 5 risks, each scored on Impact and Likelihood, with a one-line mitigation. Cover: technical (e.g. stack risk), product (e.g. scope creep), accessibility, performance, security. The artifact is persisted at \`.zelari/risks.md\` (workspace root), NOT under docs/. Do NOT emit other workspace artifacts \u2014 your role is to evaluate, not to build.`,
|
|
22848
|
-
tools: [],
|
|
23364
|
+
One \`createDocument\` with title \`risks\`: at least 5 risks, each with Impact, Likelihood, Mitigation (technical, product, a11y, performance, security). Persist at workspace risks doc \u2014 do not build product code.`,
|
|
23365
|
+
tools: ["list_files", "read_file", "grep_content"],
|
|
22849
23366
|
skills: ["document-writer", "research-analyst"]
|
|
22850
23367
|
},
|
|
22851
23368
|
{
|
|
@@ -22855,35 +23372,26 @@ Include AT LEAST 5 risks, each scored on Impact and Likelihood, with a one-line
|
|
|
22855
23372
|
role: "Final Synthesizer",
|
|
22856
23373
|
color: "#8b5cf6",
|
|
22857
23374
|
avatar: "L",
|
|
22858
|
-
systemPrompt: `You are
|
|
22859
|
-
|
|
22860
|
-
## Methodology (work in this order)
|
|
22861
|
-
1. Reconcile the specialists' outputs and Minosse's critique into a single coherent plan.
|
|
22862
|
-
2. Resolve conflicts explicitly (state which proposal won and why).
|
|
22863
|
-
3. Deliver the finished product the user asked for \u2014 complete, not summarized. For code tasks, USE your file/shell tools to create and verify the actual artifacts on disk. Prose without a successful write_file/edit_file is a failed run \u2014 the pipeline forces a retry until files change on disk.
|
|
22864
|
-
4. Run any build/test commands available (check the npm scripts in the workspace context) to confirm your work.
|
|
22865
|
-
|
|
22866
|
-
## Output expectations
|
|
22867
|
-
- If the user requested code or a feature, IMPLEMENT via native tool_call write_file/edit_file/bash \u2014 not prose alone. Prefer native tool_call over ---TOOLS--- JSON; if you use ---TOOLS---, valid JSON with escaped \\n is required. After edits, a delivery loop re-verifies motion/JS budget and forces fix passes until technical blockers clear.
|
|
22868
|
-
- Lead with a one-line summary, then the full detail of what you did.
|
|
22869
|
-
- Apply Minosse's highest-value improvements; drop descoped items.
|
|
22870
|
-
- After making changes, verify they work (compile, run tests, etc.) when feasible.
|
|
22871
|
-
- End implementation runs with a mandatory \`## Verification status\` table with columns \`Check | Tier | Status | Evidence\`. Tier is one of: claimed, grep, tool, build, n/a (claimed < grep < tool < build). Status is PASS/FAIL/N/A. Evidence is \`path:Lline\` or command output. Never write "verificato \u2713" or "nessuna regressione" without Evidence \u2014 a deterministic post-hook will flag dishonest claims and tier inflation.
|
|
22872
|
-
|
|
22873
|
-
Use the tools available to you (read_file, write_file, edit_file, bash, list_files) directly as tool calls \u2014 the harness handles execution.${CLARIFICATION_PROTOCOL8}
|
|
22874
|
-
|
|
22875
|
-
## Design-phase synthesis artifact (mandatory when running council in design-phase mode)
|
|
22876
|
-
When the council is in design-phase mode (TASK mentions design/architecture/spec, no existing codebase to edit), your final deliverable is a \`synthesis\` document via the \`createDocument\` tool (NOT \`write_file\` and NOT \`list_files\`):
|
|
22877
|
-
|
|
22878
|
-
\`\`\`
|
|
22879
|
-
createDocument({
|
|
22880
|
-
title: "synthesis",
|
|
22881
|
-
content: \`# Council Synthesis\\n\\n## Executive summary\\n<2-3 paragraphs>\\n\\n## Stack and key decisions\\n- <ADR-by-ADR summary>\\n\\n## Phases\\n<1-2 line summary per phase>\\n\\n## Top risks\\n<top 3 risks from Minosse>\\n\\n## Green-light checklist\\n- [ ] All ADRs accepted\\n- [ ] Risks have mitigations\\n- [ ] Tasks have acceptance criteria\`
|
|
22882
|
-
})
|
|
22883
|
-
\`\`\`
|
|
23375
|
+
systemPrompt: `You are Lucifero, the Final Synthesizer. For coding tasks you IMPLEMENT: write/edit files, run commands, deliver working code.
|
|
22884
23376
|
|
|
22885
|
-
|
|
22886
|
-
|
|
23377
|
+
## Methodology
|
|
23378
|
+
1. Reconcile specialists + Minosse into one plan.
|
|
23379
|
+
2. Resolve conflicts explicitly.
|
|
23380
|
+
3. Deliver the product on disk \u2014 prose without successful write_file/edit_file is a failed implementation run.
|
|
23381
|
+
4. Verify with project scripts/tests/build when available.
|
|
23382
|
+
|
|
23383
|
+
## Implementation output
|
|
23384
|
+
- Prefer **native tool_call** for write_file / edit_file / bash (legacy ---TOOLS--- only if native unavailable).
|
|
23385
|
+
- Lead with a one-line summary, then what you did.
|
|
23386
|
+
- Apply Minosse's highest-value improvements.
|
|
23387
|
+
- End with \`## Verification status\` table: \`Check | Tier | Status | Evidence\` (tier: claimed < grep < tool < build). Never claim verification without Evidence.`,
|
|
23388
|
+
designPhaseAddendum: `## Design-phase: synthesis document (mandatory)
|
|
23389
|
+
|
|
23390
|
+
Emit \`createDocument({ title: "synthesis", content: "\u2026" })\` with executive summary, stack/decisions, phases, top risks, green-light checklist.
|
|
23391
|
+
Do not use list_files as a workspace deliverable; use searchDocuments sparingly if needed.`,
|
|
23392
|
+
implementationAddendum: `## Implementation focus
|
|
23393
|
+
|
|
23394
|
+
You are the sole implementer this run. Advisors already analyzed \u2014 implement and verify. Prefer minimal diffs that match project style.`,
|
|
22887
23395
|
tools: ["read_file", "write_file", "edit_file", "bash", "list_files"],
|
|
22888
23396
|
skills: ["vault-manager", "project-planner", "idea-synthesizer"]
|
|
22889
23397
|
}
|
|
@@ -23115,8 +23623,8 @@ var init_parseCssMotion = __esm({
|
|
|
23115
23623
|
});
|
|
23116
23624
|
|
|
23117
23625
|
// packages/core/dist/council/verification/citeVerify.js
|
|
23118
|
-
import { existsSync as
|
|
23119
|
-
import { join } from "node:path";
|
|
23626
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "node:fs";
|
|
23627
|
+
import { join as join2 } from "node:path";
|
|
23120
23628
|
function extractCitations(text) {
|
|
23121
23629
|
const out = [];
|
|
23122
23630
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -23138,8 +23646,8 @@ function verifyCitations(projectRoot, synthesisText) {
|
|
|
23138
23646
|
return [];
|
|
23139
23647
|
const results = [];
|
|
23140
23648
|
for (const cite of extractCitations(synthesisText)) {
|
|
23141
|
-
const abs =
|
|
23142
|
-
if (!
|
|
23649
|
+
const abs = join2(projectRoot, cite.file);
|
|
23650
|
+
if (!existsSync10(abs)) {
|
|
23143
23651
|
results.push({
|
|
23144
23652
|
id: "synthesis.cite-invalid",
|
|
23145
23653
|
severity: "error",
|
|
@@ -23152,7 +23660,7 @@ function verifyCitations(projectRoot, synthesisText) {
|
|
|
23152
23660
|
});
|
|
23153
23661
|
continue;
|
|
23154
23662
|
}
|
|
23155
|
-
const lines =
|
|
23663
|
+
const lines = readFileSync8(abs, "utf8").split(/\r?\n/);
|
|
23156
23664
|
if (cite.line > lines.length) {
|
|
23157
23665
|
results.push({
|
|
23158
23666
|
id: "synthesis.cite-invalid",
|
|
@@ -23415,14 +23923,14 @@ var init_synthesisAudit = __esm({
|
|
|
23415
23923
|
});
|
|
23416
23924
|
|
|
23417
23925
|
// packages/core/dist/council/verification/runChecks.js
|
|
23418
|
-
import { existsSync as
|
|
23419
|
-
import { join as
|
|
23926
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "node:fs";
|
|
23927
|
+
import { join as join3 } from "node:path";
|
|
23420
23928
|
function loadNfrSpec(zelariRoot) {
|
|
23421
|
-
const path36 =
|
|
23422
|
-
if (!
|
|
23929
|
+
const path36 = join3(zelariRoot, "nfr-spec.json");
|
|
23930
|
+
if (!existsSync11(path36))
|
|
23423
23931
|
return null;
|
|
23424
23932
|
try {
|
|
23425
|
-
const raw = JSON.parse(
|
|
23933
|
+
const raw = JSON.parse(readFileSync9(path36, "utf8"));
|
|
23426
23934
|
if (raw.version !== 1 || !Array.isArray(raw.targets))
|
|
23427
23935
|
return null;
|
|
23428
23936
|
return raw;
|
|
@@ -23433,11 +23941,11 @@ function loadNfrSpec(zelariRoot) {
|
|
|
23433
23941
|
function resolveTargets(projectRoot, spec) {
|
|
23434
23942
|
const found = [];
|
|
23435
23943
|
for (const rel2 of spec.targets) {
|
|
23436
|
-
if (
|
|
23944
|
+
if (existsSync11(join3(projectRoot, rel2))) {
|
|
23437
23945
|
found.push(rel2);
|
|
23438
23946
|
}
|
|
23439
23947
|
}
|
|
23440
|
-
if (found.length === 0 &&
|
|
23948
|
+
if (found.length === 0 && existsSync11(join3(projectRoot, "index.html"))) {
|
|
23441
23949
|
return ["index.html"];
|
|
23442
23950
|
}
|
|
23443
23951
|
return found;
|
|
@@ -23492,18 +24000,18 @@ function checkDeadCssHooks(html, relFile) {
|
|
|
23492
24000
|
return results;
|
|
23493
24001
|
}
|
|
23494
24002
|
function checkPlanReality(projectRoot, zelariRoot, targets, keywords) {
|
|
23495
|
-
const planPath =
|
|
23496
|
-
if (!
|
|
24003
|
+
const planPath = join3(zelariRoot, "plan.json");
|
|
24004
|
+
if (!existsSync11(planPath) || keywords.length === 0)
|
|
23497
24005
|
return [];
|
|
23498
24006
|
let plan;
|
|
23499
24007
|
try {
|
|
23500
|
-
plan = JSON.parse(
|
|
24008
|
+
plan = JSON.parse(readFileSync9(planPath, "utf8"));
|
|
23501
24009
|
} catch {
|
|
23502
24010
|
return [];
|
|
23503
24011
|
}
|
|
23504
24012
|
const milestoneText = (plan.milestones ?? []).map((m) => `${m.name ?? ""} ${m.description ?? ""}`).join(" ").toLowerCase();
|
|
23505
24013
|
const results = [];
|
|
23506
|
-
const targetContent = targets.map((t) =>
|
|
24014
|
+
const targetContent = targets.map((t) => readFileSync9(join3(projectRoot, t), "utf8").toLowerCase()).join("\n");
|
|
23507
24015
|
for (const kw of keywords) {
|
|
23508
24016
|
const low = kw.toLowerCase();
|
|
23509
24017
|
if (!milestoneText.includes(low))
|
|
@@ -23532,14 +24040,14 @@ function checkPlanReality(projectRoot, zelariRoot, targets, keywords) {
|
|
|
23532
24040
|
return results;
|
|
23533
24041
|
}
|
|
23534
24042
|
function checkReadmeStale(projectRoot, targets) {
|
|
23535
|
-
const readmePath =
|
|
23536
|
-
if (!
|
|
24043
|
+
const readmePath = join3(projectRoot, "README.md");
|
|
24044
|
+
if (!existsSync11(readmePath) || targets.length === 0)
|
|
23537
24045
|
return [];
|
|
23538
|
-
const readme =
|
|
23539
|
-
const htmlPath =
|
|
23540
|
-
if (!
|
|
24046
|
+
const readme = readFileSync9(readmePath, "utf8");
|
|
24047
|
+
const htmlPath = join3(projectRoot, targets[0]);
|
|
24048
|
+
if (!existsSync11(htmlPath))
|
|
23541
24049
|
return [];
|
|
23542
|
-
const html =
|
|
24050
|
+
const html = readFileSync9(htmlPath, "utf8");
|
|
23543
24051
|
const sectionCount = (html.match(/<section\s+id=/gi) ?? []).length;
|
|
23544
24052
|
const readmeSections = readme.match(/(\d+)\s+sezioni/i);
|
|
23545
24053
|
if (readmeSections) {
|
|
@@ -23577,7 +24085,7 @@ function runImplementationVerification(input) {
|
|
|
23577
24085
|
forbidLayoutProps: anim.forbidLayoutProps ?? true
|
|
23578
24086
|
};
|
|
23579
24087
|
for (const rel2 of targets) {
|
|
23580
|
-
const html =
|
|
24088
|
+
const html = readFileSync9(join3(input.projectRoot, rel2), "utf8");
|
|
23581
24089
|
for (const v of scanKeyframesViolations(html, scanOpts)) {
|
|
23582
24090
|
results.push({
|
|
23583
24091
|
id: "motion.keyframes",
|
|
@@ -23633,8 +24141,8 @@ function runImplementationVerification(input) {
|
|
|
23633
24141
|
};
|
|
23634
24142
|
}
|
|
23635
24143
|
function writeVerificationReport(zelariRoot, report) {
|
|
23636
|
-
const outPath =
|
|
23637
|
-
|
|
24144
|
+
const outPath = join3(zelariRoot, "verification-report.json");
|
|
24145
|
+
writeFileSync6(outPath, JSON.stringify(report, null, 2), "utf8");
|
|
23638
24146
|
return outPath;
|
|
23639
24147
|
}
|
|
23640
24148
|
var DEFAULT_NFR_SPEC;
|
|
@@ -23663,8 +24171,8 @@ var init_runChecks = __esm({
|
|
|
23663
24171
|
});
|
|
23664
24172
|
|
|
23665
24173
|
// packages/core/dist/council/verification/microGate.js
|
|
23666
|
-
import { existsSync as
|
|
23667
|
-
import { join as
|
|
24174
|
+
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "node:fs";
|
|
24175
|
+
import { join as join4 } from "node:path";
|
|
23668
24176
|
function checkDeadHooksInHtml(html) {
|
|
23669
24177
|
const warnings = [];
|
|
23670
24178
|
const scripts = [];
|
|
@@ -23692,8 +24200,8 @@ function checkDeadHooksInHtml(html) {
|
|
|
23692
24200
|
return warnings;
|
|
23693
24201
|
}
|
|
23694
24202
|
function runMicroVerificationOnFile(projectRoot, relPath, zelariRoot) {
|
|
23695
|
-
const abs =
|
|
23696
|
-
if (!
|
|
24203
|
+
const abs = join4(projectRoot, relPath);
|
|
24204
|
+
if (!existsSync12(abs) || !/\.html?$/i.test(relPath))
|
|
23697
24205
|
return [];
|
|
23698
24206
|
const spec = (zelariRoot ? loadNfrSpec(zelariRoot) : null) ?? DEFAULT_NFR_SPEC;
|
|
23699
24207
|
const anim = spec.animation ?? { compositorOnly: true, forbidLayoutProps: true };
|
|
@@ -23701,7 +24209,7 @@ function runMicroVerificationOnFile(projectRoot, relPath, zelariRoot) {
|
|
|
23701
24209
|
compositorOnly: anim.compositorOnly ?? true,
|
|
23702
24210
|
forbidLayoutProps: anim.forbidLayoutProps ?? true
|
|
23703
24211
|
};
|
|
23704
|
-
const html =
|
|
24212
|
+
const html = readFileSync10(abs, "utf8");
|
|
23705
24213
|
const warnings = [];
|
|
23706
24214
|
for (const v of scanKeyframesViolations(html, scanOpts)) {
|
|
23707
24215
|
warnings.push({
|
|
@@ -24057,8 +24565,8 @@ var init_implementationDelivery = __esm({
|
|
|
24057
24565
|
});
|
|
24058
24566
|
|
|
24059
24567
|
// packages/core/dist/council/verification/inlineJsAutofix.js
|
|
24060
|
-
import { readFileSync as
|
|
24061
|
-
import { join as
|
|
24568
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "node:fs";
|
|
24569
|
+
import { join as join5 } from "node:path";
|
|
24062
24570
|
function minifyInlineJs(js) {
|
|
24063
24571
|
let out = js.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
24064
24572
|
out = out.split("\n").map((line) => line.replace(/\/\/.*$/, "").trimEnd()).filter((line) => line.trim().length > 0).join("\n");
|
|
@@ -24101,10 +24609,10 @@ function applyInlineJsAutofix(projectRoot, report) {
|
|
|
24101
24609
|
const fixes = [];
|
|
24102
24610
|
for (const r of fails) {
|
|
24103
24611
|
const rel2 = r.file ?? "index.html";
|
|
24104
|
-
const abs =
|
|
24612
|
+
const abs = join5(projectRoot, rel2);
|
|
24105
24613
|
let html;
|
|
24106
24614
|
try {
|
|
24107
|
-
html =
|
|
24615
|
+
html = readFileSync11(abs, "utf8");
|
|
24108
24616
|
} catch {
|
|
24109
24617
|
continue;
|
|
24110
24618
|
}
|
|
@@ -24117,7 +24625,7 @@ function applyInlineJsAutofix(projectRoot, report) {
|
|
|
24117
24625
|
if (!changed || afterBytes > maxBytes)
|
|
24118
24626
|
continue;
|
|
24119
24627
|
const nextHtml = html.replace(SCRIPT_RE, `<script>${after}</script>`);
|
|
24120
|
-
|
|
24628
|
+
writeFileSync7(abs, nextHtml, "utf8");
|
|
24121
24629
|
filesChanged.push(rel2);
|
|
24122
24630
|
fixes.push(`${rel2}: trimmed inline script ${Buffer.byteLength(before, "utf8")}\u2192${afterBytes} bytes`);
|
|
24123
24631
|
}
|
|
@@ -24137,8 +24645,8 @@ var init_inlineJsAutofix = __esm({
|
|
|
24137
24645
|
});
|
|
24138
24646
|
|
|
24139
24647
|
// packages/core/dist/agents/councilApi.js
|
|
24140
|
-
import { existsSync as
|
|
24141
|
-
import { join as
|
|
24648
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
24649
|
+
import { join as join6 } from "node:path";
|
|
24142
24650
|
function parseClarificationRequest(text) {
|
|
24143
24651
|
const start = text.indexOf(QUESTION_MARKER);
|
|
24144
24652
|
if (start < 0)
|
|
@@ -24183,29 +24691,37 @@ function restrictImplementationWrites(toolNames, opts) {
|
|
|
24183
24691
|
return toolNames;
|
|
24184
24692
|
return toolNames.filter((t) => !MUTATING_PROJECT_TOOLS.includes(t));
|
|
24185
24693
|
}
|
|
24186
|
-
function buildAgentMessages(agent, userMessage, ragContext, workspaceContext, priorOutputs, aiConfig, executableTools, runMode = "implementation") {
|
|
24694
|
+
function buildAgentMessages(agent, userMessage, ragContext, workspaceContext, priorOutputs, aiConfig, executableTools, runMode = "implementation", languageModule) {
|
|
24187
24695
|
const allToolNames = computeAgentTools(agent, aiConfig);
|
|
24188
24696
|
const toolNames = executableTools ? allToolNames.filter((n) => executableTools.has(n)) : allToolNames;
|
|
24189
|
-
const
|
|
24697
|
+
const mergedAiConfig = languageModule ? {
|
|
24698
|
+
enabledSkills: aiConfig?.enabledSkills ?? [],
|
|
24699
|
+
enabledTools: aiConfig?.enabledTools ?? [],
|
|
24700
|
+
agentSkillConfigs: aiConfig?.agentSkillConfigs ?? [],
|
|
24701
|
+
customSkills: aiConfig?.customSkills,
|
|
24702
|
+
customPromptModules: [
|
|
24703
|
+
...aiConfig?.customPromptModules ?? [],
|
|
24704
|
+
languageModule
|
|
24705
|
+
]
|
|
24706
|
+
} : aiConfig;
|
|
24707
|
+
const modeAwareAgent = {
|
|
24708
|
+
...agent,
|
|
24709
|
+
systemPrompt: resolveRoleSystemPrompt(agent, runMode)
|
|
24710
|
+
};
|
|
24711
|
+
const enhancedSystemPrompt = buildSystemPrompt(modeAwareAgent, {
|
|
24190
24712
|
tools: getAllTools(),
|
|
24191
24713
|
toolNames,
|
|
24192
|
-
aiConfig,
|
|
24714
|
+
aiConfig: mergedAiConfig,
|
|
24193
24715
|
workspaceContext,
|
|
24194
|
-
ragContext
|
|
24716
|
+
ragContext,
|
|
24717
|
+
mode: "council",
|
|
24718
|
+
includeWorkspaceInPrompt: true
|
|
24195
24719
|
});
|
|
24196
24720
|
const messages = [
|
|
24197
24721
|
{ role: "system", content: enhancedSystemPrompt },
|
|
24198
24722
|
{ role: "system", content: councilModeBanner(runMode, { isImplementer: agent.id === "lucifer" }) },
|
|
24199
24723
|
{ role: "system", content: "IMPORTANT: Before making any tool calls or expensive operations, check if the information already exists in the shared context from previous agents. Avoid redundant work." }
|
|
24200
24724
|
];
|
|
24201
|
-
if (ragContext) {
|
|
24202
|
-
messages.push({ role: "system", content: `Relevant workspace context (from RAG retrieval):
|
|
24203
|
-
${ragContext}` });
|
|
24204
|
-
}
|
|
24205
|
-
if (workspaceContext) {
|
|
24206
|
-
messages.push({ role: "system", content: `Current workspace state:
|
|
24207
|
-
${workspaceContext}` });
|
|
24208
|
-
}
|
|
24209
24725
|
if (priorOutputs.length > 0) {
|
|
24210
24726
|
const summary = priorOutputs.map((o) => `[${o.name} - ${o.role}]: ${o.content}`).join("\n\n");
|
|
24211
24727
|
messages.push({ role: "user", content: `Previous council members have said:
|
|
@@ -24300,7 +24816,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
24300
24816
|
model: effectiveModel,
|
|
24301
24817
|
provider: effectiveProvider,
|
|
24302
24818
|
sessionId,
|
|
24303
|
-
messages: buildAgentMessages(agent, userMessage, config2.ragContext, config2.workspaceContext, agentOutputs, config2.aiConfig, executableNames, runMode),
|
|
24819
|
+
messages: buildAgentMessages(agent, userMessage, config2.ragContext, config2.workspaceContext, agentOutputs, config2.aiConfig, executableNames, runMode, councilLanguageModule),
|
|
24304
24820
|
tools: agentTools,
|
|
24305
24821
|
eventBus: config2.eventBus,
|
|
24306
24822
|
toolRegistry: config2.tools,
|
|
@@ -24443,7 +24959,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
24443
24959
|
model: effectiveModel,
|
|
24444
24960
|
provider: effectiveProvider,
|
|
24445
24961
|
sessionId,
|
|
24446
|
-
messages: buildAgentMessages(oracle, `Review these proposals for: "${userMessage}"`, "", "", anonymized, config2.aiConfig, executableNames, runMode),
|
|
24962
|
+
messages: buildAgentMessages(oracle, `Review these proposals for: "${userMessage}"`, "", "", anonymized, config2.aiConfig, executableNames, runMode, councilLanguageModule),
|
|
24447
24963
|
tools: (() => {
|
|
24448
24964
|
const oracleToolNames = filterExecutable(restrictImplementationWrites(Array.from(/* @__PURE__ */ new Set([
|
|
24449
24965
|
"createDocument",
|
|
@@ -24574,7 +25090,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
24574
25090
|
model: effectiveModel,
|
|
24575
25091
|
provider: effectiveProvider,
|
|
24576
25092
|
sessionId,
|
|
24577
|
-
messages: buildAgentMessages(chairman, userMessage, config2.ragContext, config2.workspaceContext, agentOutputs, config2.aiConfig, executableNames, runMode),
|
|
25093
|
+
messages: buildAgentMessages(chairman, userMessage, config2.ragContext, config2.workspaceContext, agentOutputs, config2.aiConfig, executableNames, runMode, councilLanguageModule),
|
|
24578
25094
|
tools: chairmanTools,
|
|
24579
25095
|
eventBus: config2.eventBus,
|
|
24580
25096
|
toolRegistry: config2.tools,
|
|
@@ -24693,7 +25209,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
24693
25209
|
const zelariRoot = `${chairmanProjectRoot}/.zelari`;
|
|
24694
25210
|
const spec = loadNfrSpec(zelariRoot) ?? DEFAULT_NFR_SPEC;
|
|
24695
25211
|
for (const rel2 of spec.targets) {
|
|
24696
|
-
if (!
|
|
25212
|
+
if (!existsSync13(join6(chairmanProjectRoot, rel2)))
|
|
24697
25213
|
continue;
|
|
24698
25214
|
changedTargetFiles.add(rel2);
|
|
24699
25215
|
for (const w of runChairmanMicroGate({ projectRoot: chairmanProjectRoot, relPath: rel2, zelariRoot })) {
|
|
@@ -24713,7 +25229,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
24713
25229
|
const zelariRootReplay = `${chairmanProjectRoot}/.zelari`;
|
|
24714
25230
|
const specReplay = loadNfrSpec(zelariRootReplay) ?? DEFAULT_NFR_SPEC;
|
|
24715
25231
|
for (const rel2 of specReplay.targets) {
|
|
24716
|
-
if (!
|
|
25232
|
+
if (!existsSync13(join6(chairmanProjectRoot, rel2)))
|
|
24717
25233
|
continue;
|
|
24718
25234
|
changedTargetFiles.add(rel2);
|
|
24719
25235
|
for (const w of runChairmanMicroGate({
|
|
@@ -25159,7 +25675,7 @@ async function* runRetryTurnForMember(args) {
|
|
|
25159
25675
|
description: t.function.description,
|
|
25160
25676
|
parameters: t.function.parameters
|
|
25161
25677
|
}));
|
|
25162
|
-
const baseMessages = buildAgentMessages(args.agent, args.userMessage, args.ragContext, args.workspaceContext, args.priorOutputs, args.aiConfig, args.executableTools, args.runMode ?? "implementation");
|
|
25678
|
+
const baseMessages = buildAgentMessages(args.agent, args.userMessage, args.ragContext, args.workspaceContext, args.priorOutputs, args.aiConfig, args.executableTools, args.runMode ?? "implementation", args.languageModule);
|
|
25163
25679
|
const retryMessages = [
|
|
25164
25680
|
...baseMessages,
|
|
25165
25681
|
{
|
|
@@ -25430,8 +25946,8 @@ var init_types = __esm({
|
|
|
25430
25946
|
});
|
|
25431
25947
|
|
|
25432
25948
|
// packages/core/dist/council/verification/motionAutofix.js
|
|
25433
|
-
import { readFileSync as
|
|
25434
|
-
import { join as
|
|
25949
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "node:fs";
|
|
25950
|
+
import { join as join7 } from "node:path";
|
|
25435
25951
|
function sanitizeTransitionPart(part) {
|
|
25436
25952
|
const tokens = part.trim().split(/\s+/);
|
|
25437
25953
|
if (tokens.length === 0)
|
|
@@ -25506,10 +26022,10 @@ function applyMotionAutofix(projectRoot, report) {
|
|
|
25506
26022
|
forbidLayoutProps: DEFAULT_NFR_SPEC.animation?.forbidLayoutProps ?? true
|
|
25507
26023
|
};
|
|
25508
26024
|
for (const rel2 of targets) {
|
|
25509
|
-
const abs =
|
|
26025
|
+
const abs = join7(projectRoot, rel2);
|
|
25510
26026
|
let html;
|
|
25511
26027
|
try {
|
|
25512
|
-
html =
|
|
26028
|
+
html = readFileSync12(abs, "utf8");
|
|
25513
26029
|
} catch {
|
|
25514
26030
|
continue;
|
|
25515
26031
|
}
|
|
@@ -25524,7 +26040,7 @@ function applyMotionAutofix(projectRoot, report) {
|
|
|
25524
26040
|
const afterK = scanKeyframesViolations(final, scanOpts).length;
|
|
25525
26041
|
const afterT = scanTransitionViolations(final, scanOpts).length;
|
|
25526
26042
|
if (final !== html || beforeK > afterK || beforeT > afterT) {
|
|
25527
|
-
|
|
26043
|
+
writeFileSync8(abs, final, "utf8");
|
|
25528
26044
|
filesChanged.push(rel2);
|
|
25529
26045
|
if (beforeK > afterK)
|
|
25530
26046
|
fixes.push(`${rel2}: sanitized ${beforeK - afterK} keyframe violation(s)`);
|
|
@@ -25535,7 +26051,7 @@ function applyMotionAutofix(projectRoot, report) {
|
|
|
25535
26051
|
if (!fixes.some((f) => f.startsWith(rel2)))
|
|
25536
26052
|
fixes.push(`${rel2}: motion CSS sanitized`);
|
|
25537
26053
|
} else if (motionChanged) {
|
|
25538
|
-
|
|
26054
|
+
writeFileSync8(abs, final, "utf8");
|
|
25539
26055
|
filesChanged.push(rel2);
|
|
25540
26056
|
fixes.push(`${rel2}: motion CSS sanitized`);
|
|
25541
26057
|
}
|
|
@@ -25581,8 +26097,8 @@ var init_motionAutofix = __esm({
|
|
|
25581
26097
|
});
|
|
25582
26098
|
|
|
25583
26099
|
// packages/core/dist/council/verification/autofix.js
|
|
25584
|
-
import { readFileSync as
|
|
25585
|
-
import { join as
|
|
26100
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "node:fs";
|
|
26101
|
+
import { join as join8 } from "node:path";
|
|
25586
26102
|
function applyDeterministicAutofix(projectRoot, report) {
|
|
25587
26103
|
const motion = applyMotionAutofix(projectRoot, report);
|
|
25588
26104
|
const inlineJs = applyInlineJsAutofix(projectRoot, report);
|
|
@@ -25596,8 +26112,8 @@ function applyDeterministicAutofix(projectRoot, report) {
|
|
|
25596
26112
|
const m = r.evidence?.match(/classList\.add\(\s*['"]([\w-]+)['"]\s*\)/);
|
|
25597
26113
|
if (!m || m[1] === "rm")
|
|
25598
26114
|
continue;
|
|
25599
|
-
const abs =
|
|
25600
|
-
let html =
|
|
26115
|
+
const abs = join8(projectRoot, rel2);
|
|
26116
|
+
let html = readFileSync13(abs, "utf8");
|
|
25601
26117
|
const snippet = m[0];
|
|
25602
26118
|
if (!html.includes(snippet))
|
|
25603
26119
|
continue;
|
|
@@ -25605,7 +26121,7 @@ function applyDeterministicAutofix(projectRoot, report) {
|
|
|
25605
26121
|
const filtered = lines.filter((line) => !line.includes(snippet));
|
|
25606
26122
|
if (filtered.length === lines.length)
|
|
25607
26123
|
continue;
|
|
25608
|
-
|
|
26124
|
+
writeFileSync9(abs, filtered.join("\n"), "utf8");
|
|
25609
26125
|
filesChanged.push(rel2);
|
|
25610
26126
|
fixes.push(`removed dead hook line in ${rel2}: ${snippet}`);
|
|
25611
26127
|
}
|
|
@@ -25652,12 +26168,12 @@ var init_types2 = __esm({
|
|
|
25652
26168
|
});
|
|
25653
26169
|
|
|
25654
26170
|
// packages/core/dist/council/lessons/io.js
|
|
25655
|
-
import { readFileSync as
|
|
25656
|
-
import { join as
|
|
26171
|
+
import { readFileSync as readFileSync14 } from "node:fs";
|
|
26172
|
+
import { join as join9 } from "node:path";
|
|
25657
26173
|
function readLessonsDeduped(zelariRoot) {
|
|
25658
|
-
const path36 =
|
|
26174
|
+
const path36 = join9(zelariRoot, LESSONS_FILE);
|
|
25659
26175
|
try {
|
|
25660
|
-
const raw =
|
|
26176
|
+
const raw = readFileSync14(path36, "utf8");
|
|
25661
26177
|
const byId = /* @__PURE__ */ new Map();
|
|
25662
26178
|
for (const line of raw.split(/\r?\n/)) {
|
|
25663
26179
|
if (!line.trim())
|
|
@@ -25748,7 +26264,7 @@ var init_signatures = __esm({
|
|
|
25748
26264
|
|
|
25749
26265
|
// packages/core/dist/council/lessons/recordFailure.js
|
|
25750
26266
|
import { appendFileSync as appendFileSync2 } from "node:fs";
|
|
25751
|
-
import { join as
|
|
26267
|
+
import { join as join10 } from "node:path";
|
|
25752
26268
|
function methodologyFor(check2) {
|
|
25753
26269
|
return METHODOLOGY[check2.id] ?? `When ${check2.id} fails, fix the underlying issue and cite grep/tool evidence before claiming PASS.`;
|
|
25754
26270
|
}
|
|
@@ -25758,7 +26274,7 @@ function keywordsFrom(check2, signature) {
|
|
|
25758
26274
|
return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
|
|
25759
26275
|
}
|
|
25760
26276
|
function writeLesson(zelariRoot, lesson) {
|
|
25761
|
-
const path36 =
|
|
26277
|
+
const path36 = join10(zelariRoot, LESSONS_FILE);
|
|
25762
26278
|
appendFileSync2(path36, `${JSON.stringify(lesson)}
|
|
25763
26279
|
`, "utf8");
|
|
25764
26280
|
}
|
|
@@ -25915,8 +26431,8 @@ var init_types3 = __esm({
|
|
|
25915
26431
|
});
|
|
25916
26432
|
|
|
25917
26433
|
// packages/core/dist/council/completion/buildCompletion.js
|
|
25918
|
-
import { writeFileSync as
|
|
25919
|
-
import { join as
|
|
26434
|
+
import { writeFileSync as writeFileSync10 } from "node:fs";
|
|
26435
|
+
import { join as join11 } from "node:path";
|
|
25920
26436
|
function openFailsFromReport(report) {
|
|
25921
26437
|
if (!report)
|
|
25922
26438
|
return [];
|
|
@@ -25975,8 +26491,8 @@ function buildCouncilCompletion(input) {
|
|
|
25975
26491
|
};
|
|
25976
26492
|
}
|
|
25977
26493
|
function writeCouncilCompletion(zelariRoot, completion) {
|
|
25978
|
-
const outPath =
|
|
25979
|
-
|
|
26494
|
+
const outPath = join11(zelariRoot, "completion.json");
|
|
26495
|
+
writeFileSync10(outPath, JSON.stringify(completion, null, 2), "utf8");
|
|
25980
26496
|
return outPath;
|
|
25981
26497
|
}
|
|
25982
26498
|
var init_buildCompletion = __esm({
|
|
@@ -26165,6 +26681,8 @@ var init_promoteMember = __esm({
|
|
|
26165
26681
|
var council_exports = {};
|
|
26166
26682
|
__export(council_exports, {
|
|
26167
26683
|
AGENT_ROLES: () => AGENT_ROLES,
|
|
26684
|
+
CLARIFICATION_PROTOCOL_MODULE: () => CLARIFICATION_PROTOCOL_MODULE,
|
|
26685
|
+
CODING_PRACTICES_MODULE: () => CODING_PRACTICES_MODULE,
|
|
26168
26686
|
COLLABORATION_DIRECTIVE: () => COLLABORATION_DIRECTIVE,
|
|
26169
26687
|
COMPOSITOR_ONLY_PROPS: () => COMPOSITOR_ONLY_PROPS,
|
|
26170
26688
|
DEFAULT_NFR_SPEC: () => DEFAULT_NFR_SPEC,
|
|
@@ -26181,6 +26699,7 @@ __export(council_exports, {
|
|
|
26181
26699
|
MAX_DELIVERY_ATTEMPTS: () => MAX_DELIVERY_ATTEMPTS,
|
|
26182
26700
|
MAX_RETRY_PER_MEMBER: () => MAX_RETRY_PER_MEMBER,
|
|
26183
26701
|
MUTATING_PROJECT_TOOLS: () => MUTATING_PROJECT_TOOLS,
|
|
26702
|
+
NATIVE_TOOL_PROTOCOL_MODULE: () => NATIVE_TOOL_PROTOCOL_MODULE,
|
|
26184
26703
|
NFR_KEYWORDS: () => NFR_KEYWORDS,
|
|
26185
26704
|
NON_RETRY_AGENTS: () => NON_RETRY_AGENTS,
|
|
26186
26705
|
OUTPUT_QUALITY_DIRECTIVE: () => OUTPUT_QUALITY_DIRECTIVE,
|
|
@@ -26250,6 +26769,7 @@ __export(council_exports, {
|
|
|
26250
26769
|
renderSkillMarkdown: () => renderSkillMarkdown,
|
|
26251
26770
|
replayChairmanTextTools: () => replayChairmanTextTools,
|
|
26252
26771
|
resolveCouncilRunMode: () => resolveCouncilRunMode,
|
|
26772
|
+
resolveRoleSystemPrompt: () => resolveRoleSystemPrompt,
|
|
26253
26773
|
resolveVerifyRetryTool: () => resolveVerifyRetryTool,
|
|
26254
26774
|
restrictImplementationWrites: () => restrictImplementationWrites,
|
|
26255
26775
|
runChairmanDeliveryLoop: () => runChairmanDeliveryLoop,
|
|
@@ -26385,9 +26905,11 @@ var conversationContext_exports = {};
|
|
|
26385
26905
|
__export(conversationContext_exports, {
|
|
26386
26906
|
_resetConversationContextForTests: () => _resetConversationContextForTests,
|
|
26387
26907
|
appendMessages: () => appendMessages,
|
|
26908
|
+
buildCouncilTaskWithHistory: () => buildCouncilTaskWithHistory,
|
|
26388
26909
|
clearHistory: () => clearHistory,
|
|
26389
26910
|
compactInPlace: () => compactInPlace,
|
|
26390
26911
|
formatHistoryForCouncil: () => formatHistoryForCouncil,
|
|
26912
|
+
formatHistoryMessages: () => formatHistoryMessages,
|
|
26391
26913
|
getHistory: () => getHistory,
|
|
26392
26914
|
getLastClarification: () => getLastClarification,
|
|
26393
26915
|
hydrateHistory: () => hydrateHistory,
|
|
@@ -26448,23 +26970,56 @@ User's answer: ${picked}
|
|
|
26448
26970
|
Proceed using this answer; do not re-ask the same question unless the answer is still ambiguous.`;
|
|
26449
26971
|
}
|
|
26450
26972
|
function formatHistoryForCouncil(maxTurns = 4) {
|
|
26451
|
-
|
|
26452
|
-
|
|
26973
|
+
return formatHistoryMessages(history, maxTurns);
|
|
26974
|
+
}
|
|
26975
|
+
function formatHistoryMessages(messages, maxTurns = 6, maxTotalChars = 12e3) {
|
|
26976
|
+
if (messages.length === 0) return "";
|
|
26453
26977
|
let turns = 0;
|
|
26454
26978
|
const chunk = [];
|
|
26455
|
-
for (let i =
|
|
26456
|
-
const m =
|
|
26979
|
+
for (let i = messages.length - 1; i >= 0 && turns < maxTurns; i--) {
|
|
26980
|
+
const m = messages[i];
|
|
26457
26981
|
if (m.role === "user") {
|
|
26458
|
-
chunk.push(`User: ${truncate(m.content,
|
|
26982
|
+
chunk.push(`User: ${truncate(m.content, 800)}`);
|
|
26459
26983
|
turns += 1;
|
|
26460
26984
|
} else if (m.role === "assistant" && m.content.trim()) {
|
|
26461
|
-
chunk.push(`Assistant: ${truncate(m.content,
|
|
26985
|
+
chunk.push(`Assistant: ${truncate(m.content, 2e3)}`);
|
|
26462
26986
|
}
|
|
26463
26987
|
}
|
|
26464
26988
|
if (chunk.length === 0) return "";
|
|
26465
|
-
|
|
26466
|
-
|
|
26467
|
-
|
|
26989
|
+
let body = ["## Prior conversation (rolling context)", ...chunk.reverse()].join(
|
|
26990
|
+
"\n"
|
|
26991
|
+
);
|
|
26992
|
+
if (body.length > maxTotalChars) {
|
|
26993
|
+
body = `\u2026
|
|
26994
|
+
${body.slice(body.length - maxTotalChars)}`;
|
|
26995
|
+
}
|
|
26996
|
+
return body;
|
|
26997
|
+
}
|
|
26998
|
+
function buildCouncilTaskWithHistory(task, prior) {
|
|
26999
|
+
const messages = prior ?? [];
|
|
27000
|
+
const trimmed = task.trim();
|
|
27001
|
+
let userPart = maybeAnchorShortAnswer(task) ?? task;
|
|
27002
|
+
if (messages.length > 0 && (SHORT_CONTINUE.test(trimmed) || trimmed.length <= 40 && !trimmed.includes("\n"))) {
|
|
27003
|
+
const lastAsst = [...messages].reverse().find((m) => m.role === "assistant" && m.content.trim());
|
|
27004
|
+
if (lastAsst && SHORT_CONTINUE.test(trimmed)) {
|
|
27005
|
+
userPart = `The user says "${trimmed}" \u2014 continue from the prior conversation. Do NOT restart from zero, do NOT re-ask for the overall goal, and do NOT ignore the prior plan/risks/decisions.
|
|
27006
|
+
|
|
27007
|
+
## Prior assistant output (authoritative context)
|
|
27008
|
+
${truncate(lastAsst.content, 4500)}
|
|
27009
|
+
|
|
27010
|
+
## Instruction
|
|
27011
|
+
Proceed with the next concrete steps implied by that context (implementation when the work phase is build; otherwise the next planned actions).`;
|
|
27012
|
+
}
|
|
27013
|
+
}
|
|
27014
|
+
const block = formatHistoryMessages(messages, 6, 12e3);
|
|
27015
|
+
if (!block) return userPart;
|
|
27016
|
+
if (userPart.includes("Prior assistant output")) {
|
|
27017
|
+
return userPart;
|
|
27018
|
+
}
|
|
27019
|
+
return `${block}
|
|
27020
|
+
|
|
27021
|
+
## Current user request
|
|
27022
|
+
${userPart}`;
|
|
26468
27023
|
}
|
|
26469
27024
|
function truncate(s, max) {
|
|
26470
27025
|
const t = s.replace(/\s+/g, " ").trim();
|
|
@@ -26475,13 +27030,14 @@ function _resetConversationContextForTests() {
|
|
|
26475
27030
|
history = [];
|
|
26476
27031
|
lastClarification = null;
|
|
26477
27032
|
}
|
|
26478
|
-
var history, lastClarification;
|
|
27033
|
+
var history, lastClarification, SHORT_CONTINUE;
|
|
26479
27034
|
var init_conversationContext = __esm({
|
|
26480
27035
|
"src/cli/hooks/conversationContext.ts"() {
|
|
26481
27036
|
"use strict";
|
|
26482
27037
|
init_historyCompaction();
|
|
26483
27038
|
history = [];
|
|
26484
27039
|
lastClarification = null;
|
|
27040
|
+
SHORT_CONTINUE = /^(procedi|continua|continue|go\s*ahead|go|ok|okay|sì|si|yes|vai|avanti|next|proceed)$/i;
|
|
26485
27041
|
}
|
|
26486
27042
|
});
|
|
26487
27043
|
|
|
@@ -26562,20 +27118,20 @@ var init_phase = __esm({
|
|
|
26562
27118
|
|
|
26563
27119
|
// src/cli/workspace/paths.ts
|
|
26564
27120
|
import {
|
|
26565
|
-
mkdirSync as
|
|
26566
|
-
writeFileSync as
|
|
26567
|
-
existsSync as
|
|
27121
|
+
mkdirSync as mkdirSync7,
|
|
27122
|
+
writeFileSync as writeFileSync11,
|
|
27123
|
+
existsSync as existsSync14,
|
|
26568
27124
|
accessSync,
|
|
26569
27125
|
constants,
|
|
26570
27126
|
realpathSync
|
|
26571
27127
|
} from "node:fs";
|
|
26572
|
-
import { join as
|
|
26573
|
-
import { homedir as
|
|
27128
|
+
import { join as join12, basename } from "node:path";
|
|
27129
|
+
import { homedir as homedir5 } from "node:os";
|
|
26574
27130
|
import { createHash as createHash2 } from "node:crypto";
|
|
26575
27131
|
function resolveWorkspaceRoot(projectRoot = process.cwd()) {
|
|
26576
27132
|
const candidates = [
|
|
26577
|
-
|
|
26578
|
-
|
|
27133
|
+
join12(projectRoot, ".zelari"),
|
|
27134
|
+
join12(homedir5(), ".zelari-code", "workspace", hashProject(projectRoot))
|
|
26579
27135
|
];
|
|
26580
27136
|
for (const candidate of candidates) {
|
|
26581
27137
|
if (isWritableDir(projectRoot) || candidate !== candidates[0]) {
|
|
@@ -26591,7 +27147,7 @@ function hashProject(projectPath) {
|
|
|
26591
27147
|
}
|
|
26592
27148
|
function isWritableDir(dir) {
|
|
26593
27149
|
try {
|
|
26594
|
-
if (!
|
|
27150
|
+
if (!existsSync14(dir)) return false;
|
|
26595
27151
|
accessSync(dir, constants.W_OK);
|
|
26596
27152
|
return true;
|
|
26597
27153
|
} catch {
|
|
@@ -26599,26 +27155,26 @@ function isWritableDir(dir) {
|
|
|
26599
27155
|
}
|
|
26600
27156
|
}
|
|
26601
27157
|
function ensureWorkspaceDir(workspaceDir) {
|
|
26602
|
-
|
|
26603
|
-
if (workspaceDir.endsWith("/.zelari") &&
|
|
26604
|
-
const gitignorePath =
|
|
26605
|
-
if (!
|
|
26606
|
-
|
|
27158
|
+
mkdirSync7(workspaceDir, { recursive: true });
|
|
27159
|
+
if (workspaceDir.endsWith("/.zelari") && existsSync14(join12(workspaceDir, "..", ".git"))) {
|
|
27160
|
+
const gitignorePath = join12(workspaceDir, ".gitignore");
|
|
27161
|
+
if (!existsSync14(gitignorePath)) {
|
|
27162
|
+
writeFileSync11(gitignorePath, "*\n!.gitignore\n");
|
|
26607
27163
|
}
|
|
26608
27164
|
}
|
|
26609
27165
|
}
|
|
26610
27166
|
function workspaceFile(rootDir, kind) {
|
|
26611
27167
|
switch (kind) {
|
|
26612
27168
|
case "plan":
|
|
26613
|
-
return
|
|
27169
|
+
return join12(rootDir, "plan.md");
|
|
26614
27170
|
case "risks":
|
|
26615
|
-
return
|
|
27171
|
+
return join12(rootDir, "risks.md");
|
|
26616
27172
|
case "index":
|
|
26617
|
-
return
|
|
27173
|
+
return join12(rootDir, "workspace.json");
|
|
26618
27174
|
}
|
|
26619
27175
|
}
|
|
26620
27176
|
function workspaceArtifact(rootDir, subdir, slug) {
|
|
26621
|
-
return
|
|
27177
|
+
return join12(rootDir, subdir, `${slug}.md`);
|
|
26622
27178
|
}
|
|
26623
27179
|
function projectName(projectRoot = process.cwd()) {
|
|
26624
27180
|
return basename(realpathSync(projectRoot));
|
|
@@ -26637,8 +27193,8 @@ __export(workspaceSummary_exports, {
|
|
|
26637
27193
|
buildWorkspaceSummary: () => buildWorkspaceSummary,
|
|
26638
27194
|
buildZelariReadHint: () => buildZelariReadHint
|
|
26639
27195
|
});
|
|
26640
|
-
import { existsSync as
|
|
26641
|
-
import { join as
|
|
27196
|
+
import { existsSync as existsSync15, readFileSync as readFileSync15, readdirSync, statSync as statSync3 } from "node:fs";
|
|
27197
|
+
import { join as join13, relative } from "node:path";
|
|
26642
27198
|
function buildWorkspaceSummary(projectRoot = process.cwd(), options = {}) {
|
|
26643
27199
|
const { maxEntries = 30 } = options;
|
|
26644
27200
|
const name = safeProjectName(projectRoot);
|
|
@@ -26665,11 +27221,11 @@ function formatTaskLine(t) {
|
|
|
26665
27221
|
}
|
|
26666
27222
|
function buildPlanSummary(projectRoot = process.cwd(), options) {
|
|
26667
27223
|
const zelariRoot = resolveWorkspaceRoot(projectRoot);
|
|
26668
|
-
const planPath =
|
|
26669
|
-
if (!
|
|
27224
|
+
const planPath = join13(zelariRoot, "plan.json");
|
|
27225
|
+
if (!existsSync15(planPath)) return null;
|
|
26670
27226
|
let plan;
|
|
26671
27227
|
try {
|
|
26672
|
-
plan = JSON.parse(
|
|
27228
|
+
plan = JSON.parse(readFileSync15(planPath, "utf8"));
|
|
26673
27229
|
} catch {
|
|
26674
27230
|
return null;
|
|
26675
27231
|
}
|
|
@@ -26798,8 +27354,8 @@ function pickNextTask(open) {
|
|
|
26798
27354
|
)[0];
|
|
26799
27355
|
}
|
|
26800
27356
|
function buildZelariReadHint(projectRoot = process.cwd()) {
|
|
26801
|
-
const planPath =
|
|
26802
|
-
if (!
|
|
27357
|
+
const planPath = join13(resolveWorkspaceRoot(projectRoot), "plan.json");
|
|
27358
|
+
if (!existsSync15(planPath)) return "";
|
|
26803
27359
|
return [
|
|
26804
27360
|
"# Council workspace detected (.zelari/)",
|
|
26805
27361
|
"This project has a council workspace: .zelari/plan.json holds the plan (phases, tasks, milestones) and .zelari/plan-tasks/ holds one detail file per task.",
|
|
@@ -26814,10 +27370,10 @@ function safeProjectName(root) {
|
|
|
26814
27370
|
}
|
|
26815
27371
|
}
|
|
26816
27372
|
function readPackageJson(projectRoot) {
|
|
26817
|
-
const p3 =
|
|
26818
|
-
if (!
|
|
27373
|
+
const p3 = join13(projectRoot, "package.json");
|
|
27374
|
+
if (!existsSync15(p3)) return null;
|
|
26819
27375
|
try {
|
|
26820
|
-
return JSON.parse(
|
|
27376
|
+
return JSON.parse(readFileSync15(p3, "utf8"));
|
|
26821
27377
|
} catch {
|
|
26822
27378
|
return null;
|
|
26823
27379
|
}
|
|
@@ -26851,11 +27407,11 @@ function listShallow(projectRoot, maxEntries) {
|
|
|
26851
27407
|
out.push(`\u2026 (+${top.length - count} more)`);
|
|
26852
27408
|
break;
|
|
26853
27409
|
}
|
|
26854
|
-
const rel2 = relative(projectRoot,
|
|
27410
|
+
const rel2 = relative(projectRoot, join13(projectRoot, entry.name));
|
|
26855
27411
|
if (entry.isDirectory()) {
|
|
26856
27412
|
let inner = "";
|
|
26857
27413
|
try {
|
|
26858
|
-
const sub = readdirSync(
|
|
27414
|
+
const sub = readdirSync(join13(projectRoot, entry.name), {
|
|
26859
27415
|
withFileTypes: true
|
|
26860
27416
|
}).filter((e) => !e.name.startsWith(".")).slice(0, 4).map((e) => e.name);
|
|
26861
27417
|
if (sub.length > 0)
|
|
@@ -26906,14 +27462,14 @@ __export(storage_exports, {
|
|
|
26906
27462
|
workspaceMutex: () => workspaceMutex
|
|
26907
27463
|
});
|
|
26908
27464
|
import {
|
|
26909
|
-
readFileSync as
|
|
26910
|
-
writeFileSync as
|
|
26911
|
-
existsSync as
|
|
26912
|
-
mkdirSync as
|
|
27465
|
+
readFileSync as readFileSync16,
|
|
27466
|
+
writeFileSync as writeFileSync12,
|
|
27467
|
+
existsSync as existsSync16,
|
|
27468
|
+
mkdirSync as mkdirSync8,
|
|
26913
27469
|
readdirSync as readdirSync2,
|
|
26914
27470
|
renameSync as renameSync2
|
|
26915
27471
|
} from "node:fs";
|
|
26916
|
-
import { dirname as
|
|
27472
|
+
import { dirname as dirname3, join as join14 } from "node:path";
|
|
26917
27473
|
function parseFrontmatter(md) {
|
|
26918
27474
|
const m = FRONTMATTER_RE.exec(md);
|
|
26919
27475
|
if (!m) return { meta: {}, body: md };
|
|
@@ -27168,15 +27724,15 @@ var init_storage = __esm({
|
|
|
27168
27724
|
Storage = class {
|
|
27169
27725
|
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
27170
27726
|
read(path36) {
|
|
27171
|
-
if (!
|
|
27727
|
+
if (!existsSync16(path36)) {
|
|
27172
27728
|
throw new Error(`File not found: ${path36}`);
|
|
27173
27729
|
}
|
|
27174
|
-
const md =
|
|
27730
|
+
const md = readFileSync16(path36, "utf8");
|
|
27175
27731
|
return parseFrontmatter(md);
|
|
27176
27732
|
}
|
|
27177
27733
|
/** Read a Markdown file; returns null if not found. */
|
|
27178
27734
|
readIfExists(path36) {
|
|
27179
|
-
if (!
|
|
27735
|
+
if (!existsSync16(path36)) return null;
|
|
27180
27736
|
return this.read(path36);
|
|
27181
27737
|
}
|
|
27182
27738
|
/**
|
|
@@ -27184,16 +27740,16 @@ var init_storage = __esm({
|
|
|
27184
27740
|
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
27185
27741
|
*/
|
|
27186
27742
|
write(path36, meta3, body) {
|
|
27187
|
-
|
|
27743
|
+
mkdirSync8(dirname3(path36), { recursive: true });
|
|
27188
27744
|
const tmp = path36 + ".tmp-" + process.pid;
|
|
27189
27745
|
const md = serializeFrontmatter(meta3, body);
|
|
27190
|
-
|
|
27746
|
+
writeFileSync12(tmp, md, "utf8");
|
|
27191
27747
|
renameSync2(tmp, path36);
|
|
27192
27748
|
}
|
|
27193
27749
|
/** List all .md files in a directory (non-recursive). */
|
|
27194
27750
|
listMarkdown(dir) {
|
|
27195
|
-
if (!
|
|
27196
|
-
return readdirSync2(dir).filter((f) => f.endsWith(".md") && !f.startsWith(".")).map((f) =>
|
|
27751
|
+
if (!existsSync16(dir)) return [];
|
|
27752
|
+
return readdirSync2(dir).filter((f) => f.endsWith(".md") && !f.startsWith(".")).map((f) => join14(dir, f));
|
|
27197
27753
|
}
|
|
27198
27754
|
};
|
|
27199
27755
|
KeyedMutex = class {
|
|
@@ -27232,14 +27788,14 @@ __export(stubs_exports, {
|
|
|
27232
27788
|
resolveWorkspaceRoot: () => resolveWorkspaceRoot
|
|
27233
27789
|
});
|
|
27234
27790
|
import {
|
|
27235
|
-
existsSync as
|
|
27791
|
+
existsSync as existsSync17,
|
|
27236
27792
|
readdirSync as readdirSync3,
|
|
27237
|
-
writeFileSync as
|
|
27238
|
-
readFileSync as
|
|
27239
|
-
mkdirSync as
|
|
27793
|
+
writeFileSync as writeFileSync13,
|
|
27794
|
+
readFileSync as readFileSync17,
|
|
27795
|
+
mkdirSync as mkdirSync9,
|
|
27240
27796
|
renameSync as renameSync3
|
|
27241
27797
|
} from "node:fs";
|
|
27242
|
-
import { join as
|
|
27798
|
+
import { join as join15, basename as basename2, dirname as dirname4, relative as relative2 } from "node:path";
|
|
27243
27799
|
function createWorkspaceContext(projectRoot = process.cwd()) {
|
|
27244
27800
|
const rootDir = resolveWorkspaceRoot(projectRoot);
|
|
27245
27801
|
return {
|
|
@@ -27249,14 +27805,14 @@ function createWorkspaceContext(projectRoot = process.cwd()) {
|
|
|
27249
27805
|
};
|
|
27250
27806
|
}
|
|
27251
27807
|
function planJsonPath(ctx) {
|
|
27252
|
-
return
|
|
27808
|
+
return join15(ctx.rootDir, "plan.json");
|
|
27253
27809
|
}
|
|
27254
27810
|
function readPlan(ctx) {
|
|
27255
27811
|
const jsonPath = planJsonPath(ctx);
|
|
27256
|
-
if (
|
|
27812
|
+
if (existsSync17(jsonPath)) {
|
|
27257
27813
|
try {
|
|
27258
27814
|
const parsed = JSON.parse(
|
|
27259
|
-
|
|
27815
|
+
readFileSync17(jsonPath, "utf8")
|
|
27260
27816
|
);
|
|
27261
27817
|
return {
|
|
27262
27818
|
phases: Array.isArray(parsed.phases) ? parsed.phases : [],
|
|
@@ -27278,9 +27834,9 @@ function readPlan(ctx) {
|
|
|
27278
27834
|
}
|
|
27279
27835
|
function writePlan(ctx, summary) {
|
|
27280
27836
|
const jsonPath = planJsonPath(ctx);
|
|
27281
|
-
|
|
27837
|
+
mkdirSync9(dirname4(jsonPath), { recursive: true });
|
|
27282
27838
|
const tmp = jsonPath + ".tmp-" + process.pid;
|
|
27283
|
-
|
|
27839
|
+
writeFileSync13(tmp, JSON.stringify(summary, null, 2), "utf8");
|
|
27284
27840
|
renameSync3(tmp, jsonPath);
|
|
27285
27841
|
const mdPath = workspaceFile(ctx.rootDir, "plan");
|
|
27286
27842
|
const summaryMeta = {
|
|
@@ -27347,8 +27903,8 @@ function renderPlanBody(summary) {
|
|
|
27347
27903
|
return lines.join("\n");
|
|
27348
27904
|
}
|
|
27349
27905
|
function nextAdrId(ctx) {
|
|
27350
|
-
const decisionsDir =
|
|
27351
|
-
if (!
|
|
27906
|
+
const decisionsDir = join15(ctx.rootDir, "decisions");
|
|
27907
|
+
if (!existsSync17(decisionsDir)) return "001";
|
|
27352
27908
|
const existing = readdirSync3(decisionsDir).filter((f) => f.endsWith(".md")).map((f) => f.match(/^(\d+)-/)).filter((m) => !!m).map((m) => parseInt(m[1], 10));
|
|
27353
27909
|
const max = existing.length === 0 ? 0 : Math.max(...existing);
|
|
27354
27910
|
return String(max + 1).padStart(3, "0");
|
|
@@ -27390,7 +27946,7 @@ function addTaskRecord(ctx, summary, phaseId, t, options) {
|
|
|
27390
27946
|
status: "pending",
|
|
27391
27947
|
priority: t.priority
|
|
27392
27948
|
});
|
|
27393
|
-
const taskPath =
|
|
27949
|
+
const taskPath = join15(ctx.rootDir, "plan-tasks", `${id}.md`);
|
|
27394
27950
|
const meta3 = {
|
|
27395
27951
|
kind: "task",
|
|
27396
27952
|
id,
|
|
@@ -27432,7 +27988,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
27432
27988
|
dueDate: input.dueDate,
|
|
27433
27989
|
targetVersion: version2
|
|
27434
27990
|
});
|
|
27435
|
-
const path36 =
|
|
27991
|
+
const path36 = join15(ctx.rootDir, "milestones", `${id}.md`);
|
|
27436
27992
|
const meta3 = {
|
|
27437
27993
|
kind: "milestone",
|
|
27438
27994
|
id,
|
|
@@ -27730,8 +28286,8 @@ function createNfrSpecStub(ctx) {
|
|
|
27730
28286
|
},
|
|
27731
28287
|
planFeatureKeywords: Array.isArray(args["planFeatureKeywords"]) ? args["planFeatureKeywords"] : void 0
|
|
27732
28288
|
};
|
|
27733
|
-
const outPath =
|
|
27734
|
-
|
|
28289
|
+
const outPath = join15(ctx.rootDir, "nfr-spec.json");
|
|
28290
|
+
writeFileSync13(outPath, JSON.stringify(spec, null, 2), "utf8");
|
|
27735
28291
|
return `NFR spec written to nfr-spec.json (${targets.length} target(s)).`;
|
|
27736
28292
|
});
|
|
27737
28293
|
}
|
|
@@ -27794,18 +28350,18 @@ function searchDocumentsStub(ctx) {
|
|
|
27794
28350
|
(w) => w.length >= 3 && w !== "or" && w !== "and" && w !== "the"
|
|
27795
28351
|
);
|
|
27796
28352
|
const files = [
|
|
27797
|
-
...ctx.storage.listMarkdown(
|
|
27798
|
-
...ctx.storage.listMarkdown(
|
|
27799
|
-
...ctx.storage.listMarkdown(
|
|
27800
|
-
...ctx.storage.listMarkdown(
|
|
27801
|
-
...ctx.storage.listMarkdown(
|
|
28353
|
+
...ctx.storage.listMarkdown(join15(ctx.rootDir, "decisions")),
|
|
28354
|
+
...ctx.storage.listMarkdown(join15(ctx.rootDir, "docs")),
|
|
28355
|
+
...ctx.storage.listMarkdown(join15(ctx.rootDir, "reviews")),
|
|
28356
|
+
...ctx.storage.listMarkdown(join15(ctx.rootDir, "plan-tasks")),
|
|
28357
|
+
...ctx.storage.listMarkdown(join15(ctx.rootDir, "milestones")),
|
|
27802
28358
|
workspaceFile(ctx.rootDir, "plan"),
|
|
27803
28359
|
workspaceFile(ctx.rootDir, "risks")
|
|
27804
28360
|
];
|
|
27805
28361
|
const results = [];
|
|
27806
28362
|
for (const file2 of files) {
|
|
27807
|
-
if (!
|
|
27808
|
-
const raw =
|
|
28363
|
+
if (!existsSync17(file2)) continue;
|
|
28364
|
+
const raw = readFileSync17(file2, "utf8");
|
|
27809
28365
|
const content = raw.toLowerCase();
|
|
27810
28366
|
let idx = -1;
|
|
27811
28367
|
let matchLen = 0;
|
|
@@ -27856,9 +28412,9 @@ function linkDocumentsStub(ctx) {
|
|
|
27856
28412
|
const toId = args["toId"] ?? args["targetId"] ?? args["targetPathOrTitle"];
|
|
27857
28413
|
if (!fromId || !toId) return "linkDocuments requires fromId and toId.";
|
|
27858
28414
|
const allFiles = [
|
|
27859
|
-
...ctx.storage.listMarkdown(
|
|
27860
|
-
...ctx.storage.listMarkdown(
|
|
27861
|
-
...ctx.storage.listMarkdown(
|
|
28415
|
+
...ctx.storage.listMarkdown(join15(ctx.rootDir, "decisions")),
|
|
28416
|
+
...ctx.storage.listMarkdown(join15(ctx.rootDir, "docs")),
|
|
28417
|
+
...ctx.storage.listMarkdown(join15(ctx.rootDir, "reviews"))
|
|
27862
28418
|
];
|
|
27863
28419
|
const source = allFiles.find((f) => {
|
|
27864
28420
|
try {
|
|
@@ -27889,9 +28445,9 @@ function getDocumentBacklinksStub(ctx) {
|
|
|
27889
28445
|
const targetId = args["targetId"] ?? args["id"];
|
|
27890
28446
|
if (!targetId) return "getDocumentBacklinks requires targetId.";
|
|
27891
28447
|
const allFiles = [
|
|
27892
|
-
...ctx.storage.listMarkdown(
|
|
27893
|
-
...ctx.storage.listMarkdown(
|
|
27894
|
-
...ctx.storage.listMarkdown(
|
|
28448
|
+
...ctx.storage.listMarkdown(join15(ctx.rootDir, "decisions")),
|
|
28449
|
+
...ctx.storage.listMarkdown(join15(ctx.rootDir, "docs")),
|
|
28450
|
+
...ctx.storage.listMarkdown(join15(ctx.rootDir, "reviews"))
|
|
27895
28451
|
];
|
|
27896
28452
|
const backlinks = [];
|
|
27897
28453
|
for (const file2 of allFiles) {
|
|
@@ -27985,8 +28541,8 @@ __export(updater_exports, {
|
|
|
27985
28541
|
resolveBundledNpmCli: () => resolveBundledNpmCli
|
|
27986
28542
|
});
|
|
27987
28543
|
import { createRequire as createRequire2 } from "node:module";
|
|
27988
|
-
import { spawn as
|
|
27989
|
-
import { existsSync as
|
|
28544
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
28545
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
27990
28546
|
import path23 from "node:path";
|
|
27991
28547
|
import { fileURLToPath } from "node:url";
|
|
27992
28548
|
function resolveBundledNpmCli(execPath = process.execPath) {
|
|
@@ -27999,7 +28555,7 @@ function resolveBundledNpmCli(execPath = process.execPath) {
|
|
|
27999
28555
|
];
|
|
28000
28556
|
for (const candidate of candidates) {
|
|
28001
28557
|
try {
|
|
28002
|
-
if (
|
|
28558
|
+
if (existsSync18(candidate)) return candidate;
|
|
28003
28559
|
} catch {
|
|
28004
28560
|
}
|
|
28005
28561
|
}
|
|
@@ -28072,7 +28628,7 @@ async function checkForUpdate(fetcher = fetch, registryUrl) {
|
|
|
28072
28628
|
updateAvailable: cmp < 0
|
|
28073
28629
|
};
|
|
28074
28630
|
}
|
|
28075
|
-
async function performUpdate(packageName = "zelari-code", executor =
|
|
28631
|
+
async function performUpdate(packageName = "zelari-code", executor = spawn5, resolveNpmCli = resolveBundledNpmCli) {
|
|
28076
28632
|
const args = ["install", "-g", `${packageName}@latest`];
|
|
28077
28633
|
const primary = await runNpm(executor, args, "shim");
|
|
28078
28634
|
if (primary.ok) return primary;
|
|
@@ -28130,7 +28686,7 @@ var init_updater = __esm({
|
|
|
28130
28686
|
});
|
|
28131
28687
|
|
|
28132
28688
|
// src/cli/mcp/mcpClient.ts
|
|
28133
|
-
import { spawn as
|
|
28689
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
28134
28690
|
var DEFAULT_REQUEST_TIMEOUT_MS, INIT_TIMEOUT_MS, MCP_PROTOCOL_VERSION, McpClient;
|
|
28135
28691
|
var init_mcpClient = __esm({
|
|
28136
28692
|
"src/cli/mcp/mcpClient.ts"() {
|
|
@@ -28158,10 +28714,10 @@ var init_mcpClient = __esm({
|
|
|
28158
28714
|
env: { ...process.env, ...this.config.env ?? {} },
|
|
28159
28715
|
windowsHide: true
|
|
28160
28716
|
};
|
|
28161
|
-
const child = process.platform === "win32" ?
|
|
28717
|
+
const child = process.platform === "win32" ? spawn6(buildCmdLine(this.config.command, this.config.args ?? []), {
|
|
28162
28718
|
...spawnOpts,
|
|
28163
28719
|
shell: true
|
|
28164
|
-
}) :
|
|
28720
|
+
}) : spawn6(this.config.command, this.config.args ?? [], spawnOpts);
|
|
28165
28721
|
this.child = child;
|
|
28166
28722
|
child.stdout.setEncoding("utf8");
|
|
28167
28723
|
child.stdout.on("data", (chunk) => this.onStdout(chunk));
|
|
@@ -28305,20 +28861,20 @@ __export(mcpManager_exports, {
|
|
|
28305
28861
|
readMcpConfig: () => readMcpConfig,
|
|
28306
28862
|
registerMcpTools: () => registerMcpTools
|
|
28307
28863
|
});
|
|
28308
|
-
import { existsSync as
|
|
28309
|
-
import { join as
|
|
28310
|
-
import { homedir as
|
|
28864
|
+
import { existsSync as existsSync19, readFileSync as readFileSync18 } from "node:fs";
|
|
28865
|
+
import { join as join16 } from "node:path";
|
|
28866
|
+
import { homedir as homedir6 } from "node:os";
|
|
28311
28867
|
function readMcpConfig(projectRoot = process.cwd()) {
|
|
28312
28868
|
const merged = {};
|
|
28313
28869
|
const paths = [
|
|
28314
|
-
|
|
28315
|
-
|
|
28870
|
+
join16(homedir6(), ".zelari-code", "mcp.json"),
|
|
28871
|
+
join16(projectRoot, ".zelari", "mcp.json")
|
|
28316
28872
|
// later = higher precedence
|
|
28317
28873
|
];
|
|
28318
28874
|
for (const p3 of paths) {
|
|
28319
|
-
if (!
|
|
28875
|
+
if (!existsSync19(p3)) continue;
|
|
28320
28876
|
try {
|
|
28321
|
-
const parsed = JSON.parse(
|
|
28877
|
+
const parsed = JSON.parse(readFileSync18(p3, "utf8"));
|
|
28322
28878
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
28323
28879
|
if (!cfg || typeof cfg.command !== "string" || cfg.command.length === 0) continue;
|
|
28324
28880
|
merged[name] = cfg;
|
|
@@ -28407,6 +28963,53 @@ var init_mcpManager = __esm({
|
|
|
28407
28963
|
}
|
|
28408
28964
|
});
|
|
28409
28965
|
|
|
28966
|
+
// src/cli/workspace/projectInstructions.ts
|
|
28967
|
+
var projectInstructions_exports = {};
|
|
28968
|
+
__export(projectInstructions_exports, {
|
|
28969
|
+
loadProjectInstructions: () => loadProjectInstructions
|
|
28970
|
+
});
|
|
28971
|
+
import { existsSync as existsSync20, readFileSync as readFileSync19 } from "node:fs";
|
|
28972
|
+
import { join as join17 } from "node:path";
|
|
28973
|
+
function loadProjectInstructions(projectRoot = process.cwd(), maxChars = MAX_CHARS) {
|
|
28974
|
+
for (const name of CANDIDATES) {
|
|
28975
|
+
const full = join17(projectRoot, name);
|
|
28976
|
+
if (!existsSync20(full)) continue;
|
|
28977
|
+
try {
|
|
28978
|
+
let raw = readFileSync19(full, "utf8");
|
|
28979
|
+
raw = raw.replace(/\r\n/g, "\n").trim();
|
|
28980
|
+
if (!raw) continue;
|
|
28981
|
+
if (raw.length <= maxChars) {
|
|
28982
|
+
return { path: full, content: raw, truncated: false };
|
|
28983
|
+
}
|
|
28984
|
+
return {
|
|
28985
|
+
path: full,
|
|
28986
|
+
content: raw.slice(0, maxChars) + `
|
|
28987
|
+
|
|
28988
|
+
\u2026 [truncated; full file is ${raw.length} chars at ${name}]`,
|
|
28989
|
+
truncated: true
|
|
28990
|
+
};
|
|
28991
|
+
} catch {
|
|
28992
|
+
continue;
|
|
28993
|
+
}
|
|
28994
|
+
}
|
|
28995
|
+
return { path: null, content: "", truncated: false };
|
|
28996
|
+
}
|
|
28997
|
+
var CANDIDATES, MAX_CHARS;
|
|
28998
|
+
var init_projectInstructions = __esm({
|
|
28999
|
+
"src/cli/workspace/projectInstructions.ts"() {
|
|
29000
|
+
"use strict";
|
|
29001
|
+
CANDIDATES = [
|
|
29002
|
+
"AGENTS.md",
|
|
29003
|
+
"Agents.md",
|
|
29004
|
+
"agents.md",
|
|
29005
|
+
"CLAUDE.md",
|
|
29006
|
+
"Claude.md",
|
|
29007
|
+
"claude.md"
|
|
29008
|
+
];
|
|
29009
|
+
MAX_CHARS = 8e3;
|
|
29010
|
+
}
|
|
29011
|
+
});
|
|
29012
|
+
|
|
28410
29013
|
// src/cli/councilConfig.ts
|
|
28411
29014
|
function resolveCouncilTier(opts) {
|
|
28412
29015
|
const env = opts?.env ?? process.env;
|
|
@@ -28450,13 +29053,13 @@ var planDetect_exports = {};
|
|
|
28450
29053
|
__export(planDetect_exports, {
|
|
28451
29054
|
hasWorkspacePlan: () => hasWorkspacePlan
|
|
28452
29055
|
});
|
|
28453
|
-
import { existsSync as
|
|
28454
|
-
import { join as
|
|
29056
|
+
import { existsSync as existsSync21, readFileSync as readFileSync20 } from "node:fs";
|
|
29057
|
+
import { join as join18 } from "node:path";
|
|
28455
29058
|
function hasWorkspacePlan(projectRoot = process.cwd()) {
|
|
28456
|
-
const planPath =
|
|
28457
|
-
if (!
|
|
29059
|
+
const planPath = join18(resolveWorkspaceRoot(projectRoot), "plan.json");
|
|
29060
|
+
if (!existsSync21(planPath)) return false;
|
|
28458
29061
|
try {
|
|
28459
|
-
const parsed = JSON.parse(
|
|
29062
|
+
const parsed = JSON.parse(readFileSync20(planPath, "utf8"));
|
|
28460
29063
|
return Array.isArray(parsed.phases) && parsed.phases.length > 0;
|
|
28461
29064
|
} catch {
|
|
28462
29065
|
return false;
|
|
@@ -28560,13 +29163,13 @@ __export(agentsMd_exports, {
|
|
|
28560
29163
|
serializeAgentsMd: () => serializeAgentsMd,
|
|
28561
29164
|
updateAgentsMd: () => updateAgentsMd
|
|
28562
29165
|
});
|
|
28563
|
-
import { existsSync as
|
|
29166
|
+
import { existsSync as existsSync22, readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "node:fs";
|
|
28564
29167
|
import { createHash as createHash3 } from "node:crypto";
|
|
28565
|
-
import { join as
|
|
29168
|
+
import { join as join19 } from "node:path";
|
|
28566
29169
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
28567
29170
|
async function readPackageJson2(projectRoot) {
|
|
28568
|
-
const path36 =
|
|
28569
|
-
if (!
|
|
29171
|
+
const path36 = join19(projectRoot, "package.json");
|
|
29172
|
+
if (!existsSync22(path36)) return null;
|
|
28570
29173
|
try {
|
|
28571
29174
|
return JSON.parse(await readFile2(path36, "utf8"));
|
|
28572
29175
|
} catch {
|
|
@@ -28590,8 +29193,8 @@ async function genTechStack(ctx) {
|
|
|
28590
29193
|
].join("\n");
|
|
28591
29194
|
}
|
|
28592
29195
|
async function genDecisions(ctx) {
|
|
28593
|
-
const decisionsDir =
|
|
28594
|
-
if (!
|
|
29196
|
+
const decisionsDir = join19(ctx.rootDir, "decisions");
|
|
29197
|
+
if (!existsSync22(decisionsDir)) return "_No ADRs yet._";
|
|
28595
29198
|
const files = ctx.storage.listMarkdown(decisionsDir).sort();
|
|
28596
29199
|
const accepted = [];
|
|
28597
29200
|
const proposed = [];
|
|
@@ -28616,9 +29219,9 @@ async function genDecisions(ctx) {
|
|
|
28616
29219
|
}
|
|
28617
29220
|
async function genConventions(ctx) {
|
|
28618
29221
|
const lines = [];
|
|
28619
|
-
const claudeMd =
|
|
28620
|
-
if (
|
|
28621
|
-
const content =
|
|
29222
|
+
const claudeMd = join19(ctx.projectRoot, "CLAUDE.MD");
|
|
29223
|
+
if (existsSync22(claudeMd)) {
|
|
29224
|
+
const content = readFileSync21(claudeMd, "utf8");
|
|
28622
29225
|
const match = content.match(/## Architecture rules[\s\S]+?(?=\n## |\n*$)/);
|
|
28623
29226
|
if (match) {
|
|
28624
29227
|
lines.push('<!-- Extracted from CLAUDE.MD "Architecture rules" -->');
|
|
@@ -28650,9 +29253,9 @@ async function genBuild(ctx) {
|
|
|
28650
29253
|
].join("\n");
|
|
28651
29254
|
}
|
|
28652
29255
|
async function genOpenQuestions(ctx) {
|
|
28653
|
-
const path36 =
|
|
28654
|
-
if (!
|
|
28655
|
-
const content =
|
|
29256
|
+
const path36 = join19(ctx.rootDir, "risks.md");
|
|
29257
|
+
if (!existsSync22(path36)) return "_No open questions._";
|
|
29258
|
+
const content = readFileSync21(path36, "utf8");
|
|
28656
29259
|
const lines = content.split("\n");
|
|
28657
29260
|
const questions = [];
|
|
28658
29261
|
let currentTitle = "";
|
|
@@ -28726,9 +29329,9 @@ function titleCase(id) {
|
|
|
28726
29329
|
return id.split("-").map((w) => w[0].toUpperCase() + w.slice(1)).join(" ");
|
|
28727
29330
|
}
|
|
28728
29331
|
async function updateAgentsMd(ctx, projectRoot) {
|
|
28729
|
-
const agentsPath =
|
|
28730
|
-
if (
|
|
28731
|
-
const content =
|
|
29332
|
+
const agentsPath = join19(projectRoot, "AGENTS.MD");
|
|
29333
|
+
if (existsSync22(agentsPath)) {
|
|
29334
|
+
const content = readFileSync21(agentsPath, "utf8");
|
|
28732
29335
|
const hasAnyMarker = AUTO_SECTIONS.some((id) => content.includes(MARKER_OPEN(id)));
|
|
28733
29336
|
if (!hasAnyMarker) {
|
|
28734
29337
|
return {
|
|
@@ -28743,8 +29346,8 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
28743
29346
|
newSections.set(id, await GENERATORS[id](ctx));
|
|
28744
29347
|
}
|
|
28745
29348
|
let manualContent = "";
|
|
28746
|
-
if (
|
|
28747
|
-
const { manualBlocks } = parseAgentsMd(
|
|
29349
|
+
if (existsSync22(agentsPath)) {
|
|
29350
|
+
const { manualBlocks } = parseAgentsMd(readFileSync21(agentsPath, "utf8"));
|
|
28748
29351
|
manualContent = manualBlocks.after;
|
|
28749
29352
|
} else {
|
|
28750
29353
|
const projectName2 = projectName(projectRoot);
|
|
@@ -28760,7 +29363,7 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
28760
29363
|
""
|
|
28761
29364
|
].join("\n");
|
|
28762
29365
|
}
|
|
28763
|
-
const oldContent =
|
|
29366
|
+
const oldContent = existsSync22(agentsPath) ? readFileSync21(agentsPath, "utf8") : "";
|
|
28764
29367
|
const { sections: oldSections } = parseAgentsMd(oldContent);
|
|
28765
29368
|
const changedSections = [];
|
|
28766
29369
|
for (const id of AUTO_SECTIONS) {
|
|
@@ -28772,7 +29375,7 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
28772
29375
|
return { changed: false, sections: [] };
|
|
28773
29376
|
}
|
|
28774
29377
|
const newContent = serializeAgentsMd(manualContent, newSections);
|
|
28775
|
-
|
|
29378
|
+
writeFileSync14(agentsPath, newContent, "utf8");
|
|
28776
29379
|
return { changed: true, sections: changedSections };
|
|
28777
29380
|
}
|
|
28778
29381
|
function hash2(s) {
|
|
@@ -28897,9 +29500,9 @@ var init_completeDesign = __esm({
|
|
|
28897
29500
|
});
|
|
28898
29501
|
|
|
28899
29502
|
// src/cli/workspace/projectSmoke.ts
|
|
28900
|
-
import { spawn as
|
|
28901
|
-
import { existsSync as
|
|
28902
|
-
import { join as
|
|
29503
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
29504
|
+
import { existsSync as existsSync23, readFileSync as readFileSync22 } from "node:fs";
|
|
29505
|
+
import { join as join20 } from "node:path";
|
|
28903
29506
|
function pickSmokeScript(scripts) {
|
|
28904
29507
|
if (!scripts) return null;
|
|
28905
29508
|
for (const name of SMOKE_SCRIPT_PRIORITY) {
|
|
@@ -28911,13 +29514,13 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
28911
29514
|
if (process.env["ZELARI_SMOKE"] === "0") {
|
|
28912
29515
|
return { ran: false, reason: "ZELARI_SMOKE=0 (disabled)" };
|
|
28913
29516
|
}
|
|
28914
|
-
const pkgPath =
|
|
28915
|
-
if (!
|
|
29517
|
+
const pkgPath = join20(projectRoot, "package.json");
|
|
29518
|
+
if (!existsSync23(pkgPath)) {
|
|
28916
29519
|
return { ran: false, reason: "no package.json (skipped)" };
|
|
28917
29520
|
}
|
|
28918
29521
|
let scripts = {};
|
|
28919
29522
|
try {
|
|
28920
|
-
const pkg = JSON.parse(
|
|
29523
|
+
const pkg = JSON.parse(readFileSync22(pkgPath, "utf8"));
|
|
28921
29524
|
scripts = pkg.scripts ?? {};
|
|
28922
29525
|
} catch {
|
|
28923
29526
|
return { ran: false, reason: "package.json unreadable (skipped)" };
|
|
@@ -28927,12 +29530,12 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
28927
29530
|
return { ran: false, reason: "no typecheck/test/build script (skipped)" };
|
|
28928
29531
|
}
|
|
28929
29532
|
return await new Promise((resolveRun) => {
|
|
28930
|
-
const child = process.platform === "win32" ?
|
|
29533
|
+
const child = process.platform === "win32" ? spawn7(buildCmdLine("npm.cmd", ["run", script]), {
|
|
28931
29534
|
cwd: projectRoot,
|
|
28932
29535
|
stdio: ["ignore", "pipe", "pipe"],
|
|
28933
29536
|
env: process.env,
|
|
28934
29537
|
shell: true
|
|
28935
|
-
}) :
|
|
29538
|
+
}) : spawn7("npm", ["run", script], {
|
|
28936
29539
|
cwd: projectRoot,
|
|
28937
29540
|
stdio: ["ignore", "pipe", "pipe"],
|
|
28938
29541
|
env: process.env
|
|
@@ -29004,9 +29607,9 @@ __export(postCouncilHook_exports, {
|
|
|
29004
29607
|
runImplementationVerificationHook: () => runImplementationVerificationHook,
|
|
29005
29608
|
runPostCouncilHook: () => runPostCouncilHook
|
|
29006
29609
|
});
|
|
29007
|
-
import { spawn as
|
|
29008
|
-
import { existsSync as
|
|
29009
|
-
import { join as
|
|
29610
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
29611
|
+
import { existsSync as existsSync24, readFileSync as readFileSync23 } from "node:fs";
|
|
29612
|
+
import { join as join21 } from "node:path";
|
|
29010
29613
|
async function runCompleteDesignPostProcessor(ctx, options) {
|
|
29011
29614
|
if (options?.runMode === "implementation") {
|
|
29012
29615
|
return {
|
|
@@ -29017,9 +29620,9 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
29017
29620
|
if (process.env["ZELARI_COMPLETE_DESIGN"] === "0") {
|
|
29018
29621
|
return { ran: false, reason: "ZELARI_COMPLETE_DESIGN=0 (disabled)" };
|
|
29019
29622
|
}
|
|
29020
|
-
const planJsonPath2 =
|
|
29021
|
-
const scriptPath =
|
|
29022
|
-
if (!
|
|
29623
|
+
const planJsonPath2 = join21(ctx.rootDir, "plan.json");
|
|
29624
|
+
const scriptPath = join21(ctx.projectRoot, "complete-design.mjs");
|
|
29625
|
+
if (!existsSync24(planJsonPath2)) {
|
|
29023
29626
|
return {
|
|
29024
29627
|
ran: false,
|
|
29025
29628
|
reason: ".zelari/plan.json missing (not design-phase)"
|
|
@@ -29027,7 +29630,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
29027
29630
|
}
|
|
29028
29631
|
let phaseCount = 0;
|
|
29029
29632
|
try {
|
|
29030
|
-
const parsed = JSON.parse(
|
|
29633
|
+
const parsed = JSON.parse(readFileSync23(planJsonPath2, "utf8"));
|
|
29031
29634
|
phaseCount = Array.isArray(parsed.phases) ? parsed.phases.length : 0;
|
|
29032
29635
|
} catch {
|
|
29033
29636
|
return { ran: false, reason: ".zelari/plan.json corrupt" };
|
|
@@ -29035,7 +29638,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
29035
29638
|
if (phaseCount === 0) {
|
|
29036
29639
|
return { ran: false, reason: ".zelari/plan.json has no phases" };
|
|
29037
29640
|
}
|
|
29038
|
-
if (!
|
|
29641
|
+
if (!existsSync24(scriptPath)) {
|
|
29039
29642
|
try {
|
|
29040
29643
|
const builtin = await runBuiltinCompleteDesign(ctx);
|
|
29041
29644
|
return {
|
|
@@ -29053,7 +29656,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
29053
29656
|
}
|
|
29054
29657
|
}
|
|
29055
29658
|
return await new Promise((resolveRun) => {
|
|
29056
|
-
const child =
|
|
29659
|
+
const child = spawn8(process.execPath, [scriptPath], {
|
|
29057
29660
|
cwd: ctx.projectRoot,
|
|
29058
29661
|
stdio: ["ignore", "pipe", "pipe"],
|
|
29059
29662
|
env: process.env
|
|
@@ -29256,12 +29859,12 @@ var buildLessonsSummary_exports = {};
|
|
|
29256
29859
|
__export(buildLessonsSummary_exports, {
|
|
29257
29860
|
buildLessonsSummary: () => buildLessonsSummary
|
|
29258
29861
|
});
|
|
29259
|
-
import { existsSync as
|
|
29260
|
-
import { join as
|
|
29862
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
29863
|
+
import { join as join22 } from "node:path";
|
|
29261
29864
|
function buildLessonsSummary(projectRoot = process.cwd(), taskText) {
|
|
29262
29865
|
if (process.env["ZELARI_LESSONS"] === "0") return null;
|
|
29263
29866
|
const zelariRoot = resolveWorkspaceRoot(projectRoot);
|
|
29264
|
-
if (!
|
|
29867
|
+
if (!existsSync25(join22(zelariRoot, "lessons.jsonl"))) return null;
|
|
29265
29868
|
const lessons = recallLessons(zelariRoot, {
|
|
29266
29869
|
maxLessons: 5,
|
|
29267
29870
|
maxBytes: 2048,
|
|
@@ -29284,10 +29887,10 @@ __export(councilFeedback_exports, {
|
|
|
29284
29887
|
});
|
|
29285
29888
|
import {
|
|
29286
29889
|
promises as fs13,
|
|
29287
|
-
existsSync as
|
|
29288
|
-
readFileSync as
|
|
29289
|
-
writeFileSync as
|
|
29290
|
-
mkdirSync as
|
|
29890
|
+
existsSync as existsSync26,
|
|
29891
|
+
readFileSync as readFileSync24,
|
|
29892
|
+
writeFileSync as writeFileSync15,
|
|
29893
|
+
mkdirSync as mkdirSync10
|
|
29291
29894
|
} from "node:fs";
|
|
29292
29895
|
import path24 from "node:path";
|
|
29293
29896
|
import os8 from "node:os";
|
|
@@ -29393,9 +29996,9 @@ var init_councilFeedback = __esm({
|
|
|
29393
29996
|
}
|
|
29394
29997
|
// --- persistence ---------------------------------------------------------
|
|
29395
29998
|
load() {
|
|
29396
|
-
if (!
|
|
29999
|
+
if (!existsSync26(this.file)) return;
|
|
29397
30000
|
try {
|
|
29398
|
-
const raw =
|
|
30001
|
+
const raw = readFileSync24(this.file, "utf-8");
|
|
29399
30002
|
const parsed = JSON.parse(raw);
|
|
29400
30003
|
if (parsed && Array.isArray(parsed.entries)) {
|
|
29401
30004
|
this.entries = parsed.entries.filter(
|
|
@@ -29406,8 +30009,8 @@ var init_councilFeedback = __esm({
|
|
|
29406
30009
|
}
|
|
29407
30010
|
}
|
|
29408
30011
|
save() {
|
|
29409
|
-
|
|
29410
|
-
|
|
30012
|
+
mkdirSync10(path24.dirname(this.file), { recursive: true });
|
|
30013
|
+
writeFileSync15(
|
|
29411
30014
|
this.file,
|
|
29412
30015
|
JSON.stringify({ entries: this.entries }, null, 2),
|
|
29413
30016
|
{ encoding: "utf-8", mode: 384 }
|
|
@@ -29864,8 +30467,8 @@ __export(prereqChecks_exports, {
|
|
|
29864
30467
|
runPrereqChecks: () => runPrereqChecks
|
|
29865
30468
|
});
|
|
29866
30469
|
import { execSync, spawnSync as spawnSync2 } from "node:child_process";
|
|
29867
|
-
import { existsSync as
|
|
29868
|
-
import { dirname as
|
|
30470
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
30471
|
+
import { dirname as dirname5 } from "node:path";
|
|
29869
30472
|
function isWslBashPath2(p3) {
|
|
29870
30473
|
if (!p3 || typeof p3 !== "string") return false;
|
|
29871
30474
|
const n = p3.replace(/\//g, "\\").toLowerCase();
|
|
@@ -29927,7 +30530,7 @@ function resolveAgentShellSync() {
|
|
|
29927
30530
|
function agentProbeEnv() {
|
|
29928
30531
|
const env = { ...process.env };
|
|
29929
30532
|
try {
|
|
29930
|
-
const nodeDir =
|
|
30533
|
+
const nodeDir = dirname5(process.execPath);
|
|
29931
30534
|
if (!nodeDir) return env;
|
|
29932
30535
|
const sep = process.platform === "win32" ? ";" : ":";
|
|
29933
30536
|
const current = env.PATH ?? env.Path ?? "";
|
|
@@ -29944,7 +30547,7 @@ function agentProbeEnv() {
|
|
|
29944
30547
|
}
|
|
29945
30548
|
function existsSyncSafe2(p3) {
|
|
29946
30549
|
try {
|
|
29947
|
-
return
|
|
30550
|
+
return existsSync27(p3);
|
|
29948
30551
|
} catch {
|
|
29949
30552
|
return false;
|
|
29950
30553
|
}
|
|
@@ -30163,7 +30766,7 @@ var init_prereqChecks = __esm({
|
|
|
30163
30766
|
});
|
|
30164
30767
|
|
|
30165
30768
|
// src/cli/plugins/prefs.ts
|
|
30166
|
-
import { existsSync as
|
|
30769
|
+
import { existsSync as existsSync28, readFileSync as readFileSync25, writeFileSync as writeFileSync16, mkdirSync as mkdirSync11 } from "node:fs";
|
|
30167
30770
|
import path29 from "node:path";
|
|
30168
30771
|
import os9 from "node:os";
|
|
30169
30772
|
function getPluginPrefsPath() {
|
|
@@ -30172,8 +30775,8 @@ function getPluginPrefsPath() {
|
|
|
30172
30775
|
function getPluginPrefs() {
|
|
30173
30776
|
const file2 = getPluginPrefsPath();
|
|
30174
30777
|
try {
|
|
30175
|
-
if (!
|
|
30176
|
-
const raw =
|
|
30778
|
+
if (!existsSync28(file2)) return { ...DEFAULTS2, dontAskAgain: {} };
|
|
30779
|
+
const raw = readFileSync25(file2, "utf-8");
|
|
30177
30780
|
const parsed = JSON.parse(raw);
|
|
30178
30781
|
if (parsed && typeof parsed === "object" && parsed.dontAskAgain && typeof parsed.dontAskAgain === "object") {
|
|
30179
30782
|
const clean = {};
|
|
@@ -30188,8 +30791,8 @@ function getPluginPrefs() {
|
|
|
30188
30791
|
}
|
|
30189
30792
|
function writePluginPrefs(prefs) {
|
|
30190
30793
|
const file2 = getPluginPrefsPath();
|
|
30191
|
-
|
|
30192
|
-
|
|
30794
|
+
mkdirSync11(path29.dirname(file2), { recursive: true });
|
|
30795
|
+
writeFileSync16(file2, JSON.stringify(prefs, null, 2), {
|
|
30193
30796
|
encoding: "utf-8",
|
|
30194
30797
|
mode: 384
|
|
30195
30798
|
});
|
|
@@ -30224,7 +30827,7 @@ __export(registry_exports, {
|
|
|
30224
30827
|
findPlugin: () => findPlugin,
|
|
30225
30828
|
isBinaryOnPath: () => isBinaryOnPath
|
|
30226
30829
|
});
|
|
30227
|
-
import { existsSync as
|
|
30830
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
30228
30831
|
import path30 from "node:path";
|
|
30229
30832
|
function detectLocalBin(bin) {
|
|
30230
30833
|
return (cwd) => {
|
|
@@ -30241,7 +30844,7 @@ function isBinaryOnPath(bin, opts = {}) {
|
|
|
30241
30844
|
return false;
|
|
30242
30845
|
}
|
|
30243
30846
|
const platform = opts.platform ?? process.platform;
|
|
30244
|
-
const exists = opts.exists ??
|
|
30847
|
+
const exists = opts.exists ?? existsSync29;
|
|
30245
30848
|
const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
|
|
30246
30849
|
const pathMod = platform === "win32" ? path30.win32 : path30.posix;
|
|
30247
30850
|
const sep = platform === "win32" ? ";" : ":";
|
|
@@ -30392,7 +30995,7 @@ __export(doctor_exports, {
|
|
|
30392
30995
|
runDoctor: () => runDoctor
|
|
30393
30996
|
});
|
|
30394
30997
|
import { execSync as execSync2 } from "node:child_process";
|
|
30395
|
-
import { existsSync as
|
|
30998
|
+
import { existsSync as existsSync35, readFileSync as readFileSync30, readlinkSync, statSync as statSync6 } from "node:fs";
|
|
30396
30999
|
import { createRequire as createRequire3 } from "node:module";
|
|
30397
31000
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
30398
31001
|
import path35 from "node:path";
|
|
@@ -30400,9 +31003,9 @@ function findPackageRoot(start) {
|
|
|
30400
31003
|
let dir = start;
|
|
30401
31004
|
for (let i = 0; i < 6; i += 1) {
|
|
30402
31005
|
const candidate = path35.join(dir, "package.json");
|
|
30403
|
-
if (
|
|
31006
|
+
if (existsSync35(candidate)) {
|
|
30404
31007
|
try {
|
|
30405
|
-
const pkg = JSON.parse(
|
|
31008
|
+
const pkg = JSON.parse(readFileSync30(candidate, "utf8"));
|
|
30406
31009
|
if (pkg.name === "zelari-code") return dir;
|
|
30407
31010
|
} catch {
|
|
30408
31011
|
}
|
|
@@ -30426,7 +31029,7 @@ function tryExec(cmd) {
|
|
|
30426
31029
|
function readPackageJson3() {
|
|
30427
31030
|
try {
|
|
30428
31031
|
const pkgPath = path35.join(packageRoot, "package.json");
|
|
30429
|
-
return JSON.parse(
|
|
31032
|
+
return JSON.parse(readFileSync30(pkgPath, "utf8"));
|
|
30430
31033
|
} catch {
|
|
30431
31034
|
return null;
|
|
30432
31035
|
}
|
|
@@ -30442,7 +31045,7 @@ function checkShim(pkgName) {
|
|
|
30442
31045
|
const isWin = process.platform === "win32";
|
|
30443
31046
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
30444
31047
|
const shimPath = path35.join(prefix, shimName);
|
|
30445
|
-
if (!
|
|
31048
|
+
if (!existsSync35(shimPath)) {
|
|
30446
31049
|
return FAIL(
|
|
30447
31050
|
`shim not found at ${shimPath}
|
|
30448
31051
|
fix: npm install -g ${pkgName}@latest --force`
|
|
@@ -30451,7 +31054,7 @@ function checkShim(pkgName) {
|
|
|
30451
31054
|
try {
|
|
30452
31055
|
const st = statSync6(shimPath);
|
|
30453
31056
|
if (isWin) {
|
|
30454
|
-
const content =
|
|
31057
|
+
const content = readFileSync30(shimPath, "utf8");
|
|
30455
31058
|
if (content.includes(`${pkgName}\\bin\\`) || content.includes(`${pkgName}/bin/`)) {
|
|
30456
31059
|
return OK(`shim OK at ${shimPath} (${st.size} bytes)`);
|
|
30457
31060
|
}
|
|
@@ -30510,7 +31113,7 @@ function checkNode(pkg) {
|
|
|
30510
31113
|
}
|
|
30511
31114
|
function checkBundle() {
|
|
30512
31115
|
const bundle = path35.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
30513
|
-
if (!
|
|
31116
|
+
if (!existsSync35(bundle)) {
|
|
30514
31117
|
return FAIL(
|
|
30515
31118
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
30516
31119
|
fix: npm run build:cli (then reinstall or run via tsx)`
|
|
@@ -34643,13 +35246,22 @@ function useChatTurn(params) {
|
|
|
34643
35246
|
let systemPrompt;
|
|
34644
35247
|
try {
|
|
34645
35248
|
const languageModule = buildLanguagePolicyModuleFor(userText);
|
|
35249
|
+
const { loadProjectInstructions: loadProjectInstructions2 } = await Promise.resolve().then(() => (init_projectInstructions(), projectInstructions_exports));
|
|
35250
|
+
const projectInstructions = loadProjectInstructions2(
|
|
35251
|
+
process.cwd()
|
|
35252
|
+
).content;
|
|
34646
35253
|
systemPrompt = buildSystemPrompt(singleAgentRole, {
|
|
34647
35254
|
tools: getAllTools(),
|
|
34648
35255
|
toolNames: toolListNames,
|
|
35256
|
+
mode: "agent",
|
|
35257
|
+
projectInstructions: projectInstructions || void 0,
|
|
34649
35258
|
aiConfig: {
|
|
34650
35259
|
enabledSkills: [],
|
|
34651
35260
|
enabledTools: toolListNames,
|
|
34652
|
-
customPromptModules: [
|
|
35261
|
+
customPromptModules: [
|
|
35262
|
+
SINGLE_AGENT_IDENTITY_MODULE,
|
|
35263
|
+
languageModule
|
|
35264
|
+
],
|
|
34653
35265
|
agentSkillConfigs: []
|
|
34654
35266
|
},
|
|
34655
35267
|
workspaceContext: workspaceContext || void 0,
|
|
@@ -36439,8 +37051,8 @@ init_registry2();
|
|
|
36439
37051
|
// src/cli/plugins/installer.ts
|
|
36440
37052
|
init_cmdline();
|
|
36441
37053
|
init_updater();
|
|
36442
|
-
import { spawn as
|
|
36443
|
-
async function installPlugin(spec, cwd, executor =
|
|
37054
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
37055
|
+
async function installPlugin(spec, cwd, executor = spawn9) {
|
|
36444
37056
|
const scopeFlag = spec.installScope === "global" ? "-g" : "-D";
|
|
36445
37057
|
const args = ["install", scopeFlag, spec.npmPackage];
|
|
36446
37058
|
const primary = await runNpm2(executor, args, cwd, "shim");
|
|
@@ -36567,7 +37179,7 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
36567
37179
|
}
|
|
36568
37180
|
|
|
36569
37181
|
// src/cli/branchManager.ts
|
|
36570
|
-
import { promises as fs17, existsSync as
|
|
37182
|
+
import { promises as fs17, existsSync as existsSync30, readFileSync as readFileSync26, writeFileSync as writeFileSync17, mkdirSync as mkdirSync12, statSync as statSync4, rmSync as rmSync2 } from "node:fs";
|
|
36571
37183
|
import path32 from "node:path";
|
|
36572
37184
|
import os11 from "node:os";
|
|
36573
37185
|
var META_FILENAME = "meta.json";
|
|
@@ -36589,11 +37201,11 @@ function sessionsPathFor(name, baseDir) {
|
|
|
36589
37201
|
}
|
|
36590
37202
|
function readBranchMeta(name, baseDir) {
|
|
36591
37203
|
const metaPath = metaPathFor(name, baseDir);
|
|
36592
|
-
if (!
|
|
37204
|
+
if (!existsSync30(metaPath)) {
|
|
36593
37205
|
throw new BranchNotFoundError(`Branch "${name}" not found`);
|
|
36594
37206
|
}
|
|
36595
37207
|
try {
|
|
36596
|
-
const raw =
|
|
37208
|
+
const raw = readFileSync26(metaPath, "utf-8");
|
|
36597
37209
|
const parsed = JSON.parse(raw);
|
|
36598
37210
|
if (!parsed || typeof parsed !== "object" || typeof parsed.name !== "string" || typeof parsed.createdAt !== "number" || typeof parsed.fromSessionId !== "string") {
|
|
36599
37211
|
throw new BranchCorruptError(`Branch "${name}" meta.json is malformed`);
|
|
@@ -36610,8 +37222,8 @@ function readBranchMeta(name, baseDir) {
|
|
|
36610
37222
|
}
|
|
36611
37223
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
36612
37224
|
const metaPath = metaPathFor(name, baseDir);
|
|
36613
|
-
|
|
36614
|
-
|
|
37225
|
+
mkdirSync12(path32.dirname(metaPath), { recursive: true });
|
|
37226
|
+
writeFileSync17(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
36615
37227
|
}
|
|
36616
37228
|
async function countSessions(name, baseDir) {
|
|
36617
37229
|
const sessionsPath = sessionsPathFor(name, baseDir);
|
|
@@ -36649,7 +37261,7 @@ var SessionNotFoundError = class extends Error {
|
|
|
36649
37261
|
};
|
|
36650
37262
|
function branchExists(name, baseDir = getBranchesBaseDir()) {
|
|
36651
37263
|
const bp = branchPathFor(name, baseDir);
|
|
36652
|
-
return
|
|
37264
|
+
return existsSync30(bp) && existsSync30(metaPathFor(name, baseDir));
|
|
36653
37265
|
}
|
|
36654
37266
|
async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
|
|
36655
37267
|
if (!name || name.trim().length === 0) {
|
|
@@ -36662,12 +37274,12 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
36662
37274
|
throw new BranchAlreadyExistsError(name);
|
|
36663
37275
|
}
|
|
36664
37276
|
const sourcePath = path32.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
36665
|
-
if (!
|
|
37277
|
+
if (!existsSync30(sourcePath)) {
|
|
36666
37278
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
36667
37279
|
}
|
|
36668
37280
|
const branchPath = branchPathFor(name, baseDir);
|
|
36669
37281
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
36670
|
-
|
|
37282
|
+
mkdirSync12(branchSessionsPath, { recursive: true });
|
|
36671
37283
|
const destPath = path32.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
36672
37284
|
await fs17.copyFile(sourcePath, destPath);
|
|
36673
37285
|
const meta3 = {
|
|
@@ -36695,7 +37307,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
|
36695
37307
|
const results = [];
|
|
36696
37308
|
for (const entry of entries) {
|
|
36697
37309
|
const metaPath = metaPathFor(entry, baseDir);
|
|
36698
|
-
if (!
|
|
37310
|
+
if (!existsSync30(metaPath)) continue;
|
|
36699
37311
|
try {
|
|
36700
37312
|
const meta3 = readBranchMeta(entry, baseDir);
|
|
36701
37313
|
const sessionCount = await countSessions(entry, baseDir);
|
|
@@ -37231,7 +37843,7 @@ import path34 from "node:path";
|
|
|
37231
37843
|
import os12 from "node:os";
|
|
37232
37844
|
|
|
37233
37845
|
// src/cli/skillHistory.ts
|
|
37234
|
-
import { promises as fs19, existsSync as
|
|
37846
|
+
import { promises as fs19, existsSync as existsSync31, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync13 } from "node:fs";
|
|
37235
37847
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
37236
37848
|
async function readSkillHistory(file2) {
|
|
37237
37849
|
let raw = "";
|
|
@@ -38409,9 +39021,9 @@ function ContinueKey({ onContinue }) {
|
|
|
38409
39021
|
init_providerConfig();
|
|
38410
39022
|
|
|
38411
39023
|
// src/cli/wizard/firstRun.ts
|
|
38412
|
-
import { existsSync as
|
|
39024
|
+
import { existsSync as existsSync32 } from "node:fs";
|
|
38413
39025
|
function shouldRunWizard(input) {
|
|
38414
|
-
const exists = input.exists ??
|
|
39026
|
+
const exists = input.exists ?? existsSync32;
|
|
38415
39027
|
if (input.hasResetConfigFlag) {
|
|
38416
39028
|
return { shouldRun: true, reason: "--reset-config flag forced wizard" };
|
|
38417
39029
|
}
|
|
@@ -38696,6 +39308,7 @@ init_keyStore();
|
|
|
38696
39308
|
init_providerConfig();
|
|
38697
39309
|
init_openai_compatible();
|
|
38698
39310
|
init_phase();
|
|
39311
|
+
import { readFileSync as readFileSync27 } from "node:fs";
|
|
38699
39312
|
function parseHeadlessFlags(argv) {
|
|
38700
39313
|
if (!argv.includes("--headless")) {
|
|
38701
39314
|
return { options: null };
|
|
@@ -38708,6 +39321,7 @@ function parseHeadlessFlags(argv) {
|
|
|
38708
39321
|
let councilFlag = false;
|
|
38709
39322
|
let provider;
|
|
38710
39323
|
let model;
|
|
39324
|
+
let history2;
|
|
38711
39325
|
for (let i = 0; i < argv.length; i++) {
|
|
38712
39326
|
const arg = argv[i];
|
|
38713
39327
|
if (arg === "--headless") continue;
|
|
@@ -38756,6 +39370,32 @@ function parseHeadlessFlags(argv) {
|
|
|
38756
39370
|
} else if (arg === "--model") {
|
|
38757
39371
|
model = argv[i + 1];
|
|
38758
39372
|
i++;
|
|
39373
|
+
} else if (arg === "--history" || arg === "--history-file") {
|
|
39374
|
+
const next = argv[i + 1];
|
|
39375
|
+
if (next) {
|
|
39376
|
+
let raw = null;
|
|
39377
|
+
if (arg === "--history-file") {
|
|
39378
|
+
try {
|
|
39379
|
+
raw = readFileSync27(next, "utf-8");
|
|
39380
|
+
} catch {
|
|
39381
|
+
raw = null;
|
|
39382
|
+
}
|
|
39383
|
+
} else {
|
|
39384
|
+
raw = next;
|
|
39385
|
+
}
|
|
39386
|
+
if (raw) {
|
|
39387
|
+
try {
|
|
39388
|
+
const parsedHist = JSON.parse(raw);
|
|
39389
|
+
if (Array.isArray(parsedHist)) {
|
|
39390
|
+
history2 = parsedHist.filter(
|
|
39391
|
+
(m) => m && typeof m === "object" && typeof m.role === "string" && typeof m.content === "string"
|
|
39392
|
+
);
|
|
39393
|
+
}
|
|
39394
|
+
} catch {
|
|
39395
|
+
}
|
|
39396
|
+
}
|
|
39397
|
+
i++;
|
|
39398
|
+
}
|
|
38759
39399
|
}
|
|
38760
39400
|
}
|
|
38761
39401
|
if (councilFlag && !modeExplicit) {
|
|
@@ -38777,7 +39417,8 @@ function parseHeadlessFlags(argv) {
|
|
|
38777
39417
|
phase: phase2,
|
|
38778
39418
|
useCouncil: mode === "council",
|
|
38779
39419
|
provider,
|
|
38780
|
-
model
|
|
39420
|
+
model,
|
|
39421
|
+
...history2 && history2.length > 0 ? { history: history2 } : {}
|
|
38781
39422
|
}
|
|
38782
39423
|
};
|
|
38783
39424
|
}
|
|
@@ -38810,12 +39451,66 @@ function emitEvent(event) {
|
|
|
38810
39451
|
|
|
38811
39452
|
// src/cli/runHeadless.ts
|
|
38812
39453
|
init_harness();
|
|
39454
|
+
init_conversationContext();
|
|
38813
39455
|
init_toolRegistry();
|
|
38814
39456
|
init_skills2();
|
|
38815
39457
|
init_envNumber();
|
|
38816
39458
|
init_phaseState();
|
|
38817
39459
|
init_phase();
|
|
39460
|
+
|
|
39461
|
+
// src/cli/utils/streamScrub.ts
|
|
39462
|
+
function createStreamScrubber() {
|
|
39463
|
+
let rawBuf = "";
|
|
39464
|
+
let emittedLen = 0;
|
|
39465
|
+
const snapshot = () => {
|
|
39466
|
+
const cleaned = cleanAgentContent(rawBuf);
|
|
39467
|
+
if (cleaned.length <= emittedLen) return "";
|
|
39468
|
+
const delta = cleaned.slice(emittedLen);
|
|
39469
|
+
emittedLen = cleaned.length;
|
|
39470
|
+
return delta;
|
|
39471
|
+
};
|
|
39472
|
+
return {
|
|
39473
|
+
push(rawDelta) {
|
|
39474
|
+
rawBuf += rawDelta;
|
|
39475
|
+
return snapshot();
|
|
39476
|
+
},
|
|
39477
|
+
flush() {
|
|
39478
|
+
return snapshot();
|
|
39479
|
+
},
|
|
39480
|
+
reset() {
|
|
39481
|
+
rawBuf = "";
|
|
39482
|
+
emittedLen = 0;
|
|
39483
|
+
}
|
|
39484
|
+
};
|
|
39485
|
+
}
|
|
39486
|
+
|
|
39487
|
+
// src/cli/runHeadless.ts
|
|
38818
39488
|
async function runHeadless(opts) {
|
|
39489
|
+
let crashed = false;
|
|
39490
|
+
const handleFatal = (label, err) => {
|
|
39491
|
+
if (crashed) return;
|
|
39492
|
+
crashed = true;
|
|
39493
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
39494
|
+
const stack = err instanceof Error && err.stack ? `
|
|
39495
|
+
${err.stack}` : "";
|
|
39496
|
+
const line = `[zelari-code --headless] FATAL ${label}: ${msg}${stack}`;
|
|
39497
|
+
try {
|
|
39498
|
+
emitEvent({
|
|
39499
|
+
type: "error",
|
|
39500
|
+
severity: "fatal",
|
|
39501
|
+
message: `${label}: ${msg}`,
|
|
39502
|
+
code: "uncaught"
|
|
39503
|
+
});
|
|
39504
|
+
} catch {
|
|
39505
|
+
}
|
|
39506
|
+
try {
|
|
39507
|
+
process.stderr.write(line + "\n");
|
|
39508
|
+
} catch {
|
|
39509
|
+
}
|
|
39510
|
+
process.exit(2);
|
|
39511
|
+
};
|
|
39512
|
+
process.on("uncaughtException", (err) => handleFatal("uncaughtException", err));
|
|
39513
|
+
process.on("unhandledRejection", (err) => handleFatal("unhandledRejection", err));
|
|
38819
39514
|
setPhase(opts.phase ?? "build");
|
|
38820
39515
|
const { provider, model } = resolveHeadlessProvider(opts);
|
|
38821
39516
|
const key = await resolveHeadlessKey(provider);
|
|
@@ -38853,11 +39548,50 @@ async function runHeadless(opts) {
|
|
|
38853
39548
|
function planModeFromOpts(opts) {
|
|
38854
39549
|
return (opts.phase ?? "build") === "plan";
|
|
38855
39550
|
}
|
|
39551
|
+
var mcpExitHookInstalled = false;
|
|
39552
|
+
async function registerHeadlessMcp(toolRegistry, opts) {
|
|
39553
|
+
try {
|
|
39554
|
+
const { registerMcpTools: registerMcpTools2, closeMcpClients: closeMcpClients2 } = await Promise.resolve().then(() => (init_mcpManager(), mcpManager_exports));
|
|
39555
|
+
const mcp = await registerMcpTools2(toolRegistry, process.cwd());
|
|
39556
|
+
if (!mcpExitHookInstalled) {
|
|
39557
|
+
mcpExitHookInstalled = true;
|
|
39558
|
+
process.once("exit", () => {
|
|
39559
|
+
try {
|
|
39560
|
+
closeMcpClients2();
|
|
39561
|
+
} catch {
|
|
39562
|
+
}
|
|
39563
|
+
});
|
|
39564
|
+
}
|
|
39565
|
+
if (mcp.registered.length > 0 && opts.output === "json") {
|
|
39566
|
+
emitEvent({
|
|
39567
|
+
type: "log",
|
|
39568
|
+
message: `[headless] MCP tools: ${mcp.registered.length} registered`
|
|
39569
|
+
});
|
|
39570
|
+
}
|
|
39571
|
+
for (const w of mcp.warnings) {
|
|
39572
|
+
if (opts.output === "json") {
|
|
39573
|
+
emitEvent({ type: "log", message: `[mcp] ${w}` });
|
|
39574
|
+
} else {
|
|
39575
|
+
process.stderr.write(`[zelari-code --headless] [mcp] ${w}
|
|
39576
|
+
`);
|
|
39577
|
+
}
|
|
39578
|
+
}
|
|
39579
|
+
} catch (err) {
|
|
39580
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
39581
|
+
if (opts.output === "json") {
|
|
39582
|
+
emitEvent({ type: "log", message: `[mcp] registration skipped: ${msg}` });
|
|
39583
|
+
} else {
|
|
39584
|
+
process.stderr.write(`[zelari-code --headless] [mcp] registration skipped: ${msg}
|
|
39585
|
+
`);
|
|
39586
|
+
}
|
|
39587
|
+
}
|
|
39588
|
+
}
|
|
38856
39589
|
async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
38857
39590
|
const sessionId = crypto.randomUUID();
|
|
38858
39591
|
const { registry: toolRegistry } = createBuiltinToolRegistry({
|
|
38859
39592
|
planMode: planModeFromOpts(opts)
|
|
38860
39593
|
});
|
|
39594
|
+
await registerHeadlessMcp(toolRegistry, opts);
|
|
38861
39595
|
const tools = toolRegistry.toOpenAITools().map((t) => ({
|
|
38862
39596
|
name: t.function.name,
|
|
38863
39597
|
description: t.function.description,
|
|
@@ -38894,21 +39628,38 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
38894
39628
|
(opts.phase ?? "build") === "plan" ? "PLAN phase: explore and design only. Do not write project source files (write_file/edit_file/bash blocked). Plan artifacts under .zelari are allowed." : "BUILD phase: full tools; implement changes."
|
|
38895
39629
|
].join("\n")
|
|
38896
39630
|
};
|
|
38897
|
-
|
|
38898
|
-
|
|
38899
|
-
|
|
38900
|
-
|
|
38901
|
-
|
|
38902
|
-
|
|
38903
|
-
|
|
38904
|
-
|
|
38905
|
-
|
|
38906
|
-
|
|
38907
|
-
|
|
38908
|
-
|
|
38909
|
-
|
|
39631
|
+
const { loadProjectInstructions: loadProjectInstructions2 } = await Promise.resolve().then(() => (init_projectInstructions(), projectInstructions_exports));
|
|
39632
|
+
const projectInstructions = loadProjectInstructions2(process.cwd()).content;
|
|
39633
|
+
let sshBlock = "";
|
|
39634
|
+
try {
|
|
39635
|
+
const { formatSshTargetsForPrompt: formatSshTargetsForPrompt2 } = await Promise.resolve().then(() => (init_targets(), targets_exports));
|
|
39636
|
+
sshBlock = formatSshTargetsForPrompt2();
|
|
39637
|
+
} catch {
|
|
39638
|
+
}
|
|
39639
|
+
const rolePrompt = [headlessRole.systemPrompt, sshBlock].filter(Boolean).join("\n\n");
|
|
39640
|
+
systemPrompt = buildSystemPrompt(
|
|
39641
|
+
{ ...headlessRole, systemPrompt: rolePrompt },
|
|
39642
|
+
{
|
|
39643
|
+
tools: getAllTools(),
|
|
39644
|
+
toolNames,
|
|
39645
|
+
mode: "agent",
|
|
39646
|
+
projectInstructions: projectInstructions || void 0,
|
|
39647
|
+
aiConfig: {
|
|
39648
|
+
enabledSkills: [],
|
|
39649
|
+
enabledTools: toolNames,
|
|
39650
|
+
customPromptModules: [
|
|
39651
|
+
SINGLE_AGENT_IDENTITY_MODULE,
|
|
39652
|
+
{
|
|
39653
|
+
type: "language-policy",
|
|
39654
|
+
title: "Response Language",
|
|
39655
|
+
priority: 5,
|
|
39656
|
+
content: languageDirectiveContent
|
|
39657
|
+
}
|
|
39658
|
+
],
|
|
39659
|
+
agentSkillConfigs: []
|
|
39660
|
+
}
|
|
38910
39661
|
}
|
|
38911
|
-
|
|
39662
|
+
);
|
|
38912
39663
|
} catch {
|
|
38913
39664
|
systemPrompt = [
|
|
38914
39665
|
"You are zelari-code, a CLI coding agent. Be concise and direct.",
|
|
@@ -38917,13 +39668,19 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
38917
39668
|
languageDirectiveContent
|
|
38918
39669
|
].join("\n");
|
|
38919
39670
|
}
|
|
39671
|
+
const historySeed = (opts.history ?? []).map(
|
|
39672
|
+
(m) => m.role === "assistant" && m.content ? { ...m, content: cleanAgentContent(m.content, { stripQuestion: false }) } : m
|
|
39673
|
+
);
|
|
39674
|
+
const effectiveTask = maybeAnchorShortAnswer(opts.task) ?? opts.task;
|
|
39675
|
+
const historySeedLen = historySeed.length;
|
|
38920
39676
|
const harness = new AgentHarness({
|
|
38921
39677
|
model,
|
|
38922
39678
|
provider,
|
|
38923
39679
|
sessionId,
|
|
38924
39680
|
messages: [
|
|
38925
39681
|
{ role: "system", content: systemPrompt },
|
|
38926
|
-
|
|
39682
|
+
...historySeed,
|
|
39683
|
+
{ role: "user", content: effectiveTask }
|
|
38927
39684
|
],
|
|
38928
39685
|
tools,
|
|
38929
39686
|
toolRegistry,
|
|
@@ -38936,23 +39693,39 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
38936
39693
|
let finalReason = "completed";
|
|
38937
39694
|
let exitCode = 0;
|
|
38938
39695
|
const textBuffer = [];
|
|
39696
|
+
const scrub = createStreamScrubber();
|
|
38939
39697
|
try {
|
|
38940
39698
|
for await (const event of harness.run()) {
|
|
38941
|
-
if (
|
|
38942
|
-
|
|
39699
|
+
if (event.type === "message_start") {
|
|
39700
|
+
scrub.reset();
|
|
38943
39701
|
}
|
|
38944
|
-
if (event.type === "message_delta") {
|
|
38945
|
-
|
|
38946
|
-
|
|
39702
|
+
if (event.type === "message_delta" && typeof event.delta === "string") {
|
|
39703
|
+
const cleanDelta = scrub.push(event.delta);
|
|
39704
|
+
if (opts.output === "json") {
|
|
39705
|
+
if (cleanDelta.length > 0) {
|
|
39706
|
+
emitEvent({ ...event, delta: cleanDelta });
|
|
39707
|
+
}
|
|
39708
|
+
} else if (opts.output === "plain") {
|
|
39709
|
+
if (cleanDelta.length > 0) process.stdout.write(cleanDelta);
|
|
38947
39710
|
} else {
|
|
38948
|
-
textBuffer.push(
|
|
39711
|
+
if (cleanDelta.length > 0) textBuffer.push(cleanDelta);
|
|
38949
39712
|
}
|
|
38950
|
-
} else
|
|
38951
|
-
|
|
38952
|
-
|
|
38953
|
-
|
|
38954
|
-
if (event.
|
|
38955
|
-
|
|
39713
|
+
} else {
|
|
39714
|
+
if (opts.output === "json") {
|
|
39715
|
+
emitEvent(event);
|
|
39716
|
+
}
|
|
39717
|
+
if (event.type === "agent_end") {
|
|
39718
|
+
const tail = scrub.flush();
|
|
39719
|
+
if (tail.length > 0) {
|
|
39720
|
+
if (opts.output === "plain") process.stdout.write(tail);
|
|
39721
|
+
else textBuffer.push(tail);
|
|
39722
|
+
}
|
|
39723
|
+
finalReason = event.reason;
|
|
39724
|
+
if (event.reason === "error") exitCode = 3;
|
|
39725
|
+
} else if (event.type === "error") {
|
|
39726
|
+
if (event.severity === "fatal") {
|
|
39727
|
+
exitCode = 2;
|
|
39728
|
+
}
|
|
38956
39729
|
}
|
|
38957
39730
|
}
|
|
38958
39731
|
}
|
|
@@ -38967,10 +39740,25 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
38967
39740
|
process.stdout.write(textBuffer.join(""));
|
|
38968
39741
|
}
|
|
38969
39742
|
process.stdout.write("");
|
|
39743
|
+
if (finalReason !== "error" && opts.output === "json") {
|
|
39744
|
+
try {
|
|
39745
|
+
const all = harness.getMessages();
|
|
39746
|
+
const seedLen = 1 + historySeedLen + 1;
|
|
39747
|
+
if (all.length > seedLen) {
|
|
39748
|
+
const tail = all.slice(seedLen).map(
|
|
39749
|
+
(m) => m.role === "assistant" && m.content ? { ...m, content: cleanAgentContent(m.content, { stripQuestion: false }) } : m
|
|
39750
|
+
);
|
|
39751
|
+
if (tail.length > 0) {
|
|
39752
|
+
emitEvent({ type: "history_snapshot", messages: tail });
|
|
39753
|
+
}
|
|
39754
|
+
}
|
|
39755
|
+
} catch {
|
|
39756
|
+
}
|
|
39757
|
+
}
|
|
38970
39758
|
if (finalReason === "error") return 3;
|
|
38971
39759
|
return exitCode;
|
|
38972
39760
|
}
|
|
38973
|
-
async function buildCouncilToolRegistry(planMode) {
|
|
39761
|
+
async function buildCouncilToolRegistry(planMode, opts) {
|
|
38974
39762
|
const { registry: toolRegistry } = createBuiltinToolRegistry({ planMode });
|
|
38975
39763
|
const { createWorkspaceContext: createWorkspaceContext2, createWorkspaceStubs: createWorkspaceStubs2 } = await Promise.resolve().then(() => (init_stubs(), stubs_exports));
|
|
38976
39764
|
const { createWorkspaceToolRegistry: createWorkspaceToolRegistry2 } = await Promise.resolve().then(() => (init_toolRegistry2(), toolRegistry_exports2));
|
|
@@ -38982,17 +39770,30 @@ async function buildCouncilToolRegistry(planMode) {
|
|
|
38982
39770
|
if (td) toolRegistry.register(td);
|
|
38983
39771
|
}
|
|
38984
39772
|
setWorkspaceStubs2(createWorkspaceStubs2(realCtx));
|
|
39773
|
+
if (opts) {
|
|
39774
|
+
await registerHeadlessMcp(toolRegistry, opts);
|
|
39775
|
+
}
|
|
38985
39776
|
return { toolRegistry, workspaceCtx: realCtx };
|
|
38986
39777
|
}
|
|
38987
39778
|
async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
38988
39779
|
const { dispatchCouncil: dispatchCouncil2 } = await Promise.resolve().then(() => (init_councilDispatcher(), councilDispatcher_exports));
|
|
38989
39780
|
const sessionId = crypto.randomUUID();
|
|
38990
|
-
const { toolRegistry } = await buildCouncilToolRegistry(
|
|
39781
|
+
const { toolRegistry } = await buildCouncilToolRegistry(
|
|
39782
|
+
planModeFromOpts(opts),
|
|
39783
|
+
opts
|
|
39784
|
+
);
|
|
38991
39785
|
const { FeedbackStore: FeedbackStore2 } = await Promise.resolve().then(() => (init_councilFeedback(), councilFeedback_exports));
|
|
38992
39786
|
const feedbackStore = new FeedbackStore2();
|
|
39787
|
+
const historySeed = (opts.history ?? []).map(
|
|
39788
|
+
(m) => m.role === "assistant" && m.content ? { ...m, content: cleanAgentContent(m.content, { stripQuestion: false }) } : m
|
|
39789
|
+
);
|
|
39790
|
+
const effectiveTask = buildCouncilTaskWithHistory(opts.task, historySeed);
|
|
38993
39791
|
let exitCode = 0;
|
|
39792
|
+
const scrub = createStreamScrubber();
|
|
39793
|
+
let lastAssistantText = "";
|
|
39794
|
+
let currentAssistantText = "";
|
|
38994
39795
|
try {
|
|
38995
|
-
for await (const event of dispatchCouncil2(
|
|
39796
|
+
for await (const event of dispatchCouncil2(effectiveTask, {
|
|
38996
39797
|
apiKey: "REDACTED",
|
|
38997
39798
|
model,
|
|
38998
39799
|
provider: "openai-compatible",
|
|
@@ -39002,14 +39803,35 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
|
39002
39803
|
feedbackStore,
|
|
39003
39804
|
runMode: planModeFromOpts(opts) ? "design-phase" : "implementation"
|
|
39004
39805
|
})) {
|
|
39005
|
-
if (
|
|
39006
|
-
|
|
39007
|
-
|
|
39008
|
-
|
|
39009
|
-
|
|
39010
|
-
|
|
39011
|
-
|
|
39012
|
-
|
|
39806
|
+
if (event.type === "message_start") {
|
|
39807
|
+
scrub.reset();
|
|
39808
|
+
currentAssistantText = "";
|
|
39809
|
+
}
|
|
39810
|
+
if (event.type === "message_delta" && typeof event.delta === "string") {
|
|
39811
|
+
const cleanDelta = scrub.push(event.delta);
|
|
39812
|
+
if (cleanDelta.length > 0) currentAssistantText += cleanDelta;
|
|
39813
|
+
if (opts.output === "json") {
|
|
39814
|
+
if (cleanDelta.length > 0) emitEvent({ ...event, delta: cleanDelta });
|
|
39815
|
+
} else if (opts.output === "plain" && cleanDelta.length > 0) {
|
|
39816
|
+
process.stdout.write(cleanDelta);
|
|
39817
|
+
}
|
|
39818
|
+
} else {
|
|
39819
|
+
if (opts.output === "json") emitEvent(event);
|
|
39820
|
+
if (event.type === "message_end" || event.type === "agent_end") {
|
|
39821
|
+
const tail = scrub.flush();
|
|
39822
|
+
if (tail.length > 0) {
|
|
39823
|
+
currentAssistantText += tail;
|
|
39824
|
+
if (opts.output === "plain") process.stdout.write(tail);
|
|
39825
|
+
}
|
|
39826
|
+
if (currentAssistantText.trim()) {
|
|
39827
|
+
lastAssistantText = currentAssistantText.trim();
|
|
39828
|
+
}
|
|
39829
|
+
if (event.type === "agent_end" && event.reason === "error") {
|
|
39830
|
+
exitCode = 3;
|
|
39831
|
+
}
|
|
39832
|
+
} else if (event.type === "error" && event.severity === "fatal") {
|
|
39833
|
+
exitCode = 2;
|
|
39834
|
+
}
|
|
39013
39835
|
}
|
|
39014
39836
|
}
|
|
39015
39837
|
} catch (err) {
|
|
@@ -39019,6 +39841,16 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
|
39019
39841
|
);
|
|
39020
39842
|
return 2;
|
|
39021
39843
|
}
|
|
39844
|
+
if (opts.output === "json") {
|
|
39845
|
+
try {
|
|
39846
|
+
const snapshot = [
|
|
39847
|
+
{ role: "user", content: opts.task },
|
|
39848
|
+
...lastAssistantText ? [{ role: "assistant", content: lastAssistantText }] : []
|
|
39849
|
+
];
|
|
39850
|
+
emitEvent({ type: "history_snapshot", messages: snapshot });
|
|
39851
|
+
} catch {
|
|
39852
|
+
}
|
|
39853
|
+
}
|
|
39022
39854
|
return exitCode;
|
|
39023
39855
|
}
|
|
39024
39856
|
async function runHeadlessZelari(opts, provider, model, providerStream) {
|
|
@@ -39035,7 +39867,10 @@ async function runHeadlessZelari(opts, provider, model, providerStream) {
|
|
|
39035
39867
|
hasPlan: hasWorkspacePlan2(projectRoot)
|
|
39036
39868
|
});
|
|
39037
39869
|
const memory = await getMemoryBackend2(projectRoot);
|
|
39038
|
-
const { toolRegistry, workspaceCtx } = await buildCouncilToolRegistry(
|
|
39870
|
+
const { toolRegistry, workspaceCtx } = await buildCouncilToolRegistry(
|
|
39871
|
+
planModeFromOpts(opts),
|
|
39872
|
+
opts
|
|
39873
|
+
);
|
|
39039
39874
|
const feedbackStore = new FeedbackStore2();
|
|
39040
39875
|
const chairmanBudget = envNumber(process.env.ZELARI_MODE_MAX_TOOLS_LUCIFER, {
|
|
39041
39876
|
default: 30,
|
|
@@ -39048,11 +39883,16 @@ async function runHeadlessZelari(opts, provider, model, providerStream) {
|
|
|
39048
39883
|
process.stderr.write(message + "\n");
|
|
39049
39884
|
}
|
|
39050
39885
|
};
|
|
39886
|
+
const historySeed = (opts.history ?? []).map(
|
|
39887
|
+
(m) => m.role === "assistant" && m.content ? { ...m, content: cleanAgentContent(m.content, { stripQuestion: false }) } : m
|
|
39888
|
+
);
|
|
39889
|
+
const missionTask = buildCouncilTaskWithHistory(opts.task, historySeed);
|
|
39051
39890
|
emit(`[zelari] mission brief
|
|
39052
39891
|
${JSON.stringify({ deliverable: brief.deliverableThisMission, mvp: brief.sliceMvp?.title }, null, 0)}`);
|
|
39053
39892
|
let exitCode = 0;
|
|
39893
|
+
let lastMissionAssistant = "";
|
|
39054
39894
|
try {
|
|
39055
|
-
const state2 = await runZelariMission2(
|
|
39895
|
+
const state2 = await runZelariMission2(missionTask, brief, {
|
|
39056
39896
|
projectRoot,
|
|
39057
39897
|
memory,
|
|
39058
39898
|
emit,
|
|
@@ -39066,6 +39906,7 @@ ${ragContext}` : slicePrompt;
|
|
|
39066
39906
|
let writeCount = 0;
|
|
39067
39907
|
let chairmanErrored = false;
|
|
39068
39908
|
let membersCompleted = 0;
|
|
39909
|
+
const scrub = createStreamScrubber();
|
|
39069
39910
|
for await (const event of dispatchCouncil2(fullPrompt, {
|
|
39070
39911
|
apiKey: "REDACTED",
|
|
39071
39912
|
model,
|
|
@@ -39077,13 +39918,21 @@ ${ragContext}` : slicePrompt;
|
|
|
39077
39918
|
runMode: planModeFromOpts(opts) ? "design-phase" : runMode,
|
|
39078
39919
|
maxToolCallsChairman: chairmanBudget
|
|
39079
39920
|
})) {
|
|
39080
|
-
if (
|
|
39081
|
-
|
|
39082
|
-
} else if (event.type === "message_delta") {
|
|
39083
|
-
process.stdout.write(event.delta);
|
|
39921
|
+
if (event.type === "message_start") {
|
|
39922
|
+
scrub.reset();
|
|
39084
39923
|
}
|
|
39085
39924
|
if (event.type === "message_delta" && typeof event.delta === "string") {
|
|
39086
39925
|
synthesisText += event.delta;
|
|
39926
|
+
const cleanDelta = scrub.push(event.delta);
|
|
39927
|
+
if (opts.output === "json") {
|
|
39928
|
+
if (cleanDelta.length > 0) emitEvent({ ...event, delta: cleanDelta });
|
|
39929
|
+
} else if (opts.output === "plain" && cleanDelta.length > 0) {
|
|
39930
|
+
process.stdout.write(cleanDelta);
|
|
39931
|
+
}
|
|
39932
|
+
} else {
|
|
39933
|
+
if (opts.output === "json") {
|
|
39934
|
+
emitEvent(event);
|
|
39935
|
+
}
|
|
39087
39936
|
}
|
|
39088
39937
|
if (event.type === "tool_execution_end") {
|
|
39089
39938
|
const name = event.toolName ?? event.name ?? "";
|
|
@@ -39125,6 +39974,11 @@ ${ragContext}` : slicePrompt;
|
|
|
39125
39974
|
}
|
|
39126
39975
|
} catch {
|
|
39127
39976
|
}
|
|
39977
|
+
if (synthesisText.trim()) {
|
|
39978
|
+
lastMissionAssistant = cleanAgentContent(synthesisText, {
|
|
39979
|
+
stripQuestion: false
|
|
39980
|
+
});
|
|
39981
|
+
}
|
|
39128
39982
|
return {
|
|
39129
39983
|
completionOk,
|
|
39130
39984
|
ran: membersCompleted > 0 || synthesisText.length > 0,
|
|
@@ -39146,6 +40000,18 @@ ${ragContext}` : slicePrompt;
|
|
|
39146
40000
|
} finally {
|
|
39147
40001
|
await memory.close().catch(() => void 0);
|
|
39148
40002
|
}
|
|
40003
|
+
if (opts.output === "json") {
|
|
40004
|
+
try {
|
|
40005
|
+
emitEvent({
|
|
40006
|
+
type: "history_snapshot",
|
|
40007
|
+
messages: [
|
|
40008
|
+
{ role: "user", content: opts.task },
|
|
40009
|
+
...lastMissionAssistant ? [{ role: "assistant", content: lastMissionAssistant }] : []
|
|
40010
|
+
]
|
|
40011
|
+
});
|
|
40012
|
+
} catch {
|
|
40013
|
+
}
|
|
40014
|
+
}
|
|
39149
40015
|
return exitCode;
|
|
39150
40016
|
}
|
|
39151
40017
|
|
|
@@ -39396,9 +40262,9 @@ async function runDiscoverModels(providerArg) {
|
|
|
39396
40262
|
|
|
39397
40263
|
// src/cli/skillsMd.ts
|
|
39398
40264
|
init_skills2();
|
|
39399
|
-
import { existsSync as
|
|
39400
|
-
import { join as
|
|
39401
|
-
import { homedir as
|
|
40265
|
+
import { existsSync as existsSync33, readdirSync as readdirSync4, readFileSync as readFileSync28 } from "node:fs";
|
|
40266
|
+
import { join as join25 } from "node:path";
|
|
40267
|
+
import { homedir as homedir7 } from "node:os";
|
|
39402
40268
|
var CODING_CATEGORIES = /* @__PURE__ */ new Set([
|
|
39403
40269
|
"plan",
|
|
39404
40270
|
"refactor",
|
|
@@ -39413,10 +40279,10 @@ var CODING_CATEGORIES = /* @__PURE__ */ new Set([
|
|
|
39413
40279
|
]);
|
|
39414
40280
|
function skillMdSearchDirs(projectRoot = process.cwd()) {
|
|
39415
40281
|
return [
|
|
39416
|
-
|
|
39417
|
-
|
|
39418
|
-
|
|
39419
|
-
|
|
40282
|
+
join25(projectRoot, ".zelari", "skills"),
|
|
40283
|
+
join25(projectRoot, ".claude", "skills"),
|
|
40284
|
+
join25(projectRoot, ".opencode", "skills"),
|
|
40285
|
+
join25(homedir7(), ".zelari-code", "skills")
|
|
39420
40286
|
];
|
|
39421
40287
|
}
|
|
39422
40288
|
function parseSkillMd(content, sourcePath) {
|
|
@@ -39474,7 +40340,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
39474
40340
|
const summary = { loaded: [], skipped: [] };
|
|
39475
40341
|
const seen = new Set(options.existingIds ?? []);
|
|
39476
40342
|
for (const dir of skillMdSearchDirs(projectRoot)) {
|
|
39477
|
-
if (!
|
|
40343
|
+
if (!existsSync33(dir)) continue;
|
|
39478
40344
|
let entries;
|
|
39479
40345
|
try {
|
|
39480
40346
|
entries = readdirSync4(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -39482,10 +40348,10 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
39482
40348
|
continue;
|
|
39483
40349
|
}
|
|
39484
40350
|
for (const entry of entries) {
|
|
39485
|
-
const skillPath =
|
|
39486
|
-
if (!
|
|
40351
|
+
const skillPath = join25(dir, entry, "SKILL.md");
|
|
40352
|
+
if (!existsSync33(skillPath)) continue;
|
|
39487
40353
|
try {
|
|
39488
|
-
const parsed = parseSkillMd(
|
|
40354
|
+
const parsed = parseSkillMd(readFileSync28(skillPath, "utf8"), skillPath);
|
|
39489
40355
|
if (!parsed) {
|
|
39490
40356
|
summary.skipped.push({ path: skillPath, reason: "missing/invalid frontmatter (name, description) or empty body" });
|
|
39491
40357
|
continue;
|
|
@@ -39508,6 +40374,116 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
39508
40374
|
// src/cli/main.ts
|
|
39509
40375
|
init_skills2();
|
|
39510
40376
|
init_updater();
|
|
40377
|
+
|
|
40378
|
+
// src/cli/mcp/mcpConfigIo.ts
|
|
40379
|
+
import {
|
|
40380
|
+
existsSync as existsSync34,
|
|
40381
|
+
mkdirSync as mkdirSync14,
|
|
40382
|
+
readFileSync as readFileSync29,
|
|
40383
|
+
writeFileSync as writeFileSync18
|
|
40384
|
+
} from "node:fs";
|
|
40385
|
+
import { dirname as dirname6, join as join26 } from "node:path";
|
|
40386
|
+
import { homedir as homedir8 } from "node:os";
|
|
40387
|
+
function getUserMcpPath() {
|
|
40388
|
+
return join26(homedir8(), ".zelari-code", "mcp.json");
|
|
40389
|
+
}
|
|
40390
|
+
function getProjectMcpPath(projectRoot) {
|
|
40391
|
+
return join26(projectRoot, ".zelari", "mcp.json");
|
|
40392
|
+
}
|
|
40393
|
+
function readFile3(path36) {
|
|
40394
|
+
if (!existsSync34(path36)) return {};
|
|
40395
|
+
try {
|
|
40396
|
+
const parsed = JSON.parse(readFileSync29(path36, "utf8"));
|
|
40397
|
+
const out = {};
|
|
40398
|
+
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
40399
|
+
if (!cfg || typeof cfg.command !== "string" || !cfg.command.trim()) continue;
|
|
40400
|
+
out[name] = {
|
|
40401
|
+
command: cfg.command.trim(),
|
|
40402
|
+
args: Array.isArray(cfg.args) ? cfg.args.map(String) : void 0,
|
|
40403
|
+
env: cfg.env && typeof cfg.env === "object" ? cfg.env : void 0,
|
|
40404
|
+
enabled: cfg.enabled !== false
|
|
40405
|
+
};
|
|
40406
|
+
}
|
|
40407
|
+
return out;
|
|
40408
|
+
} catch {
|
|
40409
|
+
return {};
|
|
40410
|
+
}
|
|
40411
|
+
}
|
|
40412
|
+
function writeFile(path36, servers) {
|
|
40413
|
+
mkdirSync14(dirname6(path36), { recursive: true });
|
|
40414
|
+
const body = { mcpServers: servers };
|
|
40415
|
+
writeFileSync18(path36, `${JSON.stringify(body, null, 2)}
|
|
40416
|
+
`, "utf8");
|
|
40417
|
+
}
|
|
40418
|
+
function listMcpServers(projectRoot) {
|
|
40419
|
+
const userPath = getUserMcpPath();
|
|
40420
|
+
const userServers = readFile3(userPath);
|
|
40421
|
+
const projectPath = projectRoot && projectRoot.trim() ? getProjectMcpPath(projectRoot.trim()) : null;
|
|
40422
|
+
const projectServers = projectPath ? readFile3(projectPath) : {};
|
|
40423
|
+
const servers = [];
|
|
40424
|
+
for (const [name, cfg] of Object.entries(userServers)) {
|
|
40425
|
+
servers.push({ name, ...cfg, scope: "user", path: userPath });
|
|
40426
|
+
}
|
|
40427
|
+
for (const [name, cfg] of Object.entries(projectServers)) {
|
|
40428
|
+
servers.push({ name, ...cfg, scope: "project", path: projectPath });
|
|
40429
|
+
}
|
|
40430
|
+
servers.sort((a, b) => a.name.localeCompare(b.name));
|
|
40431
|
+
const merged = {
|
|
40432
|
+
...userServers,
|
|
40433
|
+
...projectServers
|
|
40434
|
+
};
|
|
40435
|
+
return { userPath, projectPath, servers, merged };
|
|
40436
|
+
}
|
|
40437
|
+
function upsertMcpServer(opts) {
|
|
40438
|
+
const name = opts.name.trim();
|
|
40439
|
+
if (!name || !/^[a-zA-Z0-9_-]+$/.test(name)) {
|
|
40440
|
+
return {
|
|
40441
|
+
ok: false,
|
|
40442
|
+
error: "Invalid server name (use letters, digits, _ -)"
|
|
40443
|
+
};
|
|
40444
|
+
}
|
|
40445
|
+
if (!opts.config.command?.trim()) {
|
|
40446
|
+
return { ok: false, error: "command is required" };
|
|
40447
|
+
}
|
|
40448
|
+
let path36;
|
|
40449
|
+
if (opts.scope === "user") {
|
|
40450
|
+
path36 = getUserMcpPath();
|
|
40451
|
+
} else {
|
|
40452
|
+
const root = opts.projectRoot?.trim();
|
|
40453
|
+
if (!root) {
|
|
40454
|
+
return {
|
|
40455
|
+
ok: false,
|
|
40456
|
+
error: "projectRoot required for project scope (Open Folder first)"
|
|
40457
|
+
};
|
|
40458
|
+
}
|
|
40459
|
+
path36 = getProjectMcpPath(root);
|
|
40460
|
+
}
|
|
40461
|
+
const current = readFile3(path36);
|
|
40462
|
+
current[name] = {
|
|
40463
|
+
command: opts.config.command.trim(),
|
|
40464
|
+
args: opts.config.args,
|
|
40465
|
+
env: opts.config.env,
|
|
40466
|
+
enabled: opts.config.enabled !== false
|
|
40467
|
+
};
|
|
40468
|
+
writeFile(path36, current);
|
|
40469
|
+
return { ok: true, path: path36 };
|
|
40470
|
+
}
|
|
40471
|
+
function removeMcpServer(opts) {
|
|
40472
|
+
const path36 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
|
|
40473
|
+
if (!path36) {
|
|
40474
|
+
return { ok: false, error: "projectRoot required for project scope" };
|
|
40475
|
+
}
|
|
40476
|
+
const current = readFile3(path36);
|
|
40477
|
+
if (!(opts.name in current)) {
|
|
40478
|
+
return { ok: false, error: `Server "${opts.name}" not found in ${path36}` };
|
|
40479
|
+
}
|
|
40480
|
+
delete current[opts.name];
|
|
40481
|
+
writeFile(path36, current);
|
|
40482
|
+
return { ok: true, path: path36 };
|
|
40483
|
+
}
|
|
40484
|
+
|
|
40485
|
+
// src/cli/main.ts
|
|
40486
|
+
init_targets();
|
|
39511
40487
|
var VERSION = getCurrentVersion();
|
|
39512
40488
|
function runPreflight() {
|
|
39513
40489
|
if (process.env.ZELARI_SKIP_PREFLIGHT === "1") return;
|
|
@@ -39604,10 +40580,190 @@ function pickRootComponent() {
|
|
|
39604
40580
|
}
|
|
39605
40581
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
39606
40582
|
console.log(
|
|
39607
|
-
"zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor Diagnose install health (shim, bundle, PATH, deps,\n node/git/bash in the agent shell)\n --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\n --skip-checks Skip the boot-time prerequisite check\n (alias for ZELARI_SKIP_PREFLIGHT=1)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --mode agent|council|zelari Dispatch mode (default: agent)\n --council Alias for --mode council\n --phase plan|build Work phase (default: build)\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n --print-config Print provider/model config as JSON (no secrets)\n --set-config Persist provider/model/endpoint\n --provider <id> Set active provider\n --model <id> Set model for that provider\n --endpoint <url> Custom OpenAI-compatible base URL\n --endpoint-clear Remove custom endpoint override\n --set-key Store an API key (never printed back)\n --provider <id> Provider id (required)\n --key <secret> API key (required)\n --discover-models Refresh model list for a provider\n --provider <id> Provider (default: active)\n\nEnvironment:\n ZELARI_NO_WIZARD=1 Skip the first-run wizard\n ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n ZELARI_NO_PLUGIN_PROMPT=1 Skip the boot plugin-install prompt\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
|
|
40583
|
+
"zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor Diagnose install health (shim, bundle, PATH, deps,\n node/git/bash in the agent shell)\n --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\n --skip-checks Skip the boot-time prerequisite check\n (alias for ZELARI_SKIP_PREFLIGHT=1)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --mode agent|council|zelari Dispatch mode (default: agent)\n --council Alias for --mode council\n --phase plan|build Work phase (default: build)\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n --print-config Print provider/model config as JSON (no secrets)\n --set-config Persist provider/model/endpoint\n --provider <id> Set active provider\n --model <id> Set model for that provider\n --endpoint <url> Custom OpenAI-compatible base URL\n --endpoint-clear Remove custom endpoint override\n --set-key Store an API key (never printed back)\n --provider <id> Provider id (required)\n --key <secret> API key (required)\n --discover-models Refresh model list for a provider\n --provider <id> Provider (default: active)\n --print-mcp Print MCP server config (user + project)\n --cwd <path> Project root for .zelari/mcp.json\n --set-mcp Add/update an MCP server entry\n --name <id> Server name (required)\n --command <bin> Executable (required)\n --args <json> JSON array of args (optional)\n --scope user|project Default: user\n --enabled true|false Default: true\n --cwd <path> Required when scope=project\n --remove-mcp Remove an MCP server entry\n --name <id> --scope user|project [--cwd <path>]\n --print-ssh-targets Print SSH deploy/monitor targets\n --set-ssh-target Upsert target (--json '{...}' or flags)\n --remove-ssh-target --id <id>\n --test-ssh-target --id <id> (BatchMode ssh true)\n --print-ssh-pubkey --path <private-or-.pub> (display public key)\n\nEnvironment:\n ZELARI_NO_WIZARD=1 Skip the first-run wizard\n ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n ZELARI_NO_PLUGIN_PROMPT=1 Skip the boot plugin-install prompt\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
|
|
39608
40584
|
);
|
|
39609
40585
|
process.exit(0);
|
|
39610
40586
|
}
|
|
40587
|
+
if (argv.includes("--print-mcp")) {
|
|
40588
|
+
try {
|
|
40589
|
+
const cwdIdx = argv.indexOf("--cwd");
|
|
40590
|
+
const cwd = cwdIdx >= 0 && argv[cwdIdx + 1] ? argv[cwdIdx + 1] : process.cwd();
|
|
40591
|
+
const snap = listMcpServers(cwd);
|
|
40592
|
+
console.log(JSON.stringify(snap, null, 2));
|
|
40593
|
+
process.exit(0);
|
|
40594
|
+
} catch (err) {
|
|
40595
|
+
console.error(
|
|
40596
|
+
`[zelari-code --print-mcp] ${err instanceof Error ? err.message : String(err)}`
|
|
40597
|
+
);
|
|
40598
|
+
process.exit(1);
|
|
40599
|
+
}
|
|
40600
|
+
}
|
|
40601
|
+
if (argv.includes("--set-mcp")) {
|
|
40602
|
+
try {
|
|
40603
|
+
const get = (flag) => {
|
|
40604
|
+
const i = argv.indexOf(flag);
|
|
40605
|
+
return i >= 0 && argv[i + 1] ? argv[i + 1] : void 0;
|
|
40606
|
+
};
|
|
40607
|
+
const name = get("--name");
|
|
40608
|
+
const command = get("--command");
|
|
40609
|
+
const scopeRaw = get("--scope") ?? "user";
|
|
40610
|
+
const scope = scopeRaw === "project" ? "project" : "user";
|
|
40611
|
+
const cwd = get("--cwd") ?? process.cwd();
|
|
40612
|
+
const enabledRaw = get("--enabled");
|
|
40613
|
+
const enabled = enabledRaw === void 0 ? true : enabledRaw !== "false";
|
|
40614
|
+
let args;
|
|
40615
|
+
const argsRaw = get("--args");
|
|
40616
|
+
if (argsRaw) {
|
|
40617
|
+
const parsed = JSON.parse(argsRaw);
|
|
40618
|
+
if (!Array.isArray(parsed)) throw new Error("--args must be a JSON array");
|
|
40619
|
+
args = parsed.map(String);
|
|
40620
|
+
}
|
|
40621
|
+
if (!name || !command) {
|
|
40622
|
+
throw new Error("--name and --command are required");
|
|
40623
|
+
}
|
|
40624
|
+
const result = upsertMcpServer({
|
|
40625
|
+
scope,
|
|
40626
|
+
name,
|
|
40627
|
+
projectRoot: cwd,
|
|
40628
|
+
config: { command, args, enabled }
|
|
40629
|
+
});
|
|
40630
|
+
if (!result.ok) throw new Error(result.error);
|
|
40631
|
+
console.log(JSON.stringify({ ok: true, path: result.path, name, scope }));
|
|
40632
|
+
process.exit(0);
|
|
40633
|
+
} catch (err) {
|
|
40634
|
+
console.error(
|
|
40635
|
+
`[zelari-code --set-mcp] ${err instanceof Error ? err.message : String(err)}`
|
|
40636
|
+
);
|
|
40637
|
+
process.exit(1);
|
|
40638
|
+
}
|
|
40639
|
+
}
|
|
40640
|
+
if (argv.includes("--remove-mcp")) {
|
|
40641
|
+
try {
|
|
40642
|
+
const get = (flag) => {
|
|
40643
|
+
const i = argv.indexOf(flag);
|
|
40644
|
+
return i >= 0 && argv[i + 1] ? argv[i + 1] : void 0;
|
|
40645
|
+
};
|
|
40646
|
+
const name = get("--name");
|
|
40647
|
+
const scopeRaw = get("--scope") ?? "user";
|
|
40648
|
+
const scope = scopeRaw === "project" ? "project" : "user";
|
|
40649
|
+
const cwd = get("--cwd") ?? process.cwd();
|
|
40650
|
+
if (!name) throw new Error("--name is required");
|
|
40651
|
+
const result = removeMcpServer({ scope, name, projectRoot: cwd });
|
|
40652
|
+
if (!result.ok) throw new Error(result.error);
|
|
40653
|
+
console.log(JSON.stringify({ ok: true, path: result.path, name, scope }));
|
|
40654
|
+
process.exit(0);
|
|
40655
|
+
} catch (err) {
|
|
40656
|
+
console.error(
|
|
40657
|
+
`[zelari-code --remove-mcp] ${err instanceof Error ? err.message : String(err)}`
|
|
40658
|
+
);
|
|
40659
|
+
process.exit(1);
|
|
40660
|
+
}
|
|
40661
|
+
}
|
|
40662
|
+
if (argv.includes("--print-ssh-targets")) {
|
|
40663
|
+
try {
|
|
40664
|
+
console.log(JSON.stringify(listSshTargets(), null, 2));
|
|
40665
|
+
process.exit(0);
|
|
40666
|
+
} catch (err) {
|
|
40667
|
+
console.error(
|
|
40668
|
+
`[zelari-code --print-ssh-targets] ${err instanceof Error ? err.message : String(err)}`
|
|
40669
|
+
);
|
|
40670
|
+
process.exit(1);
|
|
40671
|
+
}
|
|
40672
|
+
}
|
|
40673
|
+
if (argv.includes("--set-ssh-target")) {
|
|
40674
|
+
try {
|
|
40675
|
+
const get = (flag) => {
|
|
40676
|
+
const i = argv.indexOf(flag);
|
|
40677
|
+
return i >= 0 && argv[i + 1] ? argv[i + 1] : void 0;
|
|
40678
|
+
};
|
|
40679
|
+
let target;
|
|
40680
|
+
const jsonRaw = get("--json");
|
|
40681
|
+
if (jsonRaw) {
|
|
40682
|
+
target = JSON.parse(jsonRaw);
|
|
40683
|
+
} else {
|
|
40684
|
+
const id = get("--id");
|
|
40685
|
+
const host = get("--host");
|
|
40686
|
+
const user = get("--user");
|
|
40687
|
+
if (!id || !host || !user) {
|
|
40688
|
+
throw new Error("Need --json or --id --host --user");
|
|
40689
|
+
}
|
|
40690
|
+
const allowedRaw = get("--allowed");
|
|
40691
|
+
const authFlag = get("--auth");
|
|
40692
|
+
const auth = authFlag === "password" || get("--password") ? "password" : get("--key-path") ? "keyPath" : "agent";
|
|
40693
|
+
target = {
|
|
40694
|
+
id,
|
|
40695
|
+
name: get("--name") ?? id,
|
|
40696
|
+
host,
|
|
40697
|
+
user,
|
|
40698
|
+
port: get("--port") ? Number(get("--port")) : 22,
|
|
40699
|
+
auth,
|
|
40700
|
+
keyPath: get("--key-path"),
|
|
40701
|
+
password: get("--password"),
|
|
40702
|
+
defaultRemotePath: get("--remote-path"),
|
|
40703
|
+
allowedCommands: allowedRaw ? allowedRaw.split("|").map((s) => s.trim()).filter(Boolean) : [],
|
|
40704
|
+
enabled: get("--enabled") !== "false"
|
|
40705
|
+
};
|
|
40706
|
+
}
|
|
40707
|
+
const result = upsertSshTarget(target);
|
|
40708
|
+
if (!result.ok) throw new Error(result.error);
|
|
40709
|
+
console.log(JSON.stringify({ ok: true, id: target.id }));
|
|
40710
|
+
process.exit(0);
|
|
40711
|
+
} catch (err) {
|
|
40712
|
+
console.error(
|
|
40713
|
+
`[zelari-code --set-ssh-target] ${err instanceof Error ? err.message : String(err)}`
|
|
40714
|
+
);
|
|
40715
|
+
process.exit(1);
|
|
40716
|
+
}
|
|
40717
|
+
}
|
|
40718
|
+
if (argv.includes("--remove-ssh-target")) {
|
|
40719
|
+
try {
|
|
40720
|
+
const i = argv.indexOf("--id");
|
|
40721
|
+
const id = i >= 0 ? argv[i + 1] : void 0;
|
|
40722
|
+
if (!id) throw new Error("--id is required");
|
|
40723
|
+
const result = removeSshTarget(id);
|
|
40724
|
+
if (!result.ok) throw new Error(result.error);
|
|
40725
|
+
console.log(JSON.stringify({ ok: true, id }));
|
|
40726
|
+
process.exit(0);
|
|
40727
|
+
} catch (err) {
|
|
40728
|
+
console.error(
|
|
40729
|
+
`[zelari-code --remove-ssh-target] ${err instanceof Error ? err.message : String(err)}`
|
|
40730
|
+
);
|
|
40731
|
+
process.exit(1);
|
|
40732
|
+
}
|
|
40733
|
+
}
|
|
40734
|
+
if (argv.includes("--test-ssh-target")) {
|
|
40735
|
+
const i = argv.indexOf("--id");
|
|
40736
|
+
const id = i >= 0 ? argv[i + 1] : void 0;
|
|
40737
|
+
if (!id) {
|
|
40738
|
+
console.error("[zelari-code --test-ssh-target] --id is required");
|
|
40739
|
+
process.exit(1);
|
|
40740
|
+
}
|
|
40741
|
+
void testSshTarget(id).then((result) => {
|
|
40742
|
+
console.log(JSON.stringify(result));
|
|
40743
|
+
process.exit(result.ok ? 0 : 1);
|
|
40744
|
+
}).catch((err) => {
|
|
40745
|
+
console.error(
|
|
40746
|
+
`[zelari-code --test-ssh-target] ${err instanceof Error ? err.message : String(err)}`
|
|
40747
|
+
);
|
|
40748
|
+
process.exit(1);
|
|
40749
|
+
});
|
|
40750
|
+
return { kind: "done" };
|
|
40751
|
+
}
|
|
40752
|
+
if (argv.includes("--print-ssh-pubkey")) {
|
|
40753
|
+
try {
|
|
40754
|
+
const i = argv.indexOf("--path");
|
|
40755
|
+
const p3 = i >= 0 ? argv[i + 1] : void 0;
|
|
40756
|
+
if (!p3) throw new Error("--path is required");
|
|
40757
|
+
const result = readSshPublicKey(p3);
|
|
40758
|
+
console.log(JSON.stringify(result));
|
|
40759
|
+
process.exit(result.ok ? 0 : 1);
|
|
40760
|
+
} catch (err) {
|
|
40761
|
+
console.error(
|
|
40762
|
+
`[zelari-code --print-ssh-pubkey] ${err instanceof Error ? err.message : String(err)}`
|
|
40763
|
+
);
|
|
40764
|
+
process.exit(1);
|
|
40765
|
+
}
|
|
40766
|
+
}
|
|
39611
40767
|
if (wantsPrintConfig(argv)) {
|
|
39612
40768
|
try {
|
|
39613
40769
|
printDesktopConfig();
|