hone-ai 0.2.0 → 0.9.0
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/README.md +47 -2
- package/package.json +8 -5
- package/src/agent-client.integration.test.ts +57 -59
- package/src/agent-client.test.ts +27 -27
- package/src/agent-client.ts +109 -77
- package/src/agent.test.ts +16 -16
- package/src/agent.ts +103 -103
- package/src/agents-md-generator.test.ts +360 -0
- package/src/agents-md-generator.ts +900 -0
- package/src/config.test.ts +209 -224
- package/src/config.ts +84 -83
- package/src/errors.test.ts +211 -208
- package/src/errors.ts +107 -101
- package/src/index.integration.test.ts +327 -223
- package/src/index.ts +163 -100
- package/src/integration-test.ts +168 -137
- package/src/logger.test.ts +67 -67
- package/src/logger.ts +8 -8
- package/src/prd-generator.integration.test.ts +50 -50
- package/src/prd-generator.test.ts +66 -25
- package/src/prd-generator.ts +280 -194
- package/src/prds.test.ts +60 -65
- package/src/prds.ts +64 -62
- package/src/prompt.test.ts +154 -155
- package/src/prompt.ts +63 -65
- package/src/run.ts +147 -147
- package/src/status.test.ts +80 -80
- package/src/status.ts +40 -42
- package/src/task-generator.test.ts +93 -66
- package/src/task-generator.ts +125 -112
package/src/prompt.test.ts
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { describe, expect, test, beforeEach, afterEach } from 'bun:test'
|
|
2
|
-
import { mkdirSync, writeFileSync, rmSync, existsSync } from 'fs'
|
|
3
|
-
import { join } from 'path'
|
|
4
|
-
import { constructPrompt, type PromptPhase } from './prompt'
|
|
5
|
-
import type { HoneConfig } from './config'
|
|
1
|
+
import { describe, expect, test, beforeEach, afterEach } from 'bun:test'
|
|
2
|
+
import { mkdirSync, writeFileSync, rmSync, existsSync } from 'fs'
|
|
3
|
+
import { join } from 'path'
|
|
4
|
+
import { constructPrompt, type PromptPhase } from './prompt'
|
|
5
|
+
import type { HoneConfig } from './config'
|
|
6
6
|
|
|
7
|
-
const TEST_WORKSPACE = join(process.cwd(), '.test-prompt-workspace')
|
|
8
|
-
const TEST_PLANS_DIR = join(TEST_WORKSPACE, '.plans')
|
|
7
|
+
const TEST_WORKSPACE = join(process.cwd(), '.test-prompt-workspace')
|
|
8
|
+
const TEST_PLANS_DIR = join(TEST_WORKSPACE, '.plans')
|
|
9
9
|
|
|
10
10
|
beforeEach(() => {
|
|
11
11
|
// Create isolated test workspace
|
|
12
12
|
if (existsSync(TEST_WORKSPACE)) {
|
|
13
|
-
rmSync(TEST_WORKSPACE, { recursive: true, force: true })
|
|
13
|
+
rmSync(TEST_WORKSPACE, { recursive: true, force: true })
|
|
14
14
|
}
|
|
15
|
-
mkdirSync(TEST_PLANS_DIR, { recursive: true })
|
|
16
|
-
|
|
15
|
+
mkdirSync(TEST_PLANS_DIR, { recursive: true })
|
|
16
|
+
|
|
17
17
|
// Change to test workspace
|
|
18
|
-
process.chdir(TEST_WORKSPACE)
|
|
19
|
-
})
|
|
18
|
+
process.chdir(TEST_WORKSPACE)
|
|
19
|
+
})
|
|
20
20
|
|
|
21
21
|
afterEach(() => {
|
|
22
22
|
// Clean up and return to original directory
|
|
23
|
-
const originalDir = join(TEST_WORKSPACE, '..')
|
|
24
|
-
process.chdir(originalDir)
|
|
23
|
+
const originalDir = join(TEST_WORKSPACE, '..')
|
|
24
|
+
process.chdir(originalDir)
|
|
25
25
|
if (existsSync(TEST_WORKSPACE)) {
|
|
26
|
-
rmSync(TEST_WORKSPACE, { recursive: true, force: true })
|
|
26
|
+
rmSync(TEST_WORKSPACE, { recursive: true, force: true })
|
|
27
27
|
}
|
|
28
|
-
})
|
|
28
|
+
})
|
|
29
29
|
|
|
30
30
|
const mockConfig: HoneConfig = {
|
|
31
31
|
defaultAgent: 'claude',
|
|
32
32
|
models: {
|
|
33
33
|
opencode: 'claude-sonnet-4-20250514',
|
|
34
|
-
claude: 'claude-sonnet-4-20250514'
|
|
34
|
+
claude: 'claude-sonnet-4-20250514',
|
|
35
35
|
},
|
|
36
36
|
feedbackInstructions: 'test: bun test',
|
|
37
|
-
lintCommand: 'bun run lint'
|
|
38
|
-
}
|
|
37
|
+
lintCommand: 'bun run lint',
|
|
38
|
+
}
|
|
39
39
|
|
|
40
40
|
describe('constructPrompt', () => {
|
|
41
41
|
test('includes phase header', () => {
|
|
@@ -43,239 +43,238 @@ describe('constructPrompt', () => {
|
|
|
43
43
|
writeFileSync(
|
|
44
44
|
join(TEST_PLANS_DIR, 'tasks-test.yml'),
|
|
45
45
|
'tasks:\n - id: task-001\n status: pending\n'
|
|
46
|
-
)
|
|
47
|
-
|
|
46
|
+
)
|
|
47
|
+
|
|
48
48
|
const prompt = constructPrompt({
|
|
49
49
|
phase: 'implement',
|
|
50
50
|
featureName: 'test',
|
|
51
|
-
config: mockConfig
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
expect(prompt).toContain('# HONE: IMPLEMENT PHASE')
|
|
55
|
-
})
|
|
56
|
-
|
|
51
|
+
config: mockConfig,
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
expect(prompt).toContain('# HONE: IMPLEMENT PHASE')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
57
|
test('includes AGENTS.md reference if exists', () => {
|
|
58
|
-
writeFileSync(join(TEST_WORKSPACE, 'AGENTS.md'), '# Learning 1\nSome pattern')
|
|
58
|
+
writeFileSync(join(TEST_WORKSPACE, 'AGENTS.md'), '# Learning 1\nSome pattern')
|
|
59
59
|
writeFileSync(
|
|
60
60
|
join(TEST_PLANS_DIR, 'tasks-test.yml'),
|
|
61
61
|
'tasks:\n - id: task-001\n status: pending\n'
|
|
62
|
-
)
|
|
63
|
-
|
|
62
|
+
)
|
|
63
|
+
|
|
64
64
|
const prompt = constructPrompt({
|
|
65
65
|
phase: 'implement',
|
|
66
66
|
featureName: 'test',
|
|
67
|
-
config: mockConfig
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
expect(prompt).toContain('# CONTEXT FILES')
|
|
71
|
-
expect(prompt).toContain('@AGENTS.md')
|
|
72
|
-
})
|
|
73
|
-
|
|
67
|
+
config: mockConfig,
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
expect(prompt).toContain('# CONTEXT FILES')
|
|
71
|
+
expect(prompt).toContain('@AGENTS.md')
|
|
72
|
+
})
|
|
73
|
+
|
|
74
74
|
test('includes task file reference', () => {
|
|
75
75
|
writeFileSync(
|
|
76
76
|
join(TEST_PLANS_DIR, 'tasks-test.yml'),
|
|
77
77
|
'tasks:\n - id: task-001\n title: Test Task\n status: pending\n'
|
|
78
|
-
)
|
|
79
|
-
|
|
78
|
+
)
|
|
79
|
+
|
|
80
80
|
const prompt = constructPrompt({
|
|
81
81
|
phase: 'implement',
|
|
82
82
|
featureName: 'test',
|
|
83
|
-
config: mockConfig
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
expect(prompt).toContain('# CONTEXT FILES')
|
|
87
|
-
expect(prompt).toContain('@.plans/tasks-test.yml')
|
|
88
|
-
})
|
|
89
|
-
|
|
83
|
+
config: mockConfig,
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
expect(prompt).toContain('# CONTEXT FILES')
|
|
87
|
+
expect(prompt).toContain('@.plans/tasks-test.yml')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
90
|
test('includes progress file reference if exists', () => {
|
|
91
91
|
writeFileSync(
|
|
92
92
|
join(TEST_PLANS_DIR, 'tasks-test.yml'),
|
|
93
93
|
'tasks:\n - id: task-001\n status: pending\n'
|
|
94
|
-
)
|
|
95
|
-
writeFileSync(
|
|
96
|
-
|
|
97
|
-
'Previous iteration completed'
|
|
98
|
-
);
|
|
99
|
-
|
|
94
|
+
)
|
|
95
|
+
writeFileSync(join(TEST_PLANS_DIR, 'progress-test.txt'), 'Previous iteration completed')
|
|
96
|
+
|
|
100
97
|
const prompt = constructPrompt({
|
|
101
98
|
phase: 'implement',
|
|
102
99
|
featureName: 'test',
|
|
103
|
-
config: mockConfig
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
expect(prompt).toContain('# CONTEXT FILES')
|
|
107
|
-
expect(prompt).toContain('@.plans/progress-test.txt')
|
|
108
|
-
})
|
|
109
|
-
|
|
100
|
+
config: mockConfig,
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
expect(prompt).toContain('# CONTEXT FILES')
|
|
104
|
+
expect(prompt).toContain('@.plans/progress-test.txt')
|
|
105
|
+
})
|
|
106
|
+
|
|
110
107
|
test('implement phase includes task selection instructions', () => {
|
|
111
108
|
writeFileSync(
|
|
112
109
|
join(TEST_PLANS_DIR, 'tasks-test.yml'),
|
|
113
110
|
'tasks:\n - id: task-001\n status: pending\n'
|
|
114
|
-
)
|
|
115
|
-
|
|
111
|
+
)
|
|
112
|
+
|
|
116
113
|
const prompt = constructPrompt({
|
|
117
114
|
phase: 'implement',
|
|
118
115
|
featureName: 'test',
|
|
119
|
-
config: mockConfig
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
expect(prompt).toContain('# TASK SELECTION')
|
|
123
|
-
expect(prompt).toContain('bun test')
|
|
124
|
-
})
|
|
125
|
-
|
|
116
|
+
config: mockConfig,
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
expect(prompt).toContain('# TASK SELECTION')
|
|
120
|
+
expect(prompt).toContain('bun test')
|
|
121
|
+
})
|
|
122
|
+
|
|
126
123
|
test('implement phase includes lint command if configured', () => {
|
|
127
124
|
writeFileSync(
|
|
128
125
|
join(TEST_PLANS_DIR, 'tasks-test.yml'),
|
|
129
126
|
'tasks:\n - id: task-001\n status: pending\n'
|
|
130
|
-
)
|
|
131
|
-
|
|
127
|
+
)
|
|
128
|
+
|
|
132
129
|
const prompt = constructPrompt({
|
|
133
130
|
phase: 'implement',
|
|
134
131
|
featureName: 'test',
|
|
135
|
-
config: mockConfig
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
expect(prompt).toContain('bun run lint')
|
|
139
|
-
})
|
|
140
|
-
|
|
132
|
+
config: mockConfig,
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
expect(prompt).toContain('bun run lint')
|
|
136
|
+
})
|
|
137
|
+
|
|
141
138
|
test('review phase includes review checklist', () => {
|
|
142
139
|
writeFileSync(
|
|
143
140
|
join(TEST_PLANS_DIR, 'tasks-test.yml'),
|
|
144
141
|
'tasks:\n - id: task-001\n status: pending\n'
|
|
145
|
-
)
|
|
146
|
-
|
|
142
|
+
)
|
|
143
|
+
|
|
147
144
|
const prompt = constructPrompt({
|
|
148
145
|
phase: 'review',
|
|
149
146
|
featureName: 'test',
|
|
150
147
|
config: mockConfig,
|
|
151
|
-
taskId: 'task-001'
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
expect(prompt).toContain('# HONE: REVIEW PHASE')
|
|
155
|
-
expect(prompt).toContain('# REVIEW CHECKLIST')
|
|
156
|
-
expect(prompt).toContain('Correctness')
|
|
157
|
-
expect(prompt).toContain('task-001')
|
|
158
|
-
})
|
|
159
|
-
|
|
148
|
+
taskId: 'task-001',
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
expect(prompt).toContain('# HONE: REVIEW PHASE')
|
|
152
|
+
expect(prompt).toContain('# REVIEW CHECKLIST')
|
|
153
|
+
expect(prompt).toContain('Correctness')
|
|
154
|
+
expect(prompt).toContain('task-001')
|
|
155
|
+
})
|
|
156
|
+
|
|
160
157
|
test('finalize phase includes review feedback', () => {
|
|
161
158
|
writeFileSync(
|
|
162
159
|
join(TEST_PLANS_DIR, 'tasks-test.yml'),
|
|
163
160
|
'tasks:\n - id: task-001\n status: pending\n'
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
const feedback = '**Issue**: Missing error handling\n**Suggestion**: Add try-catch'
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
const feedback = '**Issue**: Missing error handling\n**Suggestion**: Add try-catch'
|
|
167
164
|
const prompt = constructPrompt({
|
|
168
165
|
phase: 'finalize',
|
|
169
166
|
featureName: 'test',
|
|
170
167
|
config: mockConfig,
|
|
171
168
|
taskId: 'task-001',
|
|
172
|
-
reviewFeedback: feedback
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
expect(prompt).toContain('# HONE: FINALIZE PHASE')
|
|
176
|
-
expect(prompt).toContain('# REVIEW FEEDBACK')
|
|
177
|
-
expect(prompt).toContain('Missing error handling')
|
|
178
|
-
})
|
|
179
|
-
|
|
169
|
+
reviewFeedback: feedback,
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
expect(prompt).toContain('# HONE: FINALIZE PHASE')
|
|
173
|
+
expect(prompt).toContain('# REVIEW FEEDBACK')
|
|
174
|
+
expect(prompt).toContain('Missing error handling')
|
|
175
|
+
})
|
|
176
|
+
|
|
180
177
|
test('finalize phase without review feedback shows default message', () => {
|
|
181
178
|
writeFileSync(
|
|
182
179
|
join(TEST_PLANS_DIR, 'tasks-test.yml'),
|
|
183
180
|
'tasks:\n - id: task-001\n status: pending\n'
|
|
184
|
-
)
|
|
185
|
-
|
|
181
|
+
)
|
|
182
|
+
|
|
186
183
|
const prompt = constructPrompt({
|
|
187
184
|
phase: 'finalize',
|
|
188
185
|
featureName: 'test',
|
|
189
186
|
config: mockConfig,
|
|
190
|
-
taskId: 'task-001'
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
expect(prompt).toContain('No review feedback provided')
|
|
194
|
-
})
|
|
195
|
-
|
|
187
|
+
taskId: 'task-001',
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
expect(prompt).toContain('No review feedback provided')
|
|
191
|
+
})
|
|
192
|
+
|
|
196
193
|
test('handles missing AGENTS.md gracefully', () => {
|
|
197
194
|
writeFileSync(
|
|
198
195
|
join(TEST_PLANS_DIR, 'tasks-test.yml'),
|
|
199
196
|
'tasks:\n - id: task-001\n status: pending\n'
|
|
200
|
-
)
|
|
201
|
-
|
|
197
|
+
)
|
|
198
|
+
|
|
202
199
|
const prompt = constructPrompt({
|
|
203
200
|
phase: 'implement',
|
|
204
201
|
featureName: 'test',
|
|
205
|
-
config: mockConfig
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
expect(prompt).not.toContain('@AGENTS.md')
|
|
209
|
-
expect(prompt).toContain('# TASK SELECTION')
|
|
210
|
-
})
|
|
211
|
-
|
|
202
|
+
config: mockConfig,
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
expect(prompt).not.toContain('@AGENTS.md')
|
|
206
|
+
expect(prompt).toContain('# TASK SELECTION')
|
|
207
|
+
})
|
|
208
|
+
|
|
212
209
|
test('handles missing progress file gracefully', () => {
|
|
213
210
|
writeFileSync(
|
|
214
211
|
join(TEST_PLANS_DIR, 'tasks-test.yml'),
|
|
215
212
|
'tasks:\n - id: task-001\n status: pending\n'
|
|
216
|
-
)
|
|
217
|
-
|
|
213
|
+
)
|
|
214
|
+
|
|
218
215
|
const prompt = constructPrompt({
|
|
219
216
|
phase: 'implement',
|
|
220
217
|
featureName: 'test',
|
|
221
|
-
config: mockConfig
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
expect(prompt).not.toContain('progress-test.txt')
|
|
225
|
-
expect(prompt).toContain('# TASK SELECTION')
|
|
226
|
-
})
|
|
227
|
-
|
|
218
|
+
config: mockConfig,
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
expect(prompt).not.toContain('progress-test.txt')
|
|
222
|
+
expect(prompt).toContain('# TASK SELECTION')
|
|
223
|
+
})
|
|
224
|
+
|
|
228
225
|
test('uses default feedback instructions if not configured', () => {
|
|
229
226
|
writeFileSync(
|
|
230
227
|
join(TEST_PLANS_DIR, 'tasks-test.yml'),
|
|
231
228
|
'tasks:\n - id: task-001\n status: pending\n'
|
|
232
|
-
)
|
|
233
|
-
|
|
229
|
+
)
|
|
230
|
+
|
|
234
231
|
const configWithoutFeedback: HoneConfig = {
|
|
235
232
|
...mockConfig,
|
|
236
|
-
feedbackInstructions: undefined
|
|
237
|
-
}
|
|
238
|
-
|
|
233
|
+
feedbackInstructions: undefined,
|
|
234
|
+
}
|
|
235
|
+
|
|
239
236
|
const prompt = constructPrompt({
|
|
240
237
|
phase: 'implement',
|
|
241
238
|
featureName: 'test',
|
|
242
|
-
config: configWithoutFeedback
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
expect(prompt).toContain('test: bun test')
|
|
246
|
-
})
|
|
247
|
-
|
|
239
|
+
config: configWithoutFeedback,
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
expect(prompt).toContain('test: bun test')
|
|
243
|
+
})
|
|
244
|
+
|
|
248
245
|
test('implement phase includes failure handling instructions', () => {
|
|
249
246
|
writeFileSync(
|
|
250
247
|
join(TEST_PLANS_DIR, 'tasks-test.yml'),
|
|
251
248
|
'tasks:\n - id: task-001\n status: pending\n'
|
|
252
|
-
)
|
|
253
|
-
|
|
249
|
+
)
|
|
250
|
+
|
|
254
251
|
const prompt = constructPrompt({
|
|
255
252
|
phase: 'implement',
|
|
256
253
|
featureName: 'test',
|
|
257
|
-
config: mockConfig
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
expect(prompt).toContain('If tests fail or there are errors you cannot fix')
|
|
261
|
-
expect(prompt).toContain('DO NOT output TASK_COMPLETED')
|
|
262
|
-
expect(prompt).toContain('task will remain pending')
|
|
263
|
-
expect(prompt).toContain('Only output this marker if the task is fully complete')
|
|
264
|
-
})
|
|
265
|
-
|
|
254
|
+
config: mockConfig,
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
expect(prompt).toContain('If tests fail or there are errors you cannot fix')
|
|
258
|
+
expect(prompt).toContain('DO NOT output TASK_COMPLETED')
|
|
259
|
+
expect(prompt).toContain('task will remain pending')
|
|
260
|
+
expect(prompt).toContain('Only output this marker if the task is fully complete')
|
|
261
|
+
})
|
|
262
|
+
|
|
266
263
|
test('implement phase enforces task file isolation', () => {
|
|
267
264
|
writeFileSync(
|
|
268
265
|
join(TEST_PLANS_DIR, 'tasks-test.yml'),
|
|
269
266
|
'tasks:\n - id: task-001\n status: pending\n'
|
|
270
|
-
)
|
|
271
|
-
|
|
267
|
+
)
|
|
268
|
+
|
|
272
269
|
const prompt = constructPrompt({
|
|
273
270
|
phase: 'implement',
|
|
274
271
|
featureName: 'test',
|
|
275
|
-
config: mockConfig
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
expect(prompt).toContain(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
272
|
+
config: mockConfig,
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
expect(prompt).toContain(
|
|
276
|
+
'CRITICAL: You MUST only choose tasks from the task file referenced in CONTEXT FILES'
|
|
277
|
+
)
|
|
278
|
+
expect(prompt).toContain('Do NOT select tasks from any other task file')
|
|
279
|
+
})
|
|
280
|
+
})
|
package/src/prompt.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { existsSync } from 'fs'
|
|
2
|
-
import { join, relative } from 'path'
|
|
3
|
-
import { getPlansDir, type HoneConfig } from './config'
|
|
1
|
+
import { existsSync } from 'fs'
|
|
2
|
+
import { join, relative } from 'path'
|
|
3
|
+
import { getPlansDir, type HoneConfig } from './config'
|
|
4
4
|
|
|
5
|
-
export type PromptPhase = 'implement' | 'review' | 'finalize'
|
|
5
|
+
export type PromptPhase = 'implement' | 'review' | 'finalize'
|
|
6
6
|
|
|
7
7
|
export interface PromptOptions {
|
|
8
|
-
phase: PromptPhase
|
|
9
|
-
featureName: string
|
|
10
|
-
config: HoneConfig
|
|
11
|
-
taskId?: string
|
|
12
|
-
reviewFeedback?: string
|
|
8
|
+
phase: PromptPhase
|
|
9
|
+
featureName: string
|
|
10
|
+
config: HoneConfig
|
|
11
|
+
taskId?: string
|
|
12
|
+
reviewFeedback?: string
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -17,49 +17,49 @@ export interface PromptOptions {
|
|
|
17
17
|
* References files using @<path> syntax instead of embedding content.
|
|
18
18
|
*/
|
|
19
19
|
export function constructPrompt(options: PromptOptions): string {
|
|
20
|
-
const { phase, featureName, config, taskId, reviewFeedback } = options
|
|
21
|
-
|
|
22
|
-
const parts: string[] = []
|
|
23
|
-
|
|
20
|
+
const { phase, featureName, config, taskId, reviewFeedback } = options
|
|
21
|
+
|
|
22
|
+
const parts: string[] = []
|
|
23
|
+
|
|
24
24
|
// Build list of file references (relative to project root)
|
|
25
|
-
const fileRefs: string[] = []
|
|
26
|
-
const cwd = process.cwd()
|
|
27
|
-
|
|
25
|
+
const fileRefs: string[] = []
|
|
26
|
+
const cwd = process.cwd()
|
|
27
|
+
|
|
28
28
|
// Add task file reference (required)
|
|
29
|
-
const taskPath = join(getPlansDir(), `tasks-${featureName}.yml`)
|
|
29
|
+
const taskPath = join(getPlansDir(), `tasks-${featureName}.yml`)
|
|
30
30
|
if (existsSync(taskPath)) {
|
|
31
|
-
const relPath = relative(cwd, taskPath)
|
|
32
|
-
fileRefs.push(`@${relPath}`)
|
|
31
|
+
const relPath = relative(cwd, taskPath)
|
|
32
|
+
fileRefs.push(`@${relPath}`)
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
// Add progress file reference if exists
|
|
36
|
-
const progressPath = join(getPlansDir(), `progress-${featureName}.txt`)
|
|
36
|
+
const progressPath = join(getPlansDir(), `progress-${featureName}.txt`)
|
|
37
37
|
if (existsSync(progressPath)) {
|
|
38
|
-
const relPath = relative(cwd, progressPath)
|
|
39
|
-
fileRefs.push(`@${relPath}`)
|
|
38
|
+
const relPath = relative(cwd, progressPath)
|
|
39
|
+
fileRefs.push(`@${relPath}`)
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
// Add AGENTS.md reference if exists
|
|
43
|
-
const agentsPath = join(cwd, 'AGENTS.md')
|
|
43
|
+
const agentsPath = join(cwd, 'AGENTS.md')
|
|
44
44
|
if (existsSync(agentsPath)) {
|
|
45
|
-
fileRefs.push(`@AGENTS.md`)
|
|
45
|
+
fileRefs.push(`@AGENTS.md`)
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
// Add phase-specific header with file references
|
|
49
|
-
parts.push(getPhaseHeader(phase))
|
|
50
|
-
parts.push('')
|
|
51
|
-
|
|
49
|
+
parts.push(getPhaseHeader(phase))
|
|
50
|
+
parts.push('')
|
|
51
|
+
|
|
52
52
|
if (fileRefs.length > 0) {
|
|
53
|
-
parts.push('# CONTEXT FILES')
|
|
54
|
-
parts.push('')
|
|
55
|
-
parts.push(fileRefs.join(' '))
|
|
56
|
-
parts.push('')
|
|
53
|
+
parts.push('# CONTEXT FILES')
|
|
54
|
+
parts.push('')
|
|
55
|
+
parts.push(fileRefs.join(' '))
|
|
56
|
+
parts.push('')
|
|
57
57
|
}
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
// Add phase-specific instructions
|
|
60
|
-
parts.push(getPhaseInstructions(phase, config, taskId, reviewFeedback))
|
|
61
|
-
|
|
62
|
-
return parts.join('\n')
|
|
60
|
+
parts.push(getPhaseInstructions(phase, config, taskId, reviewFeedback))
|
|
61
|
+
|
|
62
|
+
return parts.join('\n')
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
/**
|
|
@@ -68,11 +68,11 @@ export function constructPrompt(options: PromptOptions): string {
|
|
|
68
68
|
function getPhaseHeader(phase: PromptPhase): string {
|
|
69
69
|
switch (phase) {
|
|
70
70
|
case 'implement':
|
|
71
|
-
return '# HONE: IMPLEMENT PHASE'
|
|
71
|
+
return '# HONE: IMPLEMENT PHASE'
|
|
72
72
|
case 'review':
|
|
73
|
-
return '# HONE: REVIEW PHASE'
|
|
73
|
+
return '# HONE: REVIEW PHASE'
|
|
74
74
|
case 'finalize':
|
|
75
|
-
return '# HONE: FINALIZE PHASE'
|
|
75
|
+
return '# HONE: FINALIZE PHASE'
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -85,16 +85,16 @@ function getPhaseInstructions(
|
|
|
85
85
|
taskId?: string,
|
|
86
86
|
reviewFeedback?: string
|
|
87
87
|
): string {
|
|
88
|
-
const feedbackInstructions = config.feedbackInstructions || 'test: bun test'
|
|
89
|
-
const lintCommand = config.lintCommand
|
|
90
|
-
|
|
88
|
+
const feedbackInstructions = config.feedbackInstructions || 'test: bun test'
|
|
89
|
+
const lintCommand = config.lintCommand
|
|
90
|
+
|
|
91
91
|
switch (phase) {
|
|
92
92
|
case 'implement':
|
|
93
|
-
return getImplementInstructions(feedbackInstructions, lintCommand)
|
|
93
|
+
return getImplementInstructions(feedbackInstructions, lintCommand)
|
|
94
94
|
case 'review':
|
|
95
|
-
return getReviewInstructions(taskId)
|
|
95
|
+
return getReviewInstructions(taskId)
|
|
96
96
|
case 'finalize':
|
|
97
|
-
return getFinalizeInstructions(feedbackInstructions, lintCommand, reviewFeedback)
|
|
97
|
+
return getFinalizeInstructions(feedbackInstructions, lintCommand, reviewFeedback)
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -137,12 +137,12 @@ Only run feedback loops AFTER you have fully completed implementing the task.
|
|
|
137
137
|
|
|
138
138
|
Run feedback loops ONLY when the task implementation is complete:
|
|
139
139
|
|
|
140
|
-
${feedbackInstructions}
|
|
140
|
+
${feedbackInstructions}`
|
|
141
141
|
|
|
142
142
|
if (lintCommand) {
|
|
143
|
-
instructions += `\n${lintCommand}
|
|
143
|
+
instructions += `\n${lintCommand}`
|
|
144
144
|
}
|
|
145
|
-
|
|
145
|
+
|
|
146
146
|
instructions += `
|
|
147
147
|
|
|
148
148
|
If CLI or script, run them and verify output.
|
|
@@ -161,9 +161,9 @@ At the end, output on a single line:
|
|
|
161
161
|
TASK_COMPLETED: <task-id>
|
|
162
162
|
|
|
163
163
|
This allows hone to track which task you completed.
|
|
164
|
-
Only output this marker if the task is fully complete and all feedback loops pass
|
|
164
|
+
Only output this marker if the task is fully complete and all feedback loops pass.`
|
|
165
165
|
|
|
166
|
-
return instructions
|
|
166
|
+
return instructions
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
/**
|
|
@@ -198,7 +198,7 @@ Provide specific, actionable feedback. If everything looks good, say "LGTM" (Loo
|
|
|
198
198
|
Structure your feedback as:
|
|
199
199
|
- **Issue**: Description of the problem
|
|
200
200
|
- **Suggestion**: How to fix it
|
|
201
|
-
- **Priority**: critical | high | medium | low
|
|
201
|
+
- **Priority**: critical | high | medium | low`
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
/**
|
|
@@ -213,7 +213,7 @@ function getFinalizeInstructions(
|
|
|
213
213
|
|
|
214
214
|
Finalize the task by applying review feedback and updating all tracking files.
|
|
215
215
|
|
|
216
|
-
IMPORTANT: This is the ONLY phase where git commits are allowed. If you already committed in
|
|
216
|
+
IMPORTANT: This is the ONLY phase where git commits are allowed. If you already committed in
|
|
217
217
|
the implement phase, you did it wrong - but continue with finalization anyway.
|
|
218
218
|
|
|
219
219
|
# REVIEW FEEDBACK
|
|
@@ -227,10 +227,10 @@ ${reviewFeedback || 'No review feedback provided (review was skipped or approved
|
|
|
227
227
|
|
|
228
228
|
2. **Run Final Feedback Loops** (if needed)
|
|
229
229
|
- If you applied feedback or made any changes:
|
|
230
|
-
${feedbackInstructions}
|
|
230
|
+
${feedbackInstructions}`
|
|
231
231
|
|
|
232
232
|
if (lintCommand) {
|
|
233
|
-
instructions += `\n ${lintCommand}
|
|
233
|
+
instructions += `\n ${lintCommand}`
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
instructions += `
|
|
@@ -247,23 +247,23 @@ ${reviewFeedback || 'No review feedback provided (review was skipped or approved
|
|
|
247
247
|
TASK-XXX: <task-title>
|
|
248
248
|
Date: <ISO-8601-datetime>
|
|
249
249
|
================================================================================
|
|
250
|
-
|
|
250
|
+
|
|
251
251
|
Summary:
|
|
252
252
|
<concise summary of what was done>
|
|
253
|
-
|
|
253
|
+
|
|
254
254
|
Files Changed:
|
|
255
255
|
- file1.ts (created/modified/deleted with brief description)
|
|
256
256
|
- file2.ts (...)
|
|
257
|
-
|
|
257
|
+
|
|
258
258
|
Key Decisions:
|
|
259
259
|
- Decision 1
|
|
260
260
|
- Decision 2
|
|
261
|
-
|
|
261
|
+
|
|
262
262
|
Next Task: <next-task-id> or "All tasks complete"
|
|
263
263
|
\`\`\`
|
|
264
264
|
|
|
265
265
|
5. **Update AGENTS.md** (if learnings exist)
|
|
266
|
-
- Add useful learnings and
|
|
266
|
+
- Add useful learnings and gotchas under appropriate heading
|
|
267
267
|
- Be terse - only add truly useful info that future agents need
|
|
268
268
|
- Don't duplicate existing info
|
|
269
269
|
|
|
@@ -287,9 +287,7 @@ If you cannot commit (e.g., no changes to commit), that indicates a problem - in
|
|
|
287
287
|
At the end, output on a single line:
|
|
288
288
|
FINALIZED: <task-id>
|
|
289
289
|
|
|
290
|
-
Only output this marker AFTER you have successfully created the git commit
|
|
290
|
+
Only output this marker AFTER you have successfully created the git commit.`
|
|
291
291
|
|
|
292
|
-
return instructions
|
|
292
|
+
return instructions
|
|
293
293
|
}
|
|
294
|
-
|
|
295
|
-
|