@fractary/faber-cli 1.1.0 → 1.3.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/dist/commands/plan/index.d.ts +11 -0
- package/dist/commands/plan/index.d.ts.map +1 -0
- package/dist/commands/plan/index.js +383 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/lib/anthropic-client.d.ts +69 -0
- package/dist/lib/anthropic-client.d.ts.map +1 -0
- package/dist/lib/anthropic-client.js +225 -0
- package/dist/lib/config.d.ts +30 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +127 -0
- package/dist/lib/repo-client.d.ts +78 -0
- package/dist/lib/repo-client.d.ts.map +1 -0
- package/dist/lib/repo-client.js +151 -0
- package/dist/lib/sdk-config-adapter.d.ts +24 -0
- package/dist/lib/sdk-config-adapter.d.ts.map +1 -0
- package/dist/lib/sdk-config-adapter.js +50 -0
- package/dist/lib/sdk-type-adapter.d.ts +43 -0
- package/dist/lib/sdk-type-adapter.d.ts.map +1 -0
- package/dist/lib/sdk-type-adapter.js +42 -0
- package/dist/types/config.d.ts +34 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +6 -0
- package/dist/utils/prompt.d.ts +8 -0
- package/dist/utils/prompt.d.ts.map +1 -0
- package/dist/utils/prompt.js +19 -0
- package/dist/utils/validation.d.ts +71 -0
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +157 -0
- package/package.json +2 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan command - FABER CLI planning command
|
|
3
|
+
*
|
|
4
|
+
* Batch workflow planning for GitHub issues
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
/**
|
|
8
|
+
* Create the plan command
|
|
9
|
+
*/
|
|
10
|
+
export declare function createPlanCommand(): Command;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/plan/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+CpC;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAkB3C"}
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan command - FABER CLI planning command
|
|
3
|
+
*
|
|
4
|
+
* Batch workflow planning for GitHub issues
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { AnthropicClient } from '../../lib/anthropic-client.js';
|
|
9
|
+
import { RepoClient } from '../../lib/repo-client.js';
|
|
10
|
+
import { ConfigManager } from '../../lib/config.js';
|
|
11
|
+
import { prompt } from '../../utils/prompt.js';
|
|
12
|
+
import { validateWorkIds, validateLabels, validateWorkflowName, validatePlanId, } from '../../utils/validation.js';
|
|
13
|
+
import fs from 'fs/promises';
|
|
14
|
+
import path from 'path';
|
|
15
|
+
/**
|
|
16
|
+
* Create the plan command
|
|
17
|
+
*/
|
|
18
|
+
export function createPlanCommand() {
|
|
19
|
+
return new Command('plan')
|
|
20
|
+
.description('Plan workflows for GitHub issues')
|
|
21
|
+
.option('--work-id <ids>', 'Comma-separated list of work item IDs (e.g., "258,259,260")')
|
|
22
|
+
.option('--work-label <labels>', 'Comma-separated label filters (e.g., "workflow:etl,status:approved")')
|
|
23
|
+
.option('--workflow <name>', 'Override workflow (default: read from issue "workflow:*" label)')
|
|
24
|
+
.option('--no-worktree', 'Skip worktree creation')
|
|
25
|
+
.option('--no-branch', 'Skip branch creation')
|
|
26
|
+
.option('--skip-confirm', 'Skip confirmation prompt (use with caution)')
|
|
27
|
+
.option('--output <format>', 'Output format: text|json|yaml', 'text')
|
|
28
|
+
.option('--json', 'Output as JSON (shorthand for --output json)')
|
|
29
|
+
.action(async (options) => {
|
|
30
|
+
try {
|
|
31
|
+
await executePlanCommand(options);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
handlePlanError(error, options);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Main execution logic for plan command
|
|
40
|
+
*/
|
|
41
|
+
async function executePlanCommand(options) {
|
|
42
|
+
const outputFormat = options.json ? 'json' : options.output || 'text';
|
|
43
|
+
// Validate arguments
|
|
44
|
+
if (!options.workId && !options.workLabel) {
|
|
45
|
+
throw new Error('Either --work-id or --work-label must be provided');
|
|
46
|
+
}
|
|
47
|
+
if (options.workId && options.workLabel) {
|
|
48
|
+
throw new Error('Cannot use both --work-id and --work-label at the same time');
|
|
49
|
+
}
|
|
50
|
+
// Initialize clients
|
|
51
|
+
const config = await ConfigManager.load();
|
|
52
|
+
const repoClient = new RepoClient(config);
|
|
53
|
+
const anthropicClient = new AnthropicClient(config);
|
|
54
|
+
if (outputFormat === 'text') {
|
|
55
|
+
console.log(chalk.blue('FABER CLI - Workflow Planning'));
|
|
56
|
+
console.log(chalk.gray('═'.repeat(50)));
|
|
57
|
+
}
|
|
58
|
+
// Step 1: Fetch issues from GitHub
|
|
59
|
+
if (outputFormat === 'text') {
|
|
60
|
+
console.log(chalk.cyan('\n→ Fetching issues from GitHub...'));
|
|
61
|
+
}
|
|
62
|
+
let issues;
|
|
63
|
+
try {
|
|
64
|
+
if (options.workId) {
|
|
65
|
+
// Validate work IDs before fetching
|
|
66
|
+
const ids = validateWorkIds(options.workId);
|
|
67
|
+
issues = await repoClient.fetchIssues(ids);
|
|
68
|
+
if (outputFormat === 'text') {
|
|
69
|
+
console.log(chalk.green(` ✓ Fetched ${issues.length} issue(s) by ID`));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else if (options.workLabel) {
|
|
73
|
+
// Validate labels before searching
|
|
74
|
+
const labels = validateLabels(options.workLabel);
|
|
75
|
+
issues = await repoClient.searchIssues(labels);
|
|
76
|
+
if (outputFormat === 'text') {
|
|
77
|
+
console.log(chalk.green(` ✓ Found ${issues.length} issue(s) matching labels`));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
throw new Error('No issues to process');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
if (error instanceof Error && error.message.includes('not yet implemented')) {
|
|
86
|
+
if (outputFormat === 'text') {
|
|
87
|
+
console.log(chalk.yellow('\n⚠️ fractary-repo commands not yet available'));
|
|
88
|
+
console.log(chalk.gray(' This command requires fractary-repo plugin implementation.'));
|
|
89
|
+
console.log(chalk.gray(' See SPEC-00030-FRACTARY-REPO-ENHANCEMENTS.md'));
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.log(JSON.stringify({
|
|
93
|
+
status: 'error',
|
|
94
|
+
error: {
|
|
95
|
+
code: 'DEPENDENCY_NOT_AVAILABLE',
|
|
96
|
+
message: 'fractary-repo commands not yet implemented',
|
|
97
|
+
details: 'See SPEC-00030-FRACTARY-REPO-ENHANCEMENTS.md',
|
|
98
|
+
},
|
|
99
|
+
}, null, 2));
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
if (issues.length === 0) {
|
|
106
|
+
if (outputFormat === 'text') {
|
|
107
|
+
console.log(chalk.yellow('\n⚠️ No issues found'));
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
console.log(JSON.stringify({ status: 'success', issues: [], message: 'No issues found' }, null, 2));
|
|
111
|
+
}
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// Step 2: Extract workflows from labels or prompt user
|
|
115
|
+
if (outputFormat === 'text') {
|
|
116
|
+
console.log(chalk.cyan('\n→ Identifying workflows...'));
|
|
117
|
+
}
|
|
118
|
+
const availableWorkflows = await loadAvailableWorkflows(config);
|
|
119
|
+
const issuesWithWorkflows = await assignWorkflows(issues, availableWorkflows, options, outputFormat);
|
|
120
|
+
// Step 3: Show confirmation prompt
|
|
121
|
+
if (!options.skipConfirm) {
|
|
122
|
+
const confirmed = await showConfirmationPrompt(issuesWithWorkflows, config, outputFormat);
|
|
123
|
+
if (!confirmed) {
|
|
124
|
+
if (outputFormat === 'text') {
|
|
125
|
+
console.log(chalk.yellow('\n✖ Planning cancelled'));
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
console.log(JSON.stringify({ status: 'cancelled', message: 'User cancelled planning' }, null, 2));
|
|
129
|
+
}
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Step 4: Plan each issue
|
|
134
|
+
if (outputFormat === 'text') {
|
|
135
|
+
console.log(chalk.cyan('\n→ Planning workflows...'));
|
|
136
|
+
}
|
|
137
|
+
const results = [];
|
|
138
|
+
for (const issue of issuesWithWorkflows) {
|
|
139
|
+
if (outputFormat === 'text') {
|
|
140
|
+
console.log(chalk.gray(`\n[${results.length + 1}/${issuesWithWorkflows.length}] Issue #${issue.number}: ${issue.title}`));
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const result = await planSingleIssue(issue, config, repoClient, anthropicClient, options, outputFormat);
|
|
144
|
+
results.push(result);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
148
|
+
if (outputFormat === 'text') {
|
|
149
|
+
console.log(chalk.red(` ✗ Error: ${errorMessage}`));
|
|
150
|
+
}
|
|
151
|
+
results.push({
|
|
152
|
+
issue,
|
|
153
|
+
planId: '',
|
|
154
|
+
branch: '',
|
|
155
|
+
worktree: '',
|
|
156
|
+
error: errorMessage,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Step 5: Output summary
|
|
161
|
+
if (outputFormat === 'json') {
|
|
162
|
+
console.log(JSON.stringify({
|
|
163
|
+
status: 'success',
|
|
164
|
+
total: results.length,
|
|
165
|
+
successful: results.filter(r => !r.error).length,
|
|
166
|
+
failed: results.filter(r => r.error).length,
|
|
167
|
+
results,
|
|
168
|
+
}, null, 2));
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
outputTextSummary(results);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Load available workflow configurations
|
|
176
|
+
*/
|
|
177
|
+
async function loadAvailableWorkflows(config) {
|
|
178
|
+
const workflowDir = config.workflow?.config_path || './plugins/faber/config/workflows';
|
|
179
|
+
try {
|
|
180
|
+
const files = await fs.readdir(workflowDir);
|
|
181
|
+
return files
|
|
182
|
+
.filter(f => f.endsWith('.json'))
|
|
183
|
+
.map(f => path.basename(f, '.json'));
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
// Default workflows if directory doesn't exist
|
|
187
|
+
return ['core', 'etl', 'bugfix', 'feature'];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Assign workflows to issues (extract from labels or prompt user)
|
|
192
|
+
*/
|
|
193
|
+
async function assignWorkflows(issues, availableWorkflows, options, outputFormat) {
|
|
194
|
+
const issuesWithWorkflows = [];
|
|
195
|
+
for (const issue of issues) {
|
|
196
|
+
let workflow = options.workflow; // Command-line override
|
|
197
|
+
// Validate workflow override if provided
|
|
198
|
+
if (workflow) {
|
|
199
|
+
validateWorkflowName(workflow);
|
|
200
|
+
}
|
|
201
|
+
if (!workflow) {
|
|
202
|
+
// Extract from issue labels
|
|
203
|
+
const workflowLabel = issue.labels.find(label => label.startsWith('workflow:'));
|
|
204
|
+
if (workflowLabel) {
|
|
205
|
+
workflow = workflowLabel.replace('workflow:', '');
|
|
206
|
+
// Validate extracted workflow name
|
|
207
|
+
validateWorkflowName(workflow);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (!workflow) {
|
|
211
|
+
// Prompt user
|
|
212
|
+
if (outputFormat === 'text') {
|
|
213
|
+
console.log(chalk.yellow(`\n⚠️ Issue #${issue.number} is missing a workflow label:`));
|
|
214
|
+
console.log(chalk.gray(` ${issue.title}`));
|
|
215
|
+
console.log(chalk.gray(` Available workflows: ${availableWorkflows.join(', ')}`));
|
|
216
|
+
workflow = await prompt(` Select workflow for this issue [${availableWorkflows[0]}]: `);
|
|
217
|
+
if (!workflow) {
|
|
218
|
+
workflow = availableWorkflows[0];
|
|
219
|
+
}
|
|
220
|
+
if (!availableWorkflows.includes(workflow)) {
|
|
221
|
+
throw new Error(`Invalid workflow: ${workflow}. Available: ${availableWorkflows.join(', ')}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
throw new Error(`Issue #${issue.number} is missing workflow label and interactive prompts are disabled in JSON mode`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
issuesWithWorkflows.push({ ...issue, workflow });
|
|
229
|
+
}
|
|
230
|
+
if (outputFormat === 'text') {
|
|
231
|
+
console.log(chalk.green(` ✓ All issues have workflows assigned`));
|
|
232
|
+
}
|
|
233
|
+
return issuesWithWorkflows;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Show confirmation prompt before planning
|
|
237
|
+
*/
|
|
238
|
+
async function showConfirmationPrompt(issues, config, outputFormat) {
|
|
239
|
+
if (outputFormat !== 'text') {
|
|
240
|
+
return true; // Skip in JSON mode
|
|
241
|
+
}
|
|
242
|
+
console.log(chalk.cyan('\n📋 Will plan workflows for the following issues:\n'));
|
|
243
|
+
for (const issue of issues) {
|
|
244
|
+
const { organization, project } = getRepoInfoFromConfig(config);
|
|
245
|
+
const branch = `feature/${issue.number}`;
|
|
246
|
+
const worktree = `~/.claude-worktrees/${organization}-${project}-${issue.number}`;
|
|
247
|
+
console.log(chalk.bold(`#${issue.number}: ${issue.title}`));
|
|
248
|
+
console.log(chalk.gray(` Workflow: ${issue.workflow}`));
|
|
249
|
+
console.log(chalk.gray(` Branch: ${branch}`));
|
|
250
|
+
console.log(chalk.gray(` Worktree: ${worktree}`));
|
|
251
|
+
console.log();
|
|
252
|
+
}
|
|
253
|
+
const response = await prompt('Proceed? [Y/n]: ');
|
|
254
|
+
return !response || response.toLowerCase() === 'y' || response.toLowerCase() === 'yes';
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Plan a single issue
|
|
258
|
+
*/
|
|
259
|
+
async function planSingleIssue(issue, config, repoClient, anthropicClient, options, outputFormat) {
|
|
260
|
+
const { organization, project } = getRepoInfoFromConfig(config);
|
|
261
|
+
const branch = `feature/${issue.number}`;
|
|
262
|
+
const worktree = `~/.claude-worktrees/${organization}-${project}-${issue.number}`;
|
|
263
|
+
// Generate plan via Anthropic API
|
|
264
|
+
if (outputFormat === 'text') {
|
|
265
|
+
console.log(chalk.gray(' → Generating plan...'));
|
|
266
|
+
}
|
|
267
|
+
const plan = await anthropicClient.generatePlan({
|
|
268
|
+
workflow: issue.workflow,
|
|
269
|
+
issueTitle: issue.title,
|
|
270
|
+
issueDescription: issue.description,
|
|
271
|
+
issueNumber: issue.number,
|
|
272
|
+
});
|
|
273
|
+
const planId = plan.plan_id;
|
|
274
|
+
// Create branch
|
|
275
|
+
if (!options.noBranch) {
|
|
276
|
+
if (outputFormat === 'text') {
|
|
277
|
+
console.log(chalk.gray(` → Creating branch: ${branch}...`));
|
|
278
|
+
}
|
|
279
|
+
await repoClient.createBranch(branch);
|
|
280
|
+
}
|
|
281
|
+
// Create worktree
|
|
282
|
+
let worktreePath = worktree;
|
|
283
|
+
if (!options.noWorktree) {
|
|
284
|
+
if (outputFormat === 'text') {
|
|
285
|
+
console.log(chalk.gray(` → Creating worktree: ${worktree}...`));
|
|
286
|
+
}
|
|
287
|
+
const worktreeResult = await repoClient.createWorktree({
|
|
288
|
+
workId: issue.number.toString(),
|
|
289
|
+
path: worktree,
|
|
290
|
+
});
|
|
291
|
+
worktreePath = worktreeResult.absolute_path;
|
|
292
|
+
}
|
|
293
|
+
// Write plan to worktree
|
|
294
|
+
if (!options.noWorktree) {
|
|
295
|
+
// Validate plan ID format (prevent path traversal via malicious plan IDs)
|
|
296
|
+
validatePlanId(planId);
|
|
297
|
+
const planDir = path.join(worktreePath, '.fractary', 'plans');
|
|
298
|
+
await fs.mkdir(planDir, { recursive: true });
|
|
299
|
+
// Construct and validate path
|
|
300
|
+
const planPath = path.join(planDir, `${planId}.json`);
|
|
301
|
+
// Note: path.join automatically normalizes and prevents basic traversal,
|
|
302
|
+
// but we validate the plan ID format as an additional layer of defense
|
|
303
|
+
await fs.writeFile(planPath, JSON.stringify(plan, null, 2));
|
|
304
|
+
if (outputFormat === 'text') {
|
|
305
|
+
console.log(chalk.gray(` → Plan written to ${planPath}`));
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// Update GitHub issue with plan_id
|
|
309
|
+
if (outputFormat === 'text') {
|
|
310
|
+
console.log(chalk.gray(` → Updating GitHub issue...`));
|
|
311
|
+
}
|
|
312
|
+
await repoClient.updateIssue({
|
|
313
|
+
id: issue.number.toString(),
|
|
314
|
+
comment: `🤖 Workflow plan created: ${planId}`,
|
|
315
|
+
addLabel: 'faber:planned',
|
|
316
|
+
});
|
|
317
|
+
if (outputFormat === 'text') {
|
|
318
|
+
console.log(chalk.green(` ✓ Plan: ${planId}`));
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
issue,
|
|
322
|
+
planId,
|
|
323
|
+
branch,
|
|
324
|
+
worktree: worktreePath,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Get repository info from config
|
|
329
|
+
*/
|
|
330
|
+
function getRepoInfoFromConfig(config) {
|
|
331
|
+
return {
|
|
332
|
+
organization: config.github?.organization || 'unknown',
|
|
333
|
+
project: config.github?.project || 'unknown',
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Output text summary
|
|
338
|
+
*/
|
|
339
|
+
function outputTextSummary(results) {
|
|
340
|
+
console.log(chalk.cyan('\n' + '═'.repeat(50)));
|
|
341
|
+
const successful = results.filter(r => !r.error);
|
|
342
|
+
const failed = results.filter(r => r.error);
|
|
343
|
+
if (successful.length > 0) {
|
|
344
|
+
console.log(chalk.green(`\n✓ Planned ${successful.length} workflow(s) successfully:\n`));
|
|
345
|
+
successful.forEach((result, index) => {
|
|
346
|
+
console.log(chalk.bold(`[${index + 1}/${successful.length}] Issue #${result.issue.number}: ${result.issue.title}`));
|
|
347
|
+
console.log(chalk.gray(` Workflow: ${result.issue.workflow}`));
|
|
348
|
+
console.log(chalk.gray(` Plan: ${result.planId}`));
|
|
349
|
+
console.log(chalk.gray(` Branch: ${result.branch}`));
|
|
350
|
+
console.log(chalk.gray(` Worktree: ${result.worktree}`));
|
|
351
|
+
console.log();
|
|
352
|
+
console.log(chalk.cyan(' To execute:'));
|
|
353
|
+
console.log(chalk.gray(` cd ${result.worktree} && claude`));
|
|
354
|
+
console.log(chalk.gray(` /fractary-faber:workflow-run ${result.issue.number}`));
|
|
355
|
+
console.log();
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
if (failed.length > 0) {
|
|
359
|
+
console.log(chalk.red(`\n✗ Failed to plan ${failed.length} workflow(s):\n`));
|
|
360
|
+
failed.forEach((result, index) => {
|
|
361
|
+
console.log(chalk.bold(`[${index + 1}/${failed.length}] Issue #${result.issue.number}: ${result.issue.title}`));
|
|
362
|
+
console.log(chalk.red(` Error: ${result.error}`));
|
|
363
|
+
console.log();
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Error handling
|
|
369
|
+
*/
|
|
370
|
+
function handlePlanError(error, options) {
|
|
371
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
372
|
+
const outputFormat = options.json ? 'json' : options.output || 'text';
|
|
373
|
+
if (outputFormat === 'json') {
|
|
374
|
+
console.error(JSON.stringify({
|
|
375
|
+
status: 'error',
|
|
376
|
+
error: { code: 'PLAN_ERROR', message },
|
|
377
|
+
}));
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
console.error(chalk.red('Error:'), message);
|
|
381
|
+
}
|
|
382
|
+
process.exit(1);
|
|
383
|
+
}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC;;GAEG;AACH,wBAAgB,cAAc,IAAI,OAAO,CA2IxC"}
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import { createRepoCommand } from './commands/repo/index.js';
|
|
|
13
13
|
import { createSpecCommand } from './commands/spec/index.js';
|
|
14
14
|
import { createLogsCommand } from './commands/logs/index.js';
|
|
15
15
|
import { createInitCommand } from './commands/init.js';
|
|
16
|
+
import { createPlanCommand } from './commands/plan/index.js';
|
|
16
17
|
const version = '1.0.0';
|
|
17
18
|
/**
|
|
18
19
|
* Create and configure the main CLI program
|
|
@@ -27,6 +28,7 @@ export function createFaberCLI() {
|
|
|
27
28
|
program.option('--debug', 'Enable debug output');
|
|
28
29
|
// Workflow commands (top-level) - NEW NAMES
|
|
29
30
|
program.addCommand(createInitCommand()); // workflow-init
|
|
31
|
+
program.addCommand(createPlanCommand()); // plan - NEW
|
|
30
32
|
program.addCommand(createRunCommand()); // workflow-run
|
|
31
33
|
program.addCommand(createStatusCommand()); // workflow-status
|
|
32
34
|
program.addCommand(createResumeCommand()); // workflow-resume
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic API Client
|
|
3
|
+
*
|
|
4
|
+
* Generates workflow plans via Claude API
|
|
5
|
+
*/
|
|
6
|
+
import type { FaberConfig } from '../types/config.js';
|
|
7
|
+
interface GeneratePlanInput {
|
|
8
|
+
workflow: string;
|
|
9
|
+
issueTitle: string;
|
|
10
|
+
issueDescription: string;
|
|
11
|
+
issueNumber: number;
|
|
12
|
+
}
|
|
13
|
+
interface WorkflowPlan {
|
|
14
|
+
plan_id: string;
|
|
15
|
+
created_by: string;
|
|
16
|
+
cli_version: string;
|
|
17
|
+
created_at: string;
|
|
18
|
+
issue: {
|
|
19
|
+
source: string;
|
|
20
|
+
id: string;
|
|
21
|
+
url: string;
|
|
22
|
+
};
|
|
23
|
+
branch: string;
|
|
24
|
+
worktree: string;
|
|
25
|
+
workflow: string;
|
|
26
|
+
phases: any[];
|
|
27
|
+
[key: string]: any;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Anthropic API Client
|
|
31
|
+
*/
|
|
32
|
+
export declare class AnthropicClient {
|
|
33
|
+
private client;
|
|
34
|
+
private config;
|
|
35
|
+
private git;
|
|
36
|
+
private ajv;
|
|
37
|
+
private planSchema;
|
|
38
|
+
constructor(config: FaberConfig);
|
|
39
|
+
/**
|
|
40
|
+
* Load plan JSON schema for validation
|
|
41
|
+
*/
|
|
42
|
+
private loadPlanSchema;
|
|
43
|
+
/**
|
|
44
|
+
* Validate plan JSON against schema
|
|
45
|
+
*/
|
|
46
|
+
private validatePlan;
|
|
47
|
+
/**
|
|
48
|
+
* Generate workflow plan via Claude API
|
|
49
|
+
*/
|
|
50
|
+
generatePlan(input: GeneratePlanInput): Promise<WorkflowPlan>;
|
|
51
|
+
/**
|
|
52
|
+
* Load workflow configuration
|
|
53
|
+
*/
|
|
54
|
+
private loadWorkflowConfig;
|
|
55
|
+
/**
|
|
56
|
+
* Construct planning prompt for Claude
|
|
57
|
+
*/
|
|
58
|
+
private constructPlanningPrompt;
|
|
59
|
+
/**
|
|
60
|
+
* Extract JSON from Claude response
|
|
61
|
+
*/
|
|
62
|
+
private extractJsonFromResponse;
|
|
63
|
+
/**
|
|
64
|
+
* Extract repository organization and project name using SDK Git class
|
|
65
|
+
*/
|
|
66
|
+
private extractRepoInfo;
|
|
67
|
+
}
|
|
68
|
+
export {};
|
|
69
|
+
//# sourceMappingURL=anthropic-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic-client.d.ts","sourceRoot":"","sources":["../../src/lib/anthropic-client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAKtD,UAAU,iBAAiB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,YAAY;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAC;QACf,EAAE,EAAE,MAAM,CAAC;QACX,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,GAAG,EAAE,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,UAAU,CAAM;gBAEZ,MAAM,EAAE,WAAW;IAe/B;;OAEG;YACW,cAAc;IAiB5B;;OAEG;IACH,OAAO,CAAC,YAAY;IAepB;;OAEG;IACG,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC;IA6DnE;;OAEG;YACW,kBAAkB;IAchC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAqD/B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAgB/B;;OAEG;YACW,eAAe;CAqB9B"}
|