@ghl-ai/aw 0.1.50-beta.0 → 0.1.50
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/c4/templates/scripts/aw-c4-bootstrap.sh +4 -5
- package/cli.mjs +9 -21
- package/commands/c4.mjs +9 -17
- package/commands/doctor.mjs +5 -2
- package/commands/init.mjs +54 -117
- package/commands/integration.mjs +111 -0
- package/commands/nuke.mjs +3 -1
- package/commands/pull.mjs +2 -3
- package/commands/push.mjs +29 -715
- package/commands/startup.mjs +3 -22
- package/constants.mjs +0 -23
- package/ecc.mjs +1 -1
- package/git.mjs +4 -6
- package/integrate.mjs +21 -94
- package/integrations/context-mode.mjs +514 -0
- package/integrations/index.mjs +31 -0
- package/link.mjs +3 -119
- package/mcp.mjs +16 -132
- package/package.json +4 -4
- package/render-rules.mjs +1 -25
- package/startup.mjs +8 -52
- package/commands/integrations.mjs +0 -254
- package/commands/mcp.mjs +0 -90
- package/integrations.mjs +0 -971
|
@@ -12,13 +12,12 @@
|
|
|
12
12
|
# - .codex/scripts/codex-web-bootstrap.sh (passes --harness codex-web)
|
|
13
13
|
#
|
|
14
14
|
# Override knobs (env):
|
|
15
|
-
# AW_PACKAGE npm spec to install. Defaults to @ghl-ai/aw@latest.
|
|
16
|
-
#
|
|
17
|
-
# CI runs.
|
|
15
|
+
# AW_PACKAGE npm spec to install. Defaults to @ghl-ai/aw@latest. Override
|
|
16
|
+
# with @ghl-ai/aw@beta to opt into pre-release builds, or pin to
|
|
17
|
+
# @ghl-ai/aw@0.1.x for reproducible CI runs.
|
|
18
18
|
set -Eeuo pipefail
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
# no-token diagnostics stay centralized in the CLI preflight.
|
|
20
|
+
: "${GITHUB_PAT:?ERROR: Set GITHUB_PAT in your harness secrets UI before running aw c4}"
|
|
22
21
|
|
|
23
22
|
# Ensure npm is on PATH. Cursor Cloud's install shell is non-interactive — nvm
|
|
24
23
|
# is not auto-sourced, and Node may not be pre-installed at all. Walk common
|
package/cli.mjs
CHANGED
|
@@ -21,13 +21,13 @@ const COMMANDS = {
|
|
|
21
21
|
doctor: () => import('./commands/doctor.mjs').then(m => m.doctorCommand),
|
|
22
22
|
routing: () => import('./commands/startup.mjs').then(m => m.routingCommand),
|
|
23
23
|
startup: () => import('./commands/startup.mjs').then(m => m.startupCommand),
|
|
24
|
-
mcp: () => import('./commands/mcp.mjs').then(m => m.mcpCommand),
|
|
25
24
|
search: () => import('./commands/search.mjs').then(m => m.searchCommand),
|
|
26
25
|
link: () => import('./commands/link-project.mjs').then(m => m.linkProjectCommand),
|
|
27
26
|
nuke: () => import('./commands/nuke.mjs').then(m => m.nukeCommand),
|
|
28
27
|
daemon: () => import('./commands/daemon.mjs').then(m => m.daemonCommand),
|
|
28
|
+
integration: () => import('./commands/integration.mjs').then(m => m.integrationCommand),
|
|
29
|
+
integrations: () => import('./commands/integration.mjs').then(m => m.integrationCommand),
|
|
29
30
|
telemetry: () => import('./commands/telemetry.mjs').then(m => m.telemetryCommand),
|
|
30
|
-
integrations: () => import('./commands/integrations.mjs').then(m => m.integrationsCommand),
|
|
31
31
|
'slack-sim': () => import('./commands/slack-sim.mjs').then(m => m.slackSimCommand),
|
|
32
32
|
c4: () => import('./commands/c4.mjs').then(m => m.c4Command),
|
|
33
33
|
'init-repo': () => import('./commands/init-repo.mjs').then(m => m.initRepoCommand),
|
|
@@ -50,9 +50,6 @@ function parseArgs(argv) {
|
|
|
50
50
|
if (arg === '--dry-run') {
|
|
51
51
|
args['--dry-run'] = true;
|
|
52
52
|
i++;
|
|
53
|
-
} else if (arg === '--aw-docs-only' || arg === '--docs-only') {
|
|
54
|
-
args[arg] = true;
|
|
55
|
-
i++;
|
|
56
53
|
} else if (arg === '-v' || arg === '--verbose') {
|
|
57
54
|
args['-v'] = true;
|
|
58
55
|
i++;
|
|
@@ -87,13 +84,10 @@ function printHelp() {
|
|
|
87
84
|
const sec = (title) => `\n ${chalk.bold.underline(title)}`;
|
|
88
85
|
const help = [
|
|
89
86
|
sec('Setup'),
|
|
90
|
-
cmd('aw init', 'Initialize workspace (
|
|
87
|
+
cmd('aw init', 'Initialize workspace (platform/ only)'),
|
|
91
88
|
cmd('aw init --namespace <team/sub-team>', 'Add a team namespace (optional)'),
|
|
92
|
-
cmd('aw init skills <path...>', 'Initialize/link only specific skill folders'),
|
|
93
|
-
cmd('aw init --no-integrations', 'Skip integration setup (Codex, Caveman, Graphify, etc)'),
|
|
94
89
|
` ${chalk.dim('Teams: platform, revex, mobile, commerce, leadgen, crm, marketplace, ai')}`,
|
|
95
90
|
` ${chalk.dim('Example: aw init --namespace revex/courses')}`,
|
|
96
|
-
` ${chalk.dim('Example: aw init skills platform/core/skills/pr-review')}`,
|
|
97
91
|
cmd('aw init-repo', 'Scaffold cloud-bootstrap files (idempotent, --dry-run/--force/--diff)'),
|
|
98
92
|
|
|
99
93
|
sec('Download'),
|
|
@@ -103,8 +97,6 @@ function printHelp() {
|
|
|
103
97
|
|
|
104
98
|
sec('Upload'),
|
|
105
99
|
cmd('aw push', 'Push all modified files (creates one PR)'),
|
|
106
|
-
cmd('aw push --aw-docs-only', 'Publish generated .aw_docs companions and print share links'),
|
|
107
|
-
cmd('aw push --aw-docs-only --feature <slug>', 'Publish one .aw_docs feature folder and print share links'),
|
|
108
100
|
cmd('aw push <path>', 'Push file, folder, or namespace to registry'),
|
|
109
101
|
cmd('aw push-rules [path]', 'Push platform rules to platform-docs'),
|
|
110
102
|
cmd('aw push --dry-run [path]', 'Preview what would be pushed'),
|
|
@@ -117,15 +109,12 @@ function printHelp() {
|
|
|
117
109
|
cmd('aw doctor', 'Run a health check for routing, MCP, plugin, and AW ECC surfaces'),
|
|
118
110
|
cmd('aw link', 'Link current project as a git worktree (wires IDE symlinks)'),
|
|
119
111
|
cmd('aw routing status', 'Show global AW session-routing mode for Claude/Cursor/Codex'),
|
|
120
|
-
cmd('aw routing disable', 'Disable
|
|
121
|
-
cmd('aw routing enable', 'Re-enable
|
|
122
|
-
cmd('aw
|
|
123
|
-
cmd('aw
|
|
124
|
-
cmd('aw
|
|
125
|
-
cmd('aw
|
|
126
|
-
cmd('aw integrations add <key>', 'Install a specific tool (e.g. codex, caveman)'),
|
|
127
|
-
cmd('aw integrations remove <key>', 'Remove a tool'),
|
|
128
|
-
cmd('aw integrations bundle <name>', 'Install a preset bundle'),
|
|
112
|
+
cmd('aw routing disable', 'Disable automatic AW session routing globally'),
|
|
113
|
+
cmd('aw routing enable', 'Re-enable automatic AW session routing globally'),
|
|
114
|
+
cmd('aw integration list', 'List addable third-party integrations'),
|
|
115
|
+
cmd('aw integration add <name>', 'Add a specific integration, e.g. context-mode'),
|
|
116
|
+
cmd('aw integration status <name>', 'Show integration health and configured surfaces'),
|
|
117
|
+
cmd('aw integration remove <name>', 'Remove a specific integration'),
|
|
129
118
|
cmd('aw drop <path>', 'Stop syncing or delete local content'),
|
|
130
119
|
cmd('aw nuke', 'Remove entire .aw_registry/ & start fresh'),
|
|
131
120
|
cmd('aw daemon install', 'Auto-pull on a schedule (macOS launchd / Linux cron)'),
|
|
@@ -154,7 +143,6 @@ function printHelp() {
|
|
|
154
143
|
cmd('aw pull <team>/agents', 'All agents from a team'),
|
|
155
144
|
cmd('aw pull <team>/agents/<name>', 'One specific agent'),
|
|
156
145
|
cmd('aw pull <team>/skills/<name>', 'One specific skill folder'),
|
|
157
|
-
cmd('aw init skills <team>/<path>/skills/<name>', 'Install and link only selected skill symlinks'),
|
|
158
146
|
'',
|
|
159
147
|
` ${chalk.dim('# Push your local changes to registry')}`,
|
|
160
148
|
cmd('aw push', 'Push all modified files (one PR)'),
|
package/commands/c4.mjs
CHANGED
|
@@ -24,7 +24,6 @@ import { spawnSync as nodeSpawnSync } from 'node:child_process';
|
|
|
24
24
|
import { existsSync as fsExistsSync } from 'node:fs';
|
|
25
25
|
import { join } from 'node:path';
|
|
26
26
|
import * as c4Default from '../c4/index.mjs';
|
|
27
|
-
import { isMcpEnabled } from '../mcp.mjs';
|
|
28
27
|
|
|
29
28
|
/* ─────────────────────────────────────────────────────────────────────────
|
|
30
29
|
* Constants — referenced by self-tests.
|
|
@@ -350,10 +349,10 @@ export async function c4Command(rawArgs, overrides = {}) {
|
|
|
350
349
|
return exit(0);
|
|
351
350
|
}
|
|
352
351
|
|
|
353
|
-
// Step 6 — npm install -g @ghl-ai/aw
|
|
354
|
-
const npmRes = spawnSync('npm', ['install', '-g', '@ghl-ai/aw
|
|
352
|
+
// Step 6 — npm install -g @ghl-ai/aw.
|
|
353
|
+
const npmRes = spawnSync('npm', ['install', '-g', '@ghl-ai/aw'], { stdio: 'pipe' });
|
|
355
354
|
if (npmRes && npmRes.status !== 0) {
|
|
356
|
-
writer.stderr('[aw-c4] npm install -g @ghl-ai/aw
|
|
355
|
+
writer.stderr('[aw-c4] npm install -g @ghl-ai/aw failed (non-fatal); using existing aw if present\n');
|
|
357
356
|
}
|
|
358
357
|
|
|
359
358
|
// Step 7 — aw init --silent.
|
|
@@ -381,12 +380,7 @@ export async function c4Command(rawArgs, overrides = {}) {
|
|
|
381
380
|
}
|
|
382
381
|
|
|
383
382
|
// Step 11 — MCP register.
|
|
384
|
-
|
|
385
|
-
if (mcpEnabled) {
|
|
386
|
-
safe('registerGhlAiMcp', () => c4.registerGhlAiMcp(harness, home, token), writer);
|
|
387
|
-
} else {
|
|
388
|
-
writer.stdout('[aw-c4] MCP disabled; skipping registerGhlAiMcp\n');
|
|
389
|
-
}
|
|
383
|
+
safe('registerGhlAiMcp', () => c4.registerGhlAiMcp(harness, home, token), writer);
|
|
390
384
|
|
|
391
385
|
// Step 12 — slash command surface.
|
|
392
386
|
safe('ensureCommandSurface', () => c4.ensureCommandSurface({ harness, home, eccHome }), writer);
|
|
@@ -409,13 +403,11 @@ export async function c4Command(rawArgs, overrides = {}) {
|
|
|
409
403
|
safe('ensureRepoLocalIgnore', () => c4.ensureRepoLocalIgnore({ cwd, harness }), writer);
|
|
410
404
|
|
|
411
405
|
// Step 15 — MCP smoke probe (best-effort).
|
|
412
|
-
const mcpProbe =
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
)
|
|
418
|
-
: null;
|
|
406
|
+
const mcpProbe = await safeAsync(
|
|
407
|
+
'probeMcpServer',
|
|
408
|
+
() => c4.probeMcpServer({ url: c4.MCP_URL_DEFAULT, token }),
|
|
409
|
+
writer,
|
|
410
|
+
);
|
|
419
411
|
|
|
420
412
|
// Step 16 — self-tests.
|
|
421
413
|
const self = runSelfTests({ harness, c4, home, awHome, eccHome });
|
package/commands/doctor.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import * as fmt from '../fmt.mjs';
|
|
|
7
7
|
import { chalk } from '../fmt.mjs';
|
|
8
8
|
import { AW_ECC_TAG } from '../ecc.mjs';
|
|
9
9
|
import { getSupportedHarnessPhaseEntries } from '../hook-manifest.mjs';
|
|
10
|
+
import { getContextModeDoctorStatus } from '../integrations/context-mode.mjs';
|
|
10
11
|
import { getStartupStatus, hasLegacyRepoStartupDefaults } from '../startup.mjs';
|
|
11
12
|
|
|
12
13
|
const STATUS_PRIORITY = { pass: 0, warn: 1, fail: 2 };
|
|
@@ -209,7 +210,7 @@ function directoryContainsGeneratedRuleFiles(dirPath, extension) {
|
|
|
209
210
|
try {
|
|
210
211
|
return readdirSync(dirPath, { withFileTypes: true })
|
|
211
212
|
.filter(entry => entry.isFile() && entry.name.endsWith(extension))
|
|
212
|
-
.some(entry => readText(join(dirPath, entry.name)).
|
|
213
|
+
.some(entry => readText(join(dirPath, entry.name)).startsWith(GENERATED_RULE_HEADER));
|
|
213
214
|
} catch {
|
|
214
215
|
return false;
|
|
215
216
|
}
|
|
@@ -500,7 +501,7 @@ function listGeneratedRuleFiles(dirPath, extension) {
|
|
|
500
501
|
return readdirSync(dirPath, { withFileTypes: true })
|
|
501
502
|
.filter(entry => entry.isFile() && entry.name.endsWith(extension))
|
|
502
503
|
.map(entry => join(dirPath, entry.name))
|
|
503
|
-
.filter(filePath => readText(filePath).
|
|
504
|
+
.filter(filePath => readText(filePath).startsWith(GENERATED_RULE_HEADER));
|
|
504
505
|
} catch {
|
|
505
506
|
return [];
|
|
506
507
|
}
|
|
@@ -1026,6 +1027,8 @@ function buildDoctorChecks(homeDir, cwd) {
|
|
|
1026
1027
|
),
|
|
1027
1028
|
);
|
|
1028
1029
|
|
|
1030
|
+
checks.push(...getContextModeDoctorStatus(homeDir, { env: process.env }));
|
|
1031
|
+
|
|
1029
1032
|
const eccDir = join(homeDir, '.aw-ecc');
|
|
1030
1033
|
const eccSyncScript = join(eccDir, 'scripts', 'sync-ecc-to-codex.sh');
|
|
1031
1034
|
if (!existsSync(eccDir)) {
|
package/commands/init.mjs
CHANGED
|
@@ -23,9 +23,10 @@ import * as p from '@clack/prompts';
|
|
|
23
23
|
import * as config from '../config.mjs';
|
|
24
24
|
import * as fmt from '../fmt.mjs';
|
|
25
25
|
import { chalk, setSilent } from '../fmt.mjs';
|
|
26
|
-
import { linkWorkspace
|
|
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 { ensureContextModeIntegration, isContextModeRequested } from '../integrations/context-mode.mjs';
|
|
29
30
|
import { applyStoredStartupPreferences, ensureAwRuntimeHook } from '../startup.mjs';
|
|
30
31
|
import { installLocalCommitHook } from '../hooks.mjs';
|
|
31
32
|
import { autoUpdate, promptUpdate } from '../update.mjs';
|
|
@@ -34,7 +35,6 @@ import { loadConfig as ensureTelemetryConfig } from '../telemetry.mjs';
|
|
|
34
35
|
import { installAwEcc, AW_ECC_TAG } from '../ecc.mjs';
|
|
35
36
|
import { removeWorkspaceHookDefaults } from '../codex.mjs';
|
|
36
37
|
import { readHookManifest, pruneStaleHooks, writeHookManifest } from '../hook-cleanup.mjs';
|
|
37
|
-
import { promptAndInstall, autoInstallIntegrations } from '../integrations.mjs';
|
|
38
38
|
import {
|
|
39
39
|
initPersistentClone,
|
|
40
40
|
isValidClone,
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
syncWorktreeSparseCheckout,
|
|
49
49
|
findNearestWorktree,
|
|
50
50
|
} from '../git.mjs';
|
|
51
|
-
import { REGISTRY_DIR, REGISTRY_REPO, REGISTRY_URL,
|
|
51
|
+
import { REGISTRY_DIR, REGISTRY_REPO, REGISTRY_URL, RULES_SOURCE_DIR, RULES_RUNTIME_DIR } from '../constants.mjs';
|
|
52
52
|
import { syncFileTree } from '../file-tree.mjs';
|
|
53
53
|
|
|
54
54
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -92,6 +92,32 @@ function syncHomeAndProjectInstructions(cwd, namespace) {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
function maybeConfigureContextMode(args, { silent = false } = {}) {
|
|
96
|
+
if (!isContextModeRequested(args, process.env)) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const result = ensureContextModeIntegration(HOME, {
|
|
101
|
+
env: process.env,
|
|
102
|
+
silent,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (!silent) {
|
|
106
|
+
for (const warning of result.warnings) {
|
|
107
|
+
fmt.logWarn(warning);
|
|
108
|
+
}
|
|
109
|
+
if (result.changedFiles.length > 0) {
|
|
110
|
+
fmt.logStep(`Context Mode configured (${result.changedFiles.length} file${result.changedFiles.length === 1 ? '' : 's'})`);
|
|
111
|
+
} else if (result.warnings.length > 0) {
|
|
112
|
+
fmt.logWarn('Context Mode was requested but not configured');
|
|
113
|
+
} else {
|
|
114
|
+
fmt.logStep('Context Mode already configured');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
|
|
95
121
|
/**
|
|
96
122
|
* Create scaffold directories for a new team namespace so the developer
|
|
97
123
|
* has the standard folder structure ready for agents, skills, commands, and evals.
|
|
@@ -108,39 +134,10 @@ function scaffoldNamespace(awHome, folderName) {
|
|
|
108
134
|
}
|
|
109
135
|
}
|
|
110
136
|
|
|
111
|
-
function getSkillInitSpecs(args) {
|
|
112
|
-
const positional = args._positional || [];
|
|
113
|
-
const mode = positional[0];
|
|
114
|
-
const rawTargets = [];
|
|
115
|
-
|
|
116
|
-
if (mode === 'skill' || mode === 'skills') {
|
|
117
|
-
rawTargets.push(...positional.slice(1));
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (args['--skill']) {
|
|
121
|
-
rawTargets.push(args['--skill']);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if ((mode === 'skill' || mode === 'skills' || args['--skill']) && rawTargets.length === 0) {
|
|
125
|
-
fmt.cancel(`Missing skill path.\n\n ${chalk.dim('Example:')} ${chalk.bold('aw init skills platform/core/skills/pr-review')}`);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (rawTargets.length === 0) return [];
|
|
129
|
-
|
|
130
|
-
try {
|
|
131
|
-
return [...new Map(rawTargets.map(input => {
|
|
132
|
-
const spec = normalizeSkillTarget(input);
|
|
133
|
-
return [spec.registryPath, spec];
|
|
134
|
-
})).values()];
|
|
135
|
-
} catch (e) {
|
|
136
|
-
fmt.cancel(e.message);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
137
|
// ── Ensure ~/.aw/.git/info/exclude has the whitelist block ─────────────
|
|
141
138
|
//
|
|
142
|
-
// Strategy: only .aw_registry/, .aw_rules/, content
|
|
143
|
-
//
|
|
139
|
+
// Strategy: only .aw_registry/, .aw_rules/, content/ are tracked — everything
|
|
140
|
+
// else at the top level of ~/.aw/ is local-only (telemetry/, hooks/, logs,
|
|
144
141
|
// .DS_Store, etc.). We write to .git/info/exclude (not tracked .gitignore)
|
|
145
142
|
// so upstream pulls never conflict.
|
|
146
143
|
|
|
@@ -154,7 +151,6 @@ const AW_MANAGED_BLOCK = [
|
|
|
154
151
|
'!/.aw_registry',
|
|
155
152
|
'!/.aw_rules',
|
|
156
153
|
'!/content',
|
|
157
|
-
'!/.aw_docs',
|
|
158
154
|
'',
|
|
159
155
|
'# Nested local state within whitelisted dirs',
|
|
160
156
|
'/.aw_registry/.sync-config.json',
|
|
@@ -243,15 +239,6 @@ export async function initCommand(args) {
|
|
|
243
239
|
let namespace = args['--namespace'] || null;
|
|
244
240
|
let user = args['--user'] || '';
|
|
245
241
|
const silent = args['--silent'] === true;
|
|
246
|
-
const skipIntegrations = args['--no-integrations'] === true;
|
|
247
|
-
const skillSpecs = getSkillInitSpecs(args);
|
|
248
|
-
const skillTargets = skillSpecs.map(spec => spec.registryPath);
|
|
249
|
-
const isSkillInit = skillTargets.length > 0;
|
|
250
|
-
const skillNamespace = skillSpecs[0]?.namespace || null;
|
|
251
|
-
|
|
252
|
-
if (isSkillInit && namespace) {
|
|
253
|
-
fmt.cancel('Use either `aw init skills <path...>` or `aw init --namespace <team/sub-team>`, not both.');
|
|
254
|
-
}
|
|
255
242
|
|
|
256
243
|
// In silent mode, suppress ALL fmt output and show a single spinner.
|
|
257
244
|
// setSilent(true) makes every fmt.* call a no-op — internal functions
|
|
@@ -360,28 +347,10 @@ export async function initCommand(args) {
|
|
|
360
347
|
if (isGitNative) {
|
|
361
348
|
const cfg = config.load(GLOBAL_AW_DIR);
|
|
362
349
|
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
: [];
|
|
366
|
-
const isNewSubTeam = !isSkillInit && folderName && cfg && !cfg.include.includes(folderName);
|
|
367
|
-
|
|
368
|
-
if (isSkillInit) {
|
|
369
|
-
if (newSkillTargets.length > 0) {
|
|
370
|
-
if (!silent) fmt.logStep(`Adding ${chalk.cyan(newSkillTargets.length)} skill${newSkillTargets.length > 1 ? 's' : ''}...`);
|
|
371
|
-
addToSparseCheckout(AW_HOME, [
|
|
372
|
-
...newSkillTargets.map(target => `.aw_registry/${target}`),
|
|
373
|
-
DOCS_SOURCE_DIR,
|
|
374
|
-
AW_DOCS_DIR,
|
|
375
|
-
RULES_SOURCE_DIR,
|
|
376
|
-
`.aw_registry/AW-PROTOCOL.md`,
|
|
377
|
-
]);
|
|
378
|
-
for (const target of newSkillTargets) config.addPattern(GLOBAL_AW_DIR, target);
|
|
379
|
-
} else if (!silent) {
|
|
380
|
-
fmt.logStep('Already initialized — syncing selected skills...');
|
|
381
|
-
}
|
|
382
|
-
} else if (isNewSubTeam) {
|
|
350
|
+
const isNewSubTeam = folderName && cfg && !cfg.include.includes(folderName);
|
|
351
|
+
if (isNewSubTeam) {
|
|
383
352
|
if (!silent) fmt.logStep(`Adding sub-team ${chalk.cyan(folderName)}...`);
|
|
384
|
-
const newSparsePaths = [`.aw_registry/${folderName}`,
|
|
353
|
+
const newSparsePaths = [`.aw_registry/${folderName}`, 'content', RULES_SOURCE_DIR];
|
|
385
354
|
addToSparseCheckout(AW_HOME, newSparsePaths);
|
|
386
355
|
config.addPattern(GLOBAL_AW_DIR, folderName);
|
|
387
356
|
scaffoldNamespace(AW_HOME, folderName);
|
|
@@ -407,7 +376,6 @@ export async function initCommand(args) {
|
|
|
407
376
|
|
|
408
377
|
ensureAwGitignore(AW_HOME);
|
|
409
378
|
const freshCfg = config.load(GLOBAL_AW_DIR);
|
|
410
|
-
const activeNamespace = skillNamespace || freshCfg?.namespace || team;
|
|
411
379
|
syncRulesTargets(HOME);
|
|
412
380
|
if (cwd !== HOME) {
|
|
413
381
|
syncRulesTargets(cwd);
|
|
@@ -430,8 +398,9 @@ export async function initCommand(args) {
|
|
|
430
398
|
await installAwEcc(cwd, { silent });
|
|
431
399
|
|
|
432
400
|
ensureAwRuntimeHook(HOME);
|
|
433
|
-
syncHomeAndProjectInstructions(cwd,
|
|
434
|
-
await setupMcp(HOME,
|
|
401
|
+
syncHomeAndProjectInstructions(cwd, freshCfg?.namespace || team);
|
|
402
|
+
await setupMcp(HOME, freshCfg?.namespace || team, { silent });
|
|
403
|
+
maybeConfigureContextMode(args, { silent });
|
|
435
404
|
applyStoredStartupPreferences(HOME);
|
|
436
405
|
const removedLegacyStartupFiles = cwd !== HOME ? removeWorkspaceHookDefaults(cwd) : [];
|
|
437
406
|
installGlobalHooks();
|
|
@@ -465,22 +434,14 @@ export async function initCommand(args) {
|
|
|
465
434
|
if (!silent) fmt.logStep('Wiring IDE symlinks...');
|
|
466
435
|
const projectRegistryDir = cwd !== HOME ? join(cwd, '.aw', REGISTRY_DIR) : null;
|
|
467
436
|
const awDirForLinks = (projectRegistryDir && existsSync(projectRegistryDir)) ? projectRegistryDir : null;
|
|
468
|
-
const symlinks =
|
|
469
|
-
|
|
470
|
-
: linkWorkspace(HOME, awDirForLinks, { silent: true });
|
|
471
|
-
const commands = isSkillInit ? 0 : generateCommands(HOME, { silent: true });
|
|
437
|
+
const symlinks = linkWorkspace(HOME, awDirForLinks, { silent: true });
|
|
438
|
+
const commands = generateCommands(HOME, { silent: true });
|
|
472
439
|
if (cwd !== HOME) installLocalCommitHook(cwd);
|
|
473
|
-
if (!silent) fmt.logStep(`IDE wired — ${chalk.bold(symlinks)}
|
|
440
|
+
if (!silent) fmt.logStep(`IDE wired — ${chalk.bold(symlinks)} symlinks · ${chalk.bold(commands)} commands`);
|
|
474
441
|
|
|
475
442
|
// Write hook manifest after all hook installation is complete
|
|
476
443
|
try { writeHookManifest({ eccVersion: AW_ECC_TAG, awVersion: VERSION }); } catch { /* best effort */ }
|
|
477
444
|
|
|
478
|
-
// Auto-install suggested integrations (Codex, Caveman, Graphify, etc) - unless --no-integrations
|
|
479
|
-
let installedIntegrations = [];
|
|
480
|
-
if (!silent && !skipIntegrations && !isNewSubTeam) {
|
|
481
|
-
installedIntegrations = await autoInstallIntegrations(activeNamespace, { silent });
|
|
482
|
-
}
|
|
483
|
-
|
|
484
445
|
if (silent) {
|
|
485
446
|
if (silentSpinner) { silentSpinner.stop('Done'); setSilent(false); }
|
|
486
447
|
autoUpdate(await args._updateCheck);
|
|
@@ -489,12 +450,11 @@ export async function initCommand(args) {
|
|
|
489
450
|
`⟁ ${isNewSubTeam ? `Sub-team ${chalk.cyan(folderName)} added` : 'Up to date'}`,
|
|
490
451
|
'',
|
|
491
452
|
` ${chalk.green('✓')} Registry synced`,
|
|
492
|
-
` ${chalk.green('✓')} IDE refreshed — ${chalk.bold(symlinks)}
|
|
453
|
+
` ${chalk.green('✓')} IDE refreshed — ${chalk.bold(symlinks)} symlinks · ${chalk.bold(commands)} commands`,
|
|
493
454
|
removedLegacyStartupFiles.length > 0
|
|
494
455
|
? ` ${chalk.green('✓')} Removed ${removedLegacyStartupFiles.length} legacy repo startup file${removedLegacyStartupFiles.length > 1 ? 's' : ''}`
|
|
495
456
|
: null,
|
|
496
457
|
cwd !== HOME && isWorktree(join(cwd, '.aw')) ? ` ${chalk.green('✓')} Project linked` : null,
|
|
497
|
-
installedIntegrations.length > 0 ? ` ${chalk.green('✓')} Integrations: ${installedIntegrations.join(', ')}` : null,
|
|
498
458
|
].filter(Boolean).join('\n'));
|
|
499
459
|
}
|
|
500
460
|
return;
|
|
@@ -523,24 +483,13 @@ export async function initCommand(args) {
|
|
|
523
483
|
}
|
|
524
484
|
|
|
525
485
|
// Determine sparse paths
|
|
526
|
-
const sparsePaths =
|
|
527
|
-
|
|
528
|
-
...skillTargets.map(target => `.aw_registry/${target}`),
|
|
529
|
-
DOCS_SOURCE_DIR,
|
|
530
|
-
AW_DOCS_DIR,
|
|
531
|
-
RULES_SOURCE_DIR,
|
|
532
|
-
`.aw_registry/AW-PROTOCOL.md`,
|
|
533
|
-
`CODEOWNERS`,
|
|
534
|
-
]
|
|
535
|
-
: [`.aw_registry/platform`, DOCS_SOURCE_DIR, AW_DOCS_DIR, RULES_SOURCE_DIR, `.aw_registry/AW-PROTOCOL.md`, `CODEOWNERS`];
|
|
536
|
-
if (!isSkillInit && folderName) {
|
|
486
|
+
const sparsePaths = [`.aw_registry/platform`, `content`, RULES_SOURCE_DIR, `.aw_registry/AW-PROTOCOL.md`, `CODEOWNERS`];
|
|
487
|
+
if (folderName) {
|
|
537
488
|
sparsePaths.push(`.aw_registry/${folderName}`);
|
|
538
489
|
}
|
|
539
490
|
|
|
540
491
|
fmt.note([
|
|
541
|
-
|
|
542
|
-
? `${chalk.dim('skills:')} ${skillTargets.join(', ')}`
|
|
543
|
-
: (folderName ? `${chalk.dim('namespace:')} ${folderName}` : `${chalk.dim('namespace:')} ${chalk.dim('none')}`),
|
|
492
|
+
folderName ? `${chalk.dim('namespace:')} ${folderName}` : `${chalk.dim('namespace:')} ${chalk.dim('none')}`,
|
|
544
493
|
user ? `${chalk.dim('user:')} ${user}` : null,
|
|
545
494
|
`${chalk.dim('version:')} v${VERSION}`,
|
|
546
495
|
].filter(Boolean).join('\n'), 'Config');
|
|
@@ -580,11 +529,8 @@ export async function initCommand(args) {
|
|
|
580
529
|
}
|
|
581
530
|
|
|
582
531
|
// Create sync config — default to 'platform' when no namespace specified
|
|
583
|
-
const
|
|
584
|
-
|
|
585
|
-
if (isSkillInit) {
|
|
586
|
-
for (const target of skillTargets) config.addPattern(GLOBAL_AW_DIR, target);
|
|
587
|
-
} else if (folderName) {
|
|
532
|
+
const cfg = config.create(GLOBAL_AW_DIR, { namespace: team || 'platform', user });
|
|
533
|
+
if (folderName) {
|
|
588
534
|
config.addPattern(GLOBAL_AW_DIR, folderName);
|
|
589
535
|
scaffoldNamespace(AW_HOME, folderName);
|
|
590
536
|
}
|
|
@@ -605,9 +551,10 @@ export async function initCommand(args) {
|
|
|
605
551
|
|
|
606
552
|
// Parallel batch B: post-ECC setup (instructions and MCP are independent)
|
|
607
553
|
const [, mcpFiles] = await Promise.all([
|
|
608
|
-
Promise.resolve(syncHomeAndProjectInstructions(cwd,
|
|
609
|
-
setupMcp(HOME,
|
|
554
|
+
Promise.resolve(syncHomeAndProjectInstructions(cwd, team)),
|
|
555
|
+
setupMcp(HOME, team, { silent }),
|
|
610
556
|
]);
|
|
557
|
+
maybeConfigureContextMode(args, { silent });
|
|
611
558
|
// applyStoredStartupPreferences reads settings written by ECC — keep after batch B
|
|
612
559
|
applyStoredStartupPreferences(HOME);
|
|
613
560
|
const removedLegacyStartupFiles = cwd !== HOME ? removeWorkspaceHookDefaults(cwd) : [];
|
|
@@ -644,13 +591,11 @@ export async function initCommand(args) {
|
|
|
644
591
|
const awDirForLinks = (projectRegistryDir && existsSync(projectRegistryDir)) ? projectRegistryDir : null;
|
|
645
592
|
// Parallel batch C: symlinks + commands are independent
|
|
646
593
|
if (cwd !== HOME) installLocalCommitHook(cwd);
|
|
647
|
-
const [symlinks, commands] =
|
|
648
|
-
|
|
649
|
-
:
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
];
|
|
653
|
-
if (!silent) fmt.logStep(`IDE wired — ${chalk.bold(symlinks)} ${isSkillInit ? 'skill symlinks' : 'symlinks'} · ${chalk.bold(commands)} commands`);
|
|
594
|
+
const [symlinks, commands] = [
|
|
595
|
+
linkWorkspace(HOME, awDirForLinks, { silent: true }),
|
|
596
|
+
generateCommands(HOME, { silent: true }),
|
|
597
|
+
];
|
|
598
|
+
if (!silent) fmt.logStep(`IDE wired — ${chalk.bold(symlinks)} symlinks · ${chalk.bold(commands)} commands`);
|
|
654
599
|
|
|
655
600
|
// Write hook manifest after all hook installation is complete
|
|
656
601
|
try { writeHookManifest({ eccVersion: AW_ECC_TAG, awVersion: VERSION }); } catch { /* best effort */ }
|
|
@@ -658,12 +603,6 @@ export async function initCommand(args) {
|
|
|
658
603
|
// Ensure telemetry config exists (generates machine_id on first run)
|
|
659
604
|
ensureTelemetryConfig();
|
|
660
605
|
|
|
661
|
-
// Auto-install suggested integrations (Codex, Caveman, Graphify, etc) - unless --no-integrations
|
|
662
|
-
let installedIntegrations = [];
|
|
663
|
-
if (!silent && !skipIntegrations) {
|
|
664
|
-
installedIntegrations = await autoInstallIntegrations(activeNamespace, { silent });
|
|
665
|
-
}
|
|
666
|
-
|
|
667
606
|
// Offer to update if a newer version is available
|
|
668
607
|
if (!silent) await promptUpdate(await args._updateCheck);
|
|
669
608
|
|
|
@@ -683,12 +622,10 @@ export async function initCommand(args) {
|
|
|
683
622
|
hooksInstalled ? ` ${chalk.green('✓')} Git hooks: auto-sync on pull/clone (core.hooksPath)` : null,
|
|
684
623
|
` ${chalk.green('✓')} IDE task: auto-sync on workspace open`,
|
|
685
624
|
cwd !== HOME && isWorktree(join(cwd, '.aw')) ? ` ${chalk.green('✓')} Linked in current project` : null,
|
|
686
|
-
installedIntegrations.length > 0 ? ` ${chalk.green('✓')} Integrations: ${installedIntegrations.join(', ')}` : null,
|
|
687
625
|
'',
|
|
688
626
|
` ${chalk.dim('Existing repos:')} ${chalk.bold('cd <project> && aw link')}`,
|
|
689
627
|
` ${chalk.dim('New clones:')} auto-linked via git hook`,
|
|
690
628
|
` ${chalk.dim('Update:')} ${chalk.bold('aw init')} ${chalk.dim('(or auto on pull/IDE open)')}`,
|
|
691
|
-
` ${chalk.dim('Integrations:')} ${chalk.bold('aw integrations')} ${chalk.dim('(manage Codex, Caveman, etc)')}`,
|
|
692
629
|
` ${chalk.dim('Uninstall:')} ${chalk.bold('aw nuke')}`,
|
|
693
630
|
].filter(Boolean).join('\n'));
|
|
694
631
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
|
|
3
|
+
import * as fmt from '../fmt.mjs';
|
|
4
|
+
import { listIntegrations, resolveIntegration } from '../integrations/index.mjs';
|
|
5
|
+
|
|
6
|
+
function printWarnings(warnings = []) {
|
|
7
|
+
for (const warning of warnings) {
|
|
8
|
+
fmt.logWarn(warning);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
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');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
return resolveIntegration(name);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
fmt.cancel(err.message);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function integrationCommand(args) {
|
|
30
|
+
const [subcommand = 'list', name] = args._positional;
|
|
31
|
+
const home = homedir();
|
|
32
|
+
|
|
33
|
+
if (args['--help']) {
|
|
34
|
+
fmt.intro('aw integration');
|
|
35
|
+
fmt.note(
|
|
36
|
+
[
|
|
37
|
+
'aw integration list',
|
|
38
|
+
'aw integration add context-mode [--dry-run]',
|
|
39
|
+
'aw integration status context-mode',
|
|
40
|
+
'aw integration remove context-mode [--dry-run]',
|
|
41
|
+
].join('\n'),
|
|
42
|
+
'Usage',
|
|
43
|
+
);
|
|
44
|
+
fmt.outro('Done');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
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
|
+
);
|
|
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
|
+
}
|
|
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;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
fmt.cancel(`Unknown integration subcommand: ${subcommand}. Use add / remove / status / list`);
|
|
111
|
+
}
|
package/commands/nuke.mjs
CHANGED
|
@@ -14,6 +14,7 @@ import { chalk } from '../fmt.mjs';
|
|
|
14
14
|
import { removeGlobalHooks } from '../hooks.mjs';
|
|
15
15
|
import { uninstallAwEcc } from '../ecc.mjs';
|
|
16
16
|
import { removeMcpConfig } from '../mcp.mjs';
|
|
17
|
+
import { removeContextModeIntegration } from '../integrations/context-mode.mjs';
|
|
17
18
|
import { listProjectWorktrees } from '../git.mjs';
|
|
18
19
|
import { removeWorkspaceHookDefaults } from '../codex.mjs';
|
|
19
20
|
import { readHookManifest, pruneStaleHooks, removeHookManifest } from '../hook-cleanup.mjs';
|
|
@@ -245,6 +246,7 @@ export async function nukeCommand(args) {
|
|
|
245
246
|
const ideCount = removeIdeSymlinks();
|
|
246
247
|
uninstallAwEcc();
|
|
247
248
|
removeMcpConfig();
|
|
249
|
+
removeContextModeIntegration(HOME);
|
|
248
250
|
// Prune stale hook entries using manifest (catches entries not tracked by install-state)
|
|
249
251
|
if (hookManifest) pruneStaleHooks(hookManifest);
|
|
250
252
|
removeHookManifest();
|
|
@@ -364,7 +366,7 @@ export async function nukeCommand(args) {
|
|
|
364
366
|
'',
|
|
365
367
|
` ${chalk.green('✓')} Generated files cleaned`,
|
|
366
368
|
` ${chalk.green('✓')} IDE symlinks cleaned`,
|
|
367
|
-
` ${chalk.green('✓')} MCP config removed (ghl-ai
|
|
369
|
+
` ${chalk.green('✓')} MCP config removed (ghl-ai and context-mode)`,
|
|
368
370
|
` ${chalk.green('✓')} aw-ecc engine removed`,
|
|
369
371
|
` ${chalk.green('✓')} Project worktrees removed`,
|
|
370
372
|
` ${chalk.green('✓')} Project symlinks cleaned`,
|