agentic-workflow-manager 3.13.1 → 3.13.2

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.
@@ -99,6 +99,14 @@ function providerCheckLine(check) {
99
99
  function renderProviderReport(report) {
100
100
  const lines = [];
101
101
  lines.push(picocolors_1.default.bold('AWM · harness status'));
102
+ lines.push(picocolors_1.default.dim(` platform: ${(0, paths_1.platformLabel)()}`));
103
+ // Advisory only, native Windows only. This is the text rendered by the
104
+ // real `awm doctor` command (`runDoctor` below) — the diagnostics surface
105
+ // an operator actually goes looking at for platform-specific detail.
106
+ // `init`/`update`/`sync` also note it once each, at the top of their own
107
+ // run (`runInit`/`runUpdateCore`/`runSyncCore`), independently of this.
108
+ if ((0, paths_1.isWindowsNative)())
109
+ lines.push(picocolors_1.default.dim(` ${paths_1.WINDOWS_KNOWN_GAP.replace(/\n\s*/g, ' ')}`));
102
110
  lines.push('');
103
111
  for (const provider of report.providers) {
104
112
  lines.push(`Provider: ${provider.label} ${picocolors_1.default.dim(`(${provider.tier})`)}`);
@@ -125,6 +125,11 @@ function reportInitFailure(o) {
125
125
  return 2;
126
126
  }
127
127
  async function runInit(opts = {}) {
128
+ // Fires at most once per `awm init` run (native Windows only) — this is
129
+ // the single emission point; `renderReport`/`renderInitOutcome` below do
130
+ // NOT also embed it (that used to triple-fire the same text: once here,
131
+ // once in the "Initial state" render, once in "Final state").
132
+ (0, paths_1.noteWindowsCaveat)((m) => console.log(picocolors_1.default.dim(`ℹ ${m}`)));
128
133
  const cwd = opts.cwd ?? process.cwd();
129
134
  const agent = opts.agent === undefined ? 'claude-code' : (0, providers_1.requireAgentTarget)(opts.agent);
130
135
  // R2: gate BEFORE anything is read or written — an unsupported provider
@@ -313,7 +318,6 @@ function registerInitCommand(program) {
313
318
  .option('--machine-only', 'Only run machine-level steps (skip project steps)')
314
319
  .option('--json', 'Emit the InitOutcome as JSON — on success and on failure (failed steps + rollback)')
315
320
  .action(async (options) => {
316
- (0, paths_1.warnIfUnsupportedPlatform)((m) => console.warn(picocolors_1.default.yellow(`⚠ ${m}`)));
317
321
  const code = await runInit({
318
322
  yes: options.yes,
319
323
  agent: options.agent,
@@ -16,6 +16,7 @@ exports.runSyncCore = runSyncCore;
16
16
  // intro/outro chrome lives in `index.ts`'s Commander registration instead
17
17
  // (which already imports clack at the top and is never `require()`d by tests).
18
18
  const picocolors_1 = __importDefault(require("picocolors"));
19
+ const paths_1 = require("../core/paths");
19
20
  const profile_1 = require("../core/profile");
20
21
  const registries_1 = require("../core/registries");
21
22
  const bundles_1 = require("../core/bundles");
@@ -31,6 +32,9 @@ const defaultDeps = {
31
32
  };
32
33
  /** Core, UI-free `awm sync` logic — see `runSync` for the Commander-facing wrapper. */
33
34
  async function runSyncCore(options, deps = {}) {
35
+ // Fires at most once per `awm sync` run, native Windows only — the single
36
+ // emission point for this command (mirrors `runInit`/`runUpdateCore`).
37
+ (0, paths_1.noteWindowsCaveat)((m) => console.log(picocolors_1.default.dim(`ℹ ${m}`)));
34
38
  const d = { ...defaultDeps, ...deps };
35
39
  const cwd = options.cwd ?? process.cwd();
36
40
  const projectRoot = (0, profile_1.findProjectRoot)(cwd);
@@ -20,6 +20,7 @@ exports.runUpdateCore = runUpdateCore;
20
20
  // instead (which already imports clack at the top and is never `require()`d
21
21
  // by tests).
22
22
  const picocolors_1 = __importDefault(require("picocolors"));
23
+ const paths_1 = require("../core/paths");
23
24
  const config_1 = require("../utils/config");
24
25
  const agent_targets_1 = require("../core/agent-targets");
25
26
  const registries_1 = require("../core/registries");
@@ -49,6 +50,9 @@ const defaultDeps = {
49
50
  * `runInit`'s `actions` seam).
50
51
  */
51
52
  async function runUpdateCore(options = {}, deps = {}) {
53
+ // Fires at most once per `awm update` run, native Windows only — the
54
+ // single emission point for this command (mirrors `runInit`/`runSyncCore`).
55
+ (0, paths_1.noteWindowsCaveat)((m) => console.log(picocolors_1.default.dim(`ℹ ${m}`)));
52
56
  const d = { ...defaultDeps, ...deps };
53
57
  const prefs = (0, config_1.getPreferences)();
54
58
  const resolved = (0, agent_targets_1.resolveAgentTargetsOrError)({ prefs, explicit: options.agent });
@@ -3,13 +3,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.WINDOWS_NATIVE_WARNING = void 0;
6
+ exports.WINDOWS_KNOWN_GAP = void 0;
7
7
  exports.homeDir = homeDir;
8
8
  exports.awmHome = awmHome;
9
9
  exports.platform = platform;
10
10
  exports.isWindowsNative = isWindowsNative;
11
11
  exports.platformLabel = platformLabel;
12
- exports.warnIfUnsupportedPlatform = warnIfUnsupportedPlatform;
12
+ exports.noteWindowsCaveat = noteWindowsCaveat;
13
13
  exports.resolveOnPath = resolveOnPath;
14
14
  // cli/src/core/paths.ts
15
15
  //
@@ -35,11 +35,14 @@ function platform() {
35
35
  function isWindowsNative() {
36
36
  return platform() === 'win32';
37
37
  }
38
- /** Human-friendly platform label for diagnostics. */
38
+ /** Human-friendly platform label for diagnostics. Windows is a first-class,
39
+ * CI-verified platform since R6 (`.github/workflows/ci.yml` runs the full
40
+ * suite on `ubuntu-latest` + `windows-latest` on every PR) — this no longer
41
+ * hedges toward WSL. */
39
42
  function platformLabel() {
40
43
  switch (platform()) {
41
44
  case 'win32':
42
- return 'Windows (native — not supported yet, use WSL)';
45
+ return 'Windows (native, CI-verified)';
43
46
  case 'darwin':
44
47
  return 'macOS';
45
48
  case 'linux':
@@ -48,13 +51,28 @@ function platformLabel() {
48
51
  return platform();
49
52
  }
50
53
  }
51
- exports.WINDOWS_NATIVE_WARNING = 'AWM detected native Windows. Native support is deferred; the recommended path today is WSL.\n' +
52
- ' Install WSL (https://learn.microsoft.com/windows/wsl/install) and run AWM inside your Linux distro.\n' +
53
- ' Continuing in best-effort mode, but some steps (symlinks, hooks) may not work.';
54
- /** Emit the unsupported-platform warning via the provided logger, only on native Windows. */
55
- function warnIfUnsupportedPlatform(log) {
54
+ /**
55
+ * The one honest, narrow gap left on native Windows: `awm watch`'s supervisor
56
+ * crash-recovery E2E tests (spawn -> identity capture -> adoption after the
57
+ * supervisor is killed) never converged on real `windows-latest` CI despite 4
58
+ * evidence-based fix attempts in R6 (WMI-based `refIsAlive`, `activitySnapshot`
59
+ * degraded off `ps`/`pgrep`, `spawnStructured`'s `detached` flag tried both
60
+ * ways) — scoped POSIX-only (`itPosix`) in `cli/tests/commands/watch/
61
+ * supervisor-loop.test.ts` (2 tests) and `cli/tests/commands/watch/
62
+ * e2e-crash.test.ts` (2 tests); see the `refIsAlive` comment in
63
+ * `cli/src/core/journal/process.ts` for the full investigation. This is
64
+ * deliberately narrow, not a blanket "some things may not work" hedge:
65
+ * `awm init`/`update`/`sync`/`sensors`/`preflight`/`doctor`/hooks are all
66
+ * exercised green by the same CI matrix and are unaffected by this gap.
67
+ */
68
+ exports.WINDOWS_KNOWN_GAP = 'AWM on native Windows: supported and continuously verified in CI (ubuntu-latest + windows-latest, every PR).\n' +
69
+ ' One known, narrow gap: `awm watch`\'s supervisor crash-recovery (spawn -> identity capture -> adoption\n' +
70
+ ' after the supervisor is killed) has not yet converged on real windows-latest CI — see\n' +
71
+ ' cli/src/core/journal/process.ts (refIsAlive) for detail. Everything else is CI-verified on Windows.';
72
+ /** Emit `WINDOWS_KNOWN_GAP` via the provided logger, only on native Windows. */
73
+ function noteWindowsCaveat(log) {
56
74
  if (isWindowsNative())
57
- log(exports.WINDOWS_NATIVE_WARNING);
75
+ log(exports.WINDOWS_KNOWN_GAP);
58
76
  }
59
77
  /** Resolve a binary on PATH portably: `where` on win32, POSIX `command -v` elsewhere. */
60
78
  function resolveOnPath(bin) {
package/dist/src/index.js CHANGED
@@ -42,7 +42,6 @@ const sync_1 = require("./commands/sync");
42
42
  const update_1 = require("./commands/update");
43
43
  const agent_targets_1 = require("./core/agent-targets");
44
44
  const update_check_1 = require("./core/update-check");
45
- const paths_1 = require("./core/paths");
46
45
  const cli_version_1 = require("./core/cli-version");
47
46
  const program = new commander_1.Command();
48
47
  program.name('awm').description('Agentic Workflow Manager').version((0, cli_version_1.cliVersion)());
@@ -390,7 +389,6 @@ program.command('update')
390
389
  .option('-a, --agent <agent>', `Target agent(s), comma-separated: ${providers_1.AGENT_TARGETS.join(', ')} (defaults to every enabled agent)`)
391
390
  .action(async (options) => {
392
391
  (0, prompts_1.intro)(picocolors_1.default.bgCyan(picocolors_1.default.black(' AWM - Update Registries ')));
393
- (0, paths_1.warnIfUnsupportedPlatform)((m) => console.warn(picocolors_1.default.yellow(`⚠ ${m}`)));
394
392
  const result = await (0, update_1.runUpdateCore)(options);
395
393
  (0, prompts_1.outro)(result.code === 0 ? '✅ Registries, skills and hooks updated.' : picocolors_1.default.red('Update failed — see errors above.'));
396
394
  process.exitCode = result.code;
@@ -401,7 +399,6 @@ program.command('sync')
401
399
  .option('-m, --method <method>', 'Install method: symlink or copy', 'symlink')
402
400
  .action(async (options) => {
403
401
  (0, prompts_1.intro)(picocolors_1.default.bgCyan(picocolors_1.default.black(' AWM - Sync Project Profile ')));
404
- (0, paths_1.warnIfUnsupportedPlatform)((m) => console.warn(picocolors_1.default.yellow(`⚠ ${m}`)));
405
402
  const { code } = await (0, sync_1.runSyncCore)(options);
406
403
  (0, prompts_1.outro)(code === 0 ? 'Done.' : picocolors_1.default.red('Sync failed — see errors above.'));
407
404
  process.exitCode = code;
@@ -6,17 +6,61 @@ describe('doctor renderReport — platform line', () => {
6
6
  afterEach(() => {
7
7
  Object.defineProperty(process, 'platform', { value: realPlatform, configurable: true });
8
8
  });
9
+ function setPlatform(p) {
10
+ Object.defineProperty(process, 'platform', { value: p, configurable: true });
11
+ }
9
12
  function emptyReport() {
10
13
  return { overall: 'healthy', hasProject: false, projectName: undefined, results: [] };
11
14
  }
12
15
  it('renders the platform label under the Machine header', () => {
13
- Object.defineProperty(process, 'platform', { value: 'linux', configurable: true });
16
+ setPlatform('linux');
14
17
  const out = (0, doctor_1.renderReport)(emptyReport());
15
18
  expect(out).toContain('platform: Linux');
16
19
  });
17
- it('flags native Windows with a WSL hint', () => {
18
- Object.defineProperty(process, 'platform', { value: 'win32', configurable: true });
20
+ it('labels native Windows as supported and CI-verified, not deferred to WSL', () => {
21
+ setPlatform('win32');
22
+ const out = (0, doctor_1.renderReport)(emptyReport());
23
+ expect(out).toContain('platform: Windows (native, CI-verified)');
24
+ expect(out).not.toContain('WSL');
25
+ });
26
+ // `renderReport` (init's before/after dashboard) deliberately does NOT
27
+ // embed the Windows caveat — it used to (R7), which made a single `awm
28
+ // init` run print the same caveat text 3 times (once via `noteWindowsCaveat`
29
+ // at the top of `runInit`, once for "Initial state", once for "Final
30
+ // state"). The caveat now lives solely in `runInit`'s own single emission
31
+ // and in `renderProviderReport` below (the real `awm doctor` output).
32
+ it('never embeds the awm-watch supervisor caveat in the init before/after dashboard', () => {
33
+ setPlatform('win32');
19
34
  const out = (0, doctor_1.renderReport)(emptyReport());
20
- expect(out).toContain('WSL');
35
+ expect(out).not.toMatch(/awm watch/i);
36
+ });
37
+ });
38
+ // Regression coverage for the confirmed bug: the `WINDOWS_KNOWN_GAP` caveat
39
+ // used to live only inside `renderReport`, whose only callers are `init.ts`'s
40
+ // before/after dashboard — `awm doctor`'s REAL command path
41
+ // (`runDoctor` → `renderProviderReport`) never touched it, so a user running
42
+ // `awm doctor` on native Windows never saw the caveat its own code comment
43
+ // claimed doctor was the place for. This exercises `renderProviderReport`
44
+ // directly, the function `runDoctor` actually calls for its text output.
45
+ describe('doctor renderProviderReport — platform caveat (the real `awm doctor` path)', () => {
46
+ const realPlatform = process.platform;
47
+ afterEach(() => {
48
+ Object.defineProperty(process, 'platform', { value: realPlatform, configurable: true });
49
+ });
50
+ function setPlatform(p) {
51
+ Object.defineProperty(process, 'platform', { value: p, configurable: true });
52
+ }
53
+ function emptyProviderReport() {
54
+ return { overall: 'healthy', providers: [] };
55
+ }
56
+ it('surfaces the narrow awm-watch supervisor caveat on native Windows only, not on macOS or Linux', () => {
57
+ setPlatform('win32');
58
+ const winOut = (0, doctor_1.renderProviderReport)(emptyProviderReport());
59
+ expect(winOut).toMatch(/awm watch/i);
60
+ for (const p of ['linux', 'darwin']) {
61
+ setPlatform(p);
62
+ const out = (0, doctor_1.renderProviderReport)(emptyProviderReport());
63
+ expect(out).not.toMatch(/awm watch/i);
64
+ }
21
65
  });
22
66
  });
@@ -121,6 +121,25 @@ describe('runDoctor', () => {
121
121
  const code = (0, doctor_1.runDoctor)({ cwd: tmpHome });
122
122
  expect(code).toBe(1);
123
123
  });
124
+ // Finding #1 (R7 QA): the real `awm doctor` command (this `runDoctor`,
125
+ // text mode — not `renderReport`, which only `init.ts` calls) must
126
+ // actually surface the Windows caveat on native Windows.
127
+ it('surfaces the Windows caveat in real text output on native Windows only', () => {
128
+ const realPlatform = process.platform;
129
+ try {
130
+ Object.defineProperty(process, 'platform', { value: 'win32', configurable: true });
131
+ writeSpy.mockClear();
132
+ (0, doctor_1.runDoctor)({ cwd: tmpHome });
133
+ expect(stdout()).toMatch(/awm watch/i);
134
+ Object.defineProperty(process, 'platform', { value: 'linux', configurable: true });
135
+ writeSpy.mockClear();
136
+ (0, doctor_1.runDoctor)({ cwd: tmpHome });
137
+ expect(stdout()).not.toMatch(/awm watch/i);
138
+ }
139
+ finally {
140
+ Object.defineProperty(process, 'platform', { value: realPlatform, configurable: true });
141
+ }
142
+ });
124
143
  it('--json emits a parseable provider report and keeps the same exit code', () => {
125
144
  const code = (0, doctor_1.runDoctor)({ cwd: tmpHome, json: true });
126
145
  const parsed = JSON.parse(stdout());
@@ -70,6 +70,45 @@ describe('runInit', () => {
70
70
  else
71
71
  process.env.AWM_HOME = originalAwmHome;
72
72
  });
73
+ // Finding #2/#3 (R7 QA): the Windows caveat used to fire via a direct
74
+ // call in registerInitCommand's Commander `.action()` closure — a call
75
+ // site no test exercised, since every other `runInit` test here bypasses
76
+ // the Commander wrapper entirely. It now lives at the top of `runInit`
77
+ // itself (the unit these tests already drive directly), firing at most
78
+ // once per run, native Windows only.
79
+ describe('Windows caveat (noteWindowsCaveat)', () => {
80
+ const realPlatform = process.platform;
81
+ let logSpy;
82
+ beforeEach(() => {
83
+ logSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
84
+ });
85
+ afterEach(() => {
86
+ logSpy.mockRestore();
87
+ Object.defineProperty(process, 'platform', { value: realPlatform, configurable: true });
88
+ });
89
+ it('logs the caveat exactly once on native Windows', async () => {
90
+ Object.defineProperty(process, 'platform', { value: 'win32', configurable: true });
91
+ const { runInit } = require('../../src/commands/init');
92
+ await runInit({
93
+ cwd: tmpHome,
94
+ yes: true,
95
+ actions: { syncCache: async () => { }, installHook: () => ({ status: 'installed' }) },
96
+ });
97
+ const caveatCalls = logSpy.mock.calls.filter((c) => /awm watch/i.test(String(c[0])));
98
+ expect(caveatCalls).toHaveLength(1);
99
+ });
100
+ it('never logs the caveat on non-Windows platforms', async () => {
101
+ Object.defineProperty(process, 'platform', { value: 'linux', configurable: true });
102
+ const { runInit } = require('../../src/commands/init');
103
+ await runInit({
104
+ cwd: tmpHome,
105
+ yes: true,
106
+ actions: { syncCache: async () => { }, installHook: () => ({ status: 'installed' }) },
107
+ });
108
+ const caveatCalls = logSpy.mock.calls.filter((c) => /awm watch/i.test(String(c[0])));
109
+ expect(caveatCalls).toHaveLength(0);
110
+ });
111
+ });
73
112
  it('returns exit 1 on a bare HOME and never prompts with --yes (cache stubbed)', async () => {
74
113
  const { runInit } = require('../../src/commands/init');
75
114
  const code = await runInit({
@@ -208,6 +208,71 @@ describe('multi-agent targeting (add/remove/sync/update/doctor)', () => {
208
208
  });
209
209
  expect(outcome.code).toBe(1); // no silent catch{} — verifies Step 6's "no aborta" removal
210
210
  });
211
+ // Finding #3 (R7 QA): `noteWindowsCaveat` used to be called only from the
212
+ // raw Commander `.action()` closures in `index.ts` (update.ts:441,
213
+ // sync.ts:453 pre-fix) — call sites no test exercised, since `update`/
214
+ // `sync` are always driven here via `runUpdateCore`/`runSyncCore`
215
+ // directly. It now lives at the top of those core functions themselves,
216
+ // firing at most once per run, native Windows only.
217
+ describe('Windows caveat (noteWindowsCaveat) — update & sync', () => {
218
+ const realPlatform = process.platform;
219
+ let logSpy;
220
+ beforeEach(() => {
221
+ logSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
222
+ });
223
+ afterEach(() => {
224
+ logSpy.mockRestore();
225
+ Object.defineProperty(process, 'platform', { value: realPlatform, configurable: true });
226
+ });
227
+ function caveatCalls() {
228
+ return logSpy.mock.calls.filter((c) => /awm watch/i.test(String(c[0])));
229
+ }
230
+ it('update logs the caveat exactly once on native Windows, never on Linux', async () => {
231
+ writePrefs(['claude-code']);
232
+ const { runUpdateCore } = require('../../src/commands/update');
233
+ const deps = {
234
+ syncRegistries: async () => [],
235
+ verifyMinCliVersions: () => [],
236
+ regenerateGlobalContext: () => [],
237
+ planReconciliation: () => ({ operations: [], records: [], reports: [] }),
238
+ applyInstallPlan: () => ({ installed: [], skipped: [], transactionId: 'tx', modifiedFiles: [] }),
239
+ resyncInstalledHooks: () => [],
240
+ offerSelfUpdate: async () => { },
241
+ };
242
+ Object.defineProperty(process, 'platform', { value: 'win32', configurable: true });
243
+ await runUpdateCore({}, deps);
244
+ expect(caveatCalls()).toHaveLength(1);
245
+ logSpy.mockClear();
246
+ Object.defineProperty(process, 'platform', { value: 'linux', configurable: true });
247
+ await runUpdateCore({}, deps);
248
+ expect(caveatCalls()).toHaveLength(0);
249
+ });
250
+ it('sync logs the caveat exactly once on native Windows, never on Linux', async () => {
251
+ writePrefs(['claude-code']);
252
+ const projectRoot = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-sync-project-'));
253
+ fs_1.default.mkdirSync(path_1.default.join(projectRoot, '.awm'), { recursive: true });
254
+ fs_1.default.writeFileSync(path_1.default.join(projectRoot, '.awm', 'profile.json'), JSON.stringify({ extensions: [] }));
255
+ const { runSyncCore } = require('../../src/commands/sync');
256
+ const deps = {
257
+ syncRegistries: async () => [],
258
+ verifyMinCliVersions: () => [],
259
+ verifyProjectPins: async () => [],
260
+ syncProfile: () => ({ installed: [], skipped: [], extensions: [], transactionIds: [], modifiedFiles: [] }),
261
+ };
262
+ try {
263
+ Object.defineProperty(process, 'platform', { value: 'win32', configurable: true });
264
+ await runSyncCore({ cwd: projectRoot }, deps);
265
+ expect(caveatCalls()).toHaveLength(1);
266
+ logSpy.mockClear();
267
+ Object.defineProperty(process, 'platform', { value: 'linux', configurable: true });
268
+ await runSyncCore({ cwd: projectRoot }, deps);
269
+ expect(caveatCalls()).toHaveLength(0);
270
+ }
271
+ finally {
272
+ fs_1.default.rmSync(projectRoot, { recursive: true, force: true });
273
+ }
274
+ });
275
+ });
211
276
  it('sync reports a syncProfile failure and returns a non-zero exit code instead of crashing uncaught', async () => {
212
277
  writePrefs(['claude-code'], 'claude-code');
213
278
  const projectRoot = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-sync-project-'));
@@ -67,17 +67,44 @@ describe('core/paths', () => {
67
67
  setPlatform('darwin');
68
68
  expect((0, paths_1.platformLabel)()).toBe('macOS');
69
69
  setPlatform('win32');
70
- expect((0, paths_1.platformLabel)()).toContain('WSL');
70
+ // Windows is a first-class, CI-verified platform since R6 — the label no
71
+ // longer hedges toward WSL, and must not silently regress back to it.
72
+ expect((0, paths_1.platformLabel)()).toBe('Windows (native, CI-verified)');
73
+ expect((0, paths_1.platformLabel)()).not.toContain('WSL');
71
74
  });
72
- it('warnIfUnsupportedPlatform calls the logger only on win32', () => {
75
+ it('noteWindowsCaveat calls the logger only on win32, with the narrow watch-supervisor gap', () => {
73
76
  const calls = [];
74
77
  const log = (m) => calls.push(m);
75
78
  setPlatform('linux');
76
- (0, paths_1.warnIfUnsupportedPlatform)(log);
79
+ (0, paths_1.noteWindowsCaveat)(log);
77
80
  expect(calls).toHaveLength(0);
78
81
  setPlatform('win32');
79
- (0, paths_1.warnIfUnsupportedPlatform)(log);
80
- expect(calls).toEqual([paths_1.WINDOWS_NATIVE_WARNING]);
82
+ (0, paths_1.noteWindowsCaveat)(log);
83
+ expect(calls).toEqual([paths_1.WINDOWS_KNOWN_GAP]);
84
+ // The message must assert Windows support, not disclaim it, and must name
85
+ // the one specific gap rather than a blanket "some things may not work"
86
+ // hedge — pinning both halves so neither regresses independently.
87
+ expect(paths_1.WINDOWS_KNOWN_GAP).toMatch(/supported and continuously verified/i);
88
+ expect(paths_1.WINDOWS_KNOWN_GAP).toMatch(/awm watch/i);
89
+ expect(paths_1.WINDOWS_KNOWN_GAP).not.toMatch(/WSL/i);
90
+ expect(paths_1.WINDOWS_KNOWN_GAP).not.toMatch(/not supported/i);
91
+ });
92
+ it('noteWindowsCaveat propagates a throwing logger instead of swallowing it', () => {
93
+ // `noteWindowsCaveat` has no try/catch around the logger call — callers
94
+ // (init.ts/update.ts/sync.ts/doctor.ts) all pass simple `console.log`
95
+ // wrappers that are not expected to throw, and every caller controls its
96
+ // own logger, so there is no shared reason for this helper to be
97
+ // defensive on their behalf. Pin that behavior explicitly: a throwing
98
+ // logger's error propagates out of `noteWindowsCaveat`, it is not
99
+ // swallowed.
100
+ setPlatform('win32');
101
+ const boom = new Error('logger exploded');
102
+ const throwingLog = () => { throw boom; };
103
+ expect(() => (0, paths_1.noteWindowsCaveat)(throwingLog)).toThrow(boom);
104
+ // And on non-Windows the logger is never even invoked, so a throwing
105
+ // logger is harmless there.
106
+ setPlatform('linux');
107
+ expect(() => (0, paths_1.noteWindowsCaveat)(throwingLog)).not.toThrow();
81
108
  });
82
109
  describe('resolveOnPath', () => {
83
110
  it('uses `command -v` on POSIX and returns true when the binary resolves', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-workflow-manager",
3
- "version": "3.13.1",
3
+ "version": "3.13.2",
4
4
  "main": "dist/src/index.js",
5
5
  "bin": {
6
6
  "awm": "./dist/src/index.js"