agentic-workflow-manager 2.0.1 → 2.1.1

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
@@ -9,6 +9,8 @@ const commander_1 = require("commander");
9
9
  const config_1 = require("./utils/config");
10
10
  const grouping_1 = require("./utils/grouping");
11
11
  const registry_view_1 = require("./utils/registry-view");
12
+ const tty_1 = require("./ui/tty");
13
+ const picker_1 = require("./ui/picker");
12
14
  const providers_1 = require("./providers");
13
15
  const executor_1 = require("./core/executor");
14
16
  const regenerate_1 = require("./core/context/regenerate");
@@ -34,6 +36,7 @@ const registry_1 = require("./commands/registry");
34
36
  const pin_1 = require("./commands/pin");
35
37
  const profile_pins_1 = require("./core/profile-pins");
36
38
  const update_check_1 = require("./core/update-check");
39
+ const paths_1 = require("./core/paths");
37
40
  const program = new commander_1.Command();
38
41
  program.name('awm').description('Agentic Workflow Manager').version((0, cli_version_1.cliVersion)());
39
42
  program.hook('postAction', () => {
@@ -72,6 +75,7 @@ program.command('add [name]')
72
75
  .option('-s, --scope <scope>', 'Scope: local or global')
73
76
  .option('-m, --method <method>', 'Install method: symlink or copy')
74
77
  .option('-y, --yes', 'Skip confirmation prompts')
78
+ .option('--all', 'install all artifacts from all packages without prompting')
75
79
  .action(async (name, options) => {
76
80
  (0, prompts_1.intro)(picocolors_1.default.bgCyan(picocolors_1.default.black(' AWM - Agentic Workflow Manager ')));
77
81
  // 1. Sync the registry
@@ -141,6 +145,10 @@ program.command('add [name]')
141
145
  (0, prompts_1.outro)(`✅ Installed bundle ${picocolors_1.default.cyan(matchedBundle.name)}:\n ${lines}${recordNote}`);
142
146
  return;
143
147
  }
148
+ // name given but no matching bundle found — report clearly regardless of TTY
149
+ console.error(picocolors_1.default.red(`Bundle "${name}" not found in registry.`));
150
+ console.error(picocolors_1.default.dim('Run `awm list` to see available packages.'));
151
+ process.exit(1);
144
152
  }
145
153
  // 2. Discover artifacts
146
154
  const skills = (0, discovery_1.discoverSkills)();
@@ -151,6 +159,103 @@ program.command('add [name]')
151
159
  process.exit(0);
152
160
  }
153
161
  const prefs = (0, config_1.getPreferences)();
162
+ // --all: install everything from every package headlessly
163
+ if (options.all) {
164
+ let targetAgents;
165
+ if (options.agent) {
166
+ const validAgents = Object.keys(providers_1.PROVIDERS);
167
+ const parsed = options.agent.split(',').map((a) => a.trim());
168
+ for (const a of parsed) {
169
+ if (!validAgents.includes(a)) {
170
+ console.error(picocolors_1.default.red(`Invalid agent "${a}". Use: ${validAgents.join(', ')}.`));
171
+ process.exit(1);
172
+ }
173
+ }
174
+ targetAgents = parsed;
175
+ }
176
+ else {
177
+ targetAgents = [prefs.defaultAgent];
178
+ }
179
+ let scopeVal;
180
+ if (options.scope) {
181
+ if (!['local', 'global'].includes(options.scope)) {
182
+ console.error(picocolors_1.default.red(`Invalid scope "${options.scope}". Use: local or global.`));
183
+ process.exit(1);
184
+ }
185
+ scopeVal = options.scope;
186
+ }
187
+ else {
188
+ scopeVal = prefs.defaultScope;
189
+ }
190
+ let methodVal;
191
+ if (options.method) {
192
+ if (!['symlink', 'copy'].includes(options.method)) {
193
+ console.error(picocolors_1.default.red(`Invalid method "${options.method}". Use: symlink or copy.`));
194
+ process.exit(1);
195
+ }
196
+ methodVal = options.method;
197
+ }
198
+ else {
199
+ methodVal = scopeVal === 'local' ? 'copy' : 'symlink';
200
+ }
201
+ const includeWorkflows = targetAgents.some((a) => providers_1.PROVIDERS[a].workflow !== null);
202
+ const includeAgents = targetAgents.some((a) => providers_1.PROVIDERS[a].agent !== null);
203
+ const allView = (0, registry_view_1.buildPackageView)(skills, includeWorkflows ? workflows : [], includeAgents ? agents : [], (0, bundles_1.discoverAllBundles)());
204
+ const allDedup = new Map();
205
+ for (const pkg of allView) {
206
+ for (const a of pkg.artifacts) {
207
+ allDedup.set((0, registry_view_1.artifactValue)(a), a);
208
+ }
209
+ }
210
+ const artifactsToInstall = Array.from(allDedup.values()).map((a) => ({
211
+ name: a.installName,
212
+ sourcePath: a.sourcePath,
213
+ type: a.type,
214
+ }));
215
+ if (artifactsToInstall.length === 0) {
216
+ (0, prompts_1.outro)(picocolors_1.default.yellow('No artifacts available.'));
217
+ return;
218
+ }
219
+ const installSpinner = (0, prompts_1.spinner)();
220
+ installSpinner.start('Installing artifacts...');
221
+ try {
222
+ const installed = [];
223
+ const skipped = [];
224
+ for (const currentAgent of targetAgents) {
225
+ for (const artifact of artifactsToInstall) {
226
+ if (providers_1.PROVIDERS[currentAgent][artifact.type] === null) {
227
+ skipped.push(`${artifact.name} (${currentAgent})`);
228
+ continue;
229
+ }
230
+ const targetDir = (0, providers_1.getTargetPath)(artifact.type, currentAgent, scopeVal);
231
+ const finalDest = path_1.default.join(targetDir, artifact.name);
232
+ (0, executor_1.installArtifact)(artifact.sourcePath, finalDest, methodVal);
233
+ installed.push(`${artifact.name} → ${currentAgent} (${scopeVal})`);
234
+ }
235
+ }
236
+ (0, config_1.savePreferences)({ defaultAgent: targetAgents[0], defaultScope: scopeVal, installMethod: methodVal });
237
+ installSpinner.stop('Installation complete!');
238
+ if (skipped.length > 0) {
239
+ for (const sk of skipped) {
240
+ console.log(picocolors_1.default.yellow(` ⚠️ Skipped: ${sk} (not supported by target agent)`));
241
+ }
242
+ }
243
+ const names = installed.map((n) => picocolors_1.default.green(n)).join('\n ');
244
+ (0, prompts_1.outro)(`✅ Installed:\n ${names}`);
245
+ }
246
+ catch (e) {
247
+ installSpinner.stop('Installation failed.');
248
+ console.error(picocolors_1.default.red(e.message));
249
+ process.exit(1);
250
+ }
251
+ return;
252
+ }
253
+ // Interactive selection requires a TTY. Bundle-by-name (handled above) works headless.
254
+ if (!(0, tty_1.isInteractive)()) {
255
+ console.error(picocolors_1.default.red('`awm add` needs an interactive terminal for selection.'));
256
+ console.error(picocolors_1.default.dim('Install a bundle non-interactively: `awm add <bundle>` (optionally with --agent/--scope).'));
257
+ process.exit(1);
258
+ }
154
259
  // 3. Agent & Scope Prompts (Moved up)
155
260
  let targetAgents;
156
261
  if (options.agent) {
@@ -206,27 +311,35 @@ program.command('add [name]')
206
311
  process.exit(0);
207
312
  }
208
313
  // 5. Level 1 — pick package(s)
209
- const pkgChoice = await (0, prompts_1.multiselect)({
210
- message: 'Select package(s)',
211
- options: (0, registry_view_1.buildLevel1Options)(view),
212
- required: true
314
+ const pickedPackages = await (0, picker_1.multiselectPicker)({
315
+ title: 'Select package(s)',
316
+ items: (0, registry_view_1.packagePickerItems)(view),
213
317
  });
214
- handleCancel(pkgChoice);
215
- const selectedPackages = pkgChoice
216
- .map(name => view.find(p => p.name === name))
318
+ if (pickedPackages === null) {
319
+ (0, prompts_1.outro)('Operation cancelled.');
320
+ process.exit(0);
321
+ }
322
+ if (pickedPackages.length === 0) {
323
+ (0, prompts_1.outro)(picocolors_1.default.yellow('No packages selected.'));
324
+ return;
325
+ }
326
+ const selectedPackages = pickedPackages
327
+ .map((name) => view.find((p) => p.name === name))
217
328
  .filter(Boolean);
218
329
  // 5b. Level 2 — pick skills within each package, in sequence
219
330
  const dedup = new Map();
220
331
  for (let i = 0; i < selectedPackages.length; i++) {
221
332
  const pkg = selectedPackages[i];
222
- const skillChoice = await (0, prompts_1.multiselect)({
223
- message: `[${i + 1}/${selectedPackages.length}] ${pkg.name} — select artifacts`,
224
- options: (0, registry_view_1.buildLevel2Options)(pkg),
225
- initialValues: [registry_view_1.ALL_SENTINEL],
226
- required: true
333
+ const picked = await (0, picker_1.multiselectPicker)({
334
+ title: `[${i + 1}/${selectedPackages.length}] ${pkg.name} — select artifacts`,
335
+ items: (0, registry_view_1.artifactPickerItems)(pkg),
336
+ initialSelected: [registry_view_1.ALL_SENTINEL, ...pkg.artifacts.map((a) => (0, registry_view_1.artifactValue)(a))],
227
337
  });
228
- handleCancel(skillChoice);
229
- for (const a of (0, registry_view_1.resolveLevel2Selection)(pkg, skillChoice)) {
338
+ if (picked === null) {
339
+ (0, prompts_1.outro)('Operation cancelled.');
340
+ process.exit(0);
341
+ }
342
+ for (const a of (0, registry_view_1.resolveLevel2Selection)(pkg, picked)) {
230
343
  dedup.set((0, registry_view_1.artifactValue)(a), a);
231
344
  }
232
345
  }
@@ -359,6 +472,7 @@ program.command('sync')
359
472
  .option('-m, --method <method>', 'Install method: symlink or copy', 'symlink')
360
473
  .action(async (options) => {
361
474
  (0, prompts_1.intro)(picocolors_1.default.bgCyan(picocolors_1.default.black(' AWM - Sync Project Profile ')));
475
+ (0, paths_1.warnIfUnsupportedPlatform)((m) => console.warn(picocolors_1.default.yellow(`⚠ ${m}`)));
362
476
  const projectRoot = (0, profile_1.findProjectRoot)(process.cwd());
363
477
  if (!projectRoot) {
364
478
  console.error(picocolors_1.default.red('No project root found (need a .git/, package.json, or .awm/profile.json here).'));
@@ -467,6 +581,7 @@ program.command('list [package]')
467
581
  if (packageName && options.all) {
468
582
  console.log(picocolors_1.default.dim('(Ignoring --all when a package name is provided.)'));
469
583
  }
584
+ const listWidth = process.stdout.isTTY ? process.stdout.columns : undefined;
470
585
  // Detail for a single package.
471
586
  if (packageName) {
472
587
  const { match, suggestion } = (0, registry_view_1.findPackage)(fullView, packageName);
@@ -478,7 +593,7 @@ program.command('list [package]')
478
593
  process.exit(1);
479
594
  }
480
595
  console.log();
481
- for (const line of (0, registry_view_1.packageDetailLines)(match))
596
+ for (const line of (0, registry_view_1.packageDetailLines)(match, listWidth))
482
597
  console.log(line);
483
598
  console.log();
484
599
  (0, prompts_1.outro)(`Run ${picocolors_1.default.green(`awm add`)} to install artifacts from ${picocolors_1.default.cyan(match.name)}.`);
@@ -488,7 +603,7 @@ program.command('list [package]')
488
603
  if (options.all) {
489
604
  for (const pkg of view) {
490
605
  console.log();
491
- for (const line of (0, registry_view_1.packageDetailLines)(pkg))
606
+ for (const line of (0, registry_view_1.packageDetailLines)(pkg, listWidth))
492
607
  console.log(line);
493
608
  }
494
609
  console.log();
@@ -497,7 +612,7 @@ program.command('list [package]')
497
612
  }
498
613
  // Default: compact summary.
499
614
  console.log();
500
- for (const line of (0, registry_view_1.packageSummaryLines)(view))
615
+ for (const line of (0, registry_view_1.packageSummaryLines)(view, listWidth))
501
616
  console.log(line);
502
617
  console.log();
503
618
  console.log(picocolors_1.default.dim(` awm list <pkg> · awm list --all`));
@@ -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
  },
@@ -0,0 +1,77 @@
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.TWO_PANE_MIN_COLUMNS = void 0;
7
+ exports.visibleItems = visibleItems;
8
+ exports.renderPicker = renderPicker;
9
+ const picocolors_1 = __importDefault(require("picocolors"));
10
+ const text_1 = require("./text");
11
+ exports.TWO_PANE_MIN_COLUMNS = 72;
12
+ const FOOTER = '↑↓ move · space select · tab all · ⏎ confirm · esc clear';
13
+ function visibleItems(state) {
14
+ if (!state.filter)
15
+ return state.items;
16
+ const q = state.filter.toLowerCase();
17
+ return state.items.filter((i) => i.label.toLowerCase().includes(q));
18
+ }
19
+ /** Compute a scroll window [start, end) around the cursor that fits `height` rows. */
20
+ function windowFor(cursor, total, height) {
21
+ if (total <= height)
22
+ return [0, total];
23
+ let start = cursor - Math.floor(height / 2);
24
+ start = Math.max(0, Math.min(start, total - height));
25
+ return [start, start + height];
26
+ }
27
+ function checkbox(selected) {
28
+ return selected ? picocolors_1.default.green('◼') : '◻';
29
+ }
30
+ function renderPicker(state, vp) {
31
+ const vis = visibleItems(state);
32
+ const cursor = Math.max(0, Math.min(state.cursor, vis.length - 1));
33
+ const selCount = state.selected.size;
34
+ const filterPart = state.filter ? ` filter: ${state.filter}` : '';
35
+ const header = picocolors_1.default.bold(state.title) + filterPart + (selCount ? picocolors_1.default.dim(` ${selCount} sel`) : '');
36
+ const out = [header];
37
+ if (vis.length === 0) {
38
+ out.push(picocolors_1.default.dim(' (no matches)'));
39
+ out.push(FOOTER);
40
+ return out;
41
+ }
42
+ const bodyHeight = Math.max(3, vp.rows - 4); // header + footer + breathing room
43
+ const [start, end] = windowFor(cursor, vis.length, bodyHeight);
44
+ const twoPane = vp.columns >= exports.TWO_PANE_MIN_COLUMNS;
45
+ const listWidth = twoPane ? Math.floor(vp.columns * 0.5) : vp.columns - 2;
46
+ const rows = [];
47
+ for (let i = start; i < end; i++) {
48
+ const item = vis[i];
49
+ const isCursor = i === cursor;
50
+ const marker = isCursor ? picocolors_1.default.cyan('❯') : ' ';
51
+ const box = checkbox(state.selected.has(item.value));
52
+ const labelWidth = listWidth - 5; // wide cursor marker (2 cells) + space + checkbox + space
53
+ const label = (0, text_1.truncate)(item.label, labelWidth);
54
+ rows.push(`${marker} ${box} ${label}`);
55
+ }
56
+ if (twoPane) {
57
+ const detailWidth = vp.columns - listWidth - 3; // gutter " │ "
58
+ const detail = (0, text_1.wrap)(vis[cursor].description, detailWidth).slice(0, rows.length);
59
+ const height = Math.max(rows.length, detail.length);
60
+ for (let i = 0; i < height; i++) {
61
+ const leftPadded = padToWidth(rows[i] ?? '', listWidth);
62
+ const right = detail[i] ?? '';
63
+ out.push(`${leftPadded} ${picocolors_1.default.dim('│')} ${picocolors_1.default.dim(right)}`);
64
+ }
65
+ }
66
+ else {
67
+ out.push(...rows);
68
+ out.push(picocolors_1.default.dim(' ' + ((0, text_1.wrap)(vis[cursor].description, vp.columns - 4)[0] ?? '')));
69
+ }
70
+ out.push(FOOTER);
71
+ return out;
72
+ }
73
+ /** Pad a possibly-colored string to `width` visible cells. */
74
+ function padToWidth(s, width) {
75
+ const pad = width - (0, text_1.displayWidth)(s);
76
+ return pad > 0 ? s + ' '.repeat(pad) : s;
77
+ }