@ghl-ai/aw 0.1.57 → 0.1.58-beta.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.
- package/cli.mjs +5 -2
- package/commands/init.mjs +13 -56
- package/commands/integration.mjs +77 -38
- package/commands/integrations.mjs +100 -84
- package/ecc.mjs +3 -14
- package/git.mjs +0 -2
- package/integrations/context-mode.mjs +89 -312
- package/integrations/index.mjs +31 -0
- package/integrations.mjs +40 -153
- package/link.mjs +69 -3
- package/package.json +3 -4
- package/update.mjs +14 -29
- package/package-manager.mjs +0 -72
package/cli.mjs
CHANGED
|
@@ -168,10 +168,13 @@ 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,
|
|
172
|
-
cmd('aw integrations add <key>', 'Install a specific tool (e.g. codex, caveman
|
|
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)'),
|
|
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'),
|
|
175
178
|
cmd('aw drop <path>', 'Stop syncing or delete local content'),
|
|
176
179
|
cmd('aw nuke', 'Remove entire .aw_registry/ & start fresh'),
|
|
177
180
|
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
|
|
21
|
+
import { fileURLToPath } 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 { isContextModeRequested } from '../integrations/context-mode.mjs';
|
|
29
|
+
import { ensureContextModeIntegration, 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 { promptAndInstall, autoInstallIntegrations } from '../integrations.mjs';
|
|
39
39
|
import {
|
|
40
40
|
initPersistentClone,
|
|
41
41
|
isValidClone,
|
|
@@ -65,41 +65,6 @@ 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
|
-
|
|
103
68
|
function syncRulesTargets(targetDir) {
|
|
104
69
|
const rulesSrc = join(AW_HOME, RULES_SOURCE_DIR);
|
|
105
70
|
if (!existsSync(rulesSrc)) return false;
|
|
@@ -129,24 +94,23 @@ function syncHomeAndProjectInstructions(cwd, namespace) {
|
|
|
129
94
|
}
|
|
130
95
|
}
|
|
131
96
|
|
|
132
|
-
|
|
97
|
+
function maybeConfigureContextMode(args, { silent = false } = {}) {
|
|
133
98
|
if (!isContextModeRequested(args, process.env)) {
|
|
134
99
|
return null;
|
|
135
100
|
}
|
|
136
101
|
|
|
137
|
-
const result =
|
|
138
|
-
home: HOME,
|
|
102
|
+
const result = ensureContextModeIntegration(HOME, {
|
|
139
103
|
env: process.env,
|
|
140
104
|
silent,
|
|
141
105
|
});
|
|
142
106
|
|
|
143
107
|
if (!silent) {
|
|
144
|
-
for (const warning of result.warnings
|
|
108
|
+
for (const warning of result.warnings) {
|
|
145
109
|
fmt.logWarn(warning);
|
|
146
110
|
}
|
|
147
|
-
if (
|
|
111
|
+
if (result.changedFiles.length > 0) {
|
|
148
112
|
fmt.logStep(`Context Mode configured (${result.changedFiles.length} file${result.changedFiles.length === 1 ? '' : 's'})`);
|
|
149
|
-
} else if (
|
|
113
|
+
} else if (result.warnings.length > 0) {
|
|
150
114
|
fmt.logWarn('Context Mode was requested but not configured');
|
|
151
115
|
} else {
|
|
152
116
|
fmt.logStep('Context Mode already configured');
|
|
@@ -440,7 +404,7 @@ export async function initCommand(args) {
|
|
|
440
404
|
ensureAwRuntimeHook(HOME);
|
|
441
405
|
syncHomeAndProjectInstructions(cwd, freshCfg?.namespace || team);
|
|
442
406
|
await setupMcp(HOME, freshCfg?.namespace || team, { silent });
|
|
443
|
-
|
|
407
|
+
maybeConfigureContextMode(args, { silent });
|
|
444
408
|
applyStoredStartupPreferences(HOME);
|
|
445
409
|
const removedLegacyStartupFiles = cwd !== HOME ? removeWorkspaceHookDefaults(cwd) : [];
|
|
446
410
|
installGlobalHooks();
|
|
@@ -480,7 +444,7 @@ export async function initCommand(args) {
|
|
|
480
444
|
if (!silent) fmt.logStep(`IDE wired — ${chalk.bold(symlinks)} symlinks · ${chalk.bold(commands)} commands`);
|
|
481
445
|
|
|
482
446
|
// Write hook manifest after all hook installation is complete
|
|
483
|
-
|
|
447
|
+
try { writeHookManifest({ eccVersion: AW_ECC_TAG, awVersion: VERSION }); } catch { /* best effort */ }
|
|
484
448
|
|
|
485
449
|
// Auto-install suggested integrations (Codex, Caveman, Graphify, etc) - unless --no-integrations
|
|
486
450
|
let installedIntegrations = [];
|
|
@@ -490,14 +454,7 @@ export async function initCommand(args) {
|
|
|
490
454
|
|
|
491
455
|
if (silent) {
|
|
492
456
|
if (silentSpinner) { silentSpinner.stop('Done'); setSilent(false); }
|
|
493
|
-
|
|
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
|
-
}
|
|
457
|
+
autoUpdate(await args._updateCheck);
|
|
501
458
|
} else {
|
|
502
459
|
fmt.outro([
|
|
503
460
|
`⟁ ${isNewSubTeam ? `Sub-team ${chalk.cyan(folderName)} added` : 'Up to date'}`,
|
|
@@ -608,7 +565,7 @@ export async function initCommand(args) {
|
|
|
608
565
|
Promise.resolve(syncHomeAndProjectInstructions(cwd, team)),
|
|
609
566
|
setupMcp(HOME, team, { silent }),
|
|
610
567
|
]);
|
|
611
|
-
|
|
568
|
+
maybeConfigureContextMode(args, { silent });
|
|
612
569
|
// applyStoredStartupPreferences reads settings written by ECC — keep after batch B
|
|
613
570
|
applyStoredStartupPreferences(HOME);
|
|
614
571
|
const removedLegacyStartupFiles = cwd !== HOME ? removeWorkspaceHookDefaults(cwd) : [];
|
|
@@ -673,7 +630,7 @@ export async function initCommand(args) {
|
|
|
673
630
|
|
|
674
631
|
// Write hook manifest after all hook installation is complete, including
|
|
675
632
|
// bundled usage hooks, so `aw nuke` can prune AW-managed settings entries.
|
|
676
|
-
|
|
633
|
+
try { writeHookManifest({ eccVersion: AW_ECC_TAG, awVersion: VERSION }); } catch { /* best effort */ }
|
|
677
634
|
|
|
678
635
|
// Auto-install suggested integrations (Codex, Caveman, Graphify, etc) - unless --no-integrations
|
|
679
636
|
let installedIntegrations = [];
|
package/commands/integration.mjs
CHANGED
|
@@ -1,52 +1,43 @@
|
|
|
1
|
-
// commands/integration.mjs — Deprecated compatibility wrapper for `aw integration`.
|
|
2
|
-
|
|
3
1
|
import { homedir } from 'node:os';
|
|
4
2
|
|
|
5
3
|
import * as fmt from '../fmt.mjs';
|
|
6
|
-
import {
|
|
7
|
-
import { integrationsCommand } from './integrations.mjs';
|
|
4
|
+
import { listIntegrations, resolveIntegration } from '../integrations/index.mjs';
|
|
8
5
|
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
fmt.
|
|
12
|
-
}
|
|
13
|
-
if (!INTEGRATIONS[key]) {
|
|
14
|
-
fmt.cancel(`Unknown integration: ${key}`);
|
|
6
|
+
function printWarnings(warnings = []) {
|
|
7
|
+
for (const warning of warnings) {
|
|
8
|
+
fmt.logWarn(warning);
|
|
15
9
|
}
|
|
16
10
|
}
|
|
17
11
|
|
|
18
|
-
function
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
function printChangedFiles(changedFiles = []) {
|
|
13
|
+
if (changedFiles.length === 0) return;
|
|
14
|
+
fmt.note(changedFiles.map(file => file.replace(homedir(), '~')).join('\n'), 'Changed files');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function requireIntegration(name) {
|
|
18
|
+
if (!name) {
|
|
19
|
+
fmt.cancel('Missing integration name. Known integrations: context-mode');
|
|
22
20
|
}
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
);
|
|
22
|
+
try {
|
|
23
|
+
return resolveIntegration(name);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
fmt.cancel(err.message);
|
|
26
|
+
}
|
|
35
27
|
}
|
|
36
28
|
|
|
37
29
|
export async function integrationCommand(args) {
|
|
38
|
-
const [subcommand = 'list',
|
|
30
|
+
const [subcommand = 'list', name] = args._positional;
|
|
39
31
|
const home = homedir();
|
|
40
32
|
|
|
41
33
|
if (args['--help']) {
|
|
42
34
|
fmt.intro('aw integration');
|
|
43
|
-
fmt.logWarn('`aw integration` is deprecated. Use `aw integrations` for new automation.');
|
|
44
35
|
fmt.note(
|
|
45
36
|
[
|
|
46
37
|
'aw integration list',
|
|
47
|
-
'aw integration add
|
|
48
|
-
'aw integration status
|
|
49
|
-
'aw integration remove
|
|
38
|
+
'aw integration add context-mode [--dry-run]',
|
|
39
|
+
'aw integration status context-mode',
|
|
40
|
+
'aw integration remove context-mode [--dry-run]',
|
|
50
41
|
].join('\n'),
|
|
51
42
|
'Usage',
|
|
52
43
|
);
|
|
@@ -54,18 +45,66 @@ export async function integrationCommand(args) {
|
|
|
54
45
|
return;
|
|
55
46
|
}
|
|
56
47
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
48
|
+
if (subcommand === 'list') {
|
|
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
|
+
);
|
|
63
55
|
fmt.outro('Done');
|
|
64
56
|
return;
|
|
65
57
|
}
|
|
66
58
|
|
|
67
|
-
|
|
68
|
-
|
|
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
|
+
}
|
|
91
|
+
|
|
92
|
+
if (subcommand === 'status') {
|
|
93
|
+
fmt.intro(`aw integration status ${integration.name}`);
|
|
94
|
+
const status = integration.status(home, { env: process.env });
|
|
95
|
+
fmt.note(
|
|
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
|
+
);
|
|
106
|
+
fmt.outro('Done');
|
|
107
|
+
return;
|
|
69
108
|
}
|
|
70
109
|
|
|
71
110
|
fmt.cancel(`Unknown integration subcommand: ${subcommand}. Use add / remove / status / list`);
|
|
@@ -1,53 +1,16 @@
|
|
|
1
1
|
// commands/integrations.mjs — CLI command: aw integrations add/remove/list/bundle
|
|
2
2
|
|
|
3
|
-
import { homedir } from 'node:os';
|
|
4
|
-
|
|
5
3
|
import * as p from '@clack/prompts';
|
|
6
4
|
import * as fmt from '../fmt.mjs';
|
|
7
5
|
import { chalk } from '../fmt.mjs';
|
|
8
6
|
import {
|
|
9
7
|
INTEGRATIONS,
|
|
10
8
|
BUNDLES,
|
|
11
|
-
getIntegrationSummaries,
|
|
12
9
|
installIntegration,
|
|
13
|
-
integrationSucceeded,
|
|
14
10
|
removeIntegration,
|
|
11
|
+
getInstalledList,
|
|
15
12
|
} from '../integrations.mjs';
|
|
16
13
|
|
|
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
|
-
|
|
51
14
|
export async function integrationsCommand(args) {
|
|
52
15
|
const subcommand = args._positional[0];
|
|
53
16
|
|
|
@@ -76,16 +39,92 @@ async function cmdList() {
|
|
|
76
39
|
subtitle: ' Available tools and MCP servers',
|
|
77
40
|
});
|
|
78
41
|
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
42
|
+
const installed = getInstalledList();
|
|
43
|
+
|
|
44
|
+
// Group by type
|
|
45
|
+
const plugins = Object.entries(INTEGRATIONS).filter(
|
|
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'
|
|
83
56
|
);
|
|
84
57
|
|
|
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
|
+
|
|
85
126
|
fmt.logMessage(`\n${chalk.dim('Commands:')}`);
|
|
86
127
|
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`);
|
|
89
128
|
fmt.logMessage(` aw integrations remove <key> Remove a tool`);
|
|
90
129
|
fmt.logMessage(` aw integrations bundle <name> Install a preset bundle`);
|
|
91
130
|
}
|
|
@@ -103,11 +142,12 @@ async function cmdAdd(args) {
|
|
|
103
142
|
|
|
104
143
|
if (!INTEGRATIONS[key]) {
|
|
105
144
|
// Suggest similar keys
|
|
145
|
+
const available = Object.keys(INTEGRATIONS);
|
|
106
146
|
fmt.cancel(
|
|
107
147
|
[
|
|
108
148
|
`Unknown integration: ${chalk.red(key)}`,
|
|
109
149
|
'',
|
|
110
|
-
`Available: ${
|
|
150
|
+
`Available: ${available.join(', ')}`,
|
|
111
151
|
].join('\n')
|
|
112
152
|
);
|
|
113
153
|
}
|
|
@@ -115,33 +155,13 @@ async function cmdAdd(args) {
|
|
|
115
155
|
const integration = INTEGRATIONS[key];
|
|
116
156
|
fmt.intro(`Installing ${integration.label}`);
|
|
117
157
|
|
|
118
|
-
const
|
|
119
|
-
home: homedir(),
|
|
120
|
-
env: process.env,
|
|
121
|
-
dryRun: args['--dry-run'] === true,
|
|
122
|
-
silent: false,
|
|
123
|
-
});
|
|
158
|
+
const success = await installIntegration(key, { silent: false });
|
|
124
159
|
|
|
125
|
-
if (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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;
|
|
160
|
+
if (success) {
|
|
161
|
+
fmt.outro(`✓ ${integration.label} installed successfully`);
|
|
162
|
+
} else {
|
|
163
|
+
fmt.cancel(`Failed to install ${integration.label}`);
|
|
141
164
|
}
|
|
142
|
-
|
|
143
|
-
if (!integrationSucceeded(result)) fmt.cancel(`Failed to install ${integration.label}`);
|
|
144
|
-
fmt.outro(`✓ ${integration.label} installed successfully`);
|
|
145
165
|
}
|
|
146
166
|
|
|
147
167
|
// ────────────────────────────────────────────────────────────────────────────────
|
|
@@ -156,11 +176,12 @@ async function cmdRemove(args) {
|
|
|
156
176
|
}
|
|
157
177
|
|
|
158
178
|
if (!INTEGRATIONS[key]) {
|
|
179
|
+
const available = Object.keys(INTEGRATIONS);
|
|
159
180
|
fmt.cancel(
|
|
160
181
|
[
|
|
161
182
|
`Unknown integration: ${chalk.red(key)}`,
|
|
162
183
|
'',
|
|
163
|
-
`Available: ${
|
|
184
|
+
`Available: ${available.join(', ')}`,
|
|
164
185
|
].join('\n')
|
|
165
186
|
);
|
|
166
187
|
}
|
|
@@ -168,18 +189,13 @@ async function cmdRemove(args) {
|
|
|
168
189
|
const integration = INTEGRATIONS[key];
|
|
169
190
|
fmt.intro(`Removing ${integration.label}`);
|
|
170
191
|
|
|
171
|
-
const
|
|
172
|
-
home: homedir(),
|
|
173
|
-
dryRun: args['--dry-run'] === true,
|
|
174
|
-
silent: false,
|
|
175
|
-
});
|
|
192
|
+
const success = await removeIntegration(key, { silent: false });
|
|
176
193
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
: `✓ ${integration.label} removed successfully`);
|
|
194
|
+
if (success) {
|
|
195
|
+
fmt.outro(`✓ ${integration.label} removed successfully`);
|
|
196
|
+
} else {
|
|
197
|
+
fmt.cancel(`Failed to remove ${integration.label}`);
|
|
198
|
+
}
|
|
183
199
|
}
|
|
184
200
|
|
|
185
201
|
// ────────────────────────────────────────────────────────────────────────────────
|
|
@@ -228,8 +244,8 @@ async function cmdBundle(args) {
|
|
|
228
244
|
// Install all
|
|
229
245
|
let successCount = 0;
|
|
230
246
|
for (const key of bundle.includes) {
|
|
231
|
-
const
|
|
232
|
-
if (
|
|
247
|
+
const success = await installIntegration(key, { silent: false });
|
|
248
|
+
if (success) successCount++;
|
|
233
249
|
}
|
|
234
250
|
|
|
235
251
|
fmt.outro(
|
package/ecc.mjs
CHANGED
|
@@ -12,12 +12,7 @@ import { applyStoredStartupPreferences } from "./startup.mjs";
|
|
|
12
12
|
|
|
13
13
|
const AW_ECC_REPO_SSH = "git@github.com:shreyansh-ghl/aw-ecc.git";
|
|
14
14
|
const AW_ECC_REPO_HTTPS = "https://github.com/shreyansh-ghl/aw-ecc.git";
|
|
15
|
-
export const AW_ECC_TAG = "v1.4.
|
|
16
|
-
const REQUIRED_ECC_FILES = [
|
|
17
|
-
"package.json",
|
|
18
|
-
"scripts/install-apply.js",
|
|
19
|
-
"scripts/sync-ecc-to-codex.sh",
|
|
20
|
-
];
|
|
15
|
+
export const AW_ECC_TAG = "v1.4.62";
|
|
21
16
|
|
|
22
17
|
const MARKETPLACE_NAME = "aw-marketplace";
|
|
23
18
|
const PLUGIN_KEY = `aw@${MARKETPLACE_NAME}`;
|
|
@@ -90,15 +85,11 @@ async function cloneWithRefAsync(url, ref, dest) {
|
|
|
90
85
|
}
|
|
91
86
|
}
|
|
92
87
|
|
|
93
|
-
function hasRequiredEccFiles(dest) {
|
|
94
|
-
return REQUIRED_ECC_FILES.every((relPath) => existsSync(join(dest, relPath)));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
88
|
async function cloneOrUpdateAsync(tag, dest) {
|
|
98
89
|
const overrideUrl = process.env.AW_ECC_CLONE_URL;
|
|
99
90
|
const overrideRef = process.env.AW_ECC_CLONE_REF?.trim();
|
|
100
91
|
|
|
101
|
-
if (existsSync(join(dest, ".git"))
|
|
92
|
+
if (existsSync(join(dest, ".git"))) {
|
|
102
93
|
try {
|
|
103
94
|
if (!overrideUrl && !overrideRef) {
|
|
104
95
|
await runA(`git -C ${dest} fetch --quiet --depth 1 origin tag ${tag}`);
|
|
@@ -111,7 +102,6 @@ async function cloneOrUpdateAsync(tag, dest) {
|
|
|
111
102
|
}
|
|
112
103
|
// Restore working tree — pruneStaleHooks may have deleted tracked files
|
|
113
104
|
await runA(`git -C ${dest} checkout -- .`);
|
|
114
|
-
if (!hasRequiredEccFiles(dest)) throw new Error("aw-ecc checkout is missing required files");
|
|
115
105
|
return;
|
|
116
106
|
} catch { /* fall through to fresh clone */ }
|
|
117
107
|
}
|
|
@@ -198,7 +188,7 @@ function cloneOrUpdate(tag, dest) {
|
|
|
198
188
|
// AW_ECC_CLONE_REF lets aw init install ECC from a non-default branch or tag.
|
|
199
189
|
const overrideRef = process.env.AW_ECC_CLONE_REF?.trim();
|
|
200
190
|
|
|
201
|
-
if (existsSync(join(dest, ".git"))
|
|
191
|
+
if (existsSync(join(dest, ".git"))) {
|
|
202
192
|
try {
|
|
203
193
|
if (!overrideUrl && !overrideRef) {
|
|
204
194
|
run(`git -C ${dest} fetch --quiet --depth 1 origin tag ${tag}`);
|
|
@@ -211,7 +201,6 @@ function cloneOrUpdate(tag, dest) {
|
|
|
211
201
|
}
|
|
212
202
|
// Restore working tree — pruneStaleHooks may have deleted tracked files
|
|
213
203
|
run(`git -C ${dest} checkout -- .`);
|
|
214
|
-
if (!hasRequiredEccFiles(dest)) throw new Error("aw-ecc checkout is missing required files");
|
|
215
204
|
return;
|
|
216
205
|
} catch { /* fall through to fresh clone */ }
|
|
217
206
|
}
|
package/git.mjs
CHANGED
|
@@ -453,7 +453,6 @@ export function detectChanges(awHome, registryDir) {
|
|
|
453
453
|
if (!line || line.length < 3) continue;
|
|
454
454
|
const xy = line.slice(0, 2);
|
|
455
455
|
const filePath = line.slice(3).trim();
|
|
456
|
-
if (filePath === `${registryDir}/.aw-upgrade.log`) continue;
|
|
457
456
|
const registryPath = filePath.startsWith(registryDir + '/') ? filePath.slice(registryDir.length + 1) : filePath;
|
|
458
457
|
const entry = { path: filePath, registryPath };
|
|
459
458
|
|
|
@@ -486,7 +485,6 @@ export function getStagedFiles(awHome, registryDir) {
|
|
|
486
485
|
if (!line) continue;
|
|
487
486
|
const [status, filePath] = line.split('\t');
|
|
488
487
|
if (!filePath?.startsWith(registryDir + '/')) continue;
|
|
489
|
-
if (filePath === `${registryDir}/.aw-upgrade.log`) continue;
|
|
490
488
|
result.push({
|
|
491
489
|
path: filePath,
|
|
492
490
|
registryPath: filePath.slice(registryDir.length + 1),
|