@indigoai-us/hq-cli 5.345.122 → 5.345.123

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 (30) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +15 -0
  3. package/assets/scaffold/core/scripts/handoff-sync-prefetch.sh +121 -0
  4. package/assets/scaffold/core/scripts/handoff-sync-publish.sh +124 -0
  5. package/assets/scaffold/core/scripts/knowledge-pulse-lock.sh +27 -0
  6. package/assets/scaffold/core/scripts/lib/session-skill-catalog.sh +226 -0
  7. package/assets/scaffold/core/scripts/skill-installed.sh +43 -0
  8. package/dist/command-catalog.generated.d.ts +93 -0
  9. package/dist/command-catalog.generated.js +111 -0
  10. package/dist/command-registration-plan.d.ts +6 -0
  11. package/dist/command-registration-plan.js +1 -0
  12. package/dist/commands/core-bucket-c-handoff-sync-fast.d.ts +3 -0
  13. package/dist/commands/core-bucket-c-handoff-sync-fast.js +16 -0
  14. package/dist/commands/core-bucket-c-handoff-sync-native.d.ts +45 -0
  15. package/dist/commands/core-bucket-c-handoff-sync-native.js +300 -0
  16. package/dist/commands/core-bucket-c-small-fast.d.ts +5 -0
  17. package/dist/commands/core-bucket-c-small-fast.js +19 -0
  18. package/dist/commands/core-bucket-c-small-native.d.ts +11 -0
  19. package/dist/commands/core-bucket-c-small-native.js +197 -0
  20. package/dist/commands/core-derive-trigger-facts-native.js +4 -1
  21. package/dist/commands/core.js +25 -0
  22. package/dist/commands/deploy-comments.d.ts +2 -0
  23. package/dist/commands/deploy-comments.js +2 -2
  24. package/dist/commands/deploy-list.d.ts +22 -0
  25. package/dist/commands/deploy-list.js +153 -0
  26. package/dist/commands/lanes.js +41 -2
  27. package/dist/fast-scaffold.js +48 -0
  28. package/dist/lib/core-utils/session-company.d.ts +3 -0
  29. package/dist/lib/core-utils/session-company.js +78 -0
  30. package/package.json +1 -1
@@ -0,0 +1,11 @@
1
+ export declare const KNOWLEDGE_PULSE_LOCK_ASSET = "core/scripts/knowledge-pulse-lock.sh";
2
+ export declare const SKILL_INSTALLED_ASSET = "core/scripts/skill-installed.sh";
3
+ export type BucketCSmallResult = {
4
+ code: number;
5
+ stdout: string;
6
+ stderr: string;
7
+ };
8
+ export declare function mkdirFailure(error: unknown, target: string, platform?: NodeJS.Platform): string;
9
+ export declare function runKnowledgePulseLock(args: readonly string[]): BucketCSmallResult;
10
+ export declare function runSkillInstalled(args: readonly string[], root: string, env?: NodeJS.ProcessEnv): Promise<BucketCSmallResult>;
11
+ //# sourceMappingURL=core-bucket-c-small-native.d.ts.map
@@ -0,0 +1,197 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ export const KNOWLEDGE_PULSE_LOCK_ASSET = "core/scripts/knowledge-pulse-lock.sh";
4
+ export const SKILL_INSTALLED_ASSET = "core/scripts/skill-installed.sh";
5
+ const failure = (stderr, code = 1) => ({ code, stdout: "", stderr: `${stderr}\n` });
6
+ export function mkdirFailure(error, target, platform = process.platform) {
7
+ const fsError = error;
8
+ // Node's Windows fs wrapper may report this ENOTDIR as code UNKNOWN, errno -20.
9
+ let code = platform === "win32" && fsError.errno === -20 ? "ENOTDIR" : fsError.code;
10
+ const absolute = path.resolve(target);
11
+ const parsed = path.parse(absolute);
12
+ const parts = absolute.slice(parsed.root.length).split(path.sep).filter(Boolean);
13
+ if (platform === "win32" && parts.some((part) => part.length > 255))
14
+ code = "ENAMETOOLONG";
15
+ const reason = {
16
+ EACCES: "Permission denied",
17
+ EEXIST: "File exists",
18
+ ELOOP: "Too many levels of symbolic links",
19
+ EMFILE: "Too many open files",
20
+ ENAMETOOLONG: "File name too long",
21
+ ENFILE: "Too many open files in system",
22
+ ENOENT: "No such file or directory",
23
+ ENOMEM: "Cannot allocate memory",
24
+ ENOSPC: "No space left on device",
25
+ ENOTDIR: "Not a directory",
26
+ EPERM: "Operation not permitted",
27
+ EROFS: "Read-only file system",
28
+ }[code ?? ""] ?? "Input/output error";
29
+ let failingPath = target;
30
+ if (code === "ENOTDIR" || code === "ELOOP" || code === "ENAMETOOLONG") {
31
+ let current = parsed.root;
32
+ for (const part of parts) {
33
+ current = path.join(current, part);
34
+ if (platform === "win32" && part.length > 255) {
35
+ failingPath = current;
36
+ break;
37
+ }
38
+ try {
39
+ if (!fs.statSync(current).isDirectory()) {
40
+ failingPath = current;
41
+ break;
42
+ }
43
+ }
44
+ catch (error) {
45
+ if (error.code === code)
46
+ failingPath = current;
47
+ break;
48
+ }
49
+ }
50
+ }
51
+ if (platform === "darwin")
52
+ return `mkdir: ${failingPath}: ${reason}`;
53
+ if (platform === "win32") {
54
+ const shellPathParts = path.parse(failingPath);
55
+ // MSYS converts the incoming drive path for mkdir, retaining Node's root slash and adding its own.
56
+ const driveRoot = shellPathParts.root.endsWith("\\") ? `${shellPathParts.root}\\` : shellPathParts.root;
57
+ const shellPath = `${driveRoot}${failingPath.slice(shellPathParts.root.length).replaceAll("\\", "/")}`;
58
+ return `mkdir: cannot create directory '${shellPath}': ${reason}`;
59
+ }
60
+ return `mkdir: cannot create directory ‘${failingPath}’: ${reason}`;
61
+ }
62
+ export function runKnowledgePulseLock(args) {
63
+ if (args.length !== 4 || args[0] !== "claim") {
64
+ return failure("Usage: knowledge-pulse-lock.sh claim <claim-root> <company-slug> <YYYY-MM-DD>", 2);
65
+ }
66
+ const [_, claimRoot, company, date] = args;
67
+ if (!claimRoot || !/^[a-z0-9][a-z0-9-]*$/.test(company) || !/^\d{4}-\d{2}-\d{2}$/.test(date)) {
68
+ return failure("knowledge-pulse-lock: invalid claim root, company slug, or date", 2);
69
+ }
70
+ try {
71
+ fs.mkdirSync(claimRoot, { recursive: true });
72
+ }
73
+ catch (error) {
74
+ return failure(mkdirFailure(error, claimRoot));
75
+ }
76
+ const marker = path.join(claimRoot, `${company}-${date}.claimed`);
77
+ try {
78
+ fs.mkdirSync(marker);
79
+ return { code: 0, stdout: "claimed\n", stderr: "" };
80
+ }
81
+ catch (error) {
82
+ if (error.code === "EEXIST") {
83
+ try {
84
+ if (fs.statSync(marker).isDirectory())
85
+ return { code: 0, stdout: "already-claimed\n", stderr: "" };
86
+ }
87
+ catch { /* fall through to the shell-compatible failure */ }
88
+ }
89
+ return failure("knowledge-pulse-lock: could not create claim marker");
90
+ }
91
+ }
92
+ function skillName(file) {
93
+ try {
94
+ if (!fs.statSync(file).isFile())
95
+ return undefined;
96
+ const contents = fs.readFileSync(file, "utf8");
97
+ const lines = (process.platform === "win32" ? contents.replaceAll("\r\n", "\n") : contents).split("\n");
98
+ let inFrontmatter = false;
99
+ let name;
100
+ for (const line of lines) {
101
+ if (/^---[ \t]*$/.test(line)) {
102
+ if (inFrontmatter)
103
+ return name;
104
+ inFrontmatter = true;
105
+ continue;
106
+ }
107
+ if (!inFrontmatter || !/^name:[ \t]*/.test(line))
108
+ continue;
109
+ name = line.replace(/^name:[ \t]*/, "").replace(/^["']|["']$/g, "");
110
+ }
111
+ }
112
+ catch { /* unreadable skill files are skipped by the shell catalog */ }
113
+ return undefined;
114
+ }
115
+ function skillFiles(root, companies) {
116
+ const files = [];
117
+ const appendDirectories = (base, skipHidden) => {
118
+ let names;
119
+ try {
120
+ names = fs.readdirSync(base).sort((a, b) => a.localeCompare(b, "en"));
121
+ }
122
+ catch {
123
+ return;
124
+ }
125
+ for (const name of names) {
126
+ if (name.startsWith(".") || (skipHidden && (name.startsWith("_") || name.startsWith("README"))))
127
+ continue;
128
+ const dir = path.join(base, name);
129
+ try {
130
+ if (!fs.statSync(dir).isDirectory())
131
+ continue;
132
+ const file = path.join(dir, "SKILL.md");
133
+ if (fs.statSync(file).isFile())
134
+ files.push(file);
135
+ }
136
+ catch { /* non-directories and missing/unreadable files are skipped */ }
137
+ }
138
+ };
139
+ for (const company of companies)
140
+ appendDirectories(path.join(root, "companies", company, "skills"), false);
141
+ appendDirectories(path.join(root, ".claude", "skills"), true);
142
+ let packages = [];
143
+ try {
144
+ packages = fs.readdirSync(path.join(root, "core", "packages")).sort((a, b) => a.localeCompare(b, "en"));
145
+ }
146
+ catch {
147
+ return files;
148
+ }
149
+ for (const pkg of packages)
150
+ appendDirectories(path.join(root, "core", "packages", pkg, "skills"), false);
151
+ return files;
152
+ }
153
+ export async function runSkillInstalled(args, root, env = process.env) {
154
+ const skill = args[0] ?? "";
155
+ let company = args[1] ?? env.HQ_ACTIVE_COMPANY ?? "";
156
+ if (!company) {
157
+ const [{ sessionCompanySlugs }, { fastFlagEnabledDefaultOn }, { MULTI_COMPANY_SESSION_LOCK_FLAG }] = await Promise.all([
158
+ import("../lib/core-utils/session-company.js"),
159
+ import("../native-fast-route.js"),
160
+ import("../lib/cli-flag-keys.js"),
161
+ ]);
162
+ const multiCompanyLockEnabled = await fastFlagEnabledDefaultOn(MULTI_COMPANY_SESSION_LOCK_FLAG, env);
163
+ company = sessionCompanySlugs(root, env, multiCompanyLockEnabled).join(",");
164
+ }
165
+ if (!/^[a-z0-9][a-z0-9_-]*$/.test(skill))
166
+ return { code: 1, stdout: "", stderr: "" };
167
+ if (!company)
168
+ company = "__hq_unbound_session__";
169
+ const companies = company.split(",");
170
+ if (companies.at(-1) === "")
171
+ companies.pop();
172
+ if (companies.some((slug) => !/^[a-z][a-z0-9_-]*$/.test(slug)))
173
+ return { code: 1, stdout: "", stderr: "" };
174
+ const resolver = path.join(root, "core", "scripts", "lib", "session-skill-catalog.sh");
175
+ try {
176
+ fs.accessSync(resolver, fs.constants.R_OK);
177
+ }
178
+ catch {
179
+ return failure(`skill availability resolver is missing: ${resolver}`, 2);
180
+ }
181
+ const seen = new Set();
182
+ for (const file of skillFiles(root, companies)) {
183
+ const name = skillName(file);
184
+ if (!name || seen.has(name))
185
+ continue;
186
+ seen.add(name);
187
+ if (name === skill) {
188
+ try {
189
+ if (fs.statSync(file).isFile())
190
+ return { code: 0, stdout: "", stderr: "" };
191
+ }
192
+ catch { /* catalog entry disappeared, continue */ }
193
+ }
194
+ }
195
+ return { code: 1, stdout: "", stderr: "" };
196
+ }
197
+ //# sourceMappingURL=core-bucket-c-small-native.js.map
@@ -591,7 +591,10 @@ export function runDeriveTriggerFacts(options) {
591
591
  }
592
592
  }
593
593
  export function runDeriveTriggerFactsFast(argv, stdin, cwd, hqRoot) {
594
- const result = runDeriveTriggerFactsFastResult(argv, stdin, cwd, hqRoot);
594
+ // The shell fast route runs from the live root and injects it as
595
+ // CLAUDE_PROJECT_DIR. Keep the native handler's PWD fallback aligned with
596
+ // that route when the caller supplied --hq-root.
597
+ const result = runDeriveTriggerFactsFastResult(argv, stdin, hqRoot ?? cwd, hqRoot);
595
598
  if (result.stdout.length)
596
599
  process.stdout.write(result.stdout);
597
600
  if (result.stderr.length)
@@ -218,12 +218,36 @@ export const REBUILD_INDEX_TARGETS = [
218
218
  * would make the forwarders harder to audit against this table.
219
219
  */
220
220
  export const SCAFFOLD_COMMANDS = [
221
+ {
222
+ name: "handoff-sync-prefetch",
223
+ asset: "core/scripts/handoff-sync-prefetch.sh",
224
+ root: "live",
225
+ summary: "Pull the latest continuity pointer before a session resumes",
226
+ },
227
+ {
228
+ name: "handoff-sync-publish",
229
+ asset: "core/scripts/handoff-sync-publish.sh",
230
+ root: "live",
231
+ summary: "Publish the session continuity pointer to the personal vault",
232
+ },
221
233
  {
222
234
  name: "hq-task",
223
235
  asset: "core/scripts/hq-task.sh",
224
236
  root: "live",
225
237
  summary: "List and update tasks on personal or company boards",
226
238
  },
239
+ {
240
+ name: "knowledge-pulse-lock",
241
+ asset: "core/scripts/knowledge-pulse-lock.sh",
242
+ root: "live",
243
+ summary: "Atomically claim a company knowledge-pulse run",
244
+ },
245
+ {
246
+ name: "skill-installed",
247
+ asset: "core/scripts/skill-installed.sh",
248
+ root: "live",
249
+ summary: "Check whether a company-scoped skill is installed",
250
+ },
227
251
  // Session/harness plumbing relocated out of the HQ tree so a single published
228
252
  // CLI owns it. Both are `root: "live"`: they read and write the live HQ tree
229
253
  // (workspace/sessions, companies/, workspace/orchestrator/hook-state), so the
@@ -250,6 +274,7 @@ export const SCAFFOLD_COMMANDS = [
250
274
  */
251
275
  export const SCAFFOLD_ONLY_ASSETS = [
252
276
  { asset: "core/scripts/hq-task.mjs", root: "live", summary: "Sibling implementation used by the hq-task shell rollback companion" },
277
+ { asset: "core/scripts/lib/session-skill-catalog.sh", root: "live", summary: "Shared skill catalog helper used by bundled skill-installed" },
253
278
  { asset: "core/scripts/session-journal.sh", root: "live", summary: "Rollback companion for native session journal" },
254
279
  { asset: "core/scripts/session-project.sh", root: "live", summary: "Rollback companion for native session project" },
255
280
  { asset: "core/scripts/session-title.sh", root: "live", summary: "Rollback companion for native session title" },
@@ -1,3 +1,5 @@
1
1
  import { Command } from "commander";
2
+ export declare function displayText(value: unknown): string;
3
+ export declare function safeJson(value: object): string;
2
4
  export declare function registerDeployCommentsCommand(deploy: Command): void;
3
5
  //# sourceMappingURL=deploy-comments.d.ts.map
@@ -74,7 +74,7 @@ async function listComments(token, appId, orgHeaders) {
74
74
  }
75
75
  return { commentsEnabled: result.commentsEnabled, comments: result.comments };
76
76
  }
77
- function displayText(value) {
77
+ export function displayText(value) {
78
78
  return [...String(value ?? "")].map((character) => {
79
79
  const code = character.codePointAt(0) ?? 0;
80
80
  return isUnsafeTerminalCodePoint(code)
@@ -88,7 +88,7 @@ function isUnsafeTerminalCodePoint(code) {
88
88
  || (code >= 0x202a && code <= 0x202e)
89
89
  || (code >= 0x2066 && code <= 0x2069);
90
90
  }
91
- function safeJson(value) {
91
+ export function safeJson(value) {
92
92
  return JSON.stringify(value, null, 2).replace(/[\u007f-\u009f\u202a-\u202e\u2066-\u2069]/gu, (character) => `\\u${(character.codePointAt(0) ?? 0).toString(16).padStart(4, "0")}`);
93
93
  }
94
94
  function printCommentLine(comment) {
@@ -0,0 +1,22 @@
1
+ import { Command } from "commander";
2
+ type ApiApp = Record<string, unknown>;
3
+ export interface DeployAppSummary {
4
+ id: string | null;
5
+ name: string | null;
6
+ title: string | null;
7
+ subdomain: string | null;
8
+ url: string | null;
9
+ type: string | null;
10
+ accessMode: string | null;
11
+ status: string | null;
12
+ createdAt: string | null;
13
+ updatedAt: string | null;
14
+ lastVisitAt: string | null;
15
+ }
16
+ /** Public app base domain, derived from the deploy API host (api.<base>). */
17
+ export declare function appBaseDomain(apiUrl?: string): string | null;
18
+ export declare function summarizeApp(app: ApiApp): DeployAppSummary;
19
+ export declare function matchesQuery(app: DeployAppSummary, query: string | undefined): boolean;
20
+ export declare function registerDeployListCommand(deploy: Command): void;
21
+ export {};
22
+ //# sourceMappingURL=deploy-list.d.ts.map
@@ -0,0 +1,153 @@
1
+ import { DEPLOY_API_URL, deployApiRequest } from "../lib/deploy-api.js";
2
+ import { listDeployApps } from "../lib/deploy-apps.js";
3
+ import { DEFAULT_HQ_ROOT, ensureCognitoToken } from "../utils/cognito-session.js";
4
+ import { displayText, safeJson } from "./deploy-comments.js";
5
+ import { readActiveCompanySlug } from "./skill.js";
6
+ class DeployListError extends Error {
7
+ }
8
+ const SLUG_PATTERN = /^[a-z0-9][a-z0-9-]{0,62}$/;
9
+ function resolveCompany(companyFlag) {
10
+ const slug = companyFlag ?? readActiveCompanySlug(DEFAULT_HQ_ROOT);
11
+ if (slug === undefined) {
12
+ throw new DeployListError("No active company. Pass --company <slug>.");
13
+ }
14
+ if (!SLUG_PATTERN.test(slug))
15
+ throw new DeployListError("The company slug is not valid.");
16
+ return slug;
17
+ }
18
+ function str(value) {
19
+ return typeof value === "string" && value.length > 0 ? value : null;
20
+ }
21
+ /** Public app base domain, derived from the deploy API host (api.<base>). */
22
+ export function appBaseDomain(apiUrl = DEPLOY_API_URL) {
23
+ try {
24
+ const host = new URL(apiUrl).hostname;
25
+ return host.startsWith("api.") ? host.slice(4) : null;
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ function appUrl(app, subdomain) {
32
+ const url = str(app.url);
33
+ if (url)
34
+ return url;
35
+ const base = appBaseDomain();
36
+ return subdomain && base ? `https://${subdomain}.${base}` : null;
37
+ }
38
+ export function summarizeApp(app) {
39
+ const subdomain = str(app.subdomain);
40
+ return {
41
+ id: str(app.id),
42
+ name: str(app.name),
43
+ title: str(app.title),
44
+ subdomain,
45
+ url: appUrl(app, subdomain),
46
+ type: str(app.type),
47
+ accessMode: str(app.accessMode),
48
+ status: str(app.status),
49
+ createdAt: str(app.createdAt),
50
+ updatedAt: str(app.updatedAt),
51
+ lastVisitAt: str(app.lastVisitAt),
52
+ };
53
+ }
54
+ export function matchesQuery(app, query) {
55
+ if (!query)
56
+ return true;
57
+ const needle = query.toLowerCase();
58
+ return [app.name, app.subdomain, app.title].some((field) => field?.toLowerCase().includes(needle) ?? false);
59
+ }
60
+ async function responseError(response, slug) {
61
+ const body = await response.json().catch(() => ({}));
62
+ const raw = body.error?.code;
63
+ const code = typeof raw === "string" && /^[A-Z0-9_]+$/.test(raw) ? raw : `HTTP_${response.status}`;
64
+ if (response.status === 401) {
65
+ return new DeployListError(`hq-deploy rejected your sign-in. Run 'hq login' and retry. (${code})`);
66
+ }
67
+ if (response.status === 403) {
68
+ return new DeployListError(`Company '${slug}' was not found in hq-deploy, or you do not have access to it. (${code})`);
69
+ }
70
+ if (response.status === 404) {
71
+ return new DeployListError(`Company '${slug}' was not found in hq-deploy. (${code})`);
72
+ }
73
+ return new DeployListError(`Could not list deploy apps (HTTP ${response.status}, ${code}).`);
74
+ }
75
+ async function fetchPage(token, slug, cursor) {
76
+ const route = cursor ? `/api/apps?cursor=${encodeURIComponent(cursor)}` : "/api/apps";
77
+ let response;
78
+ try {
79
+ response = await deployApiRequest(token, route, "GET", undefined, { "X-Org-Slug": slug });
80
+ }
81
+ catch {
82
+ throw new DeployListError("Could not reach hq-deploy.");
83
+ }
84
+ if (!response.ok)
85
+ throw await responseError(response, slug);
86
+ try {
87
+ return await response.json();
88
+ }
89
+ catch {
90
+ throw new DeployListError("hq-deploy returned an invalid response.");
91
+ }
92
+ }
93
+ function printApp(app) {
94
+ const updated = app.updatedAt ?? app.lastVisitAt ?? app.createdAt;
95
+ console.log([
96
+ displayText(app.name ?? app.subdomain ?? app.id ?? "unnamed"),
97
+ displayText(app.url ?? "-"),
98
+ displayText(app.type ?? "-"),
99
+ displayText(app.accessMode ?? "-"),
100
+ displayText(updated ?? "-"),
101
+ ].join(" | "));
102
+ }
103
+ async function listAction(query, options) {
104
+ const slug = resolveCompany(options.company);
105
+ const token = await ensureCognitoToken();
106
+ let raw;
107
+ try {
108
+ raw = await listDeployApps((cursor) => fetchPage(token, slug, cursor));
109
+ }
110
+ catch (error) {
111
+ if (error instanceof DeployListError)
112
+ throw error;
113
+ throw new DeployListError(error instanceof Error ? error.message : "Could not list deploy apps.");
114
+ }
115
+ const apps = raw
116
+ .filter((app) => typeof app === "object" && app !== null)
117
+ .map(summarizeApp)
118
+ .filter((app) => matchesQuery(app, query));
119
+ if (options.json) {
120
+ console.log(safeJson({ company: slug, query: query ?? null, apps }));
121
+ return;
122
+ }
123
+ if (apps.length === 0) {
124
+ if (query) {
125
+ console.log(`No deploy apps in '${slug}' matched '${displayText(query)}'.`);
126
+ }
127
+ else {
128
+ console.log(`hq-deploy returned no apps for '${slug}'.`);
129
+ console.log("Hint: this list can come back empty while the org still has apps. "
130
+ + "Check the HQ console before concluding none exist.");
131
+ }
132
+ return;
133
+ }
134
+ for (const app of apps)
135
+ printApp(app);
136
+ }
137
+ export function registerDeployListCommand(deploy) {
138
+ deploy
139
+ .command("list [query]")
140
+ .description("List deploy apps in your company, optionally filtered by name or subdomain")
141
+ .option("--company <slug>", "use this company instead of the active company")
142
+ .option("--json", "print the matching apps as JSON")
143
+ .action(async (query, options) => {
144
+ try {
145
+ await listAction(query, options);
146
+ }
147
+ catch (error) {
148
+ console.error(error instanceof DeployListError ? error.message : "Deploy list command failed.");
149
+ process.exitCode = 1;
150
+ }
151
+ });
152
+ }
153
+ //# sourceMappingURL=deploy-list.js.map
@@ -38,6 +38,7 @@ import { registerLanesLinkCommand } from "./lanes-link.js";
38
38
  import { laneDepthCap } from "../lib/lanes/depth-cap.js";
39
39
  import { isSsmRunCommandProcess } from "../lib/lanes/ssm-command-context.js";
40
40
  import { runResidentCommandWithPackageLease } from "../utils/resident-package-preload.js";
41
+ import { codexEnvEvidence, grokEnvEvidence } from "../lib/doctor/platform.js";
41
42
  import { readLanesFlagLocalOverride, readLanesRegistryFlag, readLanesRegistryFlagFailOff, readLanesRegistryFlagFailOpen } from "../lib/lanes/registry-flag.js";
42
43
  import { readLaneStatusRowFields } from "../lib/lanes/status-fields.js";
43
44
  import { appendProjectFocusOverride, readProjectStoryFocusData, removeProjectFocusOverride } from "../lib/lanes/story-ledger.js";
@@ -74,7 +75,7 @@ import { sessionIdFromEnv } from "../lib/mesh/live/session-identity.js";
74
75
  import { listLaneReviews } from "../lib/lanes/reviews.js";
75
76
  import { effectiveAccountHome } from "../lib/lanes/account-home.js";
76
77
  import { remoteControlSelectionConfig, remoteControlSessionHome } from "../lib/lanes/remote-control-lane.js";
77
- import { AccountResolutionError, compareLaneQueueOrder, LANE_PROVIDERS, LANE_STATES, QUESTION_STATUSES, SENIOR_KINDS, accountStatus, appendCappedHistory, canonicalizeHome, createLane, decideAdmission, defaultLivenessDeps, defaultSystemdUserDir, defaultThrottleDeps, installReconcileTimer, providerPathEnv, probeDrainHookRegistration, isLanesError, isLaneVersionConflict, isTerminalLaneState, isFinishedResumableHireChild, childrenHoldingHireGrantCarve, laneHoldsHireGrantCarve, assignedChildHireGrants, hireGrantCapacityError, planHireGrantBackfill, terminalChildResumeHireGrantError, withHireGrantAdmission, laneStoryLedgerEnabled, syncLaneToProjectStoryLedger, removeLaneFromProjectStoryLedger, validateProjectStories, buildLaneStatusSnapshot, formatLaneStatusHuman, listLaneIds, listQuestionIds, loadLanesConfig, laneRunsHere, MAX_LANE_DEPTH, MONITOR_ERROR_EXIT_CODE, readLiveMonitorEvidence, resolveMachineIdentity as lookupMachineIdentity, loadLivenessIndex, parseLaneProvider, parseLaneRecord, parseLaneState, parseQuestionRecord, parseSeniorKind, readAllLanes, readLane, sanitizeHostComponent, retryUpdateLane, syncLaneRecordFromOwner, readQuestion, resolveAccount, resolveLaneWorker, ignoredWorkerProfileFields, loadWorkerProfile, loadWorkerProfileWithRegistry, registryWorkerRecord, runReconcilePass, DEAD_HOST_MS, sessionLiveness, tryReadLane, uninstallReconcileTimer, updateLane, updateQuestion, dropboxDir, laneDeliveryMechanism, hasActiveAdmissionReservation, hasActivePendingResume, hasRecordedSessionId, hasEarlierEligibleQueuedLane, laneAdmissionCapReason, providerSupportsSessionResume, providerSessionPortable, isFailedQuotaKeepAliveLane, sessionNotPortableMessage, withLaneAdmissionLock, withAllLaneAdmissionLocks, readWorkerRunResult, resumeCommandRefusal, withLaneHireLocks, raiseLaneQuestion, sha256Text, } from "../lib/lanes/index.js";
78
+ import { AccountResolutionError, compareLaneQueueOrder, LANE_PROVIDERS, LANE_STATES, QUESTION_STATUSES, SENIOR_KINDS, accountStatus, appendCappedHistory, canonicalizeHome, createLane, decideAdmission, defaultLivenessDeps, defaultSystemdUserDir, defaultThrottleDeps, installReconcileTimer, providerPathEnv, probeDrainHookRegistration, isLanesError, isLaneVersionConflict, isTerminalLaneState, isFinishedResumableHireChild, childrenHoldingHireGrantCarve, laneHoldsHireGrantCarve, assignedChildHireGrants, hireGrantCapacityError, planHireGrantBackfill, terminalChildResumeHireGrantError, withHireGrantAdmission, laneStoryLedgerEnabled, syncLaneToProjectStoryLedger, removeLaneFromProjectStoryLedger, validateProjectStories, buildLaneStatusSnapshot, formatLaneStatusHuman, listLaneIds, listQuestionIds, loadLanesConfig, laneRunsHere, MAX_LANE_DEPTH, MONITOR_ERROR_EXIT_CODE, readLiveMonitorEvidence, resolveMachineIdentity as lookupMachineIdentity, loadLivenessIndex, parseLaneProvider, isLaneProvider, parseLaneRecord, parseLaneState, parseQuestionRecord, parseSeniorKind, readAllLanes, readLane, sanitizeHostComponent, retryUpdateLane, syncLaneRecordFromOwner, readQuestion, resolveAccount, resolveLaneWorker, ignoredWorkerProfileFields, loadWorkerProfile, loadWorkerProfileWithRegistry, registryWorkerRecord, runReconcilePass, DEAD_HOST_MS, sessionLiveness, tryReadLane, uninstallReconcileTimer, updateLane, updateQuestion, dropboxDir, laneDeliveryMechanism, hasActiveAdmissionReservation, hasActivePendingResume, hasRecordedSessionId, hasEarlierEligibleQueuedLane, laneAdmissionCapReason, providerSupportsSessionResume, providerSessionPortable, isFailedQuotaKeepAliveLane, sessionNotPortableMessage, withLaneAdmissionLock, withAllLaneAdmissionLocks, readWorkerRunResult, resumeCommandRefusal, withLaneHireLocks, raiseLaneQuestion, sha256Text, } from "../lib/lanes/index.js";
78
79
  import { laneStories, storyWarning, } from "../lib/lanes/stories.js";
79
80
  import { resolveLaneKeepAlive } from "../lib/lanes/profile.js";
80
81
  import { laneCompanyUidSource, resolveLaneCompany } from "../lib/lanes/lane-company.js";
@@ -2295,6 +2296,41 @@ function callerDepth(hqRoot, ctx) {
2295
2296
  }
2296
2297
  return 0;
2297
2298
  }
2299
+ /**
2300
+ * Engine of the orchestrator that is creating a lane, used as the provider
2301
+ * fallback when neither --provider nor the worker profile names one. Order:
2302
+ * the senior lane's provider, the senior session's `conduct_engine` metadata,
2303
+ * then the harness markers in the environment (`HQ_HARNESS`, Codex/Grok
2304
+ * runtime variables, `CLAUDECODE=1`). Returns undefined when none apply.
2305
+ */
2306
+ function orchestratorEngine(hqRoot, senior, ctx) {
2307
+ if (senior.kind === "lane" && senior.id) {
2308
+ try {
2309
+ const provider = readLane(hqRoot, senior.id).provider;
2310
+ if (isLaneProvider(provider))
2311
+ return provider;
2312
+ }
2313
+ catch {
2314
+ // Fall through to the environment markers.
2315
+ }
2316
+ }
2317
+ if (senior.kind === "session" && senior.id) {
2318
+ const engine = readSessionMeta(hqRoot, senior.id, ctx)?.conduct_engine;
2319
+ const value = typeof engine === "string" ? engine.trim().toLowerCase() : "";
2320
+ if (isLaneProvider(value))
2321
+ return value;
2322
+ }
2323
+ const harness = ctx.env.HQ_HARNESS?.trim().toLowerCase() ?? "";
2324
+ if (isLaneProvider(harness))
2325
+ return harness;
2326
+ if (codexEnvEvidence(ctx.env).length > 0)
2327
+ return "codex";
2328
+ if (grokEnvEvidence(ctx.env).length > 0)
2329
+ return "grok";
2330
+ if (ctx.env.CLAUDECODE?.trim() === "1")
2331
+ return "claude";
2332
+ return undefined;
2333
+ }
2298
2334
  function defaultSenior(ctx) {
2299
2335
  const parent = ctx.env.HQ_PARENT_SESSION_ID?.trim();
2300
2336
  if (parent)
@@ -3450,7 +3486,10 @@ async function runCreate(ctx, opts) {
3450
3486
  const profile = resolvedWorker?.profile;
3451
3487
  const provider = explicitProvider ??
3452
3488
  profile?.meta.provider ??
3453
- parseLaneProvider(opts.remoteControl === true ? "claude" : "codex");
3489
+ (opts.remoteControl === true ? parseLaneProvider("claude") : orchestratorEngine(hqRoot, senior, ctx));
3490
+ if (provider === undefined) {
3491
+ throw new LanesError("provider_required", "Cannot tell which engine runs the orchestrator session, so no lane provider was chosen. Pass --provider claude, --provider codex, or --provider grok.", { accepted: [...LANE_PROVIDERS] });
3492
+ }
3454
3493
  if (opts.remoteControl === true && provider !== "claude") {
3455
3494
  throw new LanesError("remote_control_requires_claude", "--remote-control requires a Claude lane", { provider });
3456
3495
  }
@@ -160,6 +160,54 @@ export function isDefaultOnFastFamily(flagKey) {
160
160
  }
161
161
  /** The relocated scripts that qualify for the entrypoint fast path. */
162
162
  export const FAST_SCAFFOLD_SCRIPTS = [
163
+ {
164
+ name: "handoff-sync-prefetch",
165
+ kind: "native",
166
+ modulePath: "./commands/core-bucket-c-handoff-sync-fast.js",
167
+ exportName: "runHandoffSyncPrefetchFast",
168
+ flagKey: "hq-core-native-fast-route",
169
+ familyFlagKey: "core-native-bucket-c",
170
+ asset: "core/scripts/handoff-sync-prefetch.sh",
171
+ root: "live",
172
+ shellCwd: "live",
173
+ shellHqRoot: "live",
174
+ },
175
+ {
176
+ name: "knowledge-pulse-lock",
177
+ kind: "native",
178
+ modulePath: "./commands/core-bucket-c-small-fast.js",
179
+ exportName: "runKnowledgePulseLockFast",
180
+ flagKey: "hq-core-native-fast-route",
181
+ familyFlagKey: "core-native-bucket-c",
182
+ asset: "core/scripts/knowledge-pulse-lock.sh",
183
+ root: "live",
184
+ shellCwd: "live",
185
+ shellHqRoot: "live",
186
+ },
187
+ {
188
+ name: "handoff-sync-publish",
189
+ kind: "native",
190
+ modulePath: "./commands/core-bucket-c-handoff-sync-fast.js",
191
+ exportName: "runHandoffSyncPublishFast",
192
+ flagKey: "hq-core-native-fast-route",
193
+ familyFlagKey: "core-native-bucket-c",
194
+ asset: "core/scripts/handoff-sync-publish.sh",
195
+ root: "live",
196
+ shellCwd: "live",
197
+ shellHqRoot: "live",
198
+ },
199
+ {
200
+ name: "skill-installed",
201
+ kind: "native",
202
+ modulePath: "./commands/core-bucket-c-small-fast.js",
203
+ exportName: "runSkillInstalledFast",
204
+ flagKey: "hq-core-native-fast-route",
205
+ familyFlagKey: "core-native-bucket-c",
206
+ asset: "core/scripts/skill-installed.sh",
207
+ root: "live",
208
+ shellCwd: "live",
209
+ shellHqRoot: "live",
210
+ },
163
211
  {
164
212
  name: "sentry report",
165
213
  kind: "native",
@@ -0,0 +1,3 @@
1
+ /** Reads the active session's company lock without importing the full hq-session command. */
2
+ export declare function sessionCompanySlugs(root: string, env?: NodeJS.ProcessEnv, multiCompanyLockEnabled?: boolean): string[];
3
+ //# sourceMappingURL=session-company.d.ts.map