@indigoai-us/hq-cli 5.322.0 → 5.324.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/assets/scaffold/core/scripts/derive-trigger-facts.sh +263 -0
  3. package/assets/scaffold/core/scripts/lib/transcript-tail.sh +98 -0
  4. package/assets/scaffold/core/scripts/lib/trigger-fact-text.awk +86 -0
  5. package/dist/bin/hq-auth-refresh.d.ts +5 -1
  6. package/dist/bin/hq-auth-refresh.js +33 -1
  7. package/dist/command-catalog.generated.d.ts +138 -6
  8. package/dist/command-catalog.generated.js +178 -6
  9. package/dist/command-registration-plan.d.ts +24 -0
  10. package/dist/command-registration-plan.js +4 -0
  11. package/dist/commands/anywhere-runtime-flag.d.ts +9 -0
  12. package/dist/commands/anywhere-runtime-flag.js +22 -0
  13. package/dist/commands/cloud.js +23 -4
  14. package/dist/commands/core-derive-trigger-facts-command.d.ts +3 -0
  15. package/dist/commands/core-derive-trigger-facts-command.js +47 -0
  16. package/dist/commands/core-derive-trigger-facts-native.d.ts +21 -0
  17. package/dist/commands/core-derive-trigger-facts-native.js +492 -0
  18. package/dist/commands/core.js +6 -0
  19. package/dist/commands/daemon.js +39 -2
  20. package/dist/commands/db-provision.js +6 -1
  21. package/dist/commands/doctor.js +8 -4
  22. package/dist/commands/feedback.d.ts +2 -0
  23. package/dist/commands/feedback.js +10 -0
  24. package/dist/commands/install-global.d.ts +46 -0
  25. package/dist/commands/install-global.js +674 -0
  26. package/dist/commands/link.d.ts +30 -0
  27. package/dist/commands/link.js +184 -0
  28. package/dist/commands/mcp-status.d.ts +6 -0
  29. package/dist/commands/mcp-status.js +21 -4
  30. package/dist/commands/pkg-install.js +20 -1
  31. package/dist/commands/update.d.ts +19 -0
  32. package/dist/commands/update.js +52 -6
  33. package/dist/fast-core.d.ts +2 -1
  34. package/dist/fast-core.js +12 -8
  35. package/dist/fast-scaffold.js +13 -0
  36. package/dist/index.js +9 -7
  37. package/dist/lib/cli-invite-nudge.d.ts +25 -0
  38. package/dist/lib/cli-invite-nudge.js +152 -0
  39. package/dist/lib/core-utils/timeout-guard-native-flags.d.ts +6 -0
  40. package/dist/lib/core-utils/timeout-guard-native-flags.js +30 -0
  41. package/dist/lib/core-utils/timeout-guard-native.d.ts +0 -3
  42. package/dist/lib/core-utils/timeout-guard-native.js +4 -30
  43. package/dist/lib/core-utils/timeout-guard.d.ts +2 -0
  44. package/dist/lib/core-utils/timeout-guard.js +2 -1
  45. package/dist/lib/daemon/convergence.d.ts +42 -0
  46. package/dist/lib/daemon/convergence.js +66 -0
  47. package/dist/lib/daemon/heartbeat.d.ts +43 -0
  48. package/dist/lib/daemon/heartbeat.js +70 -0
  49. package/dist/lib/daemon/ops/journal.d.ts +29 -0
  50. package/dist/lib/daemon/ops/journal.js +141 -0
  51. package/dist/lib/daemon/ops/memory.d.ts +20 -0
  52. package/dist/lib/daemon/ops/memory.js +65 -0
  53. package/dist/lib/daemon/ops/mesh.d.ts +38 -0
  54. package/dist/lib/daemon/ops/mesh.js +70 -0
  55. package/dist/lib/daemon/ops/policy.d.ts +78 -0
  56. package/dist/lib/daemon/ops/policy.js +283 -0
  57. package/dist/lib/daemon/protocol.d.ts +68 -0
  58. package/dist/lib/daemon/protocol.js +154 -0
  59. package/dist/lib/daemon/run.d.ts +10 -0
  60. package/dist/lib/daemon/run.js +85 -3
  61. package/dist/lib/daemon/server.d.ts +36 -0
  62. package/dist/lib/daemon/server.js +306 -0
  63. package/dist/lib/daemon/session-registry.d.ts +30 -0
  64. package/dist/lib/daemon/session-registry.js +42 -0
  65. package/dist/lib/doctor/checks/anywhere.d.ts +38 -0
  66. package/dist/lib/doctor/checks/anywhere.js +182 -0
  67. package/dist/lib/doctor/registry.js +6 -0
  68. package/dist/lib/first-sync-next-step.d.ts +10 -1
  69. package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
  70. package/dist/lib/flag-registry-worker-source.generated.js +1 -1
  71. package/dist/lib/install/claude-targets.d.ts +47 -0
  72. package/dist/lib/install/claude-targets.js +203 -0
  73. package/dist/lib/install/codex-targets.d.ts +57 -0
  74. package/dist/lib/install/codex-targets.js +187 -0
  75. package/dist/lib/mesh/live/session-seq.js +62 -9
  76. package/dist/lib/registry/normalize-remote.d.ts +21 -0
  77. package/dist/lib/registry/normalize-remote.js +68 -0
  78. package/dist/lib/registry/registry.d.ts +75 -0
  79. package/dist/lib/registry/registry.js +261 -0
  80. package/dist/main.js +3 -1
  81. package/dist/utils/cli-telemetry.d.ts +11 -0
  82. package/dist/utils/cli-telemetry.js +76 -0
  83. package/dist/utils/cognito-session.d.ts +5 -1
  84. package/dist/utils/cognito-session.js +8 -12
  85. package/dist/utils/feedback-diagnostics.d.ts +2 -0
  86. package/dist/utils/run-bundled-script.d.ts +7 -4
  87. package/dist/utils/run-bundled-script.js +47 -12
  88. package/dist/utils/version-check.d.ts +2 -0
  89. package/dist/utils/version-check.js +7 -0
  90. package/package.json +2 -2
@@ -0,0 +1,21 @@
1
+ export declare const DERIVE_TRIGGER_FACTS_ASSET = "core/scripts/derive-trigger-facts.sh";
2
+ export type DeriveTriggerRunOptions = {
3
+ argv: readonly string[];
4
+ stdin: string;
5
+ cwd: string;
6
+ hqRoot: string;
7
+ env?: Record<string, string | undefined>;
8
+ /** Test seam for the shell's `git -C ... rev-parse` lookup. */
9
+ gitBranch?: string;
10
+ /** Test seam for `command -v jq`; production resolves jq from PATH. */
11
+ jqAvailable?: boolean;
12
+ };
13
+ export type DeriveTriggerRunResult = {
14
+ code: number;
15
+ stdout: Buffer;
16
+ stderr: Buffer;
17
+ };
18
+ /** Run the native handler contract, retaining exact bytes for differential tests. */
19
+ export declare function runDeriveTriggerFacts(options: DeriveTriggerRunOptions): DeriveTriggerRunResult;
20
+ export declare function runDeriveTriggerFactsFast(argv: readonly string[], stdin: string, cwd: string, hqRoot: string | undefined): number;
21
+ //# sourceMappingURL=core-derive-trigger-facts-native.d.ts.map
@@ -0,0 +1,492 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import { runBundledScript } from "../utils/run-bundled-script.js";
5
+ export const DERIVE_TRIGGER_FACTS_ASSET = "core/scripts/derive-trigger-facts.sh";
6
+ const TEXT_FACT_AWK = "core/scripts/lib/trigger-fact-text.awk";
7
+ const TRANSCRIPT_TAIL_MAX_BYTES = 1_048_576;
8
+ function effectiveEnvironment(options) {
9
+ return {
10
+ ...process.env,
11
+ ...(options.env ?? {}),
12
+ HQ_ROOT: options.hqRoot,
13
+ CLAUDE_PROJECT_DIR: options.hqRoot,
14
+ };
15
+ }
16
+ class ShellFallbackRequired extends Error {
17
+ }
18
+ function asciiLower(value) {
19
+ return value.replace(/[A-Z]/g, (letter) => String.fromCharCode(letter.charCodeAt(0) + 32));
20
+ }
21
+ function localeLower(value, env) {
22
+ const locale = env.LC_ALL || env.LC_CTYPE || env.LANG || "C";
23
+ if (/^(?:C|POSIX)(?:$|\.(?!utf-?8$))/i.test(locale))
24
+ return asciiLower(value);
25
+ // glibc applies towlower independently to each character. Lowercase code
26
+ // points separately so JavaScript does not apply context-sensitive sigma.
27
+ // glibc maps U+0130 to simple `i`; JavaScript otherwise expands it.
28
+ return Array.from(value, (character) => character === "\u0130" ? "i" : character.toLowerCase()).join("");
29
+ }
30
+ function hasGlobPattern(value) {
31
+ // The bracket is a literal glob metacharacter in this predicate.
32
+ // eslint-disable-next-line no-useless-escape -- Keep the set as `*`, `?`, or `[`.
33
+ return /[*?\[]/.test(value);
34
+ }
35
+ function globSegmentMatches(pattern, value) {
36
+ let expression = "^";
37
+ for (let index = 0; index < pattern.length; index += 1) {
38
+ const character = pattern[index] ?? "";
39
+ if (character === "*")
40
+ expression += ".*";
41
+ else if (character === "?")
42
+ expression += ".";
43
+ else if (character === "[") {
44
+ const close = pattern.indexOf("]", index + 1);
45
+ if (close > index + 1) {
46
+ let characterClass = pattern.slice(index + 1, close);
47
+ if (characterClass.startsWith("!"))
48
+ characterClass = `^${characterClass.slice(1)}`;
49
+ expression += `[${characterClass}]`;
50
+ index = close;
51
+ }
52
+ else
53
+ expression += "\\[";
54
+ }
55
+ else
56
+ expression += character.replace(/[\\^$+?.()|{}]/g, "\\$&");
57
+ }
58
+ try {
59
+ return new RegExp(`${expression}$`).test(value);
60
+ }
61
+ catch {
62
+ return false;
63
+ }
64
+ }
65
+ function expandBashGlob(token, cwd) {
66
+ if (!hasGlobPattern(token))
67
+ return [token];
68
+ const absolute = token.startsWith(path.sep);
69
+ const parts = token.split(path.sep).filter((part, index) => !(absolute && index === 0));
70
+ let candidates = [absolute ? path.sep : ""];
71
+ let matchedPattern = false;
72
+ for (const part of parts) {
73
+ if (!hasGlobPattern(part)) {
74
+ candidates = candidates.map((parent) => !parent ? part : parent === path.sep ? `${path.sep}${part}` : `${parent}${path.sep}${part}`);
75
+ continue;
76
+ }
77
+ matchedPattern = true;
78
+ const expanded = [];
79
+ for (const parent of candidates) {
80
+ const directory = absolute ? (parent || path.sep) : path.resolve(cwd, parent || ".");
81
+ let names;
82
+ try {
83
+ names = fs.readdirSync(directory).sort();
84
+ }
85
+ catch {
86
+ continue;
87
+ }
88
+ for (const name of names) {
89
+ if (name.startsWith(".") && !part.startsWith("."))
90
+ continue;
91
+ if (!globSegmentMatches(part, name))
92
+ continue;
93
+ expanded.push(!parent ? name : parent === path.sep ? `${path.sep}${name}` : `${parent}${path.sep}${name}`);
94
+ }
95
+ }
96
+ if (expanded.length === 0)
97
+ return [token];
98
+ candidates = expanded;
99
+ }
100
+ return matchedPattern ? candidates : [token];
101
+ }
102
+ function bashUnquotedWords(facts, cwd) {
103
+ return facts.join(" ").split(/[\t\n ]+/).filter(Boolean).flatMap((word) => expandBashGlob(word, cwd));
104
+ }
105
+ function commandOnPath(command, env) {
106
+ const pathValue = env.PATH ?? "";
107
+ for (const directory of pathValue.split(path.delimiter)) {
108
+ const candidate = path.join(directory || process.cwd(), command);
109
+ try {
110
+ fs.accessSync(candidate, fs.constants.X_OK);
111
+ return true;
112
+ }
113
+ catch {
114
+ // PATH probes intentionally ignore missing and inaccessible entries.
115
+ }
116
+ }
117
+ return false;
118
+ }
119
+ function jqField(value) {
120
+ // jq's `// ""` falls through for null and false only.
121
+ return value === null || value === undefined || value === false ? "" : value;
122
+ }
123
+ function jqText(value) {
124
+ const selected = jqField(value);
125
+ if (typeof selected === "string")
126
+ return selected;
127
+ if (typeof selected === "boolean")
128
+ return selected ? "true" : "";
129
+ if (typeof selected === "number" && Number.isFinite(selected)) {
130
+ throw new ShellFallbackRequired("numeric jq tostring requires shell serialization");
131
+ }
132
+ if (typeof selected === "object") {
133
+ if (selected === null)
134
+ return "";
135
+ throw new ShellFallbackRequired("compound jq tostring requires shell serialization");
136
+ }
137
+ return "";
138
+ }
139
+ function nestedField(record, first, second) {
140
+ const parent = jqField(record[first]);
141
+ if (parent === "")
142
+ return "";
143
+ if (typeof parent !== "object" || parent === null || Array.isArray(parent)) {
144
+ throw new ShellFallbackRequired(`unexpected ${first} payload shape`);
145
+ }
146
+ return jqField(parent[second]);
147
+ }
148
+ function parsePayload(raw) {
149
+ let parsed;
150
+ try {
151
+ parsed = JSON.parse(raw);
152
+ }
153
+ catch {
154
+ throw new ShellFallbackRequired("malformed hook JSON");
155
+ }
156
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
157
+ throw new ShellFallbackRequired("unexpected hook payload shape");
158
+ }
159
+ const record = parsed;
160
+ const toolResponse = jqField(record.tool_response);
161
+ const fields = {
162
+ tool: jqText(record.tool_name),
163
+ command: jqText(nestedField(record, "tool_input", "command")),
164
+ runInBackground: jqText(nestedField(record, "tool_input", "run_in_background")),
165
+ toolResponse: typeof toolResponse === "string" ? toolResponse : jqText(toolResponse),
166
+ prompt: jqText(record.prompt),
167
+ cwd: jqText(record.cwd),
168
+ sessionId: jqText(record.session_id),
169
+ transcriptPath: jqText(record.transcript_path),
170
+ };
171
+ if (Object.values(fields).some((field) => field.includes("\0"))) {
172
+ throw new ShellFallbackRequired("NUL-delimited jq fields require shell semantics");
173
+ }
174
+ return fields;
175
+ }
176
+ function matchTextFacts(rawText, env) {
177
+ const text = localeLower(rawText, env);
178
+ const facts = [];
179
+ const words = /[a-z][a-z0-9_-]+/g;
180
+ for (const match of text.matchAll(words))
181
+ facts.push(match[0]);
182
+ if (/(^|[^a-z0-9_])aws_profile/.test(text) || /op:\/\/|\.env([^a-z0-9]|$)/.test(text)) {
183
+ facts.push("secret");
184
+ }
185
+ if (/(^|[^a-z0-9_])(main|master|staging|production)([^a-z0-9_]|$)/.test(text) || /release\//.test(text)) {
186
+ facts.push("shared_branch");
187
+ }
188
+ if (/(^|[^a-z0-9])sk-[a-z0-9]/.test(text) ||
189
+ /(^|[^a-z0-9])(gh[opsur]_|github_pat_)[a-z0-9_]/.test(text) ||
190
+ /(^|[^a-z0-9])akia[a-z0-9][a-z0-9]/.test(text) ||
191
+ /(^|[^a-z0-9])xox[bpsa]-[a-z0-9]/.test(text) ||
192
+ /(^|[^a-z0-9])glpat-[a-z0-9]/.test(text) ||
193
+ /-----begin[a-z -]*private key/.test(text) ||
194
+ /(^|[^a-z0-9])bearer [a-z0-9._-][a-z0-9._-][a-z0-9._-]/.test(text)) {
195
+ facts.push("apikey", "secret");
196
+ }
197
+ if (/successfully (merged|deployed|pushed|published|created)/.test(text) ||
198
+ /(deployment|deploy|build|release) (complete|completed|succeeded|ready)/.test(text) ||
199
+ /merged pull request/.test(text) ||
200
+ /pull request #?[0-9]+ [\s\S]* merged/.test(text)) {
201
+ facts.push("completed");
202
+ }
203
+ const filenames = /\.?[a-z0-9_][a-z0-9_./-]*\.[a-z][a-z0-9]+/g;
204
+ for (const match of text.matchAll(filenames)) {
205
+ const filename = match[0].slice(match[0].lastIndexOf("/") + 1);
206
+ const dot = filename.lastIndexOf(".");
207
+ facts.push(`.${filename.slice(dot + 1)}`, filename);
208
+ }
209
+ const slashCommands = / \/[a-z][a-z0-9-]*/g;
210
+ for (const match of `${" "}${text}`.matchAll(slashCommands)) {
211
+ facts.push(match[0].slice(1));
212
+ }
213
+ return facts;
214
+ }
215
+ function isSharedBranch(branch, env) {
216
+ const value = localeLower(branch, env);
217
+ return /(^|[^a-z0-9_])(main|master|staging|production)([^a-z0-9_]|$)/.test(value) || /release\//.test(value);
218
+ }
219
+ function normalizeFacts(facts, branch, cwd, env) {
220
+ const seen = new Set();
221
+ const output = [];
222
+ const expandedText = bashUnquotedWords(facts, cwd).join("\n");
223
+ const lower = localeLower(expandedText, env);
224
+ // AWK's [[:space:]] splitter recognizes these bytes after Bash has split and
225
+ // pathname-expanded the unquoted FACTS parameter expansion.
226
+ for (const token of lower.split(/[\t\n\v\f\r ]+/).filter(Boolean)) {
227
+ if (seen.has(token))
228
+ continue;
229
+ seen.add(token);
230
+ output.push(token);
231
+ }
232
+ if (isSharedBranch(branch, env) && !seen.has("shared_branch"))
233
+ output.push("shared_branch");
234
+ return output.join(" ");
235
+ }
236
+ function companyFromMeta(file) {
237
+ let contents;
238
+ try {
239
+ if (!fs.statSync(file).isFile())
240
+ return "";
241
+ contents = fs.readFileSync(file, "utf8");
242
+ }
243
+ catch {
244
+ return "";
245
+ }
246
+ for (const line of contents.split(/\n/)) {
247
+ const fields = line.trim().split(/[\t\v\f\r ]+/);
248
+ if (fields[0] !== "company_slug:")
249
+ continue;
250
+ let value = line.replace(/^[^:]+:[\t\n\v\f\r ]*/, "");
251
+ value = value.replace(/^"|"$/g, "");
252
+ return value;
253
+ }
254
+ return "";
255
+ }
256
+ function resolveCompanyFact(payloadCwd, sessionId, hqRoot, fallbackCwd, env) {
257
+ let scope = env.HQ_POLICY_COMPANY ?? "";
258
+ const cwd = payloadCwd || env.CLAUDE_PROJECT_DIR || fallbackCwd;
259
+ if (!scope) {
260
+ const marker = cwd.lastIndexOf("companies/");
261
+ if (marker >= 0)
262
+ scope = cwd.slice(marker + "companies/".length).split("/")[0] ?? "";
263
+ }
264
+ if (!scope && sessionId) {
265
+ const metaPath = path.join(hqRoot, "workspace", "sessions", sessionId, "meta.yaml");
266
+ scope = companyFromMeta(metaPath);
267
+ }
268
+ return Boolean(scope);
269
+ }
270
+ function readBranch(cwd, env) {
271
+ const result = spawnSync("git", ["-C", cwd, "rev-parse", "--abbrev-ref", "HEAD"], {
272
+ env: { ...process.env, ...env },
273
+ stdio: ["ignore", "pipe", "ignore"],
274
+ });
275
+ if (result.error || result.status !== 0)
276
+ return "";
277
+ return Buffer.from(result.stdout ?? "").toString("utf8").replace(/\n+$/, "");
278
+ }
279
+ function transcriptAssistantText(transcriptPath) {
280
+ let stat;
281
+ let raw;
282
+ try {
283
+ stat = fs.statSync(transcriptPath);
284
+ if (!stat.isFile())
285
+ return "";
286
+ if (stat.size > TRANSCRIPT_TAIL_MAX_BYTES + 1) {
287
+ throw new ShellFallbackRequired("bounded transcript tail requires shell semantics");
288
+ }
289
+ raw = fs.readFileSync(transcriptPath);
290
+ }
291
+ catch (error) {
292
+ if (error instanceof ShellFallbackRequired)
293
+ throw error;
294
+ throw new ShellFallbackRequired("transcript could not be read exactly");
295
+ }
296
+ if (raw.includes(0))
297
+ throw new ShellFallbackRequired("NUL transcript needs jq semantics");
298
+ const records = [];
299
+ for (const line of raw.toString("utf8").split(/\n/)) {
300
+ if (!line.trim())
301
+ continue;
302
+ let parsed;
303
+ try {
304
+ parsed = JSON.parse(line);
305
+ }
306
+ catch {
307
+ throw new ShellFallbackRequired("malformed transcript JSON");
308
+ }
309
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
310
+ throw new ShellFallbackRequired("transcript line requires jq object indexing");
311
+ }
312
+ const event = parsed;
313
+ const typeValue = jqField(event.type);
314
+ const eventType = typeof typeValue === "string" ? typeValue : typeValue === true ? "true" : "";
315
+ const message = event.message;
316
+ if (message !== null && message !== undefined && (typeof message !== "object" || Array.isArray(message))) {
317
+ throw new ShellFallbackRequired("transcript message requires jq object indexing");
318
+ }
319
+ let content = message?.content;
320
+ if (content === null || content === undefined || content === false)
321
+ content = event.content;
322
+ let text = "";
323
+ if (Array.isArray(content)) {
324
+ const selected = [];
325
+ for (const item of content) {
326
+ if (typeof item !== "object" || item === null || Array.isArray(item)) {
327
+ throw new ShellFallbackRequired("transcript content item requires jq object indexing");
328
+ }
329
+ if (item.type !== "text")
330
+ continue;
331
+ const itemText = item.text;
332
+ if (itemText === null || itemText === undefined)
333
+ selected.push("");
334
+ else if (typeof itemText === "string")
335
+ selected.push(itemText);
336
+ else
337
+ throw new ShellFallbackRequired("transcript content requires jq join semantics");
338
+ }
339
+ text = selected.join(" ");
340
+ }
341
+ else if (typeof content === "string") {
342
+ text = content;
343
+ }
344
+ if (eventType === "user")
345
+ records.length = 0;
346
+ else if (eventType === "assistant") {
347
+ records.push(...(records.length ? [" "] : []), text);
348
+ }
349
+ }
350
+ return records.join("").replace(/\n+$/, "");
351
+ }
352
+ function computeOutput(options) {
353
+ const args = [...options.argv];
354
+ if (args.length === 0 || args.length > 2)
355
+ throw new ShellFallbackRequired("unsupported argv");
356
+ const event = args[0] ?? "";
357
+ const withAssistantIntent = args[1] === "--with-assistant-intent";
358
+ if (args.length === 2 && !withAssistantIntent)
359
+ throw new ShellFallbackRequired("unsupported argv");
360
+ if (!["PreToolUse", "PostToolUse", "UserPromptSubmit", "AssistantIntent"].includes(event)) {
361
+ throw new ShellFallbackRequired("unsupported event");
362
+ }
363
+ const paired = withAssistantIntent && (event === "PreToolUse" || event === "UserPromptSubmit");
364
+ const env = effectiveEnvironment(options);
365
+ const jqAvailable = options.jqAvailable ?? commandOnPath("jq", env);
366
+ let payload = {
367
+ tool: "", command: "", runInBackground: "", toolResponse: "", prompt: "",
368
+ cwd: "", sessionId: "", transcriptPath: "",
369
+ };
370
+ if (jqAvailable)
371
+ payload = parsePayload(options.stdin || "{}");
372
+ const payloadCwd = payload.cwd || env.CLAUDE_PROJECT_DIR || options.hqRoot;
373
+ const company = resolveCompanyFact(payload.cwd, payload.sessionId, options.hqRoot, options.hqRoot, env);
374
+ const derive = (kind) => {
375
+ const facts = ["always"];
376
+ let branch = "";
377
+ if (kind === "PreToolUse" || kind === "PostToolUse") {
378
+ if (payload.tool === "Bash") {
379
+ if (kind === "PreToolUse") {
380
+ facts.push(...matchTextFacts(payload.command, env));
381
+ if (payload.runInBackground === "true")
382
+ facts.push("run_in_background");
383
+ }
384
+ else
385
+ facts.push(...matchTextFacts(payload.toolResponse, env));
386
+ }
387
+ else if (payload.tool !== "") {
388
+ if (kind === "PreToolUse")
389
+ facts.push(localeLower(payload.tool, env));
390
+ else
391
+ facts.push(...matchTextFacts(payload.toolResponse, env));
392
+ }
393
+ }
394
+ else if (kind === "UserPromptSubmit") {
395
+ facts.push(...matchTextFacts(payload.prompt, env));
396
+ }
397
+ if (company)
398
+ facts.push("company");
399
+ if (kind === "AssistantIntent") {
400
+ if (payload.transcriptPath && jqAvailable) {
401
+ try {
402
+ if (fs.statSync(payload.transcriptPath).isFile()) {
403
+ const lookback = transcriptAssistantText(payload.transcriptPath);
404
+ if (lookback)
405
+ facts.push(...matchTextFacts(lookback, env));
406
+ }
407
+ }
408
+ catch (error) {
409
+ if (error instanceof ShellFallbackRequired)
410
+ throw error;
411
+ // The shell checks -f before invoking jq; missing files add no facts.
412
+ }
413
+ }
414
+ }
415
+ else {
416
+ if (payloadCwd.includes("repos/public/") || payloadCwd.includes("repos/private/"))
417
+ facts.push("repo");
418
+ branch = options.gitBranch ?? readBranch(payloadCwd, env);
419
+ }
420
+ return normalizeFacts(facts, branch, options.cwd, env);
421
+ };
422
+ const primary = derive(event);
423
+ return paired ? `${primary}\n${derive("AssistantIntent")}\n` : `${primary}\n`;
424
+ }
425
+ function shellFallback(options) {
426
+ const env = { ...process.env, ...(options.env ?? {}) };
427
+ delete env.HQ_ROOT;
428
+ delete env.CLAUDE_PROJECT_DIR;
429
+ const result = runBundledScript({
430
+ asset: DERIVE_TRIGGER_FACTS_ASSET,
431
+ args: [...options.argv],
432
+ cwd: options.hqRoot,
433
+ hqRoot: options.hqRoot,
434
+ stdin: options.stdin,
435
+ captureOutput: true,
436
+ env,
437
+ });
438
+ return {
439
+ code: result.code,
440
+ stdout: result.stdout ?? Buffer.alloc(0),
441
+ stderr: result.stderr ?? Buffer.alloc(0),
442
+ };
443
+ }
444
+ function missingSupportLibrary(hqRoot) {
445
+ const required = [
446
+ [
447
+ "core/scripts/lib/trigger-fact-text.awk",
448
+ "ERROR: derive-trigger-facts: missing core/scripts/lib/trigger-fact-text.awk",
449
+ ],
450
+ [
451
+ "core/scripts/lib/transcript-tail.sh",
452
+ "ERROR: derive-trigger-facts: missing core/scripts/lib/transcript-tail.sh",
453
+ ],
454
+ ];
455
+ for (const [relativePath, message] of required) {
456
+ try {
457
+ fs.accessSync(path.join(hqRoot, relativePath), fs.constants.R_OK);
458
+ }
459
+ catch {
460
+ return message;
461
+ }
462
+ }
463
+ return undefined;
464
+ }
465
+ /** Run the native handler contract, retaining exact bytes for differential tests. */
466
+ export function runDeriveTriggerFacts(options) {
467
+ const missingLibrary = missingSupportLibrary(options.hqRoot);
468
+ if (missingLibrary) {
469
+ return {
470
+ code: 1,
471
+ stdout: Buffer.alloc(0),
472
+ stderr: Buffer.from(`${missingLibrary}\n`),
473
+ };
474
+ }
475
+ try {
476
+ return { code: 0, stdout: Buffer.from(computeOutput(options)), stderr: Buffer.alloc(0) };
477
+ }
478
+ catch (error) {
479
+ if (!(error instanceof ShellFallbackRequired))
480
+ throw error;
481
+ return shellFallback(options);
482
+ }
483
+ }
484
+ export function runDeriveTriggerFactsFast(argv, stdin, cwd, hqRoot) {
485
+ const result = runDeriveTriggerFacts({ argv, stdin, cwd, hqRoot: hqRoot ?? cwd });
486
+ if (result.stdout.length)
487
+ process.stdout.write(result.stdout);
488
+ if (result.stderr.length)
489
+ process.stderr.write(result.stderr);
490
+ return result.code;
491
+ }
492
+ //# sourceMappingURL=core-derive-trigger-facts-native.js.map
@@ -46,6 +46,8 @@ import { registerCorePinNative } from "./core-pin-native.js";
46
46
  import { registerCoreRefreshVaultAccessNative } from "./core-refresh-vault-access-native.js";
47
47
  import { registerCoreWorkMeshBindTrustedNative } from "./core-work-mesh-bind-trusted-native.js";
48
48
  import { registerCoreHookHelperNativeCommands } from "./core-native-hook-helper-commands.js";
49
+ import { registerCoreDeriveTriggerFactsCommand } from "./core-derive-trigger-facts-command.js";
50
+ import { DERIVE_TRIGGER_FACTS_ASSET } from "./core-derive-trigger-facts-native.js";
49
51
  import { registerCoreHookStateHelpers } from "./core-hook-state-helpers-native.js";
50
52
  import { registerCoreHookBatchCommand } from "./core-hook-batch-command.js";
51
53
  import { resolveLiveRoot } from "../utils/hq-roots.js";
@@ -253,6 +255,9 @@ export const SCAFFOLD_ONLY_ASSETS = [
253
255
  { asset: "core/scripts/lib/work-mesh-enqueue.sh", root: "live", summary: "Work Mesh Live spool enqueue lib sourced by hooks" },
254
256
  { asset: "core/scripts/lib/work-mesh-live-hook.sh", root: "live", summary: "Work Mesh Live hook helpers sourced by rebind" },
255
257
  { asset: "core/scripts/lib/work-mesh-live-rebind.sh", root: "live", summary: "Work Mesh Live session rebind library sourced by hooks and the native rollback command" },
258
+ { asset: DERIVE_TRIGGER_FACTS_ASSET, root: "live", summary: "Rollback companion for native derive-trigger-facts" },
259
+ { asset: "core/scripts/lib/trigger-fact-text.awk", root: "live", summary: "Shared trigger fact tokenizer used by hooks" },
260
+ { asset: "core/scripts/lib/transcript-tail.sh", root: "live", summary: "Bounded transcript reader used by trigger helper rollback" },
256
261
  // Wave 2 handoff/worktree commands retain their old scripts for one release,
257
262
  // behind the shared default-off rollback flag.
258
263
  { asset: WORKTREE_GC_ASSET, root: "live", summary: "Rollback companion for native worktree-gc" },
@@ -397,6 +402,7 @@ export function registerCoreCommands(program) {
397
402
  registerCoreDmBindNative(core);
398
403
  registerCoreWorkMeshBindTrustedNative(core);
399
404
  registerCoreHookHelperNativeCommands(core);
405
+ registerCoreDeriveTriggerFactsCommand(core);
400
406
  registerCoreHookStateHelpers(core);
401
407
  registerCoreHookBatchCommand(core);
402
408
  const sessionHelperCommands = [
@@ -31,6 +31,7 @@ import { processCommandLine } from "../lib/daemon/lock.js";
31
31
  import { writeControlRequest } from "../lib/daemon/requests.js";
32
32
  import { isSyncRunnerCommandLine, readWatchRunnerOwnerStatus, watchRunnerOwnerStatusPath } from "../lib/daemon/sync-runner.js";
33
33
  import { getSyncModeForCompany, setSyncMode } from "./sync-mode.js";
34
+ import { requireAnywhereRuntime } from "./anywhere-runtime-flag.js";
34
35
  import { resolveDefaultHqRoot } from "../utils/cognito-session.js";
35
36
  class DaemonCommandError extends Error {
36
37
  }
@@ -766,6 +767,37 @@ export function registerDaemonCommand(program, over = {}) {
766
767
  hqBinary: d.hqBinary,
767
768
  });
768
769
  }));
770
+ daemon
771
+ .command("call <op>")
772
+ .description("Send one request to hqd (policy.check, journal.append, memory.get, memory.put, mesh.presence, …) and print the result as JSON")
773
+ .option("--json <args>", "Request args as a JSON object", "{}")
774
+ .option("--timeout <ms>", "Give up after this many milliseconds")
775
+ .action(wrap(async (d, op, opts) => {
776
+ requireAnywhereRuntime();
777
+ let args;
778
+ try {
779
+ args = JSON.parse(opts.json);
780
+ }
781
+ catch {
782
+ throw new Error("--json must be valid JSON");
783
+ }
784
+ if (!args || typeof args !== "object" || Array.isArray(args))
785
+ throw new Error("--json must be a JSON object");
786
+ const timeoutMs = Number(opts.timeout ?? (op === "policy.check" ? "40000" : "20000"));
787
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
788
+ throw new Error("--timeout must be a positive number of milliseconds");
789
+ const { hqdRequest, hqdSocketPath, HqdClientError } = await import("../lib/daemon/protocol.js");
790
+ try {
791
+ const result = await hqdRequest(op, args, { socketPath: hqdSocketPath(d.home, d.env), timeoutMs });
792
+ d.out(JSON.stringify(result, null, 2));
793
+ }
794
+ catch (err) {
795
+ if (!(err instanceof HqdClientError))
796
+ throw err;
797
+ d.out(JSON.stringify({ error: { code: err.code, message: err.message } }, null, 2));
798
+ process.exitCode = 1;
799
+ }
800
+ }));
769
801
  daemon
770
802
  .command("status")
771
803
  .description("Show the machine type and each service's state")
@@ -828,8 +860,13 @@ export function registerDaemonCommand(program, over = {}) {
828
860
  .action(wrap((d, unit, opts) => runLogs(d, unit, opts)));
829
861
  daemon
830
862
  .command("restart [unit]")
831
- .description("Restart one service, or every running service")
832
- .action(wrap(async (d, unit) => queueRequest(d, unit ? { action: "restart", unit } : { action: "restart" }, unit ? `restart ${unit}` : "restart all services")));
863
+ .description("Restart one service, every service, or the daemon process")
864
+ .option("--daemon", "Restart the supervisor process itself")
865
+ .action(wrap(async (d, unit, opts) => {
866
+ if (opts.daemon && unit)
867
+ throw new DaemonCommandError("--daemon cannot be combined with a service name");
868
+ return queueRequest(d, opts.daemon ? { action: "restart-daemon" } : unit ? { action: "restart", unit } : { action: "restart" }, opts.daemon ? "restart daemon" : unit ? `restart ${unit}` : "restart all services");
869
+ }));
833
870
  daemon
834
871
  .command("trigger <unit>")
835
872
  .description("Run a scheduled service now (for example search-index)")
@@ -65,7 +65,12 @@ export function registerDbProvisionCommand(db, depsFactory = defaultDeps) {
65
65
  throw error;
66
66
  const msg = error instanceof Error ? error.message : "Unknown error";
67
67
  const status = error.status;
68
- if (status === 402 || /PLAN_REQUIRED|Team plan|Workforce|\$500/i.test(msg)) {
68
+ const code = error.code;
69
+ if (code === "REMOTE_DB_NOT_AVAILABLE" || status === 501) {
70
+ console.error(chalk.red("Error:"), "Remote databases are not available yet.");
71
+ console.error(chalk.dim("Local hq db status|sql|migrate still work offline."));
72
+ }
73
+ else if (status === 402 || /PLAN_REQUIRED|Team plan|Workforce|\$500/i.test(msg)) {
69
74
  console.error(chalk.red("Error:"), "Remote vault DB requires the HQ Workforce plan ($500/mo).");
70
75
  console.error(chalk.dim("Local databases still work: hq db status|sql|migrate — no HQ Workforce plan required."));
71
76
  }
@@ -37,6 +37,7 @@ import { isCompanyJournalSlug } from "../lib/doctor/checks/sync-health.js";
37
37
  import { readCompaniesManifestMap } from "../lib/work-context/repo-remote.js";
38
38
  import * as readline from "node:readline";
39
39
  import { runSideEffectTests } from "../lib/doctor/deep/sandbox.js";
40
+ import { anywhereRuntimeEnabled } from "./anywhere-runtime-flag.js";
40
41
  /**
41
42
  * Marker directories that identify an HQ tree root. A directory is an HQ root
42
43
  * only when it contains ALL of these. `.claude` + `core` are present in both
@@ -90,7 +91,8 @@ export async function runDoctor(options = {}) {
90
91
  const write = options.stdout ?? ((chunk) => void process.stdout.write(chunk));
91
92
  const writeErr = options.stderr ?? ((chunk) => void process.stderr.write(chunk));
92
93
  const hqRoot = options.root ?? resolveHqRoot(options.cwd);
93
- if (!hqRoot) {
94
+ const standaloneAnywhere = !hqRoot && options.only?.length === 1 && options.only[0] === "anywhere" && anywhereRuntimeEnabled();
95
+ if (!hqRoot && !standaloneAnywhere) {
94
96
  const from = options.cwd ?? process.cwd();
95
97
  writeErr(`hq doctor: not inside an HQ tree.\n` +
96
98
  ` Searched upward from: ${from}\n` +
@@ -121,7 +123,9 @@ export async function runDoctor(options = {}) {
121
123
  // The detected platform and the session id are exposed to every check so the
122
124
  // host-specific runtime probe (US-006) can decide UNKNOWN vs FAIL vs UNTESTED.
123
125
  const context = {
124
- hqRoot,
126
+ // Anywhere checks inspect ~/.hq and are intentionally runnable from a foreign
127
+ // repo. No other family is selected in this standalone path.
128
+ hqRoot: hqRoot ?? "",
125
129
  platform: { id: platform.id, evidence: platform.evidence },
126
130
  sessionId: options.sessionId,
127
131
  liveRuntimes: options.liveRuntimes === true,
@@ -166,7 +170,7 @@ export async function runDoctor(options = {}) {
166
170
  ? await resolveClientHealthReasonCodesCheckIds(families, options.resolveClientHealthReasonCodesForCompany)
167
171
  : undefined;
168
172
  write(renderJson(buildDoctorJson({
169
- hqRoot,
173
+ hqRoot: hqRoot ?? "",
170
174
  families,
171
175
  platform,
172
176
  scope,
@@ -175,7 +179,7 @@ export async function runDoctor(options = {}) {
175
179
  }
176
180
  else {
177
181
  write(renderText({
178
- hqRoot,
182
+ hqRoot: hqRoot ?? "",
179
183
  families,
180
184
  platform,
181
185
  verbose: options.verbose ?? false,
@@ -2,6 +2,8 @@ import { Command } from "commander";
2
2
  import { type DiagnosticsBlob } from "../utils/feedback-diagnostics.js";
3
3
  import { collectFeedbackLogs, feedbackLogsEnabled } from "../utils/feedback-logs.js";
4
4
  import { type LogBundleRef } from "../utils/feedback-log-bundle.js";
5
+ export declare const FEEDBACK_LATEST_VERSION_FLAG = "feedback.latest-version";
6
+ export declare const FEEDBACK_LATEST_VERSION_FLAG_FALLBACK = false;
5
7
  export declare const BODY_MAX_BYTES: number;
6
8
  /** Validate fields that must be valid before feedback attachments are uploaded. */
7
9
  export declare function validateFeedbackContent(title: string, body: string): void;