@outerlayer/cli 0.1.0

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.
Files changed (55) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +202 -0
  3. package/README.md +320 -0
  4. package/dist/build-info.json +1 -0
  5. package/dist/chunk-4H56Y7O6.js +589 -0
  6. package/dist/chunk-A3WLZX2F.js +120 -0
  7. package/dist/chunk-A6NNKRJU.js +650 -0
  8. package/dist/chunk-D77LS3UI.js +42 -0
  9. package/dist/chunk-DEDV5V4U.js +87324 -0
  10. package/dist/chunk-ECSYRPCC.js +334 -0
  11. package/dist/chunk-IWIUYLDR.js +472 -0
  12. package/dist/chunk-JJP7YLMN.js +25 -0
  13. package/dist/chunk-KFYJV2ZG.js +7978 -0
  14. package/dist/chunk-OZ7C3XUE.js +34 -0
  15. package/dist/chunk-RCQXYLMO.js +96 -0
  16. package/dist/chunk-TFUIDMOB.js +34 -0
  17. package/dist/chunk-VNVZDWO3.js +10 -0
  18. package/dist/chunk-VU34RWDU.js +339 -0
  19. package/dist/chunk-WQ6VGRGZ.js +150 -0
  20. package/dist/chunk-XN2XZFTK.js +165 -0
  21. package/dist/chunk-YMTXR7PJ.js +200 -0
  22. package/dist/chunk-YRYMNBGQ.js +54 -0
  23. package/dist/chunk-Z5GEFMRW.js +45 -0
  24. package/dist/chunk-ZJSNLVCE.js +58 -0
  25. package/dist/cli-3JYGL2OB.js +5311 -0
  26. package/dist/config-POF7DEQW.js +7 -0
  27. package/dist/context-materialize-J6CRQ7O7.js +589 -0
  28. package/dist/emit-artifact-cmd-23F4YKQ3.js +339 -0
  29. package/dist/emit-cmd-TWSYYEZI.js +208 -0
  30. package/dist/emit-commit-credit-cmd-2GX5M2BR.js +121 -0
  31. package/dist/emit-finding-cmd-6K7ERPKG.js +235 -0
  32. package/dist/emit-result-cmd-NDFHMJX7.js +225 -0
  33. package/dist/hook-fast-CBW4ZETX.js +8 -0
  34. package/dist/hook-wrap-fast-36WGE6EX.js +8 -0
  35. package/dist/import-capture-cmd-EUMBGIV3.js +176 -0
  36. package/dist/import-ruler-cmd-7LH2QOMG.js +173 -0
  37. package/dist/index.d.ts +2 -0
  38. package/dist/index.js +25 -0
  39. package/dist/init-PTBITAUO.js +103 -0
  40. package/dist/login-cmd-IRX6LZT7.js +62 -0
  41. package/dist/logs-TRNPQM42.js +63 -0
  42. package/dist/loop-NFSOBUUJ.js +646 -0
  43. package/dist/mcp-install-cmd-FDQH6SEN.js +75 -0
  44. package/dist/mcp-serve-cmd-57EZZOTL.js +123 -0
  45. package/dist/paths-D2VGWWFI.js +6 -0
  46. package/dist/pidfile-PTW76F56.js +8 -0
  47. package/dist/status-WGOJTXZF.js +94 -0
  48. package/dist/statusline-fast-Z5U5CEC6.js +8 -0
  49. package/dist/sync-cmd-C5UGI4SF.js +15 -0
  50. package/dist/watch-V3K4PESQ.js +70 -0
  51. package/dist/work-claim-cmd-FQDWQVT2.js +98 -0
  52. package/dist/work-cmd-CW26EZZO.js +16 -0
  53. package/dist/work-launch-YNNCKIH3.js +6 -0
  54. package/dist/work-pr-cmd-W2QNMWXP.js +115 -0
  55. package/package.json +58 -0
@@ -0,0 +1,334 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { homeFromEnv } from './chunk-VNVZDWO3.js';
4
+ import { existsSync, readFileSync, mkdirSync, copyFileSync, writeFileSync } from 'fs';
5
+ import { join, dirname } from 'path';
6
+
7
+ createRequire(import.meta.url);
8
+ var REGISTERED_EVENTS = ["SessionStart", "SessionEnd", "Stop"];
9
+ var MARKER = "_outerlayer";
10
+ function settingsPath(scope, opts = {}) {
11
+ const home = opts.home ?? homeFromEnv();
12
+ const cwd = opts.cwd ?? process.cwd();
13
+ return scope === "user" ? join(home, ".claude", "settings.json") : join(cwd, ".claude", "settings.json");
14
+ }
15
+ var WRAP_MARKER = "_outerlayerWrapped";
16
+ function tokenizeCommand(command) {
17
+ const tokens = [];
18
+ let current = "";
19
+ let inToken = false;
20
+ let quote = null;
21
+ for (const ch of command) {
22
+ if (quote !== null) {
23
+ if (ch === quote) quote = null;
24
+ else current += ch;
25
+ } else if (ch === '"' || ch === "'") {
26
+ quote = ch;
27
+ inToken = true;
28
+ } else if (/\s/.test(ch)) {
29
+ if (inToken) {
30
+ tokens.push(current);
31
+ current = "";
32
+ inToken = false;
33
+ }
34
+ } else {
35
+ current += ch;
36
+ inToken = true;
37
+ }
38
+ }
39
+ if (quote !== null) return null;
40
+ if (inToken) tokens.push(current);
41
+ return tokens;
42
+ }
43
+ function quoteBin(bin) {
44
+ return /\s/.test(bin) ? `"${bin}"` : bin;
45
+ }
46
+ function isOurBin(bin, cliBin) {
47
+ if (cliBin !== void 0 && normalizeBinPath(bin) === normalizeBinPath(cliBin)) return true;
48
+ const parts = bin.replace(/\\/g, "/").split("/");
49
+ const base = (parts.pop() ?? "").toLowerCase();
50
+ if (base === "outerlayer" || base === "outerlayer.exe" || base === "outerlayer.cmd") return true;
51
+ return parts.some((p) => p.toLowerCase() === ".outerlayer");
52
+ }
53
+ function normalizeBinPath(bin) {
54
+ return bin.replace(/\\/g, "/");
55
+ }
56
+ function parseCliInvocation(command, cliBin) {
57
+ if (typeof command !== "string") return null;
58
+ const tokens = tokenizeCommand(command.trim());
59
+ if (!tokens) return null;
60
+ let i = 0;
61
+ const launcher = (tokens[0] ?? "").replace(/\\/g, "/").split("/").pop() ?? "";
62
+ if (launcher === "node" || launcher === "node.exe" || launcher === "npx") i = 1;
63
+ const bin = tokens[i];
64
+ if (!bin || !isOurBin(bin, cliBin)) return null;
65
+ return tokens.slice(i + 1);
66
+ }
67
+ function parseLifecycleHookCommand(command, cliBin) {
68
+ const args = parseCliInvocation(command, cliBin);
69
+ if (!args || args.length !== 2 || args[0] !== "hook") return null;
70
+ return REGISTERED_EVENTS.includes(args[1]) ? args[1] : null;
71
+ }
72
+ function decodeVerifiedWrapId(token) {
73
+ if (typeof token !== "string") return null;
74
+ const original = decodeWrappedCommand(token);
75
+ const canonical = encodeWrappedCommand(original);
76
+ return canonical.replace(/=+$/, "") === token.replace(/=+$/, "") ? original : null;
77
+ }
78
+ function parseWrappedHookCommand(command, cliBin) {
79
+ const args = parseCliInvocation(command, cliBin);
80
+ if (!args || args[0] !== "hook-wrap" || args[1] !== "--id") return null;
81
+ if (args.length === 4 ? args[3] !== "--capture-output" : args.length !== 3) return null;
82
+ return decodeVerifiedWrapId(args[2]);
83
+ }
84
+ function isOurHookCommand(h) {
85
+ return isOurHookCommandFor(h, void 0);
86
+ }
87
+ function isOurHookCommandFor(h, cliBin) {
88
+ return h[MARKER] === true || parseLifecycleHookCommand(h.command, cliBin) !== null;
89
+ }
90
+ function wrappedOriginalCommand(h, cliBin) {
91
+ if (typeof h[WRAP_MARKER] === "string") return h[WRAP_MARKER];
92
+ return parseWrappedHookCommand(h.command, cliBin);
93
+ }
94
+ function isWrapped(h, cliBin) {
95
+ return wrappedOriginalCommand(h, cliBin) !== null;
96
+ }
97
+ function cliBinResolvable(cliBin) {
98
+ if (!cliBin.includes("/") && !cliBin.includes("\\")) return true;
99
+ return existsSync(cliBin);
100
+ }
101
+ function readSettings(path) {
102
+ if (!existsSync(path)) return null;
103
+ const text = readFileSync(path, "utf8");
104
+ if (text.trim() === "") return {};
105
+ try {
106
+ return JSON.parse(text);
107
+ } catch (err) {
108
+ throw new SettingsParseError(path, err);
109
+ }
110
+ }
111
+ var SettingsParseError = class extends Error {
112
+ constructor(path, cause) {
113
+ super(`refusing to modify ${path}: it is not valid JSON (${cause instanceof Error ? cause.message : cause})`);
114
+ this.path = path;
115
+ this.cause = cause;
116
+ this.name = "SettingsParseError";
117
+ }
118
+ };
119
+ function ourEntry(cliBin, event) {
120
+ return {
121
+ hooks: [{ type: "command", command: `${quoteBin(cliBin)} hook ${event}`, [MARKER]: true }]
122
+ };
123
+ }
124
+ function pruneOurHooks(blocks, cliBin) {
125
+ let removedAny = false;
126
+ const kept = blocks.filter((block) => {
127
+ const original = block.hooks ?? [];
128
+ const remaining = original.filter((h) => !isOurHookCommandFor(h, cliBin));
129
+ if (remaining.length !== original.length) {
130
+ removedAny = true;
131
+ block.hooks = remaining;
132
+ }
133
+ return remaining.length > 0 || original.length === 0;
134
+ });
135
+ return { kept, removedAny };
136
+ }
137
+ function mergeHooks(settings, cliBin) {
138
+ const next = settings ? structuredClone(settings) : {};
139
+ next.hooks = next.hooks ? { ...next.hooks } : {};
140
+ let changed = false;
141
+ for (const event of REGISTERED_EVENTS) {
142
+ const existing = next.hooks[event] ?? [];
143
+ const before = JSON.stringify(existing);
144
+ const foreign = pruneOurHooks(existing, cliBin).kept;
145
+ const desired = [...foreign, ourEntry(cliBin, event)];
146
+ if (before !== JSON.stringify(desired)) changed = true;
147
+ next.hooks[event] = desired;
148
+ }
149
+ return { next, changed };
150
+ }
151
+ function removeHooks(settings, cliBin) {
152
+ const next = settings ? structuredClone(settings) : {};
153
+ if (!next.hooks) return { next, changed: false };
154
+ let changed = false;
155
+ for (const event of Object.keys(next.hooks)) {
156
+ const { kept, removedAny } = pruneOurHooks(next.hooks[event] ?? [], cliBin);
157
+ if (removedAny) changed = true;
158
+ if (kept.length === 0) delete next.hooks[event];
159
+ else next.hooks[event] = kept;
160
+ }
161
+ if (next.hooks && Object.keys(next.hooks).length === 0) delete next.hooks;
162
+ return { next, changed };
163
+ }
164
+ function writeSettings(path, settings, now = Date.now) {
165
+ mkdirSync(dirname(path), { recursive: true });
166
+ let backupPath;
167
+ if (existsSync(path)) {
168
+ backupPath = `${path}.bak-${now()}`;
169
+ copyFileSync(path, backupPath);
170
+ }
171
+ writeSettingsPreservingTrailingNewline(path, settings);
172
+ return backupPath;
173
+ }
174
+ function writeSettingsPreservingTrailingNewline(path, settings) {
175
+ writeFileSync(path, JSON.stringify(settings, null, 2) + "\n");
176
+ }
177
+ function encodeWrappedCommand(command) {
178
+ return Buffer.from(command, "utf8").toString("base64");
179
+ }
180
+ function decodeWrappedCommand(id) {
181
+ return Buffer.from(id, "base64").toString("utf8");
182
+ }
183
+ function listHookCandidates(settings, cliBin) {
184
+ const out = [];
185
+ const hooks = settings?.hooks ?? {};
186
+ for (const event of Object.keys(hooks)) {
187
+ (hooks[event] ?? []).forEach((matcher, matcherIndex) => {
188
+ matcher.hooks.forEach((h, hookIndex) => {
189
+ if (isOurHookCommandFor(h, cliBin)) return;
190
+ const original = wrappedOriginalCommand(h, cliBin);
191
+ out.push({
192
+ event,
193
+ matcherIndex,
194
+ hookIndex,
195
+ matcher: matcher.matcher,
196
+ command: original ?? h.command,
197
+ wrapped: original !== null
198
+ });
199
+ });
200
+ });
201
+ }
202
+ return out;
203
+ }
204
+ function wrapHookCommand(cliBin, h, opts = {}) {
205
+ if (isWrapped(h, cliBin)) return h;
206
+ const id = encodeWrappedCommand(h.command);
207
+ const flag = opts.captureOutput ? " --capture-output" : "";
208
+ return { type: "command", command: `${quoteBin(cliBin)} hook-wrap --id ${id}${flag}`, [WRAP_MARKER]: h.command };
209
+ }
210
+ function unwrapHookCommand(h, cliBin) {
211
+ const original = wrappedOriginalCommand(h, cliBin);
212
+ if (original === null) return h;
213
+ return { type: "command", command: original };
214
+ }
215
+ var AUTO_WRAP_EVENTS = ["PreToolUse", "PostToolUse"];
216
+ function wrapHooks(settings, cliBin, opts = {}) {
217
+ const next = settings ? structuredClone(settings) : {};
218
+ const wrapped = [];
219
+ if (!cliBinResolvable(cliBin)) {
220
+ return { next, changed: false, wrapped, skippedUnresolvedBin: true };
221
+ }
222
+ let changed = false;
223
+ if (!next.hooks) return { next, changed, wrapped };
224
+ for (const event of Object.keys(next.hooks)) {
225
+ if (opts.events && !opts.events.includes(event)) continue;
226
+ const matchers = next.hooks[event] ?? [];
227
+ for (let mi = 0; mi < matchers.length; mi++) {
228
+ const matcher = matchers[mi];
229
+ for (let hi = 0; hi < matcher.hooks.length; hi++) {
230
+ const h = matcher.hooks[hi];
231
+ if (isOurHookCommandFor(h, cliBin) || isWrapped(h, cliBin)) continue;
232
+ const candidate = {
233
+ event,
234
+ matcherIndex: mi,
235
+ hookIndex: hi,
236
+ matcher: matcher.matcher,
237
+ command: h.command,
238
+ wrapped: false
239
+ };
240
+ if (opts.select && !opts.select(candidate)) continue;
241
+ matcher.hooks[hi] = wrapHookCommand(cliBin, h, { captureOutput: opts.captureOutput });
242
+ wrapped.push(candidate);
243
+ changed = true;
244
+ }
245
+ }
246
+ }
247
+ return { next, changed, wrapped };
248
+ }
249
+ function statuslineCommand(cliBin) {
250
+ return `${quoteBin(cliBin)} statusline`;
251
+ }
252
+ function statuslineWrapCommand(cliBin, original) {
253
+ return `${quoteBin(cliBin)} statusline --wrap-id ${encodeWrappedCommand(original)}`;
254
+ }
255
+ function isOurStatuslineSlot(slot, cliBin) {
256
+ if (slot[MARKER] === true) return true;
257
+ const args = parseCliInvocation(slot.command, cliBin);
258
+ if (!args || args[0] !== "statusline") return false;
259
+ return args.length === 1 || statuslineWrappedOriginal(slot, cliBin) !== null;
260
+ }
261
+ function statuslineWrappedOriginal(slot, cliBin) {
262
+ if (typeof slot[WRAP_MARKER] === "string") return slot[WRAP_MARKER];
263
+ const args = parseCliInvocation(slot.command, cliBin);
264
+ if (!args || args.length !== 3 || args[0] !== "statusline" || args[1] !== "--wrap-id") return null;
265
+ return decodeVerifiedWrapId(args[2]);
266
+ }
267
+ function mergeStatusline(settings, cliBin) {
268
+ const next = settings ? structuredClone(settings) : {};
269
+ const slot = next.statusLine;
270
+ if (slot === void 0 || slot === null) {
271
+ next.statusLine = { type: "command", command: statuslineCommand(cliBin), [MARKER]: true };
272
+ return { next, changed: true, outcome: "installed" };
273
+ }
274
+ if (typeof slot !== "object") {
275
+ return { next, changed: false, outcome: "skipped" };
276
+ }
277
+ if (isOurStatuslineSlot(slot, cliBin)) {
278
+ const original2 = statuslineWrappedOriginal(slot, cliBin);
279
+ const desired = original2 !== null ? statuslineWrapCommand(cliBin, original2) : statuslineCommand(cliBin);
280
+ const stripped = slot[MARKER] !== true || original2 !== null && typeof slot[WRAP_MARKER] !== "string";
281
+ if (slot.command === desired && !stripped) {
282
+ return { next, changed: false, outcome: "unchanged", ...original2 !== null ? { wrappedCommand: original2 } : {} };
283
+ }
284
+ slot.command = desired;
285
+ slot[MARKER] = true;
286
+ if (original2 !== null) slot[WRAP_MARKER] = original2;
287
+ return { next, changed: true, outcome: "repaired", ...original2 !== null ? { wrappedCommand: original2 } : {} };
288
+ }
289
+ if (slot.type !== "command" || typeof slot.command !== "string") {
290
+ return { next, changed: false, outcome: "skipped" };
291
+ }
292
+ const original = slot.command;
293
+ slot.command = statuslineWrapCommand(cliBin, original);
294
+ slot[MARKER] = true;
295
+ slot[WRAP_MARKER] = original;
296
+ return { next, changed: true, outcome: "wrapped", wrappedCommand: original };
297
+ }
298
+ function removeStatusline(settings, cliBin) {
299
+ const next = settings ? structuredClone(settings) : {};
300
+ const slot = next.statusLine;
301
+ if (!slot || typeof slot !== "object" || !isOurStatuslineSlot(slot, cliBin)) {
302
+ return { next, changed: false };
303
+ }
304
+ const original = statuslineWrappedOriginal(slot, cliBin);
305
+ if (original !== null) {
306
+ delete slot[MARKER];
307
+ delete slot[WRAP_MARKER];
308
+ slot.command = original;
309
+ return { next, changed: true, restoredCommand: original };
310
+ }
311
+ delete next.statusLine;
312
+ return { next, changed: true };
313
+ }
314
+ function unwrapHooks(settings, cliBin) {
315
+ const next = settings ? structuredClone(settings) : {};
316
+ let changed = false;
317
+ let unwrapped = 0;
318
+ if (!next.hooks) return { next, changed, unwrapped };
319
+ for (const event of Object.keys(next.hooks)) {
320
+ const matchers = next.hooks[event] ?? [];
321
+ for (const matcher of matchers) {
322
+ for (let hi = 0; hi < matcher.hooks.length; hi++) {
323
+ const h = matcher.hooks[hi];
324
+ if (!isWrapped(h, cliBin)) continue;
325
+ matcher.hooks[hi] = unwrapHookCommand(h, cliBin);
326
+ unwrapped++;
327
+ changed = true;
328
+ }
329
+ }
330
+ }
331
+ return { next, changed, unwrapped };
332
+ }
333
+
334
+ export { AUTO_WRAP_EVENTS, REGISTERED_EVENTS, SettingsParseError, cliBinResolvable, decodeWrappedCommand, isOurHookCommand, isOurStatuslineSlot, listHookCandidates, mergeHooks, mergeStatusline, readSettings, removeHooks, removeStatusline, settingsPath, statuslineWrappedOriginal, unwrapHooks, wrapHooks, wrappedOriginalCommand, writeSettings };