@ghl-ai/aw 0.1.50-beta.1 → 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 -125
- 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 -19
- package/integrate.mjs +21 -94
- package/integrations/context-mode.mjs +514 -0
- package/integrations/index.mjs +31 -0
- package/link.mjs +33 -159
- 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,13 +35,11 @@ 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,
|
|
41
41
|
fetchAndMerge,
|
|
42
42
|
addToSparseCheckout,
|
|
43
|
-
setSparseCheckout,
|
|
44
43
|
addProjectWorktree,
|
|
45
44
|
isWorktree,
|
|
46
45
|
includeToSparsePaths,
|
|
@@ -49,7 +48,7 @@ import {
|
|
|
49
48
|
syncWorktreeSparseCheckout,
|
|
50
49
|
findNearestWorktree,
|
|
51
50
|
} from '../git.mjs';
|
|
52
|
-
import { REGISTRY_DIR, REGISTRY_REPO, REGISTRY_URL,
|
|
51
|
+
import { REGISTRY_DIR, REGISTRY_REPO, REGISTRY_URL, RULES_SOURCE_DIR, RULES_RUNTIME_DIR } from '../constants.mjs';
|
|
53
52
|
import { syncFileTree } from '../file-tree.mjs';
|
|
54
53
|
|
|
55
54
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -93,6 +92,32 @@ function syncHomeAndProjectInstructions(cwd, namespace) {
|
|
|
93
92
|
}
|
|
94
93
|
}
|
|
95
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
|
+
|
|
96
121
|
/**
|
|
97
122
|
* Create scaffold directories for a new team namespace so the developer
|
|
98
123
|
* has the standard folder structure ready for agents, skills, commands, and evals.
|
|
@@ -109,60 +134,10 @@ function scaffoldNamespace(awHome, folderName) {
|
|
|
109
134
|
}
|
|
110
135
|
}
|
|
111
136
|
|
|
112
|
-
function getSkillInitSpecs(args) {
|
|
113
|
-
const positional = args._positional || [];
|
|
114
|
-
const mode = positional[0];
|
|
115
|
-
const rawTargets = [];
|
|
116
|
-
|
|
117
|
-
if (mode === 'skill' || mode === 'skills') {
|
|
118
|
-
rawTargets.push(...positional.slice(1));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (args['--skill']) {
|
|
122
|
-
rawTargets.push(args['--skill']);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if ((mode === 'skill' || mode === 'skills' || args['--skill']) && rawTargets.length === 0) {
|
|
126
|
-
fmt.cancel(`Missing skill path.\n\n ${chalk.dim('Example:')} ${chalk.bold('aw init skills platform/core/skills/pr-review')}`);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (rawTargets.length === 0) return [];
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
return [...new Map(rawTargets.map(input => {
|
|
133
|
-
const spec = normalizeSkillTarget(input);
|
|
134
|
-
return [spec.registryPath, spec];
|
|
135
|
-
})).values()];
|
|
136
|
-
} catch (e) {
|
|
137
|
-
fmt.cancel(e.message);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function skillInitSparsePaths(skillSpecs) {
|
|
142
|
-
const paths = new Set([
|
|
143
|
-
DOCS_SOURCE_DIR,
|
|
144
|
-
AW_DOCS_DIR,
|
|
145
|
-
RULES_SOURCE_DIR,
|
|
146
|
-
`${REGISTRY_DIR}/AW-PROTOCOL.md`,
|
|
147
|
-
`CODEOWNERS`,
|
|
148
|
-
]);
|
|
149
|
-
|
|
150
|
-
for (const spec of skillSpecs) {
|
|
151
|
-
paths.add(`${REGISTRY_DIR}/${spec.registryPath}`);
|
|
152
|
-
|
|
153
|
-
for (let i = 0; i <= spec.segments.length; i++) {
|
|
154
|
-
const scope = [spec.namespace, ...spec.segments.slice(0, i)].join('/');
|
|
155
|
-
paths.add(`${REGISTRY_DIR}/${scope}/references`);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return [...paths];
|
|
160
|
-
}
|
|
161
|
-
|
|
162
137
|
// ── Ensure ~/.aw/.git/info/exclude has the whitelist block ─────────────
|
|
163
138
|
//
|
|
164
|
-
// Strategy: only .aw_registry/, .aw_rules/, content
|
|
165
|
-
//
|
|
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,
|
|
166
141
|
// .DS_Store, etc.). We write to .git/info/exclude (not tracked .gitignore)
|
|
167
142
|
// so upstream pulls never conflict.
|
|
168
143
|
|
|
@@ -176,7 +151,6 @@ const AW_MANAGED_BLOCK = [
|
|
|
176
151
|
'!/.aw_registry',
|
|
177
152
|
'!/.aw_rules',
|
|
178
153
|
'!/content',
|
|
179
|
-
'!/.aw_docs',
|
|
180
154
|
'',
|
|
181
155
|
'# Nested local state within whitelisted dirs',
|
|
182
156
|
'/.aw_registry/.sync-config.json',
|
|
@@ -265,15 +239,6 @@ export async function initCommand(args) {
|
|
|
265
239
|
let namespace = args['--namespace'] || null;
|
|
266
240
|
let user = args['--user'] || '';
|
|
267
241
|
const silent = args['--silent'] === true;
|
|
268
|
-
const skipIntegrations = args['--no-integrations'] === true;
|
|
269
|
-
const skillSpecs = getSkillInitSpecs(args);
|
|
270
|
-
const skillTargets = skillSpecs.map(spec => spec.registryPath);
|
|
271
|
-
const isSkillInit = skillTargets.length > 0;
|
|
272
|
-
const skillNamespace = skillSpecs[0]?.namespace || null;
|
|
273
|
-
|
|
274
|
-
if (isSkillInit && namespace) {
|
|
275
|
-
fmt.cancel('Use either `aw init skills <path...>` or `aw init --namespace <team/sub-team>`, not both.');
|
|
276
|
-
}
|
|
277
242
|
|
|
278
243
|
// In silent mode, suppress ALL fmt output and show a single spinner.
|
|
279
244
|
// setSilent(true) makes every fmt.* call a no-op — internal functions
|
|
@@ -382,21 +347,10 @@ export async function initCommand(args) {
|
|
|
382
347
|
if (isGitNative) {
|
|
383
348
|
const cfg = config.load(GLOBAL_AW_DIR);
|
|
384
349
|
|
|
385
|
-
const isNewSubTeam =
|
|
386
|
-
|
|
387
|
-
if (isSkillInit) {
|
|
388
|
-
if (!silent) fmt.logStep('Syncing selected skills...');
|
|
389
|
-
setSparseCheckout(AW_HOME, skillInitSparsePaths(skillSpecs));
|
|
390
|
-
if (cfg) {
|
|
391
|
-
config.save(GLOBAL_AW_DIR, {
|
|
392
|
-
...cfg,
|
|
393
|
-
namespace: skillNamespace || cfg.namespace || 'platform',
|
|
394
|
-
include: skillTargets,
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
} else if (isNewSubTeam) {
|
|
350
|
+
const isNewSubTeam = folderName && cfg && !cfg.include.includes(folderName);
|
|
351
|
+
if (isNewSubTeam) {
|
|
398
352
|
if (!silent) fmt.logStep(`Adding sub-team ${chalk.cyan(folderName)}...`);
|
|
399
|
-
const newSparsePaths = [`.aw_registry/${folderName}`,
|
|
353
|
+
const newSparsePaths = [`.aw_registry/${folderName}`, 'content', RULES_SOURCE_DIR];
|
|
400
354
|
addToSparseCheckout(AW_HOME, newSparsePaths);
|
|
401
355
|
config.addPattern(GLOBAL_AW_DIR, folderName);
|
|
402
356
|
scaffoldNamespace(AW_HOME, folderName);
|
|
@@ -422,7 +376,6 @@ export async function initCommand(args) {
|
|
|
422
376
|
|
|
423
377
|
ensureAwGitignore(AW_HOME);
|
|
424
378
|
const freshCfg = config.load(GLOBAL_AW_DIR);
|
|
425
|
-
const activeNamespace = skillNamespace || freshCfg?.namespace || team;
|
|
426
379
|
syncRulesTargets(HOME);
|
|
427
380
|
if (cwd !== HOME) {
|
|
428
381
|
syncRulesTargets(cwd);
|
|
@@ -445,8 +398,9 @@ export async function initCommand(args) {
|
|
|
445
398
|
await installAwEcc(cwd, { silent });
|
|
446
399
|
|
|
447
400
|
ensureAwRuntimeHook(HOME);
|
|
448
|
-
syncHomeAndProjectInstructions(cwd,
|
|
449
|
-
await setupMcp(HOME,
|
|
401
|
+
syncHomeAndProjectInstructions(cwd, freshCfg?.namespace || team);
|
|
402
|
+
await setupMcp(HOME, freshCfg?.namespace || team, { silent });
|
|
403
|
+
maybeConfigureContextMode(args, { silent });
|
|
450
404
|
applyStoredStartupPreferences(HOME);
|
|
451
405
|
const removedLegacyStartupFiles = cwd !== HOME ? removeWorkspaceHookDefaults(cwd) : [];
|
|
452
406
|
installGlobalHooks();
|
|
@@ -480,22 +434,14 @@ export async function initCommand(args) {
|
|
|
480
434
|
if (!silent) fmt.logStep('Wiring IDE symlinks...');
|
|
481
435
|
const projectRegistryDir = cwd !== HOME ? join(cwd, '.aw', REGISTRY_DIR) : null;
|
|
482
436
|
const awDirForLinks = (projectRegistryDir && existsSync(projectRegistryDir)) ? projectRegistryDir : null;
|
|
483
|
-
const symlinks =
|
|
484
|
-
|
|
485
|
-
: linkWorkspace(HOME, awDirForLinks, { silent: true });
|
|
486
|
-
const commands = isSkillInit ? 0 : generateCommands(HOME, { silent: true });
|
|
437
|
+
const symlinks = linkWorkspace(HOME, awDirForLinks, { silent: true });
|
|
438
|
+
const commands = generateCommands(HOME, { silent: true });
|
|
487
439
|
if (cwd !== HOME) installLocalCommitHook(cwd);
|
|
488
|
-
if (!silent) fmt.logStep(`IDE wired — ${chalk.bold(symlinks)}
|
|
440
|
+
if (!silent) fmt.logStep(`IDE wired — ${chalk.bold(symlinks)} symlinks · ${chalk.bold(commands)} commands`);
|
|
489
441
|
|
|
490
442
|
// Write hook manifest after all hook installation is complete
|
|
491
443
|
try { writeHookManifest({ eccVersion: AW_ECC_TAG, awVersion: VERSION }); } catch { /* best effort */ }
|
|
492
444
|
|
|
493
|
-
// Auto-install suggested integrations (Codex, Caveman, Graphify, etc) - unless --no-integrations
|
|
494
|
-
let installedIntegrations = [];
|
|
495
|
-
if (!silent && !skipIntegrations && !isNewSubTeam) {
|
|
496
|
-
installedIntegrations = await autoInstallIntegrations(activeNamespace, { silent });
|
|
497
|
-
}
|
|
498
|
-
|
|
499
445
|
if (silent) {
|
|
500
446
|
if (silentSpinner) { silentSpinner.stop('Done'); setSilent(false); }
|
|
501
447
|
autoUpdate(await args._updateCheck);
|
|
@@ -504,12 +450,11 @@ export async function initCommand(args) {
|
|
|
504
450
|
`⟁ ${isNewSubTeam ? `Sub-team ${chalk.cyan(folderName)} added` : 'Up to date'}`,
|
|
505
451
|
'',
|
|
506
452
|
` ${chalk.green('✓')} Registry synced`,
|
|
507
|
-
` ${chalk.green('✓')} IDE refreshed — ${chalk.bold(symlinks)}
|
|
453
|
+
` ${chalk.green('✓')} IDE refreshed — ${chalk.bold(symlinks)} symlinks · ${chalk.bold(commands)} commands`,
|
|
508
454
|
removedLegacyStartupFiles.length > 0
|
|
509
455
|
? ` ${chalk.green('✓')} Removed ${removedLegacyStartupFiles.length} legacy repo startup file${removedLegacyStartupFiles.length > 1 ? 's' : ''}`
|
|
510
456
|
: null,
|
|
511
457
|
cwd !== HOME && isWorktree(join(cwd, '.aw')) ? ` ${chalk.green('✓')} Project linked` : null,
|
|
512
|
-
installedIntegrations.length > 0 ? ` ${chalk.green('✓')} Integrations: ${installedIntegrations.join(', ')}` : null,
|
|
513
458
|
].filter(Boolean).join('\n'));
|
|
514
459
|
}
|
|
515
460
|
return;
|
|
@@ -538,17 +483,13 @@ export async function initCommand(args) {
|
|
|
538
483
|
}
|
|
539
484
|
|
|
540
485
|
// Determine sparse paths
|
|
541
|
-
const sparsePaths =
|
|
542
|
-
|
|
543
|
-
: [`.aw_registry/platform`, DOCS_SOURCE_DIR, AW_DOCS_DIR, RULES_SOURCE_DIR, `.aw_registry/AW-PROTOCOL.md`, `CODEOWNERS`];
|
|
544
|
-
if (!isSkillInit && folderName) {
|
|
486
|
+
const sparsePaths = [`.aw_registry/platform`, `content`, RULES_SOURCE_DIR, `.aw_registry/AW-PROTOCOL.md`, `CODEOWNERS`];
|
|
487
|
+
if (folderName) {
|
|
545
488
|
sparsePaths.push(`.aw_registry/${folderName}`);
|
|
546
489
|
}
|
|
547
490
|
|
|
548
491
|
fmt.note([
|
|
549
|
-
|
|
550
|
-
? `${chalk.dim('skills:')} ${skillTargets.join(', ')}`
|
|
551
|
-
: (folderName ? `${chalk.dim('namespace:')} ${folderName}` : `${chalk.dim('namespace:')} ${chalk.dim('none')}`),
|
|
492
|
+
folderName ? `${chalk.dim('namespace:')} ${folderName}` : `${chalk.dim('namespace:')} ${chalk.dim('none')}`,
|
|
552
493
|
user ? `${chalk.dim('user:')} ${user}` : null,
|
|
553
494
|
`${chalk.dim('version:')} v${VERSION}`,
|
|
554
495
|
].filter(Boolean).join('\n'), 'Config');
|
|
@@ -588,11 +529,8 @@ export async function initCommand(args) {
|
|
|
588
529
|
}
|
|
589
530
|
|
|
590
531
|
// Create sync config — default to 'platform' when no namespace specified
|
|
591
|
-
const
|
|
592
|
-
|
|
593
|
-
if (isSkillInit) {
|
|
594
|
-
for (const target of skillTargets) config.addPattern(GLOBAL_AW_DIR, target);
|
|
595
|
-
} else if (folderName) {
|
|
532
|
+
const cfg = config.create(GLOBAL_AW_DIR, { namespace: team || 'platform', user });
|
|
533
|
+
if (folderName) {
|
|
596
534
|
config.addPattern(GLOBAL_AW_DIR, folderName);
|
|
597
535
|
scaffoldNamespace(AW_HOME, folderName);
|
|
598
536
|
}
|
|
@@ -613,9 +551,10 @@ export async function initCommand(args) {
|
|
|
613
551
|
|
|
614
552
|
// Parallel batch B: post-ECC setup (instructions and MCP are independent)
|
|
615
553
|
const [, mcpFiles] = await Promise.all([
|
|
616
|
-
Promise.resolve(syncHomeAndProjectInstructions(cwd,
|
|
617
|
-
setupMcp(HOME,
|
|
554
|
+
Promise.resolve(syncHomeAndProjectInstructions(cwd, team)),
|
|
555
|
+
setupMcp(HOME, team, { silent }),
|
|
618
556
|
]);
|
|
557
|
+
maybeConfigureContextMode(args, { silent });
|
|
619
558
|
// applyStoredStartupPreferences reads settings written by ECC — keep after batch B
|
|
620
559
|
applyStoredStartupPreferences(HOME);
|
|
621
560
|
const removedLegacyStartupFiles = cwd !== HOME ? removeWorkspaceHookDefaults(cwd) : [];
|
|
@@ -652,13 +591,11 @@ export async function initCommand(args) {
|
|
|
652
591
|
const awDirForLinks = (projectRegistryDir && existsSync(projectRegistryDir)) ? projectRegistryDir : null;
|
|
653
592
|
// Parallel batch C: symlinks + commands are independent
|
|
654
593
|
if (cwd !== HOME) installLocalCommitHook(cwd);
|
|
655
|
-
const [symlinks, commands] =
|
|
656
|
-
|
|
657
|
-
:
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
];
|
|
661
|
-
if (!silent) fmt.logStep(`IDE wired — ${chalk.bold(symlinks)} ${isSkillInit ? 'targeted 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`);
|
|
662
599
|
|
|
663
600
|
// Write hook manifest after all hook installation is complete
|
|
664
601
|
try { writeHookManifest({ eccVersion: AW_ECC_TAG, awVersion: VERSION }); } catch { /* best effort */ }
|
|
@@ -666,12 +603,6 @@ export async function initCommand(args) {
|
|
|
666
603
|
// Ensure telemetry config exists (generates machine_id on first run)
|
|
667
604
|
ensureTelemetryConfig();
|
|
668
605
|
|
|
669
|
-
// Auto-install suggested integrations (Codex, Caveman, Graphify, etc) - unless --no-integrations
|
|
670
|
-
let installedIntegrations = [];
|
|
671
|
-
if (!silent && !skipIntegrations) {
|
|
672
|
-
installedIntegrations = await autoInstallIntegrations(activeNamespace, { silent });
|
|
673
|
-
}
|
|
674
|
-
|
|
675
606
|
// Offer to update if a newer version is available
|
|
676
607
|
if (!silent) await promptUpdate(await args._updateCheck);
|
|
677
608
|
|
|
@@ -691,12 +622,10 @@ export async function initCommand(args) {
|
|
|
691
622
|
hooksInstalled ? ` ${chalk.green('✓')} Git hooks: auto-sync on pull/clone (core.hooksPath)` : null,
|
|
692
623
|
` ${chalk.green('✓')} IDE task: auto-sync on workspace open`,
|
|
693
624
|
cwd !== HOME && isWorktree(join(cwd, '.aw')) ? ` ${chalk.green('✓')} Linked in current project` : null,
|
|
694
|
-
installedIntegrations.length > 0 ? ` ${chalk.green('✓')} Integrations: ${installedIntegrations.join(', ')}` : null,
|
|
695
625
|
'',
|
|
696
626
|
` ${chalk.dim('Existing repos:')} ${chalk.bold('cd <project> && aw link')}`,
|
|
697
627
|
` ${chalk.dim('New clones:')} auto-linked via git hook`,
|
|
698
628
|
` ${chalk.dim('Update:')} ${chalk.bold('aw init')} ${chalk.dim('(or auto on pull/IDE open)')}`,
|
|
699
|
-
` ${chalk.dim('Integrations:')} ${chalk.bold('aw integrations')} ${chalk.dim('(manage Codex, Caveman, etc)')}`,
|
|
700
629
|
` ${chalk.dim('Uninstall:')} ${chalk.bold('aw nuke')}`,
|
|
701
630
|
].filter(Boolean).join('\n'));
|
|
702
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`,
|