@halyard/cli 0.1.2 → 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.
- package/README.md +88 -1
- package/dist/cli.js +27 -4
- package/dist/cli.js.map +1 -1
- package/dist/commands/api-keys.d.ts +3 -0
- package/dist/commands/api-keys.d.ts.map +1 -0
- package/dist/commands/api-keys.js +90 -0
- package/dist/commands/api-keys.js.map +1 -0
- package/dist/commands/hook.d.ts +6 -0
- package/dist/commands/hook.d.ts.map +1 -0
- package/dist/commands/hook.js +106 -0
- package/dist/commands/hook.js.map +1 -0
- package/dist/commands/login.d.ts +17 -0
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +111 -14
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/orgs.d.ts +3 -0
- package/dist/commands/orgs.d.ts.map +1 -0
- package/dist/commands/orgs.js +102 -0
- package/dist/commands/orgs.js.map +1 -0
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +29 -6
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/sessions.d.ts +3 -0
- package/dist/commands/sessions.d.ts.map +1 -0
- package/dist/commands/sessions.js +139 -0
- package/dist/commands/sessions.js.map +1 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +28 -3
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/skills.d.ts +12 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +171 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +51 -49
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/users.d.ts +13 -0
- package/dist/commands/users.d.ts.map +1 -0
- package/dist/commands/users.js +205 -0
- package/dist/commands/users.js.map +1 -0
- package/dist/commands/whoami.d.ts +21 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +77 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/lib/api-client.d.ts +64 -2
- package/dist/lib/api-client.d.ts.map +1 -1
- package/dist/lib/api-client.js +32 -7
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/auth.d.ts +42 -6
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +101 -27
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/config-files.d.ts +6 -0
- package/dist/lib/config-files.d.ts.map +1 -0
- package/dist/lib/config-files.js +27 -0
- package/dist/lib/config-files.js.map +1 -0
- package/dist/lib/config.d.ts +13 -7
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +22 -8
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/copilot-export.d.ts +31 -0
- package/dist/lib/copilot-export.d.ts.map +1 -0
- package/dist/lib/copilot-export.js +129 -0
- package/dist/lib/copilot-export.js.map +1 -0
- package/dist/lib/discovery.d.ts +60 -2
- package/dist/lib/discovery.d.ts.map +1 -1
- package/dist/lib/discovery.js +187 -15
- package/dist/lib/discovery.js.map +1 -1
- package/dist/lib/hook-discovery.d.ts +43 -0
- package/dist/lib/hook-discovery.d.ts.map +1 -0
- package/dist/lib/hook-discovery.js +90 -0
- package/dist/lib/hook-discovery.js.map +1 -0
- package/dist/lib/hook-input.d.ts +36 -0
- package/dist/lib/hook-input.d.ts.map +1 -0
- package/dist/lib/hook-input.js +161 -0
- package/dist/lib/hook-input.js.map +1 -0
- package/dist/lib/hook-runner.d.ts +48 -0
- package/dist/lib/hook-runner.d.ts.map +1 -0
- package/dist/lib/hook-runner.js +306 -0
- package/dist/lib/hook-runner.js.map +1 -0
- package/dist/lib/http.d.ts +18 -0
- package/dist/lib/http.d.ts.map +1 -0
- package/dist/lib/http.js +54 -0
- package/dist/lib/http.js.map +1 -0
- package/dist/lib/opencode-export.d.ts +22 -0
- package/dist/lib/opencode-export.d.ts.map +1 -0
- package/dist/lib/opencode-export.js +53 -0
- package/dist/lib/opencode-export.js.map +1 -0
- package/dist/lib/output.d.ts +27 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +74 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/preflight.d.ts +35 -0
- package/dist/lib/preflight.d.ts.map +1 -0
- package/dist/lib/preflight.js +54 -0
- package/dist/lib/preflight.js.map +1 -0
- package/dist/lib/repo-binding.d.ts +43 -0
- package/dist/lib/repo-binding.d.ts.map +1 -0
- package/dist/lib/repo-binding.js +158 -0
- package/dist/lib/repo-binding.js.map +1 -0
- package/dist/lib/repo-identity.d.ts +29 -0
- package/dist/lib/repo-identity.d.ts.map +1 -0
- package/dist/lib/repo-identity.js +113 -0
- package/dist/lib/repo-identity.js.map +1 -0
- package/dist/lib/setup-hooks.d.ts +41 -0
- package/dist/lib/setup-hooks.d.ts.map +1 -0
- package/dist/lib/setup-hooks.js +276 -0
- package/dist/lib/setup-hooks.js.map +1 -0
- package/dist/lib/setup.d.ts +4 -1
- package/dist/lib/setup.d.ts.map +1 -1
- package/dist/lib/setup.js +20 -31
- package/dist/lib/setup.js.map +1 -1
- package/dist/lib/skill-batches.d.ts +16 -0
- package/dist/lib/skill-batches.d.ts.map +1 -0
- package/dist/lib/skill-batches.js +35 -0
- package/dist/lib/skill-batches.js.map +1 -0
- package/dist/lib/skill-files.d.ts +4 -0
- package/dist/lib/skill-files.d.ts.map +1 -0
- package/dist/lib/skill-files.js +36 -0
- package/dist/lib/skill-files.js.map +1 -0
- package/dist/lib/skill-fingerprint.d.ts +55 -0
- package/dist/lib/skill-fingerprint.d.ts.map +1 -0
- package/dist/lib/skill-fingerprint.js +204 -0
- package/dist/lib/skill-fingerprint.js.map +1 -0
- package/dist/lib/skills-discovery.d.ts +57 -0
- package/dist/lib/skills-discovery.d.ts.map +1 -0
- package/dist/lib/skills-discovery.js +157 -0
- package/dist/lib/skills-discovery.js.map +1 -0
- package/dist/lib/skills-sync.d.ts +66 -0
- package/dist/lib/skills-sync.d.ts.map +1 -0
- package/dist/lib/skills-sync.js +251 -0
- package/dist/lib/skills-sync.js.map +1 -0
- package/dist/lib/sync-runner.d.ts +56 -0
- package/dist/lib/sync-runner.d.ts.map +1 -0
- package/dist/lib/sync-runner.js +106 -0
- package/dist/lib/sync-runner.js.map +1 -0
- package/dist/lib/sync-state.d.ts +36 -6
- package/dist/lib/sync-state.d.ts.map +1 -1
- package/dist/lib/sync-state.js +41 -11
- package/dist/lib/sync-state.js.map +1 -1
- package/package.json +10 -5
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface RepoIdentity {
|
|
2
|
+
/** Absolute MAIN repo root (parent of the git common dir; worktrees collapse here). */
|
|
3
|
+
rootDir: string;
|
|
4
|
+
/** Normalized remote.origin.url, or null when the repo has no origin. */
|
|
5
|
+
remote: string | null;
|
|
6
|
+
}
|
|
7
|
+
/** Reset the per-process identity cache (tests). */
|
|
8
|
+
export declare function clearRepoIdentityCache(): void;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the repo identity for a directory: main-repo root via
|
|
11
|
+
* `git rev-parse --git-common-dir` plus the normalized origin remote.
|
|
12
|
+
* Nonexistent dirs and git failures resolve to null (unresolved).
|
|
13
|
+
* Results are cached per dir for the life of the process.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveRepoIdentity(dir: string): RepoIdentity | null;
|
|
16
|
+
/**
|
|
17
|
+
* Normalize a git remote URL so transport variants compare equal:
|
|
18
|
+
* `git@github.com:ajust-au/agent.git` === `https://github.com/ajust-au/agent`.
|
|
19
|
+
* Lowercases the host, strips protocol/credentials/port, and trims a
|
|
20
|
+
* trailing `.git` / slashes. Returns null when unparseable.
|
|
21
|
+
*/
|
|
22
|
+
export declare function normalizeRemoteUrl(url: string): string | null;
|
|
23
|
+
/**
|
|
24
|
+
* Do two identities point at the same repository?
|
|
25
|
+
* Common-dir (main root) equality wins; otherwise fall back to remote
|
|
26
|
+
* equality, but only when both remotes are defined.
|
|
27
|
+
*/
|
|
28
|
+
export declare function sameRepo(a: RepoIdentity | null, b: RepoIdentity | null): boolean;
|
|
29
|
+
//# sourceMappingURL=repo-identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo-identity.d.ts","sourceRoot":"","sources":["../../src/lib/repo-identity.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,uFAAuF;IACvF,OAAO,EAAE,MAAM,CAAA;IACf,yEAAyE;IACzE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAID,oDAAoD;AACpD,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAOpE;AAyCD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA8B7D;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,YAAY,GAAG,IAAI,EAAE,CAAC,EAAE,YAAY,GAAG,IAAI,GAAG,OAAO,CAKhF"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, realpathSync } from 'node:fs';
|
|
3
|
+
import { basename, dirname, resolve } from 'node:path';
|
|
4
|
+
const identityCache = new Map();
|
|
5
|
+
/** Reset the per-process identity cache (tests). */
|
|
6
|
+
export function clearRepoIdentityCache() {
|
|
7
|
+
identityCache.clear();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the repo identity for a directory: main-repo root via
|
|
11
|
+
* `git rev-parse --git-common-dir` plus the normalized origin remote.
|
|
12
|
+
* Nonexistent dirs and git failures resolve to null (unresolved).
|
|
13
|
+
* Results are cached per dir for the life of the process.
|
|
14
|
+
*/
|
|
15
|
+
export function resolveRepoIdentity(dir) {
|
|
16
|
+
const key = resolve(dir);
|
|
17
|
+
const cached = identityCache.get(key);
|
|
18
|
+
if (cached !== undefined)
|
|
19
|
+
return cached;
|
|
20
|
+
const identity = computeIdentity(key);
|
|
21
|
+
identityCache.set(key, identity);
|
|
22
|
+
return identity;
|
|
23
|
+
}
|
|
24
|
+
function computeIdentity(dir) {
|
|
25
|
+
if (!existsSync(dir))
|
|
26
|
+
return null;
|
|
27
|
+
const commonDirOut = git(dir, ['rev-parse', '--git-common-dir']);
|
|
28
|
+
if (!commonDirOut)
|
|
29
|
+
return null;
|
|
30
|
+
const commonDir = resolve(dir, commonDirOut);
|
|
31
|
+
// Canonicalize so a session recorded under a symlinked path (macOS
|
|
32
|
+
// /var → /private/var, ~/repos symlinks) still matches the binding root,
|
|
33
|
+
// which git reports fully resolved.
|
|
34
|
+
const rootDir = canonical(basename(commonDir) === '.git' ? dirname(commonDir) : commonDir);
|
|
35
|
+
const remoteOut = git(dir, ['config', '--get', 'remote.origin.url']);
|
|
36
|
+
const remote = remoteOut ? normalizeRemoteUrl(remoteOut) : null;
|
|
37
|
+
return { rootDir, remote };
|
|
38
|
+
}
|
|
39
|
+
function canonical(path) {
|
|
40
|
+
try {
|
|
41
|
+
return realpathSync(path);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return path;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function git(dir, args) {
|
|
48
|
+
try {
|
|
49
|
+
const res = spawnSync('git', ['-C', dir, ...args], {
|
|
50
|
+
encoding: 'utf-8',
|
|
51
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
52
|
+
});
|
|
53
|
+
if (res.error || res.status !== 0)
|
|
54
|
+
return null;
|
|
55
|
+
const out = res.stdout.trim();
|
|
56
|
+
return out.length > 0 ? out : null;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Normalize a git remote URL so transport variants compare equal:
|
|
64
|
+
* `git@github.com:ajust-au/agent.git` === `https://github.com/ajust-au/agent`.
|
|
65
|
+
* Lowercases the host, strips protocol/credentials/port, and trims a
|
|
66
|
+
* trailing `.git` / slashes. Returns null when unparseable.
|
|
67
|
+
*/
|
|
68
|
+
export function normalizeRemoteUrl(url) {
|
|
69
|
+
const trimmed = url.trim();
|
|
70
|
+
if (!trimmed)
|
|
71
|
+
return null;
|
|
72
|
+
let host;
|
|
73
|
+
let path;
|
|
74
|
+
// Protocol form: https://host/path, ssh://git@host:port/path, git://host/path
|
|
75
|
+
const protoMatch = /^[a-z][a-z0-9+.-]*:\/\/(?:[^@/]+@)?([^/:]+)(?::\d+)?\/(.*)$/i.exec(trimmed);
|
|
76
|
+
// SCP-like form: git@host:path
|
|
77
|
+
const scpMatch = /^(?:[^@/]+@)?([^/:]+):(.*)$/.exec(trimmed);
|
|
78
|
+
if (protoMatch) {
|
|
79
|
+
host = protoMatch[1];
|
|
80
|
+
path = protoMatch[2];
|
|
81
|
+
}
|
|
82
|
+
else if (scpMatch) {
|
|
83
|
+
host = scpMatch[1];
|
|
84
|
+
path = scpMatch[2];
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
if (!host || path === undefined)
|
|
90
|
+
return null;
|
|
91
|
+
const cleanPath = path
|
|
92
|
+
.replace(/\.git$/i, '')
|
|
93
|
+
.replace(/\/+$/, '')
|
|
94
|
+
.replace(/^\/+/, '');
|
|
95
|
+
if (!cleanPath)
|
|
96
|
+
return null;
|
|
97
|
+
return `${host.toLowerCase()}/${cleanPath}`;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Do two identities point at the same repository?
|
|
101
|
+
* Common-dir (main root) equality wins; otherwise fall back to remote
|
|
102
|
+
* equality, but only when both remotes are defined.
|
|
103
|
+
*/
|
|
104
|
+
export function sameRepo(a, b) {
|
|
105
|
+
if (!a || !b)
|
|
106
|
+
return false;
|
|
107
|
+
if (a.rootDir === b.rootDir)
|
|
108
|
+
return true;
|
|
109
|
+
if (a.remote !== null && b.remote !== null)
|
|
110
|
+
return a.remote === b.remote;
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=repo-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo-identity.js","sourceRoot":"","sources":["../../src/lib/repo-identity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `halyard setup` scaffolding for session upload: a tiny launcher plus one
|
|
3
|
+
* lifecycle-hook config per client, all pointing at `halyard hook`.
|
|
4
|
+
*
|
|
5
|
+
* Merges are idempotent and keyed on the launcher path, so re-running setup
|
|
6
|
+
* (or after a template change) updates our entries and leaves everything else alone.
|
|
7
|
+
*/
|
|
8
|
+
export declare const HOOK_LAUNCHER_PATH = ".halyard/hook.sh";
|
|
9
|
+
export type HookScaffoldTarget = 'launcher' | 'codex' | 'cursor' | 'gemini' | 'copilot' | 'opencode' | 'grok';
|
|
10
|
+
export type HookScaffoldStatus = 'updated' | 'unchanged' | 'error';
|
|
11
|
+
export interface HookScaffoldResult {
|
|
12
|
+
target: HookScaffoldTarget;
|
|
13
|
+
status: HookScaffoldStatus;
|
|
14
|
+
location: string;
|
|
15
|
+
detail: string;
|
|
16
|
+
}
|
|
17
|
+
export interface HookScaffoldOptions {
|
|
18
|
+
dryRun?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface LifecycleHookSpec {
|
|
21
|
+
start: Record<string, unknown>;
|
|
22
|
+
end: Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
export declare function launcherScript(): string;
|
|
25
|
+
export declare function claudeHookSpec(): LifecycleHookSpec;
|
|
26
|
+
/** Codex caps SessionEnd at 3 seconds, so the end hook detaches. */
|
|
27
|
+
export declare function codexHookSpec(): LifecycleHookSpec;
|
|
28
|
+
/** Gemini timeouts are milliseconds and entries carry a name. */
|
|
29
|
+
export declare function geminiHookSpec(): LifecycleHookSpec;
|
|
30
|
+
/** Merge start/end hooks into a Claude-shaped settings object (Claude, Codex, Gemini). */
|
|
31
|
+
export declare function mergeLifecycleHooks(input: unknown, spec: LifecycleHookSpec, events?: {
|
|
32
|
+
start: string;
|
|
33
|
+
end: string;
|
|
34
|
+
}): Record<string, unknown>;
|
|
35
|
+
export declare function mergeCursorHooks(input: unknown): Record<string, unknown>;
|
|
36
|
+
export declare function copilotHooksFile(): string;
|
|
37
|
+
export declare function grokHooksFile(): string;
|
|
38
|
+
export declare function opencodePluginFile(): string;
|
|
39
|
+
/** Everything except Claude's settings.json, which `runSetup` owns (it also enables the plugin there). */
|
|
40
|
+
export declare function scaffoldHooks(cwd: string, options?: HookScaffoldOptions): HookScaffoldResult[];
|
|
41
|
+
//# sourceMappingURL=setup-hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup-hooks.d.ts","sourceRoot":"","sources":["../../src/lib/setup-hooks.ts"],"names":[],"mappings":"AAKA;;;;;;GAMG;AAEH,eAAO,MAAM,kBAAkB,qBAAqB,CAAA;AAEpD,MAAM,MAAM,kBAAkB,GAC1B,UAAU,GACV,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,UAAU,GACV,MAAM,CAAA;AACV,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAA;AAElE,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,kBAAkB,CAAA;IAC1B,MAAM,EAAE,kBAAkB,CAAA;IAC1B,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC7B;AAED,wBAAgB,cAAc,IAAI,MAAM,CAgBvC;AAMD,wBAAgB,cAAc,IAAI,iBAAiB,CAiBlD;AAED,oEAAoE;AACpE,wBAAgB,aAAa,IAAI,iBAAiB,CAejD;AAED,iEAAiE;AACjE,wBAAgB,cAAc,IAAI,iBAAiB,CAiBlD;AAgCD,0FAA0F;AAC1F,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,iBAAiB,EACvB,MAAM,GAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAiD,GACpF,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWxE;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAOzC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAWtC;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAkE3C;AA8ED,0GAA0G;AAC1G,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,mBAAwB,GAChC,kBAAkB,EAAE,CAoCtB"}
|
|
@@ -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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"}
|
package/dist/lib/setup.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { type HookScaffoldTarget } from './setup-hooks.js';
|
|
1
2
|
export declare const HALYARD_MCP_URL = "https://mcp.usehalyard.ai";
|
|
2
3
|
export declare const HALYARD_CLAUDE_MARKETPLACE_NAME = "halyard-labs";
|
|
3
4
|
export declare const HALYARD_CLAUDE_MARKETPLACE_REPO = "halyard-labs/claude-plugin-marketplace";
|
|
4
5
|
export declare const HALYARD_CLAUDE_PLUGIN_ID = "halyard@halyard-labs";
|
|
5
|
-
export type SetupTarget = 'claude' | 'codex' | 'cursor';
|
|
6
|
+
export type SetupTarget = 'claude' | 'codex' | 'cursor' | HookScaffoldTarget;
|
|
6
7
|
export type SetupStatus = 'updated' | 'unchanged' | 'skipped' | 'error';
|
|
7
8
|
export interface SetupResult {
|
|
8
9
|
target: SetupTarget;
|
|
@@ -19,6 +20,8 @@ export interface SetupOptions {
|
|
|
19
20
|
dryRun?: boolean;
|
|
20
21
|
homeDir?: string;
|
|
21
22
|
runner?: CommandRunner;
|
|
23
|
+
/** Write the session-upload hooks (launcher + per-client config). Default true. */
|
|
24
|
+
hooks?: boolean;
|
|
22
25
|
}
|
|
23
26
|
interface ClaudeSettings extends Record<string, unknown> {
|
|
24
27
|
enabledPlugins?: Record<string, unknown>;
|
package/dist/lib/setup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/lib/setup.ts"],"names":[],"mappings":"AAOA,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,CAAA;
|
|
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"}
|
package/dist/lib/setup.js
CHANGED
|
@@ -4,6 +4,8 @@ import { homedir } from 'node:os';
|
|
|
4
4
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
5
5
|
import { isDeepStrictEqual } from 'node:util';
|
|
6
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';
|
|
7
9
|
export const HALYARD_MCP_URL = 'https://mcp.usehalyard.ai';
|
|
8
10
|
export const HALYARD_CLAUDE_MARKETPLACE_NAME = 'halyard-labs';
|
|
9
11
|
export const HALYARD_CLAUDE_MARKETPLACE_REPO = 'halyard-labs/claude-plugin-marketplace';
|
|
@@ -20,12 +22,19 @@ export function runSetup(options = {}) {
|
|
|
20
22
|
const dryRun = options.dryRun ?? false;
|
|
21
23
|
const homeDir = options.homeDir ?? homedir();
|
|
22
24
|
const runner = options.runner ?? createSystemRunner();
|
|
25
|
+
const hooks = options.hooks ?? true;
|
|
23
26
|
const paths = getSetupPaths(cwd, homeDir);
|
|
24
|
-
|
|
25
|
-
setupClaudeProject(paths.claudeProject, dryRun),
|
|
27
|
+
const results = [
|
|
28
|
+
setupClaudeProject(paths.claudeProject, dryRun, hooks),
|
|
26
29
|
setupCursorProject(paths.cursorProject, dryRun),
|
|
27
30
|
setupCodexUser(paths.codexUser, dryRun, runner),
|
|
28
31
|
];
|
|
32
|
+
if (hooks) {
|
|
33
|
+
for (const entry of scaffoldHooks(cwd, { dryRun })) {
|
|
34
|
+
results.push({ ...entry, scope: 'project' });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return results;
|
|
29
38
|
}
|
|
30
39
|
export function getSetupPaths(cwd, homeDir) {
|
|
31
40
|
return {
|
|
@@ -78,21 +87,24 @@ export function mergeCodexConfig(input) {
|
|
|
78
87
|
mcp_servers: mcpServers,
|
|
79
88
|
};
|
|
80
89
|
}
|
|
81
|
-
function setupClaudeProject(filePath, dryRun) {
|
|
90
|
+
function setupClaudeProject(filePath, dryRun, hooks) {
|
|
82
91
|
const current = readJsonFile(filePath);
|
|
83
92
|
if (current instanceof Error) {
|
|
84
93
|
return result('claude', 'project', 'error', filePath, current.message);
|
|
85
94
|
}
|
|
86
|
-
|
|
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';
|
|
87
101
|
if (isDeepStrictEqual(current, next)) {
|
|
88
|
-
return result('claude', 'project', 'unchanged', filePath,
|
|
102
|
+
return result('claude', 'project', 'unchanged', filePath, `Already scaffolded: ${what}.`);
|
|
89
103
|
}
|
|
90
104
|
if (!dryRun) {
|
|
91
105
|
writeJsonFile(filePath, next);
|
|
92
106
|
}
|
|
93
|
-
return result('claude', 'project', 'updated', filePath, dryRun
|
|
94
|
-
? 'Would add the Halyard marketplace and enable the project plugin.'
|
|
95
|
-
: 'Added the Halyard marketplace and enabled the project plugin.');
|
|
107
|
+
return result('claude', 'project', 'updated', filePath, dryRun ? `Would add ${what}.` : `Added ${what}.`);
|
|
96
108
|
}
|
|
97
109
|
function setupCursorProject(filePath, dryRun) {
|
|
98
110
|
const current = readJsonFile(filePath);
|
|
@@ -130,20 +142,6 @@ function setupCodexUser(filePath, dryRun, runner) {
|
|
|
130
142
|
? 'Would add the Halyard MCP server to Codex user config.'
|
|
131
143
|
: 'Added the Halyard MCP server to Codex user config.');
|
|
132
144
|
}
|
|
133
|
-
function readJsonFile(filePath) {
|
|
134
|
-
if (!existsSync(filePath))
|
|
135
|
-
return {};
|
|
136
|
-
try {
|
|
137
|
-
return JSON.parse(readFileSync(filePath, 'utf8'));
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
return new Error(`Could not parse JSON at ${filePath}: ${formatError(error)}`);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
function writeJsonFile(filePath, value) {
|
|
144
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
145
|
-
writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
146
|
-
}
|
|
147
145
|
function readTomlFile(filePath) {
|
|
148
146
|
if (!existsSync(filePath))
|
|
149
147
|
return {};
|
|
@@ -158,15 +156,6 @@ function writeTomlFile(filePath, value) {
|
|
|
158
156
|
mkdirSync(dirname(filePath), { recursive: true });
|
|
159
157
|
writeFileSync(filePath, `${stringifyToml(asObject(value))}\n`);
|
|
160
158
|
}
|
|
161
|
-
function asObject(value) {
|
|
162
|
-
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
163
|
-
return { ...value };
|
|
164
|
-
}
|
|
165
|
-
return {};
|
|
166
|
-
}
|
|
167
|
-
function formatError(error) {
|
|
168
|
-
return error instanceof Error ? error.message : String(error);
|
|
169
|
-
}
|
|
170
159
|
function result(target, scope, status, filePath, detail) {
|
|
171
160
|
return {
|
|
172
161
|
target,
|