@manishbht/helpcode 0.2.2
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/LICENSE +21 -0
- package/README.md +186 -0
- package/dist/bin/helpcode.d.ts +5 -0
- package/dist/bin/helpcode.js +10 -0
- package/dist/bin/helpcode.js.map +1 -0
- package/dist/src/commands/apply.d.ts +15 -0
- package/dist/src/commands/apply.js +195 -0
- package/dist/src/commands/apply.js.map +1 -0
- package/dist/src/commands/ask.d.ts +12 -0
- package/dist/src/commands/ask.js +93 -0
- package/dist/src/commands/ask.js.map +1 -0
- package/dist/src/commands/init.d.ts +13 -0
- package/dist/src/commands/init.js +91 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/src/commands/reset.d.ts +8 -0
- package/dist/src/commands/reset.js +19 -0
- package/dist/src/commands/reset.js.map +1 -0
- package/dist/src/commands/run.d.ts +15 -0
- package/dist/src/commands/run.js +109 -0
- package/dist/src/commands/run.js.map +1 -0
- package/dist/src/commands/status.d.ts +4 -0
- package/dist/src/commands/status.js +53 -0
- package/dist/src/commands/status.js.map +1 -0
- package/dist/src/core/llmSelector.d.ts +65 -0
- package/dist/src/core/llmSelector.js +134 -0
- package/dist/src/core/llmSelector.js.map +1 -0
- package/dist/src/core/ollama.d.ts +43 -0
- package/dist/src/core/ollama.js +128 -0
- package/dist/src/core/ollama.js.map +1 -0
- package/dist/src/core/parser.d.ts +78 -0
- package/dist/src/core/parser.js +273 -0
- package/dist/src/core/parser.js.map +1 -0
- package/dist/src/core/patcher.d.ts +31 -0
- package/dist/src/core/patcher.js +128 -0
- package/dist/src/core/patcher.js.map +1 -0
- package/dist/src/core/project.d.ts +26 -0
- package/dist/src/core/project.js +199 -0
- package/dist/src/core/project.js.map +1 -0
- package/dist/src/core/prompt.d.ts +19 -0
- package/dist/src/core/prompt.js +121 -0
- package/dist/src/core/prompt.js.map +1 -0
- package/dist/src/core/selector.d.ts +46 -0
- package/dist/src/core/selector.js +193 -0
- package/dist/src/core/selector.js.map +1 -0
- package/dist/src/core/state.d.ts +11 -0
- package/dist/src/core/state.js +63 -0
- package/dist/src/core/state.js.map +1 -0
- package/dist/src/core/tools.d.ts +32 -0
- package/dist/src/core/tools.js +67 -0
- package/dist/src/core/tools.js.map +1 -0
- package/dist/src/core/triage.d.ts +37 -0
- package/dist/src/core/triage.js +69 -0
- package/dist/src/core/triage.js.map +1 -0
- package/dist/src/index.d.ts +12 -0
- package/dist/src/index.js +120 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lib/compress.d.ts +14 -0
- package/dist/src/lib/compress.js +35 -0
- package/dist/src/lib/compress.js.map +1 -0
- package/dist/src/lib/errors.d.ts +25 -0
- package/dist/src/lib/errors.js +32 -0
- package/dist/src/lib/errors.js.map +1 -0
- package/dist/src/lib/git.d.ts +7 -0
- package/dist/src/lib/git.js +45 -0
- package/dist/src/lib/git.js.map +1 -0
- package/dist/src/lib/runclass.d.ts +24 -0
- package/dist/src/lib/runclass.js +66 -0
- package/dist/src/lib/runclass.js.map +1 -0
- package/dist/src/lib/ui.d.ts +41 -0
- package/dist/src/lib/ui.js +92 -0
- package/dist/src/lib/ui.js.map +1 -0
- package/dist/src/types.d.ts +70 -0
- package/dist/src/types.js +7 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent agent state. Stored at .helpcode/state.json.
|
|
3
|
+
*
|
|
4
|
+
* State is intentionally readable JSON. Users can `cat` it to see exactly
|
|
5
|
+
* what helpcode is thinking. No black-box memory.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import { HelpcodeError, ErrorCode } from '../lib/errors.js';
|
|
10
|
+
const STATE_DIR = '.helpcode';
|
|
11
|
+
const STATE_FILE = path.join(STATE_DIR, 'state.json');
|
|
12
|
+
function emptyState() {
|
|
13
|
+
return {
|
|
14
|
+
version: 1,
|
|
15
|
+
currentTask: null,
|
|
16
|
+
history: [],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export function loadState(cwd = process.cwd()) {
|
|
20
|
+
const file = path.join(cwd, STATE_FILE);
|
|
21
|
+
if (!fs.existsSync(file)) {
|
|
22
|
+
return emptyState();
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const parsed = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
26
|
+
if (parsed.version !== 1) {
|
|
27
|
+
throw new HelpcodeError(ErrorCode.STATE_ERROR, `Unknown state version: ${parsed.version}`, 'This state was written by a different helpcode version. Run `helpcode reset` to start fresh.');
|
|
28
|
+
}
|
|
29
|
+
return parsed;
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
if (e instanceof HelpcodeError)
|
|
33
|
+
throw e;
|
|
34
|
+
throw new HelpcodeError(ErrorCode.STATE_ERROR, `Could not parse state file: ${e.message}`, 'Run `helpcode reset` to start fresh (this won\'t touch your code).');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function saveState(state, cwd = process.cwd()) {
|
|
38
|
+
const dir = path.join(cwd, STATE_DIR);
|
|
39
|
+
if (!fs.existsSync(dir))
|
|
40
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
41
|
+
const file = path.join(cwd, STATE_FILE);
|
|
42
|
+
const tmp = `${file}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
43
|
+
fs.writeFileSync(tmp, JSON.stringify(state, null, 2), 'utf-8');
|
|
44
|
+
fs.renameSync(tmp, file);
|
|
45
|
+
}
|
|
46
|
+
export function createTask(description) {
|
|
47
|
+
const now = new Date().toISOString();
|
|
48
|
+
return {
|
|
49
|
+
id: `task-${now.replace(/[:.]/g, '-')}`,
|
|
50
|
+
description,
|
|
51
|
+
createdAt: now,
|
|
52
|
+
iterations: 0,
|
|
53
|
+
status: 'idle',
|
|
54
|
+
lastPrompt: null,
|
|
55
|
+
lastResponseRaw: null,
|
|
56
|
+
lastTestOutput: null,
|
|
57
|
+
lastDiffsApplied: [],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export function resetState(cwd = process.cwd()) {
|
|
61
|
+
saveState(emptyState(), cwd);
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/core/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool runner. Spawns subprocesses and captures their output in a
|
|
3
|
+
* helpcode-friendly form.
|
|
4
|
+
*
|
|
5
|
+
* - Cross-platform (uses spawn directly, no shell unless asked)
|
|
6
|
+
* - Timeout enforced
|
|
7
|
+
* - Returns structured result so callers can decide what to display
|
|
8
|
+
*/
|
|
9
|
+
export interface CommandResult {
|
|
10
|
+
exitCode: number;
|
|
11
|
+
stdout: string;
|
|
12
|
+
stderr: string;
|
|
13
|
+
durationMs: number;
|
|
14
|
+
timedOut: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface RunOptions {
|
|
17
|
+
/** Timeout in seconds. Defaults to 60. */
|
|
18
|
+
timeoutSecs?: number;
|
|
19
|
+
/** Working directory. Defaults to process.cwd(). */
|
|
20
|
+
cwd?: string;
|
|
21
|
+
/** Environment variables to merge into the subprocess env. */
|
|
22
|
+
env?: NodeJS.ProcessEnv;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Run a shell command (parsed by the OS shell) and return its result.
|
|
26
|
+
* On Windows this uses cmd.exe, on POSIX it uses bash via `/bin/sh -c`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function runShellCommand(command: string, opts?: RunOptions): Promise<CommandResult>;
|
|
29
|
+
/**
|
|
30
|
+
* Run a process by argv (no shell). Use this when you control the args.
|
|
31
|
+
*/
|
|
32
|
+
export declare function runProcess(cmd: string, args: string[], opts?: RunOptions): Promise<CommandResult>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool runner. Spawns subprocesses and captures their output in a
|
|
3
|
+
* helpcode-friendly form.
|
|
4
|
+
*
|
|
5
|
+
* - Cross-platform (uses spawn directly, no shell unless asked)
|
|
6
|
+
* - Timeout enforced
|
|
7
|
+
* - Returns structured result so callers can decide what to display
|
|
8
|
+
*/
|
|
9
|
+
import { spawn } from 'child_process';
|
|
10
|
+
/**
|
|
11
|
+
* Run a shell command (parsed by the OS shell) and return its result.
|
|
12
|
+
* On Windows this uses cmd.exe, on POSIX it uses bash via `/bin/sh -c`.
|
|
13
|
+
*/
|
|
14
|
+
export async function runShellCommand(command, opts = {}) {
|
|
15
|
+
const isWindows = process.platform === 'win32';
|
|
16
|
+
const shell = isWindows ? 'cmd.exe' : '/bin/sh';
|
|
17
|
+
const shellFlag = isWindows ? '/c' : '-c';
|
|
18
|
+
return runProcess(shell, [shellFlag, command], opts);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Run a process by argv (no shell). Use this when you control the args.
|
|
22
|
+
*/
|
|
23
|
+
export async function runProcess(cmd, args, opts = {}) {
|
|
24
|
+
const timeoutMs = (opts.timeoutSecs ?? 60) * 1000;
|
|
25
|
+
const start = Date.now();
|
|
26
|
+
return new Promise(resolve => {
|
|
27
|
+
const child = spawn(cmd, args, {
|
|
28
|
+
cwd: opts.cwd ?? process.cwd(),
|
|
29
|
+
env: { ...process.env, ...opts.env },
|
|
30
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
31
|
+
shell: false,
|
|
32
|
+
});
|
|
33
|
+
const stdoutChunks = [];
|
|
34
|
+
const stderrChunks = [];
|
|
35
|
+
let timedOut = false;
|
|
36
|
+
const timer = setTimeout(() => {
|
|
37
|
+
timedOut = true;
|
|
38
|
+
try {
|
|
39
|
+
child.kill('SIGKILL');
|
|
40
|
+
}
|
|
41
|
+
catch { /* ignore */ }
|
|
42
|
+
}, timeoutMs);
|
|
43
|
+
child.stdout?.on('data', (d) => stdoutChunks.push(d));
|
|
44
|
+
child.stderr?.on('data', (d) => stderrChunks.push(d));
|
|
45
|
+
child.on('error', err => {
|
|
46
|
+
clearTimeout(timer);
|
|
47
|
+
resolve({
|
|
48
|
+
exitCode: 127,
|
|
49
|
+
stdout: '',
|
|
50
|
+
stderr: `Failed to start: ${err.message}`,
|
|
51
|
+
durationMs: Date.now() - start,
|
|
52
|
+
timedOut: false,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
child.on('close', exitCode => {
|
|
56
|
+
clearTimeout(timer);
|
|
57
|
+
resolve({
|
|
58
|
+
exitCode: timedOut ? 124 : (exitCode ?? 0),
|
|
59
|
+
stdout: Buffer.concat(stdoutChunks).toString('utf-8'),
|
|
60
|
+
stderr: Buffer.concat(stderrChunks).toString('utf-8'),
|
|
61
|
+
durationMs: Date.now() - start,
|
|
62
|
+
timedOut,
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../../src/core/tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output triage (v0.2.x) — the second local-LLM task after file selection.
|
|
3
|
+
*
|
|
4
|
+
* Long command output (a 200-line pytest dump, a webpack error wall) bloats
|
|
5
|
+
* the next Claude brief and buries the signal. helpcode already truncates
|
|
6
|
+
* deterministically (head + tail). When Ollama is available, we can do
|
|
7
|
+
* better: ask a local model to extract the key failure(s) so the brief stays
|
|
8
|
+
* tight and on-point.
|
|
9
|
+
*
|
|
10
|
+
* Same contract as the selector: the LLM is a free upgrade. If Ollama is
|
|
11
|
+
* disabled, unreachable, slow, errors, or returns nothing useful, we fall
|
|
12
|
+
* back to the existing deterministic truncation. Triage NEVER loses the
|
|
13
|
+
* output entirely — worst case you get the same truncation as before.
|
|
14
|
+
*/
|
|
15
|
+
import { OllamaSettings } from '../types.js';
|
|
16
|
+
export interface TriageResult {
|
|
17
|
+
/** The compacted output to put in the brief. */
|
|
18
|
+
text: string;
|
|
19
|
+
/** True if the local model produced the summary; false if fallback. */
|
|
20
|
+
triaged: boolean;
|
|
21
|
+
}
|
|
22
|
+
type GenerateImpl = (host: string, model: string, prompt: string, opts?: {
|
|
23
|
+
timeoutMs?: number;
|
|
24
|
+
}) => Promise<string>;
|
|
25
|
+
export interface TriageDeps {
|
|
26
|
+
/** Injectable model call for testing. Defaults to the real Ollama client. */
|
|
27
|
+
generateImpl?: GenerateImpl;
|
|
28
|
+
}
|
|
29
|
+
/** Is this output worth sending to the model? Long, and Ollama enabled. */
|
|
30
|
+
export declare function shouldTriage(output: string, ollama: Pick<OllamaSettings, 'enabled'>): boolean;
|
|
31
|
+
export declare function buildTriagePrompt(output: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Triage command output. Returns a compacted version plus whether the model
|
|
34
|
+
* was used. Never throws — any failure falls back to deterministic truncation.
|
|
35
|
+
*/
|
|
36
|
+
export declare function triageOutput(output: string, ollama: OllamaSettings, deps?: TriageDeps): Promise<TriageResult>;
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output triage (v0.2.x) — the second local-LLM task after file selection.
|
|
3
|
+
*
|
|
4
|
+
* Long command output (a 200-line pytest dump, a webpack error wall) bloats
|
|
5
|
+
* the next Claude brief and buries the signal. helpcode already truncates
|
|
6
|
+
* deterministically (head + tail). When Ollama is available, we can do
|
|
7
|
+
* better: ask a local model to extract the key failure(s) so the brief stays
|
|
8
|
+
* tight and on-point.
|
|
9
|
+
*
|
|
10
|
+
* Same contract as the selector: the LLM is a free upgrade. If Ollama is
|
|
11
|
+
* disabled, unreachable, slow, errors, or returns nothing useful, we fall
|
|
12
|
+
* back to the existing deterministic truncation. Triage NEVER loses the
|
|
13
|
+
* output entirely — worst case you get the same truncation as before.
|
|
14
|
+
*/
|
|
15
|
+
import { generate, OllamaError } from './ollama.js';
|
|
16
|
+
import { truncateLines } from '../lib/compress.js';
|
|
17
|
+
/** Output longer than this (lines) is a candidate for triage. */
|
|
18
|
+
const TRIAGE_THRESHOLD_LINES = 30;
|
|
19
|
+
/** Deterministic fallback truncation cap. */
|
|
20
|
+
const FALLBACK_MAX_LINES = 40;
|
|
21
|
+
/** Is this output worth sending to the model? Long, and Ollama enabled. */
|
|
22
|
+
export function shouldTriage(output, ollama) {
|
|
23
|
+
if (!ollama.enabled)
|
|
24
|
+
return false;
|
|
25
|
+
return output.split(/\r?\n/).length > TRIAGE_THRESHOLD_LINES;
|
|
26
|
+
}
|
|
27
|
+
export function buildTriagePrompt(output) {
|
|
28
|
+
return [
|
|
29
|
+
'The following is output from running a command (often a test run or build).',
|
|
30
|
+
'Extract ONLY the information needed to understand and fix any failure:',
|
|
31
|
+
'- which test(s) or step(s) failed, and the exact error message',
|
|
32
|
+
'- the file and line if present',
|
|
33
|
+
'Omit passing tests, setup banners, and noise. If everything passed, say so in one line.',
|
|
34
|
+
'Be concise — a few lines at most. No preamble.',
|
|
35
|
+
'',
|
|
36
|
+
'--- OUTPUT ---',
|
|
37
|
+
output,
|
|
38
|
+
'--- END OUTPUT ---',
|
|
39
|
+
].join('\n');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Triage command output. Returns a compacted version plus whether the model
|
|
43
|
+
* was used. Never throws — any failure falls back to deterministic truncation.
|
|
44
|
+
*/
|
|
45
|
+
export async function triageOutput(output, ollama, deps = {}) {
|
|
46
|
+
// Short output, or triage disabled: return as-is or truncate, no model.
|
|
47
|
+
if (!shouldTriage(output, ollama)) {
|
|
48
|
+
const lineCount = output.split(/\r?\n/).length;
|
|
49
|
+
const text = lineCount > FALLBACK_MAX_LINES
|
|
50
|
+
? truncateLines(output, FALLBACK_MAX_LINES, 'lines')
|
|
51
|
+
: output;
|
|
52
|
+
return { text, triaged: false };
|
|
53
|
+
}
|
|
54
|
+
const generateImpl = deps.generateImpl ?? generate;
|
|
55
|
+
try {
|
|
56
|
+
const summary = await generateImpl(ollama.host, ollama.model, buildTriagePrompt(output), { timeoutMs: ollama.timeoutMs });
|
|
57
|
+
if (summary && summary.trim().length > 0) {
|
|
58
|
+
return { text: summary.trim(), triaged: true };
|
|
59
|
+
}
|
|
60
|
+
// Empty model result: fall back.
|
|
61
|
+
return { text: truncateLines(output, FALLBACK_MAX_LINES, 'lines'), triaged: false };
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
// Transport/timeout/etc.: fall back to deterministic truncation.
|
|
65
|
+
void (e instanceof OllamaError);
|
|
66
|
+
return { text: truncateLines(output, FALLBACK_MAX_LINES, 'lines'), triaged: false };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=triage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"triage.js","sourceRoot":"","sources":["../../../src/core/triage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,iEAAiE;AACjE,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAClC,6CAA6C;AAC7C,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAqB9B,2EAA2E;AAC3E,MAAM,UAAU,YAAY,CAC1B,MAAc,EACd,MAAuC;IAEvC,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAClC,OAAO,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,sBAAsB,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,OAAO;QACL,6EAA6E;QAC7E,wEAAwE;QACxE,gEAAgE;QAChE,gCAAgC;QAChC,yFAAyF;QACzF,gDAAgD;QAChD,EAAE;QACF,gBAAgB;QAChB,MAAM;QACN,oBAAoB;KACrB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAc,EACd,MAAsB,EACtB,OAAmB,EAAE;IAErB,wEAAwE;IACxE,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;QAClC,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;QAC/C,MAAM,IAAI,GAAG,SAAS,GAAG,kBAAkB;YACzC,CAAC,CAAC,aAAa,CAAC,MAAM,EAAE,kBAAkB,EAAE,OAAO,CAAC;YACpD,CAAC,CAAC,MAAM,CAAC;QACX,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAClC,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,QAAQ,CAAC;IACnD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,YAAY,CAChC,MAAM,CAAC,IAAI,EACX,MAAM,CAAC,KAAK,EACZ,iBAAiB,CAAC,MAAM,CAAC,EACzB,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAChC,CAAC;QACF,IAAI,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACjD,CAAC;QACD,iCAAiC;QACjC,OAAO,EAAE,IAAI,EAAE,aAAa,CAAC,MAAM,EAAE,kBAAkB,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACtF,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,iEAAiE;QACjE,KAAK,CAAC,CAAC,YAAY,WAAW,CAAC,CAAC;QAChC,OAAO,EAAE,IAAI,EAAE,aAAa,CAAC,MAAM,EAAE,kBAAkB,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACtF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* helpcode — CLI router.
|
|
3
|
+
*
|
|
4
|
+
* Six commands, kept deliberately small:
|
|
5
|
+
* init detect project and set up .helpcode/
|
|
6
|
+
* ask compose a prompt for Claude.ai
|
|
7
|
+
* apply apply Claude's reply
|
|
8
|
+
* run run a shell command, get compact output
|
|
9
|
+
* status show current state
|
|
10
|
+
* reset clear state
|
|
11
|
+
*/
|
|
12
|
+
export declare function run(argv: string[]): Promise<number>;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* helpcode — CLI router.
|
|
3
|
+
*
|
|
4
|
+
* Six commands, kept deliberately small:
|
|
5
|
+
* init detect project and set up .helpcode/
|
|
6
|
+
* ask compose a prompt for Claude.ai
|
|
7
|
+
* apply apply Claude's reply
|
|
8
|
+
* run run a shell command, get compact output
|
|
9
|
+
* status show current state
|
|
10
|
+
* reset clear state
|
|
11
|
+
*/
|
|
12
|
+
import { handleInit } from './commands/init.js';
|
|
13
|
+
import { handleAsk } from './commands/ask.js';
|
|
14
|
+
import { handleApply } from './commands/apply.js';
|
|
15
|
+
import { handleRun } from './commands/run.js';
|
|
16
|
+
import { handleStatus } from './commands/status.js';
|
|
17
|
+
import { handleReset } from './commands/reset.js';
|
|
18
|
+
import { HelpcodeError } from './lib/errors.js';
|
|
19
|
+
import { c, log } from './lib/ui.js';
|
|
20
|
+
const VERSION = '0.2.2';
|
|
21
|
+
const HELP = `helpcode v${VERSION}
|
|
22
|
+
|
|
23
|
+
A local agent that makes Claude.ai conversations efficient enough to ship
|
|
24
|
+
real projects on a Pro subscription.
|
|
25
|
+
|
|
26
|
+
USAGE:
|
|
27
|
+
helpcode <command> [options]
|
|
28
|
+
|
|
29
|
+
COMMANDS:
|
|
30
|
+
init Detect this project and set up .helpcode/
|
|
31
|
+
ask "<task>" [options] Compose a Claude prompt for the given task
|
|
32
|
+
--files <paths...> include specific files (skips auto-selection)
|
|
33
|
+
--no-llm force keyword selection even if Ollama is on
|
|
34
|
+
--explain-selection show why each file was chosen
|
|
35
|
+
apply [--yes] [--dry-run] Apply Claude's pasted reply (from stdin)
|
|
36
|
+
run "<command>" Run a shell command, get compact output
|
|
37
|
+
status Show current task and state
|
|
38
|
+
reset [--yes] Clear state (project config is untouched)
|
|
39
|
+
|
|
40
|
+
GLOBAL FLAGS:
|
|
41
|
+
--version Print version and exit
|
|
42
|
+
--help Show this help
|
|
43
|
+
|
|
44
|
+
Read more: https://github.com/USER/helpcode
|
|
45
|
+
`;
|
|
46
|
+
export async function run(argv) {
|
|
47
|
+
if (argv.length === 0 || argv.includes('--help') || argv.includes('-h')) {
|
|
48
|
+
console.log(HELP);
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
if (argv.includes('--version') || argv.includes('-v')) {
|
|
52
|
+
console.log(VERSION);
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
const [cmd, ...rest] = argv;
|
|
56
|
+
try {
|
|
57
|
+
switch (cmd) {
|
|
58
|
+
case 'init':
|
|
59
|
+
return await handleInit({ force: rest.includes('--force') });
|
|
60
|
+
case 'ask': {
|
|
61
|
+
const files = extractFlagValues(rest, '--files');
|
|
62
|
+
const task = rest.filter(a => !a.startsWith('--') && !files.includes(a)).join(' ');
|
|
63
|
+
return await handleAsk(task, {
|
|
64
|
+
files: files.length ? files : undefined,
|
|
65
|
+
noLlm: rest.includes('--no-llm'),
|
|
66
|
+
explainSelection: rest.includes('--explain-selection'),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
case 'apply':
|
|
70
|
+
return await handleApply({
|
|
71
|
+
yes: rest.includes('--yes'),
|
|
72
|
+
dryRun: rest.includes('--dry-run'),
|
|
73
|
+
});
|
|
74
|
+
case 'run': {
|
|
75
|
+
const command = rest.filter(a => !a.startsWith('--')).join(' ');
|
|
76
|
+
const timeout = extractFlagInt(rest, '--timeout');
|
|
77
|
+
return await handleRun(command, timeout !== null ? { timeout } : {});
|
|
78
|
+
}
|
|
79
|
+
case 'status':
|
|
80
|
+
return await handleStatus();
|
|
81
|
+
case 'reset':
|
|
82
|
+
return await handleReset({ yes: rest.includes('--yes') });
|
|
83
|
+
default:
|
|
84
|
+
log.err(`Unknown command: ${cmd}`);
|
|
85
|
+
console.log();
|
|
86
|
+
console.log(HELP);
|
|
87
|
+
return 1;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
if (e instanceof HelpcodeError) {
|
|
92
|
+
log.err(e.message);
|
|
93
|
+
if (e.hint)
|
|
94
|
+
console.log(c.dim(' ' + e.hint));
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
log.err(e.message);
|
|
98
|
+
return 1;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function extractFlagValues(args, flag) {
|
|
102
|
+
const idx = args.indexOf(flag);
|
|
103
|
+
if (idx === -1)
|
|
104
|
+
return [];
|
|
105
|
+
const values = [];
|
|
106
|
+
for (let i = idx + 1; i < args.length; i++) {
|
|
107
|
+
if (args[i].startsWith('--'))
|
|
108
|
+
break;
|
|
109
|
+
values.push(args[i]);
|
|
110
|
+
}
|
|
111
|
+
return values;
|
|
112
|
+
}
|
|
113
|
+
function extractFlagInt(args, flag) {
|
|
114
|
+
const idx = args.indexOf(flag);
|
|
115
|
+
if (idx === -1 || idx + 1 >= args.length)
|
|
116
|
+
return null;
|
|
117
|
+
const n = parseInt(args[idx + 1], 10);
|
|
118
|
+
return Number.isNaN(n) ? null : n;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compression helpers — make long process output fit in a Claude prompt
|
|
3
|
+
* without losing the signal.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* If `text` exceeds `maxLines`, keep the first third and last two-thirds,
|
|
7
|
+
* replacing the middle with a one-line summary of what was omitted.
|
|
8
|
+
*/
|
|
9
|
+
export declare function truncateLines(text: string, maxLines?: number, label?: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* If stderr contains a Python traceback, return just the traceback (trimmed).
|
|
12
|
+
* Otherwise return the stderr trimmed to `maxLines`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function extractTraceback(stderr: string, maxLines?: number): string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compression helpers — make long process output fit in a Claude prompt
|
|
3
|
+
* without losing the signal.
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_MAX_LINES = 40;
|
|
6
|
+
const DEFAULT_TRACEBACK_LINES = 25;
|
|
7
|
+
/**
|
|
8
|
+
* If `text` exceeds `maxLines`, keep the first third and last two-thirds,
|
|
9
|
+
* replacing the middle with a one-line summary of what was omitted.
|
|
10
|
+
*/
|
|
11
|
+
export function truncateLines(text, maxLines = DEFAULT_MAX_LINES, label = 'lines') {
|
|
12
|
+
const lines = text.split(/\r?\n/);
|
|
13
|
+
if (lines.length <= maxLines)
|
|
14
|
+
return text;
|
|
15
|
+
const head = Math.floor(maxLines / 3);
|
|
16
|
+
const tail = maxLines - head;
|
|
17
|
+
const omitted = lines.length - maxLines;
|
|
18
|
+
return [
|
|
19
|
+
...lines.slice(0, head),
|
|
20
|
+
`... [${omitted} ${label} omitted] ...`,
|
|
21
|
+
...lines.slice(lines.length - tail),
|
|
22
|
+
].join('\n');
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* If stderr contains a Python traceback, return just the traceback (trimmed).
|
|
26
|
+
* Otherwise return the stderr trimmed to `maxLines`.
|
|
27
|
+
*/
|
|
28
|
+
export function extractTraceback(stderr, maxLines = DEFAULT_TRACEBACK_LINES) {
|
|
29
|
+
const idx = stderr.indexOf('Traceback');
|
|
30
|
+
if (idx >= 0) {
|
|
31
|
+
return truncateLines(stderr.slice(idx), maxLines, 'traceback lines');
|
|
32
|
+
}
|
|
33
|
+
return truncateLines(stderr.trim(), maxLines, 'stderr lines');
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=compress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compress.js","sourceRoot":"","sources":["../../../src/lib/compress.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAC7B,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAEnC;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,WAAmB,iBAAiB,EACpC,QAAgB,OAAO;IAEvB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE1C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC7B,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC;IAExC,OAAO;QACL,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC;QACvB,QAAQ,OAAO,IAAI,KAAK,eAAe;QACvC,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;KACpC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAc,EACd,WAAmB,uBAAuB;IAE1C,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACxC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;QACb,OAAO,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,aAAa,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Errors thrown by helpcode itself.
|
|
3
|
+
*
|
|
4
|
+
* Use HelpcodeError, never plain Error, for any user-facing failure.
|
|
5
|
+
* The CLI's top-level handler reads `.hint` and shows it to the user.
|
|
6
|
+
*/
|
|
7
|
+
export declare enum ErrorCode {
|
|
8
|
+
/** State or config file missing/malformed. */
|
|
9
|
+
STATE_ERROR = "STATE_ERROR",
|
|
10
|
+
/** Reading or writing a file failed. */
|
|
11
|
+
IO_ERROR = "IO_ERROR",
|
|
12
|
+
/** Input failed validation (parser, patcher, etc.). */
|
|
13
|
+
VALIDATION_ERROR = "VALIDATION_ERROR",
|
|
14
|
+
/** A subprocess (tests, git, etc.) failed in a way we report up. */
|
|
15
|
+
SUBPROCESS_ERROR = "SUBPROCESS_ERROR",
|
|
16
|
+
/** User cancelled or provided no input where required. */
|
|
17
|
+
USER_CANCELLED = "USER_CANCELLED",
|
|
18
|
+
/** A feature isn't implemented yet on this path. */
|
|
19
|
+
NOT_IMPLEMENTED = "NOT_IMPLEMENTED"
|
|
20
|
+
}
|
|
21
|
+
export declare class HelpcodeError extends Error {
|
|
22
|
+
readonly code: ErrorCode;
|
|
23
|
+
readonly hint: string;
|
|
24
|
+
constructor(code: ErrorCode, message: string, hint?: string);
|
|
25
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Errors thrown by helpcode itself.
|
|
3
|
+
*
|
|
4
|
+
* Use HelpcodeError, never plain Error, for any user-facing failure.
|
|
5
|
+
* The CLI's top-level handler reads `.hint` and shows it to the user.
|
|
6
|
+
*/
|
|
7
|
+
export var ErrorCode;
|
|
8
|
+
(function (ErrorCode) {
|
|
9
|
+
/** State or config file missing/malformed. */
|
|
10
|
+
ErrorCode["STATE_ERROR"] = "STATE_ERROR";
|
|
11
|
+
/** Reading or writing a file failed. */
|
|
12
|
+
ErrorCode["IO_ERROR"] = "IO_ERROR";
|
|
13
|
+
/** Input failed validation (parser, patcher, etc.). */
|
|
14
|
+
ErrorCode["VALIDATION_ERROR"] = "VALIDATION_ERROR";
|
|
15
|
+
/** A subprocess (tests, git, etc.) failed in a way we report up. */
|
|
16
|
+
ErrorCode["SUBPROCESS_ERROR"] = "SUBPROCESS_ERROR";
|
|
17
|
+
/** User cancelled or provided no input where required. */
|
|
18
|
+
ErrorCode["USER_CANCELLED"] = "USER_CANCELLED";
|
|
19
|
+
/** A feature isn't implemented yet on this path. */
|
|
20
|
+
ErrorCode["NOT_IMPLEMENTED"] = "NOT_IMPLEMENTED";
|
|
21
|
+
})(ErrorCode || (ErrorCode = {}));
|
|
22
|
+
export class HelpcodeError extends Error {
|
|
23
|
+
code;
|
|
24
|
+
hint;
|
|
25
|
+
constructor(code, message, hint = '') {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = 'HelpcodeError';
|
|
28
|
+
this.code = code;
|
|
29
|
+
this.hint = hint;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/lib/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAN,IAAY,SAaX;AAbD,WAAY,SAAS;IACnB,8CAA8C;IAC9C,wCAA2B,CAAA;IAC3B,wCAAwC;IACxC,kCAAqB,CAAA;IACrB,uDAAuD;IACvD,kDAAqC,CAAA;IACrC,oEAAoE;IACpE,kDAAqC,CAAA;IACrC,0DAA0D;IAC1D,8CAAiC,CAAA;IACjC,oDAAoD;IACpD,gDAAmC,CAAA;AACrC,CAAC,EAbW,SAAS,KAAT,SAAS,QAapB;AAED,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,IAAI,CAAY;IAChB,IAAI,CAAS;IAEtB,YAAY,IAAe,EAAE,OAAe,EAAE,IAAI,GAAG,EAAE;QACrD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git helpers. All best-effort — return null/empty when git isn't available
|
|
3
|
+
* or the directory isn't a repo. Never throws.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getLastCommitSummary(cwd?: string): string | null;
|
|
6
|
+
export declare function getUncommittedFileCount(cwd?: string): number;
|
|
7
|
+
export declare function getUncommittedFiles(cwd?: string): string[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git helpers. All best-effort — return null/empty when git isn't available
|
|
3
|
+
* or the directory isn't a repo. Never throws.
|
|
4
|
+
*/
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
function isGitRepo(cwd = process.cwd()) {
|
|
9
|
+
return fs.existsSync(path.join(cwd, '.git'));
|
|
10
|
+
}
|
|
11
|
+
function tryGit(args, cwd = process.cwd()) {
|
|
12
|
+
if (!isGitRepo(cwd))
|
|
13
|
+
return null;
|
|
14
|
+
try {
|
|
15
|
+
return execSync(`git ${args.join(' ')}`, {
|
|
16
|
+
cwd,
|
|
17
|
+
encoding: 'utf-8',
|
|
18
|
+
timeout: 5000,
|
|
19
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
20
|
+
}).trim();
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function getLastCommitSummary(cwd) {
|
|
27
|
+
return tryGit(['log', '-1', '--pretty=format:%h %s (%cr)'], cwd);
|
|
28
|
+
}
|
|
29
|
+
export function getUncommittedFileCount(cwd) {
|
|
30
|
+
const out = tryGit(['status', '--short'], cwd);
|
|
31
|
+
if (!out)
|
|
32
|
+
return 0;
|
|
33
|
+
return out.split('\n').filter(l => l.trim().length > 0).length;
|
|
34
|
+
}
|
|
35
|
+
export function getUncommittedFiles(cwd) {
|
|
36
|
+
const out = tryGit(['status', '--short'], cwd);
|
|
37
|
+
if (!out)
|
|
38
|
+
return [];
|
|
39
|
+
return out
|
|
40
|
+
.split('\n')
|
|
41
|
+
.map(l => l.trim())
|
|
42
|
+
.filter(Boolean)
|
|
43
|
+
.map(l => l.replace(/^.{1,3}/, '').trim());
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../../src/lib/git.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,SAAS,SAAS,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAC5C,OAAO,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,MAAM,CAAC,IAAc,EAAE,MAAc,OAAO,CAAC,GAAG,EAAE;IACzD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACjC,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE;YACvC,GAAG;YACH,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;SACpC,CAAC,CAAC,IAAI,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,GAAY;IAC/C,OAAO,MAAM,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,6BAA6B,CAAC,EAAE,GAAG,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,GAAY;IAClD,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,CAAC;IAC/C,IAAI,CAAC,GAAG;QAAE,OAAO,CAAC,CAAC;IACnB,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,GAAY;IAC9C,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,CAAC;IAC/C,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,OAAO,GAAG;SACP,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SAClB,MAAM,CAAC,OAAO,CAAC;SACf,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classify why a test/command run failed, so the CLI can give the right
|
|
3
|
+
* advice. The key distinction:
|
|
4
|
+
*
|
|
5
|
+
* - 'setup' : the runner never started (not installed, not on PATH,
|
|
6
|
+
* missing module). Sending this back to Claude wastes a
|
|
7
|
+
* turn — the user needs to install something.
|
|
8
|
+
* - 'timeout' : the command ran too long and was killed.
|
|
9
|
+
* - 'test' : the runner started and tests genuinely failed. THIS is
|
|
10
|
+
* the case worth sending back to Claude.
|
|
11
|
+
*
|
|
12
|
+
* This is a pure function over the captured CommandResult, so it's easy to
|
|
13
|
+
* unit-test against real-world stderr strings.
|
|
14
|
+
*/
|
|
15
|
+
import { CommandResult } from '../core/tools.js';
|
|
16
|
+
export type FailureKind = 'setup' | 'timeout' | 'test';
|
|
17
|
+
export interface FailureClassification {
|
|
18
|
+
kind: FailureKind;
|
|
19
|
+
/** A short, human-readable message describing the failure. */
|
|
20
|
+
message: string;
|
|
21
|
+
/** Actionable hint for the user, or empty string. */
|
|
22
|
+
hint: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function classifyRunFailure(result: CommandResult): FailureClassification;
|