@trenchwork/erosolar 1.1.28 → 1.1.29

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 (94) hide show
  1. package/SECURITY.md +35 -0
  2. package/dist/bin/deepseek.js +14 -8
  3. package/dist/bin/deepseek.js.map +1 -1
  4. package/dist/capabilities/index.d.ts +6 -0
  5. package/dist/capabilities/index.d.ts.map +1 -1
  6. package/dist/capabilities/index.js +6 -0
  7. package/dist/capabilities/index.js.map +1 -1
  8. package/dist/capabilities/interactionCapability.d.ts +6 -0
  9. package/dist/capabilities/interactionCapability.d.ts.map +1 -0
  10. package/dist/capabilities/interactionCapability.js +17 -0
  11. package/dist/capabilities/interactionCapability.js.map +1 -0
  12. package/dist/capabilities/mcpCapability.d.ts.map +1 -1
  13. package/dist/capabilities/mcpCapability.js +4 -2
  14. package/dist/capabilities/mcpCapability.js.map +1 -1
  15. package/dist/capabilities/monitorCapability.d.ts +6 -0
  16. package/dist/capabilities/monitorCapability.d.ts.map +1 -0
  17. package/dist/capabilities/monitorCapability.js +19 -0
  18. package/dist/capabilities/monitorCapability.js.map +1 -0
  19. package/dist/capabilities/planModeCapability.d.ts +6 -0
  20. package/dist/capabilities/planModeCapability.d.ts.map +1 -0
  21. package/dist/capabilities/planModeCapability.js +16 -0
  22. package/dist/capabilities/planModeCapability.js.map +1 -0
  23. package/dist/capabilities/scheduleCapability.d.ts +6 -0
  24. package/dist/capabilities/scheduleCapability.d.ts.map +1 -0
  25. package/dist/capabilities/scheduleCapability.js +16 -0
  26. package/dist/capabilities/scheduleCapability.js.map +1 -0
  27. package/dist/capabilities/triggerCapability.d.ts +6 -0
  28. package/dist/capabilities/triggerCapability.d.ts.map +1 -0
  29. package/dist/capabilities/triggerCapability.js +16 -0
  30. package/dist/capabilities/triggerCapability.js.map +1 -0
  31. package/dist/capabilities/worktreeCapability.d.ts +6 -0
  32. package/dist/capabilities/worktreeCapability.d.ts.map +1 -0
  33. package/dist/capabilities/worktreeCapability.js +16 -0
  34. package/dist/capabilities/worktreeCapability.js.map +1 -0
  35. package/dist/contracts/agent-schemas.json +1 -1
  36. package/dist/core/projectTracker.d.ts +96 -0
  37. package/dist/core/projectTracker.d.ts.map +1 -0
  38. package/dist/core/projectTracker.js +275 -0
  39. package/dist/core/projectTracker.js.map +1 -0
  40. package/dist/headless/interactiveShell.js +35 -0
  41. package/dist/headless/interactiveShell.js.map +1 -1
  42. package/dist/plugins/tools/mcp/mcpClient.d.ts +10 -0
  43. package/dist/plugins/tools/mcp/mcpClient.d.ts.map +1 -1
  44. package/dist/plugins/tools/mcp/mcpClient.js +6 -0
  45. package/dist/plugins/tools/mcp/mcpClient.js.map +1 -1
  46. package/dist/runtime/node.d.ts +2 -0
  47. package/dist/runtime/node.d.ts.map +1 -1
  48. package/dist/runtime/node.js +28 -28
  49. package/dist/runtime/node.js.map +1 -1
  50. package/dist/runtime/profileGates.d.ts +19 -0
  51. package/dist/runtime/profileGates.d.ts.map +1 -0
  52. package/dist/runtime/profileGates.js +23 -0
  53. package/dist/runtime/profileGates.js.map +1 -0
  54. package/dist/tools/interactionTools.d.ts +16 -0
  55. package/dist/tools/interactionTools.d.ts.map +1 -0
  56. package/dist/tools/interactionTools.js +207 -0
  57. package/dist/tools/interactionTools.js.map +1 -0
  58. package/dist/tools/monitorTools.d.ts +16 -0
  59. package/dist/tools/monitorTools.d.ts.map +1 -0
  60. package/dist/tools/monitorTools.js +159 -0
  61. package/dist/tools/monitorTools.js.map +1 -0
  62. package/dist/tools/planModeTools.d.ts +32 -0
  63. package/dist/tools/planModeTools.d.ts.map +1 -0
  64. package/dist/tools/planModeTools.js +200 -0
  65. package/dist/tools/planModeTools.js.map +1 -0
  66. package/dist/tools/scheduleTools.d.ts +39 -0
  67. package/dist/tools/scheduleTools.d.ts.map +1 -0
  68. package/dist/tools/scheduleTools.js +182 -0
  69. package/dist/tools/scheduleTools.js.map +1 -0
  70. package/dist/tools/triggerTools.d.ts +28 -0
  71. package/dist/tools/triggerTools.d.ts.map +1 -0
  72. package/dist/tools/triggerTools.js +210 -0
  73. package/dist/tools/triggerTools.js.map +1 -0
  74. package/dist/tools/worktreeTools.d.ts +21 -0
  75. package/dist/tools/worktreeTools.d.ts.map +1 -0
  76. package/dist/tools/worktreeTools.js +158 -0
  77. package/dist/tools/worktreeTools.js.map +1 -0
  78. package/dist/ui/ink/App.d.ts.map +1 -1
  79. package/dist/ui/ink/App.js +2 -1
  80. package/dist/ui/ink/App.js.map +1 -1
  81. package/dist/ui/ink/ChatStatic.d.ts.map +1 -1
  82. package/dist/ui/ink/ChatStatic.js +7 -6
  83. package/dist/ui/ink/ChatStatic.js.map +1 -1
  84. package/dist/ui/ink/StatusLine.d.ts.map +1 -1
  85. package/dist/ui/ink/StatusLine.js +2 -1
  86. package/dist/ui/ink/StatusLine.js.map +1 -1
  87. package/dist/ui/theme.d.ts.map +1 -1
  88. package/dist/ui/theme.js +31 -29
  89. package/dist/ui/theme.js.map +1 -1
  90. package/dist/utils/lambdaClient.d.ts +35 -0
  91. package/dist/utils/lambdaClient.d.ts.map +1 -0
  92. package/dist/utils/lambdaClient.js +81 -0
  93. package/dist/utils/lambdaClient.js.map +1 -0
  94. package/package.json +1 -1
@@ -0,0 +1,207 @@
1
+ /**
2
+ * AskUserQuestion + PushNotification — agent-callable user-interaction tools.
3
+ *
4
+ * AskUserQuestion presents structured single/multi-select prompts. The
5
+ * user's answer comes back as the tool result. The CLI surface uses
6
+ * src/utils/askUserPrompt for rendering; in non-interactive contexts
7
+ * (HITL pause / portal-driven runs) the prompt is queued for the host.
8
+ *
9
+ * PushNotification fires an OS-native desktop notification — useful
10
+ * when the agent is running detached (long fuzz job, scheduled run)
11
+ * and wants to flag the user without stealing focus.
12
+ */
13
+ import { execFile, execFileSync } from 'node:child_process';
14
+ import { platform } from 'node:os';
15
+ import { createInterface } from 'node:readline';
16
+ import { formatAskUserPrompt } from '../utils/askUserPrompt.js';
17
+ import { callLambda } from '../utils/lambdaClient.js';
18
+ const MAX_OPTIONS = 9;
19
+ function parseSelection(raw, optionCount, multiSelect) {
20
+ const trimmed = raw.trim();
21
+ if (!trimmed)
22
+ return [];
23
+ const tokens = multiSelect ? trimmed.split(/[,\s]+/) : [trimmed];
24
+ const out = [];
25
+ for (const token of tokens) {
26
+ const idx = Number.parseInt(token, 10) - 1;
27
+ if (Number.isFinite(idx) && idx >= 0 && idx < optionCount) {
28
+ out.push(idx);
29
+ }
30
+ }
31
+ return Array.from(new Set(out));
32
+ }
33
+ async function askViaPortal(question, header, options, multiSelect, timeoutMs) {
34
+ // Push the question to Firestore via Lambda; the portal renders it
35
+ // under the "Pending questions" panel. The user clicks an answer,
36
+ // which writes back through cliQuestionAnswer; we poll until the
37
+ // doc says answered or we hit the timeout.
38
+ const created = await callLambda('cliQuestionCreate', {
39
+ question, header, multiSelect, options,
40
+ });
41
+ if (!created.ok || !created.result?.id) {
42
+ return { selected: [], cancelled: true };
43
+ }
44
+ const id = created.result.id;
45
+ const deadline = Date.now() + timeoutMs;
46
+ const interval = 1500;
47
+ while (Date.now() < deadline) {
48
+ await new Promise((r) => setTimeout(r, interval));
49
+ const polled = await callLambda('cliQuestionPoll', { id });
50
+ if (polled.ok && polled.result?.found && polled.result.answered) {
51
+ return { selected: polled.result.selected ?? [], cancelled: false };
52
+ }
53
+ }
54
+ return { selected: [], cancelled: true };
55
+ }
56
+ async function askInternal(question, header, options, multiSelect) {
57
+ if (!process.stdin.isTTY) {
58
+ // HITL bridge: ask through the portal. 10-minute timeout by default
59
+ // so a forgotten detached agent eventually returns rather than
60
+ // hanging forever.
61
+ if (process.env['EROSOLAR_DISABLE_PORTAL_HITL'] === '1') {
62
+ return { selected: [], cancelled: true };
63
+ }
64
+ return askViaPortal(question, header, options, multiSelect, 10 * 60 * 1000);
65
+ }
66
+ const formatted = formatAskUserPrompt({ question, header, options, multiSelect });
67
+ process.stdout.write(formatted.lines.join('\n') + '\n');
68
+ process.stdout.write(multiSelect
69
+ ? 'Enter option numbers (comma-separated), or blank to cancel: '
70
+ : 'Enter option number, or blank to cancel: ');
71
+ const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: false });
72
+ try {
73
+ const line = await new Promise((resolve) => {
74
+ rl.once('line', (l) => resolve(l));
75
+ rl.once('close', () => resolve(''));
76
+ });
77
+ const indices = parseSelection(line, options.length, multiSelect);
78
+ if (indices.length === 0)
79
+ return { selected: [], cancelled: true };
80
+ return { selected: indices.map((i) => options[i].label), cancelled: false };
81
+ }
82
+ finally {
83
+ rl.close();
84
+ }
85
+ }
86
+ function notifyLinux(title, body) {
87
+ try {
88
+ execFileSync('notify-send', ['--', title, body], {
89
+ stdio: ['ignore', 'ignore', 'ignore'],
90
+ });
91
+ return true;
92
+ }
93
+ catch {
94
+ return false;
95
+ }
96
+ }
97
+ function notifyMac(title, body) {
98
+ // osascript is built into macOS; no extra install required.
99
+ const escapedTitle = title.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
100
+ const escapedBody = body.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
101
+ const script = `display notification "${escapedBody}" with title "${escapedTitle}"`;
102
+ try {
103
+ execFileSync('osascript', ['-e', script], { stdio: ['ignore', 'ignore', 'ignore'] });
104
+ return true;
105
+ }
106
+ catch {
107
+ return false;
108
+ }
109
+ }
110
+ function notifyFallback(title, body) {
111
+ // Last resort: ring the bell + write the title/body to stderr so the
112
+ // user at least sees it in the active terminal session.
113
+ process.stderr.write(`\x07[notify] ${title}\n${body}\n`);
114
+ }
115
+ export async function pushNotification(title, body) {
116
+ const t = title.trim();
117
+ const b = body.trim();
118
+ if (!t)
119
+ return 'fallback';
120
+ const os = platform();
121
+ if (os === 'linux' && notifyLinux(t, b))
122
+ return 'os';
123
+ if (os === 'darwin' && notifyMac(t, b))
124
+ return 'os';
125
+ notifyFallback(t, b);
126
+ return 'fallback';
127
+ }
128
+ export function createInteractionTools() {
129
+ return [
130
+ {
131
+ name: 'AskUserQuestion',
132
+ description: 'Ask the user a structured question (single or multi-select). ' +
133
+ 'Use to disambiguate scope, pick between trade-offs, or confirm ' +
134
+ 'an irreversible action. Each option needs a label + description.',
135
+ parameters: {
136
+ type: 'object',
137
+ properties: {
138
+ question: { type: 'string', description: 'The full question (≤200 chars).' },
139
+ header: { type: 'string', description: 'Short label (≤24 chars).' },
140
+ multiSelect: { type: 'boolean', description: 'Allow multiple answers.' },
141
+ options: {
142
+ type: 'array',
143
+ description: `Between 2 and ${MAX_OPTIONS} options. Each: { label, description }.`,
144
+ items: {
145
+ type: 'object',
146
+ properties: {
147
+ label: { type: 'string' },
148
+ description: { type: 'string' },
149
+ },
150
+ required: ['label', 'description'],
151
+ },
152
+ },
153
+ },
154
+ required: ['question', 'header', 'options'],
155
+ },
156
+ handler: async (args) => {
157
+ const question = String(args['question'] ?? '').trim();
158
+ const header = String(args['header'] ?? '').trim().slice(0, 24);
159
+ const multiSelect = Boolean(args['multiSelect']);
160
+ const rawOptions = Array.isArray(args['options']) ? args['options'] : [];
161
+ if (!question)
162
+ return 'AskUserQuestion requires `question`.';
163
+ if (rawOptions.length < 2 || rawOptions.length > MAX_OPTIONS) {
164
+ return `AskUserQuestion requires 2..${MAX_OPTIONS} options.`;
165
+ }
166
+ const options = rawOptions.map((raw) => {
167
+ const r = (raw ?? {});
168
+ return {
169
+ label: String(r['label'] ?? '').trim() || '(unlabeled)',
170
+ description: String(r['description'] ?? '').trim(),
171
+ };
172
+ });
173
+ const answer = await askInternal(question, header, options, multiSelect);
174
+ if (answer.cancelled)
175
+ return 'User cancelled the question.';
176
+ return JSON.stringify({ selected: answer.selected });
177
+ },
178
+ },
179
+ {
180
+ name: 'PushNotification',
181
+ description: 'Fire an OS-native desktop notification (Linux notify-send / macOS ' +
182
+ 'osascript). Use when the agent is running detached and wants to ' +
183
+ 'flag the user without stealing focus. Falls back to terminal-bell ' +
184
+ '+ stderr if no native channel is available.',
185
+ parameters: {
186
+ type: 'object',
187
+ properties: {
188
+ title: { type: 'string', description: 'Short notification title.' },
189
+ body: { type: 'string', description: 'Body text.' },
190
+ },
191
+ required: ['title'],
192
+ },
193
+ handler: async (args) => {
194
+ const title = String(args['title'] ?? '').trim();
195
+ if (!title)
196
+ return 'PushNotification requires a title.';
197
+ const body = String(args['body'] ?? '').trim();
198
+ const channel = await pushNotification(title, body);
199
+ return `Notification dispatched (${channel}): ${title}`;
200
+ },
201
+ },
202
+ ];
203
+ }
204
+ // Suppress "unused import" warnings for the shape execFile is here in case
205
+ // callers want a non-blocking variant later; intentionally not deleted.
206
+ void execFile;
207
+ //# sourceMappingURL=interactionTools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactionTools.js","sourceRoot":"","sources":["../../src/tools/interactionTools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Monitor — Claude-Code-parity background process management.
3
+ *
4
+ * Spawns a long-running command and lets the agent inspect/stream/stop it
5
+ * without blocking the conversation. Used for: dev servers, watchers,
6
+ * fuzzers, builds-in-progress. The Bash tool stays best for short
7
+ * commands; Monitor takes over once `run_in_background: true` is desired.
8
+ */
9
+ import type { ToolDefinition } from '../core/toolRuntime.js';
10
+ export declare function getMonitorRegistry(): ReadonlyMap<string, {
11
+ command: string;
12
+ status: string;
13
+ }>;
14
+ export declare function shutdownAllMonitors(): Promise<void>;
15
+ export declare function createMonitorTools(workingDir: string): ToolDefinition[];
16
+ //# sourceMappingURL=monitorTools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitorTools.d.ts","sourceRoot":"","sources":["../../src/tools/monitorTools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AA+B7D,wBAAgB,kBAAkB,IAAI,WAAW,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAI7F;AAED,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAWzD;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,EAAE,CA8GvE"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Monitor — Claude-Code-parity background process management.
3
+ *
4
+ * Spawns a long-running command and lets the agent inspect/stream/stop it
5
+ * without blocking the conversation. Used for: dev servers, watchers,
6
+ * fuzzers, builds-in-progress. The Bash tool stays best for short
7
+ * commands; Monitor takes over once `run_in_background: true` is desired.
8
+ */
9
+ import { spawn } from 'node:child_process';
10
+ const REGISTRY = new Map();
11
+ const MAX_BUFFER_LINES = 1000;
12
+ function nextId() {
13
+ return `mon-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
14
+ }
15
+ function appendOutput(entry, chunk) {
16
+ const text = typeof chunk === 'string' ? chunk : chunk.toString('utf-8');
17
+ for (const line of text.split(/\r?\n/)) {
18
+ if (line.length === 0)
19
+ continue;
20
+ entry.buffer.push(line);
21
+ }
22
+ if (entry.buffer.length > MAX_BUFFER_LINES) {
23
+ entry.buffer.splice(0, entry.buffer.length - MAX_BUFFER_LINES);
24
+ }
25
+ }
26
+ export function getMonitorRegistry() {
27
+ const out = new Map();
28
+ for (const [id, e] of REGISTRY)
29
+ out.set(id, { command: e.command, status: e.status });
30
+ return out;
31
+ }
32
+ export async function shutdownAllMonitors() {
33
+ for (const entry of REGISTRY.values()) {
34
+ if (entry.status === 'running') {
35
+ try {
36
+ entry.proc.kill('SIGTERM');
37
+ }
38
+ catch {
39
+ // Ignore: process may already have exited.
40
+ }
41
+ }
42
+ }
43
+ REGISTRY.clear();
44
+ }
45
+ export function createMonitorTools(workingDir) {
46
+ return [
47
+ {
48
+ name: 'MonitorStart',
49
+ description: 'Spawn a long-running shell command in the background and return ' +
50
+ 'a monitor id. Use for dev servers, watchers, fuzzers, builds. ' +
51
+ 'Subsequent MonitorRead calls stream stdout/stderr lines.',
52
+ parameters: {
53
+ type: 'object',
54
+ properties: {
55
+ command: { type: 'string', description: 'Shell command to run.' },
56
+ cwd: { type: 'string', description: 'Working directory (defaults to session cwd).' },
57
+ },
58
+ required: ['command'],
59
+ },
60
+ handler: async (args) => {
61
+ const command = String(args['command'] ?? '').trim();
62
+ if (!command)
63
+ return 'MonitorStart requires a command.';
64
+ const cwd = typeof args['cwd'] === 'string' && args['cwd'].trim() ? args['cwd'].trim() : workingDir;
65
+ const id = nextId();
66
+ const proc = spawn(command, {
67
+ cwd,
68
+ shell: true,
69
+ detached: false,
70
+ stdio: ['ignore', 'pipe', 'pipe'],
71
+ });
72
+ const entry = {
73
+ id,
74
+ command,
75
+ startedAt: new Date().toISOString(),
76
+ status: 'running',
77
+ buffer: [],
78
+ proc,
79
+ };
80
+ proc.stdout?.on('data', (chunk) => appendOutput(entry, chunk));
81
+ proc.stderr?.on('data', (chunk) => appendOutput(entry, chunk));
82
+ proc.on('exit', (code, signal) => {
83
+ entry.status = 'exited';
84
+ entry.exitCode = code;
85
+ entry.signal = signal;
86
+ });
87
+ proc.on('error', (err) => {
88
+ appendOutput(entry, `[monitor error] ${err.message}`);
89
+ entry.status = 'exited';
90
+ entry.exitCode = -1;
91
+ });
92
+ REGISTRY.set(id, entry);
93
+ return `Monitor "${id}" started: ${command}`;
94
+ },
95
+ },
96
+ {
97
+ name: 'MonitorRead',
98
+ description: 'Read buffered output from a monitor. Pass `since: <line-index>` ' +
99
+ 'to only return lines added since that index (the response includes ' +
100
+ 'a `nextCursor` to use for the next read).',
101
+ parameters: {
102
+ type: 'object',
103
+ properties: {
104
+ id: { type: 'string', description: 'Monitor id from MonitorStart.' },
105
+ since: { type: 'number', description: 'Cursor returned by previous read.' },
106
+ },
107
+ required: ['id'],
108
+ },
109
+ handler: async (args) => {
110
+ const id = String(args['id'] ?? '');
111
+ const entry = REGISTRY.get(id);
112
+ if (!entry)
113
+ return `No monitor with id "${id}".`;
114
+ const since = Number.isFinite(Number(args['since'])) ? Math.max(0, Number(args['since'])) : 0;
115
+ const slice = entry.buffer.slice(since);
116
+ const cursor = entry.buffer.length;
117
+ const head = `[${entry.status}${entry.status === 'exited' ? ` exit=${entry.exitCode ?? 'null'}` : ''}] cursor=${cursor}`;
118
+ return [head, ...slice].join('\n');
119
+ },
120
+ },
121
+ {
122
+ name: 'MonitorStop',
123
+ description: 'Send SIGTERM to a running monitor. Idempotent.',
124
+ parameters: {
125
+ type: 'object',
126
+ properties: { id: { type: 'string' } },
127
+ required: ['id'],
128
+ },
129
+ handler: async (args) => {
130
+ const id = String(args['id'] ?? '');
131
+ const entry = REGISTRY.get(id);
132
+ if (!entry)
133
+ return `No monitor with id "${id}".`;
134
+ if (entry.status === 'running') {
135
+ try {
136
+ entry.proc.kill('SIGTERM');
137
+ }
138
+ catch (err) {
139
+ return `MonitorStop kill failed: ${err instanceof Error ? err.message : String(err)}`;
140
+ }
141
+ }
142
+ return `Monitor "${id}" stopped.`;
143
+ },
144
+ },
145
+ {
146
+ name: 'MonitorList',
147
+ description: 'List active and recently-exited monitors.',
148
+ parameters: { type: 'object', properties: {} },
149
+ handler: async () => {
150
+ if (REGISTRY.size === 0)
151
+ return 'No monitors.';
152
+ return Array.from(REGISTRY.values())
153
+ .map((e) => `${e.id} [${e.status}] ${e.command}`)
154
+ .join('\n');
155
+ },
156
+ },
157
+ ];
158
+ }
159
+ //# sourceMappingURL=monitorTools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitorTools.js","sourceRoot":"","sources":["../../src/tools/monitorTools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * EnterPlanMode / ExitPlanMode — Claude-Code-parity planning workflow.
3
+ *
4
+ * The agent enters plan mode to write down a multi-step implementation
5
+ * plan that the user reviews and approves before any tool that mutates
6
+ * state runs. ExitPlanMode is called when the plan is approved.
7
+ *
8
+ * Plans are persisted to <workingDir>/.erosolar/plans/<id>.json so the
9
+ * portal can render them and the user can revisit them later.
10
+ */
11
+ import type { ToolDefinition } from '../core/toolRuntime.js';
12
+ export type PlanStepStatus = 'pending' | 'in_progress' | 'completed' | 'skipped';
13
+ export interface PlanStep {
14
+ id: string;
15
+ title: string;
16
+ detail?: string;
17
+ status: PlanStepStatus;
18
+ }
19
+ export interface PlanRecord {
20
+ id: string;
21
+ title: string;
22
+ goal?: string;
23
+ steps: PlanStep[];
24
+ status: 'draft' | 'approved' | 'completed' | 'abandoned';
25
+ createdAt: string;
26
+ updatedAt: string;
27
+ }
28
+ export declare function getActivePlan(): PlanRecord | null;
29
+ export declare function clearActivePlan(): void;
30
+ export declare function listStoredPlans(workingDir: string): PlanRecord[];
31
+ export declare function createPlanModeTools(workingDir: string): ToolDefinition[];
32
+ //# sourceMappingURL=planModeTools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planModeTools.d.ts","sourceRoot":"","sources":["../../src/tools/planModeTools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,aAAa,GAAG,WAAW,GAAG,SAAS,CAAC;AAEjF,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,CAAC;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AA8BD,wBAAgB,aAAa,IAAI,UAAU,GAAG,IAAI,CAEjD;AAED,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,EAAE,CAahE;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,EAAE,CA6IxE"}
@@ -0,0 +1,200 @@
1
+ /**
2
+ * EnterPlanMode / ExitPlanMode — Claude-Code-parity planning workflow.
3
+ *
4
+ * The agent enters plan mode to write down a multi-step implementation
5
+ * plan that the user reviews and approves before any tool that mutates
6
+ * state runs. ExitPlanMode is called when the plan is approved.
7
+ *
8
+ * Plans are persisted to <workingDir>/.erosolar/plans/<id>.json so the
9
+ * portal can render them and the user can revisit them later.
10
+ */
11
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
12
+ import { join } from 'node:path';
13
+ let ACTIVE_PLAN = null;
14
+ function planStore(workingDir) {
15
+ return join(workingDir, '.erosolar', 'plans');
16
+ }
17
+ function planPath(workingDir, id) {
18
+ return join(planStore(workingDir), `${id}.json`);
19
+ }
20
+ function safeId(input) {
21
+ const s = typeof input === 'string' ? input.trim() : '';
22
+ if (!s)
23
+ return `plan-${Date.now().toString(36)}`;
24
+ return s.replace(/[^a-zA-Z0-9_-]/g, '-').slice(0, 96);
25
+ }
26
+ function nowIso() {
27
+ return new Date().toISOString();
28
+ }
29
+ function persist(workingDir, plan) {
30
+ const dir = planStore(workingDir);
31
+ if (!existsSync(dir)) {
32
+ mkdirSync(dir, { recursive: true });
33
+ }
34
+ writeFileSync(planPath(workingDir, plan.id), JSON.stringify(plan, null, 2), 'utf-8');
35
+ }
36
+ export function getActivePlan() {
37
+ return ACTIVE_PLAN ? { ...ACTIVE_PLAN, steps: [...ACTIVE_PLAN.steps] } : null;
38
+ }
39
+ export function clearActivePlan() {
40
+ ACTIVE_PLAN = null;
41
+ }
42
+ export function listStoredPlans(workingDir) {
43
+ const dir = planStore(workingDir);
44
+ if (!existsSync(dir))
45
+ return [];
46
+ const out = [];
47
+ for (const name of readdirSync(dir)) {
48
+ if (!name.endsWith('.json'))
49
+ continue;
50
+ try {
51
+ out.push(JSON.parse(readFileSync(join(dir, name), 'utf-8')));
52
+ }
53
+ catch {
54
+ // Skip malformed plan files.
55
+ }
56
+ }
57
+ return out.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
58
+ }
59
+ export function createPlanModeTools(workingDir) {
60
+ return [
61
+ {
62
+ name: 'EnterPlanMode',
63
+ description: 'Open a structured plan for a multi-step task and pause execution ' +
64
+ 'until the user approves it. Pass title + goal + steps[]. The plan ' +
65
+ 'is persisted to .erosolar/plans/ and surfaced in the portal. Use ' +
66
+ 'this for any change spanning multiple files or systems.',
67
+ parameters: {
68
+ type: 'object',
69
+ properties: {
70
+ id: { type: 'string', description: 'Optional stable id (alnum/-/_, ≤96 chars).' },
71
+ title: { type: 'string', description: 'Short title (≤120 chars).' },
72
+ goal: { type: 'string', description: 'One-sentence goal/outcome.' },
73
+ steps: {
74
+ type: 'array',
75
+ description: 'Ordered steps. Each: { title, detail? }.',
76
+ items: {
77
+ type: 'object',
78
+ properties: {
79
+ title: { type: 'string' },
80
+ detail: { type: 'string' },
81
+ },
82
+ required: ['title'],
83
+ },
84
+ },
85
+ },
86
+ required: ['title', 'steps'],
87
+ },
88
+ handler: async (args) => {
89
+ const id = safeId(args['id']);
90
+ const title = String(args['title'] ?? '').trim().slice(0, 120) || 'Untitled plan';
91
+ const goal = typeof args['goal'] === 'string' ? args['goal'].trim() : undefined;
92
+ const rawSteps = Array.isArray(args['steps']) ? args['steps'] : [];
93
+ if (rawSteps.length === 0) {
94
+ return 'EnterPlanMode requires at least one step.';
95
+ }
96
+ const steps = rawSteps.map((raw, idx) => {
97
+ const r = raw;
98
+ return {
99
+ id: `s${idx + 1}`,
100
+ title: typeof r['title'] === 'string' ? r['title'].trim() : `Step ${idx + 1}`,
101
+ detail: typeof r['detail'] === 'string' ? r['detail'] : undefined,
102
+ status: 'pending',
103
+ };
104
+ });
105
+ const plan = {
106
+ id,
107
+ title,
108
+ ...(goal ? { goal } : {}),
109
+ steps,
110
+ status: 'draft',
111
+ createdAt: nowIso(),
112
+ updatedAt: nowIso(),
113
+ };
114
+ ACTIVE_PLAN = plan;
115
+ persist(workingDir, plan);
116
+ return [
117
+ `Plan "${id}" entered. Awaiting user approval before execution.`,
118
+ `Title: ${title}`,
119
+ goal ? `Goal: ${goal}` : null,
120
+ `Steps:`,
121
+ ...steps.map((s, i) => ` ${i + 1}. ${s.title}${s.detail ? ` — ${s.detail}` : ''}`),
122
+ ]
123
+ .filter(Boolean)
124
+ .join('\n');
125
+ },
126
+ },
127
+ {
128
+ name: 'ExitPlanMode',
129
+ description: 'Close the active plan. Pass `outcome: "approved"` once the user has ' +
130
+ 'okayed the plan and the agent is moving to execute, `"completed"` ' +
131
+ 'once all steps are done, or `"abandoned"` if the plan was discarded.',
132
+ parameters: {
133
+ type: 'object',
134
+ properties: {
135
+ outcome: {
136
+ type: 'string',
137
+ enum: ['approved', 'completed', 'abandoned'],
138
+ description: 'Terminal status for the plan.',
139
+ },
140
+ note: { type: 'string', description: 'Optional explanatory note.' },
141
+ },
142
+ required: ['outcome'],
143
+ },
144
+ handler: async (args) => {
145
+ if (!ACTIVE_PLAN) {
146
+ return 'No active plan; nothing to exit.';
147
+ }
148
+ const outcome = String(args['outcome']);
149
+ ACTIVE_PLAN = {
150
+ ...ACTIVE_PLAN,
151
+ status: outcome === 'approved' ? 'approved' : outcome,
152
+ updatedAt: nowIso(),
153
+ };
154
+ persist(workingDir, ACTIVE_PLAN);
155
+ const id = ACTIVE_PLAN.id;
156
+ if (outcome !== 'approved') {
157
+ ACTIVE_PLAN = null;
158
+ }
159
+ const note = typeof args['note'] === 'string' && args['note'].trim() ? ` — ${args['note'].trim()}` : '';
160
+ return `Plan "${id}" exited (${outcome})${note}.`;
161
+ },
162
+ },
163
+ {
164
+ name: 'UpdatePlanStep',
165
+ description: 'Update the status of a single plan step. Use during execution to ' +
166
+ 'mark each step in_progress/completed/skipped as work proceeds.',
167
+ parameters: {
168
+ type: 'object',
169
+ properties: {
170
+ stepId: { type: 'string', description: 'Step id (e.g. "s1") or 1-based index.' },
171
+ status: {
172
+ type: 'string',
173
+ enum: ['pending', 'in_progress', 'completed', 'skipped'],
174
+ },
175
+ },
176
+ required: ['stepId', 'status'],
177
+ },
178
+ handler: async (args) => {
179
+ if (!ACTIVE_PLAN)
180
+ return 'No active plan.';
181
+ const target = String(args['stepId']);
182
+ const status = String(args['status']);
183
+ let idx = ACTIVE_PLAN.steps.findIndex((s) => s.id === target);
184
+ if (idx < 0) {
185
+ const numeric = Number.parseInt(target, 10);
186
+ if (Number.isFinite(numeric))
187
+ idx = numeric - 1;
188
+ }
189
+ if (idx < 0 || idx >= ACTIVE_PLAN.steps.length) {
190
+ return `Step "${target}" not found.`;
191
+ }
192
+ ACTIVE_PLAN.steps[idx].status = status;
193
+ ACTIVE_PLAN.updatedAt = nowIso();
194
+ persist(workingDir, ACTIVE_PLAN);
195
+ return `Step ${idx + 1} (${ACTIVE_PLAN.steps[idx].title}) → ${status}.`;
196
+ },
197
+ },
198
+ ];
199
+ }
200
+ //# sourceMappingURL=planModeTools.js.map