@ghl-ai/aw 0.1.34 → 0.1.35-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/commands/init.mjs +4 -0
- package/commands/nuke.mjs +5 -1
- package/commands/push.mjs +30 -1
- package/constants.mjs +1 -1
- package/ide-hooks.mjs +193 -0
- package/package.json +3 -2
package/commands/init.mjs
CHANGED
|
@@ -19,6 +19,7 @@ import { generateCommands, copyInstructions, initAwDocs } from '../integrate.mjs
|
|
|
19
19
|
import { setupMcp } from '../mcp.mjs';
|
|
20
20
|
import { autoUpdate, promptUpdate } from '../update.mjs';
|
|
21
21
|
import { installGlobalHooks } from '../hooks.mjs';
|
|
22
|
+
import { installIdeHooks } from '../ide-hooks.mjs';
|
|
22
23
|
|
|
23
24
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
24
25
|
const VERSION = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8')).version;
|
|
@@ -206,6 +207,7 @@ export async function initCommand(args) {
|
|
|
206
207
|
setupMcp(HOME, freshCfg?.namespace || team) || [];
|
|
207
208
|
if (cwd !== HOME) setupMcp(cwd, freshCfg?.namespace || team);
|
|
208
209
|
installGlobalHooks();
|
|
210
|
+
const ideHookFiles = installIdeHooks(HOME);
|
|
209
211
|
|
|
210
212
|
// Link current project if needed
|
|
211
213
|
if (cwd !== HOME && !existsSync(join(cwd, '.aw_registry'))) {
|
|
@@ -286,6 +288,7 @@ export async function initCommand(args) {
|
|
|
286
288
|
const mcpFiles = setupMcp(HOME, team) || [];
|
|
287
289
|
if (cwd !== HOME) setupMcp(cwd, team);
|
|
288
290
|
const hooksInstalled = installGlobalHooks();
|
|
291
|
+
const ideHookFilesInit = installIdeHooks(HOME);
|
|
289
292
|
installIdeTasks();
|
|
290
293
|
|
|
291
294
|
// Step 4: Symlink in current directory if it's a git repo
|
|
@@ -304,6 +307,7 @@ export async function initCommand(args) {
|
|
|
304
307
|
createdFiles: [
|
|
305
308
|
...instructionFiles.map(p => p.startsWith(HOME) ? p.slice(HOME.length + 1) : p),
|
|
306
309
|
...mcpFiles.map(p => p.startsWith(HOME) ? p.slice(HOME.length + 1) : p),
|
|
310
|
+
...(ideHookFilesInit || []).map(p => p.startsWith(HOME) ? p.slice(HOME.length + 1) : p),
|
|
307
311
|
],
|
|
308
312
|
globalHooksDir: hooksInstalled ? join(HOME, '.aw', 'hooks') : null,
|
|
309
313
|
};
|
package/commands/nuke.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { execSync } from 'node:child_process';
|
|
|
9
9
|
import * as fmt from '../fmt.mjs';
|
|
10
10
|
import { chalk } from '../fmt.mjs';
|
|
11
11
|
import { removeGlobalHooks } from '../hooks.mjs';
|
|
12
|
+
import { removeIdeHooks } from '../ide-hooks.mjs';
|
|
12
13
|
|
|
13
14
|
const HOME = homedir();
|
|
14
15
|
const GLOBAL_AW_DIR = join(HOME, '.aw_registry');
|
|
@@ -249,7 +250,10 @@ export function nukeCommand(args) {
|
|
|
249
250
|
// 4. Remove git hooks (core.hooksPath + legacy template)
|
|
250
251
|
removeGitHooks(manifest);
|
|
251
252
|
|
|
252
|
-
// 5. Remove IDE
|
|
253
|
+
// 5. Remove IDE session hooks (superpowers bootstrap)
|
|
254
|
+
removeIdeHooks(HOME);
|
|
255
|
+
|
|
256
|
+
// 6. Remove IDE auto-init tasks
|
|
253
257
|
removeIdeTasks();
|
|
254
258
|
|
|
255
259
|
// 5b. Remove upgrade lock/log (inside .aw_registry, must happen before dir removal)
|
package/commands/push.mjs
CHANGED
|
@@ -73,12 +73,17 @@ function collectBatchFiles(folderAbsPath, workspaceDir) {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
|
-
* Collect all modified
|
|
76
|
+
* Collect all modified + untracked files for no-args push.
|
|
77
|
+
* 1. Manifest-tracked files that are modified or template-derived (never pushed).
|
|
78
|
+
* 2. Filesystem files not in the manifest at all (newly added by user).
|
|
77
79
|
* Returns array of { absPath, registryTarget, type, namespace, slug, isDir }.
|
|
78
80
|
*/
|
|
79
81
|
function collectModifiedFiles(workspaceDir) {
|
|
80
82
|
const manifest = loadManifest(workspaceDir);
|
|
83
|
+
const manifestKeys = new Set(Object.keys(manifest.files || {}));
|
|
81
84
|
const files = [];
|
|
85
|
+
|
|
86
|
+
// 1. Manifest-tracked: modified or never-pushed
|
|
82
87
|
for (const [key, entry] of Object.entries(manifest.files || {})) {
|
|
83
88
|
const filePath = join(workspaceDir, key);
|
|
84
89
|
if (!existsSync(filePath)) continue;
|
|
@@ -99,6 +104,30 @@ function collectModifiedFiles(workspaceDir) {
|
|
|
99
104
|
}
|
|
100
105
|
}
|
|
101
106
|
}
|
|
107
|
+
|
|
108
|
+
// 2. Untracked: files on disk but not in manifest (e.g. manually added)
|
|
109
|
+
for (const name of readdirSync(workspaceDir, { withFileTypes: true })) {
|
|
110
|
+
if (!name.isDirectory() || name.name.startsWith('.')) continue;
|
|
111
|
+
const nsDir = join(workspaceDir, name.name);
|
|
112
|
+
const entries = walkRegistryTree(nsDir, name.name);
|
|
113
|
+
for (const entry of entries) {
|
|
114
|
+
// Build the manifest key for this file
|
|
115
|
+
const manifestKey = (entry.type === 'skills' || entry.type === 'evals')
|
|
116
|
+
? `${entry.namespacePath}/${entry.type}/${entry.slug}/${entry.skillRelPath || entry.filename}`
|
|
117
|
+
: `${entry.namespacePath}/${entry.type}/${entry.filename}`;
|
|
118
|
+
if (manifestKeys.has(manifestKey)) continue; // Already handled above
|
|
119
|
+
const registryTarget = `${REGISTRY_DIR}/${manifestKey}`;
|
|
120
|
+
files.push({
|
|
121
|
+
absPath: entry.sourcePath,
|
|
122
|
+
registryTarget,
|
|
123
|
+
type: entry.type,
|
|
124
|
+
namespace: entry.namespacePath,
|
|
125
|
+
slug: entry.slug,
|
|
126
|
+
isDir: false,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
102
131
|
return files;
|
|
103
132
|
}
|
|
104
133
|
|
package/constants.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// constants.mjs — Single source of truth for registry settings.
|
|
2
2
|
|
|
3
3
|
/** Base branch for PRs and sync checkout */
|
|
4
|
-
export const REGISTRY_BASE_BRANCH = '
|
|
4
|
+
export const REGISTRY_BASE_BRANCH = 'sync/platform-superpowers-l8ynb';
|
|
5
5
|
|
|
6
6
|
/** Default registry repository */
|
|
7
7
|
export const REGISTRY_REPO = 'GoHighLevel/platform-docs';
|
package/ide-hooks.mjs
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// ide-hooks.mjs — Generate IDE session-start hooks for superpowers bootstrap.
|
|
2
|
+
//
|
|
3
|
+
// installIdeHooks(cwd) → writes/merges hook configs into ~/.claude/ and ~/.cursor/
|
|
4
|
+
// removeIdeHooks(cwd) → removes aw-generated hook entries from IDE hook configs
|
|
5
|
+
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
import * as fmt from './fmt.mjs';
|
|
10
|
+
|
|
11
|
+
const HOME = homedir();
|
|
12
|
+
const AW_REGISTRY = join(HOME, '.aw_registry');
|
|
13
|
+
const HOOKS_DIR = join(AW_REGISTRY, 'platform', 'superpowers', 'hooks');
|
|
14
|
+
const SESSION_START = join(HOOKS_DIR, 'session-start');
|
|
15
|
+
const RUN_HOOK_CMD = join(HOOKS_DIR, 'run-hook.cmd');
|
|
16
|
+
|
|
17
|
+
const AW_MARKER = 'aw-superpowers-session';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Install IDE session-start hooks that bootstrap using-superpowers.
|
|
21
|
+
* Only installs if the superpowers hooks exist in the registry.
|
|
22
|
+
* Merges with existing hook configs — never clobbers user hooks.
|
|
23
|
+
* @returns {string[]} paths of created/modified hook config files
|
|
24
|
+
*/
|
|
25
|
+
export function installIdeHooks(cwd) {
|
|
26
|
+
if (!existsSync(SESSION_START)) return [];
|
|
27
|
+
|
|
28
|
+
const created = [];
|
|
29
|
+
|
|
30
|
+
const claudeResult = installClaudeCodeHooks(cwd);
|
|
31
|
+
if (claudeResult) created.push(claudeResult);
|
|
32
|
+
|
|
33
|
+
const cursorResult = installCursorHooks(cwd);
|
|
34
|
+
if (cursorResult) created.push(cursorResult);
|
|
35
|
+
|
|
36
|
+
if (created.length > 0) {
|
|
37
|
+
fmt.logStep('IDE session hooks installed (superpowers bootstrap)');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return created;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Remove aw-generated hook entries from IDE hook configs.
|
|
45
|
+
*/
|
|
46
|
+
export function removeIdeHooks(cwd) {
|
|
47
|
+
removeClaudeCodeHooks(cwd);
|
|
48
|
+
removeCursorHooks(cwd);
|
|
49
|
+
fmt.logStep('IDE session hooks removed');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ── Claude Code ─────────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
function installClaudeCodeHooks(cwd) {
|
|
55
|
+
const hooksPath = join(HOME, '.claude', 'hooks.json');
|
|
56
|
+
|
|
57
|
+
const hookEntry = {
|
|
58
|
+
matcher: AW_MARKER,
|
|
59
|
+
hooks: [{
|
|
60
|
+
type: 'command',
|
|
61
|
+
command: `"${RUN_HOOK_CMD}" session-start`,
|
|
62
|
+
async: false,
|
|
63
|
+
}],
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
let config;
|
|
67
|
+
if (existsSync(hooksPath)) {
|
|
68
|
+
try {
|
|
69
|
+
config = JSON.parse(readFileSync(hooksPath, 'utf8'));
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!config.hooks) config.hooks = {};
|
|
75
|
+
if (!Array.isArray(config.hooks.SessionStart)) config.hooks.SessionStart = [];
|
|
76
|
+
|
|
77
|
+
const existing = config.hooks.SessionStart.findIndex(
|
|
78
|
+
e => e.matcher === AW_MARKER
|
|
79
|
+
);
|
|
80
|
+
if (existing !== -1) {
|
|
81
|
+
config.hooks.SessionStart[existing] = hookEntry;
|
|
82
|
+
} else {
|
|
83
|
+
config.hooks.SessionStart.push(hookEntry);
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
mkdirSync(join(HOME, '.claude'), { recursive: true });
|
|
87
|
+
config = {
|
|
88
|
+
hooks: {
|
|
89
|
+
SessionStart: [hookEntry],
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
writeFileSync(hooksPath, JSON.stringify(config, null, 2) + '\n');
|
|
95
|
+
return hooksPath;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function removeClaudeCodeHooks(cwd) {
|
|
99
|
+
const hooksPath = join(HOME, '.claude', 'hooks.json');
|
|
100
|
+
if (!existsSync(hooksPath)) return;
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const config = JSON.parse(readFileSync(hooksPath, 'utf8'));
|
|
104
|
+
if (!config.hooks?.SessionStart) return;
|
|
105
|
+
|
|
106
|
+
config.hooks.SessionStart = config.hooks.SessionStart.filter(
|
|
107
|
+
e => e.matcher !== AW_MARKER
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
if (config.hooks.SessionStart.length === 0) {
|
|
111
|
+
delete config.hooks.SessionStart;
|
|
112
|
+
}
|
|
113
|
+
if (Object.keys(config.hooks).length === 0) {
|
|
114
|
+
delete config.hooks;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (Object.keys(config).length === 0) {
|
|
118
|
+
writeFileSync(hooksPath, '{}\n');
|
|
119
|
+
} else {
|
|
120
|
+
writeFileSync(hooksPath, JSON.stringify(config, null, 2) + '\n');
|
|
121
|
+
}
|
|
122
|
+
} catch { /* best effort */ }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ── Cursor ──────────────────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
function installCursorHooks(cwd) {
|
|
128
|
+
const hooksPath = join(HOME, '.cursor', 'hooks.json');
|
|
129
|
+
|
|
130
|
+
const hookEntry = {
|
|
131
|
+
command: SESSION_START,
|
|
132
|
+
_aw: AW_MARKER,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
let config;
|
|
136
|
+
if (existsSync(hooksPath)) {
|
|
137
|
+
try {
|
|
138
|
+
config = JSON.parse(readFileSync(hooksPath, 'utf8'));
|
|
139
|
+
} catch {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!config.hooks) config.hooks = {};
|
|
144
|
+
if (!Array.isArray(config.hooks.sessionStart)) config.hooks.sessionStart = [];
|
|
145
|
+
|
|
146
|
+
const existing = config.hooks.sessionStart.findIndex(
|
|
147
|
+
e => e._aw === AW_MARKER
|
|
148
|
+
);
|
|
149
|
+
if (existing !== -1) {
|
|
150
|
+
config.hooks.sessionStart[existing] = hookEntry;
|
|
151
|
+
} else {
|
|
152
|
+
config.hooks.sessionStart.push(hookEntry);
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
mkdirSync(join(HOME, '.cursor'), { recursive: true });
|
|
156
|
+
config = {
|
|
157
|
+
version: 1,
|
|
158
|
+
hooks: {
|
|
159
|
+
sessionStart: [hookEntry],
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
writeFileSync(hooksPath, JSON.stringify(config, null, 2) + '\n');
|
|
165
|
+
return hooksPath;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function removeCursorHooks(cwd) {
|
|
169
|
+
const hooksPath = join(HOME, '.cursor', 'hooks.json');
|
|
170
|
+
if (!existsSync(hooksPath)) return;
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const config = JSON.parse(readFileSync(hooksPath, 'utf8'));
|
|
174
|
+
if (!config.hooks?.sessionStart) return;
|
|
175
|
+
|
|
176
|
+
config.hooks.sessionStart = config.hooks.sessionStart.filter(
|
|
177
|
+
e => e._aw !== AW_MARKER
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
if (config.hooks.sessionStart.length === 0) {
|
|
181
|
+
delete config.hooks.sessionStart;
|
|
182
|
+
}
|
|
183
|
+
if (config.hooks && Object.keys(config.hooks).length === 0) {
|
|
184
|
+
delete config.hooks;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (Object.keys(config).length <= 1 && config.version) {
|
|
188
|
+
writeFileSync(hooksPath, JSON.stringify({ version: config.version }, null, 2) + '\n');
|
|
189
|
+
} else {
|
|
190
|
+
writeFileSync(hooksPath, JSON.stringify(config, null, 2) + '\n');
|
|
191
|
+
}
|
|
192
|
+
} catch { /* best effort */ }
|
|
193
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghl-ai/aw",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.35-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": {
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"registry.mjs",
|
|
25
25
|
"apply.mjs",
|
|
26
26
|
"update.mjs",
|
|
27
|
-
"hooks.mjs"
|
|
27
|
+
"hooks.mjs",
|
|
28
|
+
"ide-hooks.mjs"
|
|
28
29
|
],
|
|
29
30
|
"engines": {
|
|
30
31
|
"node": ">=18.0.0"
|