@ghl-ai/aw 0.1.47-beta.1 → 0.1.47-beta.11
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 +7 -2
- package/commands/c4.mjs +14 -6
- package/commands/doctor.mjs +2 -2
- package/commands/init.mjs +6 -5
- package/commands/mcp.mjs +90 -0
- package/commands/nuke.mjs +1 -1
- package/commands/pull.mjs +3 -2
- package/commands/push.mjs +567 -29
- package/commands/startup.mjs +22 -3
- package/constants.mjs +20 -0
- package/ecc.mjs +1 -1
- package/git.mjs +6 -4
- package/integrate.mjs +94 -21
- package/mcp.mjs +132 -16
- package/package.json +1 -1
- package/render-rules.mjs +25 -1
- package/startup.mjs +52 -8
package/cli.mjs
CHANGED
|
@@ -21,6 +21,7 @@ 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),
|
|
24
25
|
search: () => import('./commands/search.mjs').then(m => m.searchCommand),
|
|
25
26
|
link: () => import('./commands/link-project.mjs').then(m => m.linkProjectCommand),
|
|
26
27
|
nuke: () => import('./commands/nuke.mjs').then(m => m.nukeCommand),
|
|
@@ -97,6 +98,7 @@ function printHelp() {
|
|
|
97
98
|
|
|
98
99
|
sec('Upload'),
|
|
99
100
|
cmd('aw push', 'Push all modified files (creates one PR)'),
|
|
101
|
+
cmd('aw push --aw-docs-only', 'Publish generated .aw_docs companions and print share links'),
|
|
100
102
|
cmd('aw push <path>', 'Push file, folder, or namespace to registry'),
|
|
101
103
|
cmd('aw push-rules [path]', 'Push platform rules to platform-docs'),
|
|
102
104
|
cmd('aw push --dry-run [path]', 'Preview what would be pushed'),
|
|
@@ -109,8 +111,11 @@ function printHelp() {
|
|
|
109
111
|
cmd('aw doctor', 'Run a health check for routing, MCP, plugin, and AW ECC surfaces'),
|
|
110
112
|
cmd('aw link', 'Link current project as a git worktree (wires IDE symlinks)'),
|
|
111
113
|
cmd('aw routing status', 'Show global AW session-routing mode for Claude/Cursor/Codex'),
|
|
112
|
-
cmd('aw routing disable', 'Disable
|
|
113
|
-
cmd('aw routing enable', 'Re-enable
|
|
114
|
+
cmd('aw routing disable', 'Disable default AW router/rules injection globally'),
|
|
115
|
+
cmd('aw routing enable', 'Re-enable default AW router/rules injection globally'),
|
|
116
|
+
cmd('aw mcp status', 'Show global AW MCP mode for Claude/Cursor/Codex'),
|
|
117
|
+
cmd('aw mcp disable', 'Remove AW-managed MCP server across Claude/Cursor/Codex'),
|
|
118
|
+
cmd('aw mcp enable', 'Restore AW-managed MCP server across Claude/Cursor/Codex'),
|
|
114
119
|
cmd('aw integrations', 'Manage third-party integrations (Codex, Caveman, etc)'),
|
|
115
120
|
cmd('aw integrations add <key>', 'Install a specific tool (e.g. codex, caveman)'),
|
|
116
121
|
cmd('aw integrations remove <key>', 'Remove a tool'),
|
package/commands/c4.mjs
CHANGED
|
@@ -24,6 +24,7 @@ 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';
|
|
27
28
|
|
|
28
29
|
/* ─────────────────────────────────────────────────────────────────────────
|
|
29
30
|
* Constants — referenced by self-tests.
|
|
@@ -380,7 +381,12 @@ export async function c4Command(rawArgs, overrides = {}) {
|
|
|
380
381
|
}
|
|
381
382
|
|
|
382
383
|
// Step 11 — MCP register.
|
|
383
|
-
|
|
384
|
+
const mcpEnabled = isMcpEnabled(home, env);
|
|
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
|
+
}
|
|
384
390
|
|
|
385
391
|
// Step 12 — slash command surface.
|
|
386
392
|
safe('ensureCommandSurface', () => c4.ensureCommandSurface({ harness, home, eccHome }), writer);
|
|
@@ -403,11 +409,13 @@ export async function c4Command(rawArgs, overrides = {}) {
|
|
|
403
409
|
safe('ensureRepoLocalIgnore', () => c4.ensureRepoLocalIgnore({ cwd, harness }), writer);
|
|
404
410
|
|
|
405
411
|
// Step 15 — MCP smoke probe (best-effort).
|
|
406
|
-
const mcpProbe =
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
412
|
+
const mcpProbe = mcpEnabled
|
|
413
|
+
? await safeAsync(
|
|
414
|
+
'probeMcpServer',
|
|
415
|
+
() => c4.probeMcpServer({ url: c4.MCP_URL_DEFAULT, token }),
|
|
416
|
+
writer,
|
|
417
|
+
)
|
|
418
|
+
: null;
|
|
411
419
|
|
|
412
420
|
// Step 16 — self-tests.
|
|
413
421
|
const self = runSelfTests({ harness, c4, home, awHome, eccHome });
|
package/commands/doctor.mjs
CHANGED
|
@@ -209,7 +209,7 @@ function directoryContainsGeneratedRuleFiles(dirPath, extension) {
|
|
|
209
209
|
try {
|
|
210
210
|
return readdirSync(dirPath, { withFileTypes: true })
|
|
211
211
|
.filter(entry => entry.isFile() && entry.name.endsWith(extension))
|
|
212
|
-
.some(entry => readText(join(dirPath, entry.name)).
|
|
212
|
+
.some(entry => readText(join(dirPath, entry.name)).includes(GENERATED_RULE_HEADER));
|
|
213
213
|
} catch {
|
|
214
214
|
return false;
|
|
215
215
|
}
|
|
@@ -500,7 +500,7 @@ function listGeneratedRuleFiles(dirPath, extension) {
|
|
|
500
500
|
return readdirSync(dirPath, { withFileTypes: true })
|
|
501
501
|
.filter(entry => entry.isFile() && entry.name.endsWith(extension))
|
|
502
502
|
.map(entry => join(dirPath, entry.name))
|
|
503
|
-
.filter(filePath => readText(filePath).
|
|
503
|
+
.filter(filePath => readText(filePath).includes(GENERATED_RULE_HEADER));
|
|
504
504
|
} catch {
|
|
505
505
|
return [];
|
|
506
506
|
}
|
package/commands/init.mjs
CHANGED
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
syncWorktreeSparseCheckout,
|
|
49
49
|
findNearestWorktree,
|
|
50
50
|
} from '../git.mjs';
|
|
51
|
-
import { REGISTRY_DIR, REGISTRY_REPO, REGISTRY_URL, RULES_SOURCE_DIR, RULES_RUNTIME_DIR } from '../constants.mjs';
|
|
51
|
+
import { REGISTRY_DIR, REGISTRY_REPO, REGISTRY_URL, DOCS_SOURCE_DIR, AW_DOCS_DIR, 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));
|
|
@@ -110,8 +110,8 @@ function scaffoldNamespace(awHome, folderName) {
|
|
|
110
110
|
|
|
111
111
|
// ── Ensure ~/.aw/.git/info/exclude has the whitelist block ─────────────
|
|
112
112
|
//
|
|
113
|
-
// Strategy: only .aw_registry/, .aw_rules/, content/ are
|
|
114
|
-
// else at the top level of ~/.aw/ is local-only (telemetry/, hooks/, logs,
|
|
113
|
+
// Strategy: only .aw_registry/, .aw_rules/, content/, and .aw_docs/ are
|
|
114
|
+
// tracked — everything else at the top level of ~/.aw/ is local-only (telemetry/, hooks/, logs,
|
|
115
115
|
// .DS_Store, etc.). We write to .git/info/exclude (not tracked .gitignore)
|
|
116
116
|
// so upstream pulls never conflict.
|
|
117
117
|
|
|
@@ -125,6 +125,7 @@ const AW_MANAGED_BLOCK = [
|
|
|
125
125
|
'!/.aw_registry',
|
|
126
126
|
'!/.aw_rules',
|
|
127
127
|
'!/content',
|
|
128
|
+
'!/.aw_docs',
|
|
128
129
|
'',
|
|
129
130
|
'# Nested local state within whitelisted dirs',
|
|
130
131
|
'/.aw_registry/.sync-config.json',
|
|
@@ -325,7 +326,7 @@ export async function initCommand(args) {
|
|
|
325
326
|
const isNewSubTeam = folderName && cfg && !cfg.include.includes(folderName);
|
|
326
327
|
if (isNewSubTeam) {
|
|
327
328
|
if (!silent) fmt.logStep(`Adding sub-team ${chalk.cyan(folderName)}...`);
|
|
328
|
-
const newSparsePaths = [`.aw_registry/${folderName}`,
|
|
329
|
+
const newSparsePaths = [`.aw_registry/${folderName}`, DOCS_SOURCE_DIR, AW_DOCS_DIR, RULES_SOURCE_DIR];
|
|
329
330
|
addToSparseCheckout(AW_HOME, newSparsePaths);
|
|
330
331
|
config.addPattern(GLOBAL_AW_DIR, folderName);
|
|
331
332
|
scaffoldNamespace(AW_HOME, folderName);
|
|
@@ -464,7 +465,7 @@ export async function initCommand(args) {
|
|
|
464
465
|
}
|
|
465
466
|
|
|
466
467
|
// Determine sparse paths
|
|
467
|
-
const sparsePaths = [`.aw_registry/platform`,
|
|
468
|
+
const sparsePaths = [`.aw_registry/platform`, DOCS_SOURCE_DIR, AW_DOCS_DIR, RULES_SOURCE_DIR, `.aw_registry/AW-PROTOCOL.md`, `CODEOWNERS`];
|
|
468
469
|
if (folderName) {
|
|
469
470
|
sparsePaths.push(`.aw_registry/${folderName}`);
|
|
470
471
|
}
|
package/commands/mcp.mjs
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
|
|
3
|
+
import * as fmt from '../fmt.mjs';
|
|
4
|
+
import { chalk } from '../fmt.mjs';
|
|
5
|
+
import {
|
|
6
|
+
getMcpStatus,
|
|
7
|
+
removeMcpConfig,
|
|
8
|
+
saveMcpPreferences,
|
|
9
|
+
setupMcp,
|
|
10
|
+
} from '../mcp.mjs';
|
|
11
|
+
|
|
12
|
+
const HOME = homedir();
|
|
13
|
+
|
|
14
|
+
function isTruthyEnv(value) {
|
|
15
|
+
return ['1', 'true', 'yes', 'on'].includes(String(value || '').trim().toLowerCase());
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function mcpCommand(args) {
|
|
19
|
+
const action = String(args._positional?.[0] || 'status').toLowerCase();
|
|
20
|
+
|
|
21
|
+
if (!['status', 'enable', 'disable'].includes(action)) {
|
|
22
|
+
fmt.cancel(`Unknown MCP action: ${action}. Use: aw mcp status|enable|disable`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
fmt.intro(`aw mcp ${action}`);
|
|
27
|
+
|
|
28
|
+
if (action === 'status') {
|
|
29
|
+
return renderStatus();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (action === 'disable') {
|
|
33
|
+
saveMcpPreferences('disabled', HOME);
|
|
34
|
+
const removed = removeMcpConfig();
|
|
35
|
+
const status = getMcpStatus(HOME);
|
|
36
|
+
fmt.outro([
|
|
37
|
+
'⟁ MCP disabled',
|
|
38
|
+
'',
|
|
39
|
+
` ${chalk.green('✓')} Preference saved: ${chalk.dim(status.preferencesPath.replace(`${HOME}/`, '~/'))}`,
|
|
40
|
+
` ${chalk.green('✓')} Removed AW-managed MCP server from ${removed} config file${removed === 1 ? '' : 's'}`,
|
|
41
|
+
].join('\n'));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
saveMcpPreferences('enabled', HOME);
|
|
46
|
+
const silent = !!args['--silent'] || isTruthyEnv(process.env.CI);
|
|
47
|
+
const updatedFiles = await setupMcp(HOME, null, { silent });
|
|
48
|
+
const status = getMcpStatus(HOME);
|
|
49
|
+
if (status.effectiveMode === 'disabled') {
|
|
50
|
+
fmt.outro([
|
|
51
|
+
'⟁ MCP preference enabled',
|
|
52
|
+
'',
|
|
53
|
+
` ${chalk.green('✓')} Preference saved: ${chalk.dim(status.preferencesPath.replace(`${HOME}/`, '~/'))}`,
|
|
54
|
+
` ${chalk.yellow('!')} ${status.envDisableMcpName}=1 override is still active`,
|
|
55
|
+
].join('\n'));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
fmt.outro([
|
|
59
|
+
'⟁ MCP enabled',
|
|
60
|
+
'',
|
|
61
|
+
` ${chalk.green('✓')} Preference saved: ${chalk.dim(status.preferencesPath.replace(`${HOME}/`, '~/'))}`,
|
|
62
|
+
updatedFiles.length > 0
|
|
63
|
+
? ` ${chalk.green('✓')} Updated ${updatedFiles.length} MCP config file${updatedFiles.length === 1 ? '' : 's'}`
|
|
64
|
+
: ` ${chalk.dim('Note:')} MCP config files already up to date`,
|
|
65
|
+
].join('\n'));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function renderStatus() {
|
|
69
|
+
const status = getMcpStatus(HOME);
|
|
70
|
+
const modeLabel = status.envDisableMcp
|
|
71
|
+
? `${status.effectiveMode} (${status.envDisableMcpName}=1 override; saved preference: ${status.mode})`
|
|
72
|
+
: status.mode;
|
|
73
|
+
|
|
74
|
+
fmt.note([
|
|
75
|
+
`${chalk.dim('mode:')} ${modeLabel}`,
|
|
76
|
+
`${chalk.dim('prefs:')} ${status.preferencesPath.replace(`${HOME}/`, '~/')}`,
|
|
77
|
+
`${chalk.dim('claude MCP:')} ${formatHealth(status.claude)}`,
|
|
78
|
+
`${chalk.dim('cursor MCP:')} ${formatHealth(status.cursor)}`,
|
|
79
|
+
`${chalk.dim('codex MCP:')} ${formatHealth(status.codex)}`,
|
|
80
|
+
`${chalk.dim('aw-ecc Codex MCP source:')} ${formatHealth(status.eccCodex)}`,
|
|
81
|
+
].join('\n'), 'MCP');
|
|
82
|
+
|
|
83
|
+
fmt.outro('⟁ MCP status complete');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function formatHealth(health) {
|
|
87
|
+
if (!health.present) return 'disabled';
|
|
88
|
+
if (health.url && health.authorization) return 'enabled';
|
|
89
|
+
return `incomplete (url=${health.url}, authorization=${health.authorization})`;
|
|
90
|
+
}
|
package/commands/nuke.mjs
CHANGED
|
@@ -364,7 +364,7 @@ export async function nukeCommand(args) {
|
|
|
364
364
|
'',
|
|
365
365
|
` ${chalk.green('✓')} Generated files cleaned`,
|
|
366
366
|
` ${chalk.green('✓')} IDE symlinks cleaned`,
|
|
367
|
-
` ${chalk.green('✓')} MCP config removed (ghl-ai from
|
|
367
|
+
` ${chalk.green('✓')} MCP config removed (ghl-ai from Claude, Cursor, and Codex configs)`,
|
|
368
368
|
` ${chalk.green('✓')} aw-ecc engine removed`,
|
|
369
369
|
` ${chalk.green('✓')} Project worktrees removed`,
|
|
370
370
|
` ${chalk.green('✓')} Project symlinks cleaned`,
|
package/commands/pull.mjs
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
REGISTRY_DIR,
|
|
28
28
|
REGISTRY_URL,
|
|
29
29
|
DOCS_SOURCE_DIR,
|
|
30
|
+
AW_DOCS_DIR,
|
|
30
31
|
RULES_SOURCE_DIR,
|
|
31
32
|
RULES_RUNTIME_DIR,
|
|
32
33
|
} from '../constants.mjs';
|
|
@@ -92,7 +93,7 @@ export async function pullCommand(args) {
|
|
|
92
93
|
// Ensure platform pulls also fetch docs and rules on older installs that
|
|
93
94
|
// pre-date the new sparse-checkout paths.
|
|
94
95
|
if (input === 'platform') {
|
|
95
|
-
addToSparseCheckout(AW_HOME, [`.aw_registry/platform`, DOCS_SOURCE_DIR, RULES_SOURCE_DIR]);
|
|
96
|
+
addToSparseCheckout(AW_HOME, [`.aw_registry/platform`, DOCS_SOURCE_DIR, AW_DOCS_DIR, RULES_SOURCE_DIR]);
|
|
96
97
|
if (!cfg.include.includes('platform')) {
|
|
97
98
|
config.addPattern(GLOBAL_AW_DIR, 'platform');
|
|
98
99
|
}
|
|
@@ -110,7 +111,7 @@ export async function pullCommand(args) {
|
|
|
110
111
|
const label = input.split('/').pop();
|
|
111
112
|
if (!cfg.include.includes(input)) {
|
|
112
113
|
log.logStep(`Adding ${chalk.cyan(label)} to sparse checkout...`);
|
|
113
|
-
addToSparseCheckout(AW_HOME, [sparsePath, DOCS_SOURCE_DIR]);
|
|
114
|
+
addToSparseCheckout(AW_HOME, [sparsePath, DOCS_SOURCE_DIR, AW_DOCS_DIR]);
|
|
114
115
|
config.addPattern(GLOBAL_AW_DIR, input);
|
|
115
116
|
addedInput = input;
|
|
116
117
|
addedSparsePath = sparsePath;
|