@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,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project detection. Sniffs the working directory to make reasonable
|
|
3
|
+
* assumptions about language, framework, and test command.
|
|
4
|
+
*
|
|
5
|
+
* The detected config is written to .helpcode/project.json by `init`.
|
|
6
|
+
* The user can edit it; helpcode never re-detects after init unless asked.
|
|
7
|
+
*/
|
|
8
|
+
import { ProjectConfig } from '../types.js';
|
|
9
|
+
export declare function detectLanguage(root: string): ProjectConfig['language'];
|
|
10
|
+
export declare function detectFramework(root: string): string | null;
|
|
11
|
+
export declare function detectTestCommand(root: string, language: ProjectConfig['language']): string | null;
|
|
12
|
+
/**
|
|
13
|
+
* Detect source directories by finding top-level dirs that actually contain
|
|
14
|
+
* source files (directly or shallowly nested), rather than matching a fixed
|
|
15
|
+
* list of names. This means non-standard layouts — Django apps like `shop/`
|
|
16
|
+
* or `billing/`, a `core/` package, etc. — are picked up automatically.
|
|
17
|
+
*
|
|
18
|
+
* Strategy: scan immediate children of root. A child dir is a source dir if
|
|
19
|
+
* it (or its shallow descendants) contains at least one source file. Also
|
|
20
|
+
* include root itself if it has source files directly. Falls back to ['.'].
|
|
21
|
+
*/
|
|
22
|
+
export declare function detectSourceDirs(root: string): string[];
|
|
23
|
+
export declare function buildDetectedConfig(root: string): ProjectConfig;
|
|
24
|
+
export declare function loadProjectConfig(cwd?: string): ProjectConfig;
|
|
25
|
+
export declare function saveProjectConfig(cfg: ProjectConfig, cwd?: string): void;
|
|
26
|
+
export declare function projectExists(cwd?: string): boolean;
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project detection. Sniffs the working directory to make reasonable
|
|
3
|
+
* assumptions about language, framework, and test command.
|
|
4
|
+
*
|
|
5
|
+
* The detected config is written to .helpcode/project.json by `init`.
|
|
6
|
+
* The user can edit it; helpcode never re-detects after init unless asked.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import { HelpcodeError, ErrorCode } from '../lib/errors.js';
|
|
11
|
+
const STATE_DIR = '.helpcode';
|
|
12
|
+
const PROJECT_FILE = path.join(STATE_DIR, 'project.json');
|
|
13
|
+
function readBlob(root, files) {
|
|
14
|
+
let blob = '';
|
|
15
|
+
for (const f of files) {
|
|
16
|
+
const p = path.join(root, f);
|
|
17
|
+
if (fs.existsSync(p)) {
|
|
18
|
+
try {
|
|
19
|
+
blob += fs.readFileSync(p, 'utf-8').toLowerCase() + '\n';
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// ignore unreadable files
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return blob;
|
|
27
|
+
}
|
|
28
|
+
export function detectLanguage(root) {
|
|
29
|
+
if (fs.existsSync(path.join(root, 'pyproject.toml')) ||
|
|
30
|
+
fs.existsSync(path.join(root, 'requirements.txt')) ||
|
|
31
|
+
fs.existsSync(path.join(root, 'setup.py')) ||
|
|
32
|
+
fs.existsSync(path.join(root, 'Pipfile'))) {
|
|
33
|
+
return 'python';
|
|
34
|
+
}
|
|
35
|
+
if (fs.existsSync(path.join(root, 'tsconfig.json'))) {
|
|
36
|
+
return 'typescript';
|
|
37
|
+
}
|
|
38
|
+
if (fs.existsSync(path.join(root, 'package.json'))) {
|
|
39
|
+
return 'javascript';
|
|
40
|
+
}
|
|
41
|
+
return 'unknown';
|
|
42
|
+
}
|
|
43
|
+
export function detectFramework(root) {
|
|
44
|
+
const blob = readBlob(root, ['requirements.txt', 'pyproject.toml', 'package.json', 'Pipfile']);
|
|
45
|
+
if (fs.existsSync(path.join(root, 'manage.py')) || blob.includes('django')) {
|
|
46
|
+
return 'Django';
|
|
47
|
+
}
|
|
48
|
+
if (blob.includes('"fastapi"') || blob.includes('fastapi==') || blob.includes('fastapi>=')) {
|
|
49
|
+
return 'FastAPI';
|
|
50
|
+
}
|
|
51
|
+
if (blob.includes('"flask"') || blob.includes('flask==') || blob.includes('flask>=')) {
|
|
52
|
+
return 'Flask';
|
|
53
|
+
}
|
|
54
|
+
if (blob.includes('"next"'))
|
|
55
|
+
return 'Next.js';
|
|
56
|
+
if (blob.includes('"express"'))
|
|
57
|
+
return 'Express';
|
|
58
|
+
if (blob.includes('"react"'))
|
|
59
|
+
return 'React';
|
|
60
|
+
if (blob.includes('"vue"'))
|
|
61
|
+
return 'Vue';
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
export function detectTestCommand(root, language) {
|
|
65
|
+
if (language === 'python') {
|
|
66
|
+
if (fs.existsSync(path.join(root, 'manage.py'))) {
|
|
67
|
+
return 'python manage.py test --verbosity=1';
|
|
68
|
+
}
|
|
69
|
+
// pytest is the default for everything else Python
|
|
70
|
+
return 'pytest -q --tb=short';
|
|
71
|
+
}
|
|
72
|
+
if (language === 'javascript' || language === 'typescript') {
|
|
73
|
+
try {
|
|
74
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf-8'));
|
|
75
|
+
if (pkg.scripts && typeof pkg.scripts.test === 'string') {
|
|
76
|
+
return 'npm test';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// fall through
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
/** Directory names we never treat as source dirs. */
|
|
86
|
+
const NON_SOURCE_DIRS = new Set([
|
|
87
|
+
'.git', '.venv', 'venv', 'env', 'node_modules', '__pycache__',
|
|
88
|
+
'.pytest_cache', '.mypy_cache', 'dist', 'build', '.next', '.idea',
|
|
89
|
+
'.vscode', '.helpcode', 'coverage', '.cache', 'site-packages',
|
|
90
|
+
'migrations', '.tox', 'htmlcov', '__snapshots__',
|
|
91
|
+
]);
|
|
92
|
+
const SOURCE_FILE_EXTS = new Set([
|
|
93
|
+
'.py', '.js', '.ts', '.jsx', '.tsx', '.go', '.rs', '.rb', '.java',
|
|
94
|
+
]);
|
|
95
|
+
/**
|
|
96
|
+
* Detect source directories by finding top-level dirs that actually contain
|
|
97
|
+
* source files (directly or shallowly nested), rather than matching a fixed
|
|
98
|
+
* list of names. This means non-standard layouts — Django apps like `shop/`
|
|
99
|
+
* or `billing/`, a `core/` package, etc. — are picked up automatically.
|
|
100
|
+
*
|
|
101
|
+
* Strategy: scan immediate children of root. A child dir is a source dir if
|
|
102
|
+
* it (or its shallow descendants) contains at least one source file. Also
|
|
103
|
+
* include root itself if it has source files directly. Falls back to ['.'].
|
|
104
|
+
*/
|
|
105
|
+
export function detectSourceDirs(root) {
|
|
106
|
+
const found = [];
|
|
107
|
+
let entries;
|
|
108
|
+
try {
|
|
109
|
+
entries = fs.readdirSync(root, { withFileTypes: true });
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return ['.'];
|
|
113
|
+
}
|
|
114
|
+
let rootHasSource = false;
|
|
115
|
+
for (const entry of entries) {
|
|
116
|
+
if (entry.isFile() && SOURCE_FILE_EXTS.has(path.extname(entry.name))) {
|
|
117
|
+
rootHasSource = true;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
for (const entry of entries) {
|
|
121
|
+
if (!entry.isDirectory())
|
|
122
|
+
continue;
|
|
123
|
+
if (NON_SOURCE_DIRS.has(entry.name) || entry.name.startsWith('.'))
|
|
124
|
+
continue;
|
|
125
|
+
if (dirContainsSource(path.join(root, entry.name), 2)) {
|
|
126
|
+
found.push(entry.name);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (rootHasSource)
|
|
130
|
+
found.unshift('.');
|
|
131
|
+
return found.length > 0 ? found : ['.'];
|
|
132
|
+
}
|
|
133
|
+
/** Does this dir contain a source file within `maxDepth` levels? */
|
|
134
|
+
function dirContainsSource(dir, maxDepth) {
|
|
135
|
+
if (maxDepth < 0)
|
|
136
|
+
return false;
|
|
137
|
+
let entries;
|
|
138
|
+
try {
|
|
139
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
for (const entry of entries) {
|
|
145
|
+
if (entry.isFile() && SOURCE_FILE_EXTS.has(path.extname(entry.name))) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const entry of entries) {
|
|
150
|
+
if (entry.isDirectory()
|
|
151
|
+
&& !NON_SOURCE_DIRS.has(entry.name)
|
|
152
|
+
&& !entry.name.startsWith('.')) {
|
|
153
|
+
if (dirContainsSource(path.join(dir, entry.name), maxDepth - 1))
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
export function buildDetectedConfig(root) {
|
|
160
|
+
const language = detectLanguage(root);
|
|
161
|
+
return {
|
|
162
|
+
root,
|
|
163
|
+
language,
|
|
164
|
+
framework: detectFramework(root),
|
|
165
|
+
testCommand: detectTestCommand(root, language),
|
|
166
|
+
sourceDirs: detectSourceDirs(root),
|
|
167
|
+
createdAt: new Date().toISOString(),
|
|
168
|
+
// Default: disabled. `init` upgrades this to enabled with a detected
|
|
169
|
+
// model if Ollama is reachable. Users can flip `enabled` any time.
|
|
170
|
+
ollama: {
|
|
171
|
+
enabled: false,
|
|
172
|
+
model: 'qwen2.5-coder:7b',
|
|
173
|
+
host: 'http://localhost:11434',
|
|
174
|
+
timeoutMs: 20000,
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
export function loadProjectConfig(cwd = process.cwd()) {
|
|
179
|
+
const file = path.join(cwd, PROJECT_FILE);
|
|
180
|
+
if (!fs.existsSync(file)) {
|
|
181
|
+
throw new HelpcodeError(ErrorCode.STATE_ERROR, `No ${PROJECT_FILE} found in ${cwd}.`, 'Run `helpcode init` first to set up the project.');
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
return JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
185
|
+
}
|
|
186
|
+
catch (e) {
|
|
187
|
+
throw new HelpcodeError(ErrorCode.STATE_ERROR, `Could not parse ${PROJECT_FILE}: ${e.message}`, 'The file may be corrupt. Run `helpcode init --force` to regenerate.');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
export function saveProjectConfig(cfg, cwd = process.cwd()) {
|
|
191
|
+
const dir = path.join(cwd, STATE_DIR);
|
|
192
|
+
if (!fs.existsSync(dir))
|
|
193
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
194
|
+
fs.writeFileSync(path.join(cwd, PROJECT_FILE), JSON.stringify(cfg, null, 2), 'utf-8');
|
|
195
|
+
}
|
|
196
|
+
export function projectExists(cwd = process.cwd()) {
|
|
197
|
+
return fs.existsSync(path.join(cwd, PROJECT_FILE));
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=project.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../../src/core/project.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a structured prompt for Claude.ai.
|
|
3
|
+
*
|
|
4
|
+
* The prompt always includes the response protocol at the bottom, so Claude
|
|
5
|
+
* sees how to format its reply on every turn (pattern training by repetition).
|
|
6
|
+
*/
|
|
7
|
+
import { ProjectConfig } from '../types.js';
|
|
8
|
+
export interface PromptInput {
|
|
9
|
+
taskDescription: string;
|
|
10
|
+
selectedFiles: string[];
|
|
11
|
+
lastTestOutput: string | null;
|
|
12
|
+
config: ProjectConfig;
|
|
13
|
+
}
|
|
14
|
+
export declare function buildPrompt(input: PromptInput): string;
|
|
15
|
+
/**
|
|
16
|
+
* Instructions appended to every prompt. This is the load-bearing protocol
|
|
17
|
+
* that makes parsing Claude's reply reliable.
|
|
18
|
+
*/
|
|
19
|
+
export declare const RESPONSE_PROTOCOL = "## Please respond in this format\n\n```\n## PLAN\nOne to three sentences describing what you'll change and why.\n\n## DIFF: path/to/file.py\n```diff\n--- a/path/to/file.py\n+++ b/path/to/file.py\n@@ -10,3 +10,3 @@\n- old line\n+ new line\n```\n\n(repeat ## DIFF: blocks as needed, one per file)\n\n## TEST\n```bash\nthe single command I should run to verify this works\n```\n\n## NOTES\n(optional) anything else worth noting \u2014 caveats, alternatives, questions.\n```\n\nRules:\n- Use unified diff format inside each `## DIFF:` block.\n- Use real context lines (with a leading space) so the diff anchors correctly.\n- Keep each diff scoped to the file named in its header.\n- Don't include explanations between sections \u2014 put commentary in `## NOTES`.";
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a structured prompt for Claude.ai.
|
|
3
|
+
*
|
|
4
|
+
* The prompt always includes the response protocol at the bottom, so Claude
|
|
5
|
+
* sees how to format its reply on every turn (pattern training by repetition).
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import { getLastCommitSummary, getUncommittedFileCount } from '../lib/git.js';
|
|
10
|
+
import { truncateLines } from '../lib/compress.js';
|
|
11
|
+
const MAX_FILE_LINES = 150;
|
|
12
|
+
export function buildPrompt(input) {
|
|
13
|
+
const parts = [];
|
|
14
|
+
parts.push('## Project context');
|
|
15
|
+
parts.push(`- Language: ${input.config.language}`);
|
|
16
|
+
if (input.config.framework) {
|
|
17
|
+
parts.push(`- Framework: ${input.config.framework}`);
|
|
18
|
+
}
|
|
19
|
+
if (input.config.testCommand) {
|
|
20
|
+
parts.push(`- Test command: \`${input.config.testCommand}\``);
|
|
21
|
+
}
|
|
22
|
+
const commit = getLastCommitSummary(input.config.root);
|
|
23
|
+
if (commit)
|
|
24
|
+
parts.push(`- Last commit: ${commit}`);
|
|
25
|
+
const uncommitted = getUncommittedFileCount(input.config.root);
|
|
26
|
+
if (uncommitted > 0)
|
|
27
|
+
parts.push(`- Uncommitted changes: ${uncommitted} file(s)`);
|
|
28
|
+
if (input.lastTestOutput && input.lastTestOutput.trim()) {
|
|
29
|
+
parts.push('');
|
|
30
|
+
parts.push('## Last test output');
|
|
31
|
+
parts.push('```');
|
|
32
|
+
parts.push(truncateLines(input.lastTestOutput, 30, 'lines'));
|
|
33
|
+
parts.push('```');
|
|
34
|
+
}
|
|
35
|
+
if (input.selectedFiles.length > 0) {
|
|
36
|
+
parts.push('');
|
|
37
|
+
parts.push(`## Files (${input.selectedFiles.length})`);
|
|
38
|
+
for (const f of input.selectedFiles) {
|
|
39
|
+
const rel = path.relative(input.config.root, f);
|
|
40
|
+
let body;
|
|
41
|
+
try {
|
|
42
|
+
const text = fs.readFileSync(f, 'utf-8');
|
|
43
|
+
const lines = text.split(/\r?\n/);
|
|
44
|
+
body = lines.length > MAX_FILE_LINES
|
|
45
|
+
? lines.slice(0, MAX_FILE_LINES).join('\n') +
|
|
46
|
+
`\n# ... [${lines.length - MAX_FILE_LINES} more lines truncated] ...`
|
|
47
|
+
: text;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
body = '(could not read file)';
|
|
51
|
+
}
|
|
52
|
+
const fence = fenceForFile(f);
|
|
53
|
+
parts.push('');
|
|
54
|
+
parts.push(`### \`${rel}\``);
|
|
55
|
+
parts.push('```' + fence);
|
|
56
|
+
parts.push(body);
|
|
57
|
+
parts.push('```');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
parts.push('');
|
|
61
|
+
parts.push('## My task');
|
|
62
|
+
parts.push(input.taskDescription);
|
|
63
|
+
parts.push('');
|
|
64
|
+
parts.push(RESPONSE_PROTOCOL);
|
|
65
|
+
return parts.join('\n');
|
|
66
|
+
}
|
|
67
|
+
function fenceForFile(filepath) {
|
|
68
|
+
const ext = path.extname(filepath);
|
|
69
|
+
const map = {
|
|
70
|
+
'.py': 'python',
|
|
71
|
+
'.js': 'javascript',
|
|
72
|
+
'.ts': 'typescript',
|
|
73
|
+
'.jsx': 'jsx',
|
|
74
|
+
'.tsx': 'tsx',
|
|
75
|
+
'.sh': 'bash',
|
|
76
|
+
'.rb': 'ruby',
|
|
77
|
+
'.go': 'go',
|
|
78
|
+
'.java': 'java',
|
|
79
|
+
'.rs': 'rust',
|
|
80
|
+
'.json': 'json',
|
|
81
|
+
'.yml': 'yaml',
|
|
82
|
+
'.yaml': 'yaml',
|
|
83
|
+
};
|
|
84
|
+
return map[ext] ?? '';
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Instructions appended to every prompt. This is the load-bearing protocol
|
|
88
|
+
* that makes parsing Claude's reply reliable.
|
|
89
|
+
*/
|
|
90
|
+
export const RESPONSE_PROTOCOL = `## Please respond in this format
|
|
91
|
+
|
|
92
|
+
\`\`\`
|
|
93
|
+
## PLAN
|
|
94
|
+
One to three sentences describing what you'll change and why.
|
|
95
|
+
|
|
96
|
+
## DIFF: path/to/file.py
|
|
97
|
+
\`\`\`diff
|
|
98
|
+
--- a/path/to/file.py
|
|
99
|
+
+++ b/path/to/file.py
|
|
100
|
+
@@ -10,3 +10,3 @@
|
|
101
|
+
- old line
|
|
102
|
+
+ new line
|
|
103
|
+
\`\`\`
|
|
104
|
+
|
|
105
|
+
(repeat ## DIFF: blocks as needed, one per file)
|
|
106
|
+
|
|
107
|
+
## TEST
|
|
108
|
+
\`\`\`bash
|
|
109
|
+
the single command I should run to verify this works
|
|
110
|
+
\`\`\`
|
|
111
|
+
|
|
112
|
+
## NOTES
|
|
113
|
+
(optional) anything else worth noting — caveats, alternatives, questions.
|
|
114
|
+
\`\`\`
|
|
115
|
+
|
|
116
|
+
Rules:
|
|
117
|
+
- Use unified diff format inside each \`## DIFF:\` block.
|
|
118
|
+
- Use real context lines (with a leading space) so the diff anchors correctly.
|
|
119
|
+
- Keep each diff scoped to the file named in its header.
|
|
120
|
+
- Don't include explanations between sections — put commentary in \`## NOTES\`.`;
|
|
121
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../../src/core/prompt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File selection — pick the files most likely relevant to a task.
|
|
3
|
+
*
|
|
4
|
+
* Two strategies:
|
|
5
|
+
* - heuristic (v0.1): filename match + content keyword + recency. Always
|
|
6
|
+
* available, synchronous, no dependencies.
|
|
7
|
+
* - llm (v0.2): a local model reasons about which files matter. Used only
|
|
8
|
+
* when Ollama is enabled in project.json AND reachable. Falls back to
|
|
9
|
+
* the heuristic on ANY failure.
|
|
10
|
+
*
|
|
11
|
+
* `selectFilesWithStrategy` is the entry point the `ask` command uses. The
|
|
12
|
+
* old synchronous `selectFiles` (heuristic) is preserved as the fallback and
|
|
13
|
+
* for callers that want the heuristic explicitly.
|
|
14
|
+
*/
|
|
15
|
+
import { ProjectConfig } from '../types.js';
|
|
16
|
+
/** How a file was chosen. `reason` is populated for the LLM strategy. */
|
|
17
|
+
export interface SelectedFile {
|
|
18
|
+
/** Absolute path. */
|
|
19
|
+
filepath: string;
|
|
20
|
+
/** Why it was chosen (LLM reasoning, or a short heuristic note). */
|
|
21
|
+
reason: string;
|
|
22
|
+
}
|
|
23
|
+
export interface SelectionResult {
|
|
24
|
+
files: SelectedFile[];
|
|
25
|
+
/** Which strategy actually produced the result. */
|
|
26
|
+
strategy: 'llm' | 'heuristic';
|
|
27
|
+
/** If the LLM was attempted but fell back, why. Empty otherwise. */
|
|
28
|
+
fallbackReason: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The entry point used by `ask`. Chooses the LLM strategy when configured and
|
|
32
|
+
* reachable, otherwise the heuristic. Never throws — any LLM failure degrades
|
|
33
|
+
* to the heuristic with a recorded reason.
|
|
34
|
+
*/
|
|
35
|
+
export declare function selectFilesWithStrategy(taskDescription: string, config: ProjectConfig, opts?: {
|
|
36
|
+
forceHeuristic?: boolean;
|
|
37
|
+
}): Promise<SelectionResult>;
|
|
38
|
+
/** Walk every source file under the configured source dirs (uncapped). */
|
|
39
|
+
export declare function walkAllSourceFiles(config: ProjectConfig): string[];
|
|
40
|
+
/**
|
|
41
|
+
* Walk source dirs, scoring each candidate file against the task description.
|
|
42
|
+
* Returns up to MAX_RESULTS files sorted by descending score. (Heuristic.)
|
|
43
|
+
*/
|
|
44
|
+
export declare function selectFiles(taskDescription: string, config: ProjectConfig): string[];
|
|
45
|
+
/** Score a single file against task keywords. Higher is better. */
|
|
46
|
+
export declare function scoreFile(filepath: string, keywords: string[]): number;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File selection — pick the files most likely relevant to a task.
|
|
3
|
+
*
|
|
4
|
+
* Two strategies:
|
|
5
|
+
* - heuristic (v0.1): filename match + content keyword + recency. Always
|
|
6
|
+
* available, synchronous, no dependencies.
|
|
7
|
+
* - llm (v0.2): a local model reasons about which files matter. Used only
|
|
8
|
+
* when Ollama is enabled in project.json AND reachable. Falls back to
|
|
9
|
+
* the heuristic on ANY failure.
|
|
10
|
+
*
|
|
11
|
+
* `selectFilesWithStrategy` is the entry point the `ask` command uses. The
|
|
12
|
+
* old synchronous `selectFiles` (heuristic) is preserved as the fallback and
|
|
13
|
+
* for callers that want the heuristic explicitly.
|
|
14
|
+
*/
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
import { isOllamaReachable, OllamaError } from './ollama.js';
|
|
18
|
+
import { llmSelectFiles } from './llmSelector.js';
|
|
19
|
+
const IGNORE_DIRS = new Set([
|
|
20
|
+
'.git', '.venv', 'venv', 'env', 'node_modules', '__pycache__',
|
|
21
|
+
'.pytest_cache', '.mypy_cache', 'dist', 'build', '.next',
|
|
22
|
+
'.idea', '.vscode', '.helpcode', 'coverage', '.cache',
|
|
23
|
+
]);
|
|
24
|
+
const SOURCE_EXTS = new Set([
|
|
25
|
+
'.py', '.js', '.ts', '.jsx', '.tsx', '.sh', '.rb', '.go',
|
|
26
|
+
'.java', '.rs', '.c', '.cpp', '.h', '.hpp',
|
|
27
|
+
]);
|
|
28
|
+
const MAX_RESULTS = 6;
|
|
29
|
+
const RECENCY_WINDOW_DAYS = 14;
|
|
30
|
+
const LLM_CANDIDATE_CAP = 60; // cap files sent to the model (design §3.1)
|
|
31
|
+
/**
|
|
32
|
+
* The entry point used by `ask`. Chooses the LLM strategy when configured and
|
|
33
|
+
* reachable, otherwise the heuristic. Never throws — any LLM failure degrades
|
|
34
|
+
* to the heuristic with a recorded reason.
|
|
35
|
+
*/
|
|
36
|
+
export async function selectFilesWithStrategy(taskDescription, config, opts = {}) {
|
|
37
|
+
const ollama = config.ollama;
|
|
38
|
+
const wantLlm = !opts.forceHeuristic && ollama?.enabled === true;
|
|
39
|
+
if (wantLlm && ollama) {
|
|
40
|
+
const reachable = await isOllamaReachable(ollama.host, { timeoutMs: 1000 });
|
|
41
|
+
if (!reachable) {
|
|
42
|
+
return heuristicResult(taskDescription, config, 'Ollama not reachable');
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const allFiles = walkAllSourceFiles(config).slice(0, LLM_CANDIDATE_CAP);
|
|
46
|
+
const selections = await llmSelectFiles(taskDescription, allFiles, config.root, {
|
|
47
|
+
host: ollama.host,
|
|
48
|
+
model: ollama.model,
|
|
49
|
+
count: MAX_RESULTS,
|
|
50
|
+
timeoutMs: ollama.timeoutMs,
|
|
51
|
+
});
|
|
52
|
+
if (selections.length === 0) {
|
|
53
|
+
return heuristicResult(taskDescription, config, 'model returned no usable files');
|
|
54
|
+
}
|
|
55
|
+
// Map relative paths back to absolute for the caller
|
|
56
|
+
const files = selections.map(s => ({
|
|
57
|
+
filepath: path.join(config.root, s.path),
|
|
58
|
+
reason: s.reason,
|
|
59
|
+
}));
|
|
60
|
+
return { files, strategy: 'llm', fallbackReason: '' };
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
const why = e instanceof OllamaError ? e.message : e.message;
|
|
64
|
+
return heuristicResult(taskDescription, config, why);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return heuristicResult(taskDescription, config, '');
|
|
68
|
+
}
|
|
69
|
+
function heuristicResult(taskDescription, config, fallbackReason) {
|
|
70
|
+
const files = selectFiles(taskDescription, config).map(filepath => ({
|
|
71
|
+
filepath,
|
|
72
|
+
reason: 'matched keywords / recently modified',
|
|
73
|
+
}));
|
|
74
|
+
return { files, strategy: 'heuristic', fallbackReason };
|
|
75
|
+
}
|
|
76
|
+
/** Walk every source file under the configured source dirs (uncapped). */
|
|
77
|
+
export function walkAllSourceFiles(config) {
|
|
78
|
+
const out = [];
|
|
79
|
+
for (const dir of config.sourceDirs) {
|
|
80
|
+
const start = dir === '.' ? config.root : path.join(config.root, dir);
|
|
81
|
+
if (!fs.existsSync(start))
|
|
82
|
+
continue;
|
|
83
|
+
walk(start, f => out.push(f));
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Walk source dirs, scoring each candidate file against the task description.
|
|
89
|
+
* Returns up to MAX_RESULTS files sorted by descending score. (Heuristic.)
|
|
90
|
+
*/
|
|
91
|
+
export function selectFiles(taskDescription, config) {
|
|
92
|
+
const keywords = extractKeywords(taskDescription);
|
|
93
|
+
const candidates = [];
|
|
94
|
+
for (const dir of config.sourceDirs) {
|
|
95
|
+
const start = path.join(config.root, dir);
|
|
96
|
+
if (!fs.existsSync(start))
|
|
97
|
+
continue;
|
|
98
|
+
walk(start, file => {
|
|
99
|
+
const score = scoreFile(file, keywords);
|
|
100
|
+
if (score > 0)
|
|
101
|
+
candidates.push({ filepath: file, score });
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
candidates.sort((a, b) => b.score - a.score);
|
|
105
|
+
return candidates.slice(0, MAX_RESULTS).map(c => c.filepath);
|
|
106
|
+
}
|
|
107
|
+
/** Score a single file against task keywords. Higher is better. */
|
|
108
|
+
export function scoreFile(filepath, keywords) {
|
|
109
|
+
let score = 0;
|
|
110
|
+
let content = '';
|
|
111
|
+
try {
|
|
112
|
+
content = fs.readFileSync(filepath, 'utf-8');
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// unreadable; rely on filename match only below
|
|
116
|
+
}
|
|
117
|
+
// Skip empty or near-empty files entirely. They add noise to briefs
|
|
118
|
+
// without contributing useful context (think: empty __init__.py).
|
|
119
|
+
if (content.trim().length < 10) {
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
const base = path.basename(filepath).toLowerCase();
|
|
123
|
+
for (const kw of keywords) {
|
|
124
|
+
if (base.includes(kw))
|
|
125
|
+
score += 10;
|
|
126
|
+
}
|
|
127
|
+
const lowerContent = content.toLowerCase();
|
|
128
|
+
for (const kw of keywords) {
|
|
129
|
+
const matches = countOccurrences(lowerContent, kw);
|
|
130
|
+
if (matches > 0)
|
|
131
|
+
score += Math.min(matches, 5); // diminishing returns
|
|
132
|
+
}
|
|
133
|
+
// Recency bonus
|
|
134
|
+
try {
|
|
135
|
+
const ageDays = (Date.now() - fs.statSync(filepath).mtimeMs) / (1000 * 60 * 60 * 24);
|
|
136
|
+
if (ageDays <= RECENCY_WINDOW_DAYS)
|
|
137
|
+
score += 3;
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// ignore
|
|
141
|
+
}
|
|
142
|
+
return score;
|
|
143
|
+
}
|
|
144
|
+
function extractKeywords(text) {
|
|
145
|
+
// Lowercase, strip punctuation, drop stopwords and very short words
|
|
146
|
+
const stopwords = new Set([
|
|
147
|
+
'the', 'a', 'an', 'and', 'or', 'but', 'is', 'are', 'was', 'were',
|
|
148
|
+
'to', 'of', 'in', 'on', 'at', 'for', 'with', 'by', 'from', 'as',
|
|
149
|
+
'how', 'why', 'what', 'when', 'where', 'do', 'does', 'did', 'i',
|
|
150
|
+
'my', 'me', 'we', 'our', 'this', 'that', 'these', 'those', 'it',
|
|
151
|
+
'be', 'been', 'have', 'has', 'had', 'can', 'should', 'would',
|
|
152
|
+
'fix', 'add', 'make', 'change',
|
|
153
|
+
]);
|
|
154
|
+
return text
|
|
155
|
+
.toLowerCase()
|
|
156
|
+
.replace(/[^a-z0-9\s]/g, ' ')
|
|
157
|
+
.split(/\s+/)
|
|
158
|
+
.filter(w => w.length > 2 && !stopwords.has(w));
|
|
159
|
+
}
|
|
160
|
+
function countOccurrences(haystack, needle) {
|
|
161
|
+
if (!needle)
|
|
162
|
+
return 0;
|
|
163
|
+
let count = 0;
|
|
164
|
+
let idx = 0;
|
|
165
|
+
while ((idx = haystack.indexOf(needle, idx)) !== -1) {
|
|
166
|
+
count++;
|
|
167
|
+
idx += needle.length;
|
|
168
|
+
}
|
|
169
|
+
return count;
|
|
170
|
+
}
|
|
171
|
+
function walk(dir, callback) {
|
|
172
|
+
let entries;
|
|
173
|
+
try {
|
|
174
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
for (const entry of entries) {
|
|
180
|
+
const full = path.join(dir, entry.name);
|
|
181
|
+
if (entry.isDirectory()) {
|
|
182
|
+
if (IGNORE_DIRS.has(entry.name) || entry.name.startsWith('.'))
|
|
183
|
+
continue;
|
|
184
|
+
walk(full, callback);
|
|
185
|
+
}
|
|
186
|
+
else if (entry.isFile()) {
|
|
187
|
+
if (SOURCE_EXTS.has(path.extname(entry.name))) {
|
|
188
|
+
callback(full);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=selector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selector.js","sourceRoot":"","sources":["../../../src/core/selector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
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 { AgentState, CurrentTask } from '../types.js';
|
|
8
|
+
export declare function loadState(cwd?: string): AgentState;
|
|
9
|
+
export declare function saveState(state: AgentState, cwd?: string): void;
|
|
10
|
+
export declare function createTask(description: string): CurrentTask;
|
|
11
|
+
export declare function resetState(cwd?: string): void;
|