agentic-workflow-manager 2.0.1 → 2.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.
@@ -9,6 +9,7 @@ exports.registerDoctorCommand = registerDoctorCommand;
9
9
  const picocolors_1 = __importDefault(require("picocolors"));
10
10
  const context_1 = require("../core/diagnostics/context");
11
11
  const checks_1 = require("../core/diagnostics/checks");
12
+ const paths_1 = require("../core/paths");
12
13
  function glyph(status) {
13
14
  if (status === 'ok')
14
15
  return picocolors_1.default.green('✔');
@@ -33,6 +34,7 @@ function renderReport(report) {
33
34
  lines.push(picocolors_1.default.bold('AWM · harness status'));
34
35
  lines.push('');
35
36
  lines.push('Machine (global)');
37
+ lines.push(picocolors_1.default.dim(` platform: ${(0, paths_1.platformLabel)()}`));
36
38
  for (const r of report.results.filter((x) => x.level === 'machine'))
37
39
  lines.push(line(r));
38
40
  lines.push('');
@@ -8,6 +8,7 @@ exports.installHook = installHook;
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const providers_1 = require("../../providers");
11
+ const paths_1 = require("../../core/paths");
11
12
  function syncFile(source, dest, method) {
12
13
  try {
13
14
  fs_1.default.unlinkSync(dest);
@@ -26,8 +27,7 @@ function syncFile(source, dest, method) {
26
27
  function backupSettings(settingsPath) {
27
28
  if (!fs_1.default.existsSync(settingsPath))
28
29
  return null;
29
- const awmHome = process.env.AWM_HOME || path_1.default.join(process.env.HOME, '.awm');
30
- const backupDir = path_1.default.join(awmHome, 'backups');
30
+ const backupDir = path_1.default.join((0, paths_1.awmHome)(), 'backups');
31
31
  fs_1.default.mkdirSync(backupDir, { recursive: true });
32
32
  const ts = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '-').slice(0, 19);
33
33
  const backupPath = path_1.default.join(backupDir, `settings.json.${ts}.bak`);
@@ -57,13 +57,19 @@ function installHook(options) {
57
57
  fs_1.default.mkdirSync(config.scriptsDir, { recursive: true });
58
58
  syncFile(path_1.default.join(sourceHooks, 'session-start'), path_1.default.join(config.scriptsDir, 'session-start'), options.installMethod);
59
59
  syncFile(path_1.default.join(sourceHooks, 'run-hook.cmd'), path_1.default.join(config.scriptsDir, 'run-hook.cmd'), options.installMethod);
60
- // 3. Symlink the skill (ALWAYS symlink so awm update propagates)
60
+ // 3. Link the skill (default: symlink so 'awm update' propagates; fall back to copy if symlink is unavailable, e.g. Windows without Developer Mode)
61
61
  const skillDest = path_1.default.join(config.scriptsDir, 'using-awm.md');
62
62
  try {
63
63
  fs_1.default.unlinkSync(skillDest);
64
64
  }
65
65
  catch { /* not exists */ }
66
- fs_1.default.symlinkSync(sourceSkill, skillDest);
66
+ try {
67
+ fs_1.default.symlinkSync(sourceSkill, skillDest);
68
+ }
69
+ catch {
70
+ // best-effort: copy the single skill file; 'awm update' will not auto-propagate
71
+ fs_1.default.copyFileSync(sourceSkill, skillDest);
72
+ }
67
73
  // 4. Backup settings if it exists
68
74
  const backupPath = backupSettings(config.settingsPath);
69
75
  // 5. Read or initialize settings
@@ -7,11 +7,11 @@ exports.uninstallHook = uninstallHook;
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const providers_1 = require("../../providers");
10
+ const paths_1 = require("../../core/paths");
10
11
  function backupSettings(settingsPath) {
11
12
  if (!fs_1.default.existsSync(settingsPath))
12
13
  return null;
13
- const awmHome = process.env.AWM_HOME || path_1.default.join(process.env.HOME, '.awm');
14
- const backupDir = path_1.default.join(awmHome, 'backups');
14
+ const backupDir = path_1.default.join((0, paths_1.awmHome)(), 'backups');
15
15
  fs_1.default.mkdirSync(backupDir, { recursive: true });
16
16
  const ts = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '-').slice(0, 19);
17
17
  const backupPath = path_1.default.join(backupDir, `settings.json.${ts}.bak`);
@@ -49,6 +49,7 @@ const bundles_1 = require("../core/bundles");
49
49
  const registries_1 = require("../core/registries");
50
50
  const orchestrator_1 = require("../core/init/orchestrator");
51
51
  const steps_1 = require("../core/init/steps");
52
+ const paths_1 = require("../core/paths");
52
53
  // ---------------------------------------------------------------------------
53
54
  // Rendering
54
55
  // ---------------------------------------------------------------------------
@@ -170,6 +171,7 @@ function registerInitCommand(program) {
170
171
  .option('--machine-only', 'Only run machine-level steps (skip project steps)')
171
172
  .option('--json', 'Emit the InitOutcome as JSON')
172
173
  .action(async (options) => {
174
+ (0, paths_1.warnIfUnsupportedPlatform)((m) => console.warn(picocolors_1.default.yellow(`⚠ ${m}`)));
173
175
  const code = await runInit({
174
176
  yes: options.yes,
175
177
  agent: options.agent,
@@ -1,6 +1,10 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.parseEslintOutput = parseEslintOutput;
7
+ const path_1 = __importDefault(require("path"));
4
8
  function parseEslintOutput(raw) {
5
9
  let parsed;
6
10
  try {
@@ -15,7 +19,9 @@ function parseEslintOutput(raw) {
15
19
  for (const msg of file.messages) {
16
20
  if (msg.severity < 2 || msg.line == null || msg.column == null)
17
21
  continue;
18
- const rel = file.filePath.startsWith(cwd + '/') ? file.filePath.slice(cwd.length + 1) : file.filePath;
22
+ const rel = file.filePath.startsWith(cwd + path_1.default.sep)
23
+ ? path_1.default.relative(cwd, file.filePath)
24
+ : file.filePath;
19
25
  errors.push({
20
26
  file: rel,
21
27
  line: msg.line,
@@ -7,12 +7,12 @@ exports.installSensorHook = installSensorHook;
7
7
  exports.uninstallSensorHook = uninstallSensorHook;
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const path_1 = __importDefault(require("path"));
10
- const os_1 = __importDefault(require("os"));
10
+ const paths_1 = require("../../core/paths");
11
11
  const POST_TOOL_USE_EVENT = 'PostToolUse';
12
12
  const POST_TOOL_USE_MATCHER = 'Write|Edit|MultiEdit';
13
13
  const AWM_SENSOR_CMD = 'awm sensors run --fast';
14
14
  function defaultSettingsPath() {
15
- return path_1.default.join(process.env.HOME ?? os_1.default.homedir(), '.claude', 'settings.json');
15
+ return path_1.default.join((0, paths_1.homeDir)(), '.claude', 'settings.json');
16
16
  }
17
17
  function readSettings(p) {
18
18
  if (!fs_1.default.existsSync(p))
@@ -31,8 +31,7 @@ function isAwmEntry(e) {
31
31
  function backupSettings(settingsPath) {
32
32
  if (!fs_1.default.existsSync(settingsPath))
33
33
  return undefined;
34
- const awmHome = process.env.AWM_HOME || path_1.default.join(process.env.HOME ?? os_1.default.homedir(), '.awm');
35
- const backupDir = path_1.default.join(awmHome, 'backups');
34
+ const backupDir = path_1.default.join((0, paths_1.awmHome)(), 'backups');
36
35
  fs_1.default.mkdirSync(backupDir, { recursive: true });
37
36
  const ts = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '-').slice(0, 19);
38
37
  const backupPath = path_1.default.join(backupDir, `settings.json.${ts}.sensor.bak`);
@@ -7,14 +7,11 @@ exports.globalContextPath = globalContextPath;
7
7
  exports.materialize = materialize;
8
8
  // cli/src/core/context/materializer.ts
9
9
  const fs_1 = __importDefault(require("fs"));
10
- const os_1 = __importDefault(require("os"));
11
10
  const path_1 = __importDefault(require("path"));
12
11
  const provider_1 = require("./provider");
13
- function awmHome() {
14
- return process.env.AWM_HOME || path_1.default.join(process.env.HOME || os_1.default.homedir(), '.awm');
15
- }
12
+ const paths_1 = require("../paths");
16
13
  function globalContextPath() {
17
- return path_1.default.join(awmHome(), 'context', 'awm-context.md');
14
+ return path_1.default.join((0, paths_1.awmHome)(), 'context', 'awm-context.md');
18
15
  }
19
16
  function materialize(ctx, absPath, scope) {
20
17
  let onDisk = null;
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.gatherContext = gatherContext;
7
7
  // src/core/diagnostics/context.ts
8
8
  const fs_1 = __importDefault(require("fs"));
9
- const os_1 = __importDefault(require("os"));
10
9
  const path_1 = __importDefault(require("path"));
11
10
  const child_process_1 = require("child_process");
12
11
  const providers_1 = require("../../providers");
@@ -16,8 +15,7 @@ const status_1 = require("../../commands/hooks/status");
16
15
  const profile_1 = require("../profile");
17
16
  const bundles_1 = require("../bundles");
18
17
  const skill_integrity_1 = require("../skill-integrity");
19
- function home() { return process.env.HOME || os_1.default.homedir(); }
20
- function awmHome() { return process.env.AWM_HOME || path_1.default.join(home(), '.awm'); }
18
+ const paths_1 = require("../paths");
21
19
  // Estado de un artefacto en <dir>/<skill>: link vivo / symlink colgante / ausente.
22
20
  function linkState(dir, skill) {
23
21
  const p = path_1.default.join(dir, skill);
@@ -122,7 +120,7 @@ function gatherMachine(bundles, agent = 'claude-code') {
122
120
  // ambient (deseados desde ~/.awm/config.json)
123
121
  let wanted = [];
124
122
  try {
125
- const cfg = JSON.parse(fs_1.default.readFileSync(path_1.default.join(awmHome(), 'config.json'), 'utf-8'));
123
+ const cfg = JSON.parse(fs_1.default.readFileSync(path_1.default.join((0, paths_1.awmHome)(), 'config.json'), 'utf-8'));
126
124
  if (Array.isArray(cfg.ambient)) {
127
125
  wanted = cfg.ambient.filter((x) => typeof x === 'string');
128
126
  }
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.WINDOWS_NATIVE_WARNING = void 0;
7
+ exports.homeDir = homeDir;
8
+ exports.awmHome = awmHome;
9
+ exports.platform = platform;
10
+ exports.isWindowsNative = isWindowsNative;
11
+ exports.platformLabel = platformLabel;
12
+ exports.warnIfUnsupportedPlatform = warnIfUnsupportedPlatform;
13
+ // cli/src/core/paths.ts
14
+ //
15
+ // Single source of truth for home / AWM_HOME resolution and platform detection.
16
+ // Functions are evaluated at CALL TIME (not require time) so env overrides are
17
+ // always honored and tests need no jest.resetModules().
18
+ const os_1 = __importDefault(require("os"));
19
+ const path_1 = __importDefault(require("path"));
20
+ /** User home directory with a robust fallback. Never returns a raw, possibly-empty process.env.HOME. */
21
+ function homeDir() {
22
+ return process.env.HOME || os_1.default.homedir();
23
+ }
24
+ /** AWM home directory (~/.awm), honoring the AWM_HOME override. */
25
+ function awmHome() {
26
+ return process.env.AWM_HOME || path_1.default.join(homeDir(), '.awm');
27
+ }
28
+ /** Raw platform string (wrapper over process.platform for testability). */
29
+ function platform() {
30
+ return process.platform;
31
+ }
32
+ /** True only on native Windows. WSL reports 'linux', so this returns false there. */
33
+ function isWindowsNative() {
34
+ return platform() === 'win32';
35
+ }
36
+ /** Human-friendly platform label for diagnostics. */
37
+ function platformLabel() {
38
+ switch (platform()) {
39
+ case 'win32':
40
+ return 'Windows (native — not supported yet, use WSL)';
41
+ case 'darwin':
42
+ return 'macOS';
43
+ case 'linux':
44
+ return 'Linux';
45
+ default:
46
+ return platform();
47
+ }
48
+ }
49
+ exports.WINDOWS_NATIVE_WARNING = 'AWM detected native Windows. Native support is deferred; the recommended path today is WSL.\n' +
50
+ ' Install WSL (https://learn.microsoft.com/windows/wsl/install) and run AWM inside your Linux distro.\n' +
51
+ ' Continuing in best-effort mode, but some steps (symlinks, hooks) may not work.';
52
+ /** Emit the unsupported-platform warning via the provided logger, only on native Windows. */
53
+ function warnIfUnsupportedPlatform(log) {
54
+ if (isWindowsNative())
55
+ log(exports.WINDOWS_NATIVE_WARNING);
56
+ }
@@ -19,13 +19,13 @@ exports.verifyMinCliVersions = verifyMinCliVersions;
19
19
  // src/core/registries.ts
20
20
  const fs_1 = __importDefault(require("fs"));
21
21
  const path_1 = __importDefault(require("path"));
22
- const os_1 = __importDefault(require("os"));
23
22
  const simple_git_1 = __importDefault(require("simple-git"));
24
23
  const registry_1 = require("./registry");
25
24
  const versioning_1 = require("./versioning");
26
25
  const cli_version_1 = require("./cli-version");
27
- // Evaluated at require-time — tests must use jest.resetModules() + late require() to pick up env overrides.
28
- const AWM_HOME = process.env.AWM_HOME || path_1.default.join(process.env.HOME || os_1.default.homedir(), '.awm');
26
+ const paths_1 = require("./paths");
27
+ // Computed at require-time by calling the shared resolver (single source of truth: core/paths.ts).
28
+ const AWM_HOME = (0, paths_1.awmHome)();
29
29
  exports.REGISTRIES_DIR = path_1.default.join(AWM_HOME, 'registries');
30
30
  exports.REGISTRIES_CONFIG_PATH = path_1.default.join(AWM_HOME, 'registries.json');
31
31
  exports.CONTENT_DIR_NAMES = ['skills', 'bundles', 'workflows', 'agents'];
@@ -16,17 +16,16 @@ exports.offerSelfUpdate = offerSelfUpdate;
16
16
  // `awm update`. AWM_NO_UPDATE_CHECK=1 desactiva ambas (tests, CI).
17
17
  const fs_1 = __importDefault(require("fs"));
18
18
  const path_1 = __importDefault(require("path"));
19
- const os_1 = __importDefault(require("os"));
20
19
  const child_process_1 = require("child_process");
21
20
  const picocolors_1 = __importDefault(require("picocolors"));
22
21
  const prompts_1 = require("@clack/prompts");
23
22
  const cli_version_1 = require("./cli-version");
24
23
  const versioning_1 = require("./versioning");
24
+ const paths_1 = require("./paths");
25
25
  const TTL_MS = 24 * 60 * 60 * 1000;
26
26
  const REGISTRY_URL = `https://registry.npmjs.org/${cli_version_1.CLI_PACKAGE_NAME}/latest`;
27
27
  function cacheFile() {
28
- const home = process.env.AWM_HOME || path_1.default.join(process.env.HOME || os_1.default.homedir(), '.awm');
29
- return path_1.default.join(home, 'update-check.json');
28
+ return path_1.default.join((0, paths_1.awmHome)(), 'update-check.json');
30
29
  }
31
30
  function readUpdateCache() {
32
31
  try {
package/dist/src/index.js CHANGED
@@ -34,6 +34,7 @@ const registry_1 = require("./commands/registry");
34
34
  const pin_1 = require("./commands/pin");
35
35
  const profile_pins_1 = require("./core/profile-pins");
36
36
  const update_check_1 = require("./core/update-check");
37
+ const paths_1 = require("./core/paths");
37
38
  const program = new commander_1.Command();
38
39
  program.name('awm').description('Agentic Workflow Manager').version((0, cli_version_1.cliVersion)());
39
40
  program.hook('postAction', () => {
@@ -359,6 +360,7 @@ program.command('sync')
359
360
  .option('-m, --method <method>', 'Install method: symlink or copy', 'symlink')
360
361
  .action(async (options) => {
361
362
  (0, prompts_1.intro)(picocolors_1.default.bgCyan(picocolors_1.default.black(' AWM - Sync Project Profile ')));
363
+ (0, paths_1.warnIfUnsupportedPlatform)((m) => console.warn(picocolors_1.default.yellow(`⚠ ${m}`)));
362
364
  const projectRoot = (0, profile_1.findProjectRoot)(process.cwd());
363
365
  if (!projectRoot) {
364
366
  console.error(picocolors_1.default.red('No project root found (need a .git/, package.json, or .awm/profile.json here).'));
@@ -8,10 +8,10 @@ exports.getTargetPath = getTargetPath;
8
8
  exports.getHookConfig = getHookConfig;
9
9
  exports.getInjection = getInjection;
10
10
  // src/providers/index.ts
11
- const os_1 = __importDefault(require("os"));
12
11
  const path_1 = __importDefault(require("path"));
13
- const homedir = process.env.HOME || os_1.default.homedir();
14
- const awmHome = process.env.AWM_HOME || path_1.default.join(homedir, '.awm');
12
+ const paths_1 = require("../core/paths");
13
+ const homedir = (0, paths_1.homeDir)();
14
+ const awmHomeDir = (0, paths_1.awmHome)();
15
15
  exports.PROVIDERS = {
16
16
  antigravity: {
17
17
  label: 'Antigravity',
@@ -38,7 +38,7 @@ exports.PROVIDERS = {
38
38
  hooks: {
39
39
  type: 'cc-settings-merge',
40
40
  settingsPath: path_1.default.join(homedir, '.claude/settings.json'),
41
- scriptsDir: path_1.default.join(awmHome, 'hooks'),
41
+ scriptsDir: path_1.default.join(awmHomeDir, 'hooks'),
42
42
  matcher: 'startup|clear|compact',
43
43
  eventName: 'SessionStart'
44
44
  },
@@ -8,14 +8,14 @@ exports.savePreferences = savePreferences;
8
8
  // src/utils/config.ts
9
9
  const fs_1 = __importDefault(require("fs"));
10
10
  const path_1 = __importDefault(require("path"));
11
- const os_1 = __importDefault(require("os"));
11
+ const paths_1 = require("../core/paths");
12
12
  const DEFAULT_PREFS = {
13
13
  defaultAgent: 'antigravity',
14
14
  installMethod: 'symlink',
15
15
  defaultScope: 'local'
16
16
  };
17
17
  function prefsDir() {
18
- return process.env.AWM_HOME || path_1.default.join(process.env.HOME || os_1.default.homedir(), '.awm');
18
+ return (0, paths_1.awmHome)();
19
19
  }
20
20
  function getPreferences() {
21
21
  const file = path_1.default.join(prefsDir(), 'preferences.json');
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const doctor_1 = require("../../src/commands/doctor");
4
+ describe('doctor renderReport — platform line', () => {
5
+ const realPlatform = process.platform;
6
+ afterEach(() => {
7
+ Object.defineProperty(process, 'platform', { value: realPlatform, configurable: true });
8
+ });
9
+ function emptyReport() {
10
+ return { overall: 'healthy', hasProject: false, projectName: undefined, results: [] };
11
+ }
12
+ it('renders the platform label under the Machine header', () => {
13
+ Object.defineProperty(process, 'platform', { value: 'linux', configurable: true });
14
+ const out = (0, doctor_1.renderReport)(emptyReport());
15
+ expect(out).toContain('platform: Linux');
16
+ });
17
+ it('flags native Windows with a WSL hint', () => {
18
+ Object.defineProperty(process, 'platform', { value: 'win32', configurable: true });
19
+ const out = (0, doctor_1.renderReport)(emptyReport());
20
+ expect(out).toContain('WSL');
21
+ });
22
+ });
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fs_1 = __importDefault(require("fs"));
7
+ const path_1 = __importDefault(require("path"));
8
+ const os_1 = __importDefault(require("os"));
9
+ describe('hooks/install — skill symlink fallback to copy', () => {
10
+ let tmpHome;
11
+ let origHome;
12
+ let origAwmHome;
13
+ let symlinkSpy;
14
+ beforeEach(() => {
15
+ tmpHome = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-symlink-fb-'));
16
+ origHome = process.env.HOME;
17
+ origAwmHome = process.env.AWM_HOME;
18
+ process.env.HOME = tmpHome;
19
+ process.env.AWM_HOME = path_1.default.join(tmpHome, '.awm');
20
+ jest.resetModules();
21
+ });
22
+ afterEach(() => {
23
+ symlinkSpy?.mockRestore();
24
+ fs_1.default.rmSync(tmpHome, { recursive: true, force: true });
25
+ if (origHome === undefined)
26
+ delete process.env.HOME;
27
+ else
28
+ process.env.HOME = origHome;
29
+ if (origAwmHome === undefined)
30
+ delete process.env.AWM_HOME;
31
+ else
32
+ process.env.AWM_HOME = origAwmHome;
33
+ });
34
+ function seedRegistry(root) {
35
+ const hooksDir = path_1.default.join(root, 'hooks');
36
+ const skillDir = path_1.default.join(root, 'skills', 'using-awm');
37
+ fs_1.default.mkdirSync(hooksDir, { recursive: true });
38
+ fs_1.default.mkdirSync(skillDir, { recursive: true });
39
+ fs_1.default.writeFileSync(path_1.default.join(hooksDir, 'session-start'), '#!/bin/sh\n');
40
+ fs_1.default.writeFileSync(path_1.default.join(hooksDir, 'run-hook.cmd'), '#!/bin/sh\n');
41
+ fs_1.default.writeFileSync(path_1.default.join(skillDir, 'SKILL.md'), '# using-awm\n');
42
+ }
43
+ it('copies the skill when symlink throws (EPERM), preserving content', () => {
44
+ const registryRoot = path_1.default.join(tmpHome, 'registry');
45
+ seedRegistry(registryRoot);
46
+ // Force symlinkSync to fail like a platform without symlink permission.
47
+ symlinkSpy = jest.spyOn(fs_1.default, 'symlinkSync').mockImplementation(() => {
48
+ const err = new Error('EPERM: operation not permitted, symlink');
49
+ err.code = 'EPERM';
50
+ throw err;
51
+ });
52
+ const { installHook } = require('../../../src/commands/hooks/install');
53
+ const result = installHook({ agent: 'claude-code', registryRoot, installMethod: 'copy' });
54
+ const skillDest = path_1.default.join(result.scriptsDir, 'using-awm.md');
55
+ expect(fs_1.default.existsSync(skillDest)).toBe(true);
56
+ expect(fs_1.default.lstatSync(skillDest).isSymbolicLink()).toBe(false); // it was copied, not linked
57
+ expect(fs_1.default.readFileSync(skillDest, 'utf-8')).toContain('using-awm');
58
+ });
59
+ });
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const path_1 = __importDefault(require("path"));
7
+ const eslint_1 = require("../../../src/commands/sensors/formatters/eslint");
8
+ describe('parseEslintOutput — relative path normalization', () => {
9
+ it('produces a cwd-relative path using the OS separator', () => {
10
+ const cwd = process.cwd();
11
+ const abs = path_1.default.join(cwd, 'src', 'foo.ts');
12
+ const raw = JSON.stringify([
13
+ { filePath: abs, messages: [{ ruleId: 'no-eval', severity: 2, message: 'no eval', line: 3, column: 1 }] },
14
+ ]);
15
+ const errors = (0, eslint_1.parseEslintOutput)(raw);
16
+ expect(errors).toHaveLength(1);
17
+ const first = errors[0];
18
+ if (!first)
19
+ throw new Error('Expected at least one error');
20
+ // path.join('src','foo.ts') uses the OS separator; on POSIX this is 'src/foo.ts'
21
+ expect(first.file).toBe(path_1.default.join('src', 'foo.ts'));
22
+ const file = first.file ?? '';
23
+ expect(file.startsWith(path_1.default.sep)).toBe(false); // genuinely relative
24
+ });
25
+ it('preserves the absolute path for files outside cwd', () => {
26
+ const abs = '/some/other/project/file.ts';
27
+ const raw = JSON.stringify([
28
+ { filePath: abs, messages: [{ ruleId: 'no-eval', severity: 2, message: 'no eval', line: 1, column: 1 }] },
29
+ ]);
30
+ const errors = (0, eslint_1.parseEslintOutput)(raw);
31
+ expect(errors).toHaveLength(1);
32
+ expect(errors[0]?.file).toBe(abs); // absolute path preserved, no traversal strings
33
+ });
34
+ });
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const os_1 = __importDefault(require("os"));
7
+ const path_1 = __importDefault(require("path"));
8
+ const paths_1 = require("../../src/core/paths");
9
+ describe('core/paths', () => {
10
+ let origHome;
11
+ let origAwmHome;
12
+ const realPlatform = process.platform;
13
+ beforeEach(() => {
14
+ origHome = process.env.HOME;
15
+ origAwmHome = process.env.AWM_HOME;
16
+ });
17
+ afterEach(() => {
18
+ if (origHome === undefined)
19
+ delete process.env.HOME;
20
+ else
21
+ process.env.HOME = origHome;
22
+ if (origAwmHome === undefined)
23
+ delete process.env.AWM_HOME;
24
+ else
25
+ process.env.AWM_HOME = origAwmHome;
26
+ Object.defineProperty(process, 'platform', { value: realPlatform, configurable: true });
27
+ });
28
+ function setPlatform(p) {
29
+ Object.defineProperty(process, 'platform', { value: p, configurable: true });
30
+ }
31
+ it('homeDir uses process.env.HOME when set', () => {
32
+ process.env.HOME = '/tmp/fake-home';
33
+ expect((0, paths_1.homeDir)()).toBe('/tmp/fake-home');
34
+ });
35
+ it('homeDir falls back to os.homedir() when HOME is unset', () => {
36
+ delete process.env.HOME;
37
+ expect((0, paths_1.homeDir)()).toBe(os_1.default.homedir());
38
+ });
39
+ it('awmHome honors AWM_HOME override', () => {
40
+ process.env.AWM_HOME = '/tmp/custom-awm';
41
+ expect((0, paths_1.awmHome)()).toBe('/tmp/custom-awm');
42
+ });
43
+ it('awmHome defaults to <home>/.awm when AWM_HOME is unset', () => {
44
+ delete process.env.AWM_HOME;
45
+ process.env.HOME = '/tmp/fake-home';
46
+ expect((0, paths_1.awmHome)()).toBe(path_1.default.join('/tmp/fake-home', '.awm'));
47
+ });
48
+ it('platform reflects process.platform', () => {
49
+ setPlatform('linux');
50
+ expect((0, paths_1.platform)()).toBe('linux');
51
+ });
52
+ it('isWindowsNative is true only on win32', () => {
53
+ setPlatform('win32');
54
+ expect((0, paths_1.isWindowsNative)()).toBe(true);
55
+ setPlatform('linux');
56
+ expect((0, paths_1.isWindowsNative)()).toBe(false);
57
+ setPlatform('darwin');
58
+ expect((0, paths_1.isWindowsNative)()).toBe(false);
59
+ });
60
+ it('platformLabel describes each known platform', () => {
61
+ setPlatform('linux');
62
+ expect((0, paths_1.platformLabel)()).toBe('Linux');
63
+ setPlatform('darwin');
64
+ expect((0, paths_1.platformLabel)()).toBe('macOS');
65
+ setPlatform('win32');
66
+ expect((0, paths_1.platformLabel)()).toContain('WSL');
67
+ });
68
+ it('warnIfUnsupportedPlatform calls the logger only on win32', () => {
69
+ const calls = [];
70
+ const log = (m) => calls.push(m);
71
+ setPlatform('linux');
72
+ (0, paths_1.warnIfUnsupportedPlatform)(log);
73
+ expect(calls).toHaveLength(0);
74
+ setPlatform('win32');
75
+ (0, paths_1.warnIfUnsupportedPlatform)(log);
76
+ expect(calls).toEqual([paths_1.WINDOWS_NATIVE_WARNING]);
77
+ });
78
+ });
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "agentic-workflow-manager",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "main": "dist/src/index.js",
5
5
  "bin": {
6
6
  "awm": "./dist/src/index.js"
7
7
  },
8
- "files": ["dist"],
8
+ "files": [
9
+ "dist"
10
+ ],
9
11
  "repository": {
10
12
  "type": "git",
11
13
  "url": "git+https://github.com/Kodria/agentic-workflow.git",