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.
- package/dist/src/commands/doctor.js +2 -0
- package/dist/src/commands/hooks/install.js +10 -4
- package/dist/src/commands/hooks/uninstall.js +2 -2
- package/dist/src/commands/init.js +2 -0
- package/dist/src/commands/sensors/formatters/eslint.js +7 -1
- package/dist/src/commands/sensors/install.js +3 -4
- package/dist/src/core/context/materializer.js +2 -5
- package/dist/src/core/diagnostics/context.js +2 -4
- package/dist/src/core/paths.js +56 -0
- package/dist/src/core/registries.js +3 -3
- package/dist/src/core/update-check.js +2 -3
- package/dist/src/index.js +132 -17
- package/dist/src/providers/index.js +4 -4
- package/dist/src/ui/picker-view.js +77 -0
- package/dist/src/ui/picker.js +194 -0
- package/dist/src/ui/text.js +75 -0
- package/dist/src/ui/tty.js +15 -0
- package/dist/src/utils/config.js +2 -2
- package/dist/src/utils/registry-view.js +35 -21
- package/dist/tests/commands/doctor-platform.test.js +22 -0
- package/dist/tests/commands/hooks/install-symlink-fallback.test.js +59 -0
- package/dist/tests/commands/sensors/eslint-formatter.test.js +34 -0
- package/dist/tests/core/paths.test.js +78 -0
- package/dist/tests/ui/picker-reducer.test.js +92 -0
- package/dist/tests/ui/picker-shell.test.js +59 -0
- package/dist/tests/ui/picker-view.test.js +57 -0
- package/dist/tests/ui/text.test.js +55 -0
- package/dist/tests/ui/tty.test.js +39 -0
- package/dist/tests/utils/registry-view.test.js +39 -36
- package/package.json +4 -2
|
@@ -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
|
|
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.
|
|
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
|
-
|
|
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
|
|
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 +
|
|
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
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
28
|
-
|
|
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
|
-
|
|
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
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
.
|
|
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
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
229
|
-
|
|
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
|
|
14
|
-
const
|
|
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(
|
|
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
|
+
}
|