@ghl-ai/aw 0.1.58-beta.0 → 0.1.59
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/cli.mjs +2 -5
- package/commands/init.mjs +56 -13
- package/commands/integration.mjs +38 -77
- package/commands/integrations.mjs +84 -100
- package/commands/push.mjs +48 -20
- package/ecc.mjs +14 -3
- package/git.mjs +2 -0
- package/integrations/context-mode.mjs +312 -89
- package/integrations.mjs +153 -40
- package/package-manager.mjs +72 -0
- package/package.json +4 -3
- package/registry.mjs +27 -8
- package/update.mjs +29 -14
- package/integrations/index.mjs +0 -31
package/cli.mjs
CHANGED
|
@@ -168,13 +168,10 @@ function printHelp() {
|
|
|
168
168
|
cmd('aw mcp status', 'Show global AW MCP mode for Claude/Cursor/Codex'),
|
|
169
169
|
cmd('aw mcp disable', 'Remove AW-managed MCP server across Claude/Cursor/Codex'),
|
|
170
170
|
cmd('aw mcp enable', 'Restore AW-managed MCP server across Claude/Cursor/Codex'),
|
|
171
|
-
cmd('aw integrations', 'Manage third-party integrations (Codex, Caveman, etc)'),
|
|
172
|
-
cmd('aw integrations add <key>', 'Install a specific tool (e.g. codex, caveman)'),
|
|
171
|
+
cmd('aw integrations', 'Manage third-party integrations (Codex, Caveman, Context Mode, etc)'),
|
|
172
|
+
cmd('aw integrations add <key>', 'Install a specific tool (e.g. codex, caveman, context-mode)'),
|
|
173
173
|
cmd('aw integrations remove <key>', 'Remove a tool'),
|
|
174
174
|
cmd('aw integrations bundle <name>', 'Install a preset bundle'),
|
|
175
|
-
cmd('aw integration add <name>', 'Configure an installed local integration, e.g. context-mode'),
|
|
176
|
-
cmd('aw integration status <name>', 'Show local integration health'),
|
|
177
|
-
cmd('aw integration remove <name>', 'Remove AW-managed local integration entries'),
|
|
178
175
|
cmd('aw drop <path>', 'Stop syncing or delete local content'),
|
|
179
176
|
cmd('aw nuke', 'Remove entire .aw_registry/ & start fresh'),
|
|
180
177
|
cmd('aw daemon install', 'Auto-pull on a schedule (macOS launchd / Linux cron)'),
|
package/commands/init.mjs
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
import { execSync } from 'node:child_process';
|
|
19
19
|
import { join, dirname, sep } from 'node:path';
|
|
20
20
|
import { homedir } from 'node:os';
|
|
21
|
-
import { fileURLToPath } from 'node:url';
|
|
21
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
22
22
|
import * as p from '@clack/prompts';
|
|
23
23
|
import * as config from '../config.mjs';
|
|
24
24
|
import * as fmt from '../fmt.mjs';
|
|
@@ -26,7 +26,7 @@ import { chalk, setSilent } from '../fmt.mjs';
|
|
|
26
26
|
import { linkWorkspace } from '../link.mjs';
|
|
27
27
|
import { generateCommands, copyInstructions, initAwDocs, syncHomeHarnessInstructions } from '../integrate.mjs';
|
|
28
28
|
import { setupMcp } from '../mcp.mjs';
|
|
29
|
-
import {
|
|
29
|
+
import { isContextModeRequested } from '../integrations/context-mode.mjs';
|
|
30
30
|
import { applyStoredStartupPreferences, ensureAwRuntimeHook, isDefaultRoutingEnabled } from '../startup.mjs';
|
|
31
31
|
import { installLocalCommitHook } from '../hooks.mjs';
|
|
32
32
|
import { autoUpdate, promptUpdate } from '../update.mjs';
|
|
@@ -35,7 +35,7 @@ import { loadConfig as ensureTelemetryConfig } from '../telemetry.mjs';
|
|
|
35
35
|
import { installAwEcc, AW_ECC_TAG } from '../ecc.mjs';
|
|
36
36
|
import { removeWorkspaceHookDefaults } from '../codex.mjs';
|
|
37
37
|
import { readHookManifest, pruneStaleHooks, writeHookManifest } from '../hook-cleanup.mjs';
|
|
38
|
-
import {
|
|
38
|
+
import { installIntegration, autoInstallIntegrations } from '../integrations.mjs';
|
|
39
39
|
import {
|
|
40
40
|
initPersistentClone,
|
|
41
41
|
isValidClone,
|
|
@@ -65,6 +65,41 @@ const HOME = (() => { try { return realpathSync(_rawHome); } catch { return _raw
|
|
|
65
65
|
const GLOBAL_AW_DIR = join(HOME, '.aw_registry');
|
|
66
66
|
const AW_HOME = join(HOME, '.aw');
|
|
67
67
|
|
|
68
|
+
function errorMessage(error) {
|
|
69
|
+
return error instanceof Error ? error.message : String(error);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function writeHookManifestBestEffort(manifest, context) {
|
|
73
|
+
try {
|
|
74
|
+
writeHookManifest(manifest);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
fmt.logWarn(`Could not persist hook manifest${context ? ` ${context}` : ''}: ${errorMessage(error)}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function syncEccAfterAwUpdate(updateResult, cwd, { silent = false } = {}) {
|
|
81
|
+
if (updateResult?.status !== 'upgraded' || !updateResult.packageRoot) {
|
|
82
|
+
return { synced: false, reason: updateResult?.reason || updateResult?.status || 'not-upgraded' };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const eccModulePath = join(updateResult.packageRoot, 'ecc.mjs');
|
|
86
|
+
if (!existsSync(eccModulePath)) {
|
|
87
|
+
return { synced: false, reason: 'missing-ecc-module' };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const moduleUrl = `${pathToFileURL(eccModulePath).href}?aw-post-update=${Date.now()}`;
|
|
92
|
+
const ecc = await import(moduleUrl);
|
|
93
|
+
await ecc.installAwEcc(cwd, { silent });
|
|
94
|
+
return { synced: true, eccVersion: ecc.AW_ECC_TAG || null };
|
|
95
|
+
} catch (err) {
|
|
96
|
+
if (!silent) {
|
|
97
|
+
fmt.logWarn(`AW updated, but post-update ECC sync failed: ${err.message}`);
|
|
98
|
+
}
|
|
99
|
+
return { synced: false, reason: err.message };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
68
103
|
function syncRulesTargets(targetDir) {
|
|
69
104
|
const rulesSrc = join(AW_HOME, RULES_SOURCE_DIR);
|
|
70
105
|
if (!existsSync(rulesSrc)) return false;
|
|
@@ -94,23 +129,24 @@ function syncHomeAndProjectInstructions(cwd, namespace) {
|
|
|
94
129
|
}
|
|
95
130
|
}
|
|
96
131
|
|
|
97
|
-
function maybeConfigureContextMode(args, { silent = false } = {}) {
|
|
132
|
+
async function maybeConfigureContextMode(args, { silent = false } = {}) {
|
|
98
133
|
if (!isContextModeRequested(args, process.env)) {
|
|
99
134
|
return null;
|
|
100
135
|
}
|
|
101
136
|
|
|
102
|
-
const result =
|
|
137
|
+
const result = await installIntegration('context-mode', {
|
|
138
|
+
home: HOME,
|
|
103
139
|
env: process.env,
|
|
104
140
|
silent,
|
|
105
141
|
});
|
|
106
142
|
|
|
107
143
|
if (!silent) {
|
|
108
|
-
for (const warning of result.warnings) {
|
|
144
|
+
for (const warning of result.warnings || []) {
|
|
109
145
|
fmt.logWarn(warning);
|
|
110
146
|
}
|
|
111
|
-
if (result.changedFiles.length > 0) {
|
|
147
|
+
if ((result.changedFiles || []).length > 0) {
|
|
112
148
|
fmt.logStep(`Context Mode configured (${result.changedFiles.length} file${result.changedFiles.length === 1 ? '' : 's'})`);
|
|
113
|
-
} else if (result.warnings.length > 0) {
|
|
149
|
+
} else if ((result.warnings || []).length > 0) {
|
|
114
150
|
fmt.logWarn('Context Mode was requested but not configured');
|
|
115
151
|
} else {
|
|
116
152
|
fmt.logStep('Context Mode already configured');
|
|
@@ -404,7 +440,7 @@ export async function initCommand(args) {
|
|
|
404
440
|
ensureAwRuntimeHook(HOME);
|
|
405
441
|
syncHomeAndProjectInstructions(cwd, freshCfg?.namespace || team);
|
|
406
442
|
await setupMcp(HOME, freshCfg?.namespace || team, { silent });
|
|
407
|
-
maybeConfigureContextMode(args, { silent });
|
|
443
|
+
await maybeConfigureContextMode(args, { silent });
|
|
408
444
|
applyStoredStartupPreferences(HOME);
|
|
409
445
|
const removedLegacyStartupFiles = cwd !== HOME ? removeWorkspaceHookDefaults(cwd) : [];
|
|
410
446
|
installGlobalHooks();
|
|
@@ -444,7 +480,7 @@ export async function initCommand(args) {
|
|
|
444
480
|
if (!silent) fmt.logStep(`IDE wired — ${chalk.bold(symlinks)} symlinks · ${chalk.bold(commands)} commands`);
|
|
445
481
|
|
|
446
482
|
// Write hook manifest after all hook installation is complete
|
|
447
|
-
|
|
483
|
+
writeHookManifestBestEffort({ eccVersion: AW_ECC_TAG, awVersion: VERSION });
|
|
448
484
|
|
|
449
485
|
// Auto-install suggested integrations (Codex, Caveman, Graphify, etc) - unless --no-integrations
|
|
450
486
|
let installedIntegrations = [];
|
|
@@ -454,7 +490,14 @@ export async function initCommand(args) {
|
|
|
454
490
|
|
|
455
491
|
if (silent) {
|
|
456
492
|
if (silentSpinner) { silentSpinner.stop('Done'); setSilent(false); }
|
|
457
|
-
autoUpdate(await args._updateCheck);
|
|
493
|
+
const updateResult = autoUpdate(await args._updateCheck);
|
|
494
|
+
const postUpdateEcc = await syncEccAfterAwUpdate(updateResult, cwd, { silent: true });
|
|
495
|
+
if (postUpdateEcc.synced) {
|
|
496
|
+
writeHookManifestBestEffort(
|
|
497
|
+
{ eccVersion: postUpdateEcc.eccVersion, awVersion: updateResult.to },
|
|
498
|
+
'after AW update',
|
|
499
|
+
);
|
|
500
|
+
}
|
|
458
501
|
} else {
|
|
459
502
|
fmt.outro([
|
|
460
503
|
`⟁ ${isNewSubTeam ? `Sub-team ${chalk.cyan(folderName)} added` : 'Up to date'}`,
|
|
@@ -565,7 +608,7 @@ export async function initCommand(args) {
|
|
|
565
608
|
Promise.resolve(syncHomeAndProjectInstructions(cwd, team)),
|
|
566
609
|
setupMcp(HOME, team, { silent }),
|
|
567
610
|
]);
|
|
568
|
-
maybeConfigureContextMode(args, { silent });
|
|
611
|
+
await maybeConfigureContextMode(args, { silent });
|
|
569
612
|
// applyStoredStartupPreferences reads settings written by ECC — keep after batch B
|
|
570
613
|
applyStoredStartupPreferences(HOME);
|
|
571
614
|
const removedLegacyStartupFiles = cwd !== HOME ? removeWorkspaceHookDefaults(cwd) : [];
|
|
@@ -630,7 +673,7 @@ export async function initCommand(args) {
|
|
|
630
673
|
|
|
631
674
|
// Write hook manifest after all hook installation is complete, including
|
|
632
675
|
// bundled usage hooks, so `aw nuke` can prune AW-managed settings entries.
|
|
633
|
-
|
|
676
|
+
writeHookManifestBestEffort({ eccVersion: AW_ECC_TAG, awVersion: VERSION });
|
|
634
677
|
|
|
635
678
|
// Auto-install suggested integrations (Codex, Caveman, Graphify, etc) - unless --no-integrations
|
|
636
679
|
let installedIntegrations = [];
|
package/commands/integration.mjs
CHANGED
|
@@ -1,43 +1,52 @@
|
|
|
1
|
+
// commands/integration.mjs — Deprecated compatibility wrapper for `aw integration`.
|
|
2
|
+
|
|
1
3
|
import { homedir } from 'node:os';
|
|
2
4
|
|
|
3
5
|
import * as fmt from '../fmt.mjs';
|
|
4
|
-
import {
|
|
6
|
+
import { getIntegrationSummary, INTEGRATIONS } from '../integrations.mjs';
|
|
7
|
+
import { integrationsCommand } from './integrations.mjs';
|
|
5
8
|
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
fmt.
|
|
9
|
+
function requireKnownIntegration(key) {
|
|
10
|
+
if (!key) {
|
|
11
|
+
fmt.cancel(`Missing integration name. Known integrations: ${Object.keys(INTEGRATIONS).join(', ')}`);
|
|
12
|
+
}
|
|
13
|
+
if (!INTEGRATIONS[key]) {
|
|
14
|
+
fmt.cancel(`Unknown integration: ${key}`);
|
|
9
15
|
}
|
|
10
16
|
}
|
|
11
17
|
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function requireIntegration(name) {
|
|
18
|
-
if (!name) {
|
|
19
|
-
fmt.cancel('Missing integration name. Known integrations: context-mode');
|
|
18
|
+
function printStatus(key, home) {
|
|
19
|
+
const status = getIntegrationSummary(key, { home, env: process.env });
|
|
20
|
+
if (!status) {
|
|
21
|
+
fmt.cancel(`Unknown integration: ${key}`);
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
fmt.note(
|
|
25
|
+
[
|
|
26
|
+
`state: ${status.state}`,
|
|
27
|
+
`installed: ${status.installed}`,
|
|
28
|
+
`configured: ${status.configured}`,
|
|
29
|
+
status.version ? `version: ${status.version}` : null,
|
|
30
|
+
status.path ? `path: ${status.path}` : null,
|
|
31
|
+
`summary: ${status.summary}`,
|
|
32
|
+
].filter(Boolean).join('\n'),
|
|
33
|
+
key,
|
|
34
|
+
);
|
|
27
35
|
}
|
|
28
36
|
|
|
29
37
|
export async function integrationCommand(args) {
|
|
30
|
-
const [subcommand = 'list',
|
|
38
|
+
const [subcommand = 'list', key] = args._positional;
|
|
31
39
|
const home = homedir();
|
|
32
40
|
|
|
33
41
|
if (args['--help']) {
|
|
34
42
|
fmt.intro('aw integration');
|
|
43
|
+
fmt.logWarn('`aw integration` is deprecated. Use `aw integrations` for new automation.');
|
|
35
44
|
fmt.note(
|
|
36
45
|
[
|
|
37
46
|
'aw integration list',
|
|
38
|
-
'aw integration add
|
|
39
|
-
'aw integration status
|
|
40
|
-
'aw integration remove
|
|
47
|
+
'aw integration add <key> [--dry-run] [--strict]',
|
|
48
|
+
'aw integration status <key>',
|
|
49
|
+
'aw integration remove <key> [--dry-run]',
|
|
41
50
|
].join('\n'),
|
|
42
51
|
'Usage',
|
|
43
52
|
);
|
|
@@ -45,67 +54,19 @@ export async function integrationCommand(args) {
|
|
|
45
54
|
return;
|
|
46
55
|
}
|
|
47
56
|
|
|
48
|
-
|
|
49
|
-
fmt.intro('aw integration list');
|
|
50
|
-
const rows = listIntegrations(home, { env: process.env });
|
|
51
|
-
fmt.note(
|
|
52
|
-
rows.map(row => `${row.name} ${row.state} ${row.summary}`).join('\n'),
|
|
53
|
-
'Known integrations',
|
|
54
|
-
);
|
|
55
|
-
fmt.outro('Done');
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const integration = requireIntegration(name);
|
|
60
|
-
|
|
61
|
-
if (subcommand === 'add') {
|
|
62
|
-
fmt.intro(`aw integration add ${integration.name}`);
|
|
63
|
-
const result = integration.add(home, {
|
|
64
|
-
env: process.env,
|
|
65
|
-
dryRun: args['--dry-run'] === true,
|
|
66
|
-
});
|
|
67
|
-
printWarnings(result.warnings);
|
|
68
|
-
printChangedFiles(result.changedFiles);
|
|
69
|
-
if (result.changedFiles.length > 0) {
|
|
70
|
-
fmt.outro(`${integration.name} configured`);
|
|
71
|
-
} else if (result.warnings.length > 0) {
|
|
72
|
-
fmt.outro(`${integration.name} not configured; binary is missing or config was unsafe to edit`);
|
|
73
|
-
} else {
|
|
74
|
-
fmt.outro(`${integration.name} already configured`);
|
|
75
|
-
}
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (subcommand === 'remove') {
|
|
80
|
-
fmt.intro(`aw integration remove ${integration.name}`);
|
|
81
|
-
const result = integration.remove(home, {
|
|
82
|
-
dryRun: args['--dry-run'] === true,
|
|
83
|
-
});
|
|
84
|
-
printWarnings(result.warnings);
|
|
85
|
-
printChangedFiles(result.changedFiles);
|
|
86
|
-
fmt.outro(result.changedFiles.length > 0
|
|
87
|
-
? `${integration.name} removed`
|
|
88
|
-
: `${integration.name} not configured`);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
57
|
+
fmt.logWarn('`aw integration` is deprecated. Use `aw integrations` instead.');
|
|
91
58
|
|
|
92
59
|
if (subcommand === 'status') {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
[
|
|
97
|
-
`state: ${status.state}`,
|
|
98
|
-
`installed: ${status.installed}`,
|
|
99
|
-
`configured: ${status.configured}`,
|
|
100
|
-
status.version ? `version: ${status.version}` : null,
|
|
101
|
-
status.path ? `path: ${status.path}` : null,
|
|
102
|
-
`summary: ${status.summary}`,
|
|
103
|
-
].filter(Boolean).join('\n'),
|
|
104
|
-
integration.name,
|
|
105
|
-
);
|
|
60
|
+
requireKnownIntegration(key);
|
|
61
|
+
fmt.intro(`aw integration status ${key}`);
|
|
62
|
+
printStatus(key, home);
|
|
106
63
|
fmt.outro('Done');
|
|
107
64
|
return;
|
|
108
65
|
}
|
|
109
66
|
|
|
67
|
+
if (['list', 'add', 'remove', 'bundle'].includes(subcommand)) {
|
|
68
|
+
return integrationsCommand(args);
|
|
69
|
+
}
|
|
70
|
+
|
|
110
71
|
fmt.cancel(`Unknown integration subcommand: ${subcommand}. Use add / remove / status / list`);
|
|
111
72
|
}
|
|
@@ -1,16 +1,53 @@
|
|
|
1
1
|
// commands/integrations.mjs — CLI command: aw integrations add/remove/list/bundle
|
|
2
2
|
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
|
|
3
5
|
import * as p from '@clack/prompts';
|
|
4
6
|
import * as fmt from '../fmt.mjs';
|
|
5
7
|
import { chalk } from '../fmt.mjs';
|
|
6
8
|
import {
|
|
7
9
|
INTEGRATIONS,
|
|
8
10
|
BUNDLES,
|
|
11
|
+
getIntegrationSummaries,
|
|
9
12
|
installIntegration,
|
|
13
|
+
integrationSucceeded,
|
|
10
14
|
removeIntegration,
|
|
11
|
-
getInstalledList,
|
|
12
15
|
} from '../integrations.mjs';
|
|
13
16
|
|
|
17
|
+
function availableKeys() {
|
|
18
|
+
return Object.keys(INTEGRATIONS).join(', ');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function printWarnings(warnings = []) {
|
|
22
|
+
for (const warning of warnings) fmt.logWarn(warning);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function printChangedFiles(changedFiles = []) {
|
|
26
|
+
if (!changedFiles.length) return;
|
|
27
|
+
fmt.note(changedFiles.map(file => file.replace(homedir(), '~')).join('\n'), 'Changed files');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function printContextModeAddResult(result) {
|
|
31
|
+
printWarnings(result.warnings);
|
|
32
|
+
if (result.plannedInstall) {
|
|
33
|
+
fmt.note(result.install?.commandLine || 'install context-mode@latest', 'Planned package install');
|
|
34
|
+
}
|
|
35
|
+
if (result.binary?.present) {
|
|
36
|
+
fmt.note(
|
|
37
|
+
[
|
|
38
|
+
`binary: ${result.binary.path}`,
|
|
39
|
+
`source: ${result.binary.source}`,
|
|
40
|
+
result.installedPackage ? 'package: installed during this run' : 'package: reused existing binary',
|
|
41
|
+
].filter(Boolean).join('\n'),
|
|
42
|
+
'Context Mode',
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
printChangedFiles(result.changedFiles);
|
|
46
|
+
if (result.configuredHarnesses?.length) {
|
|
47
|
+
fmt.note(result.configuredHarnesses.join('\n'), 'Configured surfaces');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
14
51
|
export async function integrationsCommand(args) {
|
|
15
52
|
const subcommand = args._positional[0];
|
|
16
53
|
|
|
@@ -39,92 +76,16 @@ async function cmdList() {
|
|
|
39
76
|
subtitle: ' Available tools and MCP servers',
|
|
40
77
|
});
|
|
41
78
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
([, i]) => i.type === 'plugin'
|
|
47
|
-
);
|
|
48
|
-
const remoteRcps = Object.entries(INTEGRATIONS).filter(
|
|
49
|
-
([, i]) => i.type === 'remote-mcp'
|
|
50
|
-
);
|
|
51
|
-
const universalInstallers = Object.entries(INTEGRATIONS).filter(
|
|
52
|
-
([, i]) => i.type === 'universal-installer'
|
|
53
|
-
);
|
|
54
|
-
const pythonClis = Object.entries(INTEGRATIONS).filter(
|
|
55
|
-
([, i]) => i.type === 'python-cli'
|
|
79
|
+
const rows = getIntegrationSummaries({ home: homedir(), env: process.env });
|
|
80
|
+
fmt.note(
|
|
81
|
+
rows.map(row => `${row.name.padEnd(14)} ${row.state.padEnd(11)} ${row.summary}`).join('\n'),
|
|
82
|
+
'Available integrations',
|
|
56
83
|
);
|
|
57
84
|
|
|
58
|
-
const typeIcon = (type) =>
|
|
59
|
-
type === 'plugin' ? '🔌' : type === 'remote-mcp' ? '🌐' : type === 'universal-installer' ? '🪨' : '⚙️';
|
|
60
|
-
|
|
61
|
-
// Installed section
|
|
62
|
-
if (installed.length > 0) {
|
|
63
|
-
fmt.logMessage(`\n${chalk.bold.underline('Installed')}`);
|
|
64
|
-
for (const key of installed) {
|
|
65
|
-
const integration = INTEGRATIONS[key];
|
|
66
|
-
if (!integration) continue;
|
|
67
|
-
fmt.logSuccess(` ${typeIcon(integration.type)} ${integration.label}`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Available Plugins
|
|
72
|
-
fmt.logMessage(`\n${chalk.bold.underline('Available Plugins')}`);
|
|
73
|
-
for (const [key, integration] of plugins) {
|
|
74
|
-
if (!installed.includes(key)) {
|
|
75
|
-
fmt.logMessage(
|
|
76
|
-
` 🔌 ${integration.label.padEnd(25)} — ${integration.description}`
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Available Remote MCPs
|
|
82
|
-
fmt.logMessage(`\n${chalk.bold.underline('Available Remote MCPs')}`);
|
|
83
|
-
for (const [key, integration] of remoteRcps) {
|
|
84
|
-
if (!installed.includes(key)) {
|
|
85
|
-
fmt.logMessage(
|
|
86
|
-
` 🌐 ${integration.label.padEnd(25)} — ${integration.description}`
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Universal Installers
|
|
92
|
-
if (universalInstallers.length > 0) {
|
|
93
|
-
fmt.logMessage(`\n${chalk.bold.underline('Universal Tools')}`);
|
|
94
|
-
for (const [key, integration] of universalInstallers) {
|
|
95
|
-
if (!installed.includes(key)) {
|
|
96
|
-
fmt.logMessage(
|
|
97
|
-
` 🪨 ${integration.label.padEnd(25)} — ${integration.description}`
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Python CLIs
|
|
104
|
-
if (pythonClis.length > 0) {
|
|
105
|
-
fmt.logMessage(`\n${chalk.bold.underline('Available Python Tools')}`);
|
|
106
|
-
for (const [key, integration] of pythonClis) {
|
|
107
|
-
if (!installed.includes(key)) {
|
|
108
|
-
fmt.logMessage(
|
|
109
|
-
` 🐍 ${integration.label.padEnd(25)} — ${integration.description}`
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Bundles
|
|
116
|
-
fmt.logMessage(`\n${chalk.bold.underline('Bundles')}`);
|
|
117
|
-
for (const [bundleKey, bundle] of Object.entries(BUNDLES)) {
|
|
118
|
-
fmt.logMessage(
|
|
119
|
-
` 📦 ${bundle.label.padEnd(25)} — ${bundle.description}`
|
|
120
|
-
);
|
|
121
|
-
fmt.logMessage(
|
|
122
|
-
` Includes: ${bundle.includes.map((k) => INTEGRATIONS[k].label).join(', ')}`
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
85
|
fmt.logMessage(`\n${chalk.dim('Commands:')}`);
|
|
127
86
|
fmt.logMessage(` aw integrations add <key> Install a specific tool`);
|
|
87
|
+
fmt.logMessage(` aw integrations add <key> --dry-run`);
|
|
88
|
+
fmt.logMessage(` aw integrations add context-mode --strict`);
|
|
128
89
|
fmt.logMessage(` aw integrations remove <key> Remove a tool`);
|
|
129
90
|
fmt.logMessage(` aw integrations bundle <name> Install a preset bundle`);
|
|
130
91
|
}
|
|
@@ -142,12 +103,11 @@ async function cmdAdd(args) {
|
|
|
142
103
|
|
|
143
104
|
if (!INTEGRATIONS[key]) {
|
|
144
105
|
// Suggest similar keys
|
|
145
|
-
const available = Object.keys(INTEGRATIONS);
|
|
146
106
|
fmt.cancel(
|
|
147
107
|
[
|
|
148
108
|
`Unknown integration: ${chalk.red(key)}`,
|
|
149
109
|
'',
|
|
150
|
-
`Available: ${
|
|
110
|
+
`Available: ${availableKeys()}`,
|
|
151
111
|
].join('\n')
|
|
152
112
|
);
|
|
153
113
|
}
|
|
@@ -155,13 +115,33 @@ async function cmdAdd(args) {
|
|
|
155
115
|
const integration = INTEGRATIONS[key];
|
|
156
116
|
fmt.intro(`Installing ${integration.label}`);
|
|
157
117
|
|
|
158
|
-
const
|
|
118
|
+
const result = await installIntegration(key, {
|
|
119
|
+
home: homedir(),
|
|
120
|
+
env: process.env,
|
|
121
|
+
dryRun: args['--dry-run'] === true,
|
|
122
|
+
silent: false,
|
|
123
|
+
});
|
|
159
124
|
|
|
160
|
-
if (
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
125
|
+
if (integration.type === 'context-mode') {
|
|
126
|
+
printContextModeAddResult(result);
|
|
127
|
+
if (!result.success) {
|
|
128
|
+
const message = result.error === 'partial-config'
|
|
129
|
+
? `${integration.label} was only partially configured`
|
|
130
|
+
: `${integration.label} was not configured`;
|
|
131
|
+
if (args['--strict'] === true) {
|
|
132
|
+
fmt.cancel(message);
|
|
133
|
+
}
|
|
134
|
+
fmt.outro(message);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
fmt.outro(args['--dry-run'] === true
|
|
138
|
+
? `${integration.label} install/config dry run complete`
|
|
139
|
+
: `${integration.label} configured`);
|
|
140
|
+
return;
|
|
164
141
|
}
|
|
142
|
+
|
|
143
|
+
if (!integrationSucceeded(result)) fmt.cancel(`Failed to install ${integration.label}`);
|
|
144
|
+
fmt.outro(`✓ ${integration.label} installed successfully`);
|
|
165
145
|
}
|
|
166
146
|
|
|
167
147
|
// ────────────────────────────────────────────────────────────────────────────────
|
|
@@ -176,12 +156,11 @@ async function cmdRemove(args) {
|
|
|
176
156
|
}
|
|
177
157
|
|
|
178
158
|
if (!INTEGRATIONS[key]) {
|
|
179
|
-
const available = Object.keys(INTEGRATIONS);
|
|
180
159
|
fmt.cancel(
|
|
181
160
|
[
|
|
182
161
|
`Unknown integration: ${chalk.red(key)}`,
|
|
183
162
|
'',
|
|
184
|
-
`Available: ${
|
|
163
|
+
`Available: ${availableKeys()}`,
|
|
185
164
|
].join('\n')
|
|
186
165
|
);
|
|
187
166
|
}
|
|
@@ -189,13 +168,18 @@ async function cmdRemove(args) {
|
|
|
189
168
|
const integration = INTEGRATIONS[key];
|
|
190
169
|
fmt.intro(`Removing ${integration.label}`);
|
|
191
170
|
|
|
192
|
-
const
|
|
171
|
+
const result = await removeIntegration(key, {
|
|
172
|
+
home: homedir(),
|
|
173
|
+
dryRun: args['--dry-run'] === true,
|
|
174
|
+
silent: false,
|
|
175
|
+
});
|
|
193
176
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
177
|
+
printWarnings(result.warnings);
|
|
178
|
+
printChangedFiles(result.changedFiles);
|
|
179
|
+
if (!integrationSucceeded(result)) fmt.cancel(`Failed to remove ${integration.label}`);
|
|
180
|
+
fmt.outro(args['--dry-run'] === true
|
|
181
|
+
? `${integration.label} removal dry run complete`
|
|
182
|
+
: `✓ ${integration.label} removed successfully`);
|
|
199
183
|
}
|
|
200
184
|
|
|
201
185
|
// ────────────────────────────────────────────────────────────────────────────────
|
|
@@ -244,8 +228,8 @@ async function cmdBundle(args) {
|
|
|
244
228
|
// Install all
|
|
245
229
|
let successCount = 0;
|
|
246
230
|
for (const key of bundle.includes) {
|
|
247
|
-
const
|
|
248
|
-
if (
|
|
231
|
+
const result = await installIntegration(key, { silent: false });
|
|
232
|
+
if (integrationSucceeded(result)) successCount++;
|
|
249
233
|
}
|
|
250
234
|
|
|
251
235
|
fmt.outro(
|