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/src/config.ts CHANGED
@@ -1,85 +1,86 @@
1
- import { existsSync, mkdirSync } from 'fs';
2
- import { readFile, writeFile } from 'fs/promises';
3
- import { join } from 'path';
4
- import * as yaml from 'js-yaml';
5
- import { exitWithError, ErrorMessages } from './errors';
1
+ import { existsSync, mkdirSync } from 'fs'
2
+ import { readFile, writeFile } from 'fs/promises'
3
+ import { join } from 'path'
4
+ import * as yaml from 'js-yaml'
5
+ import { exitWithError, ErrorMessages } from './errors'
6
6
 
7
7
  export interface HoneConfig {
8
- defaultAgent: 'opencode' | 'claude';
8
+ defaultAgent: 'opencode' | 'claude'
9
9
  models: {
10
- opencode: string;
11
- claude: string;
10
+ opencode: string
11
+ claude: string
12
12
  // Phase-specific model overrides (optional)
13
- prd?: string;
14
- prdToTasks?: string;
15
- implement?: string;
16
- review?: string;
17
- finalize?: string;
18
- };
19
- feedbackInstructions?: string;
20
- lintCommand?: string;
13
+ prd?: string
14
+ prdToTasks?: string
15
+ implement?: string
16
+ review?: string
17
+ finalize?: string
18
+ agentsMd?: string
19
+ }
20
+ feedbackInstructions?: string
21
+ lintCommand?: string
21
22
  }
22
23
 
23
24
  const DEFAULT_CONFIG: HoneConfig = {
24
25
  defaultAgent: 'claude',
25
26
  models: {
26
27
  opencode: 'claude-sonnet-4-20250514',
27
- claude: 'claude-sonnet-4-20250514'
28
+ claude: 'claude-sonnet-4-20250514',
28
29
  },
29
30
  feedbackInstructions: 'test: bun test',
30
- lintCommand: undefined
31
- };
31
+ lintCommand: undefined,
32
+ }
32
33
 
33
34
  export function getPlansDir(): string {
34
- return join(process.cwd(), '.plans');
35
+ return join(process.cwd(), '.plans')
35
36
  }
36
37
 
37
38
  export function ensurePlansDir(): void {
38
- const plansDir = getPlansDir();
39
+ const plansDir = getPlansDir()
39
40
  if (!existsSync(plansDir)) {
40
- mkdirSync(plansDir, { recursive: true });
41
+ mkdirSync(plansDir, { recursive: true })
41
42
  }
42
43
  }
43
44
 
44
45
  export function getConfigPath(): string {
45
- return join(getPlansDir(), 'hone.config.yml');
46
+ return join(getPlansDir(), 'hone.config.yml')
46
47
  }
47
48
 
48
49
  export async function loadConfig(): Promise<HoneConfig> {
49
- ensurePlansDir();
50
-
51
- const configPath = getConfigPath();
52
-
50
+ ensurePlansDir()
51
+
52
+ const configPath = getConfigPath()
53
+
53
54
  if (!existsSync(configPath)) {
54
- await writeFile(configPath, yaml.dump(DEFAULT_CONFIG));
55
- return DEFAULT_CONFIG;
55
+ await writeFile(configPath, yaml.dump(DEFAULT_CONFIG))
56
+ return DEFAULT_CONFIG
56
57
  }
57
-
58
+
58
59
  try {
59
- const content = await readFile(configPath, 'utf-8');
60
- const config = yaml.load(content) as Partial<HoneConfig>;
60
+ const content = await readFile(configPath, 'utf-8')
61
+ const config = yaml.load(content) as Partial<HoneConfig>
61
62
  // Deep merge models to preserve defaults
62
- return {
63
- ...DEFAULT_CONFIG,
63
+ return {
64
+ ...DEFAULT_CONFIG,
64
65
  ...config,
65
- models: { ...DEFAULT_CONFIG.models, ...config.models }
66
- };
66
+ models: { ...DEFAULT_CONFIG.models, ...config.models },
67
+ }
67
68
  } catch (error) {
68
- console.error('Error reading config, using defaults:', error);
69
- return DEFAULT_CONFIG;
69
+ console.error('Error reading config, using defaults:', error)
70
+ return DEFAULT_CONFIG
70
71
  }
71
72
  }
72
73
 
73
74
  export async function saveConfig(config: HoneConfig): Promise<void> {
74
- ensurePlansDir();
75
- const configPath = getConfigPath();
76
- await writeFile(configPath, yaml.dump(config));
75
+ ensurePlansDir()
76
+ const configPath = getConfigPath()
77
+ await writeFile(configPath, yaml.dump(config))
77
78
  }
78
79
 
79
- export type AgentType = 'opencode' | 'claude';
80
+ export type AgentType = 'opencode' | 'claude'
80
81
 
81
82
  export function isValidAgent(agent: string): agent is AgentType {
82
- return agent === 'opencode' || agent === 'claude';
83
+ return agent === 'opencode' || agent === 'claude'
83
84
  }
84
85
 
85
86
  export async function resolveAgent(flagAgent?: string): Promise<AgentType> {
@@ -89,44 +90,44 @@ export async function resolveAgent(flagAgent?: string): Promise<AgentType> {
89
90
  exitWithError(
90
91
  'Error: Invalid agent',
91
92
  `Agent "${flagAgent}" is not valid. Must be "opencode" or "claude".`
92
- );
93
+ )
93
94
  }
94
- return flagAgent;
95
+ return flagAgent
95
96
  }
96
-
97
- const config = await loadConfig();
98
- return config.defaultAgent;
97
+
98
+ const config = await loadConfig()
99
+ return config.defaultAgent
99
100
  }
100
101
 
101
102
  export interface InitResult {
102
- plansCreated: boolean;
103
- configCreated: boolean;
103
+ plansCreated: boolean
104
+ configCreated: boolean
104
105
  }
105
106
 
106
107
  export async function initProject(): Promise<InitResult> {
107
- const plansDir = getPlansDir();
108
- const configPath = getConfigPath();
109
-
110
- const plansExisted = existsSync(plansDir);
111
- const configExisted = existsSync(configPath);
112
-
108
+ const plansDir = getPlansDir()
109
+ const configPath = getConfigPath()
110
+
111
+ const plansExisted = existsSync(plansDir)
112
+ const configExisted = existsSync(configPath)
113
+
113
114
  // Ensure .plans directory exists
114
115
  if (!plansExisted) {
115
- mkdirSync(plansDir, { recursive: true });
116
+ mkdirSync(plansDir, { recursive: true })
116
117
  }
117
-
118
+
118
119
  // Create config file if it doesn't exist
119
120
  if (!configExisted) {
120
- await writeFile(configPath, yaml.dump(DEFAULT_CONFIG));
121
+ await writeFile(configPath, yaml.dump(DEFAULT_CONFIG))
121
122
  }
122
-
123
+
123
124
  return {
124
125
  plansCreated: !plansExisted,
125
- configCreated: !configExisted
126
- };
126
+ configCreated: !configExisted,
127
+ }
127
128
  }
128
129
 
129
- export type ModelPhase = 'prd' | 'prdToTasks' | 'implement' | 'review' | 'finalize';
130
+ export type ModelPhase = 'prd' | 'prdToTasks' | 'implement' | 'review' | 'finalize' | 'agentsMd'
130
131
 
131
132
  /**
132
133
  * Resolve the model to use for a specific phase.
@@ -137,20 +138,20 @@ export function resolveModelForPhase(
137
138
  phase?: ModelPhase,
138
139
  agent?: AgentType
139
140
  ): string {
140
- const resolvedAgent = agent || config.defaultAgent;
141
-
141
+ const resolvedAgent = agent || config.defaultAgent
142
+
142
143
  // 1. Check phase-specific model override
143
144
  if (phase && config.models[phase]) {
144
- return config.models[phase]!;
145
+ return config.models[phase]!
145
146
  }
146
-
147
+
147
148
  // 2. Fall back to agent-specific model
148
149
  if (config.models[resolvedAgent]) {
149
- return config.models[resolvedAgent];
150
+ return config.models[resolvedAgent]
150
151
  }
151
-
152
+
152
153
  // 3. Fall back to default model
153
- return 'claude-sonnet-4-20250514';
154
+ return 'claude-sonnet-4-20250514'
154
155
  }
155
156
 
156
157
  /**
@@ -158,29 +159,29 @@ export function resolveModelForPhase(
158
159
  * Ensures model names follow the correct format.
159
160
  */
160
161
  export function validateConfig(config: HoneConfig): { valid: boolean; errors: string[] } {
161
- const errors: string[] = [];
162
- const modelRegex = /^claude-(sonnet|opus)-\d+-\d{8}$/;
163
-
162
+ const errors: string[] = []
163
+ const modelRegex = /^claude-(sonnet|opus)-\d+-\d{8}$/
164
+
164
165
  // Validate agent-specific models
165
166
  if (config.models.opencode && !modelRegex.test(config.models.opencode)) {
166
- errors.push(`Invalid model format for opencode: ${config.models.opencode}`);
167
+ errors.push(`Invalid model format for opencode: ${config.models.opencode}`)
167
168
  }
168
-
169
+
169
170
  if (config.models.claude && !modelRegex.test(config.models.claude)) {
170
- errors.push(`Invalid model format for claude: ${config.models.claude}`);
171
+ errors.push(`Invalid model format for claude: ${config.models.claude}`)
171
172
  }
172
-
173
+
173
174
  // Validate phase-specific models if present
174
- const phases: ModelPhase[] = ['prd', 'prdToTasks', 'implement', 'review', 'finalize'];
175
+ const phases: ModelPhase[] = ['prd', 'prdToTasks', 'implement', 'review', 'finalize', 'agentsMd']
175
176
  for (const phase of phases) {
176
- const model = config.models[phase];
177
+ const model = config.models[phase]
177
178
  if (model && !modelRegex.test(model)) {
178
- errors.push(`Invalid model format for phase ${phase}: ${model}`);
179
+ errors.push(`Invalid model format for phase ${phase}: ${model}`)
179
180
  }
180
181
  }
181
-
182
+
182
183
  return {
183
184
  valid: errors.length === 0,
184
- errors
185
- };
185
+ errors,
186
+ }
186
187
  }