@fractary/faber-cli 1.5.28 → 1.5.30
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/changelog/index.d.ts +11 -0
- package/dist/commands/changelog/index.d.ts.map +1 -0
- package/dist/commands/changelog/index.js +201 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/lib/anthropic-client.d.ts.map +1 -1
- package/dist/lib/anthropic-client.js +4 -3
- package/dist/utils/validation.d.ts +2 -2
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +7 -6
- package/package.json +1 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Changelog subcommand - Machine-readable changelog management
|
|
3
|
+
*
|
|
4
|
+
* Provides emit, query, aggregate, and read-run operations via ChangelogManager SDK.
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
/**
|
|
8
|
+
* Create the changelog command tree
|
|
9
|
+
*/
|
|
10
|
+
export declare function createChangelogCommand(): Command;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/changelog/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAUhD"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Changelog subcommand - Machine-readable changelog management
|
|
3
|
+
*
|
|
4
|
+
* Provides emit, query, aggregate, and read-run operations via ChangelogManager SDK.
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { ChangelogManager } from '@fractary/faber';
|
|
9
|
+
/**
|
|
10
|
+
* Create the changelog command tree
|
|
11
|
+
*/
|
|
12
|
+
export function createChangelogCommand() {
|
|
13
|
+
const changelog = new Command('changelog')
|
|
14
|
+
.description('Machine-readable changelog management');
|
|
15
|
+
changelog.addCommand(createChangelogEmitCommand());
|
|
16
|
+
changelog.addCommand(createChangelogQueryCommand());
|
|
17
|
+
changelog.addCommand(createChangelogAggregateCommand());
|
|
18
|
+
changelog.addCommand(createChangelogReadRunCommand());
|
|
19
|
+
return changelog;
|
|
20
|
+
}
|
|
21
|
+
function createChangelogEmitCommand() {
|
|
22
|
+
return new Command('emit')
|
|
23
|
+
.description('Emit a changelog entry for a completed step')
|
|
24
|
+
.requiredOption('--run-id <id>', 'Run identifier')
|
|
25
|
+
.requiredOption('--step-id <id>', 'Step identifier')
|
|
26
|
+
.requiredOption('--step-name <name>', 'Human-readable step name')
|
|
27
|
+
.requiredOption('--phase <phase>', 'FABER phase')
|
|
28
|
+
.requiredOption('--status <status>', 'Step status (success, warning, failure, skipped)')
|
|
29
|
+
.requiredOption('--event-type <type>', 'Semantic event type (e.g., CODE_MERGED)')
|
|
30
|
+
.requiredOption('--workflow-id <id>', 'Workflow identifier')
|
|
31
|
+
.requiredOption('--work-id <id>', 'Work item identifier')
|
|
32
|
+
.option('--target <target>', 'Target branch/environment/resource')
|
|
33
|
+
.option('--environment <env>', 'Target environment (test, prod)')
|
|
34
|
+
.option('--message <msg>', 'Human-readable description')
|
|
35
|
+
.option('--duration-ms <ms>', 'Step duration in milliseconds')
|
|
36
|
+
.option('--metadata <json>', 'Step-specific metadata (JSON string)')
|
|
37
|
+
.option('--custom <json>', 'Project-specific custom data (JSON string)')
|
|
38
|
+
.option('--json', 'Output as JSON')
|
|
39
|
+
.action(async (options) => {
|
|
40
|
+
try {
|
|
41
|
+
const manager = new ChangelogManager();
|
|
42
|
+
const entry = manager.emit({
|
|
43
|
+
run_id: options.runId,
|
|
44
|
+
step_id: options.stepId,
|
|
45
|
+
step_name: options.stepName,
|
|
46
|
+
phase: options.phase,
|
|
47
|
+
status: options.status,
|
|
48
|
+
event_type: options.eventType,
|
|
49
|
+
workflow_id: options.workflowId,
|
|
50
|
+
work_id: options.workId,
|
|
51
|
+
target: options.target,
|
|
52
|
+
environment: options.environment,
|
|
53
|
+
message: options.message,
|
|
54
|
+
duration_ms: options.durationMs ? parseInt(options.durationMs, 10) : undefined,
|
|
55
|
+
metadata: options.metadata ? JSON.parse(options.metadata) : undefined,
|
|
56
|
+
custom: options.custom ? JSON.parse(options.custom) : undefined,
|
|
57
|
+
});
|
|
58
|
+
if (options.json) {
|
|
59
|
+
console.log(JSON.stringify({ status: 'success', data: entry }, null, 2));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log(chalk.green(`Emitted changelog entry: ${entry.event_type}`));
|
|
63
|
+
console.log(chalk.gray(` Event ID: ${entry.event_id}`));
|
|
64
|
+
console.log(chalk.gray(` Step: ${entry.step_name}`));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
handleChangelogError(error, options);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function createChangelogQueryCommand() {
|
|
73
|
+
return new Command('query')
|
|
74
|
+
.description('Query the project-level changelog')
|
|
75
|
+
.option('--event-type <type>', 'Filter by event type')
|
|
76
|
+
.option('--target <target>', 'Filter by target')
|
|
77
|
+
.option('--phase <phase>', 'Filter by phase')
|
|
78
|
+
.option('--status <status>', 'Filter by status')
|
|
79
|
+
.option('--work-id <id>', 'Filter by work item ID')
|
|
80
|
+
.option('--since <date>', 'Filter entries after this date (ISO 8601)')
|
|
81
|
+
.option('--until <date>', 'Filter entries before this date (ISO 8601)')
|
|
82
|
+
.option('--limit <n>', 'Maximum entries to return')
|
|
83
|
+
.option('--json', 'Output as JSON')
|
|
84
|
+
.action(async (options) => {
|
|
85
|
+
try {
|
|
86
|
+
const manager = new ChangelogManager();
|
|
87
|
+
const result = manager.query({
|
|
88
|
+
event_type: options.eventType,
|
|
89
|
+
target: options.target,
|
|
90
|
+
phase: options.phase,
|
|
91
|
+
status: options.status,
|
|
92
|
+
work_id: options.workId,
|
|
93
|
+
since: options.since,
|
|
94
|
+
until: options.until,
|
|
95
|
+
limit: options.limit ? parseInt(options.limit, 10) : undefined,
|
|
96
|
+
});
|
|
97
|
+
if (options.json) {
|
|
98
|
+
console.log(JSON.stringify({ status: 'success', data: result }, null, 2));
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
if (result.entries.length === 0) {
|
|
102
|
+
console.log(chalk.yellow('No changelog entries found'));
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
console.log(chalk.bold(`Changelog entries (${result.entries.length} of ${result.total}):\n`));
|
|
106
|
+
for (const entry of result.entries) {
|
|
107
|
+
const statusColor = entry.status === 'success' ? chalk.green
|
|
108
|
+
: entry.status === 'failure' ? chalk.red
|
|
109
|
+
: entry.status === 'warning' ? chalk.yellow
|
|
110
|
+
: chalk.gray;
|
|
111
|
+
console.log(`${statusColor(entry.status.padEnd(8))} ${chalk.cyan(entry.event_type.padEnd(22))} ${entry.step_name}`);
|
|
112
|
+
console.log(chalk.gray(` ${entry.timestamp} phase:${entry.phase} work:${entry.work_id}${entry.environment ? ' env:' + entry.environment : ''}`));
|
|
113
|
+
if (entry.message) {
|
|
114
|
+
console.log(chalk.gray(` ${entry.message}`));
|
|
115
|
+
}
|
|
116
|
+
console.log('');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
handleChangelogError(error, options);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function createChangelogAggregateCommand() {
|
|
127
|
+
return new Command('aggregate')
|
|
128
|
+
.description('Aggregate per-run changelog to project-level file')
|
|
129
|
+
.requiredOption('--run-id <id>', 'Run identifier to aggregate')
|
|
130
|
+
.option('--json', 'Output as JSON')
|
|
131
|
+
.action(async (options) => {
|
|
132
|
+
try {
|
|
133
|
+
const manager = new ChangelogManager();
|
|
134
|
+
const result = manager.aggregate(options.runId);
|
|
135
|
+
if (options.json) {
|
|
136
|
+
console.log(JSON.stringify({ status: 'success', data: result }, null, 2));
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
if (result.entries_aggregated === 0) {
|
|
140
|
+
console.log(chalk.yellow('No entries to aggregate'));
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
console.log(chalk.green(`Aggregated ${result.entries_aggregated} changelog entries`));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
handleChangelogError(error, options);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
function createChangelogReadRunCommand() {
|
|
153
|
+
return new Command('read-run')
|
|
154
|
+
.description('Read changelog entries for a specific run')
|
|
155
|
+
.requiredOption('--run-id <id>', 'Run identifier')
|
|
156
|
+
.option('--json', 'Output as JSON')
|
|
157
|
+
.action(async (options) => {
|
|
158
|
+
try {
|
|
159
|
+
const manager = new ChangelogManager();
|
|
160
|
+
const entries = manager.readRun(options.runId);
|
|
161
|
+
if (options.json) {
|
|
162
|
+
console.log(JSON.stringify({ status: 'success', data: { entries, total: entries.length } }, null, 2));
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
if (entries.length === 0) {
|
|
166
|
+
console.log(chalk.yellow('No changelog entries for this run'));
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
console.log(chalk.bold(`Run changelog (${entries.length} entries):\n`));
|
|
170
|
+
for (const entry of entries) {
|
|
171
|
+
const statusColor = entry.status === 'success' ? chalk.green
|
|
172
|
+
: entry.status === 'failure' ? chalk.red
|
|
173
|
+
: entry.status === 'warning' ? chalk.yellow
|
|
174
|
+
: chalk.gray;
|
|
175
|
+
console.log(`${statusColor(entry.status.padEnd(8))} ${chalk.cyan(entry.event_type.padEnd(22))} ${entry.step_name}`);
|
|
176
|
+
if (entry.message) {
|
|
177
|
+
console.log(chalk.gray(` ${entry.message}`));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
handleChangelogError(error, options);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
// Error handling
|
|
189
|
+
function handleChangelogError(error, options) {
|
|
190
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
191
|
+
if (options.json) {
|
|
192
|
+
console.error(JSON.stringify({
|
|
193
|
+
status: 'error',
|
|
194
|
+
error: { code: 'CHANGELOG_ERROR', message },
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
console.error(chalk.red('Error:'), message);
|
|
199
|
+
}
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsCpC,wBAAgB,mBAAmB,IAAI,OAAO,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsCpC,wBAAgB,mBAAmB,IAAI,OAAO,CAkf7C"}
|
package/dist/commands/config.js
CHANGED
|
@@ -127,6 +127,7 @@ export function createConfigCommand() {
|
|
|
127
127
|
.option('--default-workflow <id>', 'Default workflow ID', 'default')
|
|
128
128
|
.option('--autonomy <level>', 'Autonomy level (dry-run|assisted|guarded|autonomous)', 'guarded')
|
|
129
129
|
.option('--runs-path <path>', 'Directory for run artifacts', '.fractary/faber/runs')
|
|
130
|
+
.option('--changelog-path <path>', 'Path for project-level changelog file', '.fractary/changelog.ndjson')
|
|
130
131
|
.option('--force', 'Overwrite existing configuration')
|
|
131
132
|
.action(async (options) => {
|
|
132
133
|
try {
|
|
@@ -155,6 +156,9 @@ export function createConfigCommand() {
|
|
|
155
156
|
runs: {
|
|
156
157
|
path: options.runsPath,
|
|
157
158
|
},
|
|
159
|
+
changelog: {
|
|
160
|
+
path: options.changelogPath,
|
|
161
|
+
},
|
|
158
162
|
};
|
|
159
163
|
// Load existing config or create empty
|
|
160
164
|
let config = loadYamlConfig({ warnMissingEnvVars: false }) || { version: '2.0' };
|
|
@@ -197,6 +201,7 @@ ${yaml.dump(manifest, { indent: 2, lineWidth: 100 })}`;
|
|
|
197
201
|
console.log(` Default workflow: ${options.defaultWorkflow}`);
|
|
198
202
|
console.log(` Autonomy: ${options.autonomy}`);
|
|
199
203
|
console.log(` Runs path: ${options.runsPath}`);
|
|
204
|
+
console.log(` Changelog path: ${options.changelogPath}`);
|
|
200
205
|
}
|
|
201
206
|
catch (error) {
|
|
202
207
|
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
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;AAMH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;GAKG;AAMH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqBpC;;GAEG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAsDxC"}
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import { createSessionLoadCommand, createSessionSaveCommand } from './commands/s
|
|
|
15
15
|
import { createWorkCommand } from './commands/work/index.js';
|
|
16
16
|
import { createRepoCommand } from './commands/repo/index.js';
|
|
17
17
|
import { createLogsCommand } from './commands/logs/index.js';
|
|
18
|
+
import { createChangelogCommand } from './commands/changelog/index.js';
|
|
18
19
|
import { createMigrateCommand } from './commands/migrate.js';
|
|
19
20
|
import { createPlanCommand } from './commands/plan/index.js';
|
|
20
21
|
import { createAuthCommand } from './commands/auth/index.js';
|
|
@@ -59,6 +60,7 @@ export function createFaberCLI() {
|
|
|
59
60
|
program.addCommand(createWorkCommand());
|
|
60
61
|
program.addCommand(createRepoCommand());
|
|
61
62
|
program.addCommand(createLogsCommand());
|
|
63
|
+
program.addCommand(createChangelogCommand());
|
|
62
64
|
// Help command
|
|
63
65
|
program.addCommand(new Command('help')
|
|
64
66
|
.description('Show help for all commands')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-client.d.ts","sourceRoot":"","sources":["../../src/lib/anthropic-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAE/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAK5D,UAAU,iBAAiB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,SAAS,CAAC;IACzB,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,cAAc,EAAE,IAAI,CAAC;IACrB,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;KACpC,CAAC;IACF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE;QACR,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,aAAa,EAAE,SAAS,CAAC;QACzB,YAAY,EAAE,IAAI,CAAC;QACnB,aAAa,EAAE,IAAI,CAAC;KACrB,CAAC;IACF,QAAQ,EAAE;QACR,EAAE,EAAE,MAAM,CAAC;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,iBAAiB,EAAE,MAAM,EAAE,CAAC;QAC5B,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;KACpC,CAAC;IACF,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC/B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,GAAG,UAAU,CAAC;QAChC,cAAc,EAAE,MAAM,CAAC;QACvB,MAAM,EAAE,SAAS,CAAC;QAClB,UAAU,EAAE,IAAI,CAAC;QACjB,YAAY,EAAE,IAAI,CAAC;QACnB,OAAO,EAAE,KAAK,EAAE,CAAC;KAClB,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,UAAU,CAAM;gBAEZ,MAAM,EAAE,iBAAiB;IAMrC;;OAEG;YACW,cAAc;IAe5B;;OAEG;IACH,OAAO,CAAC,YAAY;IAcpB;;;;;;OAMG;IACG,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"anthropic-client.d.ts","sourceRoot":"","sources":["../../src/lib/anthropic-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAE/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAK5D,UAAU,iBAAiB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,SAAS,CAAC;IACzB,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,cAAc,EAAE,IAAI,CAAC;IACrB,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;KACpC,CAAC;IACF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE;QACR,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,aAAa,EAAE,SAAS,CAAC;QACzB,YAAY,EAAE,IAAI,CAAC;QACnB,aAAa,EAAE,IAAI,CAAC;KACrB,CAAC;IACF,QAAQ,EAAE;QACR,EAAE,EAAE,MAAM,CAAC;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,iBAAiB,EAAE,MAAM,EAAE,CAAC;QAC5B,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;KACpC,CAAC;IACF,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC/B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,GAAG,UAAU,CAAC;QAChC,cAAc,EAAE,MAAM,CAAC;QACvB,MAAM,EAAE,SAAS,CAAC;QAClB,UAAU,EAAE,IAAI,CAAC;QACjB,YAAY,EAAE,IAAI,CAAC;QACnB,OAAO,EAAE,KAAK,EAAE,CAAC;KAClB,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,UAAU,CAAM;gBAEZ,MAAM,EAAE,iBAAiB;IAMrC;;OAEG;YACW,cAAc;IAe5B;;OAEG;IACH,OAAO,CAAC,YAAY;IAcpB;;;;;;OAMG;IACG,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC;IA+FnE;;OAEG;YACW,eAAe;CAoB9B"}
|
|
@@ -71,7 +71,9 @@ export class AnthropicClient {
|
|
|
71
71
|
const workflowConfig = await resolver.resolveWorkflow(input.workflow);
|
|
72
72
|
// Extract repo info
|
|
73
73
|
const { organization, project } = await this.extractRepoInfo();
|
|
74
|
-
// Generate plan ID
|
|
74
|
+
// Generate plan ID: {org}-{project}-{work-id}
|
|
75
|
+
// Timestamp is omitted from plan-id since run-ids already contain timestamps.
|
|
76
|
+
// This keeps plan directories human-readable and scoped to the work item.
|
|
75
77
|
const now = new Date();
|
|
76
78
|
const year = now.getFullYear().toString();
|
|
77
79
|
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
@@ -79,9 +81,8 @@ export class AnthropicClient {
|
|
|
79
81
|
const hour = String(now.getHours()).padStart(2, '0');
|
|
80
82
|
const minute = String(now.getMinutes()).padStart(2, '0');
|
|
81
83
|
const second = String(now.getSeconds()).padStart(2, '0');
|
|
82
|
-
const timestamp = `${year}${month}${day}-${hour}${minute}${second}`;
|
|
83
84
|
const subproject = `issue-${input.issueNumber}`;
|
|
84
|
-
const planId = `${slugify(organization)}-${slugify(project)}-${input.issueNumber}
|
|
85
|
+
const planId = `${slugify(organization)}-${slugify(project)}-${input.issueNumber}`;
|
|
85
86
|
// Build the plan deterministically — no LLM call needed
|
|
86
87
|
const plan = {
|
|
87
88
|
id: planId,
|
|
@@ -99,8 +99,8 @@ export declare function validateJsonSize(jsonString: string, maxSizeBytes?: numb
|
|
|
99
99
|
export declare function slugify(input: string): string;
|
|
100
100
|
/**
|
|
101
101
|
* Validates plan ID format
|
|
102
|
-
* Plan IDs follow format: {org}-{project}-{work-id}
|
|
103
|
-
* Also accepts legacy format:
|
|
102
|
+
* Plan IDs follow format: {org}-{project}-{work-id}
|
|
103
|
+
* Also accepts legacy format with timestamp: {org}-{project}-{work-id}-{YYYYMMDD}-{HHMMSS}
|
|
104
104
|
*
|
|
105
105
|
* @param planId - Plan ID to validate
|
|
106
106
|
* @returns True if valid
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAY1E;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMrG;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAQ7E;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAWtD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBzD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAYpD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBvD;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAYlE;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAoC3E;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,GAAE,MAAoB,GAAG,OAAO,CAYhG;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM7C;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAY1E;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMrG;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAQ7E;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAWtD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBzD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAYpD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBvD;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAYlE;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAoC3E;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,GAAE,MAAoB,GAAG,OAAO,CAYhG;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM7C;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAatD"}
|
package/dist/utils/validation.js
CHANGED
|
@@ -205,19 +205,20 @@ export function slugify(input) {
|
|
|
205
205
|
}
|
|
206
206
|
/**
|
|
207
207
|
* Validates plan ID format
|
|
208
|
-
* Plan IDs follow format: {org}-{project}-{work-id}
|
|
209
|
-
* Also accepts legacy format:
|
|
208
|
+
* Plan IDs follow format: {org}-{project}-{work-id}
|
|
209
|
+
* Also accepts legacy format with timestamp: {org}-{project}-{work-id}-{YYYYMMDD}-{HHMMSS}
|
|
210
210
|
*
|
|
211
211
|
* @param planId - Plan ID to validate
|
|
212
212
|
* @returns True if valid
|
|
213
213
|
* @throws Error if invalid
|
|
214
214
|
*/
|
|
215
215
|
export function validatePlanId(planId) {
|
|
216
|
-
// Accepts
|
|
217
|
-
//
|
|
218
|
-
|
|
216
|
+
// Accepts slug segments. The new format ends with a work-id (digits or slug).
|
|
217
|
+
// Legacy format may end with -{8digits}-{6digits} timestamp suffix.
|
|
218
|
+
// Only [a-z0-9-] allowed, which inherently prevents path traversal.
|
|
219
|
+
const planIdPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*(?:-\d{8}-\d{6})?$/;
|
|
219
220
|
if (!planIdPattern.test(planId)) {
|
|
220
|
-
throw new Error(`Invalid plan ID format: "${planId}". Expected format: {org}-{project}-{work-id}
|
|
221
|
+
throw new Error(`Invalid plan ID format: "${planId}". Expected format: {org}-{project}-{work-id}`);
|
|
221
222
|
}
|
|
222
223
|
return true;
|
|
223
224
|
}
|