@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.
@@ -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,CA6e7C"}
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"}
@@ -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)}`);
@@ -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;AAoBpC;;GAEG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAqDxC"}
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":"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"}
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 matching faber-planner format: {org}-{project}-{subproject}-{timestamp}
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}-${timestamp}`;
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}-{YYYYMMDD}-{HHMMSS}
103
- * Also accepts legacy format: fractary-faber-{work-id}-{YYYYMMDD}-{HHMMSS}
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,CAYtD"}
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"}
@@ -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}-{YYYYMMDD}-{HHMMSS}
209
- * Also accepts legacy format: fractary-faber-{work-id}-{YYYYMMDD}-{HHMMSS}
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 one or more slug segments followed by -{digits}-{8digits}-{6digits}
217
- // Only [a-z0-9-] allowed, which inherently prevents path traversal
218
- const planIdPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*-\d+-\d{8}-\d{6}$/;
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}-{YYYYMMDD}-{HHMMSS}`);
221
+ throw new Error(`Invalid plan ID format: "${planId}". Expected format: {org}-{project}-{work-id}`);
221
222
  }
222
223
  return true;
223
224
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fractary/faber-cli",
3
- "version": "1.5.28",
3
+ "version": "1.5.30",
4
4
  "description": "FABER CLI - Command-line interface for FABER development toolkit",
5
5
  "main": "dist/index.js",
6
6
  "bin": {