@ghl-ai/aw 0.1.38-beta.9 → 0.1.39-beta.1
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 +0 -7
- package/commands/init.mjs +5 -31
- package/commands/link-project.mjs +1 -12
- package/commands/nuke.mjs +4 -14
- package/commands/pull.mjs +4 -83
- package/commands/push.mjs +4 -4
- package/commands/search.mjs +1 -1
- package/commands/telemetry.mjs +3 -59
- package/ecc.mjs +18 -94
- package/integrate.mjs +20 -171
- package/link.mjs +1 -36
- package/package.json +2 -8
- package/paths.mjs +1 -1
- package/registry.mjs +1 -1
- package/render-rules.mjs +8 -48
- package/codex.mjs +0 -839
- package/commands/doctor.mjs +0 -1085
- package/commands/startup.mjs +0 -87
- package/hook-manifest.mjs +0 -195
- package/hooks/codex-home.mjs +0 -184
- package/hooks/shared-phase-scripts.mjs +0 -86
- package/startup.mjs +0 -562
package/cli.mjs
CHANGED
|
@@ -18,9 +18,6 @@ const COMMANDS = {
|
|
|
18
18
|
'push-rules': () => import('./commands/push-rules.mjs').then(m => m.pushRulesCommand),
|
|
19
19
|
drop: () => import('./commands/drop.mjs').then(m => m.dropCommand),
|
|
20
20
|
status: () => import('./commands/status.mjs').then(m => m.statusCommand),
|
|
21
|
-
doctor: () => import('./commands/doctor.mjs').then(m => m.doctorCommand),
|
|
22
|
-
routing: () => import('./commands/startup.mjs').then(m => m.routingCommand),
|
|
23
|
-
startup: () => import('./commands/startup.mjs').then(m => m.startupCommand),
|
|
24
21
|
search: () => import('./commands/search.mjs').then(m => m.searchCommand),
|
|
25
22
|
link: () => import('./commands/link-project.mjs').then(m => m.linkProjectCommand),
|
|
26
23
|
nuke: () => import('./commands/nuke.mjs').then(m => m.nukeCommand),
|
|
@@ -100,11 +97,7 @@ function printHelp() {
|
|
|
100
97
|
|
|
101
98
|
sec('Manage'),
|
|
102
99
|
cmd('aw status', 'Show synced paths, modified files & conflicts'),
|
|
103
|
-
cmd('aw doctor', 'Run a health check for routing, MCP, plugin, and AW ECC surfaces'),
|
|
104
100
|
cmd('aw link', 'Link current project as a git worktree (wires IDE symlinks)'),
|
|
105
|
-
cmd('aw routing status', 'Show global AW session-routing mode for Claude/Cursor/Codex'),
|
|
106
|
-
cmd('aw routing disable', 'Disable automatic AW session routing globally'),
|
|
107
|
-
cmd('aw routing enable', 'Re-enable automatic AW session routing globally'),
|
|
108
101
|
cmd('aw drop <path>', 'Stop syncing or delete local content'),
|
|
109
102
|
cmd('aw nuke', 'Remove entire .aw_registry/ & start fresh'),
|
|
110
103
|
cmd('aw daemon install', 'Auto-pull on a schedule (macOS launchd / Linux cron)'),
|
package/commands/init.mjs
CHANGED
|
@@ -23,14 +23,12 @@ import * as config from '../config.mjs';
|
|
|
23
23
|
import * as fmt from '../fmt.mjs';
|
|
24
24
|
import { chalk } from '../fmt.mjs';
|
|
25
25
|
import { linkWorkspace } from '../link.mjs';
|
|
26
|
-
import { generateCommands, copyInstructions, initAwDocs
|
|
26
|
+
import { generateCommands, copyInstructions, initAwDocs } from '../integrate.mjs';
|
|
27
27
|
import { setupMcp } from '../mcp.mjs';
|
|
28
|
-
import { applyStoredStartupPreferences, ensureAwRuntimeHook } from '../startup.mjs';
|
|
29
28
|
import { installLocalCommitHook } from '../hooks.mjs';
|
|
30
29
|
import { autoUpdate, promptUpdate } from '../update.mjs';
|
|
31
30
|
import { installGlobalHooks } from '../hooks.mjs';
|
|
32
31
|
import { installAwEcc } from '../ecc.mjs';
|
|
33
|
-
import { removeWorkspaceHookDefaults } from '../codex.mjs';
|
|
34
32
|
import {
|
|
35
33
|
initPersistentClone,
|
|
36
34
|
isValidClone,
|
|
@@ -59,19 +57,6 @@ const HOME = (() => { try { return realpathSync(_rawHome); } catch { return _raw
|
|
|
59
57
|
const GLOBAL_AW_DIR = join(HOME, '.aw_registry');
|
|
60
58
|
const AW_HOME = join(HOME, '.aw');
|
|
61
59
|
|
|
62
|
-
function syncInstructionsAndAwDocs(targetDir, namespace) {
|
|
63
|
-
copyInstructions(targetDir, null, namespace);
|
|
64
|
-
initAwDocs(targetDir);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function syncHomeAndProjectInstructions(cwd, namespace) {
|
|
68
|
-
syncHomeHarnessInstructions(HOME);
|
|
69
|
-
initAwDocs(HOME);
|
|
70
|
-
if (cwd !== HOME) {
|
|
71
|
-
syncInstructionsAndAwDocs(cwd, namespace);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
60
|
// ── Ensure ~/.aw/.gitignore has personal/local entries ───────────────────
|
|
76
61
|
|
|
77
62
|
const AW_GITIGNORE_ENTRIES = [
|
|
@@ -275,11 +260,9 @@ export async function initCommand(args) {
|
|
|
275
260
|
}
|
|
276
261
|
|
|
277
262
|
await installAwEcc(cwd, { silent });
|
|
278
|
-
|
|
279
|
-
|
|
263
|
+
copyInstructions(HOME, null, freshCfg?.namespace || team) || [];
|
|
264
|
+
initAwDocs(HOME);
|
|
280
265
|
await setupMcp(HOME, freshCfg?.namespace || team, { silent });
|
|
281
|
-
applyStoredStartupPreferences(HOME);
|
|
282
|
-
const removedLegacyStartupFiles = cwd !== HOME ? removeWorkspaceHookDefaults(cwd) : [];
|
|
283
266
|
installGlobalHooks();
|
|
284
267
|
|
|
285
268
|
// Remove old local .git/hooks/post-checkout that pre-dates core.hooksPath (creates stale .aw_registry symlink)
|
|
@@ -321,9 +304,6 @@ export async function initCommand(args) {
|
|
|
321
304
|
'',
|
|
322
305
|
` ${chalk.green('✓')} Registry synced`,
|
|
323
306
|
` ${chalk.green('✓')} IDE refreshed — ${chalk.bold(symlinks)} symlinks · ${chalk.bold(commands)} commands`,
|
|
324
|
-
removedLegacyStartupFiles.length > 0
|
|
325
|
-
? ` ${chalk.green('✓')} Removed ${removedLegacyStartupFiles.length} legacy repo startup file${removedLegacyStartupFiles.length > 1 ? 's' : ''}`
|
|
326
|
-
: null,
|
|
327
307
|
cwd !== HOME && isWorktree(join(cwd, '.aw')) ? ` ${chalk.green('✓')} Project linked` : null,
|
|
328
308
|
].filter(Boolean).join('\n'));
|
|
329
309
|
}
|
|
@@ -410,11 +390,9 @@ export async function initCommand(args) {
|
|
|
410
390
|
|
|
411
391
|
// Step 3: Setup tasks, MCP, hooks
|
|
412
392
|
await installAwEcc(cwd, { silent });
|
|
413
|
-
|
|
414
|
-
|
|
393
|
+
const instructionFiles = copyInstructions(HOME, null, team) || [];
|
|
394
|
+
initAwDocs(HOME);
|
|
415
395
|
const mcpFiles = await setupMcp(HOME, team, { silent }) || [];
|
|
416
|
-
applyStoredStartupPreferences(HOME);
|
|
417
|
-
const removedLegacyStartupFiles = cwd !== HOME ? removeWorkspaceHookDefaults(cwd) : [];
|
|
418
396
|
const hooksInstalled = installGlobalHooks();
|
|
419
397
|
installIdeTasks();
|
|
420
398
|
|
|
@@ -461,10 +439,6 @@ export async function initCommand(args) {
|
|
|
461
439
|
` ${chalk.green('✓')} Source of truth: ~/.aw/ (git clone)`,
|
|
462
440
|
` ${chalk.green('✓')} Symlink: ~/.aw_registry/ → ~/.aw/.aw_registry/`,
|
|
463
441
|
` ${chalk.green('✓')} IDE integration: ~/.claude/, ~/.cursor/, ~/.codex/`,
|
|
464
|
-
cwd !== HOME ? ` ${chalk.green('✓')} Global startup managed from ~/.claude/, ~/.cursor/, ~/.codex/` : null,
|
|
465
|
-
removedLegacyStartupFiles.length > 0
|
|
466
|
-
? ` ${chalk.green('✓')} Removed ${removedLegacyStartupFiles.length} legacy repo startup file${removedLegacyStartupFiles.length > 1 ? 's' : ''}`
|
|
467
|
-
: null,
|
|
468
442
|
hooksInstalled ? ` ${chalk.green('✓')} Git hooks: auto-sync on pull/clone (core.hooksPath)` : null,
|
|
469
443
|
` ${chalk.green('✓')} IDE task: auto-sync on workspace open`,
|
|
470
444
|
cwd !== HOME && isWorktree(join(cwd, '.aw')) ? ` ${chalk.green('✓')} Linked in current project` : null,
|
|
@@ -9,8 +9,6 @@ import { addProjectWorktree, isWorktree, isValidClone } from '../git.mjs';
|
|
|
9
9
|
import { REGISTRY_DIR, REGISTRY_URL } from '../constants.mjs';
|
|
10
10
|
import { linkWorkspace } from '../link.mjs';
|
|
11
11
|
import { generateCommands } from '../integrate.mjs';
|
|
12
|
-
import { removeWorkspaceHookDefaults } from '../codex.mjs';
|
|
13
|
-
import { applyStoredStartupPreferences } from '../startup.mjs';
|
|
14
12
|
import { installLocalCommitHook } from '../hooks.mjs';
|
|
15
13
|
|
|
16
14
|
const HOME = homedir();
|
|
@@ -43,11 +41,8 @@ export function linkProjectCommand(args) {
|
|
|
43
41
|
const awDirForLinks = existsSync(projectRegistryDir) ? projectRegistryDir : null;
|
|
44
42
|
const symlinks = linkWorkspace(HOME, awDirForLinks, { silent: true });
|
|
45
43
|
const commands = generateCommands(HOME, { silent: true });
|
|
46
|
-
applyStoredStartupPreferences(HOME);
|
|
47
44
|
installLocalCommitHook(cwd);
|
|
48
|
-
|
|
49
|
-
fmt.logSuccess(`Already linked — refreshed ${chalk.bold(symlinks)} IDE symlinks · ${chalk.bold(commands)} commands${removedLegacyStartupFiles.length > 0 ? ` · removed ${removedLegacyStartupFiles.length} legacy repo startup file${removedLegacyStartupFiles.length > 1 ? 's' : ''}` : ''}`);
|
|
50
|
-
|
|
45
|
+
fmt.logSuccess(`Already linked — refreshed ${chalk.bold(symlinks)} IDE symlinks · ${chalk.bold(commands)} commands`);
|
|
51
46
|
return;
|
|
52
47
|
}
|
|
53
48
|
|
|
@@ -57,19 +52,13 @@ export function linkProjectCommand(args) {
|
|
|
57
52
|
const awDirForLinks = existsSync(projectRegistryDir) ? projectRegistryDir : null;
|
|
58
53
|
const symlinks = linkWorkspace(HOME, awDirForLinks, { silent: true });
|
|
59
54
|
const commands = generateCommands(HOME, { silent: true });
|
|
60
|
-
applyStoredStartupPreferences(HOME);
|
|
61
55
|
installLocalCommitHook(cwd);
|
|
62
|
-
const removedLegacyStartupFiles = removeWorkspaceHookDefaults(cwd);
|
|
63
56
|
fmt.logSuccess([
|
|
64
57
|
`Project linked — ${chalk.bold(symlinks)} IDE symlinks · ${chalk.bold(commands)} commands`,
|
|
65
58
|
'',
|
|
66
59
|
` ${chalk.green('✓')} ${chalk.dim('.aw/')} git worktree (IDE git panel enabled)`,
|
|
67
60
|
` ${chalk.green('✓')} ${chalk.dim(`.aw/${REGISTRY_DIR}/`)} registry content`,
|
|
68
61
|
` ${chalk.green('✓')} ${chalk.dim('.claude/.cursor/.codex/')} IDE symlinks wired`,
|
|
69
|
-
` ${chalk.green('✓')} ${chalk.dim('startup mode')} global-first via ~/.claude/, ~/.cursor/, ~/.codex/`,
|
|
70
|
-
removedLegacyStartupFiles.length > 0
|
|
71
|
-
? ` ${chalk.green('✓')} ${chalk.dim('legacy repo hooks')} removed ${removedLegacyStartupFiles.length} AW-managed file${removedLegacyStartupFiles.length > 1 ? 's' : ''}`
|
|
72
|
-
: null,
|
|
73
62
|
].join('\n'));
|
|
74
63
|
} catch (e) {
|
|
75
64
|
fmt.cancel(`Failed to link project: ${e.message}`);
|
package/commands/nuke.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Safety guarantee: NEVER deletes files that AW didn't create.
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { join } from 'node:path';
|
|
6
6
|
import { existsSync, rmSync, lstatSync, unlinkSync, readdirSync, readFileSync, readlinkSync, writeFileSync } from 'node:fs';
|
|
7
7
|
import { homedir } from 'node:os';
|
|
8
8
|
import { execSync, exec as execCb } from 'node:child_process';
|
|
@@ -15,14 +15,13 @@ import { removeGlobalHooks } from '../hooks.mjs';
|
|
|
15
15
|
import { uninstallAwEcc } from '../ecc.mjs';
|
|
16
16
|
import { removeMcpConfig } from '../mcp.mjs';
|
|
17
17
|
import { listProjectWorktrees } from '../git.mjs';
|
|
18
|
-
import { removeWorkspaceHookDefaults } from '../codex.mjs';
|
|
19
18
|
|
|
20
19
|
const HOME = homedir();
|
|
21
20
|
const GLOBAL_AW_DIR = join(HOME, '.aw_registry');
|
|
22
21
|
const MANIFEST_PATH = join(GLOBAL_AW_DIR, '.aw-manifest.json');
|
|
23
22
|
|
|
24
23
|
const IDE_DIRS = ['.claude', '.cursor', '.codex', '.agents'];
|
|
25
|
-
const CONTENT_TYPES = ['agents', 'skills', 'commands', 'evals'
|
|
24
|
+
const CONTENT_TYPES = ['agents', 'skills', 'commands', 'evals'];
|
|
26
25
|
|
|
27
26
|
function loadManifest() {
|
|
28
27
|
if (!existsSync(MANIFEST_PATH)) return null;
|
|
@@ -140,11 +139,7 @@ async function removeProjectSymlinks() {
|
|
|
140
139
|
{ encoding: 'utf8', timeout: 30000 }
|
|
141
140
|
);
|
|
142
141
|
for (const linkPath of registryLinks.trim().split('\n').filter(Boolean)) {
|
|
143
|
-
try {
|
|
144
|
-
removeWorkspaceHookDefaults(dirname(linkPath));
|
|
145
|
-
unlinkSync(linkPath);
|
|
146
|
-
removed++;
|
|
147
|
-
} catch { /* best effort */ }
|
|
142
|
+
try { unlinkSync(linkPath); removed++; } catch { /* best effort */ }
|
|
148
143
|
}
|
|
149
144
|
|
|
150
145
|
// Also remove legacy local .git/hooks/post-checkout installed by old aw versions
|
|
@@ -222,12 +217,7 @@ export async function nukeCommand(args) {
|
|
|
222
217
|
// Remove stale local .aw_registry symlink if present (skip if cwd IS home — that's the global one)
|
|
223
218
|
if (process.cwd() !== HOME) {
|
|
224
219
|
const local = join(process.cwd(), '.aw_registry');
|
|
225
|
-
try {
|
|
226
|
-
if (lstatSync(local).isSymbolicLink()) {
|
|
227
|
-
removeWorkspaceHookDefaults(process.cwd());
|
|
228
|
-
unlinkSync(local);
|
|
229
|
-
}
|
|
230
|
-
} catch { /* fine */ }
|
|
220
|
+
try { if (lstatSync(local).isSymbolicLink()) unlinkSync(local); } catch { /* fine */ }
|
|
231
221
|
}
|
|
232
222
|
|
|
233
223
|
const manifest = loadManifest();
|
package/commands/pull.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
existsSync,
|
|
5
5
|
lstatSync,
|
|
6
6
|
} from 'node:fs';
|
|
7
|
-
import {
|
|
7
|
+
import { join, extname } from 'node:path';
|
|
8
8
|
import { homedir } from 'node:os';
|
|
9
9
|
import { exec as execCb, execSync } from 'node:child_process';
|
|
10
10
|
import { promisify } from 'node:util';
|
|
@@ -30,9 +30,7 @@ import {
|
|
|
30
30
|
} from '../constants.mjs';
|
|
31
31
|
import { collectAllPaths, syncFileTree } from '../file-tree.mjs';
|
|
32
32
|
import { linkWorkspace } from '../link.mjs';
|
|
33
|
-
import { generateCommands, copyInstructions
|
|
34
|
-
import { removeWorkspaceHookDefaults } from '../codex.mjs';
|
|
35
|
-
import { applyStoredStartupPreferences, ensureAwRuntimeHook } from '../startup.mjs';
|
|
33
|
+
import { generateCommands, copyInstructions } from '../integrate.mjs';
|
|
36
34
|
|
|
37
35
|
const HOME = homedir();
|
|
38
36
|
const AW_HOME = join(HOME, '.aw');
|
|
@@ -247,7 +245,6 @@ export async function pullCommand(args) {
|
|
|
247
245
|
if (!args._skipIntegrate) {
|
|
248
246
|
const projectRegistryDir = cwd !== HOME ? join(cwd, '.aw', REGISTRY_DIR) : null;
|
|
249
247
|
const awDirForLinks = (projectRegistryDir && existsSync(projectRegistryDir)) ? projectRegistryDir : null;
|
|
250
|
-
const startupCleanupDir = localAw ? dirname(localAw) : (cwd !== HOME ? cwd : null);
|
|
251
248
|
|
|
252
249
|
if (!silent) {
|
|
253
250
|
const ideSpinner = log.spinner();
|
|
@@ -255,18 +252,12 @@ export async function pullCommand(args) {
|
|
|
255
252
|
const symlinks = linkWorkspace(HOME, awDirForLinks, { silent: true });
|
|
256
253
|
ideSpinner.message('Generating commands...');
|
|
257
254
|
const commands = generateCommands(HOME, { silent: true });
|
|
258
|
-
|
|
259
|
-
ensureAwRuntimeHook(HOME);
|
|
260
|
-
applyStoredStartupPreferences(HOME);
|
|
261
|
-
if (startupCleanupDir) removeWorkspaceHookDefaults(startupCleanupDir);
|
|
255
|
+
copyInstructions(HOME, null, cfg.namespace);
|
|
262
256
|
ideSpinner.stop(`IDE wired — ${chalk.bold(symlinks)} symlink${symlinks !== 1 ? 's' : ''}, ${chalk.bold(commands)} command${commands !== 1 ? 's' : ''}`);
|
|
263
257
|
} else {
|
|
264
258
|
linkWorkspace(HOME, awDirForLinks, { silent: true });
|
|
265
259
|
generateCommands(HOME, { silent: true });
|
|
266
|
-
|
|
267
|
-
ensureAwRuntimeHook(HOME);
|
|
268
|
-
applyStoredStartupPreferences(HOME);
|
|
269
|
-
if (startupCleanupDir) removeWorkspaceHookDefaults(startupCleanupDir);
|
|
260
|
+
copyInstructions(HOME, null, cfg.namespace);
|
|
270
261
|
}
|
|
271
262
|
}
|
|
272
263
|
|
|
@@ -299,73 +290,3 @@ function registerMcp(namespace) {
|
|
|
299
290
|
fmt.logWarn('MCP registration failed (pull still successful)');
|
|
300
291
|
}
|
|
301
292
|
}
|
|
302
|
-
function printDryRun(actions, verbose) {
|
|
303
|
-
const counts = { ADD: 0, UPDATE: 0, CONFLICT: 0, ORPHAN: 0, UNCHANGED: 0 };
|
|
304
|
-
const lines = [];
|
|
305
|
-
|
|
306
|
-
for (const type of ['agents', 'skills', 'commands', 'evals', 'references']) {
|
|
307
|
-
const items = actions.filter(a => a.type === type);
|
|
308
|
-
if (items.length === 0) continue;
|
|
309
|
-
|
|
310
|
-
lines.push(chalk.bold(`${type}/`));
|
|
311
|
-
for (const act of items.sort((a, b) => a.targetFilename.localeCompare(b.targetFilename))) {
|
|
312
|
-
counts[act.action] = (counts[act.action] || 0) + 1;
|
|
313
|
-
if (!verbose && act.action === 'UNCHANGED') continue;
|
|
314
|
-
const ns = act.namespacePath ? chalk.dim(` [${act.namespacePath}]`) : '';
|
|
315
|
-
lines.push(` ${fmt.actionLabel(act.action)} ${act.targetFilename}${ns}`);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
if (lines.length > 0) {
|
|
320
|
-
fmt.note(lines.join('\n'), 'Dry Run');
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
fmt.logInfo(`Summary: ${fmt.countSummary(counts)}`);
|
|
324
|
-
fmt.logWarn('No files modified (--dry-run)');
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
function printSummary(actions, verbose, conflictCount) {
|
|
328
|
-
const conflicts = actions.filter(a => a.action === 'CONFLICT');
|
|
329
|
-
|
|
330
|
-
for (const type of ['agents', 'skills', 'commands', 'evals', 'references']) {
|
|
331
|
-
const typeActions = actions.filter(a => a.type === type);
|
|
332
|
-
if (typeActions.length === 0) continue;
|
|
333
|
-
|
|
334
|
-
const counts = { ADD: 0, UPDATE: 0, CONFLICT: 0, ORPHAN: 0, UNCHANGED: 0 };
|
|
335
|
-
for (const a of typeActions) counts[a.action]++;
|
|
336
|
-
|
|
337
|
-
const parts = [];
|
|
338
|
-
if (counts.ADD > 0) parts.push(chalk.green(`${counts.ADD} new`));
|
|
339
|
-
if (counts.UPDATE > 0) parts.push(chalk.cyan(`${counts.UPDATE} updated`));
|
|
340
|
-
if (counts.CONFLICT > 0) parts.push(chalk.red(`${counts.CONFLICT} conflict`));
|
|
341
|
-
if (counts.ORPHAN > 0) parts.push(chalk.yellow(`${counts.ORPHAN} removed`));
|
|
342
|
-
const detail = parts.length > 0 ? ` (${parts.join(', ')})` : '';
|
|
343
|
-
|
|
344
|
-
fmt.logSuccess(`${typeActions.length} ${type} pulled${detail}`);
|
|
345
|
-
|
|
346
|
-
if (verbose) {
|
|
347
|
-
for (const a of typeActions.filter(a => a.action !== 'UNCHANGED')) {
|
|
348
|
-
const ns = a.namespacePath ? chalk.dim(` [${a.namespacePath}]`) : '';
|
|
349
|
-
fmt.logMessage(` ${fmt.actionLabel(a.action)} ${a.targetFilename}${ns}`);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
if (conflicts.length > 0) {
|
|
355
|
-
const conflictLines = conflicts.map(c => {
|
|
356
|
-
return `${chalk.red('both modified:')} ${c.type}/${c.targetFilename}`;
|
|
357
|
-
}).join('\n');
|
|
358
|
-
|
|
359
|
-
fmt.note(
|
|
360
|
-
conflictLines + '\n\n' +
|
|
361
|
-
chalk.dim('Fix conflicts, then re-run pull to verify.\n') +
|
|
362
|
-
chalk.dim('grep -r "<<<<<<< " .aw_registry/'),
|
|
363
|
-
chalk.red('Merge Conflicts')
|
|
364
|
-
);
|
|
365
|
-
|
|
366
|
-
fmt.outro(chalk.red('Pull completed with conflicts — resolve and re-run'));
|
|
367
|
-
process.exit(1);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
fmt.outro('Pull complete');
|
|
371
|
-
}
|
package/commands/push.mjs
CHANGED
|
@@ -29,7 +29,7 @@ import { hasRulesChanges, isRulesPushInput, pushRulesCommand } from './push-rule
|
|
|
29
29
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
30
30
|
const VERSION = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8')).version;
|
|
31
31
|
|
|
32
|
-
const PUSHABLE_TYPES = ['agents', 'skills', 'commands', 'evals'
|
|
32
|
+
const PUSHABLE_TYPES = ['agents', 'skills', 'commands', 'evals'];
|
|
33
33
|
|
|
34
34
|
// ── PR content generation ────────────────────────────────────────────
|
|
35
35
|
|
|
@@ -635,8 +635,8 @@ export async function pushCommand(args) {
|
|
|
635
635
|
const registryAbsPath = join(registrySubDir, relFromRegistry);
|
|
636
636
|
const allFiles = collectBatchFiles(registryAbsPath, registrySubDir);
|
|
637
637
|
if (allFiles.length === 0) {
|
|
638
|
-
|
|
639
|
-
|
|
638
|
+
fmt.cancel(`Nothing to push in ${chalk.cyan(input)} — no agents, skills, commands, or evals found.`);
|
|
639
|
+
return;
|
|
640
640
|
}
|
|
641
641
|
// Only include files that actually have changes (new or modified)
|
|
642
642
|
const folderChanges = detectChanges(awHome, REGISTRY_DIR);
|
|
@@ -688,7 +688,7 @@ export async function pushCommand(args) {
|
|
|
688
688
|
fmt.cancel([
|
|
689
689
|
`Invalid push path: ${chalk.red(resolved.registryPath)}`,
|
|
690
690
|
'',
|
|
691
|
-
` Content must live under an ${chalk.bold('agents/')}, ${chalk.bold('skills/')}, ${chalk.bold('commands/')},
|
|
691
|
+
` Content must live under an ${chalk.bold('agents/')}, ${chalk.bold('skills/')}, ${chalk.bold('commands/')}, or ${chalk.bold('evals/')} directory.`,
|
|
692
692
|
'',
|
|
693
693
|
` ${chalk.dim('Valid examples:')}`,
|
|
694
694
|
` aw push .aw_registry/commerce/quality/agents/unit-tester.md`,
|
package/commands/search.mjs
CHANGED
|
@@ -98,7 +98,7 @@ function searchLocal(workspaceDir, query) {
|
|
|
98
98
|
if (!nsEntry.isDirectory() || nsEntry.name.startsWith('.')) continue;
|
|
99
99
|
const ns = nsEntry.name;
|
|
100
100
|
|
|
101
|
-
for (const type of ['agents', 'skills', 'commands', 'evals'
|
|
101
|
+
for (const type of ['agents', 'skills', 'commands', 'evals']) {
|
|
102
102
|
const typeDir = join(workspaceDir, ns, type);
|
|
103
103
|
if (!existsSync(typeDir)) continue;
|
|
104
104
|
|
package/commands/telemetry.mjs
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
// commands/telemetry.mjs — `aw telemetry [enable|disable|status
|
|
1
|
+
// commands/telemetry.mjs — `aw telemetry [enable|disable|status]`
|
|
2
2
|
|
|
3
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import { homedir } from 'node:os';
|
|
6
3
|
import { enableTelemetry, disableTelemetry, getStatus } from '../telemetry.mjs';
|
|
7
4
|
import * as fmt from '../fmt.mjs';
|
|
8
5
|
import { chalk } from '../fmt.mjs';
|
|
@@ -22,66 +19,13 @@ export async function telemetryCommand(args) {
|
|
|
22
19
|
return;
|
|
23
20
|
}
|
|
24
21
|
|
|
25
|
-
if (sub === 'flush-queue') {
|
|
26
|
-
await flushQueueCommand();
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
22
|
// status (default)
|
|
31
23
|
const status = getStatus();
|
|
32
24
|
fmt.intro('aw telemetry');
|
|
33
25
|
fmt.logStep(`Status: ${status.enabled ? chalk.green('enabled') : chalk.red('disabled')}`);
|
|
34
26
|
fmt.logStep(`Machine ID: ${chalk.dim(status.machine_id)}`);
|
|
35
27
|
fmt.logStep(`Config: ${chalk.dim(status.config_path)}`);
|
|
36
|
-
|
|
37
|
-
// Show queue depth if available
|
|
38
|
-
const queueFile = join(homedir(), '.aw', 'telemetry', 'queue.jsonl');
|
|
39
|
-
if (existsSync(queueFile)) {
|
|
40
|
-
try {
|
|
41
|
-
const lines = readFileSync(queueFile, 'utf8').trim().split('\n').filter(Boolean);
|
|
42
|
-
fmt.logStep(`Queue: ${chalk.yellow(lines.length)} pending prompt(s)`);
|
|
43
|
-
} catch { /* best effort */ }
|
|
44
|
-
}
|
|
45
|
-
|
|
46
28
|
fmt.logMessage('');
|
|
47
|
-
fmt.logMessage(` ${chalk.dim('aw telemetry disable')}
|
|
48
|
-
fmt.logMessage(` ${chalk.dim('aw telemetry enable')}
|
|
49
|
-
fmt.logMessage(` ${chalk.dim('aw telemetry flush-queue')} — manually flush pending queue`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async function flushQueueCommand() {
|
|
53
|
-
const eccBase = join(homedir(), '.aw-ecc');
|
|
54
|
-
const libPath = join(eccBase, 'scripts', 'hooks', 'capabilities', 'telemetry', 'telemetry-lib.js');
|
|
55
|
-
|
|
56
|
-
if (!existsSync(libPath)) {
|
|
57
|
-
fmt.logWarn('Telemetry library not found. Run aw init first.');
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const {
|
|
62
|
-
readQueue,
|
|
63
|
-
flushQueueToApi,
|
|
64
|
-
getNamespace,
|
|
65
|
-
buildTelemetryHeaders,
|
|
66
|
-
} = await import(libPath);
|
|
67
|
-
|
|
68
|
-
const entries = readQueue();
|
|
69
|
-
if (entries.length === 0) {
|
|
70
|
-
fmt.logSuccess('Queue is empty — nothing to flush.');
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
fmt.logStep(`Flushing ${entries.length} pending prompt(s)...`);
|
|
75
|
-
|
|
76
|
-
const namespace = getNamespace();
|
|
77
|
-
const headers = buildTelemetryHeaders(namespace);
|
|
78
|
-
const result = await flushQueueToApi(headers);
|
|
79
|
-
|
|
80
|
-
if (result.flushed > 0) {
|
|
81
|
-
fmt.logSuccess(`Flushed ${result.flushed} prompt(s) to API.`);
|
|
82
|
-
} else if (result.failed) {
|
|
83
|
-
fmt.logWarn('Flush failed — entries remain in queue for retry.');
|
|
84
|
-
} else {
|
|
85
|
-
fmt.logSuccess('Nothing to flush.');
|
|
86
|
-
}
|
|
29
|
+
fmt.logMessage(` ${chalk.dim('aw telemetry disable')} — opt out of anonymous analytics`);
|
|
30
|
+
fmt.logMessage(` ${chalk.dim('aw telemetry enable')} — re-enable analytics`);
|
|
87
31
|
}
|
package/ecc.mjs
CHANGED
|
@@ -6,30 +6,21 @@ import {
|
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import * as fmt from "./fmt.mjs";
|
|
9
|
-
import { applyStoredStartupPreferences } from "./startup.mjs";
|
|
10
9
|
|
|
11
10
|
const AW_ECC_REPO_SSH = "git@github.com:shreyansh-ghl/aw-ecc.git";
|
|
12
11
|
const AW_ECC_REPO_HTTPS = "https://github.com/shreyansh-ghl/aw-ecc.git";
|
|
13
|
-
|
|
12
|
+
const AW_ECC_TAG = "v1.4.29";
|
|
14
13
|
|
|
15
14
|
const MARKETPLACE_NAME = "aw-marketplace";
|
|
16
15
|
const PLUGIN_KEY = `aw@${MARKETPLACE_NAME}`;
|
|
17
16
|
|
|
18
17
|
function eccDir() { return join(homedir(), ".aw-ecc"); }
|
|
19
18
|
|
|
20
|
-
// "claude"
|
|
21
|
-
// and
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
// some optional modules depend on commands-core and cause install-apply to fail.
|
|
19
|
+
// "claude" runs install-apply.js --without baseline:commands so hooks, rules,
|
|
20
|
+
// and agents land in ~/.claude/ — but commands are skipped because the plugin
|
|
21
|
+
// marketplace (installClaudePlugin) already registers them under /aw:tdd.
|
|
22
|
+
// File-copying commands would create a duplicate flat /tdd alongside /aw:tdd.
|
|
25
23
|
const FILE_COPY_TARGETS = ["claude", "cursor", "codex"];
|
|
26
|
-
const CLAUDE_FILE_COPY_MODULES = [
|
|
27
|
-
"rules-core",
|
|
28
|
-
"agents-core",
|
|
29
|
-
"hooks-runtime",
|
|
30
|
-
"platform-configs",
|
|
31
|
-
"workflow-quality",
|
|
32
|
-
];
|
|
33
24
|
|
|
34
25
|
const TARGET_STATE = {
|
|
35
26
|
claude: { state: ".claude/ecc/install-state.json" },
|
|
@@ -49,20 +40,6 @@ function run(cmd, opts = {}) {
|
|
|
49
40
|
return execSync(cmd, { stdio: "pipe", ...opts });
|
|
50
41
|
}
|
|
51
42
|
|
|
52
|
-
function fetchOverrideRef(dest, ref) {
|
|
53
|
-
run(`git -C ${dest} fetch --quiet --depth 1 origin ${ref}`);
|
|
54
|
-
run(`git -C ${dest} checkout --quiet FETCH_HEAD`);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function cloneWithRef(url, ref, dest) {
|
|
58
|
-
try {
|
|
59
|
-
run(`git clone --quiet --depth 1 --branch ${ref} "${url}" "${dest}"`);
|
|
60
|
-
} catch {
|
|
61
|
-
run(`git clone --quiet --depth 1 "${url}" "${dest}"`);
|
|
62
|
-
fetchOverrideRef(dest, ref);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
43
|
function readIfExists(path) {
|
|
67
44
|
try { return existsSync(path) ? readFileSync(path, "utf8") : null; } catch { return null; }
|
|
68
45
|
}
|
|
@@ -119,39 +96,20 @@ function relProtectedPath(absPath, home) {
|
|
|
119
96
|
function cloneOrUpdate(tag, dest) {
|
|
120
97
|
// AW_ECC_CLONE_URL overrides the remote (used in tests to point at a local fake repo)
|
|
121
98
|
const overrideUrl = process.env.AW_ECC_CLONE_URL;
|
|
122
|
-
// AW_ECC_CLONE_REF lets aw init install ECC from a non-default branch or tag.
|
|
123
|
-
const overrideRef = process.env.AW_ECC_CLONE_REF?.trim();
|
|
124
99
|
|
|
125
100
|
if (existsSync(join(dest, ".git"))) {
|
|
126
101
|
try {
|
|
127
|
-
if (!overrideUrl
|
|
102
|
+
if (!overrideUrl) {
|
|
128
103
|
run(`git -C ${dest} fetch --quiet --depth 1 origin tag ${tag}`);
|
|
129
104
|
run(`git -C ${dest} checkout --quiet ${tag}`);
|
|
130
|
-
} else {
|
|
131
|
-
if (overrideUrl) {
|
|
132
|
-
run(`git -C ${dest} remote set-url origin "${overrideUrl}"`);
|
|
133
|
-
}
|
|
134
|
-
fetchOverrideRef(dest, overrideRef || tag);
|
|
135
105
|
}
|
|
136
106
|
return;
|
|
137
107
|
} catch { /* fall through to fresh clone */ }
|
|
138
108
|
}
|
|
139
109
|
if (existsSync(dest)) rmSync(dest, { recursive: true, force: true });
|
|
140
110
|
if (overrideUrl) {
|
|
141
|
-
|
|
142
|
-
cloneWithRef(overrideUrl, overrideRef, dest);
|
|
143
|
-
} else {
|
|
144
|
-
run(`git clone --quiet --depth 1 "${overrideUrl}" "${dest}"`);
|
|
145
|
-
}
|
|
111
|
+
run(`git clone --quiet --depth 1 "${overrideUrl}" "${dest}"`);
|
|
146
112
|
} else {
|
|
147
|
-
if (overrideRef) {
|
|
148
|
-
try {
|
|
149
|
-
cloneWithRef(AW_ECC_REPO_SSH, overrideRef, dest);
|
|
150
|
-
} catch {
|
|
151
|
-
cloneWithRef(AW_ECC_REPO_HTTPS, overrideRef, dest);
|
|
152
|
-
}
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
113
|
try {
|
|
156
114
|
run(`git clone --quiet --depth 1 --branch ${tag} ${AW_ECC_REPO_SSH} ${dest}`);
|
|
157
115
|
} catch {
|
|
@@ -216,8 +174,7 @@ function namespaceCursorCommands(home) {
|
|
|
216
174
|
|
|
217
175
|
/**
|
|
218
176
|
* Run scripts/sync-ecc-to-codex.sh from the cloned ecc repo.
|
|
219
|
-
* Generates ~/.codex/prompts
|
|
220
|
-
* using aw-*.md for AW-namespaced commands and ecc-*.md for the rest,
|
|
177
|
+
* Generates ~/.codex/prompts/ecc-*.md (Codex equivalent of slash commands)
|
|
221
178
|
* and merges ~/.codex/AGENTS.md. Best-effort — failure doesn't block init.
|
|
222
179
|
*/
|
|
223
180
|
function syncEccToCodex(repoDir) {
|
|
@@ -241,9 +198,6 @@ export async function installAwEcc(
|
|
|
241
198
|
try {
|
|
242
199
|
cloneOrUpdate(AW_ECC_TAG, repoDir);
|
|
243
200
|
|
|
244
|
-
// Ensure telemetry state directory exists (vendor-agnostic, shared across IDEs)
|
|
245
|
-
mkdirSync(join(home, ".aw", "telemetry"), { recursive: true });
|
|
246
|
-
|
|
247
201
|
// Claude Code: plugin install via marketplace CLI (proper agent dispatch)
|
|
248
202
|
if (targets.includes("claude")) {
|
|
249
203
|
try {
|
|
@@ -265,14 +219,11 @@ export async function installAwEcc(
|
|
|
265
219
|
|
|
266
220
|
// Always use HOME as cwd so files land in ~/.<target>/ globally.
|
|
267
221
|
const runCwd = homedir();
|
|
268
|
-
// For claude:
|
|
269
|
-
//
|
|
270
|
-
|
|
271
|
-
const installArgs = target === "claude"
|
|
272
|
-
? `--target ${target} --modules ${CLAUDE_FILE_COPY_MODULES.join(",")}`
|
|
273
|
-
: `--target ${target} --profile full`;
|
|
222
|
+
// For claude: skip commands (plugin handles them as /aw:tdd) but
|
|
223
|
+
// install hooks, rules, agents into ~/.claude/.
|
|
224
|
+
const withoutFlag = target === "claude" ? " --without baseline:commands" : "";
|
|
274
225
|
run(
|
|
275
|
-
`node ${join(repoDir, "scripts/install-apply.js")} ${
|
|
226
|
+
`node ${join(repoDir, "scripts/install-apply.js")} --target ${target} --profile full${withoutFlag}`,
|
|
276
227
|
{ cwd: runCwd },
|
|
277
228
|
);
|
|
278
229
|
// Move cursor commands into aw/ subfolder for namespace consistency
|
|
@@ -280,7 +231,7 @@ export async function installAwEcc(
|
|
|
280
231
|
if (target === "cursor") {
|
|
281
232
|
namespaceCursorCommands(runCwd);
|
|
282
233
|
}
|
|
283
|
-
// Run sync script for codex: generates ~/.codex/prompts
|
|
234
|
+
// Run sync script for codex: generates ~/.codex/prompts/ecc-*.md and
|
|
284
235
|
// merges AGENTS.md — Codex has no slash commands, so prompts are the
|
|
285
236
|
// equivalent of commands.
|
|
286
237
|
if (target === "codex") {
|
|
@@ -294,8 +245,6 @@ export async function installAwEcc(
|
|
|
294
245
|
}
|
|
295
246
|
}
|
|
296
247
|
|
|
297
|
-
applyStoredStartupPreferences();
|
|
298
|
-
|
|
299
248
|
if (!silent) fmt.logSuccess("aw-ecc engine installed");
|
|
300
249
|
} catch (err) {
|
|
301
250
|
if (!silent) fmt.logWarn(`aw-ecc install failed: ${err.message}`);
|
|
@@ -339,42 +288,17 @@ export function uninstallAwEcc({ silent = false } = {}) {
|
|
|
339
288
|
} catch { /* corrupted state — skip */ }
|
|
340
289
|
}
|
|
341
290
|
|
|
342
|
-
// Codex: remove
|
|
343
|
-
// (not tracked in install-state — cleaned
|
|
291
|
+
// Codex: remove ecc prompt files generated by sync-ecc-to-codex.sh
|
|
292
|
+
// (not tracked in install-state — cleaned by prefix pattern ecc-*.md)
|
|
344
293
|
const codexPromptsDir = join(HOME, ".codex", "prompts");
|
|
345
294
|
if (existsSync(codexPromptsDir)) {
|
|
346
295
|
try {
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
if (!existsSync(manifestPath)) continue;
|
|
351
|
-
try {
|
|
352
|
-
const entries = readFileSync(manifestPath, "utf8")
|
|
353
|
-
.split(/\r?\n/)
|
|
354
|
-
.map((line) => line.trim())
|
|
355
|
-
.filter(Boolean);
|
|
356
|
-
for (const entry of entries) promptFiles.add(entry);
|
|
357
|
-
} catch { /* best effort */ }
|
|
358
|
-
rmSync(manifestPath, { force: true });
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
for (const file of promptFiles) {
|
|
362
|
-
const promptPath = join(codexPromptsDir, file);
|
|
363
|
-
if (existsSync(promptPath)) {
|
|
364
|
-
rmSync(promptPath, { force: true });
|
|
296
|
+
for (const file of readdirSync(codexPromptsDir)) {
|
|
297
|
+
if (file.startsWith("ecc-") && file.endsWith(".md")) {
|
|
298
|
+
rmSync(join(codexPromptsDir, file), { force: true });
|
|
365
299
|
removed++;
|
|
366
300
|
}
|
|
367
301
|
}
|
|
368
|
-
|
|
369
|
-
for (const file of readdirSync(codexPromptsDir)) {
|
|
370
|
-
const isLegacyGeneratedPrompt = file.endsWith(".md")
|
|
371
|
-
&& (file.startsWith("ecc-") || file.startsWith("aw-"));
|
|
372
|
-
if (!isLegacyGeneratedPrompt) continue;
|
|
373
|
-
|
|
374
|
-
rmSync(join(codexPromptsDir, file), { force: true });
|
|
375
|
-
removed++;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
302
|
pruneEmptyParents(codexPromptsDir, join(HOME, ".codex"));
|
|
379
303
|
} catch { /* best effort */ }
|
|
380
304
|
}
|