@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.
- package/README.md +115 -0
- package/dist/cli.js +30 -3
- 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 +170 -65
- 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 +3 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +58 -0
- package/dist/commands/setup.js.map +1 -0
- 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 +43 -6
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +106 -25
- 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 +47 -0
- package/dist/lib/setup.d.ts.map +1 -0
- package/dist/lib/setup.js +179 -0
- package/dist/lib/setup.js.map +1 -0
- 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 +16 -8
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { RepoBinding } from './repo-binding.js';
|
|
2
|
+
export interface TargetOrg {
|
|
3
|
+
/**
|
|
4
|
+
* Bucket key for org-scoped sync state: the orgId for OAuth chains, or
|
|
5
|
+
* `api-key:<keyId>` for sk_ keys (whose org the API does not expose;
|
|
6
|
+
* server-side ingest dedupe keeps re-uploads harmless either way).
|
|
7
|
+
*/
|
|
8
|
+
stateKey: string;
|
|
9
|
+
/** Org slug when resolvable (OAuth); null for sk_ API keys. */
|
|
10
|
+
slug: string | null;
|
|
11
|
+
/** Human-readable destination, e.g. `Halyard Studio (halyard-studio)`. */
|
|
12
|
+
label: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Resolve which organization uploads will land in.
|
|
16
|
+
* sk_ API keys authenticate against /api/v1/api-keys/me (the OAuth-only
|
|
17
|
+
* /api/v1/me 401s for them); OAuth chains use /api/v1/me.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveTargetOrg(apiUrl: string): Promise<TargetOrg>;
|
|
20
|
+
/**
|
|
21
|
+
* Upload preflight shared by `sync` and `push`: resolve the target org, print
|
|
22
|
+
* where uploads are going, and hard-fail (throw) when the repo binding and the
|
|
23
|
+
* credentials disagree about the org. Runs before any upload, including
|
|
24
|
+
* --dry-run.
|
|
25
|
+
*/
|
|
26
|
+
export declare function runPreflight(options: {
|
|
27
|
+
apiUrl: string;
|
|
28
|
+
binding: RepoBinding | null;
|
|
29
|
+
/** What is being uploaded: repo root, `all sessions`, or a file path. */
|
|
30
|
+
scopeLabel: string;
|
|
31
|
+
verb?: string;
|
|
32
|
+
/** Where the `Syncing … → org` line goes (stderr for --json callers). Default: stdout. */
|
|
33
|
+
print?: (line: string) => void;
|
|
34
|
+
}): Promise<TargetOrg>;
|
|
35
|
+
//# sourceMappingURL=preflight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.d.ts","sourceRoot":"","sources":["../../src/lib/preflight.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEpD,MAAM,WAAW,SAAS;IACxB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB,+DAA+D;IAC/D,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAA;CACd;AAWD;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CA0BzE;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE;IAC1C,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;IAC3B,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0FAA0F;IAC1F,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAC/B,GAAG,OAAO,CAAC,SAAS,CAAC,CAsBrB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { apiFetch } from './http.js';
|
|
2
|
+
import { getCredentials } from './auth.js';
|
|
3
|
+
/**
|
|
4
|
+
* Resolve which organization uploads will land in.
|
|
5
|
+
* sk_ API keys authenticate against /api/v1/api-keys/me (the OAuth-only
|
|
6
|
+
* /api/v1/me 401s for them); OAuth chains use /api/v1/me.
|
|
7
|
+
*/
|
|
8
|
+
export async function resolveTargetOrg(apiUrl) {
|
|
9
|
+
const creds = getCredentials();
|
|
10
|
+
if (!creds) {
|
|
11
|
+
throw new Error('Not authenticated. Run `halyard login` or set the HALYARD_TOKEN environment variable.');
|
|
12
|
+
}
|
|
13
|
+
if (creds.accessToken.startsWith('sk_halyard_')) {
|
|
14
|
+
const key = await apiFetch(apiUrl, '/api/v1/api-keys/me');
|
|
15
|
+
return {
|
|
16
|
+
stateKey: `api-key:${key.id}`,
|
|
17
|
+
slug: null,
|
|
18
|
+
label: `organization bound to API key ${key.id}`,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const me = await apiFetch(apiUrl, '/api/v1/me');
|
|
22
|
+
if (!me.organization) {
|
|
23
|
+
throw new Error('Your account has no active organization. Run `halyard orgs list`.');
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
stateKey: me.organization.id,
|
|
27
|
+
slug: me.organization.slug,
|
|
28
|
+
label: `${me.organization.name} (${me.organization.slug})`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Upload preflight shared by `sync` and `push`: resolve the target org, print
|
|
33
|
+
* where uploads are going, and hard-fail (throw) when the repo binding and the
|
|
34
|
+
* credentials disagree about the org. Runs before any upload, including
|
|
35
|
+
* --dry-run.
|
|
36
|
+
*/
|
|
37
|
+
export async function runPreflight(options) {
|
|
38
|
+
const { apiUrl, binding, scopeLabel, verb = 'Syncing', print = console.log } = options;
|
|
39
|
+
const target = await resolveTargetOrg(apiUrl);
|
|
40
|
+
print(`${verb} ${scopeLabel} → ${target.label}`);
|
|
41
|
+
if (binding) {
|
|
42
|
+
if (target.slug === null) {
|
|
43
|
+
console.error(`Warning: this repo is bound to "${binding.org}" but an API key's organization ` +
|
|
44
|
+
`cannot be verified from the CLI - uploads go to the org the key belongs to.`);
|
|
45
|
+
}
|
|
46
|
+
else if (target.slug !== binding.org) {
|
|
47
|
+
throw new Error(`Organization mismatch: your credentials are for "${target.slug}" but this repo is ` +
|
|
48
|
+
`bound to "${binding.org}" (${binding.rootDir}/.halyard.json).\n` +
|
|
49
|
+
`Run \`halyard login\` inside this repo to store credentials for "${binding.org}".`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return target;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../src/lib/preflight.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAyB1C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAc;IACnD,MAAM,KAAK,GAAG,cAAc,EAAE,CAAA;IAC9B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,uFAAuF,CACxF,CAAA;IACH,CAAC;IAED,IAAI,KAAK,CAAC,WAAW,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAChD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAmB,MAAM,EAAE,qBAAqB,CAAC,CAAA;QAC3E,OAAO;YACL,QAAQ,EAAE,WAAW,GAAG,CAAC,EAAE,EAAE;YAC7B,IAAI,EAAE,IAAI;YACV,KAAK,EAAE,iCAAiC,GAAG,CAAC,EAAE,EAAE;SACjD,CAAA;IACH,CAAC;IAED,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAgB,MAAM,EAAE,YAAY,CAAC,CAAA;IAC9D,IAAI,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAA;IACtF,CAAC;IACD,OAAO;QACL,QAAQ,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE;QAC5B,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI;QAC1B,KAAK,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,KAAK,EAAE,CAAC,YAAY,CAAC,IAAI,GAAG;KAC3D,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAQlC;IACC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,GAAG,SAAS,EAAE,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,OAAO,CAAA;IAEtF,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAA;IAC7C,KAAK,CAAC,GAAG,IAAI,IAAI,UAAU,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,CAAA;IAEhD,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACzB,OAAO,CAAC,KAAK,CACX,mCAAmC,OAAO,CAAC,GAAG,kCAAkC;gBAC9E,6EAA6E,CAChF,CAAA;QACH,CAAC;aAAM,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC;YACvC,MAAM,IAAI,KAAK,CACb,oDAAoD,MAAM,CAAC,IAAI,qBAAqB;gBAClF,aAAa,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,OAAO,oBAAoB;gBACjE,oEAAoE,OAAO,CAAC,GAAG,IAAI,CACtF,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export declare const REPO_BINDING_FILENAME = ".halyard.json";
|
|
2
|
+
export interface RepoBinding {
|
|
3
|
+
/** Organization slug this repo is bound to. */
|
|
4
|
+
org: string;
|
|
5
|
+
/** Directory containing the binding file (main repo root for worktrees). */
|
|
6
|
+
rootDir: string;
|
|
7
|
+
}
|
|
8
|
+
/** Reset the per-process binding cache (tests). */
|
|
9
|
+
export declare function clearRepoBindingCache(): void;
|
|
10
|
+
/**
|
|
11
|
+
* Find the `.halyard.json` repo binding for a directory.
|
|
12
|
+
*
|
|
13
|
+
* Walks up from `startDir` looking for the binding file. If none is found and
|
|
14
|
+
* the directory is inside a git repo, falls back to the MAIN repo root (parent
|
|
15
|
+
* of `git rev-parse --git-common-dir`) so detached worktrees (~/.codex/worktrees
|
|
16
|
+
* and friends) inherit the binding committed at the main checkout.
|
|
17
|
+
*/
|
|
18
|
+
export declare function findRepoBinding(startDir?: string): RepoBinding | null;
|
|
19
|
+
export interface WriteBindingResult {
|
|
20
|
+
status: 'created' | 'updated' | 'unchanged';
|
|
21
|
+
filePath: string;
|
|
22
|
+
org: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Write a `.halyard.json` binding at the git toplevel of `cwd`.
|
|
26
|
+
* Throws when not inside a git repo, or when an existing binding names a
|
|
27
|
+
* different org and `force` is not set. Extra fields in an existing file are
|
|
28
|
+
* preserved on rebind.
|
|
29
|
+
*/
|
|
30
|
+
export declare function writeRepoBinding(options: {
|
|
31
|
+
org: string;
|
|
32
|
+
cwd?: string;
|
|
33
|
+
force?: boolean;
|
|
34
|
+
dryRun?: boolean;
|
|
35
|
+
}): WriteBindingResult;
|
|
36
|
+
/** Resolve the git toplevel of a directory, or null when not in a repo. */
|
|
37
|
+
export declare function gitToplevel(dir: string): string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Resolve the MAIN repo root for a directory via `git rev-parse --git-common-dir`.
|
|
40
|
+
* Returns null when git is unavailable or the directory is not inside a repo.
|
|
41
|
+
*/
|
|
42
|
+
export declare function gitMainRepoRoot(dir: string): string | null;
|
|
43
|
+
//# sourceMappingURL=repo-binding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo-binding.d.ts","sourceRoot":"","sources":["../../src/lib/repo-binding.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB,kBAAkB,CAAA;AAEpD,MAAM,WAAW,WAAW;IAC1B,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAA;IACX,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAA;CAChB;AAID,mDAAmD;AACnD,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,QAAQ,GAAE,MAAsB,GAAG,WAAW,GAAG,IAAI,CAOpF;AA0CD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,CAAA;IAC3C,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IACxC,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,GAAG,kBAAkB,CAmDrB;AAED,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYtD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgB1D"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
4
|
+
export const REPO_BINDING_FILENAME = '.halyard.json';
|
|
5
|
+
const bindingCache = new Map();
|
|
6
|
+
/** Reset the per-process binding cache (tests). */
|
|
7
|
+
export function clearRepoBindingCache() {
|
|
8
|
+
bindingCache.clear();
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Find the `.halyard.json` repo binding for a directory.
|
|
12
|
+
*
|
|
13
|
+
* Walks up from `startDir` looking for the binding file. If none is found and
|
|
14
|
+
* the directory is inside a git repo, falls back to the MAIN repo root (parent
|
|
15
|
+
* of `git rev-parse --git-common-dir`) so detached worktrees (~/.codex/worktrees
|
|
16
|
+
* and friends) inherit the binding committed at the main checkout.
|
|
17
|
+
*/
|
|
18
|
+
export function findRepoBinding(startDir = process.cwd()) {
|
|
19
|
+
const start = resolve(startDir);
|
|
20
|
+
const cached = bindingCache.get(start);
|
|
21
|
+
if (cached !== undefined)
|
|
22
|
+
return cached;
|
|
23
|
+
const binding = locateBinding(start);
|
|
24
|
+
bindingCache.set(start, binding);
|
|
25
|
+
return binding;
|
|
26
|
+
}
|
|
27
|
+
function locateBinding(start) {
|
|
28
|
+
let dir = start;
|
|
29
|
+
for (;;) {
|
|
30
|
+
const binding = readBindingFile(dir);
|
|
31
|
+
if (binding)
|
|
32
|
+
return binding;
|
|
33
|
+
const parent = dirname(dir);
|
|
34
|
+
if (parent === dir)
|
|
35
|
+
break;
|
|
36
|
+
dir = parent;
|
|
37
|
+
}
|
|
38
|
+
// Detached worktree fallback: the binding file lives in the main checkout,
|
|
39
|
+
// which is not an ancestor of the worktree directory.
|
|
40
|
+
const mainRoot = gitMainRepoRoot(start);
|
|
41
|
+
if (mainRoot && mainRoot !== start) {
|
|
42
|
+
return readBindingFile(mainRoot);
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Parse a `.halyard.json` in `dir`. Unknown extra fields are tolerated;
|
|
48
|
+
* invalid JSON or a missing `org` field is treated as not found (with a
|
|
49
|
+
* one-line warning so a broken binding is not silently ignored).
|
|
50
|
+
*/
|
|
51
|
+
function readBindingFile(dir) {
|
|
52
|
+
const filePath = join(dir, REPO_BINDING_FILENAME);
|
|
53
|
+
if (!existsSync(filePath))
|
|
54
|
+
return null;
|
|
55
|
+
try {
|
|
56
|
+
const parsed = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
57
|
+
if (typeof parsed.org === 'string' && parsed.org.trim().length > 0) {
|
|
58
|
+
return { org: parsed.org.trim(), rootDir: dir };
|
|
59
|
+
}
|
|
60
|
+
console.error(`Warning: ${filePath} has no "org" field - ignoring repo binding.`);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
console.error(`Warning: ${filePath} is not valid JSON - ignoring repo binding.`);
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Write a `.halyard.json` binding at the git toplevel of `cwd`.
|
|
69
|
+
* Throws when not inside a git repo, or when an existing binding names a
|
|
70
|
+
* different org and `force` is not set. Extra fields in an existing file are
|
|
71
|
+
* preserved on rebind.
|
|
72
|
+
*/
|
|
73
|
+
export function writeRepoBinding(options) {
|
|
74
|
+
const { org, cwd = process.cwd(), force = false, dryRun = false } = options;
|
|
75
|
+
const toplevel = gitToplevel(cwd);
|
|
76
|
+
if (!toplevel) {
|
|
77
|
+
throw new Error('Not inside a git repository. Run `halyard setup --org` from a repo, or create .halyard.json manually.');
|
|
78
|
+
}
|
|
79
|
+
const filePath = join(toplevel, REPO_BINDING_FILENAME);
|
|
80
|
+
let extraFields = {};
|
|
81
|
+
let status = 'created';
|
|
82
|
+
if (existsSync(filePath)) {
|
|
83
|
+
let existing = null;
|
|
84
|
+
try {
|
|
85
|
+
const parsed = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
86
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
87
|
+
existing = parsed;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// fall through - unparseable counts as a conflicting file
|
|
92
|
+
}
|
|
93
|
+
const existingOrg = typeof existing?.['org'] === 'string' ? existing['org'] : null;
|
|
94
|
+
if (existingOrg === org) {
|
|
95
|
+
return { status: 'unchanged', filePath, org };
|
|
96
|
+
}
|
|
97
|
+
if (!force) {
|
|
98
|
+
throw new Error(existingOrg
|
|
99
|
+
? `${filePath} already binds this repo to "${existingOrg}". Pass --force to rebind to "${org}".`
|
|
100
|
+
: `${filePath} exists but has no valid "org" field. Pass --force to overwrite.`);
|
|
101
|
+
}
|
|
102
|
+
if (existing) {
|
|
103
|
+
const { org: _org, ...rest } = existing;
|
|
104
|
+
extraFields = rest;
|
|
105
|
+
}
|
|
106
|
+
status = 'updated';
|
|
107
|
+
}
|
|
108
|
+
if (!dryRun) {
|
|
109
|
+
const hasExtras = Object.keys(extraFields).length > 0;
|
|
110
|
+
const content = hasExtras
|
|
111
|
+
? `${JSON.stringify({ org, ...extraFields }, null, 2)}\n`
|
|
112
|
+
: `{ "org": ${JSON.stringify(org)} }\n`;
|
|
113
|
+
writeFileSync(filePath, content);
|
|
114
|
+
}
|
|
115
|
+
clearRepoBindingCache();
|
|
116
|
+
return { status, filePath, org };
|
|
117
|
+
}
|
|
118
|
+
/** Resolve the git toplevel of a directory, or null when not in a repo. */
|
|
119
|
+
export function gitToplevel(dir) {
|
|
120
|
+
try {
|
|
121
|
+
const res = spawnSync('git', ['-C', dir, 'rev-parse', '--show-toplevel'], {
|
|
122
|
+
encoding: 'utf-8',
|
|
123
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
124
|
+
});
|
|
125
|
+
if (res.error || res.status !== 0)
|
|
126
|
+
return null;
|
|
127
|
+
const out = res.stdout.trim();
|
|
128
|
+
return out.length > 0 ? resolve(out) : null;
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Resolve the MAIN repo root for a directory via `git rev-parse --git-common-dir`.
|
|
136
|
+
* Returns null when git is unavailable or the directory is not inside a repo.
|
|
137
|
+
*/
|
|
138
|
+
export function gitMainRepoRoot(dir) {
|
|
139
|
+
try {
|
|
140
|
+
const res = spawnSync('git', ['-C', dir, 'rev-parse', '--git-common-dir'], {
|
|
141
|
+
encoding: 'utf-8',
|
|
142
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
143
|
+
});
|
|
144
|
+
if (res.error || res.status !== 0)
|
|
145
|
+
return null;
|
|
146
|
+
const out = res.stdout.trim();
|
|
147
|
+
if (!out)
|
|
148
|
+
return null;
|
|
149
|
+
// Relative when run from the toplevel (".git"), absolute from subdirs and
|
|
150
|
+
// worktrees - resolve against the queried dir to normalize.
|
|
151
|
+
const commonDir = resolve(dir, out);
|
|
152
|
+
return basename(commonDir) === '.git' ? dirname(commonDir) : commonDir;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=repo-binding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo-binding.js","sourceRoot":"","sources":["../../src/lib/repo-binding.ts"],"names":[],"mappings":"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"}
|
|
@@ -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"}
|