@ghl-ai/aw 0.1.44-beta.1 → 0.1.44-beta.2
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/claudePluginRegistry.mjs +142 -0
- package/c4/codexConfig.mjs +148 -0
- package/c4/codexPromptInjector.mjs +187 -0
- package/c4/commandSurface.mjs +286 -0
- package/c4/detect.mjs +62 -0
- package/c4/diagnostics.mjs +536 -0
- package/c4/eccRegistryBridge.mjs +184 -0
- package/c4/ghCli.mjs +94 -0
- package/c4/gitAuth.mjs +384 -0
- package/c4/index.mjs +64 -0
- package/c4/jsonMerge.mjs +229 -0
- package/c4/mcpServer.mjs +160 -0
- package/c4/mcpSmokeProbe.mjs +201 -0
- package/c4/preflight.mjs +254 -0
- package/c4/repoLocalClaudeSettings.mjs +54 -0
- package/c4/repoLocalIgnore.mjs +157 -0
- package/c4/repoRootInstructions.mjs +166 -0
- package/c4/secrets.mjs +55 -0
- package/c4/slimRouter.mjs +472 -0
- package/cli.mjs +7 -0
- package/commands/c4.mjs +387 -0
- package/ecc.mjs +7 -72
- package/integrate.mjs +6 -6
- package/mcp.mjs +0 -1
- package/package.json +5 -3
package/commands/c4.mjs
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commands/c4.mjs — orchestrator for `aw c4` (cloud bootstrap).
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the pilot bootstraps (`claude-web-bootstrap.sh` /
|
|
5
|
+
* `cursor-web-bootstrap.sh`) as a single 18-step Node sequence over the
|
|
6
|
+
* c4/ primitive surface. See spec.md::§"c4.mjs orchestrator contract"
|
|
7
|
+
* for the canonical list of steps and their failure modes.
|
|
8
|
+
*
|
|
9
|
+
* Two non-install modes:
|
|
10
|
+
* --dry-run stops before step 6 (no npm install, no aw init, no harness writes).
|
|
11
|
+
* --diagnose runs only steps 1, 16, 17, 18 — and ALWAYS exits 0
|
|
12
|
+
* (G7: report mode, not gate mode).
|
|
13
|
+
*
|
|
14
|
+
* Every barrel symbol used here is dependency-injected via the second
|
|
15
|
+
* argument so the test in tests/c4/c4.command.test.mjs can exercise the
|
|
16
|
+
* decision branches without spawning real subprocesses, touching the
|
|
17
|
+
* real filesystem, or hitting the real network. The default deps come
|
|
18
|
+
* from c4/index.mjs (the production wiring).
|
|
19
|
+
*
|
|
20
|
+
* Contract: spec.md::§"c4.mjs orchestrator contract", tasks.md::4.4.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { spawnSync as nodeSpawnSync } from 'node:child_process';
|
|
24
|
+
import { existsSync as fsExistsSync } from 'node:fs';
|
|
25
|
+
import { join } from 'node:path';
|
|
26
|
+
import * as c4Default from '../c4/index.mjs';
|
|
27
|
+
|
|
28
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
29
|
+
* Constants — referenced by self-tests.
|
|
30
|
+
* ───────────────────────────────────────────────────────────────────────── */
|
|
31
|
+
|
|
32
|
+
const MIN_VIEWABLE_STAGE_SKILLS = 3;
|
|
33
|
+
const SUPPORTED_HARNESSES = ['cursor-cloud', 'codex-web', 'claude-web', 'unknown'];
|
|
34
|
+
|
|
35
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
36
|
+
* Argument parsing — shared by Cli wrapper and direct callers.
|
|
37
|
+
*
|
|
38
|
+
* `args` may arrive either as the cli.mjs-parsed object (`{ '--dry-run': true,
|
|
39
|
+
* '--harness': 'codex-web', ... }`) or as a plain `string[]`. Normalize.
|
|
40
|
+
* ───────────────────────────────────────────────────────────────────────── */
|
|
41
|
+
|
|
42
|
+
function normalizeArgs(input) {
|
|
43
|
+
if (Array.isArray(input)) {
|
|
44
|
+
const out = { _positional: [] };
|
|
45
|
+
for (let i = 0; i < input.length; i++) {
|
|
46
|
+
const a = input[i];
|
|
47
|
+
if (a === '--dry-run' || a === '-v' || a === '--verbose' || a === '--diagnose' || a === '--skip-pull') {
|
|
48
|
+
out[a] = true;
|
|
49
|
+
} else if (a === '--harness') {
|
|
50
|
+
out['--harness'] = input[i + 1];
|
|
51
|
+
i++;
|
|
52
|
+
} else if (a.startsWith('--')) {
|
|
53
|
+
out[a] = true;
|
|
54
|
+
} else {
|
|
55
|
+
out._positional.push(a);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
return input ?? {};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
64
|
+
* IO defaults — easy to override in tests.
|
|
65
|
+
* ───────────────────────────────────────────────────────────────────────── */
|
|
66
|
+
|
|
67
|
+
const DEFAULT_WRITER = {
|
|
68
|
+
stdout: (s) => process.stdout.write(s),
|
|
69
|
+
stderr: (s) => process.stderr.write(s),
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
function defaultExit(code) {
|
|
73
|
+
process.exit(code);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
77
|
+
* Helpers — pure utility, no orchestration state.
|
|
78
|
+
* ───────────────────────────────────────────────────────────────────────── */
|
|
79
|
+
|
|
80
|
+
function safe(label, fn, writer) {
|
|
81
|
+
try {
|
|
82
|
+
return { ok: true, value: fn() };
|
|
83
|
+
} catch (err) {
|
|
84
|
+
writer.stderr(`[aw-c4] ${label} failed (non-fatal): ${err?.message ?? err}\n`);
|
|
85
|
+
return { ok: false, error: err };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function safeAsync(label, fn, writer) {
|
|
90
|
+
try {
|
|
91
|
+
return { ok: true, value: await fn() };
|
|
92
|
+
} catch (err) {
|
|
93
|
+
writer.stderr(`[aw-c4] ${label} failed (non-fatal): ${err?.message ?? err}\n`);
|
|
94
|
+
return { ok: false, error: err };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
99
|
+
* 6-step git auth subroutine (step 4 + step 8).
|
|
100
|
+
* ───────────────────────────────────────────────────────────────────────── */
|
|
101
|
+
|
|
102
|
+
function setupGitAuth({ token, c4, home, cwd }) {
|
|
103
|
+
c4.clearAllGitHubAuthState({ home });
|
|
104
|
+
c4.installPatInsteadOf(token);
|
|
105
|
+
c4.installCredentialHelperStore(token, home);
|
|
106
|
+
c4.ghAuthLoginWithToken(token);
|
|
107
|
+
c4.ghAuthSetupGit();
|
|
108
|
+
c4.ensureOriginRemote({ cwd });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
112
|
+
* Per-harness branch (step 10).
|
|
113
|
+
*
|
|
114
|
+
* Returns `{ ok, bridge, injector }` so the summary line can pick up
|
|
115
|
+
* codex-specific status ticks. Hard failures (bridge / injector / claude
|
|
116
|
+
* marketplace / slim card) bubble up as thrown Errors.
|
|
117
|
+
* ───────────────────────────────────────────────────────────────────────── */
|
|
118
|
+
|
|
119
|
+
function runHarnessBranch({ harness, c4, home, eccHome, cwd, writer }) {
|
|
120
|
+
if (harness === 'claude-web') {
|
|
121
|
+
// Marketplace registration is non-fatal — the slim card is the routing
|
|
122
|
+
// mechanism (state.json::resolved_decisions exit-code item 3 lists slim
|
|
123
|
+
// card only); marketplace failure must not exit 1.
|
|
124
|
+
safe('ensureClaudeMarketplace', () => c4.ensureClaudeMarketplace(home, eccHome), writer);
|
|
125
|
+
c4.installSlimRouter('claude-web', home);
|
|
126
|
+
safe('ensureRepoLocalClaudeSettings', () => c4.ensureRepoLocalClaudeSettings(cwd), writer);
|
|
127
|
+
return { ok: true };
|
|
128
|
+
}
|
|
129
|
+
if (harness === 'cursor-cloud' || harness === 'unknown') {
|
|
130
|
+
// Treat unknown as cursor-cloud (per spec.md::§"Harness detect"): the
|
|
131
|
+
// slim card with Read-syntax is the safe fallback for unrecognized
|
|
132
|
+
// harnesses.
|
|
133
|
+
c4.installSlimRouter('cursor-cloud', home);
|
|
134
|
+
return { ok: true };
|
|
135
|
+
}
|
|
136
|
+
if (harness === 'codex-web') {
|
|
137
|
+
const awHome = join(home, '.aw');
|
|
138
|
+
c4.applyEccRegistryBridge({ awHome, eccHome });
|
|
139
|
+
c4.ensureCodexHooksFlag(home);
|
|
140
|
+
const routerSkillPath = join(awHome, '.aw_registry/platform/core/skills/using-aw-skills/SKILL.md');
|
|
141
|
+
// Pass hookPath explicitly: defaultHookPath() reads os.homedir() at module
|
|
142
|
+
// load time, which ignores the orchestrator's `home` (env.HOME). Without
|
|
143
|
+
// this, installs land in the real ~/.codex even when HOME is overridden
|
|
144
|
+
// (caught by tests/c4/c4.smoke.test.mjs::"codex-web happy path").
|
|
145
|
+
const hookPath = join(home, '.codex/hooks/aw-user-prompt-submit.sh');
|
|
146
|
+
c4.installCodexPromptInjector({ hookPath, routerSkillPath });
|
|
147
|
+
return { ok: true, bridge: { ok: true }, injector: { ok: true } };
|
|
148
|
+
}
|
|
149
|
+
throw new Error(`unknown harness: ${harness}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
153
|
+
* Self-tests (step 16). Returns { ok, failures: string[] }.
|
|
154
|
+
*
|
|
155
|
+
* In install mode the orchestrator exits 1 on `ok=false`. In diagnose mode
|
|
156
|
+
* the report is always emitted and the exit is forced to 0.
|
|
157
|
+
* ───────────────────────────────────────────────────────────────────────── */
|
|
158
|
+
|
|
159
|
+
function runSelfTests({ harness, c4, home, awHome, eccHome }) {
|
|
160
|
+
const failures = [];
|
|
161
|
+
|
|
162
|
+
const skill = c4.diagnoseSkillResolution({ harness, home, awHome, eccHome });
|
|
163
|
+
if (!skill.ok) failures.push('skill-resolution');
|
|
164
|
+
|
|
165
|
+
// diagnoseCommandResolution is warn-only — does NOT add to failures.
|
|
166
|
+
const command = c4.diagnoseCommandResolution({ harness, home });
|
|
167
|
+
|
|
168
|
+
let view;
|
|
169
|
+
let injector;
|
|
170
|
+
if (harness === 'codex-web') {
|
|
171
|
+
view = c4.diagnoseAwRouterView({ awHome });
|
|
172
|
+
if (view.enumerableSkills.length < MIN_VIEWABLE_STAGE_SKILLS) failures.push('aw-router-view');
|
|
173
|
+
injector = c4.diagnosePromptRouterInjection(home, { simulateFirstTurn: true });
|
|
174
|
+
if (!injector.ok) failures.push('prompt-injector');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return { ok: failures.length === 0, failures, skill, command, view, injector };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
181
|
+
* c4Command — public entrypoint.
|
|
182
|
+
*
|
|
183
|
+
* The second argument is for tests + advanced callers; production usage
|
|
184
|
+
* goes through cli.mjs which calls `c4Command(args)` (one-arg form) and
|
|
185
|
+
* the defaults take over.
|
|
186
|
+
* ───────────────────────────────────────────────────────────────────────── */
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @param {object|string[]} rawArgs
|
|
190
|
+
* @param {object} [overrides]
|
|
191
|
+
* @param {object} [overrides.c4] Override the entire c4 barrel.
|
|
192
|
+
* @param {Function} [overrides.spawnSync]
|
|
193
|
+
* @param {object} [overrides.fs] `{ existsSync }` shape.
|
|
194
|
+
* @param {object} [overrides.writer] `{ stdout, stderr }` write fns.
|
|
195
|
+
* @param {Function} [overrides.exit]
|
|
196
|
+
* @param {NodeJS.ProcessEnv} [overrides.env]
|
|
197
|
+
* @param {string} [overrides.cwd]
|
|
198
|
+
* @param {Function} [overrides.now] Returns ms epoch — for deterministic durations in tests.
|
|
199
|
+
*/
|
|
200
|
+
export async function c4Command(rawArgs, overrides = {}) {
|
|
201
|
+
const args = normalizeArgs(rawArgs);
|
|
202
|
+
const c4 = overrides.c4 ?? c4Default;
|
|
203
|
+
const spawnSync = overrides.spawnSync ?? nodeSpawnSync;
|
|
204
|
+
const fs = overrides.fs ?? { existsSync: fsExistsSync };
|
|
205
|
+
const writer = overrides.writer ?? DEFAULT_WRITER;
|
|
206
|
+
const exit = overrides.exit ?? defaultExit;
|
|
207
|
+
const env = overrides.env ?? process.env;
|
|
208
|
+
const cwd = overrides.cwd ?? process.cwd();
|
|
209
|
+
const now = overrides.now ?? (() => Date.now());
|
|
210
|
+
|
|
211
|
+
const startTs = now();
|
|
212
|
+
|
|
213
|
+
// Step 1 — harness detection (override wins).
|
|
214
|
+
const cliHarness = args['--harness'];
|
|
215
|
+
const harness = SUPPORTED_HARNESSES.includes(cliHarness) ? cliHarness : c4.detectHarness({ env }).harness;
|
|
216
|
+
const home = env.HOME ?? '~';
|
|
217
|
+
const awHome = join(home, '.aw');
|
|
218
|
+
const awRegistry = join(awHome, '.aw_registry');
|
|
219
|
+
const eccHome = join(home, '.aw-ecc');
|
|
220
|
+
|
|
221
|
+
// Step 2 — token resolution.
|
|
222
|
+
const { token, source: tokenSource } = c4.getGithubToken(env);
|
|
223
|
+
if (!token) {
|
|
224
|
+
writer.stdout(c4.summarizeAsOneLine({
|
|
225
|
+
harness,
|
|
226
|
+
hasToken: false,
|
|
227
|
+
tokenSource: null,
|
|
228
|
+
authOk: false,
|
|
229
|
+
didInit: false,
|
|
230
|
+
durationMs: now() - startTs,
|
|
231
|
+
}) + '\n');
|
|
232
|
+
return exit(0);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// --diagnose: skip ahead to self-tests + dump + summary.
|
|
236
|
+
// G7 contract — ALWAYS exit 0, even on unexpected throws inside the
|
|
237
|
+
// diagnostic probes. The whole branch body is wrapped in try/catch so a
|
|
238
|
+
// throw from runSelfTests / dumpPostInitState / summarizeAsOneLine does
|
|
239
|
+
// not leak as a process-level non-zero exit.
|
|
240
|
+
if (args['--diagnose']) {
|
|
241
|
+
let self = {
|
|
242
|
+
ok: false,
|
|
243
|
+
failures: ['diagnose-error'],
|
|
244
|
+
skill: null,
|
|
245
|
+
command: null,
|
|
246
|
+
view: undefined,
|
|
247
|
+
injector: undefined,
|
|
248
|
+
};
|
|
249
|
+
try {
|
|
250
|
+
self = runSelfTests({ harness, c4, home, awHome, eccHome });
|
|
251
|
+
c4.dumpPostInitState({ harness, cwd, awHome, configPaths: [] });
|
|
252
|
+
} catch (err) {
|
|
253
|
+
writer.stderr(`[aw-c4] diagnose: unexpected error: ${err?.message ?? err}\n`);
|
|
254
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
writer.stdout(c4.summarizeAsOneLine({
|
|
257
|
+
harness,
|
|
258
|
+
hasToken: true,
|
|
259
|
+
tokenSource,
|
|
260
|
+
authOk: null, // diagnose does not probe auth — null avoids the misleading "GITHUB_PAT ✗" tick.
|
|
261
|
+
didInit: false,
|
|
262
|
+
durationMs: now() - startTs,
|
|
263
|
+
bridge: self.view ? { ok: self.view.ok } : undefined,
|
|
264
|
+
injector: self.injector ? { ok: self.injector.ok } : undefined,
|
|
265
|
+
diagnose: true,
|
|
266
|
+
}) + '\n');
|
|
267
|
+
} catch (err) {
|
|
268
|
+
writer.stderr(`[aw-c4] diagnose: summary line failed: ${err?.message ?? err}\n`);
|
|
269
|
+
}
|
|
270
|
+
if (!self.ok) {
|
|
271
|
+
writer.stderr(`[aw-c4] diagnose: failures = ${self.failures.join(', ')}\n`);
|
|
272
|
+
}
|
|
273
|
+
return exit(0); // G7: --diagnose ALWAYS exits 0.
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Step 3 — gh CLI.
|
|
277
|
+
safe('ensureGhCli', () => c4.ensureGhCli(), writer);
|
|
278
|
+
|
|
279
|
+
// Step 4 — 6-step git auth.
|
|
280
|
+
safe('setupGitAuth', () => setupGitAuth({ token, c4, home, cwd }), writer);
|
|
281
|
+
|
|
282
|
+
// Step 5 — preflight.
|
|
283
|
+
const preflight = await c4.preflightPlatformDocs(token);
|
|
284
|
+
if (preflight.diagnosis === 'pat-invalid') {
|
|
285
|
+
writer.stderr('[aw-c4] PAT invalid (REST 4xx). Refresh GITHUB_PAT and retry.\n');
|
|
286
|
+
writer.stdout(c4.summarizeAsOneLine({
|
|
287
|
+
harness,
|
|
288
|
+
hasToken: true,
|
|
289
|
+
tokenSource,
|
|
290
|
+
authOk: false,
|
|
291
|
+
didInit: false,
|
|
292
|
+
durationMs: now() - startTs,
|
|
293
|
+
}) + '\n');
|
|
294
|
+
return exit(0);
|
|
295
|
+
}
|
|
296
|
+
if (preflight.diagnosis !== 'ok') {
|
|
297
|
+
writer.stderr(`[aw-c4] preflight diagnosis: ${preflight.diagnosis} (continuing)\n`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// --dry-run: stop before any install side effect.
|
|
301
|
+
if (args['--dry-run']) {
|
|
302
|
+
writer.stdout(c4.summarizeAsOneLine({
|
|
303
|
+
harness,
|
|
304
|
+
hasToken: true,
|
|
305
|
+
tokenSource,
|
|
306
|
+
authOk: preflight.diagnosis === 'ok',
|
|
307
|
+
didInit: false,
|
|
308
|
+
durationMs: now() - startTs,
|
|
309
|
+
}) + '\n');
|
|
310
|
+
return exit(0);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Step 6 — npm install -g @ghl-ai/aw.
|
|
314
|
+
const npmRes = spawnSync('npm', ['install', '-g', '@ghl-ai/aw'], { stdio: 'pipe' });
|
|
315
|
+
if (npmRes && npmRes.status !== 0) {
|
|
316
|
+
writer.stderr('[aw-c4] npm install -g @ghl-ai/aw failed (non-fatal); using existing aw if present\n');
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Step 7 — aw init --silent.
|
|
320
|
+
const initRes = spawnSync('aw', ['init', '--silent'], { stdio: 'pipe' });
|
|
321
|
+
if (initRes && initRes.status !== 0) {
|
|
322
|
+
writer.stderr('[aw-c4] aw init failed; ECC verification will catch any missing artefacts\n');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Step 8 — re-apply git auth post-init.
|
|
326
|
+
safe('setupGitAuth(post-init)', () => setupGitAuth({ token, c4, home, cwd }), writer);
|
|
327
|
+
|
|
328
|
+
// Step 9 — verify ECC + registry provisioned.
|
|
329
|
+
if (!fs.existsSync(eccHome) || !fs.existsSync(awRegistry)) {
|
|
330
|
+
writer.stderr('[aw-c4] FATAL: aw init did not provision ~/.aw-ecc or ~/.aw/.aw_registry\n');
|
|
331
|
+
return exit(1);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Step 10 — per-harness branch.
|
|
335
|
+
let branch;
|
|
336
|
+
try {
|
|
337
|
+
branch = runHarnessBranch({ harness, c4, home, eccHome, cwd, writer });
|
|
338
|
+
} catch (err) {
|
|
339
|
+
writer.stderr(`[aw-c4] FATAL: harness branch (${harness}) failed: ${err?.message ?? err}\n`);
|
|
340
|
+
return exit(1);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Step 11 — MCP register.
|
|
344
|
+
safe('registerGhlAiMcp', () => c4.registerGhlAiMcp(harness, home, token), writer);
|
|
345
|
+
|
|
346
|
+
// Step 12 — slash command surface.
|
|
347
|
+
safe('ensureCommandSurface', () => c4.ensureCommandSurface({ harness, home, eccHome }), writer);
|
|
348
|
+
|
|
349
|
+
// Step 13 — repo-root context.
|
|
350
|
+
safe('copyRepoRootInstructions', () => c4.copyRepoRootInstructions(harness, cwd, eccHome), writer);
|
|
351
|
+
|
|
352
|
+
// Step 14 — repo-local ignore.
|
|
353
|
+
safe('ensureRepoLocalIgnore', () => c4.ensureRepoLocalIgnore({ cwd, harness }), writer);
|
|
354
|
+
|
|
355
|
+
// Step 15 — MCP smoke probe (best-effort).
|
|
356
|
+
const mcpProbe = await safeAsync(
|
|
357
|
+
'probeMcpServer',
|
|
358
|
+
() => c4.probeMcpServer({ url: c4.MCP_URL_DEFAULT, token }),
|
|
359
|
+
writer,
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
// Step 16 — self-tests.
|
|
363
|
+
const self = runSelfTests({ harness, c4, home, awHome, eccHome });
|
|
364
|
+
if (!self.ok) {
|
|
365
|
+
writer.stderr(`[aw-c4] FATAL: self-tests failed: ${self.failures.join(', ')}\n`);
|
|
366
|
+
c4.dumpPostInitState({ harness, cwd, awHome, configPaths: [] });
|
|
367
|
+
return exit(1);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Step 17 — post-init dump.
|
|
371
|
+
c4.dumpPostInitState({ harness, cwd, awHome, configPaths: [] });
|
|
372
|
+
|
|
373
|
+
// Step 18 — one-line summary.
|
|
374
|
+
writer.stdout(c4.summarizeAsOneLine({
|
|
375
|
+
harness,
|
|
376
|
+
hasToken: true,
|
|
377
|
+
tokenSource,
|
|
378
|
+
authOk: preflight.diagnosis === 'ok',
|
|
379
|
+
didInit: true,
|
|
380
|
+
durationMs: now() - startTs,
|
|
381
|
+
bridge: branch?.bridge,
|
|
382
|
+
injector: branch?.injector,
|
|
383
|
+
mcpProbe: mcpProbe?.value?.authStatus,
|
|
384
|
+
}) + '\n');
|
|
385
|
+
|
|
386
|
+
return exit(0);
|
|
387
|
+
}
|
package/ecc.mjs
CHANGED
|
@@ -10,9 +10,9 @@ import { homedir } from "node:os";
|
|
|
10
10
|
import * as fmt from "./fmt.mjs";
|
|
11
11
|
import { applyStoredStartupPreferences } from "./startup.mjs";
|
|
12
12
|
|
|
13
|
-
const AW_ECC_REPO_SSH = "git@github.com:
|
|
14
|
-
const AW_ECC_REPO_HTTPS = "https://github.com/
|
|
15
|
-
export const AW_ECC_TAG = "v1.4.
|
|
13
|
+
const AW_ECC_REPO_SSH = "git@github.com:shreyansh-ghl/aw-ecc.git";
|
|
14
|
+
const AW_ECC_REPO_HTTPS = "https://github.com/shreyansh-ghl/aw-ecc.git";
|
|
15
|
+
export const AW_ECC_TAG = "v1.4.43";
|
|
16
16
|
|
|
17
17
|
const MARKETPLACE_NAME = "aw-marketplace";
|
|
18
18
|
const PLUGIN_KEY = `aw@${MARKETPLACE_NAME}`;
|
|
@@ -475,75 +475,10 @@ export function uninstallAwEcc({ silent = false } = {}) {
|
|
|
475
475
|
} catch { /* best effort */ }
|
|
476
476
|
}
|
|
477
477
|
|
|
478
|
-
// Clean leftover plugin cache
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
try { rmSync(cacheDir, { recursive: true, force: true }); removed++; } catch { /* best effort */ }
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// Clean rules copied by session-start hook (plugin-only users)
|
|
487
|
-
const pluginRulesDir = join(HOME, ".claude", "rules", "platform");
|
|
488
|
-
if (existsSync(pluginRulesDir)) {
|
|
489
|
-
try { rmSync(pluginRulesDir, { recursive: true, force: true }); removed++; } catch { /* best effort */ }
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// Clean commands created by docsregistry sync (all IDEs)
|
|
493
|
-
for (const cmdDir of [
|
|
494
|
-
join(HOME, ".claude", "commands", "aw"),
|
|
495
|
-
join(HOME, ".cursor", "commands", "aw"),
|
|
496
|
-
]) {
|
|
497
|
-
if (existsSync(cmdDir)) {
|
|
498
|
-
try { rmSync(cmdDir, { recursive: true, force: true }); removed++; } catch { /* best effort */ }
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
// Strip aw-managed content from ~/.claude/CLAUDE.md (keep user content above the marker)
|
|
503
|
-
const claudeMdPath = join(HOME, ".claude", "CLAUDE.md");
|
|
504
|
-
if (existsSync(claudeMdPath)) {
|
|
505
|
-
try {
|
|
506
|
-
const content = readFileSync(claudeMdPath, "utf8");
|
|
507
|
-
const marker = "<!-- aw-managed:";
|
|
508
|
-
const idx = content.indexOf(marker);
|
|
509
|
-
if (idx !== -1) {
|
|
510
|
-
const userContent = content.slice(0, idx).trimEnd();
|
|
511
|
-
if (userContent.length > 0) {
|
|
512
|
-
writeFileSync(claudeMdPath, userContent + "\n");
|
|
513
|
-
} else {
|
|
514
|
-
rmSync(claudeMdPath, { force: true });
|
|
515
|
-
}
|
|
516
|
-
removed++;
|
|
517
|
-
}
|
|
518
|
-
} catch { /* best effort */ }
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// Clean Codex aw hooks
|
|
522
|
-
const codexHooksDir = join(HOME, ".codex", "hooks");
|
|
523
|
-
if (existsSync(codexHooksDir)) {
|
|
524
|
-
try {
|
|
525
|
-
for (const file of readdirSync(codexHooksDir)) {
|
|
526
|
-
if (file.startsWith("aw-") || file.startsWith("session-end-extract") || file.startsWith("session-stop-marker")) {
|
|
527
|
-
rmSync(join(codexHooksDir, file), { force: true });
|
|
528
|
-
removed++;
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
pruneEmptyParents(codexHooksDir, join(HOME, ".codex"));
|
|
532
|
-
} catch { /* best effort */ }
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
// Clean Codex ecc backup directories
|
|
536
|
-
const codexBackupsDir = join(HOME, ".codex", "backups");
|
|
537
|
-
if (existsSync(codexBackupsDir)) {
|
|
538
|
-
try {
|
|
539
|
-
for (const dir of readdirSync(codexBackupsDir)) {
|
|
540
|
-
if (dir.startsWith("ecc-")) {
|
|
541
|
-
rmSync(join(codexBackupsDir, dir), { recursive: true, force: true });
|
|
542
|
-
removed++;
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
pruneEmptyParents(codexBackupsDir, join(HOME, ".codex"));
|
|
546
|
-
} catch { /* best effort */ }
|
|
478
|
+
// Clean leftover manual plugin cache from older versions
|
|
479
|
+
const oldCache = join(HOME, ".claude", "plugins", "cache", "aw");
|
|
480
|
+
if (existsSync(oldCache)) {
|
|
481
|
+
try { rmSync(oldCache, { recursive: true, force: true }); removed++; } catch { /* best effort */ }
|
|
547
482
|
}
|
|
548
483
|
|
|
549
484
|
// Remove permanent aw-ecc repo clone — use rm -rf (more reliable than rmSync on git repos)
|
package/integrate.mjs
CHANGED
|
@@ -19,7 +19,7 @@ const AW_ROUTER_BRIDGE_END_MARKER = '<!-- aw-managed:end router-bridge -->';
|
|
|
19
19
|
const AGENTS_RULES_HEADER = 'Platform Rules — Non-Negotiables';
|
|
20
20
|
const CLAUDE_RULES_HEADER = 'Platform Rules (MUST)';
|
|
21
21
|
|
|
22
|
-
function generateAwRouterBridgeSection() {
|
|
22
|
+
export function generateAwRouterBridgeSection() {
|
|
23
23
|
return `${AW_ROUTER_BRIDGE_START_MARKER}
|
|
24
24
|
## ${AW_ROUTER_BRIDGE_HEADER}
|
|
25
25
|
|
|
@@ -38,11 +38,11 @@ function generateAwRouterBridgeSection() {
|
|
|
38
38
|
- For non-trivial work, inspect platform docs, runtime behavior, app surfaces, and integration boundaries first
|
|
39
39
|
- Ask one short clarifying question only when ambiguity remains after exploration
|
|
40
40
|
|
|
41
|
-
### 4.
|
|
42
|
-
- For multi-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
41
|
+
### 4. Route New Work via /aw:plan
|
|
42
|
+
- For new endpoints, services, schemas, integrations, multi-file changes, or unclear scope, route via \`/aw:plan\` before writing code
|
|
43
|
+
- Do not jump to \`incremental-implementation\` (or any cross-cutting skill) without first selecting the AW stage skill via \`using-aw-skills\`
|
|
44
|
+
- Cross-cutting skills are loaded by the stage skill when the stage skill calls for them, not directly
|
|
45
|
+
- Break work into thin reversible slices once the stage skill is chosen
|
|
46
46
|
|
|
47
47
|
### 5. Verify With Proof
|
|
48
48
|
- For bug fixes and behavior changes, use Red -> Green -> Refactor
|
package/mcp.mjs
CHANGED
|
@@ -249,7 +249,6 @@ async function resolveClickUpToken(silent = false, cwd = process.cwd()) {
|
|
|
249
249
|
return token;
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
|
|
253
252
|
/**
|
|
254
253
|
* Setup MCP configs globally for Claude Code and Cursor.
|
|
255
254
|
* Merges ghl-ai server into existing configs without overwriting other servers.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghl-ai/aw",
|
|
3
|
-
"version": "0.1.44-beta.
|
|
3
|
+
"version": "0.1.44-beta.2",
|
|
4
4
|
"description": "Agentic Workspace CLI — pull, push & manage agents, skills and commands from the registry",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"bin.js",
|
|
11
|
+
"c4/",
|
|
11
12
|
"cli.mjs",
|
|
12
13
|
"commands/",
|
|
13
14
|
"config.mjs",
|
|
@@ -50,9 +51,9 @@
|
|
|
50
51
|
"license": "MIT",
|
|
51
52
|
"scripts": {
|
|
52
53
|
"test": "yarn test:vitest && yarn test:node",
|
|
53
|
-
"test:vitest": "vitest run --reporter=verbose tests/commands tests/mcp.test.mjs tests/telemetry.test.mjs",
|
|
54
|
+
"test:vitest": "vitest run --reporter=verbose tests/commands tests/mcp.test.mjs tests/telemetry.test.mjs tests/c4",
|
|
54
55
|
"test:node": "node tests/run-node-tests.mjs",
|
|
55
|
-
"test:watch": "vitest --reporter=verbose tests/commands tests/mcp.test.mjs tests/telemetry.test.mjs",
|
|
56
|
+
"test:watch": "vitest --reporter=verbose tests/commands tests/mcp.test.mjs tests/telemetry.test.mjs tests/c4",
|
|
56
57
|
"preuninstall": "node bin.js nuke 2>/dev/null || true"
|
|
57
58
|
},
|
|
58
59
|
"publishConfig": {
|
|
@@ -60,6 +61,7 @@
|
|
|
60
61
|
},
|
|
61
62
|
"dependencies": {
|
|
62
63
|
"@clack/prompts": "0.8.2",
|
|
64
|
+
"@iarna/toml": "^2.2.5",
|
|
63
65
|
"chalk": "^5.6.2",
|
|
64
66
|
"figlet": "^1.11.0",
|
|
65
67
|
"pg": "^8.13.0"
|