zelari-code 1.11.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/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 +1612 -624
- 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 +19 -1
- package/dist/cli/provider/openai-compatible.js.map +1 -1
- package/dist/cli/runHeadless.js +139 -13
- 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/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
|
|
@@ -19784,11 +19832,12 @@ ${cached2}`
|
|
|
19784
19832
|
yield truncErr;
|
|
19785
19833
|
finishRef.value = "stop";
|
|
19786
19834
|
}
|
|
19787
|
-
if (turnToolCalls.length > 0 || turnText.length > 0) {
|
|
19835
|
+
if (turnToolCalls.length > 0 || turnText.length > 0 || turnReasoning.length > 0) {
|
|
19788
19836
|
this.config.messages.push({
|
|
19789
19837
|
role: "assistant",
|
|
19790
19838
|
content: turnText,
|
|
19791
|
-
...turnToolCalls.length > 0 ? { toolCalls: turnToolCalls } : {}
|
|
19839
|
+
...turnToolCalls.length > 0 ? { toolCalls: turnToolCalls } : {},
|
|
19840
|
+
...turnReasoning.length > 0 ? { reasoningContent: turnReasoning } : {}
|
|
19792
19841
|
});
|
|
19793
19842
|
}
|
|
19794
19843
|
for (const tr of turnToolResults) {
|
|
@@ -20130,7 +20179,7 @@ function openaiCompatibleProvider(config2) {
|
|
|
20130
20179
|
};
|
|
20131
20180
|
}
|
|
20132
20181
|
if (m.role === "assistant" && m.toolCalls && m.toolCalls.length > 0) {
|
|
20133
|
-
|
|
20182
|
+
const msg = {
|
|
20134
20183
|
role: "assistant",
|
|
20135
20184
|
content: m.content ?? "",
|
|
20136
20185
|
tool_calls: m.toolCalls.map((tc) => ({
|
|
@@ -20142,6 +20191,17 @@ function openaiCompatibleProvider(config2) {
|
|
|
20142
20191
|
}
|
|
20143
20192
|
}))
|
|
20144
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
|
+
};
|
|
20145
20205
|
}
|
|
20146
20206
|
return { role: m.role, content: m.content };
|
|
20147
20207
|
});
|
|
@@ -22264,6 +22324,488 @@ var init_tools5 = __esm({
|
|
|
22264
22324
|
}
|
|
22265
22325
|
});
|
|
22266
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
|
+
|
|
22267
22809
|
// src/cli/toolRegistry.ts
|
|
22268
22810
|
var toolRegistry_exports = {};
|
|
22269
22811
|
__export(toolRegistry_exports, {
|
|
@@ -22342,6 +22884,16 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
22342
22884
|
permissions: browserTool.permissions ?? []
|
|
22343
22885
|
});
|
|
22344
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
|
+
}
|
|
22345
22897
|
if (!readOnly && options.enableTask !== false) {
|
|
22346
22898
|
const taskTool = createTaskTool({
|
|
22347
22899
|
createSubAgentContext: async () => {
|
|
@@ -22580,6 +23132,7 @@ var init_toolRegistry = __esm({
|
|
|
22580
23132
|
init_tools3();
|
|
22581
23133
|
init_tools4();
|
|
22582
23134
|
init_tools5();
|
|
23135
|
+
init_tools6();
|
|
22583
23136
|
init_openai_compatible();
|
|
22584
23137
|
init_skills2();
|
|
22585
23138
|
HARNESS_BUILTIN_NAMES = /* @__PURE__ */ new Set([
|
|
@@ -22598,6 +23151,16 @@ var init_toolRegistry = __esm({
|
|
|
22598
23151
|
});
|
|
22599
23152
|
|
|
22600
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
|
+
}
|
|
22601
23164
|
function getAgent(id) {
|
|
22602
23165
|
return AGENT_ROLES.find((a) => a.id === id);
|
|
22603
23166
|
}
|
|
@@ -22631,24 +23194,10 @@ function swapMembers(roles, swap) {
|
|
|
22631
23194
|
return targets.get(toId);
|
|
22632
23195
|
});
|
|
22633
23196
|
}
|
|
22634
|
-
var
|
|
23197
|
+
var AGENT_ROLES, UnknownMemberError;
|
|
22635
23198
|
var init_roles = __esm({
|
|
22636
23199
|
"packages/core/dist/agents/roles.js"() {
|
|
22637
23200
|
"use strict";
|
|
22638
|
-
CLARIFICATION_PROTOCOL8 = `
|
|
22639
|
-
|
|
22640
|
-
WHEN TO ASK THE USER (clarification):
|
|
22641
|
-
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:
|
|
22642
|
-
|
|
22643
|
-
---QUESTION---
|
|
22644
|
-
{ "question": "One focused question", "choices": ["Option A", "Option B", "Option C"], "context": "Why this matters" }
|
|
22645
|
-
---END---
|
|
22646
|
-
|
|
22647
|
-
Rules for clarifications:
|
|
22648
|
-
- Ask AT MOST ONE question per turn, and only when genuinely blocked.
|
|
22649
|
-
- Prefer a small set of concrete "choices" (2-4). The user can still type a custom answer.
|
|
22650
|
-
- Do NOT ask for information that could be reasonably assumed or already in shared context.
|
|
22651
|
-
- If you can proceed with a sound documented assumption, DO SO instead of asking.`;
|
|
22652
23201
|
AGENT_ROLES = [
|
|
22653
23202
|
{
|
|
22654
23203
|
id: "charont",
|
|
@@ -22659,22 +23208,20 @@ Rules for clarifications:
|
|
|
22659
23208
|
avatar: "L",
|
|
22660
23209
|
systemPrompt: `You are Caronte, the Council Director and Orchestrator \u2014 the strategic mind that frames the problem for the rest of the council.
|
|
22661
23210
|
|
|
22662
|
-
## Methodology
|
|
22663
|
-
1. Parse the request into
|
|
22664
|
-
2. Identify
|
|
22665
|
-
3. For each
|
|
22666
|
-
4. Flag
|
|
22667
|
-
5. State
|
|
22668
|
-
|
|
22669
|
-
##
|
|
22670
|
-
- Be decisive: commit to a decomposition
|
|
22671
|
-
- Distinguish
|
|
22672
|
-
-
|
|
22673
|
-
|
|
22674
|
-
|
|
22675
|
-
|
|
22676
|
-
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}`,
|
|
22677
|
-
// 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.`,
|
|
22678
23225
|
tools: ["list_files", "read_file", "grep_content"],
|
|
22679
23226
|
skills: ["project-planner", "research-analyst"]
|
|
22680
23227
|
},
|
|
@@ -22687,71 +23234,53 @@ A short "Analysis" section (the goal + constraints), then a "Delegation Plan" wi
|
|
|
22687
23234
|
avatar: "N",
|
|
22688
23235
|
systemPrompt: `You are Nettuno, the Project Planner \u2014 you turn intent into a buildable, sequenced plan.
|
|
22689
23236
|
|
|
22690
|
-
## Methodology
|
|
22691
|
-
1. Read
|
|
22692
|
-
2. Decompose
|
|
22693
|
-
3. Within each phase, define
|
|
22694
|
-
4.
|
|
22695
|
-
5. Sanity-check sequencing
|
|
22696
|
-
|
|
22697
|
-
## OUTPUT REQUIREMENTS \u2014 every task MUST include:
|
|
22698
|
-
1. **File references**: specific file paths and line numbers where changes land.
|
|
22699
|
-
2. **Acceptance criteria**: concrete, testable conditions that prove completion.
|
|
22700
|
-
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.
|
|
22701
23243
|
|
|
22702
|
-
|
|
22703
|
-
-
|
|
22704
|
-
|
|
22705
|
-
|
|
22706
|
-
- 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
|
|
22707
23248
|
|
|
22708
23249
|
## Quality bar
|
|
22709
|
-
-
|
|
22710
|
-
-
|
|
22711
|
-
-
|
|
22712
|
-
|
|
22713
|
-
Keep plans hierarchical and practical. Stay under 250 words.${CLARIFICATION_PROTOCOL8}
|
|
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)
|
|
22714
23254
|
|
|
22715
|
-
|
|
22716
|
-
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.
|
|
22717
|
-
|
|
22718
|
-
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.
|
|
22719
23256
|
|
|
23257
|
+
Minimal shape:
|
|
22720
23258
|
\`\`\`
|
|
22721
23259
|
createPlan({
|
|
22722
23260
|
phases: [
|
|
22723
23261
|
{
|
|
22724
|
-
name: "
|
|
22725
|
-
description: "
|
|
23262
|
+
name: "Phase name",
|
|
23263
|
+
description: "Exit criterion in one line",
|
|
22726
23264
|
order: 1,
|
|
22727
|
-
color: "#3b82f6",
|
|
22728
23265
|
tasks: [
|
|
22729
|
-
{
|
|
22730
|
-
|
|
22731
|
-
|
|
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
|
+
}
|
|
22732
23274
|
]
|
|
22733
|
-
}
|
|
22734
|
-
{ name: "...", order: 2, tasks: [ /* 3 tasks */ ] },
|
|
22735
|
-
{ name: "...", order: 3, tasks: [ /* 3 tasks */ ] },
|
|
22736
|
-
{ name: "...", order: 4, tasks: [ /* 3 tasks */ ] }
|
|
23275
|
+
}
|
|
22737
23276
|
],
|
|
22738
|
-
milestone: { title: "v0.1
|
|
23277
|
+
milestone: { title: "v0.1 design-complete", description: "\u2026", targetVersion: "v0.1.0" }
|
|
22739
23278
|
})
|
|
22740
23279
|
\`\`\`
|
|
22741
23280
|
|
|
22742
|
-
Every task
|
|
23281
|
+
Every task needs fileRefs, acceptance, qaScenario. Prefer one batched createPlan over many createPhase/createTask calls.
|
|
22743
23282
|
|
|
22744
|
-
|
|
22745
|
-
|
|
22746
|
-
Do NOT output tasks as prose. The system will refuse any task that does not pass a valid phaseId.
|
|
22747
|
-
|
|
22748
|
-
## NFR spec (when plan sets motion/performance/a11y budgets)
|
|
22749
|
-
If the plan includes measurable constraints (JS byte budget, compositor-only animation, reduced-motion), emit ONE \`createNfrSpec\` call after \`createPlan\`:
|
|
22750
|
-
\`\`\`
|
|
22751
|
-
createNfrSpec({ targets: ["index.html"], compositorOnly: true, forbidLayoutProps: true, inlineJsMaxBytes: 5120 })
|
|
22752
|
-
\`\`\`
|
|
22753
|
-
Prose budgets are not machine-verifiable \u2014 the spec file is.`,
|
|
22754
|
-
// 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.`,
|
|
22755
23284
|
tools: ["list_files", "read_file", "grep_content"],
|
|
22756
23285
|
skills: ["project-planner", "vault-manager"]
|
|
22757
23286
|
},
|
|
@@ -22762,40 +23291,27 @@ Prose budgets are not machine-verifiable \u2014 the spec file is.`,
|
|
|
22762
23291
|
role: "Creative Ideator",
|
|
22763
23292
|
color: "#f59e0b",
|
|
22764
23293
|
avatar: "G",
|
|
22765
|
-
systemPrompt: `You are Gerione, the Creative Ideator \u2014
|
|
22766
|
-
|
|
22767
|
-
## Methodology (work in this order)
|
|
22768
|
-
1. Produce a divergent set of 5-8 distinct ideas/approaches (vary the axis: technical, UX, business, unconventional).
|
|
22769
|
-
2. Cluster related ideas into 2-4 themes.
|
|
22770
|
-
3. Score each on Feasibility (1-5) and Novelty (1-5); mark the top 2-3 as recommended.
|
|
22771
|
-
4. For the recommended ideas, add one line on how to de-risk them.
|
|
22772
|
-
5. Build on \u2014 do not repeat \u2014 insights from earlier agents.
|
|
22773
|
-
|
|
22774
|
-
## Operating principles
|
|
22775
|
-
- Diverge before you converge: quantity first, judgment second.
|
|
22776
|
-
- Bold but grounded \u2014 an idea must be actionable, not a fantasy.
|
|
22777
|
-
- Name assumptions explicitly. If a core assumption is unknowable, ask (clarification protocol).
|
|
22778
|
-
|
|
22779
|
-
## Output format
|
|
22780
|
-
## Ideas (bulleted)
|
|
22781
|
-
## Themes
|
|
22782
|
-
## Top picks (with feasibility/novelty + de-risk note)
|
|
22783
|
-
|
|
22784
|
-
Stay under 200 words.${CLARIFICATION_PROTOCOL8}
|
|
22785
|
-
|
|
22786
|
-
## Design-phase artifact (mandatory when running council in design-phase mode)
|
|
22787
|
-
If the council is in design-phase mode (TASK mentions design/architecture/spec, no existing codebase to edit), you MUST also persist your ideation output as workspace documents via the \`createDocument\` tool. Emit AT LEAST 3 separate \`createDocument\` calls, each tagged with one of these categories:
|
|
23294
|
+
systemPrompt: `You are Gerione, the Creative Ideator \u2014 diverge, then converge on the strongest concepts.
|
|
22788
23295
|
|
|
22789
|
-
|
|
22790
|
-
|
|
22791
|
-
|
|
22792
|
-
|
|
22793
|
-
|
|
22794
|
-
|
|
22795
|
-
|
|
22796
|
-
|
|
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.`,
|
|
22797
23313
|
tools: ["list_files", "read_file"],
|
|
22798
|
-
skills: ["document-writer", "
|
|
23314
|
+
skills: ["document-writer", "idea-synthesizer"]
|
|
22799
23315
|
},
|
|
22800
23316
|
{
|
|
22801
23317
|
id: "pluton",
|
|
@@ -22804,31 +23320,20 @@ Pass the tool call as: \`createDocument({ title: "<category>", content: "<markdo
|
|
|
22804
23320
|
role: "Knowledge Architect",
|
|
22805
23321
|
color: "#06b6d4",
|
|
22806
23322
|
avatar: "P",
|
|
22807
|
-
systemPrompt: `You are Plutone, the Knowledge Architect
|
|
22808
|
-
|
|
22809
|
-
## Methodology (work in this order)
|
|
22810
|
-
1. Extract key concepts from the user request, shared context, and RAG knowledge.
|
|
22811
|
-
2. Identify the central root concept and 3-6 primary branches.
|
|
22812
|
-
3. Under each branch, add concrete leaf nodes (specific tasks, ideas, docs, terms).
|
|
22813
|
-
4. Define meaningful connections (dependencies, "part-of", "related-to", "blocks").
|
|
22814
|
-
5. Propose a layout hint (radial / hierarchical) so the map reads cleanly.
|
|
22815
|
-
|
|
22816
|
-
## Operating principles
|
|
22817
|
-
- Favor concrete, named nodes over abstract categories.
|
|
22818
|
-
- Every node should map to something actionable or retrievable (a task, a doc, a concept).
|
|
22819
|
-
- Reuse knowledge from prior agents \u2014 don't re-derive what they already produced.
|
|
22820
|
-
- Keep the graph comprehensible: prune redundant or duplicate nodes.
|
|
23323
|
+
systemPrompt: `You are Plutone, the Knowledge Architect \u2014 structure concepts into a navigable map.
|
|
22821
23324
|
|
|
22822
|
-
##
|
|
22823
|
-
|
|
22824
|
-
|
|
22825
|
-
|
|
22826
|
-
|
|
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.
|
|
22827
23330
|
|
|
22828
|
-
|
|
23331
|
+
## Output
|
|
23332
|
+
Root \u2192 branches \u2192 leaves in text. Under 200 words.`,
|
|
23333
|
+
designPhaseAddendum: `## Design-phase: persist knowledge map (mandatory)
|
|
22829
23334
|
|
|
22830
|
-
|
|
22831
|
-
|
|
23335
|
+
One \`createDocument({ title: "knowledge-map", content: "<markdown map>" })\`.
|
|
23336
|
+
Do not rely on buildMindMap \u2014 it is not available in the CLI workspace.`,
|
|
22832
23337
|
tools: ["list_files", "read_file", "grep_content"],
|
|
22833
23338
|
skills: ["document-writer", "research-analyst"]
|
|
22834
23339
|
},
|
|
@@ -22839,40 +23344,25 @@ Do NOT rely on buildMindMap \u2014 it is not available in the CLI workspace.`,
|
|
|
22839
23344
|
role: "Quality Critic",
|
|
22840
23345
|
color: "#ef4444",
|
|
22841
23346
|
avatar: "M",
|
|
22842
|
-
systemPrompt: `You are Minosse, the Quality Critic \u2014
|
|
22843
|
-
|
|
22844
|
-
## Methodology (work in this order)
|
|
22845
|
-
1. Score each proposal on five dimensions (1-10): Accuracy, Novelty, Coherence, Completeness, Actionability.
|
|
22846
|
-
2. Identify the single biggest gap or risk across all proposals.
|
|
22847
|
-
3. Flag any contradictions between proposals (e.g., conflicting assumptions).
|
|
22848
|
-
4. Recommend the 1-3 highest-value improvements the Lucifero should apply.
|
|
22849
|
-
5. Note anything that should be cut or descoped.
|
|
22850
|
-
|
|
22851
|
-
## Operating principles
|
|
22852
|
-
- Be honest and incisive \u2014 a weak critique helps no one. Name specific defects, not general vibes.
|
|
22853
|
-
- Distinguish "wrong" from "incomplete" and "risky".
|
|
22854
|
-
- Constructive: every critique pairs with a fix or a question.
|
|
22855
|
-
|
|
22856
|
-
## Output format
|
|
22857
|
-
## Scores (per agent, per dimension)
|
|
22858
|
-
## Critical gaps
|
|
22859
|
-
## Contradictions
|
|
22860
|
-
## Top improvements
|
|
23347
|
+
systemPrompt: `You are Minosse, the Quality Critic \u2014 expose weaknesses before synthesis. Evaluate; never create product code.
|
|
22861
23348
|
|
|
22862
|
-
|
|
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.
|
|
22863
23355
|
|
|
22864
|
-
##
|
|
22865
|
-
|
|
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.
|
|
22866
23359
|
|
|
22867
|
-
|
|
22868
|
-
|
|
22869
|
-
|
|
22870
|
-
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. ...\`
|
|
22871
|
-
})
|
|
22872
|
-
\`\`\`
|
|
23360
|
+
## Output
|
|
23361
|
+
## Scores \xB7 ## Critical gaps \xB7 ## Contradictions \xB7 ## Top improvements \u2014 under 200 words.`,
|
|
23362
|
+
designPhaseAddendum: `## Design-phase: risks artifact (mandatory)
|
|
22873
23363
|
|
|
22874
|
-
|
|
22875
|
-
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"],
|
|
22876
23366
|
skills: ["document-writer", "research-analyst"]
|
|
22877
23367
|
},
|
|
22878
23368
|
{
|
|
@@ -22882,35 +23372,26 @@ Include AT LEAST 5 risks, each scored on Impact and Likelihood, with a one-line
|
|
|
22882
23372
|
role: "Final Synthesizer",
|
|
22883
23373
|
color: "#8b5cf6",
|
|
22884
23374
|
avatar: "L",
|
|
22885
|
-
systemPrompt: `You are
|
|
23375
|
+
systemPrompt: `You are Lucifero, the Final Synthesizer. For coding tasks you IMPLEMENT: write/edit files, run commands, deliver working code.
|
|
22886
23376
|
|
|
22887
|
-
## Methodology
|
|
22888
|
-
1. Reconcile
|
|
22889
|
-
2. Resolve conflicts explicitly
|
|
22890
|
-
3. Deliver the
|
|
22891
|
-
4.
|
|
22892
|
-
|
|
22893
|
-
##
|
|
22894
|
-
-
|
|
22895
|
-
- Lead with a one-line summary, then
|
|
22896
|
-
- Apply Minosse's highest-value improvements
|
|
22897
|
-
-
|
|
22898
|
-
|
|
22899
|
-
|
|
22900
|
-
|
|
22901
|
-
|
|
22902
|
-
|
|
22903
|
-
|
|
22904
|
-
|
|
22905
|
-
\`\`\`
|
|
22906
|
-
createDocument({
|
|
22907
|
-
title: "synthesis",
|
|
22908
|
-
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\`
|
|
22909
|
-
})
|
|
22910
|
-
\`\`\`
|
|
22911
|
-
|
|
22912
|
-
DO NOT call \`list_files\` \u2014 it is NOT a workspace tool. Use \`searchDocuments\` if you need to look something up (limit 2-3 searches, then act on the results). Your deliverable is the synthesis document; emit it via \`createDocument\`, not via prose.`,
|
|
22913
|
-
// v0.7.2: implementation tools — the synthesizer writes/edits files and runs commands.
|
|
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.`,
|
|
22914
23395
|
tools: ["read_file", "write_file", "edit_file", "bash", "list_files"],
|
|
22915
23396
|
skills: ["vault-manager", "project-planner", "idea-synthesizer"]
|
|
22916
23397
|
}
|
|
@@ -23142,8 +23623,8 @@ var init_parseCssMotion = __esm({
|
|
|
23142
23623
|
});
|
|
23143
23624
|
|
|
23144
23625
|
// packages/core/dist/council/verification/citeVerify.js
|
|
23145
|
-
import { existsSync as
|
|
23146
|
-
import { join } from "node:path";
|
|
23626
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "node:fs";
|
|
23627
|
+
import { join as join2 } from "node:path";
|
|
23147
23628
|
function extractCitations(text) {
|
|
23148
23629
|
const out = [];
|
|
23149
23630
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -23165,8 +23646,8 @@ function verifyCitations(projectRoot, synthesisText) {
|
|
|
23165
23646
|
return [];
|
|
23166
23647
|
const results = [];
|
|
23167
23648
|
for (const cite of extractCitations(synthesisText)) {
|
|
23168
|
-
const abs =
|
|
23169
|
-
if (!
|
|
23649
|
+
const abs = join2(projectRoot, cite.file);
|
|
23650
|
+
if (!existsSync10(abs)) {
|
|
23170
23651
|
results.push({
|
|
23171
23652
|
id: "synthesis.cite-invalid",
|
|
23172
23653
|
severity: "error",
|
|
@@ -23179,7 +23660,7 @@ function verifyCitations(projectRoot, synthesisText) {
|
|
|
23179
23660
|
});
|
|
23180
23661
|
continue;
|
|
23181
23662
|
}
|
|
23182
|
-
const lines =
|
|
23663
|
+
const lines = readFileSync8(abs, "utf8").split(/\r?\n/);
|
|
23183
23664
|
if (cite.line > lines.length) {
|
|
23184
23665
|
results.push({
|
|
23185
23666
|
id: "synthesis.cite-invalid",
|
|
@@ -23442,14 +23923,14 @@ var init_synthesisAudit = __esm({
|
|
|
23442
23923
|
});
|
|
23443
23924
|
|
|
23444
23925
|
// packages/core/dist/council/verification/runChecks.js
|
|
23445
|
-
import { existsSync as
|
|
23446
|
-
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";
|
|
23447
23928
|
function loadNfrSpec(zelariRoot) {
|
|
23448
|
-
const path36 =
|
|
23449
|
-
if (!
|
|
23929
|
+
const path36 = join3(zelariRoot, "nfr-spec.json");
|
|
23930
|
+
if (!existsSync11(path36))
|
|
23450
23931
|
return null;
|
|
23451
23932
|
try {
|
|
23452
|
-
const raw = JSON.parse(
|
|
23933
|
+
const raw = JSON.parse(readFileSync9(path36, "utf8"));
|
|
23453
23934
|
if (raw.version !== 1 || !Array.isArray(raw.targets))
|
|
23454
23935
|
return null;
|
|
23455
23936
|
return raw;
|
|
@@ -23460,11 +23941,11 @@ function loadNfrSpec(zelariRoot) {
|
|
|
23460
23941
|
function resolveTargets(projectRoot, spec) {
|
|
23461
23942
|
const found = [];
|
|
23462
23943
|
for (const rel2 of spec.targets) {
|
|
23463
|
-
if (
|
|
23944
|
+
if (existsSync11(join3(projectRoot, rel2))) {
|
|
23464
23945
|
found.push(rel2);
|
|
23465
23946
|
}
|
|
23466
23947
|
}
|
|
23467
|
-
if (found.length === 0 &&
|
|
23948
|
+
if (found.length === 0 && existsSync11(join3(projectRoot, "index.html"))) {
|
|
23468
23949
|
return ["index.html"];
|
|
23469
23950
|
}
|
|
23470
23951
|
return found;
|
|
@@ -23519,18 +24000,18 @@ function checkDeadCssHooks(html, relFile) {
|
|
|
23519
24000
|
return results;
|
|
23520
24001
|
}
|
|
23521
24002
|
function checkPlanReality(projectRoot, zelariRoot, targets, keywords) {
|
|
23522
|
-
const planPath =
|
|
23523
|
-
if (!
|
|
24003
|
+
const planPath = join3(zelariRoot, "plan.json");
|
|
24004
|
+
if (!existsSync11(planPath) || keywords.length === 0)
|
|
23524
24005
|
return [];
|
|
23525
24006
|
let plan;
|
|
23526
24007
|
try {
|
|
23527
|
-
plan = JSON.parse(
|
|
24008
|
+
plan = JSON.parse(readFileSync9(planPath, "utf8"));
|
|
23528
24009
|
} catch {
|
|
23529
24010
|
return [];
|
|
23530
24011
|
}
|
|
23531
24012
|
const milestoneText = (plan.milestones ?? []).map((m) => `${m.name ?? ""} ${m.description ?? ""}`).join(" ").toLowerCase();
|
|
23532
24013
|
const results = [];
|
|
23533
|
-
const targetContent = targets.map((t) =>
|
|
24014
|
+
const targetContent = targets.map((t) => readFileSync9(join3(projectRoot, t), "utf8").toLowerCase()).join("\n");
|
|
23534
24015
|
for (const kw of keywords) {
|
|
23535
24016
|
const low = kw.toLowerCase();
|
|
23536
24017
|
if (!milestoneText.includes(low))
|
|
@@ -23559,14 +24040,14 @@ function checkPlanReality(projectRoot, zelariRoot, targets, keywords) {
|
|
|
23559
24040
|
return results;
|
|
23560
24041
|
}
|
|
23561
24042
|
function checkReadmeStale(projectRoot, targets) {
|
|
23562
|
-
const readmePath =
|
|
23563
|
-
if (!
|
|
24043
|
+
const readmePath = join3(projectRoot, "README.md");
|
|
24044
|
+
if (!existsSync11(readmePath) || targets.length === 0)
|
|
23564
24045
|
return [];
|
|
23565
|
-
const readme =
|
|
23566
|
-
const htmlPath =
|
|
23567
|
-
if (!
|
|
24046
|
+
const readme = readFileSync9(readmePath, "utf8");
|
|
24047
|
+
const htmlPath = join3(projectRoot, targets[0]);
|
|
24048
|
+
if (!existsSync11(htmlPath))
|
|
23568
24049
|
return [];
|
|
23569
|
-
const html =
|
|
24050
|
+
const html = readFileSync9(htmlPath, "utf8");
|
|
23570
24051
|
const sectionCount = (html.match(/<section\s+id=/gi) ?? []).length;
|
|
23571
24052
|
const readmeSections = readme.match(/(\d+)\s+sezioni/i);
|
|
23572
24053
|
if (readmeSections) {
|
|
@@ -23604,7 +24085,7 @@ function runImplementationVerification(input) {
|
|
|
23604
24085
|
forbidLayoutProps: anim.forbidLayoutProps ?? true
|
|
23605
24086
|
};
|
|
23606
24087
|
for (const rel2 of targets) {
|
|
23607
|
-
const html =
|
|
24088
|
+
const html = readFileSync9(join3(input.projectRoot, rel2), "utf8");
|
|
23608
24089
|
for (const v of scanKeyframesViolations(html, scanOpts)) {
|
|
23609
24090
|
results.push({
|
|
23610
24091
|
id: "motion.keyframes",
|
|
@@ -23660,8 +24141,8 @@ function runImplementationVerification(input) {
|
|
|
23660
24141
|
};
|
|
23661
24142
|
}
|
|
23662
24143
|
function writeVerificationReport(zelariRoot, report) {
|
|
23663
|
-
const outPath =
|
|
23664
|
-
|
|
24144
|
+
const outPath = join3(zelariRoot, "verification-report.json");
|
|
24145
|
+
writeFileSync6(outPath, JSON.stringify(report, null, 2), "utf8");
|
|
23665
24146
|
return outPath;
|
|
23666
24147
|
}
|
|
23667
24148
|
var DEFAULT_NFR_SPEC;
|
|
@@ -23690,8 +24171,8 @@ var init_runChecks = __esm({
|
|
|
23690
24171
|
});
|
|
23691
24172
|
|
|
23692
24173
|
// packages/core/dist/council/verification/microGate.js
|
|
23693
|
-
import { existsSync as
|
|
23694
|
-
import { join as
|
|
24174
|
+
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "node:fs";
|
|
24175
|
+
import { join as join4 } from "node:path";
|
|
23695
24176
|
function checkDeadHooksInHtml(html) {
|
|
23696
24177
|
const warnings = [];
|
|
23697
24178
|
const scripts = [];
|
|
@@ -23719,8 +24200,8 @@ function checkDeadHooksInHtml(html) {
|
|
|
23719
24200
|
return warnings;
|
|
23720
24201
|
}
|
|
23721
24202
|
function runMicroVerificationOnFile(projectRoot, relPath, zelariRoot) {
|
|
23722
|
-
const abs =
|
|
23723
|
-
if (!
|
|
24203
|
+
const abs = join4(projectRoot, relPath);
|
|
24204
|
+
if (!existsSync12(abs) || !/\.html?$/i.test(relPath))
|
|
23724
24205
|
return [];
|
|
23725
24206
|
const spec = (zelariRoot ? loadNfrSpec(zelariRoot) : null) ?? DEFAULT_NFR_SPEC;
|
|
23726
24207
|
const anim = spec.animation ?? { compositorOnly: true, forbidLayoutProps: true };
|
|
@@ -23728,7 +24209,7 @@ function runMicroVerificationOnFile(projectRoot, relPath, zelariRoot) {
|
|
|
23728
24209
|
compositorOnly: anim.compositorOnly ?? true,
|
|
23729
24210
|
forbidLayoutProps: anim.forbidLayoutProps ?? true
|
|
23730
24211
|
};
|
|
23731
|
-
const html =
|
|
24212
|
+
const html = readFileSync10(abs, "utf8");
|
|
23732
24213
|
const warnings = [];
|
|
23733
24214
|
for (const v of scanKeyframesViolations(html, scanOpts)) {
|
|
23734
24215
|
warnings.push({
|
|
@@ -24084,8 +24565,8 @@ var init_implementationDelivery = __esm({
|
|
|
24084
24565
|
});
|
|
24085
24566
|
|
|
24086
24567
|
// packages/core/dist/council/verification/inlineJsAutofix.js
|
|
24087
|
-
import { readFileSync as
|
|
24088
|
-
import { join as
|
|
24568
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "node:fs";
|
|
24569
|
+
import { join as join5 } from "node:path";
|
|
24089
24570
|
function minifyInlineJs(js) {
|
|
24090
24571
|
let out = js.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
24091
24572
|
out = out.split("\n").map((line) => line.replace(/\/\/.*$/, "").trimEnd()).filter((line) => line.trim().length > 0).join("\n");
|
|
@@ -24128,10 +24609,10 @@ function applyInlineJsAutofix(projectRoot, report) {
|
|
|
24128
24609
|
const fixes = [];
|
|
24129
24610
|
for (const r of fails) {
|
|
24130
24611
|
const rel2 = r.file ?? "index.html";
|
|
24131
|
-
const abs =
|
|
24612
|
+
const abs = join5(projectRoot, rel2);
|
|
24132
24613
|
let html;
|
|
24133
24614
|
try {
|
|
24134
|
-
html =
|
|
24615
|
+
html = readFileSync11(abs, "utf8");
|
|
24135
24616
|
} catch {
|
|
24136
24617
|
continue;
|
|
24137
24618
|
}
|
|
@@ -24144,7 +24625,7 @@ function applyInlineJsAutofix(projectRoot, report) {
|
|
|
24144
24625
|
if (!changed || afterBytes > maxBytes)
|
|
24145
24626
|
continue;
|
|
24146
24627
|
const nextHtml = html.replace(SCRIPT_RE, `<script>${after}</script>`);
|
|
24147
|
-
|
|
24628
|
+
writeFileSync7(abs, nextHtml, "utf8");
|
|
24148
24629
|
filesChanged.push(rel2);
|
|
24149
24630
|
fixes.push(`${rel2}: trimmed inline script ${Buffer.byteLength(before, "utf8")}\u2192${afterBytes} bytes`);
|
|
24150
24631
|
}
|
|
@@ -24164,8 +24645,8 @@ var init_inlineJsAutofix = __esm({
|
|
|
24164
24645
|
});
|
|
24165
24646
|
|
|
24166
24647
|
// packages/core/dist/agents/councilApi.js
|
|
24167
|
-
import { existsSync as
|
|
24168
|
-
import { join as
|
|
24648
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
24649
|
+
import { join as join6 } from "node:path";
|
|
24169
24650
|
function parseClarificationRequest(text) {
|
|
24170
24651
|
const start = text.indexOf(QUESTION_MARKER);
|
|
24171
24652
|
if (start < 0)
|
|
@@ -24210,29 +24691,37 @@ function restrictImplementationWrites(toolNames, opts) {
|
|
|
24210
24691
|
return toolNames;
|
|
24211
24692
|
return toolNames.filter((t) => !MUTATING_PROJECT_TOOLS.includes(t));
|
|
24212
24693
|
}
|
|
24213
|
-
function buildAgentMessages(agent, userMessage, ragContext, workspaceContext, priorOutputs, aiConfig, executableTools, runMode = "implementation") {
|
|
24694
|
+
function buildAgentMessages(agent, userMessage, ragContext, workspaceContext, priorOutputs, aiConfig, executableTools, runMode = "implementation", languageModule) {
|
|
24214
24695
|
const allToolNames = computeAgentTools(agent, aiConfig);
|
|
24215
24696
|
const toolNames = executableTools ? allToolNames.filter((n) => executableTools.has(n)) : allToolNames;
|
|
24216
|
-
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, {
|
|
24217
24712
|
tools: getAllTools(),
|
|
24218
24713
|
toolNames,
|
|
24219
|
-
aiConfig,
|
|
24714
|
+
aiConfig: mergedAiConfig,
|
|
24220
24715
|
workspaceContext,
|
|
24221
|
-
ragContext
|
|
24716
|
+
ragContext,
|
|
24717
|
+
mode: "council",
|
|
24718
|
+
includeWorkspaceInPrompt: true
|
|
24222
24719
|
});
|
|
24223
24720
|
const messages = [
|
|
24224
24721
|
{ role: "system", content: enhancedSystemPrompt },
|
|
24225
24722
|
{ role: "system", content: councilModeBanner(runMode, { isImplementer: agent.id === "lucifer" }) },
|
|
24226
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." }
|
|
24227
24724
|
];
|
|
24228
|
-
if (ragContext) {
|
|
24229
|
-
messages.push({ role: "system", content: `Relevant workspace context (from RAG retrieval):
|
|
24230
|
-
${ragContext}` });
|
|
24231
|
-
}
|
|
24232
|
-
if (workspaceContext) {
|
|
24233
|
-
messages.push({ role: "system", content: `Current workspace state:
|
|
24234
|
-
${workspaceContext}` });
|
|
24235
|
-
}
|
|
24236
24725
|
if (priorOutputs.length > 0) {
|
|
24237
24726
|
const summary = priorOutputs.map((o) => `[${o.name} - ${o.role}]: ${o.content}`).join("\n\n");
|
|
24238
24727
|
messages.push({ role: "user", content: `Previous council members have said:
|
|
@@ -24327,7 +24816,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
24327
24816
|
model: effectiveModel,
|
|
24328
24817
|
provider: effectiveProvider,
|
|
24329
24818
|
sessionId,
|
|
24330
|
-
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),
|
|
24331
24820
|
tools: agentTools,
|
|
24332
24821
|
eventBus: config2.eventBus,
|
|
24333
24822
|
toolRegistry: config2.tools,
|
|
@@ -24470,7 +24959,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
24470
24959
|
model: effectiveModel,
|
|
24471
24960
|
provider: effectiveProvider,
|
|
24472
24961
|
sessionId,
|
|
24473
|
-
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),
|
|
24474
24963
|
tools: (() => {
|
|
24475
24964
|
const oracleToolNames = filterExecutable(restrictImplementationWrites(Array.from(/* @__PURE__ */ new Set([
|
|
24476
24965
|
"createDocument",
|
|
@@ -24601,7 +25090,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
24601
25090
|
model: effectiveModel,
|
|
24602
25091
|
provider: effectiveProvider,
|
|
24603
25092
|
sessionId,
|
|
24604
|
-
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),
|
|
24605
25094
|
tools: chairmanTools,
|
|
24606
25095
|
eventBus: config2.eventBus,
|
|
24607
25096
|
toolRegistry: config2.tools,
|
|
@@ -24720,7 +25209,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
24720
25209
|
const zelariRoot = `${chairmanProjectRoot}/.zelari`;
|
|
24721
25210
|
const spec = loadNfrSpec(zelariRoot) ?? DEFAULT_NFR_SPEC;
|
|
24722
25211
|
for (const rel2 of spec.targets) {
|
|
24723
|
-
if (!
|
|
25212
|
+
if (!existsSync13(join6(chairmanProjectRoot, rel2)))
|
|
24724
25213
|
continue;
|
|
24725
25214
|
changedTargetFiles.add(rel2);
|
|
24726
25215
|
for (const w of runChairmanMicroGate({ projectRoot: chairmanProjectRoot, relPath: rel2, zelariRoot })) {
|
|
@@ -24740,7 +25229,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
24740
25229
|
const zelariRootReplay = `${chairmanProjectRoot}/.zelari`;
|
|
24741
25230
|
const specReplay = loadNfrSpec(zelariRootReplay) ?? DEFAULT_NFR_SPEC;
|
|
24742
25231
|
for (const rel2 of specReplay.targets) {
|
|
24743
|
-
if (!
|
|
25232
|
+
if (!existsSync13(join6(chairmanProjectRoot, rel2)))
|
|
24744
25233
|
continue;
|
|
24745
25234
|
changedTargetFiles.add(rel2);
|
|
24746
25235
|
for (const w of runChairmanMicroGate({
|
|
@@ -25186,7 +25675,7 @@ async function* runRetryTurnForMember(args) {
|
|
|
25186
25675
|
description: t.function.description,
|
|
25187
25676
|
parameters: t.function.parameters
|
|
25188
25677
|
}));
|
|
25189
|
-
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);
|
|
25190
25679
|
const retryMessages = [
|
|
25191
25680
|
...baseMessages,
|
|
25192
25681
|
{
|
|
@@ -25457,8 +25946,8 @@ var init_types = __esm({
|
|
|
25457
25946
|
});
|
|
25458
25947
|
|
|
25459
25948
|
// packages/core/dist/council/verification/motionAutofix.js
|
|
25460
|
-
import { readFileSync as
|
|
25461
|
-
import { join as
|
|
25949
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "node:fs";
|
|
25950
|
+
import { join as join7 } from "node:path";
|
|
25462
25951
|
function sanitizeTransitionPart(part) {
|
|
25463
25952
|
const tokens = part.trim().split(/\s+/);
|
|
25464
25953
|
if (tokens.length === 0)
|
|
@@ -25533,10 +26022,10 @@ function applyMotionAutofix(projectRoot, report) {
|
|
|
25533
26022
|
forbidLayoutProps: DEFAULT_NFR_SPEC.animation?.forbidLayoutProps ?? true
|
|
25534
26023
|
};
|
|
25535
26024
|
for (const rel2 of targets) {
|
|
25536
|
-
const abs =
|
|
26025
|
+
const abs = join7(projectRoot, rel2);
|
|
25537
26026
|
let html;
|
|
25538
26027
|
try {
|
|
25539
|
-
html =
|
|
26028
|
+
html = readFileSync12(abs, "utf8");
|
|
25540
26029
|
} catch {
|
|
25541
26030
|
continue;
|
|
25542
26031
|
}
|
|
@@ -25551,7 +26040,7 @@ function applyMotionAutofix(projectRoot, report) {
|
|
|
25551
26040
|
const afterK = scanKeyframesViolations(final, scanOpts).length;
|
|
25552
26041
|
const afterT = scanTransitionViolations(final, scanOpts).length;
|
|
25553
26042
|
if (final !== html || beforeK > afterK || beforeT > afterT) {
|
|
25554
|
-
|
|
26043
|
+
writeFileSync8(abs, final, "utf8");
|
|
25555
26044
|
filesChanged.push(rel2);
|
|
25556
26045
|
if (beforeK > afterK)
|
|
25557
26046
|
fixes.push(`${rel2}: sanitized ${beforeK - afterK} keyframe violation(s)`);
|
|
@@ -25562,7 +26051,7 @@ function applyMotionAutofix(projectRoot, report) {
|
|
|
25562
26051
|
if (!fixes.some((f) => f.startsWith(rel2)))
|
|
25563
26052
|
fixes.push(`${rel2}: motion CSS sanitized`);
|
|
25564
26053
|
} else if (motionChanged) {
|
|
25565
|
-
|
|
26054
|
+
writeFileSync8(abs, final, "utf8");
|
|
25566
26055
|
filesChanged.push(rel2);
|
|
25567
26056
|
fixes.push(`${rel2}: motion CSS sanitized`);
|
|
25568
26057
|
}
|
|
@@ -25608,8 +26097,8 @@ var init_motionAutofix = __esm({
|
|
|
25608
26097
|
});
|
|
25609
26098
|
|
|
25610
26099
|
// packages/core/dist/council/verification/autofix.js
|
|
25611
|
-
import { readFileSync as
|
|
25612
|
-
import { join as
|
|
26100
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "node:fs";
|
|
26101
|
+
import { join as join8 } from "node:path";
|
|
25613
26102
|
function applyDeterministicAutofix(projectRoot, report) {
|
|
25614
26103
|
const motion = applyMotionAutofix(projectRoot, report);
|
|
25615
26104
|
const inlineJs = applyInlineJsAutofix(projectRoot, report);
|
|
@@ -25623,8 +26112,8 @@ function applyDeterministicAutofix(projectRoot, report) {
|
|
|
25623
26112
|
const m = r.evidence?.match(/classList\.add\(\s*['"]([\w-]+)['"]\s*\)/);
|
|
25624
26113
|
if (!m || m[1] === "rm")
|
|
25625
26114
|
continue;
|
|
25626
|
-
const abs =
|
|
25627
|
-
let html =
|
|
26115
|
+
const abs = join8(projectRoot, rel2);
|
|
26116
|
+
let html = readFileSync13(abs, "utf8");
|
|
25628
26117
|
const snippet = m[0];
|
|
25629
26118
|
if (!html.includes(snippet))
|
|
25630
26119
|
continue;
|
|
@@ -25632,7 +26121,7 @@ function applyDeterministicAutofix(projectRoot, report) {
|
|
|
25632
26121
|
const filtered = lines.filter((line) => !line.includes(snippet));
|
|
25633
26122
|
if (filtered.length === lines.length)
|
|
25634
26123
|
continue;
|
|
25635
|
-
|
|
26124
|
+
writeFileSync9(abs, filtered.join("\n"), "utf8");
|
|
25636
26125
|
filesChanged.push(rel2);
|
|
25637
26126
|
fixes.push(`removed dead hook line in ${rel2}: ${snippet}`);
|
|
25638
26127
|
}
|
|
@@ -25679,12 +26168,12 @@ var init_types2 = __esm({
|
|
|
25679
26168
|
});
|
|
25680
26169
|
|
|
25681
26170
|
// packages/core/dist/council/lessons/io.js
|
|
25682
|
-
import { readFileSync as
|
|
25683
|
-
import { join as
|
|
26171
|
+
import { readFileSync as readFileSync14 } from "node:fs";
|
|
26172
|
+
import { join as join9 } from "node:path";
|
|
25684
26173
|
function readLessonsDeduped(zelariRoot) {
|
|
25685
|
-
const path36 =
|
|
26174
|
+
const path36 = join9(zelariRoot, LESSONS_FILE);
|
|
25686
26175
|
try {
|
|
25687
|
-
const raw =
|
|
26176
|
+
const raw = readFileSync14(path36, "utf8");
|
|
25688
26177
|
const byId = /* @__PURE__ */ new Map();
|
|
25689
26178
|
for (const line of raw.split(/\r?\n/)) {
|
|
25690
26179
|
if (!line.trim())
|
|
@@ -25775,7 +26264,7 @@ var init_signatures = __esm({
|
|
|
25775
26264
|
|
|
25776
26265
|
// packages/core/dist/council/lessons/recordFailure.js
|
|
25777
26266
|
import { appendFileSync as appendFileSync2 } from "node:fs";
|
|
25778
|
-
import { join as
|
|
26267
|
+
import { join as join10 } from "node:path";
|
|
25779
26268
|
function methodologyFor(check2) {
|
|
25780
26269
|
return METHODOLOGY[check2.id] ?? `When ${check2.id} fails, fix the underlying issue and cite grep/tool evidence before claiming PASS.`;
|
|
25781
26270
|
}
|
|
@@ -25785,7 +26274,7 @@ function keywordsFrom(check2, signature) {
|
|
|
25785
26274
|
return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
|
|
25786
26275
|
}
|
|
25787
26276
|
function writeLesson(zelariRoot, lesson) {
|
|
25788
|
-
const path36 =
|
|
26277
|
+
const path36 = join10(zelariRoot, LESSONS_FILE);
|
|
25789
26278
|
appendFileSync2(path36, `${JSON.stringify(lesson)}
|
|
25790
26279
|
`, "utf8");
|
|
25791
26280
|
}
|
|
@@ -25942,8 +26431,8 @@ var init_types3 = __esm({
|
|
|
25942
26431
|
});
|
|
25943
26432
|
|
|
25944
26433
|
// packages/core/dist/council/completion/buildCompletion.js
|
|
25945
|
-
import { writeFileSync as
|
|
25946
|
-
import { join as
|
|
26434
|
+
import { writeFileSync as writeFileSync10 } from "node:fs";
|
|
26435
|
+
import { join as join11 } from "node:path";
|
|
25947
26436
|
function openFailsFromReport(report) {
|
|
25948
26437
|
if (!report)
|
|
25949
26438
|
return [];
|
|
@@ -26002,8 +26491,8 @@ function buildCouncilCompletion(input) {
|
|
|
26002
26491
|
};
|
|
26003
26492
|
}
|
|
26004
26493
|
function writeCouncilCompletion(zelariRoot, completion) {
|
|
26005
|
-
const outPath =
|
|
26006
|
-
|
|
26494
|
+
const outPath = join11(zelariRoot, "completion.json");
|
|
26495
|
+
writeFileSync10(outPath, JSON.stringify(completion, null, 2), "utf8");
|
|
26007
26496
|
return outPath;
|
|
26008
26497
|
}
|
|
26009
26498
|
var init_buildCompletion = __esm({
|
|
@@ -26192,6 +26681,8 @@ var init_promoteMember = __esm({
|
|
|
26192
26681
|
var council_exports = {};
|
|
26193
26682
|
__export(council_exports, {
|
|
26194
26683
|
AGENT_ROLES: () => AGENT_ROLES,
|
|
26684
|
+
CLARIFICATION_PROTOCOL_MODULE: () => CLARIFICATION_PROTOCOL_MODULE,
|
|
26685
|
+
CODING_PRACTICES_MODULE: () => CODING_PRACTICES_MODULE,
|
|
26195
26686
|
COLLABORATION_DIRECTIVE: () => COLLABORATION_DIRECTIVE,
|
|
26196
26687
|
COMPOSITOR_ONLY_PROPS: () => COMPOSITOR_ONLY_PROPS,
|
|
26197
26688
|
DEFAULT_NFR_SPEC: () => DEFAULT_NFR_SPEC,
|
|
@@ -26208,6 +26699,7 @@ __export(council_exports, {
|
|
|
26208
26699
|
MAX_DELIVERY_ATTEMPTS: () => MAX_DELIVERY_ATTEMPTS,
|
|
26209
26700
|
MAX_RETRY_PER_MEMBER: () => MAX_RETRY_PER_MEMBER,
|
|
26210
26701
|
MUTATING_PROJECT_TOOLS: () => MUTATING_PROJECT_TOOLS,
|
|
26702
|
+
NATIVE_TOOL_PROTOCOL_MODULE: () => NATIVE_TOOL_PROTOCOL_MODULE,
|
|
26211
26703
|
NFR_KEYWORDS: () => NFR_KEYWORDS,
|
|
26212
26704
|
NON_RETRY_AGENTS: () => NON_RETRY_AGENTS,
|
|
26213
26705
|
OUTPUT_QUALITY_DIRECTIVE: () => OUTPUT_QUALITY_DIRECTIVE,
|
|
@@ -26277,6 +26769,7 @@ __export(council_exports, {
|
|
|
26277
26769
|
renderSkillMarkdown: () => renderSkillMarkdown,
|
|
26278
26770
|
replayChairmanTextTools: () => replayChairmanTextTools,
|
|
26279
26771
|
resolveCouncilRunMode: () => resolveCouncilRunMode,
|
|
26772
|
+
resolveRoleSystemPrompt: () => resolveRoleSystemPrompt,
|
|
26280
26773
|
resolveVerifyRetryTool: () => resolveVerifyRetryTool,
|
|
26281
26774
|
restrictImplementationWrites: () => restrictImplementationWrites,
|
|
26282
26775
|
runChairmanDeliveryLoop: () => runChairmanDeliveryLoop,
|
|
@@ -26412,9 +26905,11 @@ var conversationContext_exports = {};
|
|
|
26412
26905
|
__export(conversationContext_exports, {
|
|
26413
26906
|
_resetConversationContextForTests: () => _resetConversationContextForTests,
|
|
26414
26907
|
appendMessages: () => appendMessages,
|
|
26908
|
+
buildCouncilTaskWithHistory: () => buildCouncilTaskWithHistory,
|
|
26415
26909
|
clearHistory: () => clearHistory,
|
|
26416
26910
|
compactInPlace: () => compactInPlace,
|
|
26417
26911
|
formatHistoryForCouncil: () => formatHistoryForCouncil,
|
|
26912
|
+
formatHistoryMessages: () => formatHistoryMessages,
|
|
26418
26913
|
getHistory: () => getHistory,
|
|
26419
26914
|
getLastClarification: () => getLastClarification,
|
|
26420
26915
|
hydrateHistory: () => hydrateHistory,
|
|
@@ -26475,23 +26970,56 @@ User's answer: ${picked}
|
|
|
26475
26970
|
Proceed using this answer; do not re-ask the same question unless the answer is still ambiguous.`;
|
|
26476
26971
|
}
|
|
26477
26972
|
function formatHistoryForCouncil(maxTurns = 4) {
|
|
26478
|
-
|
|
26479
|
-
|
|
26973
|
+
return formatHistoryMessages(history, maxTurns);
|
|
26974
|
+
}
|
|
26975
|
+
function formatHistoryMessages(messages, maxTurns = 6, maxTotalChars = 12e3) {
|
|
26976
|
+
if (messages.length === 0) return "";
|
|
26480
26977
|
let turns = 0;
|
|
26481
26978
|
const chunk = [];
|
|
26482
|
-
for (let i =
|
|
26483
|
-
const m =
|
|
26979
|
+
for (let i = messages.length - 1; i >= 0 && turns < maxTurns; i--) {
|
|
26980
|
+
const m = messages[i];
|
|
26484
26981
|
if (m.role === "user") {
|
|
26485
|
-
chunk.push(`User: ${truncate(m.content,
|
|
26982
|
+
chunk.push(`User: ${truncate(m.content, 800)}`);
|
|
26486
26983
|
turns += 1;
|
|
26487
26984
|
} else if (m.role === "assistant" && m.content.trim()) {
|
|
26488
|
-
chunk.push(`Assistant: ${truncate(m.content,
|
|
26985
|
+
chunk.push(`Assistant: ${truncate(m.content, 2e3)}`);
|
|
26489
26986
|
}
|
|
26490
26987
|
}
|
|
26491
26988
|
if (chunk.length === 0) return "";
|
|
26492
|
-
|
|
26493
|
-
|
|
26494
|
-
|
|
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}`;
|
|
26495
27023
|
}
|
|
26496
27024
|
function truncate(s, max) {
|
|
26497
27025
|
const t = s.replace(/\s+/g, " ").trim();
|
|
@@ -26502,13 +27030,14 @@ function _resetConversationContextForTests() {
|
|
|
26502
27030
|
history = [];
|
|
26503
27031
|
lastClarification = null;
|
|
26504
27032
|
}
|
|
26505
|
-
var history, lastClarification;
|
|
27033
|
+
var history, lastClarification, SHORT_CONTINUE;
|
|
26506
27034
|
var init_conversationContext = __esm({
|
|
26507
27035
|
"src/cli/hooks/conversationContext.ts"() {
|
|
26508
27036
|
"use strict";
|
|
26509
27037
|
init_historyCompaction();
|
|
26510
27038
|
history = [];
|
|
26511
27039
|
lastClarification = null;
|
|
27040
|
+
SHORT_CONTINUE = /^(procedi|continua|continue|go\s*ahead|go|ok|okay|sì|si|yes|vai|avanti|next|proceed)$/i;
|
|
26512
27041
|
}
|
|
26513
27042
|
});
|
|
26514
27043
|
|
|
@@ -26589,20 +27118,20 @@ var init_phase = __esm({
|
|
|
26589
27118
|
|
|
26590
27119
|
// src/cli/workspace/paths.ts
|
|
26591
27120
|
import {
|
|
26592
|
-
mkdirSync as
|
|
26593
|
-
writeFileSync as
|
|
26594
|
-
existsSync as
|
|
27121
|
+
mkdirSync as mkdirSync7,
|
|
27122
|
+
writeFileSync as writeFileSync11,
|
|
27123
|
+
existsSync as existsSync14,
|
|
26595
27124
|
accessSync,
|
|
26596
27125
|
constants,
|
|
26597
27126
|
realpathSync
|
|
26598
27127
|
} from "node:fs";
|
|
26599
|
-
import { join as
|
|
26600
|
-
import { homedir as
|
|
27128
|
+
import { join as join12, basename } from "node:path";
|
|
27129
|
+
import { homedir as homedir5 } from "node:os";
|
|
26601
27130
|
import { createHash as createHash2 } from "node:crypto";
|
|
26602
27131
|
function resolveWorkspaceRoot(projectRoot = process.cwd()) {
|
|
26603
27132
|
const candidates = [
|
|
26604
|
-
|
|
26605
|
-
|
|
27133
|
+
join12(projectRoot, ".zelari"),
|
|
27134
|
+
join12(homedir5(), ".zelari-code", "workspace", hashProject(projectRoot))
|
|
26606
27135
|
];
|
|
26607
27136
|
for (const candidate of candidates) {
|
|
26608
27137
|
if (isWritableDir(projectRoot) || candidate !== candidates[0]) {
|
|
@@ -26618,7 +27147,7 @@ function hashProject(projectPath) {
|
|
|
26618
27147
|
}
|
|
26619
27148
|
function isWritableDir(dir) {
|
|
26620
27149
|
try {
|
|
26621
|
-
if (!
|
|
27150
|
+
if (!existsSync14(dir)) return false;
|
|
26622
27151
|
accessSync(dir, constants.W_OK);
|
|
26623
27152
|
return true;
|
|
26624
27153
|
} catch {
|
|
@@ -26626,26 +27155,26 @@ function isWritableDir(dir) {
|
|
|
26626
27155
|
}
|
|
26627
27156
|
}
|
|
26628
27157
|
function ensureWorkspaceDir(workspaceDir) {
|
|
26629
|
-
|
|
26630
|
-
if (workspaceDir.endsWith("/.zelari") &&
|
|
26631
|
-
const gitignorePath =
|
|
26632
|
-
if (!
|
|
26633
|
-
|
|
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");
|
|
26634
27163
|
}
|
|
26635
27164
|
}
|
|
26636
27165
|
}
|
|
26637
27166
|
function workspaceFile(rootDir, kind) {
|
|
26638
27167
|
switch (kind) {
|
|
26639
27168
|
case "plan":
|
|
26640
|
-
return
|
|
27169
|
+
return join12(rootDir, "plan.md");
|
|
26641
27170
|
case "risks":
|
|
26642
|
-
return
|
|
27171
|
+
return join12(rootDir, "risks.md");
|
|
26643
27172
|
case "index":
|
|
26644
|
-
return
|
|
27173
|
+
return join12(rootDir, "workspace.json");
|
|
26645
27174
|
}
|
|
26646
27175
|
}
|
|
26647
27176
|
function workspaceArtifact(rootDir, subdir, slug) {
|
|
26648
|
-
return
|
|
27177
|
+
return join12(rootDir, subdir, `${slug}.md`);
|
|
26649
27178
|
}
|
|
26650
27179
|
function projectName(projectRoot = process.cwd()) {
|
|
26651
27180
|
return basename(realpathSync(projectRoot));
|
|
@@ -26664,8 +27193,8 @@ __export(workspaceSummary_exports, {
|
|
|
26664
27193
|
buildWorkspaceSummary: () => buildWorkspaceSummary,
|
|
26665
27194
|
buildZelariReadHint: () => buildZelariReadHint
|
|
26666
27195
|
});
|
|
26667
|
-
import { existsSync as
|
|
26668
|
-
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";
|
|
26669
27198
|
function buildWorkspaceSummary(projectRoot = process.cwd(), options = {}) {
|
|
26670
27199
|
const { maxEntries = 30 } = options;
|
|
26671
27200
|
const name = safeProjectName(projectRoot);
|
|
@@ -26692,11 +27221,11 @@ function formatTaskLine(t) {
|
|
|
26692
27221
|
}
|
|
26693
27222
|
function buildPlanSummary(projectRoot = process.cwd(), options) {
|
|
26694
27223
|
const zelariRoot = resolveWorkspaceRoot(projectRoot);
|
|
26695
|
-
const planPath =
|
|
26696
|
-
if (!
|
|
27224
|
+
const planPath = join13(zelariRoot, "plan.json");
|
|
27225
|
+
if (!existsSync15(planPath)) return null;
|
|
26697
27226
|
let plan;
|
|
26698
27227
|
try {
|
|
26699
|
-
plan = JSON.parse(
|
|
27228
|
+
plan = JSON.parse(readFileSync15(planPath, "utf8"));
|
|
26700
27229
|
} catch {
|
|
26701
27230
|
return null;
|
|
26702
27231
|
}
|
|
@@ -26825,8 +27354,8 @@ function pickNextTask(open) {
|
|
|
26825
27354
|
)[0];
|
|
26826
27355
|
}
|
|
26827
27356
|
function buildZelariReadHint(projectRoot = process.cwd()) {
|
|
26828
|
-
const planPath =
|
|
26829
|
-
if (!
|
|
27357
|
+
const planPath = join13(resolveWorkspaceRoot(projectRoot), "plan.json");
|
|
27358
|
+
if (!existsSync15(planPath)) return "";
|
|
26830
27359
|
return [
|
|
26831
27360
|
"# Council workspace detected (.zelari/)",
|
|
26832
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.",
|
|
@@ -26841,10 +27370,10 @@ function safeProjectName(root) {
|
|
|
26841
27370
|
}
|
|
26842
27371
|
}
|
|
26843
27372
|
function readPackageJson(projectRoot) {
|
|
26844
|
-
const p3 =
|
|
26845
|
-
if (!
|
|
27373
|
+
const p3 = join13(projectRoot, "package.json");
|
|
27374
|
+
if (!existsSync15(p3)) return null;
|
|
26846
27375
|
try {
|
|
26847
|
-
return JSON.parse(
|
|
27376
|
+
return JSON.parse(readFileSync15(p3, "utf8"));
|
|
26848
27377
|
} catch {
|
|
26849
27378
|
return null;
|
|
26850
27379
|
}
|
|
@@ -26878,11 +27407,11 @@ function listShallow(projectRoot, maxEntries) {
|
|
|
26878
27407
|
out.push(`\u2026 (+${top.length - count} more)`);
|
|
26879
27408
|
break;
|
|
26880
27409
|
}
|
|
26881
|
-
const rel2 = relative(projectRoot,
|
|
27410
|
+
const rel2 = relative(projectRoot, join13(projectRoot, entry.name));
|
|
26882
27411
|
if (entry.isDirectory()) {
|
|
26883
27412
|
let inner = "";
|
|
26884
27413
|
try {
|
|
26885
|
-
const sub = readdirSync(
|
|
27414
|
+
const sub = readdirSync(join13(projectRoot, entry.name), {
|
|
26886
27415
|
withFileTypes: true
|
|
26887
27416
|
}).filter((e) => !e.name.startsWith(".")).slice(0, 4).map((e) => e.name);
|
|
26888
27417
|
if (sub.length > 0)
|
|
@@ -26933,14 +27462,14 @@ __export(storage_exports, {
|
|
|
26933
27462
|
workspaceMutex: () => workspaceMutex
|
|
26934
27463
|
});
|
|
26935
27464
|
import {
|
|
26936
|
-
readFileSync as
|
|
26937
|
-
writeFileSync as
|
|
26938
|
-
existsSync as
|
|
26939
|
-
mkdirSync as
|
|
27465
|
+
readFileSync as readFileSync16,
|
|
27466
|
+
writeFileSync as writeFileSync12,
|
|
27467
|
+
existsSync as existsSync16,
|
|
27468
|
+
mkdirSync as mkdirSync8,
|
|
26940
27469
|
readdirSync as readdirSync2,
|
|
26941
27470
|
renameSync as renameSync2
|
|
26942
27471
|
} from "node:fs";
|
|
26943
|
-
import { dirname as
|
|
27472
|
+
import { dirname as dirname3, join as join14 } from "node:path";
|
|
26944
27473
|
function parseFrontmatter(md) {
|
|
26945
27474
|
const m = FRONTMATTER_RE.exec(md);
|
|
26946
27475
|
if (!m) return { meta: {}, body: md };
|
|
@@ -27195,15 +27724,15 @@ var init_storage = __esm({
|
|
|
27195
27724
|
Storage = class {
|
|
27196
27725
|
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
27197
27726
|
read(path36) {
|
|
27198
|
-
if (!
|
|
27727
|
+
if (!existsSync16(path36)) {
|
|
27199
27728
|
throw new Error(`File not found: ${path36}`);
|
|
27200
27729
|
}
|
|
27201
|
-
const md =
|
|
27730
|
+
const md = readFileSync16(path36, "utf8");
|
|
27202
27731
|
return parseFrontmatter(md);
|
|
27203
27732
|
}
|
|
27204
27733
|
/** Read a Markdown file; returns null if not found. */
|
|
27205
27734
|
readIfExists(path36) {
|
|
27206
|
-
if (!
|
|
27735
|
+
if (!existsSync16(path36)) return null;
|
|
27207
27736
|
return this.read(path36);
|
|
27208
27737
|
}
|
|
27209
27738
|
/**
|
|
@@ -27211,16 +27740,16 @@ var init_storage = __esm({
|
|
|
27211
27740
|
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
27212
27741
|
*/
|
|
27213
27742
|
write(path36, meta3, body) {
|
|
27214
|
-
|
|
27743
|
+
mkdirSync8(dirname3(path36), { recursive: true });
|
|
27215
27744
|
const tmp = path36 + ".tmp-" + process.pid;
|
|
27216
27745
|
const md = serializeFrontmatter(meta3, body);
|
|
27217
|
-
|
|
27746
|
+
writeFileSync12(tmp, md, "utf8");
|
|
27218
27747
|
renameSync2(tmp, path36);
|
|
27219
27748
|
}
|
|
27220
27749
|
/** List all .md files in a directory (non-recursive). */
|
|
27221
27750
|
listMarkdown(dir) {
|
|
27222
|
-
if (!
|
|
27223
|
-
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));
|
|
27224
27753
|
}
|
|
27225
27754
|
};
|
|
27226
27755
|
KeyedMutex = class {
|
|
@@ -27259,14 +27788,14 @@ __export(stubs_exports, {
|
|
|
27259
27788
|
resolveWorkspaceRoot: () => resolveWorkspaceRoot
|
|
27260
27789
|
});
|
|
27261
27790
|
import {
|
|
27262
|
-
existsSync as
|
|
27791
|
+
existsSync as existsSync17,
|
|
27263
27792
|
readdirSync as readdirSync3,
|
|
27264
|
-
writeFileSync as
|
|
27265
|
-
readFileSync as
|
|
27266
|
-
mkdirSync as
|
|
27793
|
+
writeFileSync as writeFileSync13,
|
|
27794
|
+
readFileSync as readFileSync17,
|
|
27795
|
+
mkdirSync as mkdirSync9,
|
|
27267
27796
|
renameSync as renameSync3
|
|
27268
27797
|
} from "node:fs";
|
|
27269
|
-
import { join as
|
|
27798
|
+
import { join as join15, basename as basename2, dirname as dirname4, relative as relative2 } from "node:path";
|
|
27270
27799
|
function createWorkspaceContext(projectRoot = process.cwd()) {
|
|
27271
27800
|
const rootDir = resolveWorkspaceRoot(projectRoot);
|
|
27272
27801
|
return {
|
|
@@ -27276,14 +27805,14 @@ function createWorkspaceContext(projectRoot = process.cwd()) {
|
|
|
27276
27805
|
};
|
|
27277
27806
|
}
|
|
27278
27807
|
function planJsonPath(ctx) {
|
|
27279
|
-
return
|
|
27808
|
+
return join15(ctx.rootDir, "plan.json");
|
|
27280
27809
|
}
|
|
27281
27810
|
function readPlan(ctx) {
|
|
27282
27811
|
const jsonPath = planJsonPath(ctx);
|
|
27283
|
-
if (
|
|
27812
|
+
if (existsSync17(jsonPath)) {
|
|
27284
27813
|
try {
|
|
27285
27814
|
const parsed = JSON.parse(
|
|
27286
|
-
|
|
27815
|
+
readFileSync17(jsonPath, "utf8")
|
|
27287
27816
|
);
|
|
27288
27817
|
return {
|
|
27289
27818
|
phases: Array.isArray(parsed.phases) ? parsed.phases : [],
|
|
@@ -27305,9 +27834,9 @@ function readPlan(ctx) {
|
|
|
27305
27834
|
}
|
|
27306
27835
|
function writePlan(ctx, summary) {
|
|
27307
27836
|
const jsonPath = planJsonPath(ctx);
|
|
27308
|
-
|
|
27837
|
+
mkdirSync9(dirname4(jsonPath), { recursive: true });
|
|
27309
27838
|
const tmp = jsonPath + ".tmp-" + process.pid;
|
|
27310
|
-
|
|
27839
|
+
writeFileSync13(tmp, JSON.stringify(summary, null, 2), "utf8");
|
|
27311
27840
|
renameSync3(tmp, jsonPath);
|
|
27312
27841
|
const mdPath = workspaceFile(ctx.rootDir, "plan");
|
|
27313
27842
|
const summaryMeta = {
|
|
@@ -27374,8 +27903,8 @@ function renderPlanBody(summary) {
|
|
|
27374
27903
|
return lines.join("\n");
|
|
27375
27904
|
}
|
|
27376
27905
|
function nextAdrId(ctx) {
|
|
27377
|
-
const decisionsDir =
|
|
27378
|
-
if (!
|
|
27906
|
+
const decisionsDir = join15(ctx.rootDir, "decisions");
|
|
27907
|
+
if (!existsSync17(decisionsDir)) return "001";
|
|
27379
27908
|
const existing = readdirSync3(decisionsDir).filter((f) => f.endsWith(".md")).map((f) => f.match(/^(\d+)-/)).filter((m) => !!m).map((m) => parseInt(m[1], 10));
|
|
27380
27909
|
const max = existing.length === 0 ? 0 : Math.max(...existing);
|
|
27381
27910
|
return String(max + 1).padStart(3, "0");
|
|
@@ -27417,7 +27946,7 @@ function addTaskRecord(ctx, summary, phaseId, t, options) {
|
|
|
27417
27946
|
status: "pending",
|
|
27418
27947
|
priority: t.priority
|
|
27419
27948
|
});
|
|
27420
|
-
const taskPath =
|
|
27949
|
+
const taskPath = join15(ctx.rootDir, "plan-tasks", `${id}.md`);
|
|
27421
27950
|
const meta3 = {
|
|
27422
27951
|
kind: "task",
|
|
27423
27952
|
id,
|
|
@@ -27459,7 +27988,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
27459
27988
|
dueDate: input.dueDate,
|
|
27460
27989
|
targetVersion: version2
|
|
27461
27990
|
});
|
|
27462
|
-
const path36 =
|
|
27991
|
+
const path36 = join15(ctx.rootDir, "milestones", `${id}.md`);
|
|
27463
27992
|
const meta3 = {
|
|
27464
27993
|
kind: "milestone",
|
|
27465
27994
|
id,
|
|
@@ -27757,8 +28286,8 @@ function createNfrSpecStub(ctx) {
|
|
|
27757
28286
|
},
|
|
27758
28287
|
planFeatureKeywords: Array.isArray(args["planFeatureKeywords"]) ? args["planFeatureKeywords"] : void 0
|
|
27759
28288
|
};
|
|
27760
|
-
const outPath =
|
|
27761
|
-
|
|
28289
|
+
const outPath = join15(ctx.rootDir, "nfr-spec.json");
|
|
28290
|
+
writeFileSync13(outPath, JSON.stringify(spec, null, 2), "utf8");
|
|
27762
28291
|
return `NFR spec written to nfr-spec.json (${targets.length} target(s)).`;
|
|
27763
28292
|
});
|
|
27764
28293
|
}
|
|
@@ -27821,18 +28350,18 @@ function searchDocumentsStub(ctx) {
|
|
|
27821
28350
|
(w) => w.length >= 3 && w !== "or" && w !== "and" && w !== "the"
|
|
27822
28351
|
);
|
|
27823
28352
|
const files = [
|
|
27824
|
-
...ctx.storage.listMarkdown(
|
|
27825
|
-
...ctx.storage.listMarkdown(
|
|
27826
|
-
...ctx.storage.listMarkdown(
|
|
27827
|
-
...ctx.storage.listMarkdown(
|
|
27828
|
-
...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")),
|
|
27829
28358
|
workspaceFile(ctx.rootDir, "plan"),
|
|
27830
28359
|
workspaceFile(ctx.rootDir, "risks")
|
|
27831
28360
|
];
|
|
27832
28361
|
const results = [];
|
|
27833
28362
|
for (const file2 of files) {
|
|
27834
|
-
if (!
|
|
27835
|
-
const raw =
|
|
28363
|
+
if (!existsSync17(file2)) continue;
|
|
28364
|
+
const raw = readFileSync17(file2, "utf8");
|
|
27836
28365
|
const content = raw.toLowerCase();
|
|
27837
28366
|
let idx = -1;
|
|
27838
28367
|
let matchLen = 0;
|
|
@@ -27883,9 +28412,9 @@ function linkDocumentsStub(ctx) {
|
|
|
27883
28412
|
const toId = args["toId"] ?? args["targetId"] ?? args["targetPathOrTitle"];
|
|
27884
28413
|
if (!fromId || !toId) return "linkDocuments requires fromId and toId.";
|
|
27885
28414
|
const allFiles = [
|
|
27886
|
-
...ctx.storage.listMarkdown(
|
|
27887
|
-
...ctx.storage.listMarkdown(
|
|
27888
|
-
...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"))
|
|
27889
28418
|
];
|
|
27890
28419
|
const source = allFiles.find((f) => {
|
|
27891
28420
|
try {
|
|
@@ -27916,9 +28445,9 @@ function getDocumentBacklinksStub(ctx) {
|
|
|
27916
28445
|
const targetId = args["targetId"] ?? args["id"];
|
|
27917
28446
|
if (!targetId) return "getDocumentBacklinks requires targetId.";
|
|
27918
28447
|
const allFiles = [
|
|
27919
|
-
...ctx.storage.listMarkdown(
|
|
27920
|
-
...ctx.storage.listMarkdown(
|
|
27921
|
-
...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"))
|
|
27922
28451
|
];
|
|
27923
28452
|
const backlinks = [];
|
|
27924
28453
|
for (const file2 of allFiles) {
|
|
@@ -28012,8 +28541,8 @@ __export(updater_exports, {
|
|
|
28012
28541
|
resolveBundledNpmCli: () => resolveBundledNpmCli
|
|
28013
28542
|
});
|
|
28014
28543
|
import { createRequire as createRequire2 } from "node:module";
|
|
28015
|
-
import { spawn as
|
|
28016
|
-
import { existsSync as
|
|
28544
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
28545
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
28017
28546
|
import path23 from "node:path";
|
|
28018
28547
|
import { fileURLToPath } from "node:url";
|
|
28019
28548
|
function resolveBundledNpmCli(execPath = process.execPath) {
|
|
@@ -28026,7 +28555,7 @@ function resolveBundledNpmCli(execPath = process.execPath) {
|
|
|
28026
28555
|
];
|
|
28027
28556
|
for (const candidate of candidates) {
|
|
28028
28557
|
try {
|
|
28029
|
-
if (
|
|
28558
|
+
if (existsSync18(candidate)) return candidate;
|
|
28030
28559
|
} catch {
|
|
28031
28560
|
}
|
|
28032
28561
|
}
|
|
@@ -28099,7 +28628,7 @@ async function checkForUpdate(fetcher = fetch, registryUrl) {
|
|
|
28099
28628
|
updateAvailable: cmp < 0
|
|
28100
28629
|
};
|
|
28101
28630
|
}
|
|
28102
|
-
async function performUpdate(packageName = "zelari-code", executor =
|
|
28631
|
+
async function performUpdate(packageName = "zelari-code", executor = spawn5, resolveNpmCli = resolveBundledNpmCli) {
|
|
28103
28632
|
const args = ["install", "-g", `${packageName}@latest`];
|
|
28104
28633
|
const primary = await runNpm(executor, args, "shim");
|
|
28105
28634
|
if (primary.ok) return primary;
|
|
@@ -28157,7 +28686,7 @@ var init_updater = __esm({
|
|
|
28157
28686
|
});
|
|
28158
28687
|
|
|
28159
28688
|
// src/cli/mcp/mcpClient.ts
|
|
28160
|
-
import { spawn as
|
|
28689
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
28161
28690
|
var DEFAULT_REQUEST_TIMEOUT_MS, INIT_TIMEOUT_MS, MCP_PROTOCOL_VERSION, McpClient;
|
|
28162
28691
|
var init_mcpClient = __esm({
|
|
28163
28692
|
"src/cli/mcp/mcpClient.ts"() {
|
|
@@ -28185,10 +28714,10 @@ var init_mcpClient = __esm({
|
|
|
28185
28714
|
env: { ...process.env, ...this.config.env ?? {} },
|
|
28186
28715
|
windowsHide: true
|
|
28187
28716
|
};
|
|
28188
|
-
const child = process.platform === "win32" ?
|
|
28717
|
+
const child = process.platform === "win32" ? spawn6(buildCmdLine(this.config.command, this.config.args ?? []), {
|
|
28189
28718
|
...spawnOpts,
|
|
28190
28719
|
shell: true
|
|
28191
|
-
}) :
|
|
28720
|
+
}) : spawn6(this.config.command, this.config.args ?? [], spawnOpts);
|
|
28192
28721
|
this.child = child;
|
|
28193
28722
|
child.stdout.setEncoding("utf8");
|
|
28194
28723
|
child.stdout.on("data", (chunk) => this.onStdout(chunk));
|
|
@@ -28332,20 +28861,20 @@ __export(mcpManager_exports, {
|
|
|
28332
28861
|
readMcpConfig: () => readMcpConfig,
|
|
28333
28862
|
registerMcpTools: () => registerMcpTools
|
|
28334
28863
|
});
|
|
28335
|
-
import { existsSync as
|
|
28336
|
-
import { join as
|
|
28337
|
-
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";
|
|
28338
28867
|
function readMcpConfig(projectRoot = process.cwd()) {
|
|
28339
28868
|
const merged = {};
|
|
28340
28869
|
const paths = [
|
|
28341
|
-
|
|
28342
|
-
|
|
28870
|
+
join16(homedir6(), ".zelari-code", "mcp.json"),
|
|
28871
|
+
join16(projectRoot, ".zelari", "mcp.json")
|
|
28343
28872
|
// later = higher precedence
|
|
28344
28873
|
];
|
|
28345
28874
|
for (const p3 of paths) {
|
|
28346
|
-
if (!
|
|
28875
|
+
if (!existsSync19(p3)) continue;
|
|
28347
28876
|
try {
|
|
28348
|
-
const parsed = JSON.parse(
|
|
28877
|
+
const parsed = JSON.parse(readFileSync18(p3, "utf8"));
|
|
28349
28878
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
28350
28879
|
if (!cfg || typeof cfg.command !== "string" || cfg.command.length === 0) continue;
|
|
28351
28880
|
merged[name] = cfg;
|
|
@@ -28434,6 +28963,53 @@ var init_mcpManager = __esm({
|
|
|
28434
28963
|
}
|
|
28435
28964
|
});
|
|
28436
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
|
+
|
|
28437
29013
|
// src/cli/councilConfig.ts
|
|
28438
29014
|
function resolveCouncilTier(opts) {
|
|
28439
29015
|
const env = opts?.env ?? process.env;
|
|
@@ -28477,13 +29053,13 @@ var planDetect_exports = {};
|
|
|
28477
29053
|
__export(planDetect_exports, {
|
|
28478
29054
|
hasWorkspacePlan: () => hasWorkspacePlan
|
|
28479
29055
|
});
|
|
28480
|
-
import { existsSync as
|
|
28481
|
-
import { join as
|
|
29056
|
+
import { existsSync as existsSync21, readFileSync as readFileSync20 } from "node:fs";
|
|
29057
|
+
import { join as join18 } from "node:path";
|
|
28482
29058
|
function hasWorkspacePlan(projectRoot = process.cwd()) {
|
|
28483
|
-
const planPath =
|
|
28484
|
-
if (!
|
|
29059
|
+
const planPath = join18(resolveWorkspaceRoot(projectRoot), "plan.json");
|
|
29060
|
+
if (!existsSync21(planPath)) return false;
|
|
28485
29061
|
try {
|
|
28486
|
-
const parsed = JSON.parse(
|
|
29062
|
+
const parsed = JSON.parse(readFileSync20(planPath, "utf8"));
|
|
28487
29063
|
return Array.isArray(parsed.phases) && parsed.phases.length > 0;
|
|
28488
29064
|
} catch {
|
|
28489
29065
|
return false;
|
|
@@ -28587,13 +29163,13 @@ __export(agentsMd_exports, {
|
|
|
28587
29163
|
serializeAgentsMd: () => serializeAgentsMd,
|
|
28588
29164
|
updateAgentsMd: () => updateAgentsMd
|
|
28589
29165
|
});
|
|
28590
|
-
import { existsSync as
|
|
29166
|
+
import { existsSync as existsSync22, readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "node:fs";
|
|
28591
29167
|
import { createHash as createHash3 } from "node:crypto";
|
|
28592
|
-
import { join as
|
|
29168
|
+
import { join as join19 } from "node:path";
|
|
28593
29169
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
28594
29170
|
async function readPackageJson2(projectRoot) {
|
|
28595
|
-
const path36 =
|
|
28596
|
-
if (!
|
|
29171
|
+
const path36 = join19(projectRoot, "package.json");
|
|
29172
|
+
if (!existsSync22(path36)) return null;
|
|
28597
29173
|
try {
|
|
28598
29174
|
return JSON.parse(await readFile2(path36, "utf8"));
|
|
28599
29175
|
} catch {
|
|
@@ -28617,8 +29193,8 @@ async function genTechStack(ctx) {
|
|
|
28617
29193
|
].join("\n");
|
|
28618
29194
|
}
|
|
28619
29195
|
async function genDecisions(ctx) {
|
|
28620
|
-
const decisionsDir =
|
|
28621
|
-
if (!
|
|
29196
|
+
const decisionsDir = join19(ctx.rootDir, "decisions");
|
|
29197
|
+
if (!existsSync22(decisionsDir)) return "_No ADRs yet._";
|
|
28622
29198
|
const files = ctx.storage.listMarkdown(decisionsDir).sort();
|
|
28623
29199
|
const accepted = [];
|
|
28624
29200
|
const proposed = [];
|
|
@@ -28643,9 +29219,9 @@ async function genDecisions(ctx) {
|
|
|
28643
29219
|
}
|
|
28644
29220
|
async function genConventions(ctx) {
|
|
28645
29221
|
const lines = [];
|
|
28646
|
-
const claudeMd =
|
|
28647
|
-
if (
|
|
28648
|
-
const content =
|
|
29222
|
+
const claudeMd = join19(ctx.projectRoot, "CLAUDE.MD");
|
|
29223
|
+
if (existsSync22(claudeMd)) {
|
|
29224
|
+
const content = readFileSync21(claudeMd, "utf8");
|
|
28649
29225
|
const match = content.match(/## Architecture rules[\s\S]+?(?=\n## |\n*$)/);
|
|
28650
29226
|
if (match) {
|
|
28651
29227
|
lines.push('<!-- Extracted from CLAUDE.MD "Architecture rules" -->');
|
|
@@ -28677,9 +29253,9 @@ async function genBuild(ctx) {
|
|
|
28677
29253
|
].join("\n");
|
|
28678
29254
|
}
|
|
28679
29255
|
async function genOpenQuestions(ctx) {
|
|
28680
|
-
const path36 =
|
|
28681
|
-
if (!
|
|
28682
|
-
const content =
|
|
29256
|
+
const path36 = join19(ctx.rootDir, "risks.md");
|
|
29257
|
+
if (!existsSync22(path36)) return "_No open questions._";
|
|
29258
|
+
const content = readFileSync21(path36, "utf8");
|
|
28683
29259
|
const lines = content.split("\n");
|
|
28684
29260
|
const questions = [];
|
|
28685
29261
|
let currentTitle = "";
|
|
@@ -28753,9 +29329,9 @@ function titleCase(id) {
|
|
|
28753
29329
|
return id.split("-").map((w) => w[0].toUpperCase() + w.slice(1)).join(" ");
|
|
28754
29330
|
}
|
|
28755
29331
|
async function updateAgentsMd(ctx, projectRoot) {
|
|
28756
|
-
const agentsPath =
|
|
28757
|
-
if (
|
|
28758
|
-
const content =
|
|
29332
|
+
const agentsPath = join19(projectRoot, "AGENTS.MD");
|
|
29333
|
+
if (existsSync22(agentsPath)) {
|
|
29334
|
+
const content = readFileSync21(agentsPath, "utf8");
|
|
28759
29335
|
const hasAnyMarker = AUTO_SECTIONS.some((id) => content.includes(MARKER_OPEN(id)));
|
|
28760
29336
|
if (!hasAnyMarker) {
|
|
28761
29337
|
return {
|
|
@@ -28770,8 +29346,8 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
28770
29346
|
newSections.set(id, await GENERATORS[id](ctx));
|
|
28771
29347
|
}
|
|
28772
29348
|
let manualContent = "";
|
|
28773
|
-
if (
|
|
28774
|
-
const { manualBlocks } = parseAgentsMd(
|
|
29349
|
+
if (existsSync22(agentsPath)) {
|
|
29350
|
+
const { manualBlocks } = parseAgentsMd(readFileSync21(agentsPath, "utf8"));
|
|
28775
29351
|
manualContent = manualBlocks.after;
|
|
28776
29352
|
} else {
|
|
28777
29353
|
const projectName2 = projectName(projectRoot);
|
|
@@ -28787,7 +29363,7 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
28787
29363
|
""
|
|
28788
29364
|
].join("\n");
|
|
28789
29365
|
}
|
|
28790
|
-
const oldContent =
|
|
29366
|
+
const oldContent = existsSync22(agentsPath) ? readFileSync21(agentsPath, "utf8") : "";
|
|
28791
29367
|
const { sections: oldSections } = parseAgentsMd(oldContent);
|
|
28792
29368
|
const changedSections = [];
|
|
28793
29369
|
for (const id of AUTO_SECTIONS) {
|
|
@@ -28799,7 +29375,7 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
28799
29375
|
return { changed: false, sections: [] };
|
|
28800
29376
|
}
|
|
28801
29377
|
const newContent = serializeAgentsMd(manualContent, newSections);
|
|
28802
|
-
|
|
29378
|
+
writeFileSync14(agentsPath, newContent, "utf8");
|
|
28803
29379
|
return { changed: true, sections: changedSections };
|
|
28804
29380
|
}
|
|
28805
29381
|
function hash2(s) {
|
|
@@ -28924,9 +29500,9 @@ var init_completeDesign = __esm({
|
|
|
28924
29500
|
});
|
|
28925
29501
|
|
|
28926
29502
|
// src/cli/workspace/projectSmoke.ts
|
|
28927
|
-
import { spawn as
|
|
28928
|
-
import { existsSync as
|
|
28929
|
-
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";
|
|
28930
29506
|
function pickSmokeScript(scripts) {
|
|
28931
29507
|
if (!scripts) return null;
|
|
28932
29508
|
for (const name of SMOKE_SCRIPT_PRIORITY) {
|
|
@@ -28938,13 +29514,13 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
28938
29514
|
if (process.env["ZELARI_SMOKE"] === "0") {
|
|
28939
29515
|
return { ran: false, reason: "ZELARI_SMOKE=0 (disabled)" };
|
|
28940
29516
|
}
|
|
28941
|
-
const pkgPath =
|
|
28942
|
-
if (!
|
|
29517
|
+
const pkgPath = join20(projectRoot, "package.json");
|
|
29518
|
+
if (!existsSync23(pkgPath)) {
|
|
28943
29519
|
return { ran: false, reason: "no package.json (skipped)" };
|
|
28944
29520
|
}
|
|
28945
29521
|
let scripts = {};
|
|
28946
29522
|
try {
|
|
28947
|
-
const pkg = JSON.parse(
|
|
29523
|
+
const pkg = JSON.parse(readFileSync22(pkgPath, "utf8"));
|
|
28948
29524
|
scripts = pkg.scripts ?? {};
|
|
28949
29525
|
} catch {
|
|
28950
29526
|
return { ran: false, reason: "package.json unreadable (skipped)" };
|
|
@@ -28954,12 +29530,12 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
28954
29530
|
return { ran: false, reason: "no typecheck/test/build script (skipped)" };
|
|
28955
29531
|
}
|
|
28956
29532
|
return await new Promise((resolveRun) => {
|
|
28957
|
-
const child = process.platform === "win32" ?
|
|
29533
|
+
const child = process.platform === "win32" ? spawn7(buildCmdLine("npm.cmd", ["run", script]), {
|
|
28958
29534
|
cwd: projectRoot,
|
|
28959
29535
|
stdio: ["ignore", "pipe", "pipe"],
|
|
28960
29536
|
env: process.env,
|
|
28961
29537
|
shell: true
|
|
28962
|
-
}) :
|
|
29538
|
+
}) : spawn7("npm", ["run", script], {
|
|
28963
29539
|
cwd: projectRoot,
|
|
28964
29540
|
stdio: ["ignore", "pipe", "pipe"],
|
|
28965
29541
|
env: process.env
|
|
@@ -29031,9 +29607,9 @@ __export(postCouncilHook_exports, {
|
|
|
29031
29607
|
runImplementationVerificationHook: () => runImplementationVerificationHook,
|
|
29032
29608
|
runPostCouncilHook: () => runPostCouncilHook
|
|
29033
29609
|
});
|
|
29034
|
-
import { spawn as
|
|
29035
|
-
import { existsSync as
|
|
29036
|
-
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";
|
|
29037
29613
|
async function runCompleteDesignPostProcessor(ctx, options) {
|
|
29038
29614
|
if (options?.runMode === "implementation") {
|
|
29039
29615
|
return {
|
|
@@ -29044,9 +29620,9 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
29044
29620
|
if (process.env["ZELARI_COMPLETE_DESIGN"] === "0") {
|
|
29045
29621
|
return { ran: false, reason: "ZELARI_COMPLETE_DESIGN=0 (disabled)" };
|
|
29046
29622
|
}
|
|
29047
|
-
const planJsonPath2 =
|
|
29048
|
-
const scriptPath =
|
|
29049
|
-
if (!
|
|
29623
|
+
const planJsonPath2 = join21(ctx.rootDir, "plan.json");
|
|
29624
|
+
const scriptPath = join21(ctx.projectRoot, "complete-design.mjs");
|
|
29625
|
+
if (!existsSync24(planJsonPath2)) {
|
|
29050
29626
|
return {
|
|
29051
29627
|
ran: false,
|
|
29052
29628
|
reason: ".zelari/plan.json missing (not design-phase)"
|
|
@@ -29054,7 +29630,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
29054
29630
|
}
|
|
29055
29631
|
let phaseCount = 0;
|
|
29056
29632
|
try {
|
|
29057
|
-
const parsed = JSON.parse(
|
|
29633
|
+
const parsed = JSON.parse(readFileSync23(planJsonPath2, "utf8"));
|
|
29058
29634
|
phaseCount = Array.isArray(parsed.phases) ? parsed.phases.length : 0;
|
|
29059
29635
|
} catch {
|
|
29060
29636
|
return { ran: false, reason: ".zelari/plan.json corrupt" };
|
|
@@ -29062,7 +29638,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
29062
29638
|
if (phaseCount === 0) {
|
|
29063
29639
|
return { ran: false, reason: ".zelari/plan.json has no phases" };
|
|
29064
29640
|
}
|
|
29065
|
-
if (!
|
|
29641
|
+
if (!existsSync24(scriptPath)) {
|
|
29066
29642
|
try {
|
|
29067
29643
|
const builtin = await runBuiltinCompleteDesign(ctx);
|
|
29068
29644
|
return {
|
|
@@ -29080,7 +29656,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
29080
29656
|
}
|
|
29081
29657
|
}
|
|
29082
29658
|
return await new Promise((resolveRun) => {
|
|
29083
|
-
const child =
|
|
29659
|
+
const child = spawn8(process.execPath, [scriptPath], {
|
|
29084
29660
|
cwd: ctx.projectRoot,
|
|
29085
29661
|
stdio: ["ignore", "pipe", "pipe"],
|
|
29086
29662
|
env: process.env
|
|
@@ -29283,12 +29859,12 @@ var buildLessonsSummary_exports = {};
|
|
|
29283
29859
|
__export(buildLessonsSummary_exports, {
|
|
29284
29860
|
buildLessonsSummary: () => buildLessonsSummary
|
|
29285
29861
|
});
|
|
29286
|
-
import { existsSync as
|
|
29287
|
-
import { join as
|
|
29862
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
29863
|
+
import { join as join22 } from "node:path";
|
|
29288
29864
|
function buildLessonsSummary(projectRoot = process.cwd(), taskText) {
|
|
29289
29865
|
if (process.env["ZELARI_LESSONS"] === "0") return null;
|
|
29290
29866
|
const zelariRoot = resolveWorkspaceRoot(projectRoot);
|
|
29291
|
-
if (!
|
|
29867
|
+
if (!existsSync25(join22(zelariRoot, "lessons.jsonl"))) return null;
|
|
29292
29868
|
const lessons = recallLessons(zelariRoot, {
|
|
29293
29869
|
maxLessons: 5,
|
|
29294
29870
|
maxBytes: 2048,
|
|
@@ -29311,10 +29887,10 @@ __export(councilFeedback_exports, {
|
|
|
29311
29887
|
});
|
|
29312
29888
|
import {
|
|
29313
29889
|
promises as fs13,
|
|
29314
|
-
existsSync as
|
|
29315
|
-
readFileSync as
|
|
29316
|
-
writeFileSync as
|
|
29317
|
-
mkdirSync as
|
|
29890
|
+
existsSync as existsSync26,
|
|
29891
|
+
readFileSync as readFileSync24,
|
|
29892
|
+
writeFileSync as writeFileSync15,
|
|
29893
|
+
mkdirSync as mkdirSync10
|
|
29318
29894
|
} from "node:fs";
|
|
29319
29895
|
import path24 from "node:path";
|
|
29320
29896
|
import os8 from "node:os";
|
|
@@ -29420,9 +29996,9 @@ var init_councilFeedback = __esm({
|
|
|
29420
29996
|
}
|
|
29421
29997
|
// --- persistence ---------------------------------------------------------
|
|
29422
29998
|
load() {
|
|
29423
|
-
if (!
|
|
29999
|
+
if (!existsSync26(this.file)) return;
|
|
29424
30000
|
try {
|
|
29425
|
-
const raw =
|
|
30001
|
+
const raw = readFileSync24(this.file, "utf-8");
|
|
29426
30002
|
const parsed = JSON.parse(raw);
|
|
29427
30003
|
if (parsed && Array.isArray(parsed.entries)) {
|
|
29428
30004
|
this.entries = parsed.entries.filter(
|
|
@@ -29433,8 +30009,8 @@ var init_councilFeedback = __esm({
|
|
|
29433
30009
|
}
|
|
29434
30010
|
}
|
|
29435
30011
|
save() {
|
|
29436
|
-
|
|
29437
|
-
|
|
30012
|
+
mkdirSync10(path24.dirname(this.file), { recursive: true });
|
|
30013
|
+
writeFileSync15(
|
|
29438
30014
|
this.file,
|
|
29439
30015
|
JSON.stringify({ entries: this.entries }, null, 2),
|
|
29440
30016
|
{ encoding: "utf-8", mode: 384 }
|
|
@@ -29891,8 +30467,8 @@ __export(prereqChecks_exports, {
|
|
|
29891
30467
|
runPrereqChecks: () => runPrereqChecks
|
|
29892
30468
|
});
|
|
29893
30469
|
import { execSync, spawnSync as spawnSync2 } from "node:child_process";
|
|
29894
|
-
import { existsSync as
|
|
29895
|
-
import { dirname as
|
|
30470
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
30471
|
+
import { dirname as dirname5 } from "node:path";
|
|
29896
30472
|
function isWslBashPath2(p3) {
|
|
29897
30473
|
if (!p3 || typeof p3 !== "string") return false;
|
|
29898
30474
|
const n = p3.replace(/\//g, "\\").toLowerCase();
|
|
@@ -29954,7 +30530,7 @@ function resolveAgentShellSync() {
|
|
|
29954
30530
|
function agentProbeEnv() {
|
|
29955
30531
|
const env = { ...process.env };
|
|
29956
30532
|
try {
|
|
29957
|
-
const nodeDir =
|
|
30533
|
+
const nodeDir = dirname5(process.execPath);
|
|
29958
30534
|
if (!nodeDir) return env;
|
|
29959
30535
|
const sep = process.platform === "win32" ? ";" : ":";
|
|
29960
30536
|
const current = env.PATH ?? env.Path ?? "";
|
|
@@ -29971,7 +30547,7 @@ function agentProbeEnv() {
|
|
|
29971
30547
|
}
|
|
29972
30548
|
function existsSyncSafe2(p3) {
|
|
29973
30549
|
try {
|
|
29974
|
-
return
|
|
30550
|
+
return existsSync27(p3);
|
|
29975
30551
|
} catch {
|
|
29976
30552
|
return false;
|
|
29977
30553
|
}
|
|
@@ -30190,7 +30766,7 @@ var init_prereqChecks = __esm({
|
|
|
30190
30766
|
});
|
|
30191
30767
|
|
|
30192
30768
|
// src/cli/plugins/prefs.ts
|
|
30193
|
-
import { existsSync as
|
|
30769
|
+
import { existsSync as existsSync28, readFileSync as readFileSync25, writeFileSync as writeFileSync16, mkdirSync as mkdirSync11 } from "node:fs";
|
|
30194
30770
|
import path29 from "node:path";
|
|
30195
30771
|
import os9 from "node:os";
|
|
30196
30772
|
function getPluginPrefsPath() {
|
|
@@ -30199,8 +30775,8 @@ function getPluginPrefsPath() {
|
|
|
30199
30775
|
function getPluginPrefs() {
|
|
30200
30776
|
const file2 = getPluginPrefsPath();
|
|
30201
30777
|
try {
|
|
30202
|
-
if (!
|
|
30203
|
-
const raw =
|
|
30778
|
+
if (!existsSync28(file2)) return { ...DEFAULTS2, dontAskAgain: {} };
|
|
30779
|
+
const raw = readFileSync25(file2, "utf-8");
|
|
30204
30780
|
const parsed = JSON.parse(raw);
|
|
30205
30781
|
if (parsed && typeof parsed === "object" && parsed.dontAskAgain && typeof parsed.dontAskAgain === "object") {
|
|
30206
30782
|
const clean = {};
|
|
@@ -30215,8 +30791,8 @@ function getPluginPrefs() {
|
|
|
30215
30791
|
}
|
|
30216
30792
|
function writePluginPrefs(prefs) {
|
|
30217
30793
|
const file2 = getPluginPrefsPath();
|
|
30218
|
-
|
|
30219
|
-
|
|
30794
|
+
mkdirSync11(path29.dirname(file2), { recursive: true });
|
|
30795
|
+
writeFileSync16(file2, JSON.stringify(prefs, null, 2), {
|
|
30220
30796
|
encoding: "utf-8",
|
|
30221
30797
|
mode: 384
|
|
30222
30798
|
});
|
|
@@ -30251,7 +30827,7 @@ __export(registry_exports, {
|
|
|
30251
30827
|
findPlugin: () => findPlugin,
|
|
30252
30828
|
isBinaryOnPath: () => isBinaryOnPath
|
|
30253
30829
|
});
|
|
30254
|
-
import { existsSync as
|
|
30830
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
30255
30831
|
import path30 from "node:path";
|
|
30256
30832
|
function detectLocalBin(bin) {
|
|
30257
30833
|
return (cwd) => {
|
|
@@ -30268,7 +30844,7 @@ function isBinaryOnPath(bin, opts = {}) {
|
|
|
30268
30844
|
return false;
|
|
30269
30845
|
}
|
|
30270
30846
|
const platform = opts.platform ?? process.platform;
|
|
30271
|
-
const exists = opts.exists ??
|
|
30847
|
+
const exists = opts.exists ?? existsSync29;
|
|
30272
30848
|
const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
|
|
30273
30849
|
const pathMod = platform === "win32" ? path30.win32 : path30.posix;
|
|
30274
30850
|
const sep = platform === "win32" ? ";" : ":";
|
|
@@ -30419,7 +30995,7 @@ __export(doctor_exports, {
|
|
|
30419
30995
|
runDoctor: () => runDoctor
|
|
30420
30996
|
});
|
|
30421
30997
|
import { execSync as execSync2 } from "node:child_process";
|
|
30422
|
-
import { existsSync as
|
|
30998
|
+
import { existsSync as existsSync35, readFileSync as readFileSync30, readlinkSync, statSync as statSync6 } from "node:fs";
|
|
30423
30999
|
import { createRequire as createRequire3 } from "node:module";
|
|
30424
31000
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
30425
31001
|
import path35 from "node:path";
|
|
@@ -30427,9 +31003,9 @@ function findPackageRoot(start) {
|
|
|
30427
31003
|
let dir = start;
|
|
30428
31004
|
for (let i = 0; i < 6; i += 1) {
|
|
30429
31005
|
const candidate = path35.join(dir, "package.json");
|
|
30430
|
-
if (
|
|
31006
|
+
if (existsSync35(candidate)) {
|
|
30431
31007
|
try {
|
|
30432
|
-
const pkg = JSON.parse(
|
|
31008
|
+
const pkg = JSON.parse(readFileSync30(candidate, "utf8"));
|
|
30433
31009
|
if (pkg.name === "zelari-code") return dir;
|
|
30434
31010
|
} catch {
|
|
30435
31011
|
}
|
|
@@ -30453,7 +31029,7 @@ function tryExec(cmd) {
|
|
|
30453
31029
|
function readPackageJson3() {
|
|
30454
31030
|
try {
|
|
30455
31031
|
const pkgPath = path35.join(packageRoot, "package.json");
|
|
30456
|
-
return JSON.parse(
|
|
31032
|
+
return JSON.parse(readFileSync30(pkgPath, "utf8"));
|
|
30457
31033
|
} catch {
|
|
30458
31034
|
return null;
|
|
30459
31035
|
}
|
|
@@ -30469,7 +31045,7 @@ function checkShim(pkgName) {
|
|
|
30469
31045
|
const isWin = process.platform === "win32";
|
|
30470
31046
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
30471
31047
|
const shimPath = path35.join(prefix, shimName);
|
|
30472
|
-
if (!
|
|
31048
|
+
if (!existsSync35(shimPath)) {
|
|
30473
31049
|
return FAIL(
|
|
30474
31050
|
`shim not found at ${shimPath}
|
|
30475
31051
|
fix: npm install -g ${pkgName}@latest --force`
|
|
@@ -30478,7 +31054,7 @@ function checkShim(pkgName) {
|
|
|
30478
31054
|
try {
|
|
30479
31055
|
const st = statSync6(shimPath);
|
|
30480
31056
|
if (isWin) {
|
|
30481
|
-
const content =
|
|
31057
|
+
const content = readFileSync30(shimPath, "utf8");
|
|
30482
31058
|
if (content.includes(`${pkgName}\\bin\\`) || content.includes(`${pkgName}/bin/`)) {
|
|
30483
31059
|
return OK(`shim OK at ${shimPath} (${st.size} bytes)`);
|
|
30484
31060
|
}
|
|
@@ -30537,7 +31113,7 @@ function checkNode(pkg) {
|
|
|
30537
31113
|
}
|
|
30538
31114
|
function checkBundle() {
|
|
30539
31115
|
const bundle = path35.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
30540
|
-
if (!
|
|
31116
|
+
if (!existsSync35(bundle)) {
|
|
30541
31117
|
return FAIL(
|
|
30542
31118
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
30543
31119
|
fix: npm run build:cli (then reinstall or run via tsx)`
|
|
@@ -34670,13 +35246,22 @@ function useChatTurn(params) {
|
|
|
34670
35246
|
let systemPrompt;
|
|
34671
35247
|
try {
|
|
34672
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;
|
|
34673
35253
|
systemPrompt = buildSystemPrompt(singleAgentRole, {
|
|
34674
35254
|
tools: getAllTools(),
|
|
34675
35255
|
toolNames: toolListNames,
|
|
35256
|
+
mode: "agent",
|
|
35257
|
+
projectInstructions: projectInstructions || void 0,
|
|
34676
35258
|
aiConfig: {
|
|
34677
35259
|
enabledSkills: [],
|
|
34678
35260
|
enabledTools: toolListNames,
|
|
34679
|
-
customPromptModules: [
|
|
35261
|
+
customPromptModules: [
|
|
35262
|
+
SINGLE_AGENT_IDENTITY_MODULE,
|
|
35263
|
+
languageModule
|
|
35264
|
+
],
|
|
34680
35265
|
agentSkillConfigs: []
|
|
34681
35266
|
},
|
|
34682
35267
|
workspaceContext: workspaceContext || void 0,
|
|
@@ -36466,8 +37051,8 @@ init_registry2();
|
|
|
36466
37051
|
// src/cli/plugins/installer.ts
|
|
36467
37052
|
init_cmdline();
|
|
36468
37053
|
init_updater();
|
|
36469
|
-
import { spawn as
|
|
36470
|
-
async function installPlugin(spec, cwd, executor =
|
|
37054
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
37055
|
+
async function installPlugin(spec, cwd, executor = spawn9) {
|
|
36471
37056
|
const scopeFlag = spec.installScope === "global" ? "-g" : "-D";
|
|
36472
37057
|
const args = ["install", scopeFlag, spec.npmPackage];
|
|
36473
37058
|
const primary = await runNpm2(executor, args, cwd, "shim");
|
|
@@ -36594,7 +37179,7 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
36594
37179
|
}
|
|
36595
37180
|
|
|
36596
37181
|
// src/cli/branchManager.ts
|
|
36597
|
-
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";
|
|
36598
37183
|
import path32 from "node:path";
|
|
36599
37184
|
import os11 from "node:os";
|
|
36600
37185
|
var META_FILENAME = "meta.json";
|
|
@@ -36616,11 +37201,11 @@ function sessionsPathFor(name, baseDir) {
|
|
|
36616
37201
|
}
|
|
36617
37202
|
function readBranchMeta(name, baseDir) {
|
|
36618
37203
|
const metaPath = metaPathFor(name, baseDir);
|
|
36619
|
-
if (!
|
|
37204
|
+
if (!existsSync30(metaPath)) {
|
|
36620
37205
|
throw new BranchNotFoundError(`Branch "${name}" not found`);
|
|
36621
37206
|
}
|
|
36622
37207
|
try {
|
|
36623
|
-
const raw =
|
|
37208
|
+
const raw = readFileSync26(metaPath, "utf-8");
|
|
36624
37209
|
const parsed = JSON.parse(raw);
|
|
36625
37210
|
if (!parsed || typeof parsed !== "object" || typeof parsed.name !== "string" || typeof parsed.createdAt !== "number" || typeof parsed.fromSessionId !== "string") {
|
|
36626
37211
|
throw new BranchCorruptError(`Branch "${name}" meta.json is malformed`);
|
|
@@ -36637,8 +37222,8 @@ function readBranchMeta(name, baseDir) {
|
|
|
36637
37222
|
}
|
|
36638
37223
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
36639
37224
|
const metaPath = metaPathFor(name, baseDir);
|
|
36640
|
-
|
|
36641
|
-
|
|
37225
|
+
mkdirSync12(path32.dirname(metaPath), { recursive: true });
|
|
37226
|
+
writeFileSync17(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
36642
37227
|
}
|
|
36643
37228
|
async function countSessions(name, baseDir) {
|
|
36644
37229
|
const sessionsPath = sessionsPathFor(name, baseDir);
|
|
@@ -36676,7 +37261,7 @@ var SessionNotFoundError = class extends Error {
|
|
|
36676
37261
|
};
|
|
36677
37262
|
function branchExists(name, baseDir = getBranchesBaseDir()) {
|
|
36678
37263
|
const bp = branchPathFor(name, baseDir);
|
|
36679
|
-
return
|
|
37264
|
+
return existsSync30(bp) && existsSync30(metaPathFor(name, baseDir));
|
|
36680
37265
|
}
|
|
36681
37266
|
async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
|
|
36682
37267
|
if (!name || name.trim().length === 0) {
|
|
@@ -36689,12 +37274,12 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
36689
37274
|
throw new BranchAlreadyExistsError(name);
|
|
36690
37275
|
}
|
|
36691
37276
|
const sourcePath = path32.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
36692
|
-
if (!
|
|
37277
|
+
if (!existsSync30(sourcePath)) {
|
|
36693
37278
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
36694
37279
|
}
|
|
36695
37280
|
const branchPath = branchPathFor(name, baseDir);
|
|
36696
37281
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
36697
|
-
|
|
37282
|
+
mkdirSync12(branchSessionsPath, { recursive: true });
|
|
36698
37283
|
const destPath = path32.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
36699
37284
|
await fs17.copyFile(sourcePath, destPath);
|
|
36700
37285
|
const meta3 = {
|
|
@@ -36722,7 +37307,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
|
36722
37307
|
const results = [];
|
|
36723
37308
|
for (const entry of entries) {
|
|
36724
37309
|
const metaPath = metaPathFor(entry, baseDir);
|
|
36725
|
-
if (!
|
|
37310
|
+
if (!existsSync30(metaPath)) continue;
|
|
36726
37311
|
try {
|
|
36727
37312
|
const meta3 = readBranchMeta(entry, baseDir);
|
|
36728
37313
|
const sessionCount = await countSessions(entry, baseDir);
|
|
@@ -37258,7 +37843,7 @@ import path34 from "node:path";
|
|
|
37258
37843
|
import os12 from "node:os";
|
|
37259
37844
|
|
|
37260
37845
|
// src/cli/skillHistory.ts
|
|
37261
|
-
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";
|
|
37262
37847
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
37263
37848
|
async function readSkillHistory(file2) {
|
|
37264
37849
|
let raw = "";
|
|
@@ -38436,9 +39021,9 @@ function ContinueKey({ onContinue }) {
|
|
|
38436
39021
|
init_providerConfig();
|
|
38437
39022
|
|
|
38438
39023
|
// src/cli/wizard/firstRun.ts
|
|
38439
|
-
import { existsSync as
|
|
39024
|
+
import { existsSync as existsSync32 } from "node:fs";
|
|
38440
39025
|
function shouldRunWizard(input) {
|
|
38441
|
-
const exists = input.exists ??
|
|
39026
|
+
const exists = input.exists ?? existsSync32;
|
|
38442
39027
|
if (input.hasResetConfigFlag) {
|
|
38443
39028
|
return { shouldRun: true, reason: "--reset-config flag forced wizard" };
|
|
38444
39029
|
}
|
|
@@ -38723,7 +39308,7 @@ init_keyStore();
|
|
|
38723
39308
|
init_providerConfig();
|
|
38724
39309
|
init_openai_compatible();
|
|
38725
39310
|
init_phase();
|
|
38726
|
-
import { readFileSync as
|
|
39311
|
+
import { readFileSync as readFileSync27 } from "node:fs";
|
|
38727
39312
|
function parseHeadlessFlags(argv) {
|
|
38728
39313
|
if (!argv.includes("--headless")) {
|
|
38729
39314
|
return { options: null };
|
|
@@ -38791,7 +39376,7 @@ function parseHeadlessFlags(argv) {
|
|
|
38791
39376
|
let raw = null;
|
|
38792
39377
|
if (arg === "--history-file") {
|
|
38793
39378
|
try {
|
|
38794
|
-
raw =
|
|
39379
|
+
raw = readFileSync27(next, "utf-8");
|
|
38795
39380
|
} catch {
|
|
38796
39381
|
raw = null;
|
|
38797
39382
|
}
|
|
@@ -38963,11 +39548,50 @@ ${err.stack}` : "";
|
|
|
38963
39548
|
function planModeFromOpts(opts) {
|
|
38964
39549
|
return (opts.phase ?? "build") === "plan";
|
|
38965
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
|
+
}
|
|
38966
39589
|
async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
38967
39590
|
const sessionId = crypto.randomUUID();
|
|
38968
39591
|
const { registry: toolRegistry } = createBuiltinToolRegistry({
|
|
38969
39592
|
planMode: planModeFromOpts(opts)
|
|
38970
39593
|
});
|
|
39594
|
+
await registerHeadlessMcp(toolRegistry, opts);
|
|
38971
39595
|
const tools = toolRegistry.toOpenAITools().map((t) => ({
|
|
38972
39596
|
name: t.function.name,
|
|
38973
39597
|
description: t.function.description,
|
|
@@ -39004,21 +39628,38 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
39004
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."
|
|
39005
39629
|
].join("\n")
|
|
39006
39630
|
};
|
|
39007
|
-
|
|
39008
|
-
|
|
39009
|
-
|
|
39010
|
-
|
|
39011
|
-
|
|
39012
|
-
|
|
39013
|
-
|
|
39014
|
-
|
|
39015
|
-
|
|
39016
|
-
|
|
39017
|
-
|
|
39018
|
-
|
|
39019
|
-
|
|
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
|
+
}
|
|
39020
39661
|
}
|
|
39021
|
-
|
|
39662
|
+
);
|
|
39022
39663
|
} catch {
|
|
39023
39664
|
systemPrompt = [
|
|
39024
39665
|
"You are zelari-code, a CLI coding agent. Be concise and direct.",
|
|
@@ -39117,7 +39758,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
39117
39758
|
if (finalReason === "error") return 3;
|
|
39118
39759
|
return exitCode;
|
|
39119
39760
|
}
|
|
39120
|
-
async function buildCouncilToolRegistry(planMode) {
|
|
39761
|
+
async function buildCouncilToolRegistry(planMode, opts) {
|
|
39121
39762
|
const { registry: toolRegistry } = createBuiltinToolRegistry({ planMode });
|
|
39122
39763
|
const { createWorkspaceContext: createWorkspaceContext2, createWorkspaceStubs: createWorkspaceStubs2 } = await Promise.resolve().then(() => (init_stubs(), stubs_exports));
|
|
39123
39764
|
const { createWorkspaceToolRegistry: createWorkspaceToolRegistry2 } = await Promise.resolve().then(() => (init_toolRegistry2(), toolRegistry_exports2));
|
|
@@ -39129,18 +39770,30 @@ async function buildCouncilToolRegistry(planMode) {
|
|
|
39129
39770
|
if (td) toolRegistry.register(td);
|
|
39130
39771
|
}
|
|
39131
39772
|
setWorkspaceStubs2(createWorkspaceStubs2(realCtx));
|
|
39773
|
+
if (opts) {
|
|
39774
|
+
await registerHeadlessMcp(toolRegistry, opts);
|
|
39775
|
+
}
|
|
39132
39776
|
return { toolRegistry, workspaceCtx: realCtx };
|
|
39133
39777
|
}
|
|
39134
39778
|
async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
39135
39779
|
const { dispatchCouncil: dispatchCouncil2 } = await Promise.resolve().then(() => (init_councilDispatcher(), councilDispatcher_exports));
|
|
39136
39780
|
const sessionId = crypto.randomUUID();
|
|
39137
|
-
const { toolRegistry } = await buildCouncilToolRegistry(
|
|
39781
|
+
const { toolRegistry } = await buildCouncilToolRegistry(
|
|
39782
|
+
planModeFromOpts(opts),
|
|
39783
|
+
opts
|
|
39784
|
+
);
|
|
39138
39785
|
const { FeedbackStore: FeedbackStore2 } = await Promise.resolve().then(() => (init_councilFeedback(), councilFeedback_exports));
|
|
39139
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);
|
|
39140
39791
|
let exitCode = 0;
|
|
39141
39792
|
const scrub = createStreamScrubber();
|
|
39793
|
+
let lastAssistantText = "";
|
|
39794
|
+
let currentAssistantText = "";
|
|
39142
39795
|
try {
|
|
39143
|
-
for await (const event of dispatchCouncil2(
|
|
39796
|
+
for await (const event of dispatchCouncil2(effectiveTask, {
|
|
39144
39797
|
apiKey: "REDACTED",
|
|
39145
39798
|
model,
|
|
39146
39799
|
provider: "openai-compatible",
|
|
@@ -39152,9 +39805,11 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
|
39152
39805
|
})) {
|
|
39153
39806
|
if (event.type === "message_start") {
|
|
39154
39807
|
scrub.reset();
|
|
39808
|
+
currentAssistantText = "";
|
|
39155
39809
|
}
|
|
39156
39810
|
if (event.type === "message_delta" && typeof event.delta === "string") {
|
|
39157
39811
|
const cleanDelta = scrub.push(event.delta);
|
|
39812
|
+
if (cleanDelta.length > 0) currentAssistantText += cleanDelta;
|
|
39158
39813
|
if (opts.output === "json") {
|
|
39159
39814
|
if (cleanDelta.length > 0) emitEvent({ ...event, delta: cleanDelta });
|
|
39160
39815
|
} else if (opts.output === "plain" && cleanDelta.length > 0) {
|
|
@@ -39162,10 +39817,18 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
|
39162
39817
|
}
|
|
39163
39818
|
} else {
|
|
39164
39819
|
if (opts.output === "json") emitEvent(event);
|
|
39165
|
-
if (event.type === "agent_end") {
|
|
39820
|
+
if (event.type === "message_end" || event.type === "agent_end") {
|
|
39166
39821
|
const tail = scrub.flush();
|
|
39167
|
-
if (
|
|
39168
|
-
|
|
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
|
+
}
|
|
39169
39832
|
} else if (event.type === "error" && event.severity === "fatal") {
|
|
39170
39833
|
exitCode = 2;
|
|
39171
39834
|
}
|
|
@@ -39178,6 +39841,16 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
|
39178
39841
|
);
|
|
39179
39842
|
return 2;
|
|
39180
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
|
+
}
|
|
39181
39854
|
return exitCode;
|
|
39182
39855
|
}
|
|
39183
39856
|
async function runHeadlessZelari(opts, provider, model, providerStream) {
|
|
@@ -39194,7 +39867,10 @@ async function runHeadlessZelari(opts, provider, model, providerStream) {
|
|
|
39194
39867
|
hasPlan: hasWorkspacePlan2(projectRoot)
|
|
39195
39868
|
});
|
|
39196
39869
|
const memory = await getMemoryBackend2(projectRoot);
|
|
39197
|
-
const { toolRegistry, workspaceCtx } = await buildCouncilToolRegistry(
|
|
39870
|
+
const { toolRegistry, workspaceCtx } = await buildCouncilToolRegistry(
|
|
39871
|
+
planModeFromOpts(opts),
|
|
39872
|
+
opts
|
|
39873
|
+
);
|
|
39198
39874
|
const feedbackStore = new FeedbackStore2();
|
|
39199
39875
|
const chairmanBudget = envNumber(process.env.ZELARI_MODE_MAX_TOOLS_LUCIFER, {
|
|
39200
39876
|
default: 30,
|
|
@@ -39207,11 +39883,16 @@ async function runHeadlessZelari(opts, provider, model, providerStream) {
|
|
|
39207
39883
|
process.stderr.write(message + "\n");
|
|
39208
39884
|
}
|
|
39209
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);
|
|
39210
39890
|
emit(`[zelari] mission brief
|
|
39211
39891
|
${JSON.stringify({ deliverable: brief.deliverableThisMission, mvp: brief.sliceMvp?.title }, null, 0)}`);
|
|
39212
39892
|
let exitCode = 0;
|
|
39893
|
+
let lastMissionAssistant = "";
|
|
39213
39894
|
try {
|
|
39214
|
-
const state2 = await runZelariMission2(
|
|
39895
|
+
const state2 = await runZelariMission2(missionTask, brief, {
|
|
39215
39896
|
projectRoot,
|
|
39216
39897
|
memory,
|
|
39217
39898
|
emit,
|
|
@@ -39293,6 +39974,11 @@ ${ragContext}` : slicePrompt;
|
|
|
39293
39974
|
}
|
|
39294
39975
|
} catch {
|
|
39295
39976
|
}
|
|
39977
|
+
if (synthesisText.trim()) {
|
|
39978
|
+
lastMissionAssistant = cleanAgentContent(synthesisText, {
|
|
39979
|
+
stripQuestion: false
|
|
39980
|
+
});
|
|
39981
|
+
}
|
|
39296
39982
|
return {
|
|
39297
39983
|
completionOk,
|
|
39298
39984
|
ran: membersCompleted > 0 || synthesisText.length > 0,
|
|
@@ -39314,6 +40000,18 @@ ${ragContext}` : slicePrompt;
|
|
|
39314
40000
|
} finally {
|
|
39315
40001
|
await memory.close().catch(() => void 0);
|
|
39316
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
|
+
}
|
|
39317
40015
|
return exitCode;
|
|
39318
40016
|
}
|
|
39319
40017
|
|
|
@@ -39564,9 +40262,9 @@ async function runDiscoverModels(providerArg) {
|
|
|
39564
40262
|
|
|
39565
40263
|
// src/cli/skillsMd.ts
|
|
39566
40264
|
init_skills2();
|
|
39567
|
-
import { existsSync as
|
|
39568
|
-
import { join as
|
|
39569
|
-
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";
|
|
39570
40268
|
var CODING_CATEGORIES = /* @__PURE__ */ new Set([
|
|
39571
40269
|
"plan",
|
|
39572
40270
|
"refactor",
|
|
@@ -39581,10 +40279,10 @@ var CODING_CATEGORIES = /* @__PURE__ */ new Set([
|
|
|
39581
40279
|
]);
|
|
39582
40280
|
function skillMdSearchDirs(projectRoot = process.cwd()) {
|
|
39583
40281
|
return [
|
|
39584
|
-
|
|
39585
|
-
|
|
39586
|
-
|
|
39587
|
-
|
|
40282
|
+
join25(projectRoot, ".zelari", "skills"),
|
|
40283
|
+
join25(projectRoot, ".claude", "skills"),
|
|
40284
|
+
join25(projectRoot, ".opencode", "skills"),
|
|
40285
|
+
join25(homedir7(), ".zelari-code", "skills")
|
|
39588
40286
|
];
|
|
39589
40287
|
}
|
|
39590
40288
|
function parseSkillMd(content, sourcePath) {
|
|
@@ -39642,7 +40340,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
39642
40340
|
const summary = { loaded: [], skipped: [] };
|
|
39643
40341
|
const seen = new Set(options.existingIds ?? []);
|
|
39644
40342
|
for (const dir of skillMdSearchDirs(projectRoot)) {
|
|
39645
|
-
if (!
|
|
40343
|
+
if (!existsSync33(dir)) continue;
|
|
39646
40344
|
let entries;
|
|
39647
40345
|
try {
|
|
39648
40346
|
entries = readdirSync4(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -39650,10 +40348,10 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
39650
40348
|
continue;
|
|
39651
40349
|
}
|
|
39652
40350
|
for (const entry of entries) {
|
|
39653
|
-
const skillPath =
|
|
39654
|
-
if (!
|
|
40351
|
+
const skillPath = join25(dir, entry, "SKILL.md");
|
|
40352
|
+
if (!existsSync33(skillPath)) continue;
|
|
39655
40353
|
try {
|
|
39656
|
-
const parsed = parseSkillMd(
|
|
40354
|
+
const parsed = parseSkillMd(readFileSync28(skillPath, "utf8"), skillPath);
|
|
39657
40355
|
if (!parsed) {
|
|
39658
40356
|
summary.skipped.push({ path: skillPath, reason: "missing/invalid frontmatter (name, description) or empty body" });
|
|
39659
40357
|
continue;
|
|
@@ -39676,6 +40374,116 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
39676
40374
|
// src/cli/main.ts
|
|
39677
40375
|
init_skills2();
|
|
39678
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();
|
|
39679
40487
|
var VERSION = getCurrentVersion();
|
|
39680
40488
|
function runPreflight() {
|
|
39681
40489
|
if (process.env.ZELARI_SKIP_PREFLIGHT === "1") return;
|
|
@@ -39772,10 +40580,190 @@ function pickRootComponent() {
|
|
|
39772
40580
|
}
|
|
39773
40581
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
39774
40582
|
console.log(
|
|
39775
|
-
"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"
|
|
39776
40584
|
);
|
|
39777
40585
|
process.exit(0);
|
|
39778
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
|
+
}
|
|
39779
40767
|
if (wantsPrintConfig(argv)) {
|
|
39780
40768
|
try {
|
|
39781
40769
|
printDesktopConfig();
|