@ghl-ai/aw 0.1.35-beta.1 → 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/ide-hooks.mjs +193 -0
- package/package.json +3 -2
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.35-beta.
|
|
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"
|