clavix 5.6.2 → 5.6.3
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 +50 -6
- package/dist/cli/commands/init.js +97 -74
- package/dist/cli/commands/update.js +34 -1
- package/dist/cli/commands/version.js +9 -3
- package/dist/config/integrations.json +73 -17
- package/dist/core/adapter-registry.js +13 -1
- package/dist/templates/slash-commands/_canonical/implement.md +25 -1
- package/dist/templates/slash-commands/_canonical/improve.md +1 -1
- package/dist/templates/slash-commands/_canonical/plan.md +6 -3
- package/dist/templates/slash-commands/_canonical/prd.md +1 -1
- package/dist/templates/slash-commands/_canonical/refine.md +1 -1
- package/dist/templates/slash-commands/_canonical/start.md +1 -1
- package/dist/templates/slash-commands/_canonical/summarize.md +34 -7
- package/dist/utils/schemas.d.ts +202 -0
- package/dist/utils/schemas.js +128 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -42,12 +42,56 @@ The AI agent reads the template and optimizes your prompt.
|
|
|
42
42
|
|
|
43
43
|
### 3. Choose Your Workflow
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
```mermaid
|
|
46
|
+
graph TD
|
|
47
|
+
A["/clavix:improve"] --> B["/clavix:implement"]
|
|
48
|
+
C["/clavix:prd"] --> D["/clavix:plan"]
|
|
49
|
+
D --> E["/clavix:implement"]
|
|
50
|
+
E --> F["/clavix:verify"]
|
|
51
|
+
F --> G["/clavix:archive"]
|
|
52
|
+
H["/clavix:start"] --> I["/clavix:summarize"]
|
|
53
|
+
I --> D
|
|
54
|
+
J["/clavix:refine"] --> D
|
|
55
|
+
J --> B
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
#### Quick Path (Simple Tasks)
|
|
59
|
+
```
|
|
60
|
+
/clavix:improve → /clavix:implement
|
|
61
|
+
```
|
|
62
|
+
Optimize a prompt and implement it directly.
|
|
63
|
+
|
|
64
|
+
#### Full Planning (Complex Features)
|
|
65
|
+
```
|
|
66
|
+
/clavix:prd → /clavix:plan → /clavix:implement → /clavix:verify → /clavix:archive
|
|
67
|
+
```
|
|
68
|
+
Structured planning with PRD, task breakdown, implementation, verification, and archival.
|
|
69
|
+
|
|
70
|
+
#### Exploratory (Discovery Mode)
|
|
71
|
+
```
|
|
72
|
+
/clavix:start → [conversation] → /clavix:summarize → /clavix:plan
|
|
73
|
+
```
|
|
74
|
+
Have a conversation to explore requirements, then extract and plan.
|
|
75
|
+
|
|
76
|
+
#### Refinement (Iteration)
|
|
77
|
+
```
|
|
78
|
+
/clavix:refine → (updated PRD or prompt) → continue workflow
|
|
79
|
+
```
|
|
80
|
+
Refine existing PRDs or prompts based on feedback.
|
|
81
|
+
|
|
82
|
+
### All 9 Slash Commands
|
|
83
|
+
|
|
84
|
+
| Command | Purpose |
|
|
85
|
+
|---------|---------|
|
|
86
|
+
| `/clavix:improve` | Optimize prompts (auto-selects depth) |
|
|
87
|
+
| `/clavix:prd` | Generate PRD through guided questions |
|
|
88
|
+
| `/clavix:plan` | Create task breakdown from PRD |
|
|
89
|
+
| `/clavix:implement` | Execute tasks or prompts |
|
|
90
|
+
| `/clavix:start` | Begin conversational session |
|
|
91
|
+
| `/clavix:summarize` | Extract requirements from conversation |
|
|
92
|
+
| `/clavix:refine` | Refine existing PRD or prompt |
|
|
93
|
+
| `/clavix:verify` | Verify implementation against requirements |
|
|
94
|
+
| `/clavix:archive` | Archive completed projects |
|
|
51
95
|
|
|
52
96
|
See [Getting Started](docs/getting-started.md) for the full guide.
|
|
53
97
|
|
|
@@ -19,11 +19,12 @@ import { QwenAdapter } from '../../core/adapters/qwen-adapter.js';
|
|
|
19
19
|
import { loadCommandTemplates } from '../../utils/template-loader.js';
|
|
20
20
|
import { collectLegacyCommandFiles } from '../../utils/legacy-command-cleanup.js';
|
|
21
21
|
import { CLAVIX_BLOCK_START, CLAVIX_BLOCK_END } from '../../constants.js';
|
|
22
|
+
import { validateUserConfig } from '../../utils/schemas.js';
|
|
22
23
|
export default class Init extends Command {
|
|
23
24
|
static description = 'Initialize Clavix in the current project';
|
|
24
25
|
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
25
26
|
async run() {
|
|
26
|
-
|
|
27
|
+
this.log(chalk.bold.cyan('\n🚀 Clavix Initialization\n'));
|
|
27
28
|
try {
|
|
28
29
|
const agentManager = new AgentManager();
|
|
29
30
|
let existingIntegrations = [];
|
|
@@ -31,26 +32,48 @@ export default class Init extends Command {
|
|
|
31
32
|
if (await FileSystem.exists('.clavix/config.json')) {
|
|
32
33
|
try {
|
|
33
34
|
const configContent = await FileSystem.readFile('.clavix/config.json');
|
|
34
|
-
const
|
|
35
|
-
|
|
35
|
+
const rawConfig = JSON.parse(configContent);
|
|
36
|
+
// Validate config structure with Zod
|
|
37
|
+
const validationResult = validateUserConfig(rawConfig);
|
|
38
|
+
if (validationResult.success && validationResult.data) {
|
|
39
|
+
existingIntegrations =
|
|
40
|
+
validationResult.data.integrations || validationResult.data.providers || [];
|
|
41
|
+
// Log warnings (non-blocking)
|
|
42
|
+
if (validationResult.warnings) {
|
|
43
|
+
for (const warning of validationResult.warnings) {
|
|
44
|
+
this.log(chalk.yellow(` ⚠ ${warning}`));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
this.log(chalk.yellow('⚠ Warning: Config file has invalid structure') +
|
|
50
|
+
'\n' +
|
|
51
|
+
chalk.gray(' Continuing with fresh configuration...\n'));
|
|
52
|
+
}
|
|
36
53
|
}
|
|
37
|
-
catch {
|
|
38
|
-
|
|
54
|
+
catch (error) {
|
|
55
|
+
const { getErrorMessage } = await import('../../utils/error-utils.js');
|
|
56
|
+
this.log(chalk.yellow('⚠ Warning: Could not parse existing config.json') +
|
|
57
|
+
'\n' +
|
|
58
|
+
chalk.gray(` Error: ${getErrorMessage(error)}`) +
|
|
59
|
+
'\n' +
|
|
60
|
+
chalk.gray(' Continuing with fresh configuration...\n'));
|
|
61
|
+
// Continue with empty array - will prompt for new configuration
|
|
39
62
|
}
|
|
40
63
|
}
|
|
41
64
|
// Check if already initialized
|
|
42
65
|
if (await FileSystem.exists('.clavix')) {
|
|
43
66
|
// Show current state
|
|
44
|
-
|
|
67
|
+
this.log(chalk.cyan('You have existing Clavix configuration:'));
|
|
45
68
|
if (existingIntegrations.length > 0) {
|
|
46
69
|
const displayNames = existingIntegrations.map((name) => {
|
|
47
70
|
const adapter = agentManager.getAdapter(name);
|
|
48
71
|
return adapter?.displayName || name;
|
|
49
72
|
});
|
|
50
|
-
|
|
73
|
+
this.log(chalk.gray(` Integrations: ${displayNames.join(', ')}\n`));
|
|
51
74
|
}
|
|
52
75
|
else {
|
|
53
|
-
|
|
76
|
+
this.log(chalk.gray(' Integrations: (none configured)\n'));
|
|
54
77
|
}
|
|
55
78
|
const { action } = await inquirer.prompt([
|
|
56
79
|
{
|
|
@@ -65,39 +88,39 @@ export default class Init extends Command {
|
|
|
65
88
|
},
|
|
66
89
|
]);
|
|
67
90
|
if (action === 'cancel') {
|
|
68
|
-
|
|
91
|
+
this.log(chalk.yellow('\n✓ Initialization cancelled\n'));
|
|
69
92
|
return;
|
|
70
93
|
}
|
|
71
94
|
if (action === 'update') {
|
|
72
95
|
// Just regenerate commands for existing integrations
|
|
73
|
-
|
|
96
|
+
this.log(chalk.cyan('\n📝 Regenerating commands...\n'));
|
|
74
97
|
await this.regenerateCommands(agentManager, existingIntegrations);
|
|
75
|
-
|
|
98
|
+
this.log(chalk.green('\n✅ Commands updated successfully!\n'));
|
|
76
99
|
return;
|
|
77
100
|
}
|
|
78
101
|
// Continue with reconfiguration flow below
|
|
79
102
|
}
|
|
80
103
|
// Select integrations using shared utility
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
104
|
+
this.log(chalk.gray('Select AI development tools to support:\n'));
|
|
105
|
+
this.log(chalk.gray('(Space to select, Enter to confirm)\n'));
|
|
106
|
+
this.log(chalk.cyan('ℹ'), chalk.gray('AGENTS.md is always enabled to provide universal agent guidance.\n'));
|
|
84
107
|
const { selectIntegrations, ensureMandatoryIntegrations } = await import('../../utils/integration-selector.js');
|
|
85
108
|
const userSelectedIntegrations = await selectIntegrations(agentManager, existingIntegrations);
|
|
86
109
|
// Always include AGENTS.md
|
|
87
110
|
const selectedIntegrations = ensureMandatoryIntegrations(userSelectedIntegrations);
|
|
88
111
|
if (!selectedIntegrations || selectedIntegrations.length === 0) {
|
|
89
|
-
|
|
112
|
+
this.log(chalk.red('\n✗ No integrations selected\n'));
|
|
90
113
|
return;
|
|
91
114
|
}
|
|
92
115
|
// Handle deselected integrations (cleanup prompt)
|
|
93
116
|
const deselectedIntegrations = existingIntegrations.filter((p) => !selectedIntegrations.includes(p));
|
|
94
117
|
if (deselectedIntegrations.length > 0) {
|
|
95
|
-
|
|
118
|
+
this.log(chalk.yellow('\n⚠ Previously configured but not selected:'));
|
|
96
119
|
for (const integrationName of deselectedIntegrations) {
|
|
97
120
|
const adapter = agentManager.getAdapter(integrationName);
|
|
98
121
|
const displayName = adapter?.displayName || integrationName;
|
|
99
122
|
const directory = adapter?.directory || 'unknown';
|
|
100
|
-
|
|
123
|
+
this.log(chalk.gray(` • ${displayName} (${directory})`));
|
|
101
124
|
}
|
|
102
125
|
const { cleanupAction } = await inquirer.prompt([
|
|
103
126
|
{
|
|
@@ -112,85 +135,85 @@ export default class Init extends Command {
|
|
|
112
135
|
},
|
|
113
136
|
]);
|
|
114
137
|
if (cleanupAction === 'cleanup') {
|
|
115
|
-
|
|
138
|
+
this.log(chalk.gray('\n🗑️ Cleaning up deselected integrations...'));
|
|
116
139
|
for (const integrationName of deselectedIntegrations) {
|
|
117
140
|
// Handle doc generators (AGENTS.md, OCTO.md, WARP.md, copilot-instructions)
|
|
118
141
|
if (integrationName === 'agents-md') {
|
|
119
142
|
await DocInjector.removeBlock('AGENTS.md');
|
|
120
|
-
|
|
143
|
+
this.log(chalk.gray(' ✓ Cleaned AGENTS.md Clavix block'));
|
|
121
144
|
continue;
|
|
122
145
|
}
|
|
123
146
|
if (integrationName === 'octo-md') {
|
|
124
147
|
await DocInjector.removeBlock('OCTO.md');
|
|
125
|
-
|
|
148
|
+
this.log(chalk.gray(' ✓ Cleaned OCTO.md Clavix block'));
|
|
126
149
|
continue;
|
|
127
150
|
}
|
|
128
151
|
if (integrationName === 'warp-md') {
|
|
129
152
|
await DocInjector.removeBlock('WARP.md');
|
|
130
|
-
|
|
153
|
+
this.log(chalk.gray(' ✓ Cleaned WARP.md Clavix block'));
|
|
131
154
|
continue;
|
|
132
155
|
}
|
|
133
156
|
if (integrationName === 'copilot-instructions') {
|
|
134
157
|
await DocInjector.removeBlock('.github/copilot-instructions.md');
|
|
135
|
-
|
|
158
|
+
this.log(chalk.gray(' ✓ Cleaned copilot-instructions.md Clavix block'));
|
|
136
159
|
continue;
|
|
137
160
|
}
|
|
138
161
|
// Handle Claude Code (has CLAUDE.md doc injection)
|
|
139
162
|
if (integrationName === 'claude-code') {
|
|
140
163
|
await DocInjector.removeBlock('CLAUDE.md');
|
|
141
|
-
|
|
164
|
+
this.log(chalk.gray(' ✓ Cleaned CLAUDE.md Clavix block'));
|
|
142
165
|
}
|
|
143
166
|
const adapter = agentManager.getAdapter(integrationName);
|
|
144
167
|
if (adapter) {
|
|
145
168
|
const removed = await adapter.removeAllCommands();
|
|
146
|
-
|
|
169
|
+
this.log(chalk.gray(` ✓ Removed ${removed} command(s) from ${adapter.displayName}`));
|
|
147
170
|
}
|
|
148
171
|
}
|
|
149
172
|
}
|
|
150
173
|
else if (cleanupAction === 'update') {
|
|
151
174
|
// Add them back to selection
|
|
152
175
|
selectedIntegrations.push(...deselectedIntegrations);
|
|
153
|
-
|
|
176
|
+
this.log(chalk.gray('\n✓ Keeping all integrations\n'));
|
|
154
177
|
}
|
|
155
178
|
// If 'skip': do nothing
|
|
156
179
|
}
|
|
157
180
|
// Create .clavix directory structure
|
|
158
|
-
|
|
181
|
+
this.log(chalk.cyan('\n📁 Creating directory structure...'));
|
|
159
182
|
await this.createDirectoryStructure();
|
|
160
183
|
// Generate config
|
|
161
|
-
|
|
184
|
+
this.log(chalk.cyan('⚙️ Generating configuration...'));
|
|
162
185
|
await this.generateConfig(selectedIntegrations);
|
|
163
186
|
// Generate INSTRUCTIONS.md and QUICKSTART.md
|
|
164
187
|
await this.generateInstructions();
|
|
165
188
|
await this.generateQuickstart();
|
|
166
189
|
// Generate commands for each selected integration
|
|
167
|
-
|
|
190
|
+
this.log(chalk.cyan(`\n📝 Generating commands for ${selectedIntegrations.length} integration(s)...\n`));
|
|
168
191
|
for (const integrationName of selectedIntegrations) {
|
|
169
192
|
// Handle agents-md separately (it's not an adapter)
|
|
170
193
|
if (integrationName === 'agents-md') {
|
|
171
|
-
|
|
194
|
+
this.log(chalk.gray(' ✓ Generating AGENTS.md...'));
|
|
172
195
|
await AgentsMdGenerator.generate();
|
|
173
196
|
continue;
|
|
174
197
|
}
|
|
175
198
|
// Handle copilot-instructions separately (it's not an adapter)
|
|
176
199
|
if (integrationName === 'copilot-instructions') {
|
|
177
|
-
|
|
200
|
+
this.log(chalk.gray(' ✓ Generating .github/copilot-instructions.md...'));
|
|
178
201
|
await CopilotInstructionsGenerator.generate();
|
|
179
202
|
continue;
|
|
180
203
|
}
|
|
181
204
|
// Handle octo-md separately (it's not an adapter)
|
|
182
205
|
if (integrationName === 'octo-md') {
|
|
183
|
-
|
|
206
|
+
this.log(chalk.gray(' ✓ Generating OCTO.md...'));
|
|
184
207
|
await OctoMdGenerator.generate();
|
|
185
208
|
continue;
|
|
186
209
|
}
|
|
187
210
|
if (integrationName === 'warp-md') {
|
|
188
|
-
|
|
211
|
+
this.log(chalk.gray(' ✓ Generating WARP.md...'));
|
|
189
212
|
await WarpMdGenerator.generate();
|
|
190
213
|
continue;
|
|
191
214
|
}
|
|
192
215
|
let adapter = agentManager.requireAdapter(integrationName);
|
|
193
|
-
|
|
216
|
+
this.log(chalk.gray(` ✓ Generating ${adapter.displayName} commands...`));
|
|
194
217
|
if (adapter.name === 'codex') {
|
|
195
218
|
const codexPath = adapter.getCommandPath();
|
|
196
219
|
const { confirmCodex } = await inquirer.prompt([
|
|
@@ -202,7 +225,7 @@ export default class Init extends Command {
|
|
|
202
225
|
},
|
|
203
226
|
]);
|
|
204
227
|
if (!confirmCodex) {
|
|
205
|
-
|
|
228
|
+
this.log(chalk.yellow(' ⊗ Skipped Codex CLI'));
|
|
206
229
|
continue;
|
|
207
230
|
}
|
|
208
231
|
}
|
|
@@ -221,15 +244,15 @@ export default class Init extends Command {
|
|
|
221
244
|
adapter.name === 'gemini'
|
|
222
245
|
? new GeminiAdapter({ useNamespace: false })
|
|
223
246
|
: new QwenAdapter({ useNamespace: false });
|
|
224
|
-
|
|
247
|
+
this.log(chalk.gray(` → Using ${adapter.getCommandPath()} (no namespacing)`));
|
|
225
248
|
}
|
|
226
249
|
}
|
|
227
250
|
// Validate before generating
|
|
228
251
|
if (adapter.validate) {
|
|
229
252
|
const validation = await adapter.validate();
|
|
230
253
|
if (!validation.valid) {
|
|
231
|
-
|
|
232
|
-
validation.errors?.forEach((err) =>
|
|
254
|
+
this.log(chalk.yellow(` ⚠ Validation warnings for ${adapter.displayName}:`));
|
|
255
|
+
validation.errors?.forEach((err) => this.log(chalk.yellow(` - ${err}`)));
|
|
233
256
|
const { continueAnyway } = await inquirer.prompt([
|
|
234
257
|
{
|
|
235
258
|
type: 'confirm',
|
|
@@ -239,7 +262,7 @@ export default class Init extends Command {
|
|
|
239
262
|
},
|
|
240
263
|
]);
|
|
241
264
|
if (!continueAnyway) {
|
|
242
|
-
|
|
265
|
+
this.log(chalk.yellow(` ⊗ Skipped ${adapter.displayName}`));
|
|
243
266
|
continue;
|
|
244
267
|
}
|
|
245
268
|
}
|
|
@@ -247,7 +270,7 @@ export default class Init extends Command {
|
|
|
247
270
|
// Remove all existing commands before regenerating (ensures clean state)
|
|
248
271
|
const removed = await adapter.removeAllCommands();
|
|
249
272
|
if (removed > 0) {
|
|
250
|
-
|
|
273
|
+
this.log(chalk.gray(` Removed ${removed} existing command(s)`));
|
|
251
274
|
}
|
|
252
275
|
// Generate slash commands
|
|
253
276
|
const generatedTemplates = await this.generateSlashCommands(adapter);
|
|
@@ -264,24 +287,24 @@ export default class Init extends Command {
|
|
|
264
287
|
const slashName = filename.slice(0, -adapter.fileExtension.length);
|
|
265
288
|
return `/${slashName}`;
|
|
266
289
|
});
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
290
|
+
this.log(chalk.green(` → Registered ${commandNames.join(', ')}`));
|
|
291
|
+
this.log(chalk.gray(` Commands saved to ${commandPath}`));
|
|
292
|
+
this.log(chalk.gray(' Tip: reopen the CLI or run /help to refresh the command list.'));
|
|
270
293
|
}
|
|
271
294
|
// Inject documentation blocks (Claude Code only)
|
|
272
295
|
if (integrationName === 'claude-code') {
|
|
273
|
-
|
|
296
|
+
this.log(chalk.gray(' ✓ Injecting CLAUDE.md documentation...'));
|
|
274
297
|
await this.injectDocumentation(adapter);
|
|
275
298
|
}
|
|
276
299
|
}
|
|
277
300
|
// Generate .clavix/instructions/ folder for generic integrations
|
|
278
301
|
if (InstructionsGenerator.needsGeneration(selectedIntegrations)) {
|
|
279
|
-
|
|
302
|
+
this.log(chalk.gray('\n📁 Generating .clavix/instructions/ reference folder...'));
|
|
280
303
|
await InstructionsGenerator.generate();
|
|
281
|
-
|
|
304
|
+
this.log(chalk.gray(' ✓ Created detailed workflow guides for generic integrations'));
|
|
282
305
|
}
|
|
283
306
|
// Success message with prominent command format display
|
|
284
|
-
|
|
307
|
+
this.log(chalk.bold.green('\n✅ Clavix initialized successfully!\n'));
|
|
285
308
|
// Determine the primary command format based on selected integrations
|
|
286
309
|
const colonTools = ['claude-code', 'gemini', 'qwen', 'crush', 'llxprt', 'augment'];
|
|
287
310
|
const usesColon = selectedIntegrations.some((i) => colonTools.includes(i));
|
|
@@ -289,30 +312,30 @@ export default class Init extends Command {
|
|
|
289
312
|
const separator = usesColon && !usesHyphen ? ':' : usesHyphen && !usesColon ? '-' : ':';
|
|
290
313
|
const altSeparator = separator === ':' ? '-' : ':';
|
|
291
314
|
// Show command format prominently at the TOP
|
|
292
|
-
|
|
315
|
+
this.log(chalk.bold('📋 Your command format:'), chalk.bold.cyan(`/clavix${separator}improve`));
|
|
293
316
|
if (usesColon && usesHyphen) {
|
|
294
|
-
|
|
317
|
+
this.log(chalk.gray(' (Some integrations use'), chalk.cyan(`/clavix${altSeparator}improve`), chalk.gray('instead)'));
|
|
295
318
|
}
|
|
296
|
-
|
|
319
|
+
this.log();
|
|
297
320
|
// Available commands
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
321
|
+
this.log(chalk.gray('Available slash commands:'));
|
|
322
|
+
this.log(chalk.gray(' •'), chalk.cyan(`/clavix${separator}improve`), chalk.gray('- Smart prompt optimization'));
|
|
323
|
+
this.log(chalk.gray(' •'), chalk.cyan(`/clavix${separator}prd`), chalk.gray('- Generate PRD through guided questions'));
|
|
324
|
+
this.log(chalk.gray(' •'), chalk.cyan(`/clavix${separator}plan`), chalk.gray('- Create task breakdown from PRD'));
|
|
325
|
+
this.log(chalk.gray(' •'), chalk.cyan(`/clavix${separator}implement`), chalk.gray('- Execute tasks or prompts'));
|
|
326
|
+
this.log(chalk.gray('\nNext steps:'));
|
|
327
|
+
this.log(chalk.gray(' • Slash commands are now available in your AI agent'));
|
|
328
|
+
this.log(chalk.gray(' • Run'), chalk.cyan('clavix diagnose'), chalk.gray('to verify installation'));
|
|
329
|
+
this.log();
|
|
307
330
|
}
|
|
308
331
|
catch (error) {
|
|
309
332
|
const { getErrorMessage, toError } = await import('../../utils/error-utils.js');
|
|
310
|
-
|
|
333
|
+
this.log(chalk.red('\n✗ Initialization failed:'), getErrorMessage(error));
|
|
311
334
|
if (error &&
|
|
312
335
|
typeof error === 'object' &&
|
|
313
336
|
'hint' in error &&
|
|
314
337
|
typeof error.hint === 'string') {
|
|
315
|
-
|
|
338
|
+
this.log(chalk.yellow(' Hint:'), error.hint);
|
|
316
339
|
}
|
|
317
340
|
throw toError(error);
|
|
318
341
|
}
|
|
@@ -324,36 +347,36 @@ export default class Init extends Command {
|
|
|
324
347
|
for (const integrationName of integrations) {
|
|
325
348
|
// Handle doc generators (not adapters)
|
|
326
349
|
if (integrationName === 'agents-md') {
|
|
327
|
-
|
|
350
|
+
this.log(chalk.gray(' ✓ Regenerating AGENTS.md...'));
|
|
328
351
|
await AgentsMdGenerator.generate();
|
|
329
352
|
continue;
|
|
330
353
|
}
|
|
331
354
|
if (integrationName === 'copilot-instructions') {
|
|
332
|
-
|
|
355
|
+
this.log(chalk.gray(' ✓ Regenerating .github/copilot-instructions.md...'));
|
|
333
356
|
await CopilotInstructionsGenerator.generate();
|
|
334
357
|
continue;
|
|
335
358
|
}
|
|
336
359
|
if (integrationName === 'octo-md') {
|
|
337
|
-
|
|
360
|
+
this.log(chalk.gray(' ✓ Regenerating OCTO.md...'));
|
|
338
361
|
await OctoMdGenerator.generate();
|
|
339
362
|
continue;
|
|
340
363
|
}
|
|
341
364
|
if (integrationName === 'warp-md') {
|
|
342
|
-
|
|
365
|
+
this.log(chalk.gray(' ✓ Regenerating WARP.md...'));
|
|
343
366
|
await WarpMdGenerator.generate();
|
|
344
367
|
continue;
|
|
345
368
|
}
|
|
346
369
|
// Handle regular adapters
|
|
347
370
|
const adapter = agentManager.getAdapter(integrationName);
|
|
348
371
|
if (!adapter) {
|
|
349
|
-
|
|
372
|
+
this.log(chalk.yellow(` ⚠ Unknown integration: ${integrationName}`));
|
|
350
373
|
continue;
|
|
351
374
|
}
|
|
352
|
-
|
|
375
|
+
this.log(chalk.gray(` ✓ Regenerating ${adapter.displayName} commands...`));
|
|
353
376
|
// Remove existing commands before regenerating
|
|
354
377
|
const removed = await adapter.removeAllCommands();
|
|
355
378
|
if (removed > 0) {
|
|
356
|
-
|
|
379
|
+
this.log(chalk.gray(` Removed ${removed} existing command(s)`));
|
|
357
380
|
}
|
|
358
381
|
// Generate slash commands
|
|
359
382
|
const templates = await this.generateSlashCommands(adapter);
|
|
@@ -364,17 +387,17 @@ export default class Init extends Command {
|
|
|
364
387
|
for (const file of legacyFiles) {
|
|
365
388
|
await FileSystem.remove(file);
|
|
366
389
|
}
|
|
367
|
-
|
|
390
|
+
this.log(chalk.gray(` Cleaned ${legacyFiles.length} legacy file(s)`));
|
|
368
391
|
}
|
|
369
392
|
// Re-inject documentation blocks (Claude Code only)
|
|
370
393
|
if (integrationName === 'claude-code') {
|
|
371
|
-
|
|
394
|
+
this.log(chalk.gray(' ✓ Updating CLAUDE.md documentation...'));
|
|
372
395
|
await this.injectDocumentation(adapter);
|
|
373
396
|
}
|
|
374
397
|
}
|
|
375
398
|
// Regenerate instructions folder if needed
|
|
376
399
|
if (InstructionsGenerator.needsGeneration(integrations)) {
|
|
377
|
-
|
|
400
|
+
this.log(chalk.gray('\n📁 Updating .clavix/instructions/ reference folder...'));
|
|
378
401
|
await InstructionsGenerator.generate();
|
|
379
402
|
}
|
|
380
403
|
}
|
|
@@ -529,9 +552,9 @@ To reconfigure integrations, run \`clavix init\` again.
|
|
|
529
552
|
const relativePaths = legacyFiles
|
|
530
553
|
.map((file) => path.relative(process.cwd(), file))
|
|
531
554
|
.sort((a, b) => a.localeCompare(b));
|
|
532
|
-
|
|
555
|
+
this.log(chalk.gray(` ⚠ Found ${relativePaths.length} deprecated command file(s):`));
|
|
533
556
|
for (const file of relativePaths) {
|
|
534
|
-
|
|
557
|
+
this.log(chalk.gray(` • ${file}`));
|
|
535
558
|
}
|
|
536
559
|
const { removeLegacy } = await inquirer.prompt([
|
|
537
560
|
{
|
|
@@ -542,12 +565,12 @@ To reconfigure integrations, run \`clavix init\` again.
|
|
|
542
565
|
},
|
|
543
566
|
]);
|
|
544
567
|
if (!removeLegacy) {
|
|
545
|
-
|
|
568
|
+
this.log(chalk.gray(' ⊗ Kept legacy files (deprecated naming retained)'));
|
|
546
569
|
return;
|
|
547
570
|
}
|
|
548
571
|
for (const file of legacyFiles) {
|
|
549
572
|
await FileSystem.remove(file);
|
|
550
|
-
|
|
573
|
+
this.log(chalk.gray(` ✓ Removed ${path.relative(process.cwd(), file)}`));
|
|
551
574
|
}
|
|
552
575
|
}
|
|
553
576
|
async injectDocumentation(adapter) {
|
|
@@ -11,6 +11,7 @@ import { WarpMdGenerator } from '../../core/adapters/warp-md-generator.js';
|
|
|
11
11
|
import { InstructionsGenerator } from '../../core/adapters/instructions-generator.js';
|
|
12
12
|
import { collectLegacyCommandFiles } from '../../utils/legacy-command-cleanup.js';
|
|
13
13
|
import { CLAVIX_BLOCK_START, CLAVIX_BLOCK_END } from '../../constants.js';
|
|
14
|
+
import { validateUserConfig, formatZodErrors } from '../../utils/schemas.js';
|
|
14
15
|
export default class Update extends Command {
|
|
15
16
|
static description = 'Update managed blocks and slash commands';
|
|
16
17
|
static examples = [
|
|
@@ -46,7 +47,39 @@ export default class Update extends Command {
|
|
|
46
47
|
}
|
|
47
48
|
this.log(chalk.bold.cyan('🔄 Updating Clavix integration...\n'));
|
|
48
49
|
// Load config to determine integrations
|
|
49
|
-
|
|
50
|
+
let config;
|
|
51
|
+
try {
|
|
52
|
+
const rawConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
53
|
+
// Validate config structure with Zod
|
|
54
|
+
const validationResult = validateUserConfig(rawConfig);
|
|
55
|
+
if (!validationResult.success && validationResult.errors) {
|
|
56
|
+
const errorMessages = formatZodErrors(validationResult.errors);
|
|
57
|
+
this.error(chalk.red(`Invalid config file structure: ${configPath}`) +
|
|
58
|
+
'\n' +
|
|
59
|
+
errorMessages.map((e) => chalk.gray(` - ${e}`)).join('\n') +
|
|
60
|
+
'\n' +
|
|
61
|
+
chalk.yellow(' Hint: Delete .clavix/config.json and run ') +
|
|
62
|
+
chalk.cyan('clavix init') +
|
|
63
|
+
chalk.yellow(' to regenerate.'));
|
|
64
|
+
}
|
|
65
|
+
// Log warnings (non-blocking)
|
|
66
|
+
if (validationResult.warnings) {
|
|
67
|
+
for (const warning of validationResult.warnings) {
|
|
68
|
+
this.log(chalk.yellow(` ⚠ ${warning}`));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
config = validationResult.data;
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
const { getErrorMessage } = await import('../../utils/error-utils.js');
|
|
75
|
+
this.error(chalk.red(`Invalid JSON in config file: ${configPath}`) +
|
|
76
|
+
'\n' +
|
|
77
|
+
chalk.gray(` Error: ${getErrorMessage(error)}`) +
|
|
78
|
+
'\n' +
|
|
79
|
+
chalk.yellow(' Hint: Check for syntax errors or delete .clavix/config.json and run ') +
|
|
80
|
+
chalk.cyan('clavix init') +
|
|
81
|
+
chalk.yellow(' to regenerate.'));
|
|
82
|
+
}
|
|
50
83
|
const integrations = config.integrations || config.providers || ['claude-code'];
|
|
51
84
|
const agentManager = new AgentManager();
|
|
52
85
|
const updateDocs = flags['docs-only'] || (!flags['docs-only'] && !flags['commands-only']);
|
|
@@ -13,10 +13,16 @@ export default class Version extends Command {
|
|
|
13
13
|
try {
|
|
14
14
|
const packageJsonPath = path.join(__dirname, '../../../package.json');
|
|
15
15
|
const packageJson = await fs.readJson(packageJsonPath);
|
|
16
|
-
|
|
16
|
+
this.log(chalk.cyan(`\nClavix v${packageJson.version}\n`));
|
|
17
17
|
}
|
|
18
|
-
catch {
|
|
19
|
-
|
|
18
|
+
catch (error) {
|
|
19
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error reading package.json';
|
|
20
|
+
this.error(chalk.red('Could not determine version') +
|
|
21
|
+
'\n' +
|
|
22
|
+
chalk.gray(` Error: ${errorMessage}`) +
|
|
23
|
+
'\n' +
|
|
24
|
+
chalk.yellow(' Hint: Try reinstalling Clavix with ') +
|
|
25
|
+
chalk.cyan('npm install -g clavix'));
|
|
20
26
|
}
|
|
21
27
|
}
|
|
22
28
|
}
|