@ghl-ai/aw 0.1.38-beta.12 → 0.1.38-beta.13
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 +1 -8
- package/commands/init.mjs +9 -54
- package/commands/link-project.mjs +1 -12
- package/commands/nuke.mjs +4 -14
- package/commands/pull.mjs +11 -109
- package/commands/push-rules.mjs +15 -4
- package/commands/push.mjs +4 -4
- package/commands/search.mjs +1 -1
- package/constants.mjs +1 -1
- package/ecc.mjs +18 -91
- 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 +12 -54
- package/codex.mjs +0 -839
- package/commands/doctor.mjs +0 -1087
- 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 -69
- 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)'),
|
|
@@ -134,7 +127,7 @@ function printHelp() {
|
|
|
134
127
|
cmd('aw push .aw_registry/agents/<name>.md', 'Push a single agent'),
|
|
135
128
|
cmd('aw push .aw_registry/skills/<name>/', 'Push a single skill folder'),
|
|
136
129
|
cmd('aw push .aw_rules', 'Auto-redirects to aw push-rules'),
|
|
137
|
-
cmd('aw push-rules', 'Pushes .aw_rules'),
|
|
130
|
+
cmd('aw push-rules', 'Pushes .aw_rules or .aw_registry/.aw_rules'),
|
|
138
131
|
'',
|
|
139
132
|
` ${chalk.dim('# Remove content from workspace')}`,
|
|
140
133
|
cmd('aw drop <team>', 'Stop syncing a namespace (removes all files)'),
|
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,34 +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 syncRulesTargets(targetDir) {
|
|
63
|
-
const rulesSrc = join(AW_HOME, RULES_SOURCE_DIR);
|
|
64
|
-
if (!existsSync(rulesSrc)) return false;
|
|
65
|
-
syncFileTree(rulesSrc, join(targetDir, RULES_SOURCE_DIR));
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function removeLegacyRegistryRules() {
|
|
70
|
-
try {
|
|
71
|
-
rmSync(join(GLOBAL_AW_DIR, RULES_SOURCE_DIR), { recursive: true, force: true });
|
|
72
|
-
} catch {
|
|
73
|
-
// best effort cleanup
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function syncInstructionsAndAwDocs(targetDir, namespace) {
|
|
78
|
-
copyInstructions(targetDir, null, namespace);
|
|
79
|
-
initAwDocs(targetDir);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function syncHomeAndProjectInstructions(cwd, namespace) {
|
|
83
|
-
syncHomeHarnessInstructions(HOME);
|
|
84
|
-
initAwDocs(HOME);
|
|
85
|
-
if (cwd !== HOME) {
|
|
86
|
-
syncInstructionsAndAwDocs(cwd, namespace);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
60
|
// ── Ensure ~/.aw/.gitignore has personal/local entries ───────────────────
|
|
91
61
|
|
|
92
62
|
const AW_GITIGNORE_ENTRIES = [
|
|
@@ -277,11 +247,9 @@ export async function initCommand(args) {
|
|
|
277
247
|
|
|
278
248
|
ensureAwGitignore(AW_HOME);
|
|
279
249
|
const freshCfg = config.load(GLOBAL_AW_DIR);
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
syncRulesTargets(cwd);
|
|
250
|
+
if (existsSync(GLOBAL_AW_DIR)) {
|
|
251
|
+
syncFileTree(join(AW_HOME, RULES_SOURCE_DIR), join(GLOBAL_AW_DIR, RULES_SOURCE_DIR));
|
|
283
252
|
}
|
|
284
|
-
removeLegacyRegistryRules();
|
|
285
253
|
|
|
286
254
|
// Ensure project worktree sparse checkout matches the global clone.
|
|
287
255
|
// Covers the case where a namespace was added from HOME (or another project)
|
|
@@ -292,11 +260,9 @@ export async function initCommand(args) {
|
|
|
292
260
|
}
|
|
293
261
|
|
|
294
262
|
await installAwEcc(cwd, { silent });
|
|
295
|
-
|
|
296
|
-
|
|
263
|
+
copyInstructions(HOME, null, freshCfg?.namespace || team) || [];
|
|
264
|
+
initAwDocs(HOME);
|
|
297
265
|
await setupMcp(HOME, freshCfg?.namespace || team, { silent });
|
|
298
|
-
applyStoredStartupPreferences(HOME);
|
|
299
|
-
const removedLegacyStartupFiles = cwd !== HOME ? removeWorkspaceHookDefaults(cwd) : [];
|
|
300
266
|
installGlobalHooks();
|
|
301
267
|
|
|
302
268
|
// Remove old local .git/hooks/post-checkout that pre-dates core.hooksPath (creates stale .aw_registry symlink)
|
|
@@ -338,9 +304,6 @@ export async function initCommand(args) {
|
|
|
338
304
|
'',
|
|
339
305
|
` ${chalk.green('✓')} Registry synced`,
|
|
340
306
|
` ${chalk.green('✓')} IDE refreshed — ${chalk.bold(symlinks)} symlinks · ${chalk.bold(commands)} commands`,
|
|
341
|
-
removedLegacyStartupFiles.length > 0
|
|
342
|
-
? ` ${chalk.green('✓')} Removed ${removedLegacyStartupFiles.length} legacy repo startup file${removedLegacyStartupFiles.length > 1 ? 's' : ''}`
|
|
343
|
-
: null,
|
|
344
307
|
cwd !== HOME && isWorktree(join(cwd, '.aw')) ? ` ${chalk.green('✓')} Project linked` : null,
|
|
345
308
|
].filter(Boolean).join('\n'));
|
|
346
309
|
}
|
|
@@ -421,19 +384,15 @@ export async function initCommand(args) {
|
|
|
421
384
|
if (folderName) {
|
|
422
385
|
config.addPattern(GLOBAL_AW_DIR, folderName);
|
|
423
386
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
syncRulesTargets(cwd);
|
|
387
|
+
if (existsSync(GLOBAL_AW_DIR)) {
|
|
388
|
+
syncFileTree(join(AW_HOME, RULES_SOURCE_DIR), join(GLOBAL_AW_DIR, RULES_SOURCE_DIR));
|
|
427
389
|
}
|
|
428
|
-
removeLegacyRegistryRules();
|
|
429
390
|
|
|
430
391
|
// Step 3: Setup tasks, MCP, hooks
|
|
431
392
|
await installAwEcc(cwd, { silent });
|
|
432
|
-
|
|
433
|
-
|
|
393
|
+
const instructionFiles = copyInstructions(HOME, null, team) || [];
|
|
394
|
+
initAwDocs(HOME);
|
|
434
395
|
const mcpFiles = await setupMcp(HOME, team, { silent }) || [];
|
|
435
|
-
applyStoredStartupPreferences(HOME);
|
|
436
|
-
const removedLegacyStartupFiles = cwd !== HOME ? removeWorkspaceHookDefaults(cwd) : [];
|
|
437
396
|
const hooksInstalled = installGlobalHooks();
|
|
438
397
|
installIdeTasks();
|
|
439
398
|
|
|
@@ -480,10 +439,6 @@ export async function initCommand(args) {
|
|
|
480
439
|
` ${chalk.green('✓')} Source of truth: ~/.aw/ (git clone)`,
|
|
481
440
|
` ${chalk.green('✓')} Symlink: ~/.aw_registry/ → ~/.aw/.aw_registry/`,
|
|
482
441
|
` ${chalk.green('✓')} IDE integration: ~/.claude/, ~/.cursor/, ~/.codex/`,
|
|
483
|
-
cwd !== HOME ? ` ${chalk.green('✓')} Global startup managed from ~/.claude/, ~/.cursor/, ~/.codex/` : null,
|
|
484
|
-
removedLegacyStartupFiles.length > 0
|
|
485
|
-
? ` ${chalk.green('✓')} Removed ${removedLegacyStartupFiles.length} legacy repo startup file${removedLegacyStartupFiles.length > 1 ? 's' : ''}`
|
|
486
|
-
: null,
|
|
487
442
|
hooksInstalled ? ` ${chalk.green('✓')} Git hooks: auto-sync on pull/clone (core.hooksPath)` : null,
|
|
488
443
|
` ${chalk.green('✓')} IDE task: auto-sync on workspace open`,
|
|
489
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
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
import {
|
|
4
4
|
existsSync,
|
|
5
5
|
lstatSync,
|
|
6
|
-
rmSync,
|
|
7
6
|
} from 'node:fs';
|
|
8
|
-
import {
|
|
7
|
+
import { join, extname } from 'node:path';
|
|
9
8
|
import { homedir } from 'node:os';
|
|
10
9
|
import { exec as execCb, execSync } from 'node:child_process';
|
|
11
10
|
import { promisify } from 'node:util';
|
|
@@ -31,29 +30,12 @@ import {
|
|
|
31
30
|
} from '../constants.mjs';
|
|
32
31
|
import { collectAllPaths, syncFileTree } from '../file-tree.mjs';
|
|
33
32
|
import { linkWorkspace } from '../link.mjs';
|
|
34
|
-
import { generateCommands, copyInstructions
|
|
35
|
-
import { removeWorkspaceHookDefaults } from '../codex.mjs';
|
|
36
|
-
import { applyStoredStartupPreferences, ensureAwRuntimeHook } from '../startup.mjs';
|
|
33
|
+
import { generateCommands, copyInstructions } from '../integrate.mjs';
|
|
37
34
|
|
|
38
35
|
const HOME = homedir();
|
|
39
36
|
const AW_HOME = join(HOME, '.aw');
|
|
40
37
|
const GLOBAL_AW_DIR = join(HOME, '.aw_registry');
|
|
41
38
|
|
|
42
|
-
function syncRulesTargets(targetDir) {
|
|
43
|
-
const rulesSrc = join(AW_HOME, RULES_SOURCE_DIR);
|
|
44
|
-
if (!existsSync(rulesSrc)) return false;
|
|
45
|
-
syncFileTree(rulesSrc, join(targetDir, RULES_SOURCE_DIR));
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function removeLegacyRegistryRules() {
|
|
50
|
-
try {
|
|
51
|
-
rmSync(join(GLOBAL_AW_DIR, RULES_SOURCE_DIR), { recursive: true, force: true });
|
|
52
|
-
} catch {
|
|
53
|
-
// best effort cleanup
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
39
|
export async function pullCommand(args) {
|
|
58
40
|
const input = args._positional?.[0] || '';
|
|
59
41
|
const cwd = process.cwd();
|
|
@@ -195,6 +177,13 @@ export async function pullCommand(args) {
|
|
|
195
177
|
log.logWarn(`Conflicts in: ${fetchResult.conflicts.join(', ')}`);
|
|
196
178
|
}
|
|
197
179
|
|
|
180
|
+
const rulesSrc = join(AW_HOME, RULES_SOURCE_DIR);
|
|
181
|
+
if (existsSync(rulesSrc)) {
|
|
182
|
+
const rulesDest = join(GLOBAL_AW_DIR, RULES_SOURCE_DIR);
|
|
183
|
+
syncFileTree(rulesSrc, rulesDest);
|
|
184
|
+
if (!silent) log.logSuccess('Synced .aw_rules');
|
|
185
|
+
}
|
|
186
|
+
|
|
198
187
|
// Rebase project worktree branch onto origin/main — only for legacy git worktrees.
|
|
199
188
|
// In the symlink model, <project>/.aw IS ~/.aw (same repo), so fetchAndMerge already
|
|
200
189
|
// brought it up to date. Nothing to rebase.
|
|
@@ -253,20 +242,9 @@ export async function pullCommand(args) {
|
|
|
253
242
|
}
|
|
254
243
|
}
|
|
255
244
|
|
|
256
|
-
let rulesSynced = syncRulesTargets(HOME);
|
|
257
|
-
const workspaceRoot = localAw ? dirname(localAw) : (cwd !== HOME ? cwd : null);
|
|
258
|
-
if (workspaceRoot && workspaceRoot !== HOME) {
|
|
259
|
-
rulesSynced = syncRulesTargets(workspaceRoot) || rulesSynced;
|
|
260
|
-
}
|
|
261
|
-
removeLegacyRegistryRules();
|
|
262
|
-
if (rulesSynced && !silent) {
|
|
263
|
-
log.logSuccess('Synced .aw_rules');
|
|
264
|
-
}
|
|
265
|
-
|
|
266
245
|
if (!args._skipIntegrate) {
|
|
267
246
|
const projectRegistryDir = cwd !== HOME ? join(cwd, '.aw', REGISTRY_DIR) : null;
|
|
268
247
|
const awDirForLinks = (projectRegistryDir && existsSync(projectRegistryDir)) ? projectRegistryDir : null;
|
|
269
|
-
const startupCleanupDir = localAw ? dirname(localAw) : (cwd !== HOME ? cwd : null);
|
|
270
248
|
|
|
271
249
|
if (!silent) {
|
|
272
250
|
const ideSpinner = log.spinner();
|
|
@@ -274,18 +252,12 @@ export async function pullCommand(args) {
|
|
|
274
252
|
const symlinks = linkWorkspace(HOME, awDirForLinks, { silent: true });
|
|
275
253
|
ideSpinner.message('Generating commands...');
|
|
276
254
|
const commands = generateCommands(HOME, { silent: true });
|
|
277
|
-
|
|
278
|
-
ensureAwRuntimeHook(HOME);
|
|
279
|
-
applyStoredStartupPreferences(HOME);
|
|
280
|
-
if (startupCleanupDir) removeWorkspaceHookDefaults(startupCleanupDir);
|
|
255
|
+
copyInstructions(HOME, null, cfg.namespace);
|
|
281
256
|
ideSpinner.stop(`IDE wired — ${chalk.bold(symlinks)} symlink${symlinks !== 1 ? 's' : ''}, ${chalk.bold(commands)} command${commands !== 1 ? 's' : ''}`);
|
|
282
257
|
} else {
|
|
283
258
|
linkWorkspace(HOME, awDirForLinks, { silent: true });
|
|
284
259
|
generateCommands(HOME, { silent: true });
|
|
285
|
-
|
|
286
|
-
ensureAwRuntimeHook(HOME);
|
|
287
|
-
applyStoredStartupPreferences(HOME);
|
|
288
|
-
if (startupCleanupDir) removeWorkspaceHookDefaults(startupCleanupDir);
|
|
260
|
+
copyInstructions(HOME, null, cfg.namespace);
|
|
289
261
|
}
|
|
290
262
|
}
|
|
291
263
|
|
|
@@ -318,73 +290,3 @@ function registerMcp(namespace) {
|
|
|
318
290
|
fmt.logWarn('MCP registration failed (pull still successful)');
|
|
319
291
|
}
|
|
320
292
|
}
|
|
321
|
-
function printDryRun(actions, verbose) {
|
|
322
|
-
const counts = { ADD: 0, UPDATE: 0, CONFLICT: 0, ORPHAN: 0, UNCHANGED: 0 };
|
|
323
|
-
const lines = [];
|
|
324
|
-
|
|
325
|
-
for (const type of ['agents', 'skills', 'commands', 'evals', 'references']) {
|
|
326
|
-
const items = actions.filter(a => a.type === type);
|
|
327
|
-
if (items.length === 0) continue;
|
|
328
|
-
|
|
329
|
-
lines.push(chalk.bold(`${type}/`));
|
|
330
|
-
for (const act of items.sort((a, b) => a.targetFilename.localeCompare(b.targetFilename))) {
|
|
331
|
-
counts[act.action] = (counts[act.action] || 0) + 1;
|
|
332
|
-
if (!verbose && act.action === 'UNCHANGED') continue;
|
|
333
|
-
const ns = act.namespacePath ? chalk.dim(` [${act.namespacePath}]`) : '';
|
|
334
|
-
lines.push(` ${fmt.actionLabel(act.action)} ${act.targetFilename}${ns}`);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
if (lines.length > 0) {
|
|
339
|
-
fmt.note(lines.join('\n'), 'Dry Run');
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
fmt.logInfo(`Summary: ${fmt.countSummary(counts)}`);
|
|
343
|
-
fmt.logWarn('No files modified (--dry-run)');
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
function printSummary(actions, verbose, conflictCount) {
|
|
347
|
-
const conflicts = actions.filter(a => a.action === 'CONFLICT');
|
|
348
|
-
|
|
349
|
-
for (const type of ['agents', 'skills', 'commands', 'evals', 'references']) {
|
|
350
|
-
const typeActions = actions.filter(a => a.type === type);
|
|
351
|
-
if (typeActions.length === 0) continue;
|
|
352
|
-
|
|
353
|
-
const counts = { ADD: 0, UPDATE: 0, CONFLICT: 0, ORPHAN: 0, UNCHANGED: 0 };
|
|
354
|
-
for (const a of typeActions) counts[a.action]++;
|
|
355
|
-
|
|
356
|
-
const parts = [];
|
|
357
|
-
if (counts.ADD > 0) parts.push(chalk.green(`${counts.ADD} new`));
|
|
358
|
-
if (counts.UPDATE > 0) parts.push(chalk.cyan(`${counts.UPDATE} updated`));
|
|
359
|
-
if (counts.CONFLICT > 0) parts.push(chalk.red(`${counts.CONFLICT} conflict`));
|
|
360
|
-
if (counts.ORPHAN > 0) parts.push(chalk.yellow(`${counts.ORPHAN} removed`));
|
|
361
|
-
const detail = parts.length > 0 ? ` (${parts.join(', ')})` : '';
|
|
362
|
-
|
|
363
|
-
fmt.logSuccess(`${typeActions.length} ${type} pulled${detail}`);
|
|
364
|
-
|
|
365
|
-
if (verbose) {
|
|
366
|
-
for (const a of typeActions.filter(a => a.action !== 'UNCHANGED')) {
|
|
367
|
-
const ns = a.namespacePath ? chalk.dim(` [${a.namespacePath}]`) : '';
|
|
368
|
-
fmt.logMessage(` ${fmt.actionLabel(a.action)} ${a.targetFilename}${ns}`);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if (conflicts.length > 0) {
|
|
374
|
-
const conflictLines = conflicts.map(c => {
|
|
375
|
-
return `${chalk.red('both modified:')} ${c.type}/${c.targetFilename}`;
|
|
376
|
-
}).join('\n');
|
|
377
|
-
|
|
378
|
-
fmt.note(
|
|
379
|
-
conflictLines + '\n\n' +
|
|
380
|
-
chalk.dim('Fix conflicts, then re-run pull to verify.\n') +
|
|
381
|
-
chalk.dim('grep -r "<<<<<<< " .aw_registry/'),
|
|
382
|
-
chalk.red('Merge Conflicts')
|
|
383
|
-
);
|
|
384
|
-
|
|
385
|
-
fmt.outro(chalk.red('Pull completed with conflicts — resolve and re-run'));
|
|
386
|
-
process.exit(1);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
fmt.outro('Pull complete');
|
|
390
|
-
}
|
package/commands/push-rules.mjs
CHANGED
|
@@ -23,9 +23,11 @@ export function isRulesPushInput(input) {
|
|
|
23
23
|
|
|
24
24
|
export function resolveRulesPushSource(input, cwd = process.cwd()) {
|
|
25
25
|
const localRulesRoot = join(cwd, RULES_SOURCE_DIR);
|
|
26
|
+
const syncedRulesRoot = join(cwd, '.aw_registry', RULES_SOURCE_DIR);
|
|
26
27
|
|
|
27
28
|
if (!input) {
|
|
28
29
|
if (existsSync(localRulesRoot)) return { sourceRoot: localRulesRoot, sourceType: 'local' };
|
|
30
|
+
if (existsSync(syncedRulesRoot)) return { sourceRoot: syncedRulesRoot, sourceType: 'synced' };
|
|
29
31
|
return null;
|
|
30
32
|
}
|
|
31
33
|
|
|
@@ -37,19 +39,22 @@ export function resolveRulesPushSource(input, cwd = process.cwd()) {
|
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
if (normalizedInput === `.aw_registry/${RULES_SOURCE_DIR}` || normalizedInput.startsWith(`.aw_registry/${RULES_SOURCE_DIR}/`)) {
|
|
42
|
+
if (!existsSync(syncedRulesRoot)) return null;
|
|
43
|
+
|
|
40
44
|
const relativeRulesPath = normalizedInput.slice(`.aw_registry/${RULES_SOURCE_DIR}`.length).replace(/^\/+/, '');
|
|
41
45
|
const localOverridePath = relativeRulesPath ? join(localRulesRoot, relativeRulesPath) : localRulesRoot;
|
|
42
46
|
if (existsSync(localOverridePath)) {
|
|
43
47
|
return { sourceRoot: localRulesRoot, sourceType: 'local' };
|
|
44
48
|
}
|
|
45
|
-
|
|
49
|
+
|
|
50
|
+
return { sourceRoot: syncedRulesRoot, sourceType: 'synced' };
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
return null;
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
export function hasRulesChanges(cwd = process.cwd()) {
|
|
52
|
-
const candidateDirs = [RULES_SOURCE_DIR]
|
|
57
|
+
const candidateDirs = [RULES_SOURCE_DIR, `.aw_registry/${RULES_SOURCE_DIR}`]
|
|
53
58
|
.filter(rel => existsSync(join(cwd, rel)));
|
|
54
59
|
|
|
55
60
|
if (candidateDirs.length === 0) return false;
|
|
@@ -136,7 +141,9 @@ function pushRulesTree(sourceRoot, { repo, dryRun, cwd }) {
|
|
|
136
141
|
fmt.cancel('Nothing to push — remote rules already match local content.');
|
|
137
142
|
}
|
|
138
143
|
|
|
139
|
-
const sourceType = '
|
|
144
|
+
const sourceType = normalize(sourceRoot).includes(normalize(join('.aw_registry', RULES_SOURCE_DIR)))
|
|
145
|
+
? 'synced'
|
|
146
|
+
: 'local';
|
|
140
147
|
const prTitle = buildRulesPrTitle(sourceRoot, cwd);
|
|
141
148
|
const prBody = buildRulesPrBody(sourceRoot, sourceType, cwd);
|
|
142
149
|
|
|
@@ -184,12 +191,16 @@ export function pushRulesCommand(args) {
|
|
|
184
191
|
fmt.cancel([
|
|
185
192
|
'Could not find a rules source to push.',
|
|
186
193
|
'',
|
|
187
|
-
` Checked ${chalk.cyan('.aw_rules/')}.`,
|
|
194
|
+
` Checked ${chalk.cyan('.aw_rules/')} and ${chalk.cyan('.aw_registry/.aw_rules/')}.`,
|
|
188
195
|
'',
|
|
189
196
|
' Use `aw pull platform` first or create a local `.aw_rules/` authoring tree.',
|
|
190
197
|
].join('\n'));
|
|
191
198
|
}
|
|
192
199
|
|
|
200
|
+
if (resolved.sourceType === 'synced') {
|
|
201
|
+
fmt.logWarn('Pushing from synced `.aw_registry/.aw_rules/`. Local `.aw_rules/` is safer for authoring.');
|
|
202
|
+
}
|
|
203
|
+
|
|
193
204
|
pushRulesTree(resolved.sourceRoot, { repo, dryRun, cwd });
|
|
194
205
|
}
|
|
195
206
|
|
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/constants.mjs
CHANGED
|
@@ -25,7 +25,7 @@ export const DOCS_SOURCE_DIR = 'content';
|
|
|
25
25
|
/** Persistent git clone root — ~/.aw/ */
|
|
26
26
|
export const AW_HOME = join(homedir(), '.aw');
|
|
27
27
|
|
|
28
|
-
/** Directory in platform-docs repo containing platform rules (
|
|
28
|
+
/** Directory in platform-docs repo containing platform rules (pulled into .aw_registry/.aw_rules/) */
|
|
29
29
|
export const RULES_SOURCE_DIR = '.aw_rules';
|
|
30
30
|
/** Telemetry endpoint — override with AW_TELEMETRY_URL env var */
|
|
31
31
|
export const TELEMETRY_URL = process.env.AW_TELEMETRY_URL || 'https://services.leadconnectorhq.com/agentic-workspace/api/telemetry/events';
|