hone-ai 0.5.0 ā 0.10.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 +5 -2
- 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/index.ts
CHANGED
|
@@ -1,29 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import { Command } from 'commander'
|
|
3
|
-
import { loadConfig, ensurePlansDir, resolveAgent, initProject } from './config'
|
|
4
|
-
import type { AgentType } from './config'
|
|
5
|
-
import { listPrds } from './prds'
|
|
6
|
-
import { listIncompleteTaskFiles } from './status'
|
|
7
|
-
import { generatePRD } from './prd-generator'
|
|
8
|
-
import { generateTasksFromPRD } from './task-generator'
|
|
9
|
-
import { setVerbose } from './logger'
|
|
2
|
+
import { Command } from 'commander'
|
|
3
|
+
import { loadConfig, ensurePlansDir, resolveAgent, initProject } from './config'
|
|
4
|
+
import type { AgentType } from './config'
|
|
5
|
+
import { listPrds } from './prds'
|
|
6
|
+
import { listIncompleteTaskFiles } from './status'
|
|
7
|
+
import { generatePRD } from './prd-generator'
|
|
8
|
+
import { generateTasksFromPRD } from './task-generator'
|
|
9
|
+
import { setVerbose } from './logger'
|
|
10
|
+
import packageJson from '../package.json'
|
|
10
11
|
|
|
11
|
-
const program = new Command()
|
|
12
|
+
const program = new Command()
|
|
12
13
|
|
|
13
14
|
// Get command name to avoid auto-init on 'init' command
|
|
14
|
-
const isInitCommand = process.argv[2] === 'init'
|
|
15
|
+
const isInitCommand = process.argv[2] === 'init'
|
|
15
16
|
|
|
16
17
|
// Auto-initialize for all commands except 'init'
|
|
17
18
|
if (!isInitCommand) {
|
|
18
|
-
ensurePlansDir()
|
|
19
|
-
loadConfig().catch(console.error)
|
|
19
|
+
ensurePlansDir()
|
|
20
|
+
loadConfig().catch(console.error)
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
program
|
|
23
24
|
.name('hone')
|
|
24
|
-
.description(
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
.description(
|
|
26
|
+
'AI Coding Agent Orchestrator - Orchestrate AI agents to implement features based on PRDs'
|
|
27
|
+
)
|
|
28
|
+
.version(packageJson.version, '-v, --version', 'output the current version')
|
|
29
|
+
.addHelpText(
|
|
30
|
+
'after',
|
|
31
|
+
`
|
|
27
32
|
Model Configuration:
|
|
28
33
|
Configure models in .plans/hone.config.yml:
|
|
29
34
|
|
|
@@ -35,14 +40,16 @@ Model Configuration:
|
|
|
35
40
|
implement: claude-opus-4-20250514 # Override for implementation (optional)
|
|
36
41
|
review: claude-sonnet-4-20250514 # Override for review (optional)
|
|
37
42
|
finalize: claude-sonnet-4-20250514 # Override for finalization (optional)
|
|
43
|
+
agentsMd: claude-sonnet-4-20250514 # Override for AGENTS.md generation (optional)
|
|
38
44
|
|
|
39
45
|
Phase-specific models are optional and override agent-specific models.
|
|
40
46
|
Check available models: opencode --help or claude --help
|
|
41
|
-
`
|
|
47
|
+
`
|
|
48
|
+
)
|
|
42
49
|
|
|
43
50
|
// Global flags
|
|
44
|
-
program.option('--agent <type>', 'Override default agent (opencode or claude)')
|
|
45
|
-
program.option('--verbose', 'Show detailed agent interaction logs')
|
|
51
|
+
program.option('--agent <type>', 'Override default agent (opencode or claude)')
|
|
52
|
+
program.option('--verbose', 'Show detailed agent interaction logs')
|
|
46
53
|
|
|
47
54
|
// Commands
|
|
48
55
|
program
|
|
@@ -50,127 +57,131 @@ program
|
|
|
50
57
|
.description('Initialize hone in current directory')
|
|
51
58
|
.action(async () => {
|
|
52
59
|
try {
|
|
53
|
-
const result = await initProject()
|
|
54
|
-
|
|
60
|
+
const result = await initProject()
|
|
61
|
+
|
|
55
62
|
if (!result.plansCreated && !result.configCreated) {
|
|
56
|
-
console.log('hone is already initialized in this directory.')
|
|
57
|
-
console.log('')
|
|
58
|
-
console.log(' .plans/ directory: exists')
|
|
59
|
-
console.log(' config file: exists')
|
|
60
|
-
return
|
|
63
|
+
console.log('hone is already initialized in this directory.')
|
|
64
|
+
console.log('')
|
|
65
|
+
console.log(' .plans/ directory: exists')
|
|
66
|
+
console.log(' config file: exists')
|
|
67
|
+
return
|
|
61
68
|
}
|
|
62
|
-
|
|
63
|
-
console.log('Initialized hone successfully!')
|
|
64
|
-
console.log('')
|
|
65
|
-
|
|
69
|
+
|
|
70
|
+
console.log('Initialized hone successfully!')
|
|
71
|
+
console.log('')
|
|
72
|
+
|
|
66
73
|
if (result.plansCreated) {
|
|
67
|
-
console.log(' ā Created .plans/ directory')
|
|
74
|
+
console.log(' ā Created .plans/ directory')
|
|
68
75
|
} else {
|
|
69
|
-
console.log(' ⢠.plans/ directory already exists')
|
|
76
|
+
console.log(' ⢠.plans/ directory already exists')
|
|
70
77
|
}
|
|
71
|
-
|
|
78
|
+
|
|
72
79
|
if (result.configCreated) {
|
|
73
|
-
console.log(' ā Created .plans/hone.config.yml')
|
|
80
|
+
console.log(' ā Created .plans/hone.config.yml')
|
|
74
81
|
} else {
|
|
75
|
-
console.log(' ⢠.plans/hone.config.yml already exists')
|
|
82
|
+
console.log(' ⢠.plans/hone.config.yml already exists')
|
|
76
83
|
}
|
|
77
|
-
|
|
78
|
-
console.log('')
|
|
79
|
-
console.log('Next steps:')
|
|
80
|
-
console.log(' 1. Install opencode or claude CLI (hone uses agent subprocesses)')
|
|
81
|
-
console.log(' 2. Generate a PRD: hone prd "your feature description"')
|
|
82
|
-
console.log(' 3. Generate tasks: hone prd-to-tasks .plans/prd-<feature>.md')
|
|
83
|
-
console.log(' 4. Execute tasks: hone run .plans/tasks-<feature>.yml -i 5')
|
|
84
|
+
|
|
85
|
+
console.log('')
|
|
86
|
+
console.log('Next steps:')
|
|
87
|
+
console.log(' 1. Install opencode or claude CLI (hone uses agent subprocesses)')
|
|
88
|
+
console.log(' 2. Generate a PRD: hone prd "your feature description"')
|
|
89
|
+
console.log(' 3. Generate tasks: hone prd-to-tasks .plans/prd-<feature>.md')
|
|
90
|
+
console.log(' 4. Execute tasks: hone run .plans/tasks-<feature>.yml -i 5')
|
|
84
91
|
} catch (error) {
|
|
85
|
-
console.error('\nā Error initializing hone:', error instanceof Error ? error.message : error)
|
|
86
|
-
process.exit(1)
|
|
92
|
+
console.error('\nā Error initializing hone:', error instanceof Error ? error.message : error)
|
|
93
|
+
process.exit(1)
|
|
87
94
|
}
|
|
88
|
-
})
|
|
95
|
+
})
|
|
89
96
|
|
|
90
97
|
program
|
|
91
98
|
.command('prds')
|
|
92
99
|
.description('List all PRDs in .plans/ directory')
|
|
93
100
|
.action(async () => {
|
|
94
|
-
const prds = await listPrds()
|
|
95
|
-
|
|
101
|
+
const prds = await listPrds()
|
|
102
|
+
|
|
96
103
|
if (prds.length === 0) {
|
|
97
|
-
console.log('No PRDs found in .plans/')
|
|
98
|
-
console.log('')
|
|
99
|
-
console.log('Create a PRD with: hone prd "your feature description"')
|
|
100
|
-
return
|
|
104
|
+
console.log('No PRDs found in .plans/')
|
|
105
|
+
console.log('')
|
|
106
|
+
console.log('Create a PRD with: hone prd "your feature description"')
|
|
107
|
+
return
|
|
101
108
|
}
|
|
102
|
-
|
|
103
|
-
console.log('PRDs in .plans/')
|
|
104
|
-
console.log('')
|
|
105
|
-
|
|
109
|
+
|
|
110
|
+
console.log('PRDs in .plans/')
|
|
111
|
+
console.log('')
|
|
112
|
+
|
|
106
113
|
for (const prd of prds) {
|
|
107
|
-
console.log(` .plans/${prd.filename}`)
|
|
108
|
-
console.log(` Tasks: ${prd.taskFile ? `.plans/${prd.taskFile}` : 'none'}`)
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
|
|
114
|
+
console.log(` .plans/${prd.filename}`)
|
|
115
|
+
console.log(` Tasks: ${prd.taskFile ? `.plans/${prd.taskFile}` : 'none'}`)
|
|
116
|
+
|
|
117
|
+
if (
|
|
118
|
+
prd.status === 'in progress' &&
|
|
119
|
+
prd.completedCount !== undefined &&
|
|
120
|
+
prd.totalCount !== undefined
|
|
121
|
+
) {
|
|
122
|
+
console.log(` Status: ${prd.status} (${prd.completedCount}/${prd.totalCount} completed)`)
|
|
112
123
|
} else {
|
|
113
|
-
console.log(` Status: ${prd.status}`)
|
|
124
|
+
console.log(` Status: ${prd.status}`)
|
|
114
125
|
}
|
|
115
|
-
console.log('')
|
|
126
|
+
console.log('')
|
|
116
127
|
}
|
|
117
|
-
})
|
|
128
|
+
})
|
|
118
129
|
|
|
119
130
|
program
|
|
120
131
|
.command('status')
|
|
121
132
|
.description('Show task status for incomplete task lists')
|
|
122
133
|
.action(async () => {
|
|
123
|
-
const taskFiles = await listIncompleteTaskFiles()
|
|
124
|
-
|
|
134
|
+
const taskFiles = await listIncompleteTaskFiles()
|
|
135
|
+
|
|
125
136
|
if (taskFiles.length === 0) {
|
|
126
|
-
console.log('No incomplete task lists found.')
|
|
127
|
-
console.log('')
|
|
128
|
-
console.log('All tasks completed! š')
|
|
129
|
-
return
|
|
137
|
+
console.log('No incomplete task lists found.')
|
|
138
|
+
console.log('')
|
|
139
|
+
console.log('All tasks completed! š')
|
|
140
|
+
return
|
|
130
141
|
}
|
|
131
|
-
|
|
132
|
-
console.log('Incomplete task lists:')
|
|
133
|
-
console.log('')
|
|
134
|
-
|
|
142
|
+
|
|
143
|
+
console.log('Incomplete task lists:')
|
|
144
|
+
console.log('')
|
|
145
|
+
|
|
135
146
|
for (const taskFile of taskFiles) {
|
|
136
|
-
console.log(` .plans/${taskFile.filename}`)
|
|
137
|
-
console.log(` Feature: ${taskFile.feature}`)
|
|
138
|
-
console.log(` Progress: ${taskFile.completedCount}/${taskFile.totalCount} tasks completed`)
|
|
139
|
-
|
|
147
|
+
console.log(` .plans/${taskFile.filename}`)
|
|
148
|
+
console.log(` Feature: ${taskFile.feature}`)
|
|
149
|
+
console.log(` Progress: ${taskFile.completedCount}/${taskFile.totalCount} tasks completed`)
|
|
150
|
+
|
|
140
151
|
if (taskFile.nextTask) {
|
|
141
|
-
console.log(` Next: ${taskFile.nextTask.id} - ${taskFile.nextTask.title}`)
|
|
152
|
+
console.log(` Next: ${taskFile.nextTask.id} - ${taskFile.nextTask.title}`)
|
|
142
153
|
} else {
|
|
143
|
-
console.log(` Next: (waiting for dependencies)`)
|
|
154
|
+
console.log(` Next: (waiting for dependencies)`)
|
|
144
155
|
}
|
|
145
|
-
console.log('')
|
|
156
|
+
console.log('')
|
|
146
157
|
}
|
|
147
|
-
})
|
|
158
|
+
})
|
|
148
159
|
|
|
149
160
|
program
|
|
150
161
|
.command('prd <description>')
|
|
151
|
-
.description('Generate PRD interactively from feature description')
|
|
162
|
+
.description('Generate PRD interactively from feature description (supports file paths and URLs)')
|
|
152
163
|
.action(async (description: string) => {
|
|
153
164
|
try {
|
|
154
|
-
setVerbose(program.opts().verbose || false)
|
|
155
|
-
await generatePRD(description)
|
|
165
|
+
setVerbose(program.opts().verbose || false)
|
|
166
|
+
await generatePRD(description)
|
|
156
167
|
} catch (error) {
|
|
157
|
-
console.error('\nā Error generating PRD:', error instanceof Error ? error.message : error)
|
|
158
|
-
process.exit(1)
|
|
168
|
+
console.error('\nā Error generating PRD:', error instanceof Error ? error.message : error)
|
|
169
|
+
process.exit(1)
|
|
159
170
|
}
|
|
160
|
-
})
|
|
171
|
+
})
|
|
161
172
|
|
|
162
173
|
program
|
|
163
174
|
.command('prd-to-tasks <prd-file>')
|
|
164
175
|
.description('Generate task list from PRD file')
|
|
165
176
|
.action(async (prdFile: string) => {
|
|
166
177
|
try {
|
|
167
|
-
setVerbose(program.opts().verbose || false)
|
|
168
|
-
await generateTasksFromPRD(prdFile)
|
|
178
|
+
setVerbose(program.opts().verbose || false)
|
|
179
|
+
await generateTasksFromPRD(prdFile)
|
|
169
180
|
} catch (error) {
|
|
170
|
-
console.error('\nā Error generating tasks:', error instanceof Error ? error.message : error)
|
|
171
|
-
process.exit(1)
|
|
181
|
+
console.error('\nā Error generating tasks:', error instanceof Error ? error.message : error)
|
|
182
|
+
process.exit(1)
|
|
172
183
|
}
|
|
173
|
-
})
|
|
184
|
+
})
|
|
174
185
|
|
|
175
186
|
program
|
|
176
187
|
.command('run <tasks-file>')
|
|
@@ -179,19 +190,71 @@ program
|
|
|
179
190
|
.option('--skip <phase>', 'Skip a phase (e.g., review)')
|
|
180
191
|
.action(async (tasksFile: string, options: { iterations: string; skip?: string }) => {
|
|
181
192
|
try {
|
|
182
|
-
setVerbose(program.opts().verbose || false)
|
|
183
|
-
const agent = await resolveAgent(program.opts().agent)
|
|
184
|
-
const { executeTasks } = await import('./run')
|
|
193
|
+
setVerbose(program.opts().verbose || false)
|
|
194
|
+
const agent = await resolveAgent(program.opts().agent)
|
|
195
|
+
const { executeTasks } = await import('./run')
|
|
185
196
|
await executeTasks({
|
|
186
197
|
tasksFile,
|
|
187
198
|
iterations: parseInt(options.iterations, 10),
|
|
188
199
|
agent,
|
|
189
|
-
skipPhase: options.skip as 'review' | undefined
|
|
190
|
-
})
|
|
200
|
+
skipPhase: options.skip as 'review' | undefined,
|
|
201
|
+
})
|
|
191
202
|
} catch (error) {
|
|
192
|
-
console.error('\nā Error executing tasks:', error instanceof Error ? error.message : error)
|
|
193
|
-
process.exit(1)
|
|
203
|
+
console.error('\nā Error executing tasks:', error instanceof Error ? error.message : error)
|
|
204
|
+
process.exit(1)
|
|
194
205
|
}
|
|
195
|
-
})
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
program
|
|
209
|
+
.command('agents-md')
|
|
210
|
+
.description('Generate AGENTS.md documentation for the current project')
|
|
211
|
+
.option('--overwrite', 'Overwrite existing AGENTS.md file if it exists')
|
|
212
|
+
.action(async (options: { overwrite?: boolean }) => {
|
|
213
|
+
try {
|
|
214
|
+
setVerbose(program.opts().verbose || false)
|
|
215
|
+
const agent = await resolveAgent(program.opts().agent)
|
|
216
|
+
const { generateAgentsMd } = await import('./agents-md-generator')
|
|
217
|
+
const result = await generateAgentsMd({ overwrite: options.overwrite, agent })
|
|
218
|
+
|
|
219
|
+
if (!result.success) {
|
|
220
|
+
if (result.error?.message.includes('already exists')) {
|
|
221
|
+
console.error('\nā AGENTS.md already exists')
|
|
222
|
+
console.error('\nUse --overwrite to replace the existing file.')
|
|
223
|
+
console.error('Or review the current AGENTS.md before regenerating.')
|
|
224
|
+
} else {
|
|
225
|
+
console.error('\nā Failed to generate AGENTS.md')
|
|
226
|
+
console.error(`\nError: ${result.error?.message || 'Unknown error'}`)
|
|
227
|
+
}
|
|
228
|
+
process.exit(1)
|
|
229
|
+
}
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.error('\nā Failed to generate AGENTS.md')
|
|
232
|
+
console.error(`\nError: ${error instanceof Error ? error.message : error}`)
|
|
233
|
+
process.exit(1)
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
// Handle unknown commands and options by showing help
|
|
238
|
+
program.configureOutput({
|
|
239
|
+
outputError: (str, write) => {
|
|
240
|
+
// Suppress error messages for unknown commands/options since we show help instead
|
|
241
|
+
if (!str.includes('unknown option') && !str.includes('unknown command')) {
|
|
242
|
+
write(str)
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
program.exitOverride(err => {
|
|
248
|
+
if (err.code === 'commander.unknownOption' || err.code === 'commander.unknownCommand') {
|
|
249
|
+
program.outputHelp()
|
|
250
|
+
process.exit(0)
|
|
251
|
+
}
|
|
252
|
+
// Re-throw all other errors to maintain normal behavior
|
|
253
|
+
if (err.exitCode === 0) {
|
|
254
|
+
// For normal exits (like --version, --help), just exit normally
|
|
255
|
+
process.exit(0)
|
|
256
|
+
}
|
|
257
|
+
throw err
|
|
258
|
+
})
|
|
196
259
|
|
|
197
|
-
program.parse()
|
|
260
|
+
program.parse()
|