@halyard/cli 0.1.1 → 0.2.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 (142) hide show
  1. package/README.md +115 -0
  2. package/dist/cli.js +30 -3
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/api-keys.d.ts +3 -0
  5. package/dist/commands/api-keys.d.ts.map +1 -0
  6. package/dist/commands/api-keys.js +90 -0
  7. package/dist/commands/api-keys.js.map +1 -0
  8. package/dist/commands/hook.d.ts +6 -0
  9. package/dist/commands/hook.d.ts.map +1 -0
  10. package/dist/commands/hook.js +106 -0
  11. package/dist/commands/hook.js.map +1 -0
  12. package/dist/commands/login.d.ts +17 -0
  13. package/dist/commands/login.d.ts.map +1 -1
  14. package/dist/commands/login.js +170 -65
  15. package/dist/commands/login.js.map +1 -1
  16. package/dist/commands/orgs.d.ts +3 -0
  17. package/dist/commands/orgs.d.ts.map +1 -0
  18. package/dist/commands/orgs.js +102 -0
  19. package/dist/commands/orgs.js.map +1 -0
  20. package/dist/commands/push.d.ts.map +1 -1
  21. package/dist/commands/push.js +29 -6
  22. package/dist/commands/push.js.map +1 -1
  23. package/dist/commands/sessions.d.ts +3 -0
  24. package/dist/commands/sessions.d.ts.map +1 -0
  25. package/dist/commands/sessions.js +139 -0
  26. package/dist/commands/sessions.js.map +1 -0
  27. package/dist/commands/setup.d.ts +3 -0
  28. package/dist/commands/setup.d.ts.map +1 -0
  29. package/dist/commands/setup.js +58 -0
  30. package/dist/commands/setup.js.map +1 -0
  31. package/dist/commands/skills.d.ts +12 -0
  32. package/dist/commands/skills.d.ts.map +1 -0
  33. package/dist/commands/skills.js +171 -0
  34. package/dist/commands/skills.js.map +1 -0
  35. package/dist/commands/sync.d.ts.map +1 -1
  36. package/dist/commands/sync.js +51 -49
  37. package/dist/commands/sync.js.map +1 -1
  38. package/dist/commands/users.d.ts +13 -0
  39. package/dist/commands/users.d.ts.map +1 -0
  40. package/dist/commands/users.js +205 -0
  41. package/dist/commands/users.js.map +1 -0
  42. package/dist/commands/whoami.d.ts +21 -0
  43. package/dist/commands/whoami.d.ts.map +1 -0
  44. package/dist/commands/whoami.js +77 -0
  45. package/dist/commands/whoami.js.map +1 -0
  46. package/dist/lib/api-client.d.ts +64 -2
  47. package/dist/lib/api-client.d.ts.map +1 -1
  48. package/dist/lib/api-client.js +32 -7
  49. package/dist/lib/api-client.js.map +1 -1
  50. package/dist/lib/auth.d.ts +43 -6
  51. package/dist/lib/auth.d.ts.map +1 -1
  52. package/dist/lib/auth.js +106 -25
  53. package/dist/lib/auth.js.map +1 -1
  54. package/dist/lib/config-files.d.ts +6 -0
  55. package/dist/lib/config-files.d.ts.map +1 -0
  56. package/dist/lib/config-files.js +27 -0
  57. package/dist/lib/config-files.js.map +1 -0
  58. package/dist/lib/config.d.ts +13 -7
  59. package/dist/lib/config.d.ts.map +1 -1
  60. package/dist/lib/config.js +22 -8
  61. package/dist/lib/config.js.map +1 -1
  62. package/dist/lib/copilot-export.d.ts +31 -0
  63. package/dist/lib/copilot-export.d.ts.map +1 -0
  64. package/dist/lib/copilot-export.js +129 -0
  65. package/dist/lib/copilot-export.js.map +1 -0
  66. package/dist/lib/discovery.d.ts +60 -2
  67. package/dist/lib/discovery.d.ts.map +1 -1
  68. package/dist/lib/discovery.js +187 -15
  69. package/dist/lib/discovery.js.map +1 -1
  70. package/dist/lib/hook-discovery.d.ts +43 -0
  71. package/dist/lib/hook-discovery.d.ts.map +1 -0
  72. package/dist/lib/hook-discovery.js +90 -0
  73. package/dist/lib/hook-discovery.js.map +1 -0
  74. package/dist/lib/hook-input.d.ts +36 -0
  75. package/dist/lib/hook-input.d.ts.map +1 -0
  76. package/dist/lib/hook-input.js +161 -0
  77. package/dist/lib/hook-input.js.map +1 -0
  78. package/dist/lib/hook-runner.d.ts +48 -0
  79. package/dist/lib/hook-runner.d.ts.map +1 -0
  80. package/dist/lib/hook-runner.js +306 -0
  81. package/dist/lib/hook-runner.js.map +1 -0
  82. package/dist/lib/http.d.ts +18 -0
  83. package/dist/lib/http.d.ts.map +1 -0
  84. package/dist/lib/http.js +54 -0
  85. package/dist/lib/http.js.map +1 -0
  86. package/dist/lib/opencode-export.d.ts +22 -0
  87. package/dist/lib/opencode-export.d.ts.map +1 -0
  88. package/dist/lib/opencode-export.js +53 -0
  89. package/dist/lib/opencode-export.js.map +1 -0
  90. package/dist/lib/output.d.ts +27 -0
  91. package/dist/lib/output.d.ts.map +1 -0
  92. package/dist/lib/output.js +74 -0
  93. package/dist/lib/output.js.map +1 -0
  94. package/dist/lib/preflight.d.ts +35 -0
  95. package/dist/lib/preflight.d.ts.map +1 -0
  96. package/dist/lib/preflight.js +54 -0
  97. package/dist/lib/preflight.js.map +1 -0
  98. package/dist/lib/repo-binding.d.ts +43 -0
  99. package/dist/lib/repo-binding.d.ts.map +1 -0
  100. package/dist/lib/repo-binding.js +158 -0
  101. package/dist/lib/repo-binding.js.map +1 -0
  102. package/dist/lib/repo-identity.d.ts +29 -0
  103. package/dist/lib/repo-identity.d.ts.map +1 -0
  104. package/dist/lib/repo-identity.js +113 -0
  105. package/dist/lib/repo-identity.js.map +1 -0
  106. package/dist/lib/setup-hooks.d.ts +41 -0
  107. package/dist/lib/setup-hooks.d.ts.map +1 -0
  108. package/dist/lib/setup-hooks.js +276 -0
  109. package/dist/lib/setup-hooks.js.map +1 -0
  110. package/dist/lib/setup.d.ts +47 -0
  111. package/dist/lib/setup.d.ts.map +1 -0
  112. package/dist/lib/setup.js +179 -0
  113. package/dist/lib/setup.js.map +1 -0
  114. package/dist/lib/skill-batches.d.ts +16 -0
  115. package/dist/lib/skill-batches.d.ts.map +1 -0
  116. package/dist/lib/skill-batches.js +35 -0
  117. package/dist/lib/skill-batches.js.map +1 -0
  118. package/dist/lib/skill-files.d.ts +4 -0
  119. package/dist/lib/skill-files.d.ts.map +1 -0
  120. package/dist/lib/skill-files.js +36 -0
  121. package/dist/lib/skill-files.js.map +1 -0
  122. package/dist/lib/skill-fingerprint.d.ts +55 -0
  123. package/dist/lib/skill-fingerprint.d.ts.map +1 -0
  124. package/dist/lib/skill-fingerprint.js +204 -0
  125. package/dist/lib/skill-fingerprint.js.map +1 -0
  126. package/dist/lib/skills-discovery.d.ts +57 -0
  127. package/dist/lib/skills-discovery.d.ts.map +1 -0
  128. package/dist/lib/skills-discovery.js +157 -0
  129. package/dist/lib/skills-discovery.js.map +1 -0
  130. package/dist/lib/skills-sync.d.ts +66 -0
  131. package/dist/lib/skills-sync.d.ts.map +1 -0
  132. package/dist/lib/skills-sync.js +251 -0
  133. package/dist/lib/skills-sync.js.map +1 -0
  134. package/dist/lib/sync-runner.d.ts +56 -0
  135. package/dist/lib/sync-runner.d.ts.map +1 -0
  136. package/dist/lib/sync-runner.js +106 -0
  137. package/dist/lib/sync-runner.js.map +1 -0
  138. package/dist/lib/sync-state.d.ts +36 -6
  139. package/dist/lib/sync-state.d.ts.map +1 -1
  140. package/dist/lib/sync-state.js +41 -11
  141. package/dist/lib/sync-state.js.map +1 -1
  142. package/package.json +16 -8
@@ -0,0 +1,276 @@
1
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { isDeepStrictEqual } from 'node:util';
4
+ import { asObject, formatError, readJsonFile, writeJsonFile } from './config-files.js';
5
+ /**
6
+ * `halyard setup` scaffolding for session upload: a tiny launcher plus one
7
+ * lifecycle-hook config per client, all pointing at `halyard hook`.
8
+ *
9
+ * Merges are idempotent and keyed on the launcher path, so re-running setup
10
+ * (or after a template change) updates our entries and leaves everything else alone.
11
+ */
12
+ export const HOOK_LAUNCHER_PATH = '.halyard/hook.sh';
13
+ export function launcherScript() {
14
+ return `#!/bin/sh
15
+ # Generated by \`halyard setup\`. Forwards agent lifecycle hooks to \`halyard hook\`
16
+ # when the Halyard CLI is available, and exits 0 otherwise so the agent is
17
+ # never blocked. Re-run \`halyard setup\` to regenerate.
18
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
19
+ if [ -f "$ROOT/apps/cli/dist/cli.js" ] && grep -q '"name": "@halyard/cli"' "$ROOT/apps/cli/package.json" 2>/dev/null; then
20
+ exec node "$ROOT/apps/cli/dist/cli.js" hook --root "$ROOT" "$@"
21
+ fi
22
+ BIN="$ROOT/node_modules/.bin/halyard"
23
+ [ -x "$BIN" ] || BIN="$(command -v halyard 2>/dev/null || true)"
24
+ [ -n "$BIN" ] || exit 0
25
+ # Older CLI releases have no \`hook\` command (and answer \`--help\` for any word).
26
+ "$BIN" --help 2>/dev/null | grep -q '^ hook' || exit 0
27
+ exec "$BIN" hook --root "$ROOT" "$@"
28
+ `;
29
+ }
30
+ function hookArgs(client, event) {
31
+ return `--client ${client} --event ${event}`;
32
+ }
33
+ export function claudeHookSpec() {
34
+ const command = (event) => `"$CLAUDE_PROJECT_DIR"/${HOOK_LAUNCHER_PATH} ${hookArgs('claude', event)}`;
35
+ return {
36
+ start: {
37
+ type: 'command',
38
+ command: command('start'),
39
+ timeout: 10,
40
+ statusMessage: 'Sweeping unsynced agent sessions...',
41
+ },
42
+ end: {
43
+ type: 'command',
44
+ command: command('end'),
45
+ timeout: 60,
46
+ statusMessage: 'Uploading agent session to Halyard...',
47
+ },
48
+ };
49
+ }
50
+ /** Codex caps SessionEnd at 3 seconds, so the end hook detaches. */
51
+ export function codexHookSpec() {
52
+ const prefix = 'cd "${CODEX_CWD:-$PWD}" && ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)" && "$ROOT"/';
53
+ return {
54
+ start: {
55
+ type: 'command',
56
+ command: `${prefix}${HOOK_LAUNCHER_PATH} ${hookArgs('codex', 'start')}`,
57
+ timeout: 10,
58
+ },
59
+ end: {
60
+ type: 'command',
61
+ command: `${prefix}${HOOK_LAUNCHER_PATH} ${hookArgs('codex', 'end')} --detach`,
62
+ timeout: 3,
63
+ },
64
+ };
65
+ }
66
+ /** Gemini timeouts are milliseconds and entries carry a name. */
67
+ export function geminiHookSpec() {
68
+ const command = (event) => `"$GEMINI_PROJECT_DIR"/${HOOK_LAUNCHER_PATH} ${hookArgs('gemini', event)}`;
69
+ return {
70
+ start: {
71
+ type: 'command',
72
+ name: 'halyard-session-sweep',
73
+ command: command('start'),
74
+ timeout: 10_000,
75
+ },
76
+ end: {
77
+ type: 'command',
78
+ name: 'halyard-session-upload',
79
+ command: command('end'),
80
+ timeout: 60_000,
81
+ },
82
+ };
83
+ }
84
+ function isLauncherEntry(entry) {
85
+ const command = entry['command'] ?? entry['bash'];
86
+ return typeof command === 'string' && command.includes(HOOK_LAUNCHER_PATH);
87
+ }
88
+ /** Claude-shaped: event → [{ matcher?, hooks: [entry, ...] }]. */
89
+ function upsertGrouped(groupsValue, hook) {
90
+ const groups = Array.isArray(groupsValue) ? groupsValue.map(asObject) : [];
91
+ let placed = false;
92
+ const next = groups.map((group) => {
93
+ const entries = Array.isArray(group['hooks']) ? group['hooks'].map(asObject) : [];
94
+ const index = entries.findIndex(isLauncherEntry);
95
+ if (index === -1)
96
+ return group;
97
+ placed = true;
98
+ entries[index] = hook;
99
+ return { ...group, hooks: entries };
100
+ });
101
+ if (!placed)
102
+ next.push({ hooks: [hook] });
103
+ return next;
104
+ }
105
+ /** Cursor-shaped: event → [entry, ...]. */
106
+ function upsertFlat(listValue, entry) {
107
+ const list = Array.isArray(listValue) ? listValue.map(asObject) : [];
108
+ const index = list.findIndex(isLauncherEntry);
109
+ if (index === -1)
110
+ list.push(entry);
111
+ else
112
+ list[index] = entry;
113
+ return list;
114
+ }
115
+ /** Merge start/end hooks into a Claude-shaped settings object (Claude, Codex, Gemini). */
116
+ export function mergeLifecycleHooks(input, spec, events = { start: 'SessionStart', end: 'SessionEnd' }) {
117
+ const settings = asObject(input);
118
+ const hooks = asObject(settings['hooks']);
119
+ hooks[events.start] = upsertGrouped(hooks[events.start], spec.start);
120
+ hooks[events.end] = upsertGrouped(hooks[events.end], spec.end);
121
+ return { ...settings, hooks };
122
+ }
123
+ export function mergeCursorHooks(input) {
124
+ const config = asObject(input);
125
+ const hooks = asObject(config['hooks']);
126
+ const entry = (event) => ({
127
+ command: `./${HOOK_LAUNCHER_PATH} ${hookArgs('cursor', event)}`,
128
+ timeout: event === 'end' ? 60 : 10,
129
+ });
130
+ hooks['sessionStart'] = upsertFlat(hooks['sessionStart'], entry('start'));
131
+ hooks['sessionEnd'] = upsertFlat(hooks['sessionEnd'], entry('end'));
132
+ const version = typeof config['version'] === 'number' ? config['version'] : 1;
133
+ return { ...config, version, hooks };
134
+ }
135
+ export function copilotHooksFile() {
136
+ const entry = (event) => ({
137
+ type: 'command',
138
+ bash: `./${HOOK_LAUNCHER_PATH} ${hookArgs('copilot', event)}`,
139
+ timeoutSec: event === 'end' ? 60 : 10,
140
+ });
141
+ return `${JSON.stringify({ version: 1, hooks: { sessionStart: [entry('start')], sessionEnd: [entry('end')] } }, null, 2)}\n`;
142
+ }
143
+ export function grokHooksFile() {
144
+ const entry = (event) => ({
145
+ hooks: [
146
+ {
147
+ type: 'command',
148
+ command: `"\${GROK_WORKSPACE_ROOT:-.}"/${HOOK_LAUNCHER_PATH} ${hookArgs('grok', event)}`,
149
+ timeout: event === 'end' ? 60 : 10,
150
+ },
151
+ ],
152
+ });
153
+ return `${JSON.stringify({ hooks: { SessionStart: [entry('start')], SessionEnd: [entry('end')] } }, null, 2)}\n`;
154
+ }
155
+ export function opencodePluginFile() {
156
+ return `// Generated by \`halyard setup\`. OpenCode has no shell-command hooks — extensibility
157
+ // is TypeScript plugins on its event bus — so this bridges its session lifecycle
158
+ // to the same uploader every other client uses: ${HOOK_LAUNCHER_PATH} -> \`halyard hook\`.
159
+ //
160
+ // * startup -> sweeps sessions that never uploaded
161
+ // * session.idle -> end-of-session upload, debounced: OpenCode has no
162
+ // session-end event, and the ingest endpoint keeps the FIRST
163
+ // upload it sees for a session id, so we wait for the
164
+ // session to go quiet before sending it.
165
+
166
+ const QUIET_MS = 10 * 60 * 1000
167
+
168
+ interface OpencodeEvent {
169
+ type: string
170
+ properties?: { sessionID?: string; info?: { id?: string } }
171
+ }
172
+
173
+ interface PluginContext {
174
+ directory: string
175
+ $: (
176
+ strings: TemplateStringsArray,
177
+ ...values: unknown[]
178
+ ) => {
179
+ quiet(): { nothrow(): Promise<unknown> }
180
+ }
181
+ }
182
+
183
+ export const HalyardSessionUpload = async ({ $, directory }: PluginContext) => {
184
+ const launcher = \`\${directory}/${HOOK_LAUNCHER_PATH}\`
185
+ const timers = new Map<string, ReturnType<typeof setTimeout>>()
186
+
187
+ const run = async (event: 'start' | 'end') => {
188
+ const result = $\`\${launcher} --client opencode --event \${event} --detach\`
189
+ await result.quiet().nothrow()
190
+ }
191
+
192
+ await run('start')
193
+
194
+ return {
195
+ event: async ({ event }: { event: OpencodeEvent }) => {
196
+ const sessionId = event.properties?.sessionID ?? event.properties?.info?.id ?? 'default'
197
+ if (event.type === 'session.idle') {
198
+ const pending = timers.get(sessionId)
199
+ if (pending) clearTimeout(pending)
200
+ timers.set(
201
+ sessionId,
202
+ setTimeout(() => {
203
+ timers.delete(sessionId)
204
+ void run('end')
205
+ }, QUIET_MS),
206
+ )
207
+ return
208
+ }
209
+ // Any further activity on the session restarts the quiet period.
210
+ if (event.type === 'message.updated' || event.type === 'session.updated') {
211
+ const pending = timers.get(sessionId)
212
+ if (pending) {
213
+ clearTimeout(pending)
214
+ timers.delete(sessionId)
215
+ }
216
+ }
217
+ },
218
+ }
219
+ }
220
+ `;
221
+ }
222
+ function scaffoldResult(target, status, location, detail) {
223
+ return { target, status, location, detail };
224
+ }
225
+ /** Write a file we own outright (created or replaced when its content differs). */
226
+ function writeOwnedFile(target, filePath, content, dryRun, executable = false) {
227
+ const current = existsSync(filePath) ? readFileSync(filePath, 'utf8') : null;
228
+ if (current === content) {
229
+ return scaffoldResult(target, 'unchanged', filePath, 'Halyard session-upload hook is already in place.');
230
+ }
231
+ if (!dryRun) {
232
+ mkdirSync(dirname(filePath), { recursive: true });
233
+ writeFileSync(filePath, content);
234
+ if (executable)
235
+ chmodSync(filePath, 0o755);
236
+ }
237
+ const verb = current === null ? 'create' : 'update';
238
+ return scaffoldResult(target, 'updated', filePath, dryRun
239
+ ? `Would ${verb} the Halyard session-upload hook.`
240
+ : `${verb === 'create' ? 'Created' : 'Updated'} the Halyard session-upload hook.`);
241
+ }
242
+ /** Merge our entries into a JSON config other tools also write to. */
243
+ function mergeJsonFile(target, filePath, merge, dryRun) {
244
+ const current = readJsonFile(filePath);
245
+ if (current instanceof Error)
246
+ return scaffoldResult(target, 'error', filePath, current.message);
247
+ let next;
248
+ try {
249
+ next = merge(current);
250
+ }
251
+ catch (error) {
252
+ return scaffoldResult(target, 'error', filePath, formatError(error));
253
+ }
254
+ if (isDeepStrictEqual(current, next)) {
255
+ return scaffoldResult(target, 'unchanged', filePath, 'Halyard session-upload hooks are already present.');
256
+ }
257
+ if (!dryRun)
258
+ writeJsonFile(filePath, next);
259
+ return scaffoldResult(target, 'updated', filePath, dryRun
260
+ ? 'Would add the Halyard session-upload hooks.'
261
+ : 'Added the Halyard session-upload hooks.');
262
+ }
263
+ /** Everything except Claude's settings.json, which `runSetup` owns (it also enables the plugin there). */
264
+ export function scaffoldHooks(cwd, options = {}) {
265
+ const dryRun = options.dryRun ?? false;
266
+ return [
267
+ writeOwnedFile('launcher', join(cwd, HOOK_LAUNCHER_PATH), launcherScript(), dryRun, true),
268
+ mergeJsonFile('codex', join(cwd, '.codex', 'hooks.json'), (c) => mergeLifecycleHooks(c, codexHookSpec()), dryRun),
269
+ mergeJsonFile('cursor', join(cwd, '.cursor', 'hooks.json'), (c) => mergeCursorHooks(c), dryRun),
270
+ mergeJsonFile('gemini', join(cwd, '.gemini', 'settings.json'), (c) => mergeLifecycleHooks(c, geminiHookSpec()), dryRun),
271
+ writeOwnedFile('copilot', join(cwd, '.github', 'hooks', 'halyard-session-upload.json'), copilotHooksFile(), dryRun),
272
+ writeOwnedFile('opencode', join(cwd, '.opencode', 'plugins', 'halyard-session-upload.ts'), opencodePluginFile(), dryRun),
273
+ writeOwnedFile('grok', join(cwd, '.grok', 'hooks', 'halyard-session-upload.json'), grokHooksFile(), dryRun),
274
+ ];
275
+ }
276
+ //# sourceMappingURL=setup-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-hooks.js","sourceRoot":"","sources":["../../src/lib/setup-hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { type HookScaffoldTarget } from './setup-hooks.js';
2
+ export declare const HALYARD_MCP_URL = "https://mcp.usehalyard.ai";
3
+ export declare const HALYARD_CLAUDE_MARKETPLACE_NAME = "halyard-labs";
4
+ export declare const HALYARD_CLAUDE_MARKETPLACE_REPO = "halyard-labs/claude-plugin-marketplace";
5
+ export declare const HALYARD_CLAUDE_PLUGIN_ID = "halyard@halyard-labs";
6
+ export type SetupTarget = 'claude' | 'codex' | 'cursor' | HookScaffoldTarget;
7
+ export type SetupStatus = 'updated' | 'unchanged' | 'skipped' | 'error';
8
+ export interface SetupResult {
9
+ target: SetupTarget;
10
+ scope: 'project' | 'user';
11
+ status: SetupStatus;
12
+ location: string;
13
+ detail: string;
14
+ }
15
+ export interface CommandRunner {
16
+ has(command: string): boolean;
17
+ }
18
+ export interface SetupOptions {
19
+ cwd?: string;
20
+ dryRun?: boolean;
21
+ homeDir?: string;
22
+ runner?: CommandRunner;
23
+ /** Write the session-upload hooks (launcher + per-client config). Default true. */
24
+ hooks?: boolean;
25
+ }
26
+ interface ClaudeSettings extends Record<string, unknown> {
27
+ enabledPlugins?: Record<string, unknown>;
28
+ extraKnownMarketplaces?: Record<string, unknown>;
29
+ }
30
+ interface CursorConfig extends Record<string, unknown> {
31
+ mcpServers?: Record<string, unknown>;
32
+ }
33
+ interface CodexConfig extends Record<string, unknown> {
34
+ mcp_servers?: Record<string, unknown>;
35
+ }
36
+ export declare function createSystemRunner(): CommandRunner;
37
+ export declare function runSetup(options?: SetupOptions): SetupResult[];
38
+ export declare function getSetupPaths(cwd: string, homeDir: string): {
39
+ claudeProject: string;
40
+ cursorProject: string;
41
+ codexUser: string;
42
+ };
43
+ export declare function mergeClaudeSettings(input: unknown): ClaudeSettings;
44
+ export declare function mergeCursorConfig(input: unknown): CursorConfig;
45
+ export declare function mergeCodexConfig(input: unknown): CodexConfig;
46
+ export {};
47
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/lib/setup.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,kBAAkB,EACxB,MAAM,kBAAkB,CAAA;AAEzB,eAAO,MAAM,eAAe,8BAA8B,CAAA;AAC1D,eAAO,MAAM,+BAA+B,iBAAiB,CAAA;AAC7D,eAAO,MAAM,+BAA+B,2CAA2C,CAAA;AACvF,eAAO,MAAM,wBAAwB,yBAAyB,CAAA;AAE9D,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,kBAAkB,CAAA;AAC5E,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAA;AAEvE,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,SAAS,GAAG,MAAM,CAAA;IACzB,MAAM,EAAE,WAAW,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAA;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,aAAa,CAAA;IACtB,mFAAmF;IACnF,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,UAAU,cAAe,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACtD,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACxC,sBAAsB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjD;AAED,UAAU,YAAa,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACrC;AAED,UAAU,WAAY,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACtC;AAED,wBAAgB,kBAAkB,IAAI,aAAa,CAMlD;AAED,wBAAgB,QAAQ,CAAC,OAAO,GAAE,YAAiB,GAAG,WAAW,EAAE,CAmBlE;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;EAMzD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAkBlE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAc9D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAe5D"}
@@ -0,0 +1,179 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { mkdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { dirname, join, relative, resolve } from 'node:path';
5
+ import { isDeepStrictEqual } from 'node:util';
6
+ import { parse as parseToml, stringify as stringifyToml } from 'smol-toml';
7
+ import { asObject, formatError, readJsonFile, writeJsonFile } from './config-files.js';
8
+ import { claudeHookSpec, mergeLifecycleHooks, scaffoldHooks, } from './setup-hooks.js';
9
+ export const HALYARD_MCP_URL = 'https://mcp.usehalyard.ai';
10
+ export const HALYARD_CLAUDE_MARKETPLACE_NAME = 'halyard-labs';
11
+ export const HALYARD_CLAUDE_MARKETPLACE_REPO = 'halyard-labs/claude-plugin-marketplace';
12
+ export const HALYARD_CLAUDE_PLUGIN_ID = 'halyard@halyard-labs';
13
+ export function createSystemRunner() {
14
+ return {
15
+ has(command) {
16
+ return spawnSync('which', [command], { stdio: 'ignore' }).status === 0;
17
+ },
18
+ };
19
+ }
20
+ export function runSetup(options = {}) {
21
+ const cwd = resolve(options.cwd ?? process.cwd());
22
+ const dryRun = options.dryRun ?? false;
23
+ const homeDir = options.homeDir ?? homedir();
24
+ const runner = options.runner ?? createSystemRunner();
25
+ const hooks = options.hooks ?? true;
26
+ const paths = getSetupPaths(cwd, homeDir);
27
+ const results = [
28
+ setupClaudeProject(paths.claudeProject, dryRun, hooks),
29
+ setupCursorProject(paths.cursorProject, dryRun),
30
+ setupCodexUser(paths.codexUser, dryRun, runner),
31
+ ];
32
+ if (hooks) {
33
+ for (const entry of scaffoldHooks(cwd, { dryRun })) {
34
+ results.push({ ...entry, scope: 'project' });
35
+ }
36
+ }
37
+ return results;
38
+ }
39
+ export function getSetupPaths(cwd, homeDir) {
40
+ return {
41
+ claudeProject: join(cwd, '.claude', 'settings.json'),
42
+ cursorProject: join(cwd, '.cursor', 'mcp.json'),
43
+ codexUser: join(homeDir, '.codex', 'config.toml'),
44
+ };
45
+ }
46
+ export function mergeClaudeSettings(input) {
47
+ const current = asObject(input);
48
+ const enabledPlugins = asObject(current['enabledPlugins']);
49
+ const extraKnownMarketplaces = asObject(current['extraKnownMarketplaces']);
50
+ const marketplace = asObject(extraKnownMarketplaces[HALYARD_CLAUDE_MARKETPLACE_NAME]);
51
+ enabledPlugins[HALYARD_CLAUDE_PLUGIN_ID] = true;
52
+ marketplace['source'] = {
53
+ source: 'github',
54
+ repo: HALYARD_CLAUDE_MARKETPLACE_REPO,
55
+ };
56
+ extraKnownMarketplaces[HALYARD_CLAUDE_MARKETPLACE_NAME] = marketplace;
57
+ return {
58
+ ...current,
59
+ enabledPlugins,
60
+ extraKnownMarketplaces,
61
+ };
62
+ }
63
+ export function mergeCursorConfig(input) {
64
+ const current = asObject(input);
65
+ const mcpServers = asObject(current['mcpServers']);
66
+ const halyard = asObject(mcpServers['halyard']);
67
+ mcpServers['halyard'] = {
68
+ ...halyard,
69
+ url: HALYARD_MCP_URL,
70
+ };
71
+ return {
72
+ ...current,
73
+ mcpServers,
74
+ };
75
+ }
76
+ export function mergeCodexConfig(input) {
77
+ const current = asObject(input);
78
+ const mcpServers = asObject(current['mcp_servers']);
79
+ const halyard = asObject(mcpServers['halyard']);
80
+ mcpServers['halyard'] = {
81
+ ...halyard,
82
+ enabled: true,
83
+ url: HALYARD_MCP_URL,
84
+ };
85
+ return {
86
+ ...current,
87
+ mcp_servers: mcpServers,
88
+ };
89
+ }
90
+ function setupClaudeProject(filePath, dryRun, hooks) {
91
+ const current = readJsonFile(filePath);
92
+ if (current instanceof Error) {
93
+ return result('claude', 'project', 'error', filePath, current.message);
94
+ }
95
+ let next = mergeClaudeSettings(current);
96
+ if (hooks)
97
+ next = mergeLifecycleHooks(next, claudeHookSpec());
98
+ const what = hooks
99
+ ? 'the Halyard marketplace, the project plugin and the session-upload hooks'
100
+ : 'the Halyard marketplace and the project plugin';
101
+ if (isDeepStrictEqual(current, next)) {
102
+ return result('claude', 'project', 'unchanged', filePath, `Already scaffolded: ${what}.`);
103
+ }
104
+ if (!dryRun) {
105
+ writeJsonFile(filePath, next);
106
+ }
107
+ return result('claude', 'project', 'updated', filePath, dryRun ? `Would add ${what}.` : `Added ${what}.`);
108
+ }
109
+ function setupCursorProject(filePath, dryRun) {
110
+ const current = readJsonFile(filePath);
111
+ if (current instanceof Error) {
112
+ return result('cursor', 'project', 'error', filePath, current.message);
113
+ }
114
+ const next = mergeCursorConfig(current);
115
+ if (isDeepStrictEqual(current, next)) {
116
+ return result('cursor', 'project', 'unchanged', filePath, 'Halyard MCP is already present in Cursor project config.');
117
+ }
118
+ if (!dryRun) {
119
+ writeJsonFile(filePath, next);
120
+ }
121
+ return result('cursor', 'project', 'updated', filePath, dryRun
122
+ ? 'Would add the Halyard MCP server to Cursor project config.'
123
+ : 'Added the Halyard MCP server to Cursor project config.');
124
+ }
125
+ function setupCodexUser(filePath, dryRun, runner) {
126
+ const codexDetected = runner.has('codex') || existsSync(filePath);
127
+ if (!codexDetected) {
128
+ return result('codex', 'user', 'skipped', filePath, 'Codex was not detected on this machine, so user-level Codex config was left alone.');
129
+ }
130
+ const current = readTomlFile(filePath);
131
+ if (current instanceof Error) {
132
+ return result('codex', 'user', 'error', filePath, current.message);
133
+ }
134
+ const next = mergeCodexConfig(current);
135
+ if (isDeepStrictEqual(current, next)) {
136
+ return result('codex', 'user', 'unchanged', filePath, 'Halyard MCP is already present in Codex user config.');
137
+ }
138
+ if (!dryRun) {
139
+ writeTomlFile(filePath, next);
140
+ }
141
+ return result('codex', 'user', 'updated', filePath, dryRun
142
+ ? 'Would add the Halyard MCP server to Codex user config.'
143
+ : 'Added the Halyard MCP server to Codex user config.');
144
+ }
145
+ function readTomlFile(filePath) {
146
+ if (!existsSync(filePath))
147
+ return {};
148
+ try {
149
+ return parseToml(readFileSync(filePath, 'utf8'));
150
+ }
151
+ catch (error) {
152
+ return new Error(`Could not parse TOML at ${filePath}: ${formatError(error)}`);
153
+ }
154
+ }
155
+ function writeTomlFile(filePath, value) {
156
+ mkdirSync(dirname(filePath), { recursive: true });
157
+ writeFileSync(filePath, `${stringifyToml(asObject(value))}\n`);
158
+ }
159
+ function result(target, scope, status, filePath, detail) {
160
+ return {
161
+ target,
162
+ scope,
163
+ status,
164
+ location: displayPath(filePath, scope),
165
+ detail,
166
+ };
167
+ }
168
+ function displayPath(filePath, scope) {
169
+ if (scope === 'project') {
170
+ return relative(process.cwd(), filePath) || filePath;
171
+ }
172
+ const userHome = homedir();
173
+ const fromHome = relative(userHome, filePath);
174
+ if (fromHome && !fromHome.startsWith('..')) {
175
+ return `~/${fromHome}`;
176
+ }
177
+ return relative(process.cwd(), filePath) || filePath;
178
+ }
179
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/lib/setup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Split skill uploads into request-sized batches. Two caps apply at once: a
3
+ * count (the server processes at most 25 skills per call) and a byte budget
4
+ * on the JSON body, because file contents now ride inline and the route's
5
+ * body limit is 8 MiB. A skill whose payload alone exceeds the budget still
6
+ * ships — as a batch of one — so the server, not the client, gets to reject it.
7
+ */
8
+ export interface BatchLimits {
9
+ maxItems: number;
10
+ /** Budget for the encoded `{"skills":[...]}` body. */
11
+ maxBytes: number;
12
+ }
13
+ /** Size of one item as it will appear in the request body. */
14
+ export declare function encodedBytes(value: unknown): number;
15
+ export declare function planBatches<T>(items: T[], sizeOf: (item: T) => number, limits?: BatchLimits): T[][];
16
+ //# sourceMappingURL=skill-batches.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-batches.d.ts","sourceRoot":"","sources":["../../src/lib/skill-batches.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAA;IAChB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAA;CACjB;AAOD,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEnD;AAED,wBAAgB,WAAW,CAAC,CAAC,EAC3B,KAAK,EAAE,CAAC,EAAE,EACV,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,EAC3B,MAAM,GAAE,WAAkC,GACzC,CAAC,EAAE,EAAE,CAkBP"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Split skill uploads into request-sized batches. Two caps apply at once: a
3
+ * count (the server processes at most 25 skills per call) and a byte budget
4
+ * on the JSON body, because file contents now ride inline and the route's
5
+ * body limit is 8 MiB. A skill whose payload alone exceeds the budget still
6
+ * ships — as a batch of one — so the server, not the client, gets to reject it.
7
+ */
8
+ const DEFAULT_BATCH_LIMITS = { maxItems: 25, maxBytes: 6 * 1024 * 1024 };
9
+ /** Bytes of `{"skills":[]}` — the envelope around the items. */
10
+ const ENVELOPE_BYTES = Buffer.byteLength('{"skills":[]}');
11
+ /** Size of one item as it will appear in the request body. */
12
+ export function encodedBytes(value) {
13
+ return Buffer.byteLength(JSON.stringify(value));
14
+ }
15
+ export function planBatches(items, sizeOf, limits = DEFAULT_BATCH_LIMITS) {
16
+ const batches = [];
17
+ let current = [];
18
+ let bytes = ENVELOPE_BYTES;
19
+ for (const item of items) {
20
+ const size = sizeOf(item);
21
+ const full = current.length >= limits.maxItems || bytes + size + 1 > limits.maxBytes;
22
+ if (current.length > 0 && full) {
23
+ batches.push(current);
24
+ current = [];
25
+ bytes = ENVELOPE_BYTES;
26
+ }
27
+ // Every item after the first costs its separating comma too.
28
+ bytes += size + (current.length > 0 ? 1 : 0);
29
+ current.push(item);
30
+ }
31
+ if (current.length > 0)
32
+ batches.push(current);
33
+ return batches;
34
+ }
35
+ //# sourceMappingURL=skill-batches.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-batches.js","sourceRoot":"","sources":["../../src/lib/skill-batches.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,MAAM,oBAAoB,GAAgB,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,CAAA;AAErF,gEAAgE;AAChE,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC,CAAA;AAEzD,8DAA8D;AAC9D,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAA;AACjD,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,KAAU,EACV,MAA2B,EAC3B,SAAsB,oBAAoB;IAE1C,MAAM,OAAO,GAAU,EAAE,CAAA;IACzB,IAAI,OAAO,GAAQ,EAAE,CAAA;IACrB,IAAI,KAAK,GAAG,cAAc,CAAA;IAC1B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAA;QACzB,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,IAAI,KAAK,GAAG,IAAI,GAAG,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAA;QACpF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;YAC/B,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YACrB,OAAO,GAAG,EAAE,CAAA;YACZ,KAAK,GAAG,cAAc,CAAA;QACxB,CAAC;QACD,6DAA6D;QAC7D,KAAK,IAAI,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC5C,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACpB,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC7C,OAAO,OAAO,CAAA;AAChB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { SkillFileUpload } from './api-client.js';
2
+ import type { SkillFileEntry } from './skill-fingerprint.js';
3
+ export declare function toSkillFileUpload(entry: SkillFileEntry): SkillFileUpload;
4
+ //# sourceMappingURL=skill-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-files.d.ts","sourceRoot":"","sources":["../../src/lib/skill-files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AA6B5D,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CAQxE"}
@@ -0,0 +1,36 @@
1
+ import { SKILL_FILE } from './skills-discovery.js';
2
+ /**
3
+ * Turn a fingerprinted manifest entry into its wire form. Every supporting
4
+ * file goes up verbatim: as UTF-8 text when it is text, base64 otherwise.
5
+ * SKILL.md is the one exception — its bytes already ride as `frontmatterRaw`
6
+ * + `body`, so its manifest row carries only `path` / `size` / `sha256`.
7
+ */
8
+ /** A NUL byte this early is the classic binary tell (ELF, woff2, images, UTF-16). */
9
+ const TEXT_SNIFF_BYTES = 8 * 1024;
10
+ /**
11
+ * Strict so that latin-1 or otherwise malformed bytes fall back to base64:
12
+ * the server hashes what it decodes, and only a faithful round-trip matches.
13
+ * `ignoreBOM` keeps a leading BOM in the string so it re-encodes unchanged.
14
+ */
15
+ const utf8 = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
16
+ function decodeText(bytes) {
17
+ if (bytes.subarray(0, TEXT_SNIFF_BYTES).includes(0))
18
+ return undefined;
19
+ try {
20
+ return utf8.decode(bytes);
21
+ }
22
+ catch {
23
+ return undefined;
24
+ }
25
+ }
26
+ export function toSkillFileUpload(entry) {
27
+ const { path, size, sha256, bytes } = entry;
28
+ if (path === SKILL_FILE)
29
+ return { path, size, sha256 };
30
+ const text = decodeText(bytes);
31
+ if (text === undefined) {
32
+ return { path, size, sha256, content: bytes.toString('base64'), encoding: 'base64' };
33
+ }
34
+ return { path, size, sha256, content: text, encoding: 'utf8' };
35
+ }
36
+ //# sourceMappingURL=skill-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-files.js","sourceRoot":"","sources":["../../src/lib/skill-files.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAElD;;;;;GAKG;AAEH,qFAAqF;AACrF,MAAM,gBAAgB,GAAG,CAAC,GAAG,IAAI,CAAA;AAEjC;;;;GAIG;AACH,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;AAEvE,SAAS,UAAU,CAAC,KAAa;IAC/B,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,SAAS,CAAA;IACrE,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAqB;IACrD,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAA;IAC3C,IAAI,IAAI,KAAK,UAAU;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;IACtD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAA;IAC9B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAA;IACtF,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAA;AAChE,CAAC"}