chainlesschain 0.37.9 → 0.37.11
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 +309 -19
- package/bin/chainlesschain.js +4 -0
- package/package.json +1 -1
- package/src/commands/a2a.js +374 -0
- package/src/commands/audit.js +286 -0
- package/src/commands/auth.js +387 -0
- package/src/commands/bi.js +240 -0
- package/src/commands/browse.js +184 -0
- package/src/commands/cowork.js +317 -0
- package/src/commands/did.js +376 -0
- package/src/commands/economy.js +375 -0
- package/src/commands/encrypt.js +233 -0
- package/src/commands/evolution.js +398 -0
- package/src/commands/export.js +125 -0
- package/src/commands/git.js +215 -0
- package/src/commands/hmemory.js +273 -0
- package/src/commands/hook.js +260 -0
- package/src/commands/import.js +259 -0
- package/src/commands/init.js +184 -0
- package/src/commands/instinct.js +202 -0
- package/src/commands/llm.js +155 -4
- package/src/commands/lowcode.js +320 -0
- package/src/commands/mcp.js +302 -0
- package/src/commands/memory.js +282 -0
- package/src/commands/note.js +187 -0
- package/src/commands/org.js +505 -0
- package/src/commands/p2p.js +274 -0
- package/src/commands/plugin.js +451 -0
- package/src/commands/sandbox.js +366 -0
- package/src/commands/search.js +237 -0
- package/src/commands/session.js +238 -0
- package/src/commands/skill.js +254 -201
- package/src/commands/sync.js +249 -0
- package/src/commands/tokens.js +214 -0
- package/src/commands/wallet.js +416 -0
- package/src/commands/workflow.js +359 -0
- package/src/commands/zkp.js +277 -0
- package/src/index.js +93 -1
- package/src/lib/a2a-protocol.js +371 -0
- package/src/lib/agent-coordinator.js +273 -0
- package/src/lib/agent-economy.js +369 -0
- package/src/lib/app-builder.js +377 -0
- package/src/lib/audit-logger.js +364 -0
- package/src/lib/bi-engine.js +299 -0
- package/src/lib/bm25-search.js +322 -0
- package/src/lib/browser-automation.js +216 -0
- package/src/lib/cowork/ab-comparator-cli.js +180 -0
- package/src/lib/cowork/code-knowledge-graph-cli.js +232 -0
- package/src/lib/cowork/debate-review-cli.js +144 -0
- package/src/lib/cowork/decision-kb-cli.js +153 -0
- package/src/lib/cowork/project-style-analyzer-cli.js +168 -0
- package/src/lib/cowork-adapter.js +106 -0
- package/src/lib/crypto-manager.js +246 -0
- package/src/lib/did-manager.js +270 -0
- package/src/lib/ensure-utf8.js +59 -0
- package/src/lib/evolution-system.js +508 -0
- package/src/lib/git-integration.js +220 -0
- package/src/lib/hierarchical-memory.js +471 -0
- package/src/lib/hook-manager.js +387 -0
- package/src/lib/instinct-manager.js +190 -0
- package/src/lib/knowledge-exporter.js +302 -0
- package/src/lib/knowledge-importer.js +293 -0
- package/src/lib/llm-providers.js +325 -0
- package/src/lib/mcp-client.js +413 -0
- package/src/lib/memory-manager.js +211 -0
- package/src/lib/note-versioning.js +244 -0
- package/src/lib/org-manager.js +424 -0
- package/src/lib/p2p-manager.js +317 -0
- package/src/lib/pdf-parser.js +96 -0
- package/src/lib/permission-engine.js +374 -0
- package/src/lib/plan-mode.js +333 -0
- package/src/lib/plugin-manager.js +430 -0
- package/src/lib/project-detector.js +53 -0
- package/src/lib/response-cache.js +156 -0
- package/src/lib/sandbox-v2.js +503 -0
- package/src/lib/service-container.js +183 -0
- package/src/lib/session-manager.js +189 -0
- package/src/lib/skill-loader.js +274 -0
- package/src/lib/sync-manager.js +347 -0
- package/src/lib/token-tracker.js +200 -0
- package/src/lib/wallet-manager.js +348 -0
- package/src/lib/workflow-engine.js +503 -0
- package/src/lib/zkp-engine.js +241 -0
- package/src/repl/agent-repl.js +259 -124
package/src/repl/agent-repl.js
CHANGED
|
@@ -24,6 +24,8 @@ import path from "path";
|
|
|
24
24
|
import { execSync } from "child_process";
|
|
25
25
|
import { fileURLToPath } from "url";
|
|
26
26
|
import { logger } from "../lib/logger.js";
|
|
27
|
+
import { getPlanModeManager, PlanState } from "../lib/plan-mode.js";
|
|
28
|
+
import { CLISkillLoader } from "../lib/skill-loader.js";
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
31
|
* Tool definitions for function calling
|
|
@@ -188,82 +190,34 @@ const TOOLS = [
|
|
|
188
190
|
];
|
|
189
191
|
|
|
190
192
|
/**
|
|
191
|
-
*
|
|
193
|
+
* Shared multi-layer skill loader
|
|
192
194
|
*/
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
function findSkillsDir() {
|
|
196
|
-
const candidates = [
|
|
197
|
-
path.resolve(
|
|
198
|
-
__agentDirname,
|
|
199
|
-
"../../../../../desktop-app-vue/src/main/ai-engine/cowork/skills/builtin",
|
|
200
|
-
),
|
|
201
|
-
path.resolve(
|
|
202
|
-
process.cwd(),
|
|
203
|
-
"desktop-app-vue/src/main/ai-engine/cowork/skills/builtin",
|
|
204
|
-
),
|
|
205
|
-
];
|
|
206
|
-
for (const dir of candidates) {
|
|
207
|
-
if (fs.existsSync(dir)) return dir;
|
|
208
|
-
}
|
|
209
|
-
return null;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function loadSkillList(skillsDir) {
|
|
213
|
-
const skills = [];
|
|
214
|
-
try {
|
|
215
|
-
const dirs = fs.readdirSync(skillsDir, { withFileTypes: true });
|
|
216
|
-
for (const dir of dirs) {
|
|
217
|
-
if (!dir.isDirectory()) continue;
|
|
218
|
-
const skillMd = path.join(skillsDir, dir.name, "SKILL.md");
|
|
219
|
-
if (!fs.existsSync(skillMd)) continue;
|
|
220
|
-
try {
|
|
221
|
-
const content = fs.readFileSync(skillMd, "utf8");
|
|
222
|
-
const lines = content.split("\n");
|
|
223
|
-
if (lines[0].trim() !== "---") continue;
|
|
224
|
-
let endIndex = -1;
|
|
225
|
-
for (let i = 1; i < lines.length; i++) {
|
|
226
|
-
if (lines[i].trim() === "---") {
|
|
227
|
-
endIndex = i;
|
|
228
|
-
break;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
if (endIndex === -1) continue;
|
|
232
|
-
const data = {};
|
|
233
|
-
for (const line of lines.slice(1, endIndex)) {
|
|
234
|
-
const ci = line.indexOf(":");
|
|
235
|
-
if (ci > 0) {
|
|
236
|
-
const key = line.slice(0, ci).trim();
|
|
237
|
-
const val = line
|
|
238
|
-
.slice(ci + 1)
|
|
239
|
-
.trim()
|
|
240
|
-
.replace(/^['"]|['"]$/g, "");
|
|
241
|
-
data[key] = val;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
skills.push({
|
|
245
|
-
id: data.name || dir.name,
|
|
246
|
-
description: data.description || "",
|
|
247
|
-
category: data.category || "uncategorized",
|
|
248
|
-
dirName: dir.name,
|
|
249
|
-
hasHandler: fs.existsSync(
|
|
250
|
-
path.join(skillsDir, dir.name, "handler.js"),
|
|
251
|
-
),
|
|
252
|
-
});
|
|
253
|
-
} catch {
|
|
254
|
-
// Skip malformed skill files
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
} catch {
|
|
258
|
-
// Skills dir unreadable
|
|
259
|
-
}
|
|
260
|
-
return skills;
|
|
261
|
-
}
|
|
195
|
+
const skillLoader = new CLISkillLoader();
|
|
262
196
|
|
|
263
197
|
/**
|
|
264
|
-
* Execute a tool call
|
|
198
|
+
* Execute a tool call (with plan mode filtering)
|
|
265
199
|
*/
|
|
266
200
|
async function executeTool(name, args) {
|
|
201
|
+
// Plan mode: check if tool is allowed
|
|
202
|
+
const planManager = getPlanModeManager();
|
|
203
|
+
if (planManager.isActive() && !planManager.isToolAllowed(name)) {
|
|
204
|
+
// In plan mode, log the blocked tool as a plan item
|
|
205
|
+
planManager.addPlanItem({
|
|
206
|
+
title: `${name}: ${formatToolArgs(name, args)}`,
|
|
207
|
+
tool: name,
|
|
208
|
+
params: args,
|
|
209
|
+
estimatedImpact:
|
|
210
|
+
name === "run_shell"
|
|
211
|
+
? "high"
|
|
212
|
+
: name === "write_file"
|
|
213
|
+
? "medium"
|
|
214
|
+
: "low",
|
|
215
|
+
});
|
|
216
|
+
return {
|
|
217
|
+
error: `[Plan Mode] Tool "${name}" is blocked during planning. It has been added to the plan. Use /plan approve to execute.`,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
267
221
|
switch (name) {
|
|
268
222
|
case "read_file": {
|
|
269
223
|
const filePath = path.resolve(args.path);
|
|
@@ -373,50 +327,28 @@ async function executeTool(name, args) {
|
|
|
373
327
|
}
|
|
374
328
|
|
|
375
329
|
case "run_skill": {
|
|
376
|
-
const
|
|
377
|
-
if (
|
|
330
|
+
const allSkills = skillLoader.getResolvedSkills();
|
|
331
|
+
if (allSkills.length === 0) {
|
|
378
332
|
return {
|
|
379
333
|
error:
|
|
380
|
-
"
|
|
334
|
+
"No skills found. Make sure you're in the ChainlessChain project root or have skills installed.",
|
|
381
335
|
};
|
|
382
336
|
}
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
const match = skills.find(
|
|
388
|
-
(s) => s.id === args.skill_name || s.dirName === args.skill_name,
|
|
389
|
-
);
|
|
390
|
-
if (match && match.hasHandler) {
|
|
391
|
-
const matchedPath = path.join(skillsDir, match.dirName, "handler.js");
|
|
392
|
-
try {
|
|
393
|
-
const handler = (
|
|
394
|
-
await import(`file://${matchedPath.replace(/\\/g, "/")}`)
|
|
395
|
-
).default;
|
|
396
|
-
const task = {
|
|
397
|
-
params: { input: args.input },
|
|
398
|
-
input: args.input,
|
|
399
|
-
action: args.input,
|
|
400
|
-
};
|
|
401
|
-
const context = {
|
|
402
|
-
projectRoot: process.cwd(),
|
|
403
|
-
workspacePath: process.cwd(),
|
|
404
|
-
};
|
|
405
|
-
const result = await handler.execute(task, context);
|
|
406
|
-
return result;
|
|
407
|
-
} catch (err) {
|
|
408
|
-
return { error: `Skill execution failed: ${err.message}` };
|
|
409
|
-
}
|
|
410
|
-
}
|
|
337
|
+
const match = allSkills.find(
|
|
338
|
+
(s) => s.id === args.skill_name || s.dirName === args.skill_name,
|
|
339
|
+
);
|
|
340
|
+
if (!match || !match.hasHandler) {
|
|
411
341
|
return {
|
|
412
342
|
error: `Skill "${args.skill_name}" not found or has no handler. Use list_skills to see available skills.`,
|
|
413
343
|
};
|
|
414
344
|
}
|
|
415
345
|
try {
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
346
|
+
const handlerPath = path.join(match.skillDir, "handler.js");
|
|
347
|
+
const imported = await import(
|
|
348
|
+
`file://${handlerPath.replace(/\\/g, "/")}`
|
|
349
|
+
);
|
|
350
|
+
const handler = imported.default || imported;
|
|
351
|
+
if (handler.init) await handler.init(match);
|
|
420
352
|
const task = {
|
|
421
353
|
params: { input: args.input },
|
|
422
354
|
input: args.input,
|
|
@@ -426,7 +358,7 @@ async function executeTool(name, args) {
|
|
|
426
358
|
projectRoot: process.cwd(),
|
|
427
359
|
workspacePath: process.cwd(),
|
|
428
360
|
};
|
|
429
|
-
const result = await handler.execute(task, context);
|
|
361
|
+
const result = await handler.execute(task, context, match);
|
|
430
362
|
return result;
|
|
431
363
|
} catch (err) {
|
|
432
364
|
return { error: `Skill execution failed: ${err.message}` };
|
|
@@ -434,11 +366,10 @@ async function executeTool(name, args) {
|
|
|
434
366
|
}
|
|
435
367
|
|
|
436
368
|
case "list_skills": {
|
|
437
|
-
|
|
438
|
-
if (
|
|
439
|
-
return { error: "
|
|
369
|
+
let skills = skillLoader.getResolvedSkills();
|
|
370
|
+
if (skills.length === 0) {
|
|
371
|
+
return { error: "No skills found." };
|
|
440
372
|
}
|
|
441
|
-
let skills = loadSkillList(skillsDir);
|
|
442
373
|
if (args.category) {
|
|
443
374
|
skills = skills.filter(
|
|
444
375
|
(s) => s.category.toLowerCase() === args.category.toLowerCase(),
|
|
@@ -458,8 +389,9 @@ async function executeTool(name, args) {
|
|
|
458
389
|
skills: skills.map((s) => ({
|
|
459
390
|
id: s.id,
|
|
460
391
|
category: s.category,
|
|
392
|
+
source: s.source,
|
|
461
393
|
hasHandler: s.hasHandler,
|
|
462
|
-
description: s.description.substring(0, 80),
|
|
394
|
+
description: (s.description || "").substring(0, 80),
|
|
463
395
|
})),
|
|
464
396
|
};
|
|
465
397
|
}
|
|
@@ -520,6 +452,9 @@ async function chatWithTools(messages, options) {
|
|
|
520
452
|
|
|
521
453
|
const data = await response.json();
|
|
522
454
|
// Normalize to Ollama-like format
|
|
455
|
+
if (!data.choices || !data.choices[0]) {
|
|
456
|
+
throw new Error("Invalid API response: no choices returned");
|
|
457
|
+
}
|
|
523
458
|
const choice = data.choices[0];
|
|
524
459
|
return {
|
|
525
460
|
message: choice.message,
|
|
@@ -537,7 +472,11 @@ async function agentLoop(messages, options) {
|
|
|
537
472
|
|
|
538
473
|
for (let i = 0; i < MAX_ITERATIONS; i++) {
|
|
539
474
|
const result = await chatWithTools(messages, options);
|
|
540
|
-
const msg = result
|
|
475
|
+
const msg = result?.message;
|
|
476
|
+
|
|
477
|
+
if (!msg) {
|
|
478
|
+
return "(No response from LLM)";
|
|
479
|
+
}
|
|
541
480
|
|
|
542
481
|
// Check for tool calls
|
|
543
482
|
const toolCalls = msg.tool_calls;
|
|
@@ -626,7 +565,7 @@ Key behaviors:
|
|
|
626
565
|
- When asked to create something, use write_file to create it
|
|
627
566
|
- When asked to run/test something, use run_shell to execute it
|
|
628
567
|
- When asked about files or code, use read_file and search_files to find information
|
|
629
|
-
- You have
|
|
568
|
+
- You have multi-layer skills (built-in, marketplace, global, project-level) — use list_skills to discover them and run_skill to execute them
|
|
630
569
|
- Always explain what you're doing and show results
|
|
631
570
|
- Be concise but thorough
|
|
632
571
|
|
|
@@ -643,10 +582,22 @@ export async function startAgentRepl(options = {}) {
|
|
|
643
582
|
|
|
644
583
|
const messages = [{ role: "system", content: SYSTEM_PROMPT }];
|
|
645
584
|
|
|
585
|
+
const getPrompt = () => {
|
|
586
|
+
const planManager = getPlanModeManager();
|
|
587
|
+
if (planManager.isActive()) {
|
|
588
|
+
const state = planManager.state;
|
|
589
|
+
if (state === PlanState.APPROVED || state === PlanState.EXECUTING) {
|
|
590
|
+
return chalk.green("[plan:exec] > ");
|
|
591
|
+
}
|
|
592
|
+
return chalk.yellow("[plan] > ");
|
|
593
|
+
}
|
|
594
|
+
return chalk.green("> ");
|
|
595
|
+
};
|
|
596
|
+
|
|
646
597
|
const rl = readline.createInterface({
|
|
647
598
|
input: process.stdin,
|
|
648
599
|
output: process.stdout,
|
|
649
|
-
prompt:
|
|
600
|
+
prompt: getPrompt(),
|
|
650
601
|
terminal: true,
|
|
651
602
|
});
|
|
652
603
|
|
|
@@ -661,12 +612,16 @@ export async function startAgentRepl(options = {}) {
|
|
|
661
612
|
);
|
|
662
613
|
logger.log(chalk.gray("Type /exit to quit, /help for commands\n"));
|
|
663
614
|
|
|
664
|
-
|
|
615
|
+
const prompt = () => {
|
|
616
|
+
rl.setPrompt(getPrompt());
|
|
617
|
+
rl.prompt();
|
|
618
|
+
};
|
|
619
|
+
prompt();
|
|
665
620
|
|
|
666
621
|
rl.on("line", async (input) => {
|
|
667
622
|
const trimmed = input.trim();
|
|
668
623
|
if (!trimmed) {
|
|
669
|
-
|
|
624
|
+
prompt();
|
|
670
625
|
return;
|
|
671
626
|
}
|
|
672
627
|
|
|
@@ -686,13 +641,24 @@ export async function startAgentRepl(options = {}) {
|
|
|
686
641
|
logger.log(` ${chalk.cyan("/provider")} Show/change provider`);
|
|
687
642
|
logger.log(` ${chalk.cyan("/clear")} Clear conversation`);
|
|
688
643
|
logger.log(` ${chalk.cyan("/compact")} Keep only last 4 messages`);
|
|
644
|
+
logger.log(
|
|
645
|
+
` ${chalk.cyan("/cowork")} Multi-agent collaboration (debate, compare)`,
|
|
646
|
+
);
|
|
647
|
+
logger.log(
|
|
648
|
+
` ${chalk.cyan("/plan")} Enter plan mode (read-only analysis first)`,
|
|
649
|
+
);
|
|
650
|
+
logger.log(` ${chalk.cyan("/plan show")} Show current plan`);
|
|
651
|
+
logger.log(
|
|
652
|
+
` ${chalk.cyan("/plan approve")} Approve and execute the plan`,
|
|
653
|
+
);
|
|
654
|
+
logger.log(` ${chalk.cyan("/plan reject")} Reject the plan`);
|
|
689
655
|
logger.log(chalk.bold("\nCapabilities:"));
|
|
690
656
|
logger.log(" Read, write, and edit files");
|
|
691
657
|
logger.log(" Run shell commands (git, npm, etc.)");
|
|
692
658
|
logger.log(" Search codebase by filename or content");
|
|
693
659
|
logger.log(" Run 138 built-in skills (code-review, summarize, etc.)");
|
|
694
|
-
logger.log("
|
|
695
|
-
|
|
660
|
+
logger.log(" Plan mode: analyze first, execute after approval\n");
|
|
661
|
+
prompt();
|
|
696
662
|
return;
|
|
697
663
|
}
|
|
698
664
|
|
|
@@ -704,7 +670,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
704
670
|
} else {
|
|
705
671
|
logger.info(`Current model: ${chalk.cyan(model)}`);
|
|
706
672
|
}
|
|
707
|
-
|
|
673
|
+
prompt();
|
|
708
674
|
return;
|
|
709
675
|
}
|
|
710
676
|
|
|
@@ -716,14 +682,14 @@ export async function startAgentRepl(options = {}) {
|
|
|
716
682
|
} else {
|
|
717
683
|
logger.info(`Current provider: ${chalk.cyan(provider)}`);
|
|
718
684
|
}
|
|
719
|
-
|
|
685
|
+
prompt();
|
|
720
686
|
return;
|
|
721
687
|
}
|
|
722
688
|
|
|
723
689
|
if (trimmed === "/clear") {
|
|
724
690
|
messages.length = 1; // Keep system prompt
|
|
725
691
|
logger.info("Conversation cleared");
|
|
726
|
-
|
|
692
|
+
prompt();
|
|
727
693
|
return;
|
|
728
694
|
}
|
|
729
695
|
|
|
@@ -736,7 +702,176 @@ export async function startAgentRepl(options = {}) {
|
|
|
736
702
|
messages.push(systemMsg, ...recent);
|
|
737
703
|
logger.info("Conversation compacted to last 4 messages");
|
|
738
704
|
}
|
|
739
|
-
|
|
705
|
+
prompt();
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Cowork commands
|
|
710
|
+
if (trimmed.startsWith("/cowork")) {
|
|
711
|
+
const coworkArgs = trimmed.slice(7).trim();
|
|
712
|
+
const [subCmd, ...rest] = coworkArgs.split(/\s+/);
|
|
713
|
+
const coworkInput = rest.join(" ");
|
|
714
|
+
|
|
715
|
+
if (!subCmd || subCmd === "help") {
|
|
716
|
+
logger.log(chalk.bold("\nCowork Commands:"));
|
|
717
|
+
logger.log(
|
|
718
|
+
` ${chalk.cyan("/cowork debate <file>")} Multi-perspective code review`,
|
|
719
|
+
);
|
|
720
|
+
logger.log(
|
|
721
|
+
` ${chalk.cyan("/cowork compare <prompt>")} A/B solution comparison`,
|
|
722
|
+
);
|
|
723
|
+
logger.log("");
|
|
724
|
+
} else if (subCmd === "debate" && coworkInput) {
|
|
725
|
+
try {
|
|
726
|
+
const { startDebate } =
|
|
727
|
+
await import("../lib/cowork/debate-review-cli.js");
|
|
728
|
+
let code = coworkInput;
|
|
729
|
+
let targetLabel = coworkInput;
|
|
730
|
+
const resolved = path.resolve(coworkInput);
|
|
731
|
+
if (fs.existsSync(resolved)) {
|
|
732
|
+
code = fs.readFileSync(resolved, "utf-8");
|
|
733
|
+
targetLabel = resolved;
|
|
734
|
+
if (code.length > 15000) {
|
|
735
|
+
code = code.substring(0, 15000) + "\n... (truncated)";
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
process.stdout.write(chalk.gray("\n Running debate review...\n"));
|
|
739
|
+
const result = await startDebate({
|
|
740
|
+
target: targetLabel,
|
|
741
|
+
code,
|
|
742
|
+
llmOptions: { provider, model, baseUrl, apiKey },
|
|
743
|
+
});
|
|
744
|
+
for (const review of result.reviews) {
|
|
745
|
+
const vc =
|
|
746
|
+
review.verdict === "APPROVE"
|
|
747
|
+
? chalk.green
|
|
748
|
+
: review.verdict === "REJECT"
|
|
749
|
+
? chalk.red
|
|
750
|
+
: chalk.yellow;
|
|
751
|
+
process.stdout.write(
|
|
752
|
+
` ${chalk.bold(review.role)}: ${vc(review.verdict)}\n`,
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
process.stdout.write(
|
|
756
|
+
`\n ${chalk.bold("Verdict:")} ${result.verdict} Consensus: ${result.consensusScore}%\n\n`,
|
|
757
|
+
);
|
|
758
|
+
// Add summary to conversation for context
|
|
759
|
+
messages.push({
|
|
760
|
+
role: "assistant",
|
|
761
|
+
content: `[Cowork Debate Result] ${result.verdict} (consensus: ${result.consensusScore}%)\n${result.summary.substring(0, 500)}`,
|
|
762
|
+
});
|
|
763
|
+
} catch (err) {
|
|
764
|
+
logger.error(`Debate failed: ${err.message}`);
|
|
765
|
+
}
|
|
766
|
+
} else if (subCmd === "compare" && coworkInput) {
|
|
767
|
+
try {
|
|
768
|
+
const { compare } =
|
|
769
|
+
await import("../lib/cowork/ab-comparator-cli.js");
|
|
770
|
+
process.stdout.write(chalk.gray("\n Generating variants...\n"));
|
|
771
|
+
const result = await compare({
|
|
772
|
+
prompt: coworkInput,
|
|
773
|
+
llmOptions: { provider, model, baseUrl, apiKey },
|
|
774
|
+
});
|
|
775
|
+
for (const v of result.variants) {
|
|
776
|
+
process.stdout.write(
|
|
777
|
+
` ${chalk.cyan(v.name)}: score ${v.totalScore}\n`,
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
process.stdout.write(
|
|
781
|
+
`\n ${chalk.bold("Winner:")} ${chalk.green(result.winner)}\n\n`,
|
|
782
|
+
);
|
|
783
|
+
messages.push({
|
|
784
|
+
role: "assistant",
|
|
785
|
+
content: `[Cowork Compare Result] Winner: ${result.winner}. ${result.reason}`,
|
|
786
|
+
});
|
|
787
|
+
} catch (err) {
|
|
788
|
+
logger.error(`Compare failed: ${err.message}`);
|
|
789
|
+
}
|
|
790
|
+
} else {
|
|
791
|
+
logger.info(
|
|
792
|
+
"Usage: /cowork debate <file-or-topic> or /cowork compare <prompt>",
|
|
793
|
+
);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
prompt();
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// Plan mode commands
|
|
801
|
+
if (trimmed.startsWith("/plan")) {
|
|
802
|
+
const planManager = getPlanModeManager();
|
|
803
|
+
const subCmd = trimmed.slice(5).trim();
|
|
804
|
+
|
|
805
|
+
if (!subCmd || subCmd === "enter") {
|
|
806
|
+
if (planManager.isActive()) {
|
|
807
|
+
logger.info(
|
|
808
|
+
"Already in plan mode. Use /plan show, /plan approve, or /plan reject.",
|
|
809
|
+
);
|
|
810
|
+
} else {
|
|
811
|
+
planManager.enterPlanMode({ title: "Agent Plan" });
|
|
812
|
+
logger.success(
|
|
813
|
+
"Entered plan mode. Write tools are blocked until you approve the plan.",
|
|
814
|
+
);
|
|
815
|
+
logger.info(
|
|
816
|
+
"The AI can still read files and search. Blocked tools become plan items.",
|
|
817
|
+
);
|
|
818
|
+
logger.info(
|
|
819
|
+
"Use /plan show to see the plan, /plan approve to execute.",
|
|
820
|
+
);
|
|
821
|
+
// Inject plan mode context into system prompt
|
|
822
|
+
messages.push({
|
|
823
|
+
role: "system",
|
|
824
|
+
content:
|
|
825
|
+
"[PLAN MODE ACTIVE] You are now in plan mode. You can read files, search, and analyze — but write/execute tools are blocked. Any blocked tool calls will be recorded as plan items. Analyze the task thoroughly, then the user will approve your plan.",
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
} else if (subCmd === "show") {
|
|
829
|
+
if (!planManager.isActive()) {
|
|
830
|
+
logger.info("Not in plan mode. Use /plan to enter.");
|
|
831
|
+
} else {
|
|
832
|
+
logger.log("\n" + planManager.generatePlanSummary() + "\n");
|
|
833
|
+
}
|
|
834
|
+
} else if (subCmd === "approve" || subCmd === "yes") {
|
|
835
|
+
if (!planManager.isActive()) {
|
|
836
|
+
logger.info("No plan to approve.");
|
|
837
|
+
} else if (planManager.currentPlan.items.length === 0) {
|
|
838
|
+
logger.info(
|
|
839
|
+
"Plan has no items yet. Let the AI analyze the task first.",
|
|
840
|
+
);
|
|
841
|
+
} else {
|
|
842
|
+
planManager.approvePlan();
|
|
843
|
+
logger.success(
|
|
844
|
+
`Plan approved! ${planManager.currentPlan.items.length} items ready for execution.`,
|
|
845
|
+
);
|
|
846
|
+
logger.info(
|
|
847
|
+
"Write/execute tools are now unlocked. The AI can proceed.",
|
|
848
|
+
);
|
|
849
|
+
messages.push({
|
|
850
|
+
role: "system",
|
|
851
|
+
content: `[PLAN APPROVED] The user has approved your plan with ${planManager.currentPlan.items.length} items. You can now use all tools including write_file, edit_file, run_shell, and run_skill. Execute the plan items in order.`,
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
} else if (subCmd === "reject" || subCmd === "no") {
|
|
855
|
+
if (!planManager.isActive()) {
|
|
856
|
+
logger.info("No plan to reject.");
|
|
857
|
+
} else {
|
|
858
|
+
planManager.rejectPlan("User rejected");
|
|
859
|
+
logger.info("Plan rejected. Exited plan mode.");
|
|
860
|
+
}
|
|
861
|
+
} else if (subCmd === "exit") {
|
|
862
|
+
if (planManager.isActive()) {
|
|
863
|
+
planManager.exitPlanMode({ savePlan: true });
|
|
864
|
+
logger.info("Exited plan mode.");
|
|
865
|
+
} else {
|
|
866
|
+
logger.info("Not in plan mode.");
|
|
867
|
+
}
|
|
868
|
+
} else {
|
|
869
|
+
logger.info(
|
|
870
|
+
"Unknown /plan subcommand. Try: /plan, /plan show, /plan approve, /plan reject, /plan exit",
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
prompt();
|
|
740
875
|
return;
|
|
741
876
|
}
|
|
742
877
|
|
|
@@ -773,7 +908,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
773
908
|
}
|
|
774
909
|
}
|
|
775
910
|
|
|
776
|
-
|
|
911
|
+
prompt();
|
|
777
912
|
});
|
|
778
913
|
|
|
779
914
|
rl.on("close", () => {
|