@ariso-ai/ivan 1.0.35 → 1.0.38

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.
@@ -0,0 +1,332 @@
1
+ import readline from 'readline';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ /**
5
+ * Captures additional context the user types while an Ivan command is running,
6
+ * similar to interjecting in an interactive Claude Code session.
7
+ *
8
+ * Input is read in raw mode with the echo rendered by this class on its own
9
+ * input line — a spinner redrawing its line would otherwise wipe the
10
+ * terminal's native echo, making typing invisible. While the user is
11
+ * composing, the active spinner (registered via claudeSpinner) is paused, and
12
+ * resumed when the input is submitted or cancelled.
13
+ *
14
+ * Each submitted line is either delivered immediately to a live listener (the
15
+ * SDK executor streams it into the running session mid-turn) or buffered until
16
+ * the next turn starts (the CLI executor applies it in an automatic follow-up
17
+ * turn).
18
+ */
19
+ export class InterjectionManager {
20
+ static instance = null;
21
+ static getInstance() {
22
+ if (!InterjectionManager.instance) {
23
+ InterjectionManager.instance = new InterjectionManager();
24
+ }
25
+ return InterjectionManager.instance;
26
+ }
27
+ refCount = 0;
28
+ listening = false;
29
+ keypressAttached = false;
30
+ rawModeSet = false;
31
+ buffer = '';
32
+ pending = [];
33
+ liveListener = null;
34
+ hintShown = false;
35
+ activeSpinner = null;
36
+ pausedSpinner = null;
37
+ keypressHandler = (str, key) => this.handleKeypress(str, key);
38
+ selfWrite = false;
39
+ inputLineVisible = false;
40
+ externalMidLine = false;
41
+ unhookStreams = null;
42
+ /** Interjections require an interactive terminal. */
43
+ isAvailable() {
44
+ return Boolean(process.stdin.isTTY);
45
+ }
46
+ /**
47
+ * Begin capturing keystrokes. Ref-counted so overlapping turns can
48
+ * start/stop freely; the listener is only torn down when the outermost
49
+ * stop() is reached.
50
+ */
51
+ start(quiet = false) {
52
+ if (!this.isAvailable())
53
+ return;
54
+ this.refCount++;
55
+ if (this.listening)
56
+ return;
57
+ this.listening = true;
58
+ if (!this.keypressAttached) {
59
+ readline.emitKeypressEvents(process.stdin);
60
+ this.keypressAttached = true;
61
+ }
62
+ if (typeof process.stdin.setRawMode === 'function') {
63
+ process.stdin.setRawMode(true);
64
+ this.rawModeSet = true;
65
+ }
66
+ process.stdin.on('keypress', this.keypressHandler);
67
+ process.stdin.resume();
68
+ this.hookOutputStreams();
69
+ if (!this.hintShown && !quiet) {
70
+ console.log(chalk.cyan('💬 Type a message and press Enter at any time to give Ivan additional context'));
71
+ this.hintShown = true;
72
+ }
73
+ }
74
+ /** Stop capturing keystrokes once all active turns have finished. */
75
+ stop() {
76
+ if (!this.isAvailable())
77
+ return;
78
+ if (this.refCount > 0)
79
+ this.refCount--;
80
+ if (this.refCount === 0 && this.listening) {
81
+ this.listening = false;
82
+ process.stdin.removeListener('keypress', this.keypressHandler);
83
+ if (this.rawModeSet) {
84
+ process.stdin.setRawMode(false);
85
+ this.rawModeSet = false;
86
+ }
87
+ // Release stdin so the process can exit and later prompts (inquirer)
88
+ // get a clean stream.
89
+ process.stdin.pause();
90
+ // Abandon any partially typed input and restore the spinner.
91
+ if (this.buffer) {
92
+ this.buffer = '';
93
+ this.clearInputLine();
94
+ }
95
+ this.unhookStreams?.();
96
+ this.unhookStreams = null;
97
+ this.resumeSpinner();
98
+ }
99
+ }
100
+ /**
101
+ * While the user is composing, the `💬 > ` echo owns the cursor row with no
102
+ * trailing newline — anything the executors print (tool-usage lines,
103
+ * streamed CLI output) would land right after the typed text and shove the
104
+ * echo onto a new line. Intercept stdout/stderr so external output clears
105
+ * the echo, prints on its own line, and the echo is redrawn beneath it.
106
+ */
107
+ hookOutputStreams() {
108
+ if (this.unhookStreams)
109
+ return;
110
+ const hook = (stream) => {
111
+ const original = stream.write.bind(stream);
112
+ const manager = this;
113
+ stream.write = function (chunk, encoding, callback) {
114
+ if (manager.selfWrite || manager.buffer.length === 0) {
115
+ return original(chunk, encoding, callback);
116
+ }
117
+ if (manager.inputLineVisible) {
118
+ manager.selfWrite = true;
119
+ original('\r\x1b[2K');
120
+ manager.selfWrite = false;
121
+ manager.inputLineVisible = false;
122
+ }
123
+ const result = original(chunk, encoding, callback);
124
+ const text = typeof chunk === 'string'
125
+ ? chunk
126
+ : globalThis.Buffer.from(chunk).toString('utf8');
127
+ if (text.length > 0) {
128
+ manager.externalMidLine = !text.endsWith('\n');
129
+ if (!manager.externalMidLine)
130
+ manager.render();
131
+ }
132
+ return result;
133
+ };
134
+ return () => {
135
+ stream.write = original;
136
+ };
137
+ };
138
+ const unhookStdout = hook(process.stdout);
139
+ const unhookStderr = hook(process.stderr);
140
+ this.unhookStreams = () => {
141
+ unhookStdout();
142
+ unhookStderr();
143
+ };
144
+ }
145
+ /**
146
+ * Registers the spinner currently rendering so it can be paused while the
147
+ * user types (its line redraws would erase the input echo otherwise).
148
+ */
149
+ attachSpinner(spinner) {
150
+ this.activeSpinner = spinner;
151
+ }
152
+ detachSpinner(spinner) {
153
+ if (this.activeSpinner === spinner)
154
+ this.activeSpinner = null;
155
+ if (this.pausedSpinner === spinner)
156
+ this.pausedSpinner = null;
157
+ }
158
+ /**
159
+ * Register a handler that receives interjections the moment they are typed
160
+ * (used by the SDK executor to stream them into the running session).
161
+ * Returns a release function; after release, new input buffers again.
162
+ */
163
+ setLiveListener(listener) {
164
+ this.liveListener = listener;
165
+ return () => {
166
+ if (this.liveListener === listener)
167
+ this.liveListener = null;
168
+ };
169
+ }
170
+ /** Returns and clears any buffered interjections. */
171
+ drainPending() {
172
+ return this.pending.splice(0);
173
+ }
174
+ /**
175
+ * Puts interjections back at the front of the buffer — used when a turn
176
+ * ends before delivering input it had already accepted.
177
+ */
178
+ requeue(texts) {
179
+ if (texts.length > 0)
180
+ this.pending.unshift(...texts);
181
+ }
182
+ hasPending() {
183
+ return this.pending.length > 0;
184
+ }
185
+ handleKeypress(str, key = {}) {
186
+ if (key.ctrl && key.name === 'c') {
187
+ // Raw mode suppresses the terminal's own SIGINT; re-emit it so the
188
+ // executors' Ctrl+C handlers still fire.
189
+ if (this.rawModeSet)
190
+ process.kill(process.pid, 'SIGINT');
191
+ return;
192
+ }
193
+ if (key.name === 'return' || key.name === 'enter') {
194
+ this.submit();
195
+ return;
196
+ }
197
+ if (key.name === 'backspace') {
198
+ if (this.buffer) {
199
+ this.buffer = this.buffer.slice(0, -1);
200
+ this.render();
201
+ }
202
+ return;
203
+ }
204
+ if (key.ctrl && key.name === 'u') {
205
+ this.buffer = '';
206
+ this.render();
207
+ return;
208
+ }
209
+ if (key.name === 'escape') {
210
+ this.buffer = '';
211
+ this.clearInputLine();
212
+ this.resumeSpinner();
213
+ return;
214
+ }
215
+ if (key.ctrl || key.meta)
216
+ return;
217
+ // Printable input only (pasted text arrives as a multi-char str).
218
+ if (!str)
219
+ return;
220
+ const printable = Array.from(str)
221
+ .filter((ch) => {
222
+ const code = ch.codePointAt(0) ?? 0;
223
+ return code >= 0x20 && code !== 0x7f;
224
+ })
225
+ .join('');
226
+ if (!printable)
227
+ return;
228
+ if (this.buffer.length === 0)
229
+ this.pauseSpinner();
230
+ this.buffer += printable;
231
+ this.render();
232
+ }
233
+ submit() {
234
+ const text = this.buffer.trim();
235
+ this.buffer = '';
236
+ this.clearInputLine();
237
+ if (text) {
238
+ if (this.liveListener) {
239
+ this.liveListener(text);
240
+ console.log(chalk.cyan('💬 Context sent to Claude — it will be incorporated into the current task'));
241
+ }
242
+ else {
243
+ this.pending.push(text);
244
+ console.log(chalk.cyan('💬 Context queued — Ivan will include it in the next turn'));
245
+ }
246
+ }
247
+ this.resumeSpinner();
248
+ }
249
+ /**
250
+ * Redraws the input line. The spinner is paused while composing, so this
251
+ * line owns the cursor row; hooked stdout/stderr writes clear and redraw it
252
+ * around any external output so the echo always sits on the bottom line.
253
+ */
254
+ render() {
255
+ if (!this.listening)
256
+ return;
257
+ this.selfWrite = true;
258
+ // If external output left the cursor mid-line, move past it instead of
259
+ // overwriting the partial line.
260
+ if (this.externalMidLine) {
261
+ process.stderr.write('\n');
262
+ this.externalMidLine = false;
263
+ }
264
+ process.stderr.write('\r\x1b[2K' + chalk.cyan('💬 > ') + this.buffer);
265
+ this.selfWrite = false;
266
+ this.inputLineVisible = true;
267
+ }
268
+ clearInputLine() {
269
+ this.selfWrite = true;
270
+ process.stderr.write('\r\x1b[2K');
271
+ this.selfWrite = false;
272
+ this.inputLineVisible = false;
273
+ }
274
+ pauseSpinner() {
275
+ const spinner = this.activeSpinner;
276
+ if (spinner && spinner.isSpinning) {
277
+ this.pausedSpinner = spinner;
278
+ spinner.stop();
279
+ }
280
+ }
281
+ resumeSpinner() {
282
+ // Only restart a spinner we paused ourselves that is still the active
283
+ // one — if the executor finished it (succeed/fail) while the user was
284
+ // typing, detachSpinner has already cleared it.
285
+ if (this.pausedSpinner && this.pausedSpinner === this.activeSpinner) {
286
+ this.pausedSpinner.start();
287
+ }
288
+ this.pausedSpinner = null;
289
+ }
290
+ }
291
+ /**
292
+ * Creates a spinner for phases where a Claude turn runs and interjections are
293
+ * being captured: stdin stays readable (no discardStdin) and the spinner is
294
+ * registered with the InterjectionManager so it pauses while the user types.
295
+ * Finishing the spinner (succeed/fail/warn/info/stopAndPersist) detaches it;
296
+ * plain stop() is left unwrapped because the manager itself uses it to pause.
297
+ */
298
+ export function claudeSpinner(text) {
299
+ const spinner = ora({ text, discardStdin: false });
300
+ const manager = InterjectionManager.getInstance();
301
+ manager.attachSpinner(spinner);
302
+ const finish = (fn) => (...args) => {
303
+ manager.detachSpinner(spinner);
304
+ return fn(...args);
305
+ };
306
+ spinner.succeed = finish(spinner.succeed.bind(spinner));
307
+ spinner.fail = finish(spinner.fail.bind(spinner));
308
+ spinner.warn = finish(spinner.warn.bind(spinner));
309
+ spinner.info = finish(spinner.info.bind(spinner));
310
+ spinner.stopAndPersist = finish(spinner.stopAndPersist.bind(spinner));
311
+ return spinner;
312
+ }
313
+ /**
314
+ * Appends buffered interjections to the initial prompt of the next turn.
315
+ */
316
+ export function appendInterjections(prompt, texts) {
317
+ if (texts.length === 0)
318
+ return prompt;
319
+ return `${prompt}\n\nAdditional context from the user (provided while Ivan was running — treat it as part of the task):\n${texts
320
+ .map((t) => `- ${t}`)
321
+ .join('\n')}`;
322
+ }
323
+ /**
324
+ * Formats interjections delivered as their own message — either streamed into
325
+ * a running SDK session or sent as a CLI follow-up turn.
326
+ */
327
+ export function interjectionMessage(texts) {
328
+ return `The user interjected with additional context while you were working:\n${texts
329
+ .map((t) => `- ${t}`)
330
+ .join('\n')}\nIncorporate this guidance into the task you are working on, adjusting anything you have already done if needed.`;
331
+ }
332
+ //# sourceMappingURL=interjection-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interjection-manager.js","sourceRoot":"","sources":["../../src/services/interjection-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export declare class RiskAnalysisExecutor {
2
+ private claudeExecutor;
3
+ private repositoryManager;
4
+ constructor();
5
+ executeRiskAnalysis(changes: string[]): Promise<void>;
6
+ private synthesize;
7
+ }
8
+ //# sourceMappingURL=risk-analysis-executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"risk-analysis-executor.d.ts","sourceRoot":"","sources":["../../src/services/risk-analysis-executor.ts"],"names":[],"mappings":"AAmGA,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,iBAAiB,CAAqB;;IAOxC,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAyE7C,UAAU;CAqCzB"}
@@ -0,0 +1,178 @@
1
+ import chalk from 'chalk';
2
+ import { ExecutorFactory } from './executor-factory.js';
3
+ import { createRepositoryManager } from './service-factory.js';
4
+ import { claudeSpinner } from './interjection-manager.js';
5
+ const RISK_ANALYSIS_SYSTEM_PROMPT = `You are a principal engineer performing a pre-deployment risk analysis of a set of proposed changes.
6
+
7
+ Your job is NOT to review code style or suggest improvements — it is to identify what could go wrong when these changes ship, how likely that is, how bad the blast radius would be, and what mitigations should be in place before rollout.
8
+
9
+ For every risk you identify:
10
+ - State the specific failure mode (what breaks, for whom, and when)
11
+ - Rate its likelihood (low / medium / high) and impact (low / medium / high / critical)
12
+ - Note whether it is roll-backable, and what the rollback path is
13
+ - Recommend a concrete mitigation (guard, feature flag, deploy ordering, backup, monitoring, etc.)
14
+
15
+ Ground your analysis in the actual repository: use your tools to read the code, migrations, and configuration that the described changes would touch. Do not speculate about code you have not looked at — if a change references something you cannot find in the repo, say so explicitly rather than guessing.
16
+
17
+ Be direct and specific. If an area carries no meaningful risk, say so briefly and move on.`;
18
+ const RISK_DIMENSIONS = [
19
+ {
20
+ key: 'db-migration',
21
+ label: 'Analyze DB migration risk',
22
+ prompt: `Analyze the DATABASE MIGRATION risk of the proposed changes.
23
+
24
+ Focus on:
25
+ - Schema changes: dropped/renamed columns or tables, type changes, and whether old code running during the deploy window would break against the new schema (and vice versa)
26
+ - Destructive or irreversible operations: data loss potential, and whether a down-migration actually restores state
27
+ - Locking and downtime: long-running ALTERs, index builds on large tables (are indexes created concurrently?), table rewrites, migrations that block reads or writes
28
+ - Backfills and data transformations: volume, batching, idempotency if interrupted midway
29
+ - Deploy ordering: does the migration need to run before or after the code deploy, and what happens if that ordering is violated
30
+ - Rollback: can the migration be reverted safely once new data has been written
31
+
32
+ If the changes involve no database migrations, state that clearly and rate this dimension as no risk.`
33
+ },
34
+ {
35
+ key: 'infra-security',
36
+ label: 'Analyze infra/security risk',
37
+ prompt: `Analyze the INFRASTRUCTURE and SECURITY risk of the proposed changes.
38
+
39
+ Focus on:
40
+ - Security: new or widened endpoint exposure, authentication/authorization changes, secrets or credentials in code or config, injection surfaces, permission/IAM scope changes, sensitive data logging or exposure
41
+ - Dependencies: new or upgraded packages, known-vulnerable versions, supply-chain surface
42
+ - Infrastructure/config: environment variable changes, CI/CD workflow changes, networking or firewall implications, resource limits, quota or cost blowups
43
+ - Availability: single points of failure introduced, changes to health checks, restarts or reprovisioning required by the rollout
44
+ - Blast radius: if this change misbehaves, what else does it take down
45
+
46
+ If the changes touch no infrastructure or security surface, state that clearly and rate this dimension as no risk.`
47
+ },
48
+ {
49
+ key: 'backend-service',
50
+ label: 'Analyze backend service risk',
51
+ prompt: `Analyze the BACKEND SERVICE risk of the proposed changes.
52
+
53
+ Focus on:
54
+ - API contract changes: breaking request/response shape changes, removed or renamed fields, status code changes, and impact on existing clients
55
+ - Backwards compatibility: can old and new versions of the service run side by side during rollout
56
+ - Error handling: new failure paths, unhandled promise rejections or exceptions, retry storms, timeout behavior
57
+ - Performance: N+1 queries, unbounded loops or payloads, memory growth, hot-path latency regressions
58
+ - State and concurrency: race conditions, transactional integrity, idempotency of mutating endpoints
59
+ - Third-party integrations: changed assumptions about external services, rate limits, degraded-mode behavior
60
+
61
+ If the changes do not touch backend service code, state that clearly and rate this dimension as no risk.`
62
+ },
63
+ {
64
+ key: 'worker-frontend',
65
+ label: 'Analyze worker/frontend risk',
66
+ prompt: `Analyze the WORKER (background job) and FRONTEND risk of the proposed changes.
67
+
68
+ For workers/background jobs, focus on:
69
+ - Queue and job semantics: changed job payloads breaking in-flight jobs, retry and dead-letter behavior, idempotency of re-run jobs
70
+ - Scheduling: cron or interval changes, overlap of long-running jobs, thundering-herd effects
71
+ - Failure isolation: does a failing job poison the queue or block other work
72
+
73
+ For frontend/client code, focus on:
74
+ - Client-server compatibility: cached/stale frontend bundles calling changed APIs during and after deploy
75
+ - Breaking UX flows: removed or renamed routes, changed form contracts, state management regressions
76
+ - Caching: CDN/browser cache invalidation, localStorage/session schema changes breaking returning users
77
+ - Error surfaces: unhandled rejections that blank the page vs. degrade gracefully
78
+
79
+ If the changes touch neither workers nor frontend code, state that clearly and rate this dimension as no risk.`
80
+ }
81
+ ];
82
+ function buildChangesSection(changes) {
83
+ const list = changes.map((c, i) => `${i + 1}. ${c}`).join('\n');
84
+ return `## Proposed changes under analysis
85
+
86
+ ${list}`;
87
+ }
88
+ export class RiskAnalysisExecutor {
89
+ claudeExecutor;
90
+ repositoryManager;
91
+ constructor() {
92
+ this.claudeExecutor = ExecutorFactory.getExecutor();
93
+ this.repositoryManager = createRepositoryManager();
94
+ }
95
+ async executeRiskAnalysis(changes) {
96
+ try {
97
+ await this.claudeExecutor.validateClaudeCodeInstallation();
98
+ console.log(chalk.green('✅ Claude Code configured'));
99
+ const workingDir = await this.repositoryManager.getValidWorkingDirectory();
100
+ const repoInfo = this.repositoryManager.getRepositoryInfo(workingDir);
101
+ console.log(chalk.blue(`📂 Working in: ${repoInfo.name}`));
102
+ console.log('');
103
+ console.log(chalk.blue.bold('🛡️ Running risk analysis'));
104
+ for (const [i, change] of changes.entries()) {
105
+ console.log(chalk.gray(` ${i + 1}. ${change}`));
106
+ }
107
+ console.log('');
108
+ // Suppress the executor's per-turn logging so the tree output stays readable
109
+ this.claudeExecutor.quietMode = true;
110
+ const changesSection = buildChangesSection(changes);
111
+ const dimensionResults = [];
112
+ for (const [i, dimension] of RISK_DIMENSIONS.entries()) {
113
+ const branch = i === RISK_DIMENSIONS.length - 1 ? '└' : '├';
114
+ const spinner = claudeSpinner(`${branch} ${dimension.label}`).start();
115
+ try {
116
+ const result = await this.claudeExecutor.executeTurn(`${changesSection}\n\n${dimension.prompt}`, workingDir, {
117
+ systemPrompt: RISK_ANALYSIS_SYSTEM_PROMPT,
118
+ readOnly: true,
119
+ permissionMode: 'plan'
120
+ });
121
+ dimensionResults.push({
122
+ label: dimension.label,
123
+ analysis: result.lastMessage
124
+ });
125
+ spinner.succeed(`${branch} ${dimension.label}`);
126
+ }
127
+ catch (err) {
128
+ spinner.fail(`${branch} ${dimension.label}`);
129
+ throw err;
130
+ }
131
+ }
132
+ const synthesisSpinner = claudeSpinner('Synthesizing final risk analysis').start();
133
+ let finalReport;
134
+ try {
135
+ finalReport = await this.synthesize(changes, dimensionResults, workingDir);
136
+ synthesisSpinner.succeed('Final risk analysis ready');
137
+ }
138
+ catch (err) {
139
+ synthesisSpinner.fail('Failed to synthesize final risk analysis');
140
+ throw err;
141
+ }
142
+ console.log('');
143
+ console.log(chalk.blue.bold('📋 Final Risk Analysis'));
144
+ console.log(chalk.gray('─'.repeat(60)));
145
+ console.log(finalReport);
146
+ }
147
+ finally {
148
+ this.repositoryManager.close();
149
+ }
150
+ }
151
+ async synthesize(changes, dimensionResults, workingDir) {
152
+ const findings = dimensionResults
153
+ .map((r) => `### ${r.label}\n\n${r.analysis}`)
154
+ .join('\n\n');
155
+ const synthesisPrompt = `${buildChangesSection(changes)}
156
+
157
+ Four specialist risk analyses have already been performed against these changes. Their findings are below.
158
+
159
+ ${findings}
160
+
161
+ Synthesize these into ONE final risk analysis report. Do not re-investigate the code — work from the findings above. Structure the report as:
162
+
163
+ 1. **Overall risk rating** — one of: NONE / LOW / MEDIUM / HIGH / CRITICAL, with a one-sentence justification
164
+ 2. **Risk summary by area** — a table with columns: Area (DB migration, Infra/security, Backend service, Worker/frontend), Risk level, Key concern
165
+ 3. **Top risks** — the most important risks across all areas, ordered by severity, each with its failure mode, likelihood/impact, and mitigation
166
+ 4. **Required mitigations before rollout** — a concrete pre-deploy checklist derived from the findings
167
+ 5. **Rollback plan** — how to revert if things go wrong, noting anything that is NOT cleanly reversible
168
+
169
+ Deduplicate risks that multiple analyses flagged, and drop dimensions that reported no risk (mention them only in the summary table). Your final message must be the complete report and nothing else.`;
170
+ const result = await this.claudeExecutor.executeTurn(synthesisPrompt, workingDir, {
171
+ systemPrompt: RISK_ANALYSIS_SYSTEM_PROMPT,
172
+ readOnly: true,
173
+ permissionMode: 'plan'
174
+ });
175
+ return result.lastMessage;
176
+ }
177
+ }
178
+ //# sourceMappingURL=risk-analysis-executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"risk-analysis-executor.js","sourceRoot":"","sources":["../../src/services/risk-analysis-executor.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"task-executor.d.ts","sourceRoot":"","sources":["../../src/services/task-executor.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAa/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AAOxE,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,cAAc,CAAgC;IACtD,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,iBAAiB,CAAU;;IAanC;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAmBvB;;;;OAIG;YACW,iBAAiB;IA8B/B,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,gBAAgB;IAOxB;;;;OAIG;YACW,iBAAiB;IAgI/B;;;;OAIG;YACW,aAAa;IA2DrB,eAAe,CACnB,aAAa,GAAE,OAAe,EAC9B,UAAU,CAAC,EAAE,MAAM,EACnB,IAAI,GAAE,aAAwB,EAC9B,UAAU,GAAE,OAAe,EAC3B,cAAc,CAAC,EAAE,MAAM,EAAE,GACxB,OAAO,CAAC,IAAI,CAAC;YAyRF,0BAA0B;YA6D1B,WAAW;YAuMX,wBAAwB;IAuNhC,6BAA6B,CACjC,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC;CAsJjB"}
1
+ {"version":3,"file":"task-executor.d.ts","sourceRoot":"","sources":["../../src/services/task-executor.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAc/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AAOxE,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,cAAc,CAAgC;IACtD,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,iBAAiB,CAAU;;IAanC;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAmBvB;;;;OAIG;YACW,iBAAiB;IA8B/B,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,gBAAgB;IAOxB;;;;OAIG;YACW,iBAAiB;IAkI/B;;;;OAIG;YACW,aAAa;IA2DrB,eAAe,CACnB,aAAa,GAAE,OAAe,EAC9B,UAAU,CAAC,EAAE,MAAM,EACnB,IAAI,GAAE,aAAwB,EAC9B,UAAU,GAAE,OAAe,EAC3B,cAAc,CAAC,EAAE,MAAM,EAAE,GACxB,OAAO,CAAC,IAAI,CAAC;YAyRF,0BAA0B;YA6D1B,WAAW;YAwMX,wBAAwB;IAwNhC,6BAA6B,CACjC,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC;CAsJjB"}
@@ -7,6 +7,7 @@ import { ConfigManager } from '../config.js';
7
7
  import { AddressTaskExecutor } from './address-task-executor.js';
8
8
  import { createGitManager, createPRService, createRepositoryManager } from './service-factory.js';
9
9
  import { PromptRewriter } from './prompt-rewriter.js';
10
+ import { claudeSpinner } from './interjection-manager.js';
10
11
  import { CollaborativeExecutor } from './collaborative-executor.js';
11
12
  const SELF_REVIEW_PROMPT = "ok review the changes in the current branch carefully. make sure it follows repo convention, reusable component/utils/etc. don't over-engineer, dont reinvent.";
12
13
  export class TaskExecutor {
@@ -144,7 +145,7 @@ export class TaskExecutor {
144
145
  // Prepare prompt for Claude to fix the errors
145
146
  const fixPrompt = `Fix the following pre-commit hook errors:\n\n${errorDetails}\n\nPlease fix all TypeScript errors, linting issues, and any other problems preventing the commit.`;
146
147
  if (!quiet)
147
- spinner = ora('Running Claude to fix pre-commit errors...').start();
148
+ spinner = claudeSpinner('Running Claude to fix pre-commit errors...').start();
148
149
  try {
149
150
  // Run Claude to fix the errors (pass session ID to maintain context)
150
151
  const fixResult = await this.getClaudeExecutor().executeTask(fixPrompt, executionPath, sessionId);
@@ -186,7 +187,7 @@ export class TaskExecutor {
186
187
  async runSelfReview(executionPath, taskUuid, quiet, sessionId) {
187
188
  let spinner = quiet
188
189
  ? null
189
- : ora('Running self-review with Claude Code...').start();
190
+ : claudeSpinner('Running self-review with Claude Code...').start();
190
191
  const reviewResult = await this.getClaudeExecutor().executeTask(SELF_REVIEW_PROMPT, executionPath, sessionId);
191
192
  sessionId = reviewResult.sessionId;
192
193
  if (spinner)
@@ -482,7 +483,7 @@ export class TaskExecutor {
482
483
  spinner.succeed(`Worktree created: ${worktreePath}`);
483
484
  await this.jobManager.updateTaskBranch(task.uuid, branchName);
484
485
  if (!quiet)
485
- spinner = ora('Executing task with Claude Code...').start();
486
+ spinner = claudeSpinner('Executing task with Claude Code...').start();
486
487
  // Append repository-specific instructions to the task if they exist
487
488
  let taskWithInstructions = task.description;
488
489
  if (this.repoInstructions) {
@@ -649,7 +650,7 @@ export class TaskExecutor {
649
650
  if (spinner)
650
651
  spinner.succeed('Task marked as active');
651
652
  if (!quiet)
652
- spinner = ora('Executing task with Claude Code...').start();
653
+ spinner = claudeSpinner('Executing task with Claude Code...').start();
653
654
  // Append repository-specific instructions to the task if they exist
654
655
  let taskWithInstructions = task.description;
655
656
  if (this.repoInstructions) {