harborloop 0.3.0 → 0.4.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.
Files changed (120) hide show
  1. package/bin/checksums.json +12 -0
  2. package/bin/harbor.mjs +188 -0
  3. package/package.json +14 -30
  4. package/dist/api.d.ts +0 -109
  5. package/dist/api.js +0 -409
  6. package/dist/auth/login.d.ts +0 -14
  7. package/dist/auth/login.js +0 -158
  8. package/dist/auth/loopback.d.ts +0 -11
  9. package/dist/auth/loopback.js +0 -87
  10. package/dist/auth/pkce.d.ts +0 -7
  11. package/dist/auth/pkce.js +0 -14
  12. package/dist/auth/store.d.ts +0 -26
  13. package/dist/auth/store.js +0 -107
  14. package/dist/cli-release.d.ts +0 -31
  15. package/dist/cli-release.js +0 -102
  16. package/dist/commands/bind.d.ts +0 -23
  17. package/dist/commands/bind.js +0 -86
  18. package/dist/commands/context.d.ts +0 -12
  19. package/dist/commands/context.js +0 -80
  20. package/dist/commands/daemon.d.ts +0 -39
  21. package/dist/commands/daemon.js +0 -140
  22. package/dist/commands/doctor.d.ts +0 -16
  23. package/dist/commands/doctor.js +0 -937
  24. package/dist/commands/init.d.ts +0 -36
  25. package/dist/commands/init.js +0 -192
  26. package/dist/commands/status.d.ts +0 -5
  27. package/dist/commands/status.js +0 -164
  28. package/dist/commands/sync.d.ts +0 -40
  29. package/dist/commands/sync.js +0 -242
  30. package/dist/commands/toggle.d.ts +0 -16
  31. package/dist/commands/toggle.js +0 -69
  32. package/dist/commands/unbind.d.ts +0 -14
  33. package/dist/commands/unbind.js +0 -60
  34. package/dist/commands/workspaces.d.ts +0 -18
  35. package/dist/commands/workspaces.js +0 -41
  36. package/dist/context-budget.d.ts +0 -40
  37. package/dist/context-budget.js +0 -259
  38. package/dist/daemon/install.d.ts +0 -50
  39. package/dist/daemon/install.js +0 -192
  40. package/dist/daemon/loop.d.ts +0 -15
  41. package/dist/daemon/loop.js +0 -84
  42. package/dist/daemon/state.d.ts +0 -79
  43. package/dist/daemon/state.js +0 -213
  44. package/dist/daemon/tick.d.ts +0 -37
  45. package/dist/daemon/tick.js +0 -231
  46. package/dist/documents/apply.d.ts +0 -21
  47. package/dist/documents/apply.js +0 -58
  48. package/dist/documents/bundle.d.ts +0 -41
  49. package/dist/documents/bundle.js +0 -43
  50. package/dist/documents/inspect.d.ts +0 -12
  51. package/dist/documents/inspect.js +0 -33
  52. package/dist/documents/manifest.d.ts +0 -14
  53. package/dist/documents/manifest.js +0 -52
  54. package/dist/documents/pointer.d.ts +0 -32
  55. package/dist/documents/pointer.js +0 -135
  56. package/dist/documents/write.d.ts +0 -57
  57. package/dist/documents/write.js +0 -211
  58. package/dist/endpoints.d.ts +0 -7
  59. package/dist/endpoints.js +0 -15
  60. package/dist/env-bundle.d.ts +0 -25
  61. package/dist/env-bundle.js +0 -33
  62. package/dist/env-scan.d.ts +0 -27
  63. package/dist/env-scan.js +0 -41
  64. package/dist/env-sync.d.ts +0 -31
  65. package/dist/env-sync.js +0 -37
  66. package/dist/index.d.ts +0 -11
  67. package/dist/index.js +0 -282
  68. package/dist/install-hooks.d.ts +0 -11
  69. package/dist/install-hooks.js +0 -64
  70. package/dist/lock.d.ts +0 -31
  71. package/dist/lock.js +0 -192
  72. package/dist/machine.d.ts +0 -13
  73. package/dist/machine.js +0 -53
  74. package/dist/memories.d.ts +0 -31
  75. package/dist/memories.js +0 -74
  76. package/dist/origin.d.ts +0 -4
  77. package/dist/origin.js +0 -59
  78. package/dist/paths.d.ts +0 -12
  79. package/dist/paths.js +0 -45
  80. package/dist/providers/claude.d.ts +0 -65
  81. package/dist/providers/claude.js +0 -412
  82. package/dist/providers/codex-trust.d.ts +0 -36
  83. package/dist/providers/codex-trust.js +0 -141
  84. package/dist/providers/codex.d.ts +0 -46
  85. package/dist/providers/codex.js +0 -520
  86. package/dist/providers/cursor.d.ts +0 -45
  87. package/dist/providers/cursor.js +0 -344
  88. package/dist/providers/detect.d.ts +0 -16
  89. package/dist/providers/detect.js +0 -70
  90. package/dist/providers/hook-coverage.d.ts +0 -5
  91. package/dist/providers/hook-coverage.js +0 -47
  92. package/dist/providers/hook-entry.d.ts +0 -1
  93. package/dist/providers/hook-entry.js +0 -8
  94. package/dist/providers/surfaces.d.ts +0 -19
  95. package/dist/providers/surfaces.js +0 -91
  96. package/dist/reclaim.d.ts +0 -17
  97. package/dist/reclaim.js +0 -33
  98. package/dist/render.d.ts +0 -17
  99. package/dist/render.js +0 -61
  100. package/dist/repo-sync.d.ts +0 -27
  101. package/dist/repo-sync.js +0 -34
  102. package/dist/repos.d.ts +0 -11
  103. package/dist/repos.js +0 -82
  104. package/dist/skills.d.ts +0 -84
  105. package/dist/skills.js +0 -383
  106. package/dist/slugify.d.ts +0 -1
  107. package/dist/slugify.js +0 -8
  108. package/dist/state.d.ts +0 -23
  109. package/dist/state.js +0 -161
  110. package/dist/sync-report.d.ts +0 -59
  111. package/dist/sync-report.js +0 -87
  112. package/hooks/_recall.mjs +0 -288
  113. package/hooks/_receipt.mjs +0 -47
  114. package/hooks/_shared.mjs +0 -1610
  115. package/hooks/_upgrade.mjs +0 -51
  116. package/hooks/session-capture.mjs +0 -60
  117. package/hooks/session-compact.mjs +0 -19
  118. package/hooks/session-context.mjs +0 -258
  119. package/hooks/session-report.mjs +0 -42
  120. package/hooks/session-toolcontext.mjs +0 -183
@@ -1,344 +0,0 @@
1
- import { homedir } from 'node:os';
2
- import { join } from 'node:path';
3
- import { ensureConfigDir, readIfExists, updateFileLocked } from '../lock.js';
4
- import { isUnrunnableCommandEntry } from './hook-entry.js';
5
- import { HOOK_CAPTURE_BASENAME, HOOK_CONTEXT_BASENAME, HOOK_REPORT_BASENAME, HOOK_COMPACT_BASENAME, HOOK_TOOLCONTEXT_BASENAME, LEGACY_MCP_KEYS, MCP_KEY, } from './claude.js';
6
- const HOOK_CONTEXT_TIMEOUT_S = 8;
7
- const HOOK_REPORT_TIMEOUT_S = 5;
8
- const HOOK_CAPTURE_TIMEOUT_S = 10;
9
- const HOOK_TOOLCONTEXT_TIMEOUT_S = 5;
10
- const HOOK_COMPACT_TIMEOUT_S = 2;
11
- const EVENT_SESSION_START = 'sessionStart';
12
- const EVENT_SESSION_END = 'sessionEnd';
13
- const EVENT_STOP = 'stop';
14
- const EVENT_POST_TOOL_USE = 'postToolUse';
15
- const EVENT_PRE_TOOL_USE = 'preToolUse';
16
- const EVENT_PRE_COMPACT = 'preCompact';
17
- export const MANAGED_HOOK_EVENTS = [
18
- EVENT_SESSION_START,
19
- EVENT_SESSION_END,
20
- EVENT_STOP,
21
- EVENT_POST_TOOL_USE,
22
- EVENT_PRE_TOOL_USE,
23
- EVENT_PRE_COMPACT,
24
- ];
25
- const CURSOR_HOOK_EVENTS = MANAGED_HOOK_EVENTS;
26
- const TOOLCONTEXT_EDIT_MATCHER = 'Write';
27
- const TOOLCONTEXT_SHELL_MATCHER = 'Shell';
28
- const HOOKS_CONFIG_VERSION = 1;
29
- export function resolveCursorPaths(home = homedir()) {
30
- const cursorDir = join(home, '.cursor');
31
- return {
32
- cursorDir,
33
- mcpJsonPath: join(cursorDir, 'mcp.json'),
34
- hooksJsonPath: join(cursorDir, 'hooks.json'),
35
- };
36
- }
37
- export function stripJsonComments(raw) {
38
- let out = '';
39
- let hadComments = false;
40
- let i = 0;
41
- while (i < raw.length) {
42
- const ch = raw[i];
43
- if (ch === '"') {
44
- out += ch;
45
- i += 1;
46
- while (i < raw.length) {
47
- const c = raw[i];
48
- out += c;
49
- i += 1;
50
- if (c === '\\') {
51
- if (i < raw.length) {
52
- out += raw[i];
53
- i += 1;
54
- }
55
- continue;
56
- }
57
- if (c === '"')
58
- break;
59
- }
60
- continue;
61
- }
62
- if (ch === '/' && raw[i + 1] === '/') {
63
- hadComments = true;
64
- while (i < raw.length && raw[i] !== '\n')
65
- i += 1;
66
- continue;
67
- }
68
- if (ch === '/' && raw[i + 1] === '*') {
69
- hadComments = true;
70
- i += 2;
71
- while (i < raw.length && !(raw[i] === '*' && raw[i + 1] === '/'))
72
- i += 1;
73
- i += 2;
74
- continue;
75
- }
76
- out += ch;
77
- i += 1;
78
- }
79
- return { text: out, hadComments };
80
- }
81
- function assertRoundTrips(hadComments, file) {
82
- if (!hadComments)
83
- return;
84
- throw new Error(`${file} contains comments, which this writer would delete. ` +
85
- `Remove them (or move them into a "_comment" string field) and re-run.`);
86
- }
87
- function parseRoot(existing, allowEmpty, file) {
88
- if (existing.trim().length === 0) {
89
- if (allowEmpty)
90
- return { root: {}, hadComments: false };
91
- throw new Error(`${file} is empty`);
92
- }
93
- const { text, hadComments } = stripJsonComments(existing);
94
- let root;
95
- try {
96
- root = JSON.parse(text);
97
- }
98
- catch (err) {
99
- throw new Error(`Failed to parse ${file}: ${err.message}`);
100
- }
101
- if (root === null || typeof root !== 'object' || Array.isArray(root)) {
102
- throw new Error(`${file} root is not an object`);
103
- }
104
- return { root: root, hadComments };
105
- }
106
- function parseQuietly(existing) {
107
- if (existing.trim() === '')
108
- return null;
109
- try {
110
- const root = JSON.parse(stripJsonComments(existing).text);
111
- if (root === null || typeof root !== 'object' || Array.isArray(root))
112
- return null;
113
- return root;
114
- }
115
- catch {
116
- return null;
117
- }
118
- }
119
- const serialise = (root) => `${JSON.stringify(root, null, 2)}\n`;
120
- export function buildMcpServerEntry(mcpUrl, token) {
121
- return { url: mcpUrl, headers: { Authorization: `Bearer ${token}` } };
122
- }
123
- function deleteAllOurKeys(servers) {
124
- delete servers[MCP_KEY];
125
- for (const legacy of LEGACY_MCP_KEYS)
126
- delete servers[legacy];
127
- }
128
- export function mergeMcpServer(existing, mcpUrl, token) {
129
- const { root, hadComments } = parseRoot(existing, true, 'mcp.json');
130
- assertRoundTrips(hadComments, 'mcp.json');
131
- let servers = root.mcpServers;
132
- if (servers === undefined) {
133
- servers = {};
134
- root.mcpServers = servers;
135
- }
136
- if (servers === null || typeof servers !== 'object' || Array.isArray(servers)) {
137
- throw new Error('mcp.json `mcpServers` is not an object');
138
- }
139
- deleteAllOurKeys(servers);
140
- servers[MCP_KEY] = buildMcpServerEntry(mcpUrl, token);
141
- return serialise(root);
142
- }
143
- export function removeMcpServer(existing) {
144
- if (existing.trim() === '')
145
- return existing;
146
- const { root, hadComments } = parseRoot(existing, false, 'mcp.json');
147
- assertRoundTrips(hadComments, 'mcp.json');
148
- const servers = root.mcpServers;
149
- if (servers !== null && typeof servers === 'object' && !Array.isArray(servers)) {
150
- deleteAllOurKeys(servers);
151
- }
152
- return serialise(root);
153
- }
154
- export function hasMcpServer(existing) {
155
- return readInstalledToken(existing) !== null;
156
- }
157
- export function readInstalledToken(existing) {
158
- const root = parseQuietly(existing);
159
- const servers = root?.mcpServers;
160
- if (servers === null || servers === undefined || typeof servers !== 'object')
161
- return null;
162
- const entry = servers[MCP_KEY];
163
- if (entry === null || entry === undefined || typeof entry !== 'object')
164
- return null;
165
- const headers = entry.headers;
166
- if (headers === null || headers === undefined || typeof headers !== 'object')
167
- return null;
168
- const auth = headers.Authorization;
169
- if (typeof auth !== 'string' || auth.trim() === '')
170
- return null;
171
- return auth.startsWith('Bearer ') ? auth.slice('Bearer '.length) : auth;
172
- }
173
- export function readInstalledMcpUrl(existing) {
174
- const root = parseQuietly(existing);
175
- const servers = root?.mcpServers;
176
- const entry = servers?.[MCP_KEY];
177
- return typeof entry?.url === 'string' ? entry.url : null;
178
- }
179
- const HOOK_BASENAMES = [
180
- HOOK_CONTEXT_BASENAME,
181
- HOOK_REPORT_BASENAME,
182
- HOOK_CAPTURE_BASENAME,
183
- HOOK_TOOLCONTEXT_BASENAME,
184
- HOOK_COMPACT_BASENAME,
185
- ];
186
- export function shellQuote(command) {
187
- return /^[A-Za-z0-9_@%+=:,./-]+$/.test(command)
188
- ? command
189
- : `'${command.replace(/'/g, `'\\''`)}'`;
190
- }
191
- export function isOurHookCommand(command) {
192
- if (typeof command !== 'string')
193
- return false;
194
- const bare = command.trim().replace(/^['"]|['"]$/g, '');
195
- const base = bare.split(/[\\/]/).pop();
196
- return base !== undefined && HOOK_BASENAMES.includes(base);
197
- }
198
- function stripOurHooks(event) {
199
- if (!Array.isArray(event))
200
- return [];
201
- const kept = [];
202
- for (const entry of event) {
203
- if (entry === null || typeof entry !== 'object' || Array.isArray(entry)) {
204
- kept.push(entry);
205
- continue;
206
- }
207
- if (isUnrunnableCommandEntry(entry))
208
- continue;
209
- if (isOurHookCommand(entry.command))
210
- continue;
211
- kept.push(entry);
212
- }
213
- return kept;
214
- }
215
- const hookEntry = (command, timeout, matcher) => ({
216
- ...(matcher === undefined ? {} : { matcher }),
217
- command: shellQuote(command),
218
- timeout,
219
- });
220
- export function mergeHooks(existing, paths) {
221
- const { root, hadComments } = parseRoot(existing, true, 'hooks.json');
222
- assertRoundTrips(hadComments, 'hooks.json');
223
- if (typeof root.version !== 'number' || !Number.isInteger(root.version) || root.version < 1) {
224
- root.version = HOOKS_CONFIG_VERSION;
225
- }
226
- let hooks = root.hooks;
227
- if (hooks === undefined) {
228
- hooks = {};
229
- root.hooks = hooks;
230
- }
231
- if (hooks === null || typeof hooks !== 'object' || Array.isArray(hooks)) {
232
- throw new Error('hooks.json `hooks` is not an object');
233
- }
234
- const h = hooks;
235
- h[EVENT_SESSION_START] = [
236
- ...stripOurHooks(h[EVENT_SESSION_START]),
237
- hookEntry(paths.contextHookPath, HOOK_CONTEXT_TIMEOUT_S),
238
- ];
239
- h[EVENT_SESSION_END] = [
240
- ...stripOurHooks(h[EVENT_SESSION_END]),
241
- hookEntry(paths.reportHookPath, HOOK_REPORT_TIMEOUT_S),
242
- ];
243
- h[EVENT_STOP] = [
244
- ...stripOurHooks(h[EVENT_STOP]),
245
- hookEntry(paths.captureHookPath, HOOK_CAPTURE_TIMEOUT_S),
246
- ];
247
- h[EVENT_POST_TOOL_USE] = [
248
- ...stripOurHooks(h[EVENT_POST_TOOL_USE]),
249
- hookEntry(paths.toolContextHookPath, HOOK_TOOLCONTEXT_TIMEOUT_S, TOOLCONTEXT_EDIT_MATCHER),
250
- ];
251
- h[EVENT_PRE_TOOL_USE] = [
252
- ...stripOurHooks(h[EVENT_PRE_TOOL_USE]),
253
- hookEntry(paths.toolContextHookPath, HOOK_TOOLCONTEXT_TIMEOUT_S, TOOLCONTEXT_SHELL_MATCHER),
254
- ];
255
- h[EVENT_PRE_COMPACT] = [
256
- ...stripOurHooks(h[EVENT_PRE_COMPACT]),
257
- hookEntry(paths.compactHookPath, HOOK_COMPACT_TIMEOUT_S),
258
- ];
259
- return serialise(root);
260
- }
261
- export function removeHooks(existing) {
262
- if (existing.trim() === '')
263
- return existing;
264
- const { root, hadComments } = parseRoot(existing, false, 'hooks.json');
265
- assertRoundTrips(hadComments, 'hooks.json');
266
- const hooks = root.hooks;
267
- if (hooks !== null && typeof hooks === 'object' && !Array.isArray(hooks)) {
268
- const h = hooks;
269
- for (const event of CURSOR_HOOK_EVENTS) {
270
- if (!(event in h))
271
- continue;
272
- const stripped = stripOurHooks(h[event]);
273
- if (stripped.length === 0)
274
- delete h[event];
275
- else
276
- h[event] = stripped;
277
- }
278
- if (Object.keys(h).length === 0) {
279
- delete root.hooks;
280
- delete root.version;
281
- }
282
- }
283
- return serialise(root);
284
- }
285
- export function readInstalledHooks(existing) {
286
- const commands = [];
287
- const events = [];
288
- const empty = { installed: false, commands, events };
289
- const root = parseQuietly(existing);
290
- if (root === null)
291
- return empty;
292
- const hooks = root.hooks;
293
- if (hooks === null || typeof hooks !== 'object' || Array.isArray(hooks))
294
- return empty;
295
- for (const event of CURSOR_HOOK_EVENTS) {
296
- const entries = hooks[event];
297
- if (!Array.isArray(entries))
298
- continue;
299
- const before = commands.length;
300
- for (const entry of entries) {
301
- if (entry === null || typeof entry !== 'object')
302
- continue;
303
- const command = entry.command;
304
- if (isOurHookCommand(command)) {
305
- commands.push(String(command).trim().replace(/^['"]|['"]$/g, ''));
306
- }
307
- }
308
- if (commands.length > before)
309
- events.push(event);
310
- }
311
- return { installed: commands.length > 0, commands, events };
312
- }
313
- export function hasHooks(existing) {
314
- return readInstalledHooks(existing).installed;
315
- }
316
- const ensureCursorDir = ensureConfigDir;
317
- export async function readCursorMcpJson(home) {
318
- return readIfExists(resolveCursorPaths(home).mcpJsonPath);
319
- }
320
- export async function readCursorHooksJson(home) {
321
- return readIfExists(resolveCursorPaths(home).hooksJsonPath);
322
- }
323
- export async function installMcpServer(opts) {
324
- const paths = resolveCursorPaths(opts.home);
325
- await ensureCursorDir(paths.cursorDir);
326
- await updateFileLocked(paths.mcpJsonPath, (existing) => mergeMcpServer(existing, opts.mcpUrl, opts.token));
327
- return paths.mcpJsonPath;
328
- }
329
- export async function uninstallMcpServer(home) {
330
- const paths = resolveCursorPaths(home);
331
- await updateFileLocked(paths.mcpJsonPath, (existing) => removeMcpServer(existing));
332
- return paths.mcpJsonPath;
333
- }
334
- export async function installHooks(paths, home) {
335
- const cursor = resolveCursorPaths(home);
336
- await ensureCursorDir(cursor.cursorDir);
337
- await updateFileLocked(cursor.hooksJsonPath, (existing) => mergeHooks(existing, paths));
338
- return cursor.hooksJsonPath;
339
- }
340
- export async function uninstallHooks(home) {
341
- const paths = resolveCursorPaths(home);
342
- await updateFileLocked(paths.hooksJsonPath, (existing) => removeHooks(existing));
343
- return paths.hooksJsonPath;
344
- }
@@ -1,16 +0,0 @@
1
- export type AgentId = 'claude' | 'codex' | 'cursor';
2
- export declare const MINT_PROVIDER: Record<AgentId, string>;
3
- export interface AgentPresence {
4
- readonly claude: boolean;
5
- readonly codex: boolean;
6
- readonly cursor: boolean;
7
- }
8
- export declare function detectAgents(home?: string): Promise<AgentPresence>;
9
- export interface AgentSelection {
10
- readonly agents: readonly AgentId[];
11
- readonly fellBack: boolean;
12
- }
13
- export declare function selectAgents(present: AgentPresence, flags?: {
14
- readonly codex?: boolean;
15
- readonly cursor?: boolean;
16
- }): AgentSelection;
@@ -1,70 +0,0 @@
1
- import { constants } from 'node:fs';
2
- import { access, stat } from 'node:fs/promises';
3
- import { homedir } from 'node:os';
4
- import { delimiter, join } from 'node:path';
5
- import { resolveCodexPaths } from './codex.js';
6
- import { resolveCursorPaths } from './cursor.js';
7
- export const MINT_PROVIDER = {
8
- claude: 'claude_code',
9
- codex: 'codex',
10
- cursor: 'cursor',
11
- };
12
- async function onPath(name) {
13
- const raw = process.env.PATH;
14
- if (raw === undefined || raw.trim() === '')
15
- return false;
16
- const exts = process.platform === 'win32'
17
- ? (process.env.PATHEXT ?? '.EXE;.CMD;.BAT;.COM').split(';').filter(Boolean)
18
- : [''];
19
- for (const dir of raw.split(delimiter)) {
20
- if (dir === '')
21
- continue;
22
- for (const ext of exts) {
23
- try {
24
- await access(join(dir, `${name}${ext}`), constants.X_OK);
25
- return true;
26
- }
27
- catch {
28
- }
29
- }
30
- }
31
- return false;
32
- }
33
- async function dirExists(path) {
34
- try {
35
- return (await stat(path)).isDirectory();
36
- }
37
- catch {
38
- return false;
39
- }
40
- }
41
- export async function detectAgents(home = homedir()) {
42
- const [claudeDir, claudeBin, codexDir, codexBin, cursorDir, cursorBin, cursorAgentBin] = await Promise.all([
43
- dirExists(join(home, '.claude')),
44
- onPath('claude'),
45
- dirExists(resolveCodexPaths(home).codexDir),
46
- onPath('codex'),
47
- dirExists(resolveCursorPaths(home).cursorDir),
48
- onPath('cursor'),
49
- onPath('cursor-agent'),
50
- ]);
51
- return {
52
- claude: claudeDir || claudeBin,
53
- codex: codexDir || codexBin,
54
- cursor: cursorDir || cursorBin || cursorAgentBin,
55
- };
56
- }
57
- export function selectAgents(present, flags = {}) {
58
- const codex = flags.codex ?? present.codex;
59
- const cursor = flags.cursor ?? present.cursor;
60
- const agents = [];
61
- if (present.claude)
62
- agents.push('claude');
63
- if (codex)
64
- agents.push('codex');
65
- if (cursor)
66
- agents.push('cursor');
67
- if (agents.length === 0)
68
- return { agents: ['claude'], fellBack: true };
69
- return { agents, fellBack: false };
70
- }
@@ -1,5 +0,0 @@
1
- export declare const CODEX_CAPABILITY_BY_EVENT: Readonly<Record<string, readonly string[]>>;
2
- export declare const CURSOR_CAPABILITY_BY_EVENT: Readonly<Record<string, readonly string[]>>;
3
- export declare function missingCapabilities(missing: readonly string[], capabilities?: Readonly<Record<string, readonly string[]>>): readonly string[];
4
- export declare function describeMissingHookEvents(missing: readonly string[], capabilities?: Readonly<Record<string, readonly string[]>>): string;
5
- export declare function describePartialHooks(covered: number, total: number, missing: readonly string[], capabilities?: Readonly<Record<string, readonly string[]>>): string;
@@ -1,47 +0,0 @@
1
- const CAPABILITY_BY_EVENT = {
2
- UserPromptSubmit: ['recall'],
3
- SessionEnd: ['the end-of-session report'],
4
- Stop: ['capture'],
5
- PostToolUse: ['tool context'],
6
- PreToolUse: ['tool context'],
7
- SessionStart: ['recall after a compaction'],
8
- };
9
- export const CODEX_CAPABILITY_BY_EVENT = {
10
- ...CAPABILITY_BY_EVENT,
11
- Stop: ['capture', 'the end-of-session report'],
12
- PostCompact: ['recall after a compaction'],
13
- };
14
- export const CURSOR_CAPABILITY_BY_EVENT = {
15
- sessionStart: ['recall'],
16
- preCompact: ['recall after a compaction'],
17
- sessionEnd: ['the end-of-session report'],
18
- stop: ['capture'],
19
- postToolUse: ['tool context', 'recall after a compaction'],
20
- preToolUse: ['tool context'],
21
- };
22
- function joinWords(parts) {
23
- if (parts.length <= 1)
24
- return parts[0] ?? '';
25
- return `${parts.slice(0, -1).join(', ')} and ${parts[parts.length - 1]}`;
26
- }
27
- export function missingCapabilities(missing, capabilities = CAPABILITY_BY_EVENT) {
28
- const seen = new Set();
29
- const names = [];
30
- for (const event of missing) {
31
- for (const name of capabilities[event] ?? [event]) {
32
- if (seen.has(name))
33
- continue;
34
- seen.add(name);
35
- names.push(name);
36
- }
37
- }
38
- return names;
39
- }
40
- export function describeMissingHookEvents(missing, capabilities) {
41
- return joinWords(missingCapabilities(missing, capabilities));
42
- }
43
- export function describePartialHooks(covered, total, missing, capabilities) {
44
- const names = missingCapabilities(missing, capabilities);
45
- const verb = names.length === 1 ? 'is' : 'are';
46
- return `${covered} of ${total} events registered — ${joinWords(names)} ${verb} not wired`;
47
- }
@@ -1 +0,0 @@
1
- export declare function isUnrunnableCommandEntry(entry: unknown): boolean;
@@ -1,8 +0,0 @@
1
- export function isUnrunnableCommandEntry(entry) {
2
- if (entry === null || typeof entry !== 'object' || Array.isArray(entry))
3
- return false;
4
- const record = entry;
5
- if (record.type !== 'command')
6
- return false;
7
- return typeof record.command !== 'string' || record.command.trim() === '';
8
- }
@@ -1,19 +0,0 @@
1
- import { type AgentId, type AgentPresence } from './detect.js';
2
- export declare const SKILL_FILE = "SKILL.md";
3
- export declare const AGENT_ROOT: Record<AgentId, (home: string) => string>;
4
- export declare const SKILL_ROOT: Record<AgentId, (home: string) => string>;
5
- export declare const DOCS_ROOT: Record<AgentId, (home: string) => string>;
6
- export declare const REPO_AGENT_DIR: Record<AgentId, string>;
7
- export declare const REPO_SKILL_DIR: Record<AgentId, string>;
8
- export declare const REPO_SKILL_LANE = ".<agent>/skills/";
9
- export declare const AGENT_LABEL: Record<AgentId, string>;
10
- export interface SkillRoot {
11
- readonly agent: AgentId;
12
- readonly dir: string;
13
- }
14
- export declare function resolveSkillRoots(home?: string, presence?: AgentPresence): Promise<SkillRoot[]>;
15
- export declare function resolveDocRoots(home?: string, presence?: AgentPresence): Promise<SkillRoot[]>;
16
- export declare function resolveRepoSkillRoots(repoPath: string, opts?: {
17
- readonly home?: string;
18
- readonly agents?: AgentPresence;
19
- }): Promise<SkillRoot[]>;
@@ -1,91 +0,0 @@
1
- import { access, readdir } from 'node:fs/promises';
2
- import { homedir } from 'node:os';
3
- import { join } from 'node:path';
4
- import { MANIFEST_FILE } from '../documents/manifest.js';
5
- import { resolveCodexPaths } from './codex.js';
6
- import { resolveCursorPaths } from './cursor.js';
7
- import { detectAgents } from './detect.js';
8
- export const SKILL_FILE = 'SKILL.md';
9
- export const AGENT_ROOT = {
10
- claude: (home) => join(home, '.claude'),
11
- codex: (home) => resolveCodexPaths(home).codexDir,
12
- cursor: (home) => resolveCursorPaths(home).cursorDir,
13
- };
14
- const under = (leaf) => (agent) => (home) => join(AGENT_ROOT[agent](home), leaf);
15
- export const SKILL_ROOT = {
16
- claude: under('skills')('claude'),
17
- codex: under('skills')('codex'),
18
- cursor: under('skills')('cursor'),
19
- };
20
- export const DOCS_ROOT = {
21
- claude: under('docs')('claude'),
22
- codex: under('docs')('codex'),
23
- cursor: under('docs')('cursor'),
24
- };
25
- export const REPO_AGENT_DIR = {
26
- claude: '.claude',
27
- codex: '.codex',
28
- cursor: '.cursor',
29
- };
30
- export const REPO_SKILL_DIR = {
31
- claude: `${REPO_AGENT_DIR.claude}/skills`,
32
- codex: `${REPO_AGENT_DIR.codex}/skills`,
33
- cursor: `${REPO_AGENT_DIR.cursor}/skills`,
34
- };
35
- export const REPO_SKILL_LANE = '.<agent>/skills/';
36
- export const AGENT_LABEL = {
37
- claude: 'Claude',
38
- codex: 'Codex',
39
- cursor: 'Cursor',
40
- };
41
- export async function resolveSkillRoots(home = homedir(), presence) {
42
- return resolveRoots(SKILL_ROOT, home, presence ?? (await detectAgents(home)));
43
- }
44
- export async function resolveDocRoots(home = homedir(), presence) {
45
- return resolveRoots(DOCS_ROOT, home, presence ?? (await detectAgents(home)));
46
- }
47
- export async function resolveRepoSkillRoots(repoPath, opts = {}) {
48
- const home = opts.home ?? homedir();
49
- const present = opts.agents ?? (await detectAgents(home));
50
- return resolveRoots(Object.fromEntries(Object.keys(REPO_SKILL_DIR).map((agent) => [
51
- agent,
52
- (repo) => join(repo, REPO_SKILL_DIR[agent]),
53
- ])), repoPath, present, markedSkillDirExists);
54
- }
55
- async function resolveRoots(registry, base, present, wroteHere = rootManifestExists) {
56
- const roots = [];
57
- for (const agent of Object.keys(registry)) {
58
- const dir = registry[agent](base);
59
- if (agent === 'claude' || present[agent]) {
60
- roots.push({ agent, dir });
61
- continue;
62
- }
63
- if (await wroteHere(dir))
64
- roots.push({ agent, dir });
65
- }
66
- return roots;
67
- }
68
- async function rootManifestExists(dir) {
69
- return exists(join(dir, MANIFEST_FILE));
70
- }
71
- async function markedSkillDirExists(dir) {
72
- const entries = await readdir(dir, { withFileTypes: true }).catch(() => null);
73
- if (entries === null)
74
- return false;
75
- for (const entry of entries) {
76
- if (!entry.isDirectory())
77
- continue;
78
- if (await exists(join(dir, entry.name, MANIFEST_FILE)))
79
- return true;
80
- }
81
- return false;
82
- }
83
- async function exists(path) {
84
- try {
85
- await access(path);
86
- return true;
87
- }
88
- catch {
89
- return false;
90
- }
91
- }
package/dist/reclaim.d.ts DELETED
@@ -1,17 +0,0 @@
1
- import type { AgentPresence } from './providers/detect.js';
2
- import { type SkillResult } from './skills.js';
3
- import { type DocumentResult } from './documents/write.js';
4
- import { type PointerResult } from './documents/pointer.js';
5
- import { type MemoryResult } from './memories.js';
6
- export interface ReclaimRepoSummary {
7
- readonly skills: readonly SkillResult[];
8
- readonly documents: readonly DocumentResult[];
9
- readonly pointer: readonly PointerResult[];
10
- readonly memories: readonly MemoryResult[];
11
- readonly status: 'cleaned' | 'nothing_to_do';
12
- }
13
- export interface ReclaimOptions {
14
- readonly home?: string;
15
- readonly agents?: AgentPresence;
16
- }
17
- export declare function reclaimRepoFiles(repoPath: string, opts?: ReclaimOptions): Promise<ReclaimRepoSummary>;
package/dist/reclaim.js DELETED
@@ -1,33 +0,0 @@
1
- import { join } from 'node:path';
2
- import { withFileLock } from './lock.js';
3
- import { removeRepoSkillFiles } from './skills.js';
4
- import { DOCS_DIR } from './documents/manifest.js';
5
- import { removeRepoDocumentFiles } from './documents/write.js';
6
- import { removeRepoPointerLines } from './documents/pointer.js';
7
- import { removeProjectMemoryFiles } from './memories.js';
8
- export async function reclaimRepoFiles(repoPath, opts = {}) {
9
- const skills = await withFileLock(repoPath, () => removeRepoSkillFiles(repoPath, {
10
- ...(opts.home === undefined ? {} : { home: opts.home }),
11
- ...(opts.agents === undefined ? {} : { agents: opts.agents }),
12
- }));
13
- const documents = await withFileLock(join(repoPath, DOCS_DIR), async () => {
14
- const removed = await removeRepoDocumentFiles(repoPath);
15
- const pointer = await removeRepoPointerLines(repoPath);
16
- return { removed, pointer: pointer.results };
17
- });
18
- const memories = await removeProjectMemoryFiles({
19
- projectPath: repoPath,
20
- ...(opts.home === undefined ? {} : { home: opts.home }),
21
- });
22
- return {
23
- skills: skills.results,
24
- documents: documents.removed.results,
25
- pointer: documents.pointer,
26
- memories: memories.results,
27
- status: skills.status === 'cleaned' ||
28
- documents.removed.status === 'cleaned' ||
29
- memories.results.length > 0
30
- ? 'cleaned'
31
- : 'nothing_to_do',
32
- };
33
- }
package/dist/render.d.ts DELETED
@@ -1,17 +0,0 @@
1
- export type CheckState = 'ok' | 'warn' | 'fail' | 'paused' | 'unknown';
2
- export interface Check {
3
- readonly label: string;
4
- readonly state: CheckState;
5
- readonly detail: string;
6
- readonly fix?: string;
7
- }
8
- export interface RenderOptions {
9
- readonly colour?: boolean;
10
- readonly labelWidth?: number;
11
- }
12
- export declare function renderCheck(check: Check, opts?: RenderOptions): string;
13
- export declare function renderChecks(checks: readonly Check[], opts?: RenderOptions): string;
14
- export declare function exitCodeFor(checks: readonly Check[]): number;
15
- export declare function checksToJson(checks: readonly Check[]): unknown;
16
- export declare function relativeTime(target: Date, now?: Date): string;
17
- export declare function ratio(numerator: number, denominator: number): string;