Skip to content

Commit 2f73411

Browse files
willwashburnclaude
andcommitted
fix(cli): read --version from package.json; label deterministic steps in dry-run
Two cosmetic fixes surfaced while validating the published 1.0.0 packages: - CLI `--version` was hardcoded to '0.1.0' and never tracked the version the publish workflow bumps in package.json (so it printed 0.1.0 for the 1.0.0 release). Read it from package.json at runtime via createRequire, which resolves the package root from both src/cli.ts and dist/cli.js and avoids a static JSON import that would fall outside tsconfig's rootDir. - The dry-run execution plan printed "(undefined)" for agent-less steps. Those are deterministic (shell/gate) steps; label them "(deterministic)" instead. Adds a focused formatDryRunReport unit test as a regression guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 4360cd7 commit 2f73411

3 files changed

Lines changed: 64 additions & 2 deletions

File tree

packages/cli/src/cli.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/env node
2+
import { createRequire } from 'node:module';
23
import path from 'node:path';
34
import { Command } from 'commander';
45
import {
@@ -45,12 +46,19 @@ async function runYamlWorkflow(
4546
return result;
4647
}
4748

49+
// Read the version from package.json at runtime so it stays in sync with the
50+
// version the publish workflow bumps. `../package.json` resolves to the package
51+
// root from both src/cli.ts (dev) and dist/cli.js (published). createRequire
52+
// avoids a static JSON import, which would fall outside tsconfig's rootDir.
53+
const require = createRequire(import.meta.url);
54+
const { version } = require('../package.json') as { version: string };
55+
4856
const program = new Command();
4957

5058
program
5159
.name('relayflows')
5260
.description('Run Agent Relay workflows from the command line')
53-
.version('0.1.0');
61+
.version(version);
5462

5563
program
5664
.command('run <file>')
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { describe, it, expect } from 'vitest';
2+
3+
import { formatDryRunReport } from '../dry-run-format.js';
4+
import type { DryRunReport } from '../types.js';
5+
6+
function makeReport(overrides: Partial<DryRunReport> = {}): DryRunReport {
7+
return {
8+
valid: true,
9+
errors: [],
10+
warnings: [],
11+
name: 'demo',
12+
pattern: 'dag',
13+
agents: [{ name: 'builder', cli: 'claude', stepCount: 1 }],
14+
waves: [
15+
{
16+
wave: 1,
17+
steps: [
18+
{ name: 'implement', agent: 'builder', dependsOn: [] },
19+
// Agent-less step => deterministic (shell/gate) step.
20+
{ name: 'test-gate', dependsOn: ['implement'] },
21+
],
22+
},
23+
],
24+
totalSteps: 2,
25+
estimatedWaves: 1,
26+
...overrides,
27+
};
28+
}
29+
30+
describe('formatDryRunReport', () => {
31+
it('labels agent-less steps as deterministic, not "undefined"', () => {
32+
const out = formatDryRunReport(makeReport());
33+
34+
expect(out).toContain('implement (builder)');
35+
expect(out).toContain('test-gate (deterministic)');
36+
// Regression guard: the literal "undefined" must never reach the plan.
37+
expect(out).not.toContain('(undefined)');
38+
});
39+
40+
it('still renders the agent name when a step has one', () => {
41+
const out = formatDryRunReport(
42+
makeReport({
43+
waves: [{ wave: 1, steps: [{ name: 'review', agent: 'reviewer', dependsOn: [] }] }],
44+
totalSteps: 1,
45+
})
46+
);
47+
48+
expect(out).toContain('review (reviewer)');
49+
expect(out).not.toContain('deterministic');
50+
});
51+
});

packages/core/src/dry-run-format.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ export function formatDryRunReport(report: DryRunReport): string {
5252
for (let i = 0; i < wave.steps.length; i++) {
5353
const step = wave.steps[i];
5454
const prefix = i === 0 ? ` Wave ${String(wave.wave).padStart(2)}:` : ' ';
55-
lines.push(`${prefix} ${step.name} (${step.agent})`);
55+
// Steps without an agent are deterministic (shell/gate) steps; label
56+
// them as such rather than printing the literal "undefined".
57+
const actor = step.agent ?? 'deterministic';
58+
lines.push(`${prefix} ${step.name} (${actor})`);
5659
}
5760
}
5861
lines.push('');

0 commit comments

Comments
 (0)