@smartledger.technology/openai-claw 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +134 -0
  3. package/dist/agent.js +262 -0
  4. package/dist/agent.js.map +1 -0
  5. package/dist/autopr/index.js +127 -0
  6. package/dist/autopr/index.js.map +1 -0
  7. package/dist/client.js +199 -0
  8. package/dist/client.js.map +1 -0
  9. package/dist/commands/index.js +624 -0
  10. package/dist/commands/index.js.map +1 -0
  11. package/dist/config.js +71 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/cost.js +97 -0
  14. package/dist/cost.js.map +1 -0
  15. package/dist/eval/cli.js +32 -0
  16. package/dist/eval/cli.js.map +1 -0
  17. package/dist/eval/index.js +134 -0
  18. package/dist/eval/index.js.map +1 -0
  19. package/dist/hooks/index.js +69 -0
  20. package/dist/hooks/index.js.map +1 -0
  21. package/dist/index.js +246 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/input.js +96 -0
  24. package/dist/input.js.map +1 -0
  25. package/dist/mcp/index.js +135 -0
  26. package/dist/mcp/index.js.map +1 -0
  27. package/dist/memory/compaction.js +70 -0
  28. package/dist/memory/compaction.js.map +1 -0
  29. package/dist/memory/index.js +56 -0
  30. package/dist/memory/index.js.map +1 -0
  31. package/dist/notifications/index.js +80 -0
  32. package/dist/notifications/index.js.map +1 -0
  33. package/dist/permissions/index.js +104 -0
  34. package/dist/permissions/index.js.map +1 -0
  35. package/dist/planmode.js +12 -0
  36. package/dist/planmode.js.map +1 -0
  37. package/dist/plugins/index.js +239 -0
  38. package/dist/plugins/index.js.map +1 -0
  39. package/dist/prompts/system.js +148 -0
  40. package/dist/prompts/system.js.map +1 -0
  41. package/dist/rag/index.js +158 -0
  42. package/dist/rag/index.js.map +1 -0
  43. package/dist/self-review/index.js +157 -0
  44. package/dist/self-review/index.js.map +1 -0
  45. package/dist/session.js +113 -0
  46. package/dist/session.js.map +1 -0
  47. package/dist/skills/index.js +39 -0
  48. package/dist/skills/index.js.map +1 -0
  49. package/dist/subagent.js +128 -0
  50. package/dist/subagent.js.map +1 -0
  51. package/dist/subagents/index.js +61 -0
  52. package/dist/subagents/index.js.map +1 -0
  53. package/dist/tools/bash.js +94 -0
  54. package/dist/tools/bash.js.map +1 -0
  55. package/dist/tools/edit.js +64 -0
  56. package/dist/tools/edit.js.map +1 -0
  57. package/dist/tools/glob.js +34 -0
  58. package/dist/tools/glob.js.map +1 -0
  59. package/dist/tools/grep.js +87 -0
  60. package/dist/tools/grep.js.map +1 -0
  61. package/dist/tools/index.js +44 -0
  62. package/dist/tools/index.js.map +1 -0
  63. package/dist/tools/ls.js +39 -0
  64. package/dist/tools/ls.js.map +1 -0
  65. package/dist/tools/read.js +126 -0
  66. package/dist/tools/read.js.map +1 -0
  67. package/dist/tools/semantic.js +40 -0
  68. package/dist/tools/semantic.js.map +1 -0
  69. package/dist/tools/shell.js +131 -0
  70. package/dist/tools/shell.js.map +1 -0
  71. package/dist/tools/task.js +91 -0
  72. package/dist/tools/task.js.map +1 -0
  73. package/dist/tools/todo.js +64 -0
  74. package/dist/tools/todo.js.map +1 -0
  75. package/dist/tools/types.js +7 -0
  76. package/dist/tools/types.js.map +1 -0
  77. package/dist/tools/webfetch.js +62 -0
  78. package/dist/tools/webfetch.js.map +1 -0
  79. package/dist/tools/websearch.js +82 -0
  80. package/dist/tools/websearch.js.map +1 -0
  81. package/dist/tools/write.js +39 -0
  82. package/dist/tools/write.js.map +1 -0
  83. package/dist/ui/repl.js +203 -0
  84. package/dist/ui/repl.js.map +1 -0
  85. package/dist/ui/tui/App.js +234 -0
  86. package/dist/ui/tui/App.js.map +1 -0
  87. package/dist/ui/tui/Diff.js +38 -0
  88. package/dist/ui/tui/Diff.js.map +1 -0
  89. package/dist/ui/tui/MessageView.js +50 -0
  90. package/dist/ui/tui/MessageView.js.map +1 -0
  91. package/dist/ui/tui/PermissionPrompt.js +47 -0
  92. package/dist/ui/tui/PermissionPrompt.js.map +1 -0
  93. package/dist/ui/tui/SlashSuggest.js +36 -0
  94. package/dist/ui/tui/SlashSuggest.js.map +1 -0
  95. package/dist/ui/tui/StatusBar.js +31 -0
  96. package/dist/ui/tui/StatusBar.js.map +1 -0
  97. package/dist/ui/tui/highlight.js +66 -0
  98. package/dist/ui/tui/highlight.js.map +1 -0
  99. package/dist/ui/tui/index.js +12 -0
  100. package/dist/ui/tui/index.js.map +1 -0
  101. package/dist/ui/tui/markdown.js +46 -0
  102. package/dist/ui/tui/markdown.js.map +1 -0
  103. package/dist/ui/tui/types.js +2 -0
  104. package/dist/ui/tui/types.js.map +1 -0
  105. package/dist/web/index.js +195 -0
  106. package/dist/web/index.js.map +1 -0
  107. package/package.json +79 -0
@@ -0,0 +1,624 @@
1
+ import chalk from "chalk";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { saveUserSetting } from "../config.js";
5
+ import { listMemories, writeMemory, deleteMemory } from "../memory/index.js";
6
+ import { listSkills, findSkill } from "../skills/index.js";
7
+ import { setPlanMode, planModeExtra } from "../planmode.js";
8
+ import { loadSession, saveSession, listSessions, forkSession } from "../session.js";
9
+ import { loadMcpServerSpecs, getMcpDirectory } from "../mcp/index.js";
10
+ import { readCostLog, costByDay, costByModel } from "../cost.js";
11
+ import { buildIndex, loadIndex, semanticSearch } from "../rag/index.js";
12
+ import { runSelfReview, applyProposal } from "../self-review/index.js";
13
+ import { installPlugin, removePlugin, listInstalled, searchRegistry } from "../plugins/index.js";
14
+ import { prepareUserMessage } from "../input.js";
15
+ import { HookRunner } from "../hooks/index.js";
16
+ import { spawnSync } from "node:child_process";
17
+ export const builtinCommands = [
18
+ {
19
+ name: "help",
20
+ description: "Show available commands",
21
+ run(_args, ctx) {
22
+ const lines = builtinCommands.map((c) => ` /${c.name.padEnd(12)} ${c.description}`);
23
+ const skills = listSkills(ctx.config);
24
+ const skillLines = skills.length
25
+ ? ["", chalk.bold("Skills:"), ...skills.map((s) => ` /${s.name.padEnd(12)} ${s.description}`)]
26
+ : [];
27
+ console.log([chalk.bold("Slash commands:"), ...lines, ...skillLines, "", chalk.dim("Type a message to talk to openai-claw, or /exit to quit.")].join("\n"));
28
+ },
29
+ },
30
+ {
31
+ name: "clear",
32
+ description: "Clear the conversation (keep system prompt)",
33
+ run(_args, ctx) {
34
+ ctx.agent.clear(true);
35
+ console.log(chalk.dim("conversation cleared"));
36
+ },
37
+ },
38
+ {
39
+ name: "exit",
40
+ description: "Quit openai-claw",
41
+ run(_args, ctx) {
42
+ ctx.exit();
43
+ },
44
+ },
45
+ {
46
+ name: "quit",
47
+ description: "Quit openai-claw",
48
+ run(_args, ctx) {
49
+ ctx.exit();
50
+ },
51
+ },
52
+ {
53
+ name: "model",
54
+ description: "Show or set models. /model — list all. /model <name> — set default. /model <role> <name> — set role (default/cheap/reasoning). /model use <role> — use a role for the next turn.",
55
+ run(args, ctx) {
56
+ const arg = args.trim();
57
+ if (!arg) {
58
+ console.log(`current model: ${ctx.config.model}`);
59
+ for (const [role, name] of Object.entries(ctx.config.models ?? {})) {
60
+ console.log(` ${role.padEnd(10)} → ${name}`);
61
+ }
62
+ console.log(chalk.dim("hint: /model <role> <name> or /model use <role>"));
63
+ return;
64
+ }
65
+ const parts = arg.split(/\s+/);
66
+ if (parts[0] === "use" && parts[1]) {
67
+ const role = parts[1];
68
+ if (!["default", "cheap", "reasoning"].includes(role)) {
69
+ console.log(chalk.red(`unknown role: ${role}`));
70
+ return;
71
+ }
72
+ ctx.agent.setNextRole(role);
73
+ console.log(chalk.dim(`next turn will use role=${role} (${ctx.config.models?.[role] ?? ctx.config.model})`));
74
+ return;
75
+ }
76
+ if (parts.length === 2 && ["default", "cheap", "reasoning"].includes(parts[0])) {
77
+ const role = parts[0];
78
+ const name = parts[1];
79
+ ctx.config.models = { ...(ctx.config.models ?? {}), [role]: name };
80
+ saveUserSetting(ctx.config, "models", ctx.config.models);
81
+ console.log(chalk.dim(`${role} → ${name} (saved)`));
82
+ return;
83
+ }
84
+ ctx.config.model = arg;
85
+ saveUserSetting(ctx.config, "model", arg);
86
+ console.log(chalk.dim(`model set to ${arg} (saved)`));
87
+ },
88
+ },
89
+ {
90
+ name: "mode",
91
+ description: "Show or set permission mode (ask | acceptEdits | bypassPermissions | plan)",
92
+ run(args, ctx) {
93
+ const arg = args.trim();
94
+ if (!arg) {
95
+ console.log(`permission mode: ${ctx.config.permissionMode}`);
96
+ return;
97
+ }
98
+ if (!["ask", "acceptEdits", "bypassPermissions", "plan"].includes(arg)) {
99
+ console.log(chalk.red(`unknown mode: ${arg}`));
100
+ return;
101
+ }
102
+ ctx.permissions.setMode(arg);
103
+ console.log(chalk.dim(`permission mode set to ${arg}`));
104
+ },
105
+ },
106
+ {
107
+ name: "plan",
108
+ description: "Toggle plan mode",
109
+ run(_args, ctx) {
110
+ const enabled = ctx.config.permissionMode !== "plan";
111
+ setPlanMode(ctx.config, enabled);
112
+ if (enabled) {
113
+ ctx.agent.pushUser(`<system>${planModeExtra()}</system>`);
114
+ console.log(chalk.cyan("plan mode ON — read-only investigation, no mutations"));
115
+ }
116
+ else {
117
+ console.log(chalk.cyan("plan mode OFF"));
118
+ }
119
+ },
120
+ },
121
+ {
122
+ name: "cost",
123
+ description: "Show token usage and cost. /cost — this session. /cost daily | models — historical.",
124
+ run(args, ctx) {
125
+ const arg = args.trim();
126
+ if (!arg) {
127
+ const u = ctx.agent.usage;
128
+ const cost = u.totalCostUSD > 0 ? `≈ $${u.totalCostUSD.toFixed(4)}` : "(no price table for this model)";
129
+ const cachePct = u.cacheHitRate > 0 ? ` cache=${(u.cacheHitRate * 100).toFixed(0)}%` : "";
130
+ console.log(`tokens: ${u.totalTokens} (prompt=${u.totalPromptTokens} completion=${u.totalCompletionTokens}${cachePct})\ncost: ${cost}`);
131
+ return;
132
+ }
133
+ const entries = readCostLog(ctx.config);
134
+ if (entries.length === 0) {
135
+ console.log(chalk.dim("(no cost log yet)"));
136
+ return;
137
+ }
138
+ if (arg === "daily") {
139
+ for (const row of costByDay(entries).slice(0, 14)) {
140
+ console.log(` ${row.date} $${row.costUSD.toFixed(4).padStart(8)} ${row.tokens.toString().padStart(8)}t ${row.turns}turns`);
141
+ }
142
+ return;
143
+ }
144
+ if (arg === "models") {
145
+ for (const row of costByModel(entries)) {
146
+ console.log(` ${row.model.padEnd(18)} $${row.costUSD.toFixed(4).padStart(8)} ${row.tokens.toString().padStart(8)}t ${row.turns}turns`);
147
+ }
148
+ return;
149
+ }
150
+ console.log(chalk.red("usage: /cost [daily|models]"));
151
+ },
152
+ },
153
+ {
154
+ name: "memory",
155
+ description: "Manage memory: /memory list | /memory show <name> | /memory rm <name>",
156
+ run(args, ctx) {
157
+ const [sub, ...rest] = args.trim().split(/\s+/);
158
+ if (!sub || sub === "list") {
159
+ const entries = listMemories(ctx.config);
160
+ if (entries.length === 0) {
161
+ console.log(chalk.dim("(no memories)"));
162
+ return;
163
+ }
164
+ for (const e of entries)
165
+ console.log(` ${e.name} [${e.type}] — ${e.description}`);
166
+ return;
167
+ }
168
+ if (sub === "show") {
169
+ const e = listMemories(ctx.config).find((m) => m.name === rest.join(" "));
170
+ if (!e)
171
+ return console.log(chalk.red("not found"));
172
+ console.log(`# ${e.name} [${e.type}]\n${e.description}\n\n${e.body}`);
173
+ return;
174
+ }
175
+ if (sub === "rm") {
176
+ const ok = deleteMemory(ctx.config, rest.join(" "));
177
+ console.log(ok ? chalk.dim("deleted") : chalk.red("not found"));
178
+ return;
179
+ }
180
+ console.log(chalk.red("usage: /memory [list|show <name>|rm <name>]"));
181
+ },
182
+ },
183
+ {
184
+ name: "remember",
185
+ description: "Save a memory: /remember <type> <name> :: <description> :: <body>",
186
+ run(args, ctx) {
187
+ const parts = args.split("::").map((s) => s.trim());
188
+ if (parts.length < 3) {
189
+ console.log(chalk.red("usage: /remember <type> <name> :: <description> :: <body>"));
190
+ return;
191
+ }
192
+ const [head, description, body] = parts;
193
+ const [type, ...nameParts] = head.split(/\s+/);
194
+ if (!["user", "feedback", "project", "reference"].includes(type)) {
195
+ console.log(chalk.red(`unknown type: ${type}`));
196
+ return;
197
+ }
198
+ const name = nameParts.join("-").toLowerCase().replace(/[^a-z0-9-]/g, "-");
199
+ const file = writeMemory(ctx.config, {
200
+ name,
201
+ description,
202
+ type: type,
203
+ body,
204
+ });
205
+ console.log(chalk.dim(`saved ${file}`));
206
+ },
207
+ },
208
+ {
209
+ name: "init",
210
+ description: "Create a CLAUDE.md / CLAW.md project instruction file",
211
+ run(_args, ctx) {
212
+ const file = path.join(ctx.config.workdir, "CLAW.md");
213
+ if (fs.existsSync(file)) {
214
+ console.log(chalk.dim(`${file} already exists`));
215
+ return;
216
+ }
217
+ fs.writeFileSync(file, `# Project instructions for openai-claw\n\n_(Describe the project, conventions, and anything the assistant should know about working here.)_\n`);
218
+ console.log(chalk.dim(`created ${file}`));
219
+ },
220
+ },
221
+ {
222
+ name: "img",
223
+ description: "Attach an image to the next message: /img <path> [prompt]",
224
+ run(args, ctx) {
225
+ const trimmed = args.trim();
226
+ if (!trimmed) {
227
+ console.log(chalk.red("usage: /img <path> [prompt]"));
228
+ return;
229
+ }
230
+ const [imgPath, ...promptParts] = trimmed.split(/\s+/);
231
+ const promptText = promptParts.join(" ") || "(image attached — describe it)";
232
+ const synth = `${promptText} @${imgPath}`;
233
+ const prep = prepareUserMessage(synth, ctx.config);
234
+ if (prep.attachments.length === 0) {
235
+ console.log(chalk.red(`could not attach: ${imgPath}`));
236
+ return;
237
+ }
238
+ ctx.agent.pushUser(prep.content);
239
+ console.log(chalk.dim(`queued: ${prep.attachments.join(", ")} — send any message (or just press Enter) to dispatch`));
240
+ },
241
+ },
242
+ {
243
+ name: "last",
244
+ description: "Print the full content of the most recent tool result",
245
+ run(_args, ctx) {
246
+ const msgs = ctx.agent.conversation;
247
+ for (let i = msgs.length - 1; i >= 0; i--) {
248
+ const m = msgs[i];
249
+ if (m.role === "tool") {
250
+ console.log(`# ${m.name ?? "(tool)"}`);
251
+ console.log(typeof m.content === "string" ? m.content : JSON.stringify(m.content));
252
+ return;
253
+ }
254
+ }
255
+ console.log(chalk.dim("(no tool results yet)"));
256
+ },
257
+ },
258
+ {
259
+ name: "resume",
260
+ description: "Resume a session. /resume — last. /resume list — show all. /resume <id> — by id.",
261
+ run(args, ctx) {
262
+ const arg = args.trim();
263
+ if (arg === "list") {
264
+ const sessions = listSessions(ctx.config);
265
+ if (sessions.length === 0) {
266
+ console.log(chalk.dim("(no sessions)"));
267
+ return;
268
+ }
269
+ for (const s of sessions.slice(0, 30)) {
270
+ console.log(` ${chalk.cyan(s.id.padEnd(40))} ${chalk.dim(s.savedAt)} ${chalk.dim(`(${s.messageCount}m)`)} ${s.preview}`);
271
+ }
272
+ if (sessions.length > 30)
273
+ console.log(chalk.dim(` …and ${sessions.length - 30} more`));
274
+ return;
275
+ }
276
+ const data = loadSession(ctx.config, arg || undefined);
277
+ if (!data) {
278
+ console.log(chalk.dim(arg ? `(no session with id ${arg})` : "(no saved session)"));
279
+ return;
280
+ }
281
+ ctx.agent.replaceConversation(data.messages);
282
+ if (ctx.sessionRef)
283
+ ctx.sessionRef.current = data.id;
284
+ console.log(chalk.dim(`resumed ${data.messages.length} message(s) from session ${data.id} (saved ${data.savedAt})`));
285
+ },
286
+ },
287
+ {
288
+ name: "save",
289
+ description: "Force-save the current session",
290
+ run(_args, ctx) {
291
+ const { id, file } = saveSession(ctx.config, ctx.agent.conversation, ctx.sessionRef?.current);
292
+ if (ctx.sessionRef)
293
+ ctx.sessionRef.current = id;
294
+ console.log(chalk.dim(`saved → ${file}`));
295
+ },
296
+ },
297
+ {
298
+ name: "fork",
299
+ description: "Fork the current conversation as a new session id",
300
+ run(_args, ctx) {
301
+ const { id } = forkSession(ctx.config, ctx.agent.conversation);
302
+ if (ctx.sessionRef)
303
+ ctx.sessionRef.current = id;
304
+ console.log(chalk.dim(`forked to ${id}`));
305
+ },
306
+ },
307
+ {
308
+ name: "mcp",
309
+ description: "List configured MCP servers and their loaded tools",
310
+ run(_args, ctx) {
311
+ const specs = loadMcpServerSpecs(ctx.config);
312
+ if (specs.length === 0) {
313
+ console.log(chalk.dim("(no MCP servers configured — add `mcpServers` to settings.json)"));
314
+ return;
315
+ }
316
+ for (const s of specs) {
317
+ if (s.config.type === "http") {
318
+ console.log(`${s.name}: http ${s.config.url}`);
319
+ }
320
+ else {
321
+ console.log(`${s.name}: ${s.config.command} ${(s.config.args ?? []).join(" ")}`);
322
+ }
323
+ }
324
+ },
325
+ },
326
+ {
327
+ name: "config",
328
+ description: "Print effective configuration",
329
+ run(_args, ctx) {
330
+ const { apiKey: _apiKey, ...rest } = ctx.config;
331
+ console.log(JSON.stringify(rest, null, 2));
332
+ },
333
+ },
334
+ {
335
+ name: "compact",
336
+ description: "Force a compaction pass on the current conversation",
337
+ async run(_args, ctx) {
338
+ const r = await ctx.agent.forceCompact();
339
+ if (!r) {
340
+ console.log(chalk.dim("(nothing to compact)"));
341
+ }
342
+ else {
343
+ console.log(chalk.dim(`compacted ${r.before}→${r.after} tokens`));
344
+ }
345
+ },
346
+ },
347
+ {
348
+ name: "agents",
349
+ description: "List available subagent types (builtins + .claw/agents/*.md)",
350
+ async run(_args, ctx) {
351
+ const { listSubagents } = await import("../subagents/index.js");
352
+ const agents = listSubagents(ctx.config);
353
+ for (const a of agents) {
354
+ const tools = a.tools ? ` [tools=${a.tools.length}]` : "";
355
+ const role = a.modelRole ? ` [${a.modelRole}]` : "";
356
+ console.log(` ${a.name.padEnd(20)}${tools}${role} ${a.description}`);
357
+ }
358
+ },
359
+ },
360
+ {
361
+ name: "hooks",
362
+ description: "List configured hooks",
363
+ run(_args, ctx) {
364
+ const hooks = new HookRunner(ctx.config);
365
+ // HookRunner stores hooks privately; reflect via a probe by reading settings.
366
+ const userSettings = readJsonOr(path.join(ctx.config.homeDir, "settings.json"), {});
367
+ const projSettings = readJsonOr(path.join(ctx.config.workdir, ".claw", "settings.json"), {});
368
+ const all = [...(userSettings.hooks ? entriesOf(userSettings.hooks) : []), ...(projSettings.hooks ? entriesOf(projSettings.hooks) : [])];
369
+ if (all.length === 0) {
370
+ console.log(chalk.dim("(no hooks configured — add `hooks` to settings.json)"));
371
+ return;
372
+ }
373
+ for (const [event, defs] of all) {
374
+ for (const d of defs) {
375
+ console.log(` [${event}] ${d.matcher ? `matcher=${d.matcher} ` : ""}${d.command}`);
376
+ }
377
+ }
378
+ void hooks;
379
+ },
380
+ },
381
+ {
382
+ name: "vim",
383
+ description: "Compose the next prompt in $EDITOR (defaults to vim)",
384
+ run(_args, ctx) {
385
+ const editor = process.env.EDITOR || process.env.VISUAL || "vim";
386
+ const tmp = path.join(ctx.config.projectDir, ".claw-compose.txt");
387
+ try {
388
+ fs.writeFileSync(tmp, "");
389
+ const res = spawnSync(editor, [tmp], { stdio: "inherit" });
390
+ if (res.status !== 0) {
391
+ console.log(chalk.red(`editor exited ${res.status}`));
392
+ return;
393
+ }
394
+ const body = fs.readFileSync(tmp, "utf8").trim();
395
+ fs.unlinkSync(tmp);
396
+ if (!body) {
397
+ console.log(chalk.dim("(empty — discarded)"));
398
+ return;
399
+ }
400
+ ctx.agent.pushUser(body);
401
+ console.log(chalk.dim(`queued ${body.length} chars — press Enter to send`));
402
+ }
403
+ catch (e) {
404
+ console.log(chalk.red(`/vim failed: ${e?.message ?? e}`));
405
+ }
406
+ },
407
+ },
408
+ {
409
+ name: "review",
410
+ description: "Ask the agent to review the current branch's changes",
411
+ run(_args, ctx) {
412
+ ctx.agent.pushUser("Review the pending changes on the current branch. Run `git diff` and `git status`, then evaluate correctness, edge cases, tests, and security. Report a concise punch list.");
413
+ console.log(chalk.dim("(review prompt queued — press Enter to send)"));
414
+ },
415
+ },
416
+ {
417
+ name: "index",
418
+ description: "Build or rebuild the project's semantic index (used by the Semantic tool)",
419
+ async run(_args, ctx) {
420
+ console.log(chalk.dim("indexing… this may take a moment"));
421
+ try {
422
+ const r = await buildIndex(ctx.config, (msg) => console.log(chalk.dim(` ${msg}`)));
423
+ console.log(chalk.dim(`indexed ${r.filesIndexed} file(s) → ${r.chunks} chunk(s)`));
424
+ }
425
+ catch (e) {
426
+ console.log(chalk.red(`index failed: ${e?.message ?? e}`));
427
+ }
428
+ },
429
+ },
430
+ {
431
+ name: "self-review",
432
+ description: "Scan recent sessions for repeated friction and propose new memory entries (asks before saving)",
433
+ async run(args, ctx) {
434
+ const n = parseInt(args.trim(), 10) || 20;
435
+ console.log(chalk.dim(`scanning the last ${n} sessions…`));
436
+ try {
437
+ const report = await runSelfReview(ctx.config, { sessionLimit: n });
438
+ console.log(chalk.dim(`scanned ${report.sessionsScanned} session(s); ${report.signals.correctionPhrases} correction phrase(s), ${report.signals.repeatedToolErrors} repeated tool error(s)`));
439
+ if (report.proposals.length === 0) {
440
+ console.log(chalk.dim("(no new memory proposals)"));
441
+ return;
442
+ }
443
+ for (let i = 0; i < report.proposals.length; i++) {
444
+ const p = report.proposals[i];
445
+ console.log(`\n${chalk.cyan(`#${i + 1}`)} ${chalk.bold(p.name)} [${p.type}] — ${p.description}`);
446
+ console.log(p.body.split("\n").map((l) => ` ${l}`).join("\n"));
447
+ }
448
+ console.log(chalk.dim("\nTo accept all: /self-review-accept all"));
449
+ console.log(chalk.dim("To accept specific: /self-review-accept 1 3"));
450
+ ctx.agent.__pendingReview = report;
451
+ }
452
+ catch (e) {
453
+ console.log(chalk.red(`/self-review failed: ${e?.message ?? e}`));
454
+ }
455
+ },
456
+ },
457
+ {
458
+ name: "self-review-accept",
459
+ description: "Apply proposals from the most recent /self-review. Args: 'all' or numbers like '1 3'",
460
+ run(args, ctx) {
461
+ const report = ctx.agent.__pendingReview;
462
+ if (!report || !Array.isArray(report.proposals) || report.proposals.length === 0) {
463
+ console.log(chalk.red("no pending proposals — run /self-review first"));
464
+ return;
465
+ }
466
+ const want = args.trim();
467
+ let indices;
468
+ if (want === "all") {
469
+ indices = report.proposals.map((_, i) => i);
470
+ }
471
+ else {
472
+ indices = want
473
+ .split(/\s+/)
474
+ .map((s) => parseInt(s, 10) - 1)
475
+ .filter((n) => Number.isFinite(n) && n >= 0 && n < report.proposals.length);
476
+ }
477
+ if (indices.length === 0) {
478
+ console.log(chalk.red("usage: /self-review-accept all | <1-based indices...>"));
479
+ return;
480
+ }
481
+ for (const i of indices) {
482
+ const file = applyProposal(ctx.config, report.proposals[i]);
483
+ console.log(chalk.dim(`saved ${file}`));
484
+ }
485
+ ctx.agent.__pendingReview = null;
486
+ },
487
+ },
488
+ {
489
+ name: "search",
490
+ description: "Semantic search over the project index: /search <natural-language query>",
491
+ async run(args, ctx) {
492
+ const q = args.trim();
493
+ if (!q) {
494
+ console.log(chalk.red("usage: /search <query>"));
495
+ return;
496
+ }
497
+ const idx = loadIndex(ctx.config);
498
+ if (!idx) {
499
+ console.log(chalk.red("no index — run /index first"));
500
+ return;
501
+ }
502
+ try {
503
+ const hits = await semanticSearch(ctx.config, q, 8);
504
+ for (const h of hits) {
505
+ console.log(` ${chalk.cyan(h.score.toFixed(3))} ${h.file}#${h.chunkIndex}`);
506
+ }
507
+ }
508
+ catch (e) {
509
+ console.log(chalk.red(`search failed: ${e?.message ?? e}`));
510
+ }
511
+ },
512
+ },
513
+ {
514
+ name: "plugins",
515
+ description: "Manage plugins. /plugins list — installed. /plugins search <q>. /plugins install <name|url>. /plugins remove <name>.",
516
+ run(args, ctx) {
517
+ const parts = args.trim().split(/\s+/);
518
+ const sub = parts[0] || "list";
519
+ if (sub === "list") {
520
+ const installed = listInstalled(ctx.config);
521
+ if (installed.length === 0) {
522
+ console.log(chalk.dim("(no plugins installed)"));
523
+ return;
524
+ }
525
+ for (const p of installed) {
526
+ const summary = [
527
+ p.provides.skills.length && `skills=${p.provides.skills.length}`,
528
+ p.provides.agents.length && `agents=${p.provides.agents.length}`,
529
+ p.provides.mcp.length && `mcp=${p.provides.mcp.length}`,
530
+ ].filter(Boolean).join(" ");
531
+ console.log(` ${chalk.cyan(p.name)} ${chalk.dim(p.source)} ${summary}`);
532
+ }
533
+ return;
534
+ }
535
+ if (sub === "search") {
536
+ const q = parts.slice(1).join(" ");
537
+ const hits = searchRegistry(q);
538
+ if (hits.length === 0) {
539
+ console.log(chalk.dim("(no registry matches)"));
540
+ return;
541
+ }
542
+ for (const h of hits)
543
+ console.log(` ${chalk.cyan(h.name)} ${chalk.dim(h.url)}\n ${h.description}`);
544
+ return;
545
+ }
546
+ if (sub === "install") {
547
+ const source = parts.slice(1).join(" ").trim();
548
+ if (!source) {
549
+ console.log(chalk.red("usage: /plugins install <name-or-git-url>"));
550
+ return;
551
+ }
552
+ console.log(chalk.dim(`installing ${source}…`));
553
+ const r = installPlugin(ctx.config, source);
554
+ if (!r.ok) {
555
+ console.log(chalk.red(`install failed: ${r.error}`));
556
+ return;
557
+ }
558
+ const p = r.entry;
559
+ console.log(chalk.dim(`installed ${p.name} (${p.ref?.slice(0, 7) ?? "?"}) skills=${p.provides.skills.length} agents=${p.provides.agents.length} mcp=${p.provides.mcp.length}`));
560
+ return;
561
+ }
562
+ if (sub === "remove") {
563
+ const name = parts.slice(1).join(" ").trim();
564
+ if (!name) {
565
+ console.log(chalk.red("usage: /plugins remove <name>"));
566
+ return;
567
+ }
568
+ const r = removePlugin(ctx.config, name);
569
+ if (!r.ok) {
570
+ console.log(chalk.red(r.error));
571
+ return;
572
+ }
573
+ console.log(chalk.dim(`removed ${name}`));
574
+ return;
575
+ }
576
+ console.log(chalk.red("usage: /plugins [list|search <q>|install <src>|remove <name>]"));
577
+ },
578
+ },
579
+ {
580
+ name: "mcp-resources",
581
+ description: "List resources exposed by connected MCP servers",
582
+ run() {
583
+ const dir = getMcpDirectory();
584
+ if (dir.resources.length === 0) {
585
+ console.log(chalk.dim("(no MCP resources)"));
586
+ return;
587
+ }
588
+ for (const r of dir.resources) {
589
+ console.log(` [${r.server}] ${r.uri}${r.description ? ` — ${r.description}` : ""}`);
590
+ }
591
+ },
592
+ },
593
+ ];
594
+ function readJsonOr(p, fallback) {
595
+ try {
596
+ if (!fs.existsSync(p))
597
+ return fallback;
598
+ return JSON.parse(fs.readFileSync(p, "utf8"));
599
+ }
600
+ catch {
601
+ return fallback;
602
+ }
603
+ }
604
+ function entriesOf(hooks) {
605
+ return Object.entries(hooks).map(([event, defs]) => [event, Array.isArray(defs) ? defs : []]);
606
+ }
607
+ export function findCommand(name, config) {
608
+ const builtin = builtinCommands.find((c) => c.name === name);
609
+ if (builtin)
610
+ return builtin;
611
+ const skill = findSkill(config, name);
612
+ if (skill) {
613
+ return {
614
+ name: skill.name,
615
+ description: skill.description,
616
+ run(args, ctx) {
617
+ ctx.agent.pushUser(`<skill name="${skill.name}">\n${skill.body}\n${args ? `\nArguments: ${args}` : ""}\n</skill>`);
618
+ console.log(chalk.dim(`[skill loaded: ${skill.name}]`));
619
+ },
620
+ };
621
+ }
622
+ return undefined;
623
+ }
624
+ //# sourceMappingURL=index.js.map