@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.
@@ -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:GoHighLevel/aw-ecc.git";
14
- const AW_ECC_REPO_HTTPS = "https://github.com/GoHighLevel/aw-ecc.git";
15
- export const AW_ECC_TAG = "v1.4.45";
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 directories (Claude Code may not fully remove these)
479
- for (const cacheName of ["aw", "aw-marketplace"]) {
480
- const cacheDir = join(HOME, ".claude", "plugins", "cache", cacheName);
481
- if (existsSync(cacheDir)) {
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. Build in Small Steps
42
- - For multi-step work, resolve and apply \`incremental-implementation\`
43
- - Break work into thin, reversible slices with validation at each step
44
- - Prefer extending existing patterns over inventing new ones
45
- - Never batch unrelated changes in one pass
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.1",
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"