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/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('AI Coding Agent Orchestrator - Orchestrate AI agents to implement features based on PRDs')
25
- .version('0.1.0')
26
- .addHelpText('after', `
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 (prd.status === 'in progress' && prd.completedCount !== undefined && prd.totalCount !== undefined) {
111
- console.log(` Status: ${prd.status} (${prd.completedCount}/${prd.totalCount} completed)`);
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()