@indigoai-us/hq-cli 5.345.122 → 5.345.124

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 (53) hide show
  1. package/CHANGELOG.md +25 -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/assets/scaffold/core/scripts/work-mesh-project-registration-offer.sh +77 -0
  9. package/assets/scaffold/core/scripts/workflow-gate.sh +247 -0
  10. package/dist/command-catalog.generated.d.ts +332 -0
  11. package/dist/command-catalog.generated.js +402 -0
  12. package/dist/command-registration-plan.d.ts +6 -0
  13. package/dist/command-registration-plan.js +1 -0
  14. package/dist/commands/core-bucket-c-handoff-sync-fast.d.ts +3 -0
  15. package/dist/commands/core-bucket-c-handoff-sync-fast.js +16 -0
  16. package/dist/commands/core-bucket-c-handoff-sync-native.d.ts +45 -0
  17. package/dist/commands/core-bucket-c-handoff-sync-native.js +300 -0
  18. package/dist/commands/core-bucket-c-small-fast.d.ts +5 -0
  19. package/dist/commands/core-bucket-c-small-fast.js +19 -0
  20. package/dist/commands/core-bucket-c-small-native.d.ts +11 -0
  21. package/dist/commands/core-bucket-c-small-native.js +197 -0
  22. package/dist/commands/core-bucket-c-work-mesh-offer-native.d.ts +7 -0
  23. package/dist/commands/core-bucket-c-work-mesh-offer-native.js +147 -0
  24. package/dist/commands/core-bucket-c-workflow-gate-fast.d.ts +3 -0
  25. package/dist/commands/core-bucket-c-workflow-gate-fast.js +17 -0
  26. package/dist/commands/core-bucket-c-workflow-gate-native.d.ts +7 -0
  27. package/dist/commands/core-bucket-c-workflow-gate-native.js +250 -0
  28. package/dist/commands/core-derive-trigger-facts-native.js +4 -1
  29. package/dist/commands/core.js +37 -0
  30. package/dist/commands/deploy-comments.d.ts +2 -0
  31. package/dist/commands/deploy-comments.js +2 -2
  32. package/dist/commands/deploy-list.d.ts +22 -0
  33. package/dist/commands/deploy-list.js +153 -0
  34. package/dist/commands/lanes.js +41 -2
  35. package/dist/commands/mesh.js +457 -16
  36. package/dist/fast-scaffold.js +74 -0
  37. package/dist/lib/core-utils/session-company.d.ts +3 -0
  38. package/dist/lib/core-utils/session-company.js +78 -0
  39. package/dist/lib/lanes/quota-notice.d.ts +24 -0
  40. package/dist/lib/lanes/quota-notice.js +61 -0
  41. package/dist/lib/lanes/quota.d.ts +44 -8
  42. package/dist/lib/lanes/quota.js +187 -45
  43. package/dist/lib/lanes/reconcile.js +17 -1
  44. package/dist/lib/lanes/watcher.js +13 -1
  45. package/dist/lib/mesh/api.d.ts +2 -0
  46. package/dist/lib/mesh/api.js +3 -0
  47. package/dist/lib/mesh/client.d.ts +6 -1
  48. package/dist/lib/mesh/client.js +18 -5
  49. package/dist/lib/mesh/team-coordination.d.ts +106 -0
  50. package/dist/lib/mesh/team-coordination.js +297 -0
  51. package/dist/lib/work-context/auto-bind.d.ts +75 -0
  52. package/dist/lib/work-context/auto-bind.js +245 -0
  53. package/package.json +1 -1
@@ -0,0 +1,147 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { loadCachedTokens } from "@indigoai-us/hq-cloud";
4
+ import { createFlagClient } from "@indigoai-us/hq-flags-client";
5
+ import { resolveFlagRegistryEndpoint } from "../lib/flag-registry-endpoint.js";
6
+ const success = (stdout) => ({ code: 0, stdout, stderr: "" });
7
+ const failure = (stderr) => ({ code: 1, stdout: "", stderr: `${stderr}\n` });
8
+ const COMPANY = /^[A-Za-z0-9_-]+$/;
9
+ const PROJECT = /^[A-Za-z0-9._-]+$/;
10
+ const COMPANY_UID = /^cmp_[A-Za-z0-9]{3,128}$/;
11
+ const OFFER_FLAG = "workmesh.offer-project-create-on-brainstorm";
12
+ function cloudUid(root, company) {
13
+ const file = path.join(root, "companies/manifest.yaml");
14
+ let text;
15
+ try {
16
+ text = fs.readFileSync(file, "utf8");
17
+ }
18
+ catch {
19
+ return "";
20
+ }
21
+ const lines = text.split(/\r?\n/);
22
+ let inCompanies = false;
23
+ let inCompany = false;
24
+ for (const line of lines) {
25
+ if (/^companies:\s*$/.test(line)) {
26
+ inCompanies = true;
27
+ continue;
28
+ }
29
+ if (inCompanies && /^[^\s]/.test(line)) {
30
+ inCompanies = false;
31
+ inCompany = false;
32
+ }
33
+ if (inCompanies && /^ {2}[^ #][^:]*:/.test(line)) {
34
+ const slug = line.trim().replace(/:$/, "");
35
+ inCompany = slug === company;
36
+ continue;
37
+ }
38
+ if (inCompany && /^ {2}[^\s#]/.test(line))
39
+ break;
40
+ if (inCompany && /^\s+cloud_uid:/.test(line))
41
+ return (line.split(":")[1] ?? "").trim().replace(/["']/g, "");
42
+ }
43
+ return "";
44
+ }
45
+ function boardFile(root, company) { return path.join(root, "companies", company, "board.json"); }
46
+ function matchingProjects(board, project) {
47
+ const rows = board?.projects;
48
+ if (!Array.isArray(rows))
49
+ return [];
50
+ return rows.filter((row) => Boolean(row && typeof row === "object" && (row.id === project || row.mesh_project_id === project)));
51
+ }
52
+ async function enabledForCompany(company, uid, env) {
53
+ const endpoint = resolveFlagRegistryEndpoint(undefined, env);
54
+ if (!endpoint || !COMPANY_UID.test(uid))
55
+ return false;
56
+ let client;
57
+ let failed = false;
58
+ const deadline = AbortSignal.timeout(150);
59
+ try {
60
+ client = createFlagClient({
61
+ endpoint,
62
+ companyUid: uid,
63
+ companyIdentifiers: [company],
64
+ getToken: () => loadCachedTokens()?.idToken ?? "",
65
+ refreshIntervalMs: 0,
66
+ requestTimeoutMs: 150,
67
+ onError: () => { failed = true; },
68
+ fetch: (input, init) => fetch(input, { ...init, signal: init?.signal ? AbortSignal.any([init.signal, deadline]) : deadline }),
69
+ });
70
+ await client.ready();
71
+ return !failed && !deadline.aborted && client.isEnabled(OFFER_FLAG, { fallback: false });
72
+ }
73
+ catch {
74
+ return false;
75
+ }
76
+ finally {
77
+ try {
78
+ client?.close();
79
+ }
80
+ catch { /* the shell helper defaults off on close failure */ }
81
+ }
82
+ }
83
+ function updatePromptState(root, company, project, state) {
84
+ const file = boardFile(root, company);
85
+ if (!fs.existsSync(file))
86
+ return failure(`no company board for ${company}`);
87
+ let board;
88
+ try {
89
+ board = JSON.parse(fs.readFileSync(file, "utf8"));
90
+ }
91
+ catch {
92
+ return failure(`no local Board project ${project} for ${company}`);
93
+ }
94
+ const rows = matchingProjects(board, project);
95
+ if (!rows.length)
96
+ return failure(`no local Board project ${project} for ${company}`);
97
+ const object = board;
98
+ object.projects = object.projects.map((row) => row.id === project || row.mesh_project_id === project ? { ...row, mesh_registration_prompt_state: state } : row);
99
+ const temp = path.join(path.dirname(file), `.board.json.${process.pid}.${Date.now()}.tmp`);
100
+ try {
101
+ fs.writeFileSync(temp, `${JSON.stringify(board, null, 2)}\n`);
102
+ fs.renameSync(temp, file);
103
+ }
104
+ catch (error) {
105
+ try {
106
+ fs.rmSync(temp, { force: true });
107
+ }
108
+ catch { /* ignore cleanup failure */ }
109
+ return failure(String(error.message));
110
+ }
111
+ return success("");
112
+ }
113
+ export async function runWorkMeshProjectRegistrationOffer(args, root, env = process.env) {
114
+ if (args.length === 3 && args[0] === "--check") {
115
+ const [, company, project] = args;
116
+ if (!COMPANY.test(company) || !PROJECT.test(project))
117
+ return failure("invalid company or project id");
118
+ const uid = cloudUid(root, company);
119
+ if (!uid || !COMPANY_UID.test(uid))
120
+ return success("local\n");
121
+ const file = boardFile(root, company);
122
+ let board;
123
+ try {
124
+ board = JSON.parse(fs.readFileSync(file, "utf8"));
125
+ }
126
+ catch {
127
+ return success("missing\n");
128
+ }
129
+ const [row] = matchingProjects(board, project);
130
+ if (!row)
131
+ return success("missing\n");
132
+ const registered = row.threadId !== undefined && row.threadId !== null && row.threadId !== ""
133
+ && row.channelId !== undefined && row.channelId !== null && row.channelId !== "";
134
+ const state = registered ? "registered" : String(row.mesh_registration_prompt_state ?? "offer");
135
+ if (["accepted", "deferred", "registered", "missing"].includes(state))
136
+ return success(`${state}\n`);
137
+ return success(`${await enabledForCompany(company, uid, env) ? "offer" : "off"}\n`);
138
+ }
139
+ if (args.length === 3 && (args[0] === "--accept" || args[0] === "--defer")) {
140
+ const [, company, project] = args;
141
+ if (!COMPANY.test(company) || !PROJECT.test(project))
142
+ return failure("invalid company or project id");
143
+ return updatePromptState(root, company, project, args[0] === "--accept" ? "accepted" : "deferred");
144
+ }
145
+ return failure("usage: work-mesh-project-registration-offer.sh --check <company> <project-id> | --accept <company> <project-id> | --defer <company> <project-id>");
146
+ }
147
+ //# sourceMappingURL=core-bucket-c-work-mesh-offer-native.js.map
@@ -0,0 +1,3 @@
1
+ export declare function runWorkflowGateFast(args: string[], _stdin: string, _cwd: string, root: string): Promise<number>;
2
+ export declare function runWorkMeshProjectRegistrationOfferFast(args: string[], _stdin: string, _cwd: string, root: string): Promise<number>;
3
+ //# sourceMappingURL=core-bucket-c-workflow-gate-fast.d.ts.map
@@ -0,0 +1,17 @@
1
+ import { runWorkflowGate } from "./core-bucket-c-workflow-gate-native.js";
2
+ import { runWorkMeshProjectRegistrationOffer } from "./core-bucket-c-work-mesh-offer-native.js";
3
+ async function emit(result) {
4
+ const output = await result;
5
+ if (output.stdout)
6
+ process.stdout.write(output.stdout);
7
+ if (output.stderr)
8
+ process.stderr.write(output.stderr);
9
+ return output.code;
10
+ }
11
+ export function runWorkflowGateFast(args, _stdin, _cwd, root) {
12
+ return emit(runWorkflowGate(args, root));
13
+ }
14
+ export function runWorkMeshProjectRegistrationOfferFast(args, _stdin, _cwd, root) {
15
+ return emit(runWorkMeshProjectRegistrationOffer(args, root));
16
+ }
17
+ //# sourceMappingURL=core-bucket-c-workflow-gate-fast.js.map
@@ -0,0 +1,7 @@
1
+ export type WorkflowGateResult = {
2
+ code: number;
3
+ stdout: string;
4
+ stderr: string;
5
+ };
6
+ export declare function runWorkflowGate(args: readonly string[], root: string, env?: NodeJS.ProcessEnv, now?: () => number, sleep?: () => Promise<void>): Promise<WorkflowGateResult>;
7
+ //# sourceMappingURL=core-bucket-c-workflow-gate-native.d.ts.map
@@ -0,0 +1,250 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ const ok = (stdout = "") => ({ code: 0, stdout, stderr: "" });
4
+ const fail = (message, code = 1) => ({ code, stdout: "", stderr: `${message}\n` });
5
+ const validId = /^[a-z0-9._-]+$/;
6
+ const USAGE = `workflow-gate.sh — inspect and answer human gates opened by codex-workflow.mjs gate().
7
+
8
+ A gate is a self-contained pending question file written by a paused workflow
9
+ run; writing a matching answered/<id>.json resumes that run in place. Answers
10
+ are durable: a re-launched run returns them instantly instead of re-asking
11
+ the human, so never delete answered/ files while their project is live.
12
+
13
+ Usage:
14
+ workflow-gate.sh list
15
+ workflow-gate.sh show <id>
16
+ workflow-gate.sh answer <id> <choice|N> [--notes "..."] [--by name] [--force] [--freeform]
17
+ <choice|N> an option label, or a 1-based option index
18
+ --notes free-text note stored alongside the choice
19
+ --by who answered (recorded as answered_by)
20
+ --force pre-answer a gate that has not opened yet, or overwrite an
21
+ existing answer
22
+ --freeform accept a choice that is not one of the gate's options
23
+ workflow-gate.sh wait-pending [--timeout secs]
24
+ Blocks until any pending gate exists (exit 0) or the timeout elapses
25
+ (exit 1). Monitor-friendly wake condition for watching sessions.
26
+ workflow-gate.sh watch-run <run-dir> [--timeout secs]
27
+ Run-scoped wake condition for the session that launched a workflow.
28
+ Blocks until a gate belonging to THAT run opens (prints its id, exit 0),
29
+ the run finishes (exit 3), or --timeout elapses (exit 1). Match is on
30
+ the run_dir every gate carries, which is the --run-dir the launching
31
+ session chose — no id parsing, no env coupling, no startup race.
32
+ workflow-gate.sh clear <id>
33
+ Remove both the pending and answered files for a gate id.
34
+
35
+ Env: HQ_WORKFLOW_GATES_DIR overrides the gates root (default
36
+ <hq-root>/workspace/gates); legacy CODEX_WORKFLOW_GATES_DIR is honored.
37
+ `;
38
+ function gateId(id) {
39
+ return !validId.test(id) ? fail(`workflow-gate: invalid gate id "${id}" — ids are lowercase [a-z0-9._-]`) : undefined;
40
+ }
41
+ function readJson(file) {
42
+ try {
43
+ return JSON.parse(fs.readFileSync(file, "utf8"));
44
+ }
45
+ catch {
46
+ return undefined;
47
+ }
48
+ }
49
+ function listFiles(dir) {
50
+ try {
51
+ return fs.readdirSync(dir).filter((entry) => entry.endsWith(".json")).sort().map((entry) => path.join(dir, entry));
52
+ }
53
+ catch {
54
+ return [];
55
+ }
56
+ }
57
+ function parseTimeout(args) {
58
+ let timeout;
59
+ for (let index = 0; index < args.length;) {
60
+ if (args[index] !== "--timeout")
61
+ return { error: `workflow-gate: unknown flag: ${args[index]}` };
62
+ if (index + 1 >= args.length)
63
+ return { error: "workflow-gate: --timeout needs a value" };
64
+ const raw = args[index + 1];
65
+ if (!/^\d+$/.test(raw))
66
+ return { error: "workflow-gate: --timeout must be a whole number of seconds" };
67
+ timeout = Number(raw);
68
+ index += 2;
69
+ }
70
+ return { ...(timeout === undefined ? {} : { timeout }) };
71
+ }
72
+ const waitSecond = () => new Promise((resolve) => setTimeout(resolve, 1000));
73
+ export async function runWorkflowGate(args, root, env = process.env, now = Date.now, sleep = waitSecond) {
74
+ const gatesDir = env.HQ_WORKFLOW_GATES_DIR || env.CODEX_WORKFLOW_GATES_DIR || path.join(root, "workspace/gates");
75
+ const pending = path.join(gatesDir, "pending");
76
+ const answered = path.join(gatesDir, "answered");
77
+ const [command, ...rest] = args;
78
+ if (!command)
79
+ return { code: 1, stdout: `${USAGE}\n`, stderr: "" };
80
+ if (command === "-h" || command === "--help" || command === "help")
81
+ return ok(`${USAGE}\n`);
82
+ if (command === "list") {
83
+ const files = listFiles(pending);
84
+ if (!files.length)
85
+ return ok("no pending gates\n");
86
+ return ok(files.map((file) => {
87
+ const gate = readJson(file);
88
+ if (!gate || typeof gate !== "object")
89
+ return `<unreadable> ${file}`;
90
+ const created = Date.parse(String(gate.created_at || 0));
91
+ const ageMin = Math.max(0, Math.round((now() - created) / 60000));
92
+ const options = Array.isArray(gate.options) ? gate.options.map((option, index) => `${index + 1}=${String(option?.label)}`).join(" ") : "";
93
+ return `${String(gate.id)} (${ageMin}m) ${String(gate.question)}${options ? ` [${options}]` : ""}`;
94
+ }).join("\n") + "\n");
95
+ }
96
+ if (command === "show") {
97
+ const id = rest[0] ?? "";
98
+ if (!id)
99
+ return fail("workflow-gate: usage: show <id>");
100
+ const invalid = gateId(id);
101
+ if (invalid)
102
+ return invalid;
103
+ const file = [path.join(pending, `${id}.json`), path.join(answered, `${id}.json`)].find((candidate) => {
104
+ try {
105
+ return fs.statSync(candidate).isFile();
106
+ }
107
+ catch {
108
+ return false;
109
+ }
110
+ });
111
+ if (!file)
112
+ return fail(`workflow-gate: no gate "${id}" in pending or answered`);
113
+ try {
114
+ return ok(fs.readFileSync(file, "utf8"));
115
+ }
116
+ catch {
117
+ return fail(`workflow-gate: no gate "${id}" in pending or answered`);
118
+ }
119
+ }
120
+ if (command === "answer") {
121
+ const [id = "", rawChoice = "", ...flags] = rest;
122
+ if (!id || !rawChoice)
123
+ return fail("workflow-gate: usage: answer <id> <choice|N> [--notes ...] [--by name] [--force] [--freeform]");
124
+ const invalid = gateId(id);
125
+ if (invalid)
126
+ return invalid;
127
+ let notes = "";
128
+ let by = "";
129
+ let force = false;
130
+ let freeform = false;
131
+ for (let index = 0; index < flags.length;) {
132
+ const flag = flags[index];
133
+ if (flag === "--force") {
134
+ force = true;
135
+ index += 1;
136
+ }
137
+ else if (flag === "--freeform") {
138
+ freeform = true;
139
+ index += 1;
140
+ }
141
+ else if (flag === "--notes" || flag === "--by") {
142
+ if (index + 1 >= flags.length)
143
+ return fail(`workflow-gate: ${flag} needs a value`);
144
+ if (flag === "--notes")
145
+ notes = flags[index + 1];
146
+ else
147
+ by = flags[index + 1];
148
+ index += 2;
149
+ }
150
+ else
151
+ return fail(`workflow-gate: unknown flag: ${flag}`);
152
+ }
153
+ const pendingFile = path.join(pending, `${id}.json`);
154
+ const answerFile = path.join(answered, `${id}.json`);
155
+ const gate = readJson(pendingFile);
156
+ if (!gate && !force) {
157
+ const ids = listFiles(pending).map((file) => path.basename(file, ".json"));
158
+ return fail(`workflow-gate: no pending gate "${id}"${ids.length ? ` — pending: ${ids.join(", ")}` : " — nothing is pending"} (use --force to pre-answer)`);
159
+ }
160
+ let choice = rawChoice;
161
+ const options = gate && Array.isArray(gate.options) ? gate.options : [];
162
+ if (options.length) {
163
+ if (/^\d+$/.test(rawChoice)) {
164
+ const number = Number(rawChoice);
165
+ if (number >= 1 && number <= options.length)
166
+ choice = String(options[number - 1].label);
167
+ else
168
+ return fail(`workflow-gate: index ${number} is out of range 1..${options.length} for "${id}"`);
169
+ }
170
+ else {
171
+ const hit = options.find((option) => String(option.label).toLowerCase() === rawChoice.toLowerCase());
172
+ if (hit)
173
+ choice = String(hit.label);
174
+ else if (!freeform)
175
+ return fail(`workflow-gate: "${rawChoice}" is not an option for "${id}" — options: ${options.map((option) => option.label).join(", ")} (use --freeform to record it anyway)`);
176
+ }
177
+ }
178
+ if (fs.existsSync(answerFile) && !force)
179
+ return fail(`workflow-gate: gate "${id}" is already answered (${answerFile}) — use --force to overwrite`);
180
+ try {
181
+ fs.mkdirSync(answered, { recursive: true });
182
+ const temporary = `${answerFile}.tmp-${process.pid}`;
183
+ fs.writeFileSync(temporary, `${JSON.stringify({ id, choice, ...(notes ? { notes } : {}), ...(by ? { answered_by: by } : {}), answered_at: new Date(now()).toISOString() }, null, 2)}\n`);
184
+ fs.renameSync(temporary, answerFile);
185
+ try {
186
+ fs.rmSync(pendingFile, { force: true });
187
+ }
188
+ catch { /* shell is best-effort here */ }
189
+ }
190
+ catch (error) {
191
+ return fail(`workflow-gate: ${String(error.message)}`);
192
+ }
193
+ return ok(`answered ${id} -> ${choice}\n`);
194
+ }
195
+ if (command === "wait-pending" || command === "watch-run") {
196
+ let runDir = "";
197
+ let optionArgs = rest;
198
+ if (command === "watch-run") {
199
+ runDir = rest[0] ?? "";
200
+ if (!runDir || runDir.startsWith("-"))
201
+ return fail("workflow-gate: usage: watch-run <run-dir> [--timeout secs]");
202
+ optionArgs = rest.slice(1);
203
+ }
204
+ const parsed = parseTimeout(optionArgs);
205
+ if (parsed.error)
206
+ return fail(parsed.error);
207
+ let elapsed = 0;
208
+ for (;;) {
209
+ const files = listFiles(pending);
210
+ if (command === "wait-pending" && files.length)
211
+ return ok();
212
+ if (command === "watch-run") {
213
+ const wanted = path.resolve(runDir);
214
+ for (const file of files) {
215
+ const gate = readJson(file);
216
+ if (gate?.run_dir && path.resolve(String(gate.run_dir)) === wanted)
217
+ return ok(`${String(gate.id || path.basename(file, ".json"))}\n`);
218
+ }
219
+ const journal = path.join(runDir, "journal.jsonl");
220
+ try {
221
+ if (fs.readFileSync(journal, "utf8").includes('"event":"run-done"'))
222
+ return { code: 3, stdout: "", stderr: "" };
223
+ }
224
+ catch { /* not finished */ }
225
+ }
226
+ if (parsed.timeout !== undefined && elapsed >= parsed.timeout)
227
+ return { code: 1, stdout: "", stderr: "" };
228
+ await sleep();
229
+ elapsed += 1;
230
+ }
231
+ }
232
+ if (command === "clear") {
233
+ const id = rest[0] ?? "";
234
+ if (!id)
235
+ return fail("workflow-gate: usage: clear <id>");
236
+ const invalid = gateId(id);
237
+ if (invalid)
238
+ return invalid;
239
+ try {
240
+ fs.rmSync(path.join(pending, `${id}.json`), { force: true });
241
+ fs.rmSync(path.join(answered, `${id}.json`), { force: true });
242
+ }
243
+ catch (error) {
244
+ return fail(`workflow-gate: ${String(error.message)}`);
245
+ }
246
+ return ok(`cleared ${id}\n`);
247
+ }
248
+ return fail(`workflow-gate: unknown command: ${command} (list | show | answer | wait-pending | watch-run | clear)`);
249
+ }
250
+ //# sourceMappingURL=core-bucket-c-workflow-gate-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,48 @@ 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
+ },
251
+ {
252
+ name: "work-mesh-project-registration-offer",
253
+ asset: "core/scripts/work-mesh-project-registration-offer.sh",
254
+ root: "live",
255
+ summary: "Check and record the work-mesh project registration offer",
256
+ },
257
+ {
258
+ name: "workflow-gate",
259
+ asset: "core/scripts/workflow-gate.sh",
260
+ root: "live",
261
+ summary: "Inspect, answer, and wait for workflow gates",
262
+ },
227
263
  // Session/harness plumbing relocated out of the HQ tree so a single published
228
264
  // CLI owns it. Both are `root: "live"`: they read and write the live HQ tree
229
265
  // (workspace/sessions, companies/, workspace/orchestrator/hook-state), so the
@@ -250,6 +286,7 @@ export const SCAFFOLD_COMMANDS = [
250
286
  */
251
287
  export const SCAFFOLD_ONLY_ASSETS = [
252
288
  { asset: "core/scripts/hq-task.mjs", root: "live", summary: "Sibling implementation used by the hq-task shell rollback companion" },
289
+ { asset: "core/scripts/lib/session-skill-catalog.sh", root: "live", summary: "Shared skill catalog helper used by bundled skill-installed" },
253
290
  { asset: "core/scripts/session-journal.sh", root: "live", summary: "Rollback companion for native session journal" },
254
291
  { asset: "core/scripts/session-project.sh", root: "live", summary: "Rollback companion for native session project" },
255
292
  { 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