@pencil-agent/nano-pencil 1.14.2 → 1.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-meta.json +3 -3
- package/dist/builtin-extensions.d.ts +4 -0
- package/dist/builtin-extensions.js +16 -16
- package/dist/core/config/settings-manager.d.ts +8 -1
- package/dist/core/config/settings-manager.js +9 -0
- package/dist/core/extensions/runner.d.ts +70 -4
- package/dist/core/extensions/runner.js +188 -8
- package/dist/core/extensions/types.d.ts +8 -1
- package/dist/core/i18n/slash-commands.d.ts +12 -0
- package/dist/core/i18n/slash-commands.js +16 -4
- package/dist/core/i18n/slash-commands.zh.d.ts +12 -0
- package/dist/core/i18n/slash-commands.zh.js +16 -4
- package/dist/core/runtime/agent-session.d.ts +5 -0
- package/dist/core/runtime/agent-session.js +85 -27
- package/dist/core/runtime/extension-core-bindings.d.ts +3 -3
- package/dist/core/runtime/extension-core-bindings.js +73 -20
- package/dist/core/runtime/retry-coordinator.d.ts +10 -1
- package/dist/core/runtime/retry-coordinator.js +20 -4
- package/dist/core/runtime/sdk.js +2 -1
- package/dist/core/runtime/slash-command-catalog.d.ts +2 -1
- package/dist/core/runtime/slash-command-catalog.js +9 -1
- package/dist/core/slash-commands.d.ts +12 -1
- package/dist/core/slash-commands.js +81 -32
- package/dist/core/telemetry/batching-dispatcher.d.ts +41 -0
- package/dist/core/telemetry/batching-dispatcher.js +89 -0
- package/dist/core/telemetry/build-meta.d.ts +12 -0
- package/dist/core/telemetry/build-meta.js +57 -0
- package/dist/core/telemetry/caller-context.d.ts +32 -0
- package/dist/core/telemetry/caller-context.js +19 -0
- package/dist/core/telemetry/credentials.d.ts +27 -0
- package/dist/core/telemetry/credentials.js +87 -0
- package/dist/core/telemetry/ext-events.d.ts +89 -0
- package/dist/core/telemetry/ext-events.js +189 -0
- package/dist/core/telemetry/index.d.ts +13 -0
- package/dist/core/telemetry/index.js +6 -0
- package/dist/core/telemetry/insforge-base.d.ts +37 -0
- package/dist/core/telemetry/insforge-base.js +160 -0
- package/dist/core/telemetry/types.d.ts +33 -0
- package/dist/core/telemetry/types.js +7 -0
- package/dist/extensions/defaults/browser/index.js +14 -6
- package/dist/extensions/defaults/btw/index.js +2 -2
- package/dist/extensions/defaults/debug/index.js +38 -3
- package/dist/extensions/defaults/diagnostics/index.js +12 -0
- package/dist/extensions/defaults/grub/grub-parser.d.ts +15 -1
- package/dist/extensions/defaults/grub/grub-parser.js +31 -1
- package/dist/extensions/defaults/grub/index.d.ts +1 -1
- package/dist/extensions/defaults/grub/index.js +4 -3
- package/dist/extensions/defaults/interview/index.js +2 -2
- package/dist/extensions/defaults/link-world/index.js +14 -6
- package/dist/extensions/defaults/loop/cron/cron-scheduler.js +19 -0
- package/dist/extensions/defaults/loop/index.js +35 -0
- package/dist/extensions/defaults/mcp/index.js +18 -0
- package/dist/extensions/defaults/plan/index.js +29 -11
- package/dist/extensions/defaults/presence/index.d.ts +12 -2
- package/dist/extensions/defaults/presence/index.js +77 -23
- package/dist/extensions/defaults/presence/presence-memory.d.ts +2 -1
- package/dist/extensions/defaults/presence/presence-memory.js +37 -1
- package/dist/extensions/defaults/recap/index.js +12 -0
- package/dist/extensions/defaults/sal/eval/insforge-sink.d.ts +6 -17
- package/dist/extensions/defaults/sal/eval/insforge-sink.js +40 -183
- package/dist/extensions/defaults/sal/index.js +31 -8
- package/dist/extensions/defaults/sal/sal-config.d.ts +5 -0
- package/dist/extensions/defaults/sal/sal-config.js +15 -82
- package/dist/extensions/defaults/security-audit/index.js +141 -83
- package/dist/extensions/defaults/subagent/index.js +29 -5
- package/dist/extensions/defaults/team/index.js +10 -9
- package/dist/extensions/defaults/team/team-parser.d.ts +18 -0
- package/dist/extensions/defaults/team/team-parser.js +91 -3
- package/dist/extensions/defaults/team/team-ui.js +4 -14
- package/dist/extensions/defaults/token-save/index.js +14 -1
- package/dist/extensions/optional/export-html/index.js +19 -5
- package/dist/extensions/optional/simplify/index.js +11 -5
- package/dist/modes/interactive/interactive-mode.d.ts +2 -1
- package/dist/modes/interactive/interactive-mode.js +68 -19
- package/dist/modes/interactive/slash-command-arguments.d.ts +16 -0
- package/dist/modes/interactive/slash-command-arguments.js +97 -0
- package/dist/modes/rpc/rpc-mode.d.ts +3 -0
- package/dist/modes/rpc/rpc-mode.js +40 -31
- package/dist/modes/rpc/rpc-types.d.ts +3 -0
- package/dist/node_modules/@pencil-agent/agent-core/agent.d.ts +15 -1
- package/dist/node_modules/@pencil-agent/agent-core/agent.js +13 -1
- package/dist/node_modules/@pencil-agent/agent-core/index.d.ts +2 -1
- package/dist/node_modules/@pencil-agent/agent-core/index.js +2 -1
- package/dist/node_modules/@pencil-agent/agent-core/structured-adaptive-agent-loop.d.ts +1 -1
- package/dist/node_modules/@pencil-agent/agent-core/structured-adaptive-agent-loop.js +293 -20
- package/dist/node_modules/@pencil-agent/agent-core/structured-adaptive-streaming-tool-executor.d.ts +33 -0
- package/dist/node_modules/@pencil-agent/agent-core/structured-adaptive-streaming-tool-executor.js +189 -0
- package/dist/node_modules/@pencil-agent/agent-core/structured-adaptive-tool-orchestration.d.ts +9 -0
- package/dist/node_modules/@pencil-agent/agent-core/structured-adaptive-tool-orchestration.js +30 -5
- package/dist/node_modules/@pencil-agent/agent-core/types.d.ts +90 -3
- package/dist/node_modules/@pencil-agent/agent-core/types.js +1 -1
- package/dist/node_modules/@pencil-agent/ai/models.generated.d.ts +0 -17
- package/dist/node_modules/@pencil-agent/ai/models.generated.js +29 -46
- package/dist/node_modules/@pencil-agent/tui/autocomplete.d.ts +8 -1
- package/dist/node_modules/@pencil-agent/tui/autocomplete.js +18 -2
- package/dist/node_modules/@pencil-agent/tui/index.d.ts +1 -1
- package/dist/node_modules/@pencil-agent/tui/tui.d.ts +8 -3
- package/dist/node_modules/@pencil-agent/tui/tui.js +68 -33
- package/dist/packages/mem-core/extension.js +154 -83
- package/dist/packages/mem-core/full-insights-sections.d.ts +48 -0
- package/dist/packages/mem-core/full-insights-sections.js +231 -0
- package/dist/packages/mem-core/full-insights.d.ts +1 -1
- package/dist/packages/mem-core/full-insights.js +102 -42
- package/package.json +2 -2
|
@@ -16,6 +16,13 @@ import { DEFAULT_SECURITY_CONFIG } from "./interface.js";
|
|
|
16
16
|
// Security Audit Extension
|
|
17
17
|
// ============================================================
|
|
18
18
|
const SECURITY_MESSAGE_TYPE = "security-audit";
|
|
19
|
+
const SECURITY_ROOT_COMPLETIONS = [
|
|
20
|
+
{ value: "dashboard", label: "dashboard", description: "Show recent warnings and blocked actions" },
|
|
21
|
+
{ value: "logs", label: "logs", description: "Show detailed event history" },
|
|
22
|
+
{ value: "stats", label: "stats", description: "Show event counts" },
|
|
23
|
+
{ value: "clear", label: "clear", description: "Clear saved events" },
|
|
24
|
+
];
|
|
25
|
+
const SECURITY_LOG_LIMIT_COMPLETIONS = ["10", "20", "50", "100"];
|
|
19
26
|
// Security mode: "audit" (warn only), "strict" (block dangerous)
|
|
20
27
|
const SECURITY_MODE = process.env.SECURITY_MODE || "strict";
|
|
21
28
|
const detector = new DangerDetector({
|
|
@@ -42,6 +49,26 @@ function sendSecurityNotice(api, content) {
|
|
|
42
49
|
display: true,
|
|
43
50
|
}, { triggerTurn: true });
|
|
44
51
|
}
|
|
52
|
+
function getSecurityArgumentCompletions(argumentPrefix, context) {
|
|
53
|
+
const prefix = argumentPrefix.trim().toLowerCase();
|
|
54
|
+
if (context?.previousTokens[0] === "logs" && context.tokenIndex === 1) {
|
|
55
|
+
const values = SECURITY_LOG_LIMIT_COMPLETIONS
|
|
56
|
+
.filter((value) => value.startsWith(prefix))
|
|
57
|
+
.map((value) => ({ value, label: value, description: `Show ${value} events` }));
|
|
58
|
+
return values.length > 0 ? values : null;
|
|
59
|
+
}
|
|
60
|
+
if (context && context.tokenIndex > 0)
|
|
61
|
+
return null;
|
|
62
|
+
const values = SECURITY_ROOT_COMPLETIONS.filter((item) => item.value.startsWith(prefix));
|
|
63
|
+
return values.length > 0 ? values.map((item) => ({ ...item })) : null;
|
|
64
|
+
}
|
|
65
|
+
function getSecurityLogLimitCompletions(argumentPrefix) {
|
|
66
|
+
const prefix = argumentPrefix.trim();
|
|
67
|
+
const values = SECURITY_LOG_LIMIT_COMPLETIONS
|
|
68
|
+
.filter((value) => value.startsWith(prefix))
|
|
69
|
+
.map((value) => ({ value, label: value, description: `Show ${value} events` }));
|
|
70
|
+
return values.length > 0 ? values : null;
|
|
71
|
+
}
|
|
45
72
|
function auditAndGateToolCall(api, event, ctx) {
|
|
46
73
|
const logger = getLogger(ctx);
|
|
47
74
|
const toolName = event.toolName;
|
|
@@ -97,107 +124,138 @@ function auditAndGateToolCall(api, event, ctx) {
|
|
|
97
124
|
}
|
|
98
125
|
}
|
|
99
126
|
export default function securityAuditExtension(api) {
|
|
127
|
+
const showDashboard = (ctx) => {
|
|
128
|
+
const logger = getLogger(ctx);
|
|
129
|
+
const stats = logger.getStats();
|
|
130
|
+
const logs = logger.query({ limit: 20 });
|
|
131
|
+
let content = `# 🔒 Security Audit\n\n`;
|
|
132
|
+
content += `## Statistics\n`;
|
|
133
|
+
content += `- Total Events: ${stats.totalEvents}\n`;
|
|
134
|
+
content += `- Dangerous: ${stats.byLevel.dangerous}\n`;
|
|
135
|
+
content += `- Warnings: ${stats.byLevel.warning}\n`;
|
|
136
|
+
content += `- Blocked: ${stats.byStatus.blocked}\n\n`;
|
|
137
|
+
if (stats.dangerousPatterns.length > 0) {
|
|
138
|
+
content += `## Dangerous Patterns\n`;
|
|
139
|
+
for (const p of stats.dangerousPatterns.slice(0, 5)) {
|
|
140
|
+
content += `- \`${p.pattern}\`: ${p.count}\n`;
|
|
141
|
+
}
|
|
142
|
+
content += `\n`;
|
|
143
|
+
}
|
|
144
|
+
content += `## Recent Events\n`;
|
|
145
|
+
content += `| Time | Type | Level | Target |\n`;
|
|
146
|
+
content += `|------|------|-------|--------|\n`;
|
|
147
|
+
for (const log of logs) {
|
|
148
|
+
const time = log.timestamp.split("T")[1].split(".")[0];
|
|
149
|
+
content += `| ${time} | ${log.type} | ${log.level} | \`${log.target.slice(0, 30)}...\` |\n`;
|
|
150
|
+
}
|
|
151
|
+
api.sendMessage({
|
|
152
|
+
customType: SECURITY_MESSAGE_TYPE,
|
|
153
|
+
content,
|
|
154
|
+
display: true,
|
|
155
|
+
}, { triggerTurn: false });
|
|
156
|
+
};
|
|
157
|
+
const showLogs = (args, ctx) => {
|
|
158
|
+
const logger = getLogger(ctx);
|
|
159
|
+
const limit = parseInt(args) || 50;
|
|
160
|
+
const logs = logger.query({ limit });
|
|
161
|
+
let content = `# Security Audit Logs (${logs.length} entries)\n\n`;
|
|
162
|
+
for (const log of logs) {
|
|
163
|
+
const icon = log.status === "blocked" ? "🔴" : log.level === "dangerous" ? "⚠️" : "✅";
|
|
164
|
+
content += `${icon} [${log.timestamp}] ${log.type}: ${log.operation}\n`;
|
|
165
|
+
content += ` Target: ${log.target}\n`;
|
|
166
|
+
content += ` Level: ${log.level}, Status: ${log.status}\n`;
|
|
167
|
+
if (log.reason) {
|
|
168
|
+
content += ` Reason: ${log.reason}\n`;
|
|
169
|
+
}
|
|
170
|
+
content += "\n";
|
|
171
|
+
}
|
|
172
|
+
api.sendMessage({
|
|
173
|
+
customType: SECURITY_MESSAGE_TYPE,
|
|
174
|
+
content,
|
|
175
|
+
display: true,
|
|
176
|
+
}, { triggerTurn: false });
|
|
177
|
+
};
|
|
178
|
+
const showStats = (ctx) => {
|
|
179
|
+
const logger = getLogger(ctx);
|
|
180
|
+
const stats = logger.getStats();
|
|
181
|
+
let content = `# Security Statistics\n\n`;
|
|
182
|
+
content += `## Overview\n`;
|
|
183
|
+
content += `- Period: ${stats.periodStart.split("T")[0]} ~ ${stats.periodEnd.split("T")[0]}\n`;
|
|
184
|
+
content += `- Total Events: ${stats.totalEvents}\n\n`;
|
|
185
|
+
content += `## By Level\n`;
|
|
186
|
+
content += `- Safe: ${stats.byLevel.safe}\n`;
|
|
187
|
+
content += `- Warning: ${stats.byLevel.warning}\n`;
|
|
188
|
+
content += `- Dangerous: ${stats.byLevel.dangerous}\n\n`;
|
|
189
|
+
content += `## By Status\n`;
|
|
190
|
+
content += `- Allowed: ${stats.byStatus.allowed}\n`;
|
|
191
|
+
content += `- Blocked: ${stats.byStatus.blocked}\n`;
|
|
192
|
+
content += `- Confirmed: ${stats.byStatus.confirmed}\n\n`;
|
|
193
|
+
if (stats.dangerousPatterns.length > 0) {
|
|
194
|
+
content += `## Dangerous Patterns\n`;
|
|
195
|
+
for (const p of stats.dangerousPatterns) {
|
|
196
|
+
content += `- \`${p.pattern}\`: ${p.count}\n`;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
api.sendMessage({
|
|
200
|
+
customType: SECURITY_MESSAGE_TYPE,
|
|
201
|
+
content,
|
|
202
|
+
display: true,
|
|
203
|
+
}, { triggerTurn: false });
|
|
204
|
+
};
|
|
205
|
+
const clearLogs = (ctx) => {
|
|
206
|
+
const logger = getLogger(ctx);
|
|
207
|
+
logger.clear();
|
|
208
|
+
api.sendMessage({
|
|
209
|
+
customType: SECURITY_MESSAGE_TYPE,
|
|
210
|
+
content: "✅ Security audit logs cleared.",
|
|
211
|
+
display: true,
|
|
212
|
+
}, { triggerTurn: false });
|
|
213
|
+
};
|
|
100
214
|
// /security - Show security dashboard
|
|
101
215
|
api.registerCommand("security", {
|
|
102
|
-
description: "
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
content += `|------|------|-------|--------|\n`;
|
|
123
|
-
for (const log of logs) {
|
|
124
|
-
const time = log.timestamp.split("T")[1].split(".")[0];
|
|
125
|
-
content += `| ${time} | ${log.type} | ${log.level} | \`${log.target.slice(0, 30)}...\` |\n`;
|
|
216
|
+
description: "Review security activity, logs, stats, or clear saved events",
|
|
217
|
+
getArgumentCompletions: getSecurityArgumentCompletions,
|
|
218
|
+
handler: async (args, ctx) => {
|
|
219
|
+
const [action, ...rest] = args.trim().split(/\s+/);
|
|
220
|
+
switch (action) {
|
|
221
|
+
case "":
|
|
222
|
+
case "dashboard":
|
|
223
|
+
showDashboard(ctx);
|
|
224
|
+
return;
|
|
225
|
+
case "logs":
|
|
226
|
+
showLogs(rest.join(" "), ctx);
|
|
227
|
+
return;
|
|
228
|
+
case "stats":
|
|
229
|
+
showStats(ctx);
|
|
230
|
+
return;
|
|
231
|
+
case "clear":
|
|
232
|
+
clearLogs(ctx);
|
|
233
|
+
return;
|
|
234
|
+
default:
|
|
235
|
+
ctx.ui.notify("Usage: /security [dashboard|logs|stats|clear]", "info");
|
|
126
236
|
}
|
|
127
|
-
api.sendMessage({
|
|
128
|
-
customType: SECURITY_MESSAGE_TYPE,
|
|
129
|
-
content,
|
|
130
|
-
display: true,
|
|
131
|
-
}, { triggerTurn: false });
|
|
132
237
|
},
|
|
133
238
|
});
|
|
134
239
|
// /security-logs - Show detailed logs
|
|
135
240
|
api.registerCommand("security-logs", {
|
|
136
|
-
description: "Show detailed security
|
|
241
|
+
description: "Show detailed security event history",
|
|
242
|
+
getArgumentCompletions: getSecurityLogLimitCompletions,
|
|
137
243
|
handler: async (args, ctx) => {
|
|
138
|
-
|
|
139
|
-
const limit = parseInt(args) || 50;
|
|
140
|
-
const logs = logger.query({ limit });
|
|
141
|
-
let content = `# Security Audit Logs (${logs.length} entries)\n\n`;
|
|
142
|
-
for (const log of logs) {
|
|
143
|
-
const icon = log.status === "blocked" ? "🔴" : log.level === "dangerous" ? "⚠️" : "✅";
|
|
144
|
-
content += `${icon} [${log.timestamp}] ${log.type}: ${log.operation}\n`;
|
|
145
|
-
content += ` Target: ${log.target}\n`;
|
|
146
|
-
content += ` Level: ${log.level}, Status: ${log.status}\n`;
|
|
147
|
-
if (log.reason) {
|
|
148
|
-
content += ` Reason: ${log.reason}\n`;
|
|
149
|
-
}
|
|
150
|
-
content += "\n";
|
|
151
|
-
}
|
|
152
|
-
api.sendMessage({
|
|
153
|
-
customType: SECURITY_MESSAGE_TYPE,
|
|
154
|
-
content,
|
|
155
|
-
display: true,
|
|
156
|
-
}, { triggerTurn: false });
|
|
244
|
+
showLogs(args, ctx);
|
|
157
245
|
},
|
|
158
246
|
});
|
|
159
247
|
// /security-stats - Show statistics
|
|
160
248
|
api.registerCommand("security-stats", {
|
|
161
|
-
description: "Show security
|
|
249
|
+
description: "Show security event counts",
|
|
162
250
|
handler: async (_args, ctx) => {
|
|
163
|
-
|
|
164
|
-
const stats = logger.getStats();
|
|
165
|
-
let content = `# Security Statistics\n\n`;
|
|
166
|
-
content += `## Overview\n`;
|
|
167
|
-
content += `- Period: ${stats.periodStart.split("T")[0]} ~ ${stats.periodEnd.split("T")[0]}\n`;
|
|
168
|
-
content += `- Total Events: ${stats.totalEvents}\n\n`;
|
|
169
|
-
content += `## By Level\n`;
|
|
170
|
-
content += `- Safe: ${stats.byLevel.safe}\n`;
|
|
171
|
-
content += `- Warning: ${stats.byLevel.warning}\n`;
|
|
172
|
-
content += `- Dangerous: ${stats.byLevel.dangerous}\n\n`;
|
|
173
|
-
content += `## By Status\n`;
|
|
174
|
-
content += `- Allowed: ${stats.byStatus.allowed}\n`;
|
|
175
|
-
content += `- Blocked: ${stats.byStatus.blocked}\n`;
|
|
176
|
-
content += `- Confirmed: ${stats.byStatus.confirmed}\n\n`;
|
|
177
|
-
if (stats.dangerousPatterns.length > 0) {
|
|
178
|
-
content += `## Dangerous Patterns\n`;
|
|
179
|
-
for (const p of stats.dangerousPatterns) {
|
|
180
|
-
content += `- \`${p.pattern}\`: ${p.count}\n`;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
api.sendMessage({
|
|
184
|
-
customType: SECURITY_MESSAGE_TYPE,
|
|
185
|
-
content,
|
|
186
|
-
display: true,
|
|
187
|
-
}, { triggerTurn: false });
|
|
251
|
+
showStats(ctx);
|
|
188
252
|
},
|
|
189
253
|
});
|
|
190
254
|
// /security-clear - Clear logs
|
|
191
255
|
api.registerCommand("security-clear", {
|
|
192
|
-
description: "Clear security
|
|
256
|
+
description: "Clear saved security events",
|
|
193
257
|
handler: async (_args, ctx) => {
|
|
194
|
-
|
|
195
|
-
logger.clear();
|
|
196
|
-
api.sendMessage({
|
|
197
|
-
customType: SECURITY_MESSAGE_TYPE,
|
|
198
|
-
content: "✅ Security audit logs cleared.",
|
|
199
|
-
display: true,
|
|
200
|
-
}, { triggerTurn: false });
|
|
258
|
+
clearLogs(ctx);
|
|
201
259
|
},
|
|
202
260
|
});
|
|
203
261
|
// Tool call is the authoritative pre-execution boundary. Returning
|
|
@@ -8,6 +8,17 @@ import { Box, Container, Spacer, Text } from "@pencil-agent/tui";
|
|
|
8
8
|
import { SubAgentRunner } from "./subagent-runner.js";
|
|
9
9
|
import { buildSubAgentHelp, parseSubAgentCommand } from "./subagent-parser.js";
|
|
10
10
|
const SUBAGENT_MESSAGE_TYPE = "subagent";
|
|
11
|
+
const SUBAGENT_ROOT_COMPLETIONS = [
|
|
12
|
+
{ value: "run", label: "run", description: "Start a background helper task" },
|
|
13
|
+
{ value: "stop", label: "stop", description: "Stop the current helper task" },
|
|
14
|
+
{ value: "status", label: "status", description: "Show current helper task status" },
|
|
15
|
+
{ value: "report", label: "report", description: "Show the latest helper report" },
|
|
16
|
+
{ value: "apply", label: "apply", description: "Copy isolated changes into this workspace" },
|
|
17
|
+
{ value: "help", label: "help", description: "Show helper commands" },
|
|
18
|
+
];
|
|
19
|
+
const SUBAGENT_RUN_COMPLETIONS = [
|
|
20
|
+
{ value: "--write", label: "--write", description: "Allow changes in an isolated workspace" },
|
|
21
|
+
];
|
|
11
22
|
// Global runner instance
|
|
12
23
|
let runner = null;
|
|
13
24
|
let lastReport = null;
|
|
@@ -36,6 +47,7 @@ export default async function subagentExtension(api) {
|
|
|
36
47
|
for (const commandName of commandNames) {
|
|
37
48
|
api.registerCommand(commandName, {
|
|
38
49
|
description: getCommandDescription(commandName),
|
|
50
|
+
getArgumentCompletions: (argumentPrefix, context) => getArgumentCompletions(commandName, argumentPrefix, context),
|
|
39
51
|
handler: async (args, ctx) => {
|
|
40
52
|
const parsed = parseSubAgentCommand(commandName, args);
|
|
41
53
|
if (!parsed) {
|
|
@@ -172,19 +184,31 @@ export default async function subagentExtension(api) {
|
|
|
172
184
|
});
|
|
173
185
|
}
|
|
174
186
|
}
|
|
187
|
+
function getArgumentCompletions(commandName, argumentPrefix, context) {
|
|
188
|
+
if (commandName === "subagent:run" && context && context.tokenIndex > 0)
|
|
189
|
+
return null;
|
|
190
|
+
const prefix = argumentPrefix.trim().toLowerCase();
|
|
191
|
+
const values = commandName === "subagent"
|
|
192
|
+
? SUBAGENT_ROOT_COMPLETIONS
|
|
193
|
+
: commandName === "subagent:run"
|
|
194
|
+
? SUBAGENT_RUN_COMPLETIONS
|
|
195
|
+
: [];
|
|
196
|
+
const matches = values.filter((value) => value.value.startsWith(prefix));
|
|
197
|
+
return matches.length > 0 ? matches.map((value) => ({ ...value })) : null;
|
|
198
|
+
}
|
|
175
199
|
function getCommandDescription(commandName) {
|
|
176
200
|
switch (commandName) {
|
|
177
201
|
case "subagent:run":
|
|
178
|
-
return "
|
|
202
|
+
return "Start a background helper task (/subagent:run <task> [--write])";
|
|
179
203
|
case "subagent:stop":
|
|
180
|
-
return "Stop the active
|
|
204
|
+
return "Stop the active background helper task";
|
|
181
205
|
case "subagent:status":
|
|
182
|
-
return "Show current
|
|
206
|
+
return "Show current background helper status";
|
|
183
207
|
case "subagent:report":
|
|
184
|
-
return "Show the latest
|
|
208
|
+
return "Show the latest background helper report";
|
|
185
209
|
case "subagent:apply":
|
|
186
210
|
return "Apply the latest isolated write run to the main workspace";
|
|
187
211
|
default:
|
|
188
|
-
return "
|
|
212
|
+
return "Run or inspect a background helper task";
|
|
189
213
|
}
|
|
190
214
|
}
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* /team:mode <name> <plan|execute|review> - Switch mode
|
|
24
24
|
*/
|
|
25
25
|
import { TeamRuntime } from "./team-runtime.js";
|
|
26
|
-
import { buildTeamHelp, parseTeamCommand } from "./team-parser.js";
|
|
26
|
+
import { buildTeamHelp, getTeamArgumentCompletions, parseTeamCommand } from "./team-parser.js";
|
|
27
27
|
import { executePreset, formatPresetResult } from "./team-presets.js";
|
|
28
28
|
import { formatHarnessProgress } from "./team-harness.js";
|
|
29
29
|
import { formatPsycheWeights } from "./team-psyche.js";
|
|
@@ -77,6 +77,7 @@ export default async function teamExtension(api) {
|
|
|
77
77
|
for (const commandName of commandNames) {
|
|
78
78
|
api.registerCommand(commandName, {
|
|
79
79
|
description: getCommandDescription(commandName),
|
|
80
|
+
getArgumentCompletions: (argumentPrefix, context) => getTeamArgumentCompletions(commandName, argumentPrefix, context),
|
|
80
81
|
handler: async (args, ctx) => {
|
|
81
82
|
const parsed = parseTeamCommand(commandName, args);
|
|
82
83
|
if (!parsed) {
|
|
@@ -594,32 +595,32 @@ function getCommandDescription(commandName) {
|
|
|
594
595
|
case "team:spawn":
|
|
595
596
|
return "Create a persistent teammate (/team:spawn <role> [--name <name>])";
|
|
596
597
|
case "team:send":
|
|
597
|
-
return "Send
|
|
598
|
+
return "Send work or a note to a teammate (/team:send <name> <message>)";
|
|
598
599
|
case "team:status":
|
|
599
600
|
return "Show team or teammate status";
|
|
600
601
|
case "team:stop":
|
|
601
602
|
return "Stop teammate's current turn";
|
|
602
603
|
case "team:terminate":
|
|
603
|
-
return "
|
|
604
|
+
return "Remove a teammate";
|
|
604
605
|
case "team:approve":
|
|
605
|
-
return "Approve a
|
|
606
|
+
return "Approve a teammate request";
|
|
606
607
|
case "team:mode":
|
|
607
|
-
return "
|
|
608
|
+
return "Change how a teammate works (/team:mode <name> <plan|execute|review>)";
|
|
608
609
|
case "team:preset":
|
|
609
610
|
return "Create teammates from a preset";
|
|
610
611
|
case "team:dashboard":
|
|
611
612
|
return "Toggle the team dashboard";
|
|
612
613
|
case "team:progress":
|
|
613
|
-
return "Show
|
|
614
|
+
return "Show teammate progress";
|
|
614
615
|
case "team:psyche":
|
|
615
|
-
return "Show
|
|
616
|
+
return "Show teammate decision settings";
|
|
616
617
|
case "team:task":
|
|
617
618
|
return "Manage shared team tasks";
|
|
618
619
|
case "team:mail":
|
|
619
|
-
return "
|
|
620
|
+
return "Send a note from one teammate to another";
|
|
620
621
|
case "team:allow-path":
|
|
621
622
|
return "Grant a teammate write access to a path prefix";
|
|
622
623
|
default:
|
|
623
|
-
return "
|
|
624
|
+
return "Create or manage teammates";
|
|
624
625
|
}
|
|
625
626
|
}
|
|
@@ -56,6 +56,23 @@ export interface ParsedTeamCommand {
|
|
|
56
56
|
/** For allow-path: path prefix */
|
|
57
57
|
path?: string;
|
|
58
58
|
}
|
|
59
|
+
export declare const TEAM_ROOT_COMPLETIONS: readonly ["help", "spawn", "send", "status", "stop", "terminate", "approve", "mode", "preset", "dashboard", "progress", "psyche", "task", "mail", "allow-path"];
|
|
60
|
+
export declare const VALID_ROLES: TeammateRole[];
|
|
61
|
+
export declare const VALID_MODES: TeammateMode[];
|
|
62
|
+
export declare const VALID_PRESETS: PresetName[];
|
|
63
|
+
export declare const VALID_TASK_ACTIONS: readonly ["add", "claim", "done", "block", "cancel", "list"];
|
|
64
|
+
type TeamArgumentCompletionContext = {
|
|
65
|
+
commandName: string;
|
|
66
|
+
argumentText: string;
|
|
67
|
+
argumentPrefix: string;
|
|
68
|
+
tokenIndex: number;
|
|
69
|
+
previousTokens: string[];
|
|
70
|
+
};
|
|
71
|
+
export declare function getTeamArgumentCompletions(commandName: string, argumentPrefix: string, context?: TeamArgumentCompletionContext): Array<{
|
|
72
|
+
value: string;
|
|
73
|
+
label: string;
|
|
74
|
+
description?: string;
|
|
75
|
+
}> | null;
|
|
59
76
|
/**
|
|
60
77
|
* Parse a /team command invocation.
|
|
61
78
|
*/
|
|
@@ -64,3 +81,4 @@ export declare function parseTeamCommand(commandName: string, args?: string): Pa
|
|
|
64
81
|
* Build help text for /team commands.
|
|
65
82
|
*/
|
|
66
83
|
export declare function buildTeamHelp(): string;
|
|
84
|
+
export {};
|
|
@@ -21,7 +21,24 @@
|
|
|
21
21
|
* /team:approve <request-id> - Approve permission request
|
|
22
22
|
* /team:mode <name> <plan|execute|review> - Switch mode
|
|
23
23
|
*/
|
|
24
|
-
const
|
|
24
|
+
export const TEAM_ROOT_COMPLETIONS = [
|
|
25
|
+
"help",
|
|
26
|
+
"spawn",
|
|
27
|
+
"send",
|
|
28
|
+
"status",
|
|
29
|
+
"stop",
|
|
30
|
+
"terminate",
|
|
31
|
+
"approve",
|
|
32
|
+
"mode",
|
|
33
|
+
"preset",
|
|
34
|
+
"dashboard",
|
|
35
|
+
"progress",
|
|
36
|
+
"psyche",
|
|
37
|
+
"task",
|
|
38
|
+
"mail",
|
|
39
|
+
"allow-path",
|
|
40
|
+
];
|
|
41
|
+
export const VALID_ROLES = [
|
|
25
42
|
"pm",
|
|
26
43
|
"architect",
|
|
27
44
|
"developer",
|
|
@@ -34,8 +51,79 @@ const VALID_ROLES = [
|
|
|
34
51
|
"verifier",
|
|
35
52
|
"generic",
|
|
36
53
|
];
|
|
37
|
-
const VALID_MODES = ["research", "plan", "execute", "review"];
|
|
38
|
-
const VALID_PRESETS = ["solo", "duo", "squad"];
|
|
54
|
+
export const VALID_MODES = ["research", "plan", "execute", "review"];
|
|
55
|
+
export const VALID_PRESETS = ["solo", "duo", "squad"];
|
|
56
|
+
export const VALID_TASK_ACTIONS = ["add", "claim", "done", "block", "cancel", "list"];
|
|
57
|
+
const TEAM_SPAWN_FLAGS = ["--name", "--harness"];
|
|
58
|
+
const TEAM_COMPLETION_DESCRIPTIONS = {
|
|
59
|
+
help: "Show team commands",
|
|
60
|
+
spawn: "Create a teammate",
|
|
61
|
+
send: "Send work to a teammate",
|
|
62
|
+
status: "Show teammate status",
|
|
63
|
+
stop: "Stop a teammate's current turn",
|
|
64
|
+
terminate: "Remove a teammate",
|
|
65
|
+
approve: "Approve a teammate request",
|
|
66
|
+
mode: "Change how a teammate works",
|
|
67
|
+
preset: "Start with a preset team shape",
|
|
68
|
+
dashboard: "Show or hide the team panel",
|
|
69
|
+
progress: "Show teammate progress",
|
|
70
|
+
psyche: "Show teammate decision settings",
|
|
71
|
+
task: "Manage shared team tasks",
|
|
72
|
+
mail: "Send a teammate-to-teammate note",
|
|
73
|
+
"allow-path": "Allow a teammate to write in a path",
|
|
74
|
+
pm: "Plan priorities and coordinate work",
|
|
75
|
+
architect: "Design structure and break down trade-offs",
|
|
76
|
+
developer: "Have a teammate write code",
|
|
77
|
+
designer: "Review product and interaction details",
|
|
78
|
+
"data-analyst": "Check evidence, metrics, and results",
|
|
79
|
+
researcher: "Explore read-only context",
|
|
80
|
+
reviewer: "Review code or plans without writing",
|
|
81
|
+
implementer: "Have a teammate write code",
|
|
82
|
+
planner: "Have a teammate make a plan",
|
|
83
|
+
verifier: "Check whether the work is correct",
|
|
84
|
+
generic: "Use a flexible teammate role",
|
|
85
|
+
"--name": "Choose the teammate name",
|
|
86
|
+
"--harness": "Use structured progress tracking",
|
|
87
|
+
research: "Read and investigate without writing",
|
|
88
|
+
plan: "Write a plan before code changes",
|
|
89
|
+
execute: "Allow code changes",
|
|
90
|
+
review: "Review without writing",
|
|
91
|
+
solo: "Use one focused teammate",
|
|
92
|
+
duo: "Use two teammates with complementary roles",
|
|
93
|
+
squad: "Use a larger team for broader work",
|
|
94
|
+
add: "Add a shared task",
|
|
95
|
+
claim: "Assign a task to a teammate",
|
|
96
|
+
done: "Mark a task complete",
|
|
97
|
+
block: "Mark a task blocked",
|
|
98
|
+
cancel: "Cancel a task",
|
|
99
|
+
list: "List shared tasks",
|
|
100
|
+
};
|
|
101
|
+
function getTeamCompletionValues(commandName, context) {
|
|
102
|
+
switch (commandName) {
|
|
103
|
+
case "team":
|
|
104
|
+
return !context || context.tokenIndex === 0 ? TEAM_ROOT_COMPLETIONS : [];
|
|
105
|
+
case "team:spawn":
|
|
106
|
+
if (!context)
|
|
107
|
+
return [...VALID_ROLES, ...TEAM_SPAWN_FLAGS];
|
|
108
|
+
return context.tokenIndex === 0 ? VALID_ROLES : TEAM_SPAWN_FLAGS;
|
|
109
|
+
case "team:preset":
|
|
110
|
+
return !context || context.tokenIndex === 0 ? VALID_PRESETS : [];
|
|
111
|
+
case "team:task":
|
|
112
|
+
return !context || context.tokenIndex === 0 ? VALID_TASK_ACTIONS : [];
|
|
113
|
+
case "team:mode":
|
|
114
|
+
return context?.tokenIndex === 1 ? VALID_MODES : [];
|
|
115
|
+
default:
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export function getTeamArgumentCompletions(commandName, argumentPrefix, context) {
|
|
120
|
+
const prefix = argumentPrefix.trim().toLowerCase();
|
|
121
|
+
const values = getTeamCompletionValues(commandName, context);
|
|
122
|
+
const matches = values.filter((value) => value.startsWith(prefix));
|
|
123
|
+
return matches.length > 0
|
|
124
|
+
? matches.map((value) => ({ value, label: value, description: TEAM_COMPLETION_DESCRIPTIONS[value] }))
|
|
125
|
+
: null;
|
|
126
|
+
}
|
|
39
127
|
/**
|
|
40
128
|
* Parse a /team command invocation.
|
|
41
129
|
*/
|
|
@@ -11,12 +11,8 @@ import { renderTeamDashboard, renderTeamFooterStatus } from "./team-dashboard.js
|
|
|
11
11
|
import { formatPsycheWeights } from "./team-psyche.js";
|
|
12
12
|
export const TEAM_MESSAGE_TYPE = "team";
|
|
13
13
|
let dashboardVisible = false;
|
|
14
|
-
let dashboardAutoHideTimer;
|
|
15
14
|
export function clearTeamDashboardTimer() {
|
|
16
|
-
|
|
17
|
-
clearTimeout(dashboardAutoHideTimer);
|
|
18
|
-
dashboardAutoHideTimer = undefined;
|
|
19
|
-
}
|
|
15
|
+
// Kept for the extension shutdown contract; dashboard visibility is now explicit.
|
|
20
16
|
}
|
|
21
17
|
export function createTeamMessageRenderer() {
|
|
22
18
|
return (message, _options, theme) => {
|
|
@@ -129,15 +125,9 @@ export function toggleTeamDashboard(ctx, teamRuntime) {
|
|
|
129
125
|
export function updateTeamUi(ctx, teamRuntime) {
|
|
130
126
|
const teammates = teamRuntime.getAllTeammates();
|
|
131
127
|
const hasRunning = teammates.some((teammate) => teammate.status === "running");
|
|
132
|
-
|
|
133
|
-
ctx.ui.setStatus("team", renderTeamFooterStatus(teammates));
|
|
134
|
-
ctx.ui.setWidget("team-dashboard",
|
|
135
|
-
if (!dashboardVisible && !hasRunning && teammates.length > 0) {
|
|
136
|
-
dashboardAutoHideTimer = setTimeout(() => {
|
|
137
|
-
ctx.ui.setWidget("team-dashboard", undefined);
|
|
138
|
-
dashboardAutoHideTimer = undefined;
|
|
139
|
-
}, 30_000);
|
|
140
|
-
}
|
|
128
|
+
const shouldShowTeamUi = dashboardVisible || hasRunning;
|
|
129
|
+
ctx.ui.setStatus("team", shouldShowTeamUi ? renderTeamFooterStatus(teammates) : undefined);
|
|
130
|
+
ctx.ui.setWidget("team-dashboard", shouldShowTeamUi ? renderTeamDashboard(teammates, 80, { expanded: dashboardVisible }) : undefined);
|
|
141
131
|
}
|
|
142
132
|
export function setTeamActivity(ctx, lines) {
|
|
143
133
|
const [firstLine] = lines;
|
|
@@ -4,6 +4,12 @@ import { applyTokenSavePlan } from "./runner.js";
|
|
|
4
4
|
import { planCommand } from "./rewrite.js";
|
|
5
5
|
import { applyTomlStyleFilter } from "./toml-dsl.js";
|
|
6
6
|
import { TokenSaveTracker } from "./tracking.js";
|
|
7
|
+
const TOKENSAVE_COMMAND_COMPLETIONS = [
|
|
8
|
+
{ value: "summary", label: "summary", description: "Show total shell output savings" },
|
|
9
|
+
{ value: "history", label: "history", description: "Show recent shortened commands" },
|
|
10
|
+
{ value: "reload", label: "reload", description: "Reload project output-shortening rules" },
|
|
11
|
+
{ value: "plan", label: "plan", description: "Preview how a command will be shortened" },
|
|
12
|
+
];
|
|
7
13
|
function getTextContent(event) {
|
|
8
14
|
const parts = event.content.filter((part) => part.type === "text");
|
|
9
15
|
if (parts.length === 0)
|
|
@@ -29,7 +35,14 @@ export default function tokenSaveExtension(api) {
|
|
|
29
35
|
configuredFilters = filters;
|
|
30
36
|
});
|
|
31
37
|
api.registerCommand("tokensave", {
|
|
32
|
-
description: "
|
|
38
|
+
description: "Review shell output shortening. Usage: /tokensave [summary|history|reload|plan <cmd>]",
|
|
39
|
+
getArgumentCompletions: (argumentPrefix, context) => {
|
|
40
|
+
if (context && context.tokenIndex > 0)
|
|
41
|
+
return null;
|
|
42
|
+
const prefix = argumentPrefix.trim().toLowerCase();
|
|
43
|
+
const values = TOKENSAVE_COMMAND_COMPLETIONS.filter((value) => value.value.startsWith(prefix));
|
|
44
|
+
return values.length > 0 ? values : null;
|
|
45
|
+
},
|
|
33
46
|
async handler(args, ctx) {
|
|
34
47
|
const trimmed = args.trim();
|
|
35
48
|
if (trimmed === "reload") {
|
|
@@ -16,6 +16,13 @@ import { SessionManager } from "../../../core/session/session-manager.js";
|
|
|
16
16
|
import { APP_NAME, getExportTemplateDir } from "../../../config.js";
|
|
17
17
|
import { getResolvedThemeColors, getThemeExportColors } from "../../../modes/interactive/theme/theme.js";
|
|
18
18
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const EXPORT_COMMAND_COMPLETIONS = [
|
|
20
|
+
{
|
|
21
|
+
value: "./nanopencil-session.html",
|
|
22
|
+
label: "./nanopencil-session.html",
|
|
23
|
+
description: "Choose the output file for this session export",
|
|
24
|
+
},
|
|
25
|
+
];
|
|
19
26
|
// Re-export core functions for use by AgentSession
|
|
20
27
|
export { exportSessionToHtml, exportFromFile } from "../../../core/export-html/index.js";
|
|
21
28
|
/**
|
|
@@ -239,16 +246,23 @@ export async function extExportFromFile(inputPath, options) {
|
|
|
239
246
|
export default async function exportHtmlExtension(api) {
|
|
240
247
|
// Register /export command
|
|
241
248
|
api.registerCommand("export", {
|
|
242
|
-
description: "
|
|
243
|
-
|
|
249
|
+
description: "Save this session as a shareable HTML file",
|
|
250
|
+
getArgumentCompletions: (argumentPrefix, context) => {
|
|
251
|
+
if (context && context.tokenIndex > 0)
|
|
252
|
+
return null;
|
|
253
|
+
const prefix = argumentPrefix.trim().toLowerCase();
|
|
254
|
+
const values = EXPORT_COMMAND_COMPLETIONS.filter((item) => item.value.startsWith(prefix));
|
|
255
|
+
return values.length > 0 ? values : null;
|
|
256
|
+
},
|
|
257
|
+
handler: async (args, ctx) => {
|
|
244
258
|
// Get session manager from context
|
|
245
259
|
const sessionManager = ctx.sessionManager;
|
|
246
260
|
if (!sessionManager) {
|
|
247
261
|
throw new Error("Session manager not available");
|
|
248
262
|
}
|
|
249
|
-
|
|
250
|
-
// Pass undefined for state since we don't have access to it here
|
|
251
|
-
const filePath = await extExportSessionToHtml(sessionManager, undefined);
|
|
263
|
+
const outputPath = args.trim() || undefined;
|
|
264
|
+
// Pass undefined for state since we don't have access to it here.
|
|
265
|
+
const filePath = await extExportSessionToHtml(sessionManager, undefined, outputPath);
|
|
252
266
|
console.error(`Session exported to: ${filePath}`);
|
|
253
267
|
},
|
|
254
268
|
});
|
|
@@ -17,6 +17,10 @@ import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
|
17
17
|
import { isAbsolute, join, relative, resolve } from "path";
|
|
18
18
|
import { DynamicBorder } from "../../../modes/interactive/components/dynamic-border.js";
|
|
19
19
|
import { getMarkdownTheme } from "../../../modes/interactive/theme/theme.js";
|
|
20
|
+
const SIMPLIFY_COMMAND_COMPLETIONS = [
|
|
21
|
+
{ value: "--dry-run", label: "--dry-run", description: "Preview changes without writing files" },
|
|
22
|
+
{ value: "--no-test", label: "--no-test", description: "Skip the verification command after applying changes" },
|
|
23
|
+
];
|
|
20
24
|
// =============================================================================
|
|
21
25
|
// Git Utilities
|
|
22
26
|
// =============================================================================
|
|
@@ -416,11 +420,13 @@ async function executeSimplify(options, ctx) {
|
|
|
416
420
|
// =============================================================================
|
|
417
421
|
export default function simplifyExtension(api) {
|
|
418
422
|
api.registerCommand("simplify", {
|
|
419
|
-
description: "
|
|
420
|
-
getArgumentCompletions: (prefix) => {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
423
|
+
description: "Suggest smaller code changes for files you choose",
|
|
424
|
+
getArgumentCompletions: (prefix, context) => {
|
|
425
|
+
if (context && context.tokenIndex > 0)
|
|
426
|
+
return null;
|
|
427
|
+
const normalizedPrefix = prefix.trim().toLowerCase();
|
|
428
|
+
const options = SIMPLIFY_COMMAND_COMPLETIONS.filter((item) => item.value.startsWith(normalizedPrefix));
|
|
429
|
+
return options.length > 0 ? options : null;
|
|
424
430
|
},
|
|
425
431
|
handler: async (args, ctx) => {
|
|
426
432
|
const parts = args.trim().split(/\s+/).filter(Boolean);
|