agentic-workflow-manager 8.0.0 → 8.1.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.
@@ -24,6 +24,12 @@ function fakeChild(pid = 4242) {
24
24
  child.unref = jest.fn();
25
25
  return child;
26
26
  }
27
+ function restoreEnvironment(name, value) {
28
+ if (value === undefined)
29
+ delete process.env[name];
30
+ else
31
+ process.env[name] = value;
32
+ }
27
33
  describe('runCommand — win32', () => {
28
34
  const originalPlatform = process.platform;
29
35
  beforeEach(() => {
@@ -58,11 +64,35 @@ describe('runCommand — win32', () => {
58
64
  expect(mockSpawn).toHaveBeenCalledWith(path_1.default.join(dir, 'tool.exe'), ['literal;&'], expect.objectContaining({ shell: false, detached: false }));
59
65
  child.emit('close', 0, null);
60
66
  await expect(pending).resolves.toMatchObject({ code: 0 });
61
- expect(() => (0, exec_1.runStructuredCommand)({ executable: 'tool.cmd', resolution: 'path', args: [] }, { timeout: 5000, cwd: dir })).toThrow(/wrappers/);
67
+ expect(() => (0, exec_1.runStructuredCommand)({ executable: 'tool.cmd', resolution: 'path', args: ['--version'] }, { timeout: 5000, cwd: dir })).toThrow(/wrappers/);
68
+ }
69
+ finally {
70
+ restoreEnvironment('PATH', savedPath);
71
+ restoreEnvironment('PATHEXT', savedPathExt);
72
+ fs_1.default.rmSync(dir, { recursive: true, force: true });
73
+ }
74
+ });
75
+ it('spawns the allowlisted flat-config environment for ESLint 8', async () => {
76
+ const dir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-structured-win-env-'));
77
+ const savedPath = process.env.PATH;
78
+ const savedPathExt = process.env.PATHEXT;
79
+ try {
80
+ fs_1.default.writeFileSync(path_1.default.join(dir, 'tool.exe'), 'fixture');
81
+ process.env.PATH = dir;
82
+ process.env.PATHEXT = '.EXE';
83
+ const child = fakeChild();
84
+ mockSpawn.mockReturnValue(child);
85
+ const pending = (0, exec_1.runStructuredCommand)({ executable: 'tool', resolution: 'path', args: ['--version'], environment: { ESLINT_USE_FLAT_CONFIG: 'true' } }, { timeout: 5000, cwd: dir });
86
+ expect(mockSpawn).toHaveBeenCalledWith(path_1.default.join(dir, 'tool.exe'), ['--version'], expect.objectContaining({
87
+ shell: false,
88
+ env: expect.objectContaining({ ESLINT_USE_FLAT_CONFIG: 'true' }),
89
+ }));
90
+ child.emit('close', 0, null);
91
+ await expect(pending).resolves.toMatchObject({ code: 0 });
62
92
  }
63
93
  finally {
64
- process.env.PATH = savedPath;
65
- process.env.PATHEXT = savedPathExt;
94
+ restoreEnvironment('PATH', savedPath);
95
+ restoreEnvironment('PATHEXT', savedPathExt);
66
96
  fs_1.default.rmSync(dir, { recursive: true, force: true });
67
97
  }
68
98
  });
@@ -80,14 +110,14 @@ describe('runCommand — win32', () => {
80
110
  process.env.PATHEXT = '.EXE';
81
111
  const child = fakeChild();
82
112
  mockSpawn.mockReturnValue(child);
83
- const pending = (0, exec_1.runStructuredCommand)({ executable: 'tool', resolution: 'node-modules-bin', args: [] }, { timeout: 5000, cwd: dir });
84
- expect(mockSpawn).toHaveBeenCalledWith(path_1.default.join(localBin, 'tool.exe'), [], expect.objectContaining({ shell: false }));
113
+ const pending = (0, exec_1.runStructuredCommand)({ executable: 'tool', resolution: 'node-modules-bin', args: ['--version'] }, { timeout: 5000, cwd: dir });
114
+ expect(mockSpawn).toHaveBeenCalledWith(path_1.default.join(localBin, 'tool.exe'), ['--version'], expect.objectContaining({ shell: false }));
85
115
  child.emit('close', 0, null);
86
116
  await expect(pending).resolves.toMatchObject({ code: 0 });
87
117
  }
88
118
  finally {
89
- process.env.PATH = savedPath;
90
- process.env.PATHEXT = savedPathExt;
119
+ restoreEnvironment('PATH', savedPath);
120
+ restoreEnvironment('PATHEXT', savedPathExt);
91
121
  fs_1.default.rmSync(dir, { recursive: true, force: true });
92
122
  fs_1.default.rmSync(global, { recursive: true, force: true });
93
123
  }
@@ -100,16 +130,34 @@ describe('runCommand — win32', () => {
100
130
  fs_1.default.writeFileSync(path_1.default.join(global, 'tool.exe'), 'global');
101
131
  process.env.PATH = global;
102
132
  mockSpawn.mockReturnValue(fakeChild());
103
- expect(() => (0, exec_1.runStructuredCommand)({ executable: 'tool', resolution: 'node-modules-bin', args: [] }, { timeout: 5000, cwd: dir }))
133
+ expect(() => (0, exec_1.runStructuredCommand)({ executable: 'tool', resolution: 'node-modules-bin', args: ['--version'] }, { timeout: 5000, cwd: dir }))
104
134
  .toThrow(/node_modules.*not found locally/i);
105
135
  expect(mockSpawn).not.toHaveBeenCalled();
106
136
  }
107
137
  finally {
108
- process.env.PATH = savedPath;
138
+ restoreEnvironment('PATH', savedPath);
109
139
  fs_1.default.rmSync(dir, { recursive: true, force: true });
110
140
  fs_1.default.rmSync(global, { recursive: true, force: true });
111
141
  }
112
142
  });
143
+ it('fails closed when PATHEXT contains no safe executable extension', () => {
144
+ const dir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-structured-win-pathext-'));
145
+ const savedPath = process.env.PATH;
146
+ const savedPathExt = process.env.PATHEXT;
147
+ try {
148
+ fs_1.default.writeFileSync(path_1.default.join(dir, 'tool.cmd'), 'wrapper');
149
+ process.env.PATH = dir;
150
+ process.env.PATHEXT = '.CMD';
151
+ expect(() => (0, exec_1.runStructuredCommand)({ executable: 'tool', resolution: 'path', args: ['--version'] }, { timeout: 5000, cwd: dir }))
152
+ .toThrow(/safe Windows executable|PATHEXT/i);
153
+ expect(mockSpawn).not.toHaveBeenCalled();
154
+ }
155
+ finally {
156
+ restoreEnvironment('PATH', savedPath);
157
+ restoreEnvironment('PATHEXT', savedPathExt);
158
+ fs_1.default.rmSync(dir, { recursive: true, force: true });
159
+ }
160
+ });
113
161
  it('rejects a missing Python environment executable instead of falling back to a same-named global command', () => {
114
162
  const dir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-python-env-missing-win-'));
115
163
  const global = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-python-env-global-win-'));
@@ -123,7 +171,7 @@ describe('runCommand — win32', () => {
123
171
  expect(mockSpawn).not.toHaveBeenCalled();
124
172
  }
125
173
  finally {
126
- process.env.PATH = savedPath;
174
+ restoreEnvironment('PATH', savedPath);
127
175
  fs_1.default.rmSync(dir, { recursive: true, force: true });
128
176
  fs_1.default.rmSync(global, { recursive: true, force: true });
129
177
  }
@@ -27,7 +27,7 @@ describe('runCommand — exit codes and output', () => {
27
27
  const literal = `;touch ${marker}`;
28
28
  const received = path_1.default.join(dir, 'received');
29
29
  const result = await (0, exec_1.runStructuredCommand)({
30
- executable: process.execPath,
30
+ executable: 'node',
31
31
  resolution: 'path',
32
32
  args: ['-e', "require('fs').writeFileSync(process.argv[1], process.argv[2])", received, literal],
33
33
  }, { timeout: 5000, cwd: dir });
@@ -130,6 +130,36 @@ describe('runCommand — spawn failure', () => {
130
130
  expect(r.code).not.toBe(0);
131
131
  });
132
132
  });
133
+ describe('runStructuredCommand — public boundary validation', () => {
134
+ it.each(['sh', 'cmd.exe', '/usr/bin/node', 'nested/tool', 'nested\\tool'])('rejects unsafe executable %j before resolution', executable => {
135
+ expect(() => (0, exec_1.runStructuredCommand)({ executable, resolution: 'path', args: ['--version'] }, { timeout: 5000, cwd: process.cwd() }))
136
+ .toThrow(/safe executable name|shell/i);
137
+ });
138
+ it('normalizes package-manager spellings while requiring an equivalent explicit selection', () => {
139
+ expect(() => (0, exec_1.runStructuredCommand)({ executable: 'NPM.exe', resolution: 'path', args: ['--version'] }, { timeout: 5000, cwd: process.cwd() }))
140
+ .toThrow(/packageManager/);
141
+ expect(() => (0, exec_1.runStructuredCommand)({ executable: 'NPM.exe', resolution: 'path', args: ['--version'], packageManager: 'pnpm' }, { timeout: 5000, cwd: process.cwd() }))
142
+ .toThrow(/packageManager/);
143
+ });
144
+ it.each([
145
+ { ESLINT_USE_FLAT_CONFIG: 'invalid' },
146
+ { ESLINT_USE_FLAT_CONFIG: 'true', OTHER: 'value' },
147
+ ])('rejects an unknown or expanded environment mapping at the public boundary', environment => {
148
+ expect(() => (0, exec_1.runStructuredCommand)({ executable: 'node', resolution: 'path', args: ['--version'], environment }, { timeout: 5000, cwd: process.cwd() }))
149
+ .toThrow(/exact allowlisted/);
150
+ });
151
+ it.each([
152
+ [{ executable: 'node', resolution: 'path', args: [] }, /nonempty array/],
153
+ [{ executable: 'node', resolution: 'path', args: [''] }, /nonempty single-line/],
154
+ [{ executable: 'node', resolution: 'path', args: ['one\ntwo'] }, /single-line/],
155
+ [{ executable: 'node', resolution: 'path', args: ['{files}'] }, /requires fileInput/],
156
+ [{ executable: 'node', resolution: 'path', args: ['prefix{files}'], fileInput: { placeholder: '{files}', extensions: ['.ts'] } }, /must not embed/],
157
+ [{ executable: 'node', resolution: 'path', args: ['{files}', '{files}'], fileInput: { placeholder: '{files}', extensions: ['.ts'] } }, /exactly one/],
158
+ [{ executable: 'node', resolution: 'path', args: ['--check'], fileInput: { placeholder: '{files}', extensions: ['.ts'] } }, /exactly one/],
159
+ ])('rejects malformed args or file input at the public boundary', (command, message) => {
160
+ expect(() => (0, exec_1.runStructuredCommand)(command, { timeout: 5000, cwd: process.cwd() })).toThrow(message);
161
+ });
162
+ });
133
163
  onPosix('runStructuredCommand — local node_modules binaries', () => {
134
164
  it('follows an npm-style contained shim but rejects one escaping node_modules', async () => {
135
165
  const dir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-local-bin-symlink-'));
@@ -143,11 +173,11 @@ onPosix('runStructuredCommand — local node_modules binaries', () => {
143
173
  fs_1.default.writeFileSync(outside, '#!/bin/sh\necho outside\n', { mode: 0o755 });
144
174
  fs_1.default.mkdirSync(bin, { recursive: true });
145
175
  fs_1.default.symlinkSync('../tool/bin/tool', path_1.default.join(bin, 'tool'));
146
- await expect((0, exec_1.runStructuredCommand)({ executable: 'tool', resolution: 'node-modules-bin', args: [] }, { timeout: 5000, cwd: dir }))
176
+ await expect((0, exec_1.runStructuredCommand)({ executable: 'tool', resolution: 'node-modules-bin', args: ['--version'] }, { timeout: 5000, cwd: dir }))
147
177
  .resolves.toMatchObject({ code: 0, stdout: 'contained\n' });
148
178
  fs_1.default.unlinkSync(path_1.default.join(bin, 'tool'));
149
179
  fs_1.default.symlinkSync(outside, path_1.default.join(bin, 'tool'));
150
- expect(() => (0, exec_1.runStructuredCommand)({ executable: 'tool', resolution: 'node-modules-bin', args: [] }, { timeout: 5000, cwd: dir }))
180
+ expect(() => (0, exec_1.runStructuredCommand)({ executable: 'tool', resolution: 'node-modules-bin', args: ['--version'] }, { timeout: 5000, cwd: dir }))
151
181
  .toThrow(/node_modules.*contain|local/i);
152
182
  }
153
183
  finally {
@@ -156,6 +186,24 @@ onPosix('runStructuredCommand — local node_modules binaries', () => {
156
186
  });
157
187
  });
158
188
  onPosix('runStructuredCommand — Python environments', () => {
189
+ it.each(['.venv', 'venv'])('rejects a symlinked %s ancestor instead of executing outside the project', environment => {
190
+ const dir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-python-env-ancestor-'));
191
+ const outside = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-python-env-outside-'));
192
+ const marker = path_1.default.join(dir, 'must-not-exist');
193
+ try {
194
+ const executable = path_1.default.join(outside, 'bin', 'semgrep');
195
+ fs_1.default.mkdirSync(path_1.default.dirname(executable), { recursive: true });
196
+ fs_1.default.writeFileSync(executable, `#!/bin/sh\ntouch ${marker}\n`, { mode: 0o755 });
197
+ fs_1.default.symlinkSync(outside, path_1.default.join(dir, environment));
198
+ expect(() => (0, exec_1.runStructuredCommand)({ executable: 'semgrep', resolution: 'python-environment', pythonEnvironmentRoot: environment, args: ['--validate'] }, { timeout: 5000, cwd: dir }))
199
+ .toThrow(/python.*environment.*local|contained/i);
200
+ expect(fs_1.default.existsSync(marker)).toBe(false);
201
+ }
202
+ finally {
203
+ fs_1.default.rmSync(dir, { recursive: true, force: true });
204
+ fs_1.default.rmSync(outside, { recursive: true, force: true });
205
+ }
206
+ });
159
207
  it('rejects a missing project Python executable instead of falling back to PATH', () => {
160
208
  const dir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-python-env-missing-'));
161
209
  const global = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-python-env-global-'));
@@ -7,6 +7,7 @@ const fs_1 = __importDefault(require("fs"));
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const os_1 = __importDefault(require("os"));
9
9
  const init_1 = require("../../../src/commands/sensors/init");
10
+ const status_1 = require("../../../src/commands/sensors/status");
10
11
  // Build a throwaway registry with a js-ts pack.json (the single source of truth
11
12
  // init now reads from). `defaultCmd` uses the {{SOURCE_DIRS}} placeholder for depcheck.
12
13
  function makeRegistry() {
@@ -45,9 +46,11 @@ function makeV2Registry() {
45
46
  const packDir = path_1.default.join(registryRoot, 'sensor-packs', 'js-ts');
46
47
  fs_1.default.mkdirSync(packDir, { recursive: true });
47
48
  fs_1.default.writeFileSync(path_1.default.join(packDir, 'eslint.config.awm.mjs'), 'export default []\n');
49
+ fs_1.default.writeFileSync(path_1.default.join(packDir, 'tsconfig.awm.json'), '{ "compilerOptions": { "strict": true } }\n');
48
50
  fs_1.default.writeFileSync(path_1.default.join(packDir, 'pack.json'), JSON.stringify({
49
51
  schemaVersion: 2, name: 'js-ts', description: 'fixture', detects: ['package.json'],
50
52
  coverage: { schemaVersion: 1, classes: { lint: { description: 'lint', detectors: [{ sensor: 'lint' }], remedy: { summary: 'fix lint', command: 'awm sensors init --pack js-ts' } } } },
53
+ hardening: { 'typescript-strict': { assets: ['tsconfig.awm.json'] } },
51
54
  sensors: { lint: { applicability: { allFiles: ['package.json'] }, variants: [{
52
55
  id: 'eslint-10', priority: 10, certifiedRange: '>=10.0.0 <11.0.0',
53
56
  requirements: { tool: 'eslint', toolRange: '>=10.0.0 <11.0.0', runtime: 'node', runtimeRange: '>=0.0.0' },
@@ -57,6 +60,23 @@ function makeV2Registry() {
57
60
  }));
58
61
  return registryRoot;
59
62
  }
63
+ function makeGenericV2Registry() {
64
+ const registryRoot = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-reg-generic-v2-'));
65
+ const packDir = path_1.default.join(registryRoot, 'sensor-packs', 'generic');
66
+ fs_1.default.mkdirSync(packDir, { recursive: true });
67
+ fs_1.default.writeFileSync(path_1.default.join(packDir, 'generic.config'), 'fixture\n');
68
+ fs_1.default.writeFileSync(path_1.default.join(packDir, 'pack.json'), JSON.stringify({
69
+ schemaVersion: 2, name: 'generic', description: 'fixture', detects: ['README.md'],
70
+ coverage: { schemaVersion: 1, classes: { security: { description: 'security', detectors: [{ sensor: 'security' }], remedy: { summary: 'run security', command: 'awm sensors init --pack generic' } } } },
71
+ sensors: { security: { applicability: { kind: 'explicit-or-supported-language' }, variants: [{
72
+ id: 'eslint-10', priority: 10, certifiedRange: '>=10.0.0 <11.0.0',
73
+ requirements: { tool: 'eslint', toolRange: '>=10.0.0 <11.0.0', runtime: 'node', runtimeRange: '>=0.0.0', configFiles: ['generic.config'] },
74
+ assets: ['generic.config'], formatter: 'eslint-llm', probe: { kind: 'config-present' },
75
+ command: { executable: 'eslint', resolution: 'node-modules-bin', args: ['.'] },
76
+ }] } },
77
+ }));
78
+ return registryRoot;
79
+ }
60
80
  describe('detectStack', () => {
61
81
  let tmpDir;
62
82
  beforeEach(() => { tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-init-')); });
@@ -277,6 +297,58 @@ describe('initSensors', () => {
277
297
  fs_1.default.rmSync(v2Registry, { recursive: true, force: true });
278
298
  }
279
299
  });
300
+ it('persists an explicit generic selection for live revalidation without promoting automatic generic fallback', async () => {
301
+ const v2Registry = makeGenericV2Registry();
302
+ try {
303
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, 'node_modules', 'eslint'), { recursive: true });
304
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'node_modules', 'eslint', 'package.json'), JSON.stringify({ version: '10.0.0' }));
305
+ const automatic = await (0, init_1.initSensors)({ cwd: tmpDir, registryRoot: v2Registry, configure: false });
306
+ expect(automatic.manifest).toMatchObject({ schemaVersion: 2, pack: 'generic', sensors: {} });
307
+ expect(automatic.manifest.packSelection).toBeUndefined();
308
+ const explicit = await (0, init_1.initSensors)({ cwd: tmpDir, registryRoot: v2Registry, pack: 'generic' });
309
+ const written = JSON.parse(fs_1.default.readFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), 'utf8'));
310
+ expect(explicit.manifest).toMatchObject({ schemaVersion: 2, pack: 'generic', packSelection: 'explicit', sensors: { security: { variantId: 'eslint-10' } } });
311
+ expect(written.packSelection).toBe('explicit');
312
+ await expect((0, status_1.computeSensorStatus)(tmpDir)).resolves.toMatchObject({ overall: 'HEALTHY', checks: { security: { ok: true } } });
313
+ }
314
+ finally {
315
+ fs_1.default.rmSync(v2Registry, { recursive: true, force: true });
316
+ }
317
+ });
318
+ it('never materializes an opt-in hardening asset during ordinary v2 init', async () => {
319
+ const v2Registry = makeV2Registry();
320
+ try {
321
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'package.json'), JSON.stringify({ devDependencies: { eslint: '^10.0.0' } }));
322
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, 'node_modules', 'eslint'), { recursive: true });
323
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'node_modules', 'eslint', 'package.json'), JSON.stringify({ version: '10.0.0' }));
324
+ await (0, init_1.initSensors)({ cwd: tmpDir, registryRoot: v2Registry });
325
+ expect(fs_1.default.existsSync(path_1.default.join(tmpDir, 'eslint.config.awm.mjs'))).toBe(true);
326
+ expect(fs_1.default.existsSync(path_1.default.join(tmpDir, 'tsconfig.awm.json'))).toBe(false);
327
+ }
328
+ finally {
329
+ fs_1.default.rmSync(v2Registry, { recursive: true, force: true });
330
+ }
331
+ });
332
+ it('initializes a selected v2 variant with an explicitly empty assets list', async () => {
333
+ const v2Registry = makeV2Registry();
334
+ try {
335
+ const packPath = path_1.default.join(v2Registry, 'sensor-packs', 'js-ts', 'pack.json');
336
+ const pack = JSON.parse(fs_1.default.readFileSync(packPath, 'utf8'));
337
+ pack.sensors.lint.variants[0].assets = [];
338
+ fs_1.default.writeFileSync(packPath, JSON.stringify(pack));
339
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'package.json'), JSON.stringify({ devDependencies: { eslint: '^10.0.0' } }));
340
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, 'node_modules', 'eslint'), { recursive: true });
341
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'node_modules', 'eslint', 'package.json'), JSON.stringify({ version: '10.0.0' }));
342
+ await expect((0, init_1.initSensors)({ cwd: tmpDir, registryRoot: v2Registry })).resolves.toMatchObject({
343
+ manifest: { schemaVersion: 2, sensors: { lint: { assets: [] } } },
344
+ configured: [],
345
+ });
346
+ expect(fs_1.default.existsSync(path_1.default.join(tmpDir, 'eslint.config.awm.mjs'))).toBe(false);
347
+ }
348
+ finally {
349
+ fs_1.default.rmSync(v2Registry, { recursive: true, force: true });
350
+ }
351
+ });
280
352
  it('preserves enabled and fast choices when a valid v2 manifest is re-initialized', async () => {
281
353
  const v2Registry = makeV2Registry();
282
354
  try {
@@ -8,7 +8,7 @@ const path_1 = __importDefault(require("path"));
8
8
  const child_process_1 = require("child_process");
9
9
  const CLI_ROOT = path_1.default.resolve(__dirname, '../..');
10
10
  const DIST_ENTRYPOINT = path_1.default.join(CLI_ROOT, 'dist', 'src', 'index.js');
11
- const TARGET_VERSION = '7.0.0';
11
+ const TARGET_VERSION = '8.0.0';
12
12
  function readJson(file) {
13
13
  return JSON.parse(fs_1.default.readFileSync(path_1.default.join(CLI_ROOT, file), 'utf8'));
14
14
  }
@@ -22,7 +22,7 @@ function runCompiledCli(...args) {
22
22
  return `${result.stdout}${result.stderr}`;
23
23
  }
24
24
  describe('R3 public major CLI release contract', () => {
25
- it('declares 7.0.0 consistently in package metadata and the lockfile root', () => {
25
+ it('declares 8.0.0 consistently in package metadata and the lockfile root', () => {
26
26
  const pkg = readJson('package.json');
27
27
  const lock = readJson('package-lock.json');
28
28
  const root = lock.packages[''];
@@ -30,7 +30,7 @@ describe('R3 public major CLI release contract', () => {
30
30
  expect(lock.version).toBe(TARGET_VERSION);
31
31
  expect(root.version).toBe(TARGET_VERSION);
32
32
  });
33
- it('exposes 7.0.0 from the compiled CLI while retaining its help banner', () => {
33
+ it('exposes 8.0.0 from the compiled CLI while retaining its help banner', () => {
34
34
  expect(runCompiledCli('--version').trim()).toBe(TARGET_VERSION);
35
35
  expect(runCompiledCli('--help')).toContain('Usage: awm');
36
36
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-workflow-manager",
3
- "version": "8.0.0",
3
+ "version": "8.1.0",
4
4
  "main": "dist/src/index.js",
5
5
  "bin": {
6
6
  "awm": "./dist/src/index.js"