@indigoai-us/hq-cli 5.325.0 → 5.327.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/bin/hq-auth-refresh.js +2 -3
- package/dist/command-catalog.generated.d.ts +19 -1
- package/dist/command-catalog.generated.js +21 -1
- package/dist/commands/anywhere-runtime-flag.d.ts +2 -2
- package/dist/commands/anywhere-runtime-flag.js +2 -2
- package/dist/commands/auth.js +3 -1
- package/dist/commands/core-detect-stale-review-base-fast.d.ts +2 -0
- package/dist/commands/core-detect-stale-review-base-fast.js +23 -0
- package/dist/commands/core-job-remote-launcher.js +2 -3
- package/dist/commands/core-register-project-native.js +1 -1
- package/dist/commands/core-repo-run-registry-fast.d.ts +2 -0
- package/dist/commands/core-repo-run-registry-fast.js +35 -0
- package/dist/commands/core-rrr-worktree-scope-fix-fast-flag.d.ts +7 -0
- package/dist/commands/core-rrr-worktree-scope-fix-fast-flag.js +25 -0
- package/dist/commands/core-session-journal-native.js +1 -1
- package/dist/commands/core-share-suggestion-state-fast.d.ts +2 -0
- package/dist/commands/core-share-suggestion-state-fast.js +25 -0
- package/dist/commands/daemon.js +1 -1
- package/dist/commands/install-global.d.ts +10 -0
- package/dist/commands/install-global.js +96 -41
- package/dist/commands/logout.js +2 -0
- package/dist/commands/mcp-serve.d.ts +28 -0
- package/dist/commands/mcp-serve.js +165 -0
- package/dist/commands/mcp-status.js +14 -0
- package/dist/fast-core.js +3 -6
- package/dist/fast-scaffold.js +5 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +9 -0
- package/dist/lanes-drain-hook.js +2 -2
- package/dist/lib/anywhere-runtime-fast-flag.d.ts +3 -0
- package/dist/lib/anywhere-runtime-fast-flag.js +23 -0
- package/dist/lib/anywhere-runtime-flag-key.d.ts +3 -0
- package/dist/lib/anywhere-runtime-flag-key.js +3 -0
- package/dist/lib/daemon/mcp-endpoint.d.ts +8 -0
- package/dist/lib/daemon/mcp-endpoint.js +30 -0
- package/dist/lib/daemon/mcp-oauth.d.ts +24 -0
- package/dist/lib/daemon/mcp-oauth.js +298 -0
- package/dist/lib/daemon/ops/mcp.d.ts +53 -0
- package/dist/lib/daemon/ops/mcp.js +217 -0
- package/dist/lib/daemon/protocol.d.ts +2 -1
- package/dist/lib/daemon/protocol.js +3 -1
- package/dist/lib/daemon/server.d.ts +5 -0
- package/dist/lib/daemon/server.js +78 -11
- package/dist/lib/install/claude-targets.d.ts +11 -4
- package/dist/lib/install/claude-targets.js +21 -9
- package/dist/lib/install/codex-targets.d.ts +8 -4
- package/dist/lib/install/codex-targets.js +18 -9
- package/dist/lib/lanes/watcher-main.js +2 -4
- package/dist/lib/mcp/hosted-search-tools.d.ts +4 -0
- package/dist/lib/mcp/hosted-search-tools.js +28 -0
- package/dist/lib/mcp/hq-mcp-tools.generated.d.ts +3 -0
- package/dist/lib/mcp/hq-mcp-tools.generated.js +538 -0
- package/dist/lib/mcp/prompts.d.ts +30 -0
- package/dist/lib/mcp/prompts.js +48 -0
- package/dist/lib/mcp/tools.d.ts +22 -0
- package/dist/lib/mcp/tools.js +82 -0
- package/dist/lib/mcp/types.d.ts +6 -0
- package/dist/lib/mcp/types.js +2 -0
- package/dist/lib/process-flag-company-context-fast.d.ts +4 -0
- package/dist/lib/process-flag-company-context-fast.js +20 -0
- package/dist/lib/process-flag-company-context-light.d.ts +9 -0
- package/dist/lib/process-flag-company-context-light.js +81 -0
- package/dist/lib/process-flag-company-context.d.ts +2 -4
- package/dist/lib/process-flag-company-context.js +2 -46
- package/dist/utils/hq-roots-core.d.ts +130 -0
- package/dist/utils/hq-roots-core.js +354 -0
- package/dist/utils/hq-roots.d.ts +2 -129
- package/dist/utils/hq-roots.js +2 -353
- package/dist/utils/is-main-module.d.ts +3 -0
- package/dist/utils/is-main-module.js +19 -0
- package/dist/utils/package-root-diagnostics.d.ts +5 -36
- package/dist/utils/package-root-diagnostics.js +4 -83
- package/dist/utils/package-root-resolution-error.d.ts +32 -0
- package/dist/utils/package-root-resolution-error.js +79 -0
- package/dist/utils/staged-npm-install.js +2 -1
- package/package.json +3 -1
|
@@ -10,13 +10,38 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import * as fs from 'fs';
|
|
12
12
|
import * as path from 'path';
|
|
13
|
-
import {
|
|
13
|
+
import { spawnSync } from 'node:child_process';
|
|
14
|
+
import { buildCodexServerDef, claudeConfigFormat, codexConfigFormat, isCodexInstalled, jsonFormat, mergeCodexServer, registerCodexServer, unregisterCodexServer, mergeClaudeServer, readConfigDoc, realpathOrSelf, registerClaudeServer, resolveEnv, unregisterClaudeServer, writeConfigAtomic, } from './mcp-registration.js';
|
|
14
15
|
import { seedRegistryFromHqRoot } from './link.js';
|
|
15
16
|
import { resolveHqRoot } from './doctor.js';
|
|
16
17
|
import { anywhereRuntimeEnabled, AnywhereRuntimeDisabledError } from './anywhere-runtime-flag.js';
|
|
17
18
|
import { readRegistry, registryDir, registryFile, writeRegistry } from '../lib/registry/registry.js';
|
|
19
|
+
import { DAEMON_LAUNCHD_LABEL, DAEMON_SYSTEMD_UNIT, DAEMON_WINDOWS_TASK, SYSTEM_UNIT_DIR } from '../lib/daemon/install.js';
|
|
20
|
+
import { readDaemonConfig } from '../lib/daemon/config.js';
|
|
21
|
+
import { daemonPaths } from '../lib/daemon/paths.js';
|
|
18
22
|
import { addAgentsMdBlock, addCodexHooks, classifyLegacySkills, codexAdapterPath, codexCharterSource, codexTargets, hasAllCodexHooks, HQ_OWNED_MARKER, LEGACY_BACKUP_NAME, LEGACY_SKILLS_NAME, removeAgentsMdBlock, removeCodexHooks, } from '../lib/install/codex-targets.js';
|
|
19
|
-
import { addClaudeMdBlock, addHqHooks, charterPath, claudeTargets, hasAllHqHooks, HQ_ANYWHERE_PACK, HQ_MCP_SERVER_NAME, hqSkillsSource, lineDiff, masterHookPath, removeClaudeMdBlock, removeHqHooks, SKILL_LINK_PREFIX, } from '../lib/install/claude-targets.js';
|
|
23
|
+
import { addClaudeMdBlock, addHqHooks, charterPath, claudeTargets, hasAllHqHooks, HQ_ANYWHERE_PACK, HQ_MCP_SERVER_NAME, hqSkillsSource, lineDiff, masterHookPath, shimPath, MCP_SUBPROCESS_PATH, removeClaudeMdBlock, removeHqHooks, SKILL_LINK_PREFIX, } from '../lib/install/claude-targets.js';
|
|
24
|
+
/** True when an hq daemon unit (launchd plist or systemd unit) exists. */
|
|
25
|
+
export function isDaemonInstalled(home, platform = process.platform, queryTask = (task) => spawnSync('schtasks.exe', ['/Query', '/TN', task], { encoding: 'utf8', windowsHide: true }).status === 0) {
|
|
26
|
+
if (platform === 'win32')
|
|
27
|
+
return queryTask(DAEMON_WINDOWS_TASK);
|
|
28
|
+
const candidates = platform === 'darwin'
|
|
29
|
+
? [path.join(home, 'Library', 'LaunchAgents', `${DAEMON_LAUNCHD_LABEL}.plist`)]
|
|
30
|
+
: platform === 'linux'
|
|
31
|
+
? [path.join(home, '.config', 'systemd', 'user', DAEMON_SYSTEMD_UNIT), path.join(SYSTEM_UNIT_DIR, DAEMON_SYSTEMD_UNIT)]
|
|
32
|
+
: [];
|
|
33
|
+
return candidates.some((p) => fs.existsSync(p));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* US-010: with hqd installed (and the checkout shipping the shim), hooks go to
|
|
37
|
+
* the hqd shim; otherwise to master-hook.sh / the Codex adapter.
|
|
38
|
+
*/
|
|
39
|
+
export function hookTargetFor(hqRoot, home, daemonInstalled, env = process.env) {
|
|
40
|
+
const installed = daemonInstalled ?? isDaemonInstalled(home);
|
|
41
|
+
const configuredRoot = readDaemonConfig(daemonPaths(home, env).configPath).hqRoot;
|
|
42
|
+
const daemonMatchesRoot = configuredRoot !== undefined && realpathOrSelf(configuredRoot) === realpathOrSelf(hqRoot);
|
|
43
|
+
return installed && daemonMatchesRoot && fs.existsSync(shimPath(hqRoot)) ? 'shim' : 'master';
|
|
44
|
+
}
|
|
20
45
|
export { AnywhereRuntimeDisabledError as AnywhereDisabledError };
|
|
21
46
|
export const isAnywhereEnabled = (_home, env = process.env) => anywhereRuntimeEnabled(env);
|
|
22
47
|
function readOrNull(file) {
|
|
@@ -30,11 +55,10 @@ function readOrNull(file) {
|
|
|
30
55
|
}
|
|
31
56
|
}
|
|
32
57
|
function writeFileAtomic(file, data) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const tmp = `${target}.hq-tmp-${process.pid}`;
|
|
58
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
59
|
+
const tmp = `${file}.hq-tmp-${process.pid}`;
|
|
36
60
|
fs.writeFileSync(tmp, data);
|
|
37
|
-
fs.renameSync(tmp,
|
|
61
|
+
fs.renameSync(tmp, file);
|
|
38
62
|
}
|
|
39
63
|
function canonical(v) {
|
|
40
64
|
return JSON.stringify(v, (_k, val) => val && typeof val === 'object' && !Array.isArray(val)
|
|
@@ -42,12 +66,12 @@ function canonical(v) {
|
|
|
42
66
|
: val);
|
|
43
67
|
}
|
|
44
68
|
/** JSON settings format whose verify step checks every HQ hook is registered. */
|
|
45
|
-
function settingsInstallFormat(hqRoot) {
|
|
69
|
+
function settingsInstallFormat(hqRoot, target) {
|
|
46
70
|
return {
|
|
47
71
|
...jsonFormat,
|
|
48
72
|
emptyDoc: () => ({}),
|
|
49
73
|
assertEntry(doc) {
|
|
50
|
-
if (!hasAllHqHooks(doc, hqRoot))
|
|
74
|
+
if (!hasAllHqHooks(doc, hqRoot, target))
|
|
51
75
|
throw new Error('HQ hooks not present after write');
|
|
52
76
|
},
|
|
53
77
|
};
|
|
@@ -84,9 +108,18 @@ function restoringRemovalFormat(original, stillPresent) {
|
|
|
84
108
|
},
|
|
85
109
|
};
|
|
86
110
|
}
|
|
87
|
-
function
|
|
111
|
+
function mcpManifest(opts) {
|
|
112
|
+
const command = opts.nodePath ?? process.execPath;
|
|
113
|
+
const entry = opts.cliEntry ?? fs.realpathSync(process.argv[1] ?? '');
|
|
114
|
+
for (const p of [command, entry]) {
|
|
115
|
+
if (!path.isAbsolute(p))
|
|
116
|
+
throw new Error(`HQ MCP server path must be absolute: ${p}`);
|
|
117
|
+
}
|
|
118
|
+
return { type: 'stdio', command, args: [entry, 'mcp', 'serve'], env: { PATH: MCP_SUBPROCESS_PATH } };
|
|
119
|
+
}
|
|
120
|
+
function planSkills(hqRoot, t) {
|
|
88
121
|
const source = hqSkillsSource(hqRoot);
|
|
89
|
-
const plan = { link: [],
|
|
122
|
+
const plan = { link: [], already: [], skipped: [] };
|
|
90
123
|
let names;
|
|
91
124
|
try {
|
|
92
125
|
names = fs
|
|
@@ -115,10 +148,6 @@ function planSkills(hqRoot, t, previouslyManaged = []) {
|
|
|
115
148
|
plan.link.push(name);
|
|
116
149
|
else if (st.isSymbolicLink() && path.resolve(t.skillsDir, fs.readlinkSync(link)) === target)
|
|
117
150
|
plan.already.push(name);
|
|
118
|
-
else if (st.isSymbolicLink() && previouslyManaged.includes(name)) {
|
|
119
|
-
plan.link.push(name);
|
|
120
|
-
plan.replace.push(name);
|
|
121
|
-
}
|
|
122
151
|
else
|
|
123
152
|
plan.skipped.push(name);
|
|
124
153
|
}
|
|
@@ -164,15 +193,23 @@ export function installGlobalClaude(opts) {
|
|
|
164
193
|
const t = claudeTargets(home);
|
|
165
194
|
const prior = readState(t);
|
|
166
195
|
const regFile = opts.registryFile ?? registryFile(registryDir(home, env));
|
|
196
|
+
const manifest = mcpManifest(opts);
|
|
167
197
|
const touched = [];
|
|
168
198
|
const pointerOld = readOrNull(t.hqPointer);
|
|
169
199
|
const pointerNew = `${hqRoot}\n`;
|
|
170
200
|
const mdOld = readOrNull(t.claudeMd);
|
|
171
201
|
const mdNew = addClaudeMdBlock(mdOld, hqRoot);
|
|
172
|
-
const skills = planSkills(hqRoot, t
|
|
202
|
+
const skills = planSkills(hqRoot, t);
|
|
173
203
|
const settingsOld = readOrNull(t.settingsJson);
|
|
174
204
|
const settingsDoc = readConfigDoc(t.settingsJson, jsonFormat).doc;
|
|
175
|
-
const
|
|
205
|
+
const hookTarget = hookTargetFor(hqRoot, home, opts.daemonInstalled, env);
|
|
206
|
+
const settingsNext = addHqHooks(settingsDoc, hqRoot, hookTarget);
|
|
207
|
+
const claudeDoc = readConfigDoc(t.claudeJson, claudeConfigFormat).doc;
|
|
208
|
+
// Collision check runs here too, so a dry run surfaces a conflicting "hq" server.
|
|
209
|
+
const claudeNext = mergeClaudeServer(HQ_MCP_SERVER_NAME, {
|
|
210
|
+
...manifest,
|
|
211
|
+
_hqPack: HQ_ANYWHERE_PACK,
|
|
212
|
+
})(claudeDoc);
|
|
176
213
|
if (pointerOld !== pointerNew)
|
|
177
214
|
touched.push(t.hqPointer);
|
|
178
215
|
if (mdOld !== mdNew)
|
|
@@ -181,6 +218,8 @@ export function installGlobalClaude(opts) {
|
|
|
181
218
|
touched.push(path.join(t.skillsDir, `${SKILL_LINK_PREFIX}${s}`));
|
|
182
219
|
if (settingsNext !== settingsDoc)
|
|
183
220
|
touched.push(t.settingsJson);
|
|
221
|
+
if (claudeNext !== claudeDoc)
|
|
222
|
+
touched.push(t.claudeJson);
|
|
184
223
|
touched.push(regFile, t.stateFile);
|
|
185
224
|
if (opts.dryRun) {
|
|
186
225
|
log(`hq install --global --runtime claude --dry-run (HQ root ${hqRoot})`);
|
|
@@ -192,29 +231,33 @@ export function installGlobalClaude(opts) {
|
|
|
192
231
|
}
|
|
193
232
|
log(`\n--- ${t.settingsJson}`);
|
|
194
233
|
log(lineDiff(jsonFormat.serialize(settingsDoc), jsonFormat.serialize(settingsNext)));
|
|
234
|
+
log(`\n--- ${t.claudeJson}`);
|
|
235
|
+
log(lineDiff(jsonFormat.serialize(claudeDoc), jsonFormat.serialize(claudeNext)));
|
|
195
236
|
return touched;
|
|
196
237
|
}
|
|
197
238
|
writeFileAtomic(t.hqPointer, pointerNew);
|
|
198
239
|
writeFileAtomic(t.claudeMd, mdNew);
|
|
199
240
|
fs.mkdirSync(t.skillsDir, { recursive: true });
|
|
200
|
-
for (const name of skills.replace)
|
|
201
|
-
fs.unlinkSync(path.join(t.skillsDir, `${SKILL_LINK_PREFIX}${name}`));
|
|
202
241
|
for (const name of skills.link) {
|
|
203
242
|
fs.symlinkSync(path.join(hqSkillsSource(hqRoot), name), path.join(t.skillsDir, `${SKILL_LINK_PREFIX}${name}`));
|
|
204
243
|
}
|
|
205
244
|
writeConfigAtomic({
|
|
206
245
|
target: t.settingsJson,
|
|
207
246
|
pack: HQ_ANYWHERE_PACK,
|
|
208
|
-
format: settingsInstallFormat(hqRoot),
|
|
209
|
-
merge: (doc) => addHqHooks(doc, hqRoot),
|
|
247
|
+
format: settingsInstallFormat(hqRoot, hookTarget),
|
|
248
|
+
merge: (doc) => addHqHooks(doc, hqRoot, hookTarget),
|
|
210
249
|
assertName: 'hooks',
|
|
211
250
|
env: { home },
|
|
212
251
|
});
|
|
252
|
+
registerClaudeServer({ name: HQ_MCP_SERVER_NAME, manifest, pack: HQ_ANYWHERE_PACK, env: { home } });
|
|
213
253
|
// Seeding is best-effort: the runtime wiring above is already in place, so a
|
|
214
254
|
// missing or unreadable manifest is reported rather than aborting the install.
|
|
215
255
|
let seeded = [];
|
|
256
|
+
let ownedSeeded = [];
|
|
216
257
|
try {
|
|
217
|
-
|
|
258
|
+
const result = seedRegistryFromHqRoot(hqRoot, { file: regFile });
|
|
259
|
+
seeded = result.added;
|
|
260
|
+
ownedSeeded = result.owned;
|
|
218
261
|
}
|
|
219
262
|
catch (e) {
|
|
220
263
|
log(`Registry not seeded from companies/manifest.yaml: ${e.message}`);
|
|
@@ -231,13 +274,13 @@ export function installGlobalClaude(opts) {
|
|
|
231
274
|
skipped: skills.skipped,
|
|
232
275
|
},
|
|
233
276
|
mcp: { name: HQ_MCP_SERVER_NAME, pack: HQ_ANYWHERE_PACK },
|
|
234
|
-
seeded: [...new Set([...(prior?.seeded ?? []), ...
|
|
277
|
+
seeded: [...new Set([...(prior?.seeded ?? []), ...ownedSeeded])].sort(),
|
|
235
278
|
};
|
|
236
279
|
writeFileAtomic(t.stateFile, `${JSON.stringify(state, null, 2)}\n`);
|
|
237
280
|
log(`HQ installed for Claude Code from ${hqRoot}.`);
|
|
238
281
|
log(` skills linked: ${skills.link.length} new, ${skills.already.length} already present, ${skills.skipped.length} skipped`);
|
|
239
|
-
log(` hooks: master-hook.sh registered in ${t.settingsJson}`);
|
|
240
|
-
log(
|
|
282
|
+
log(` hooks: ${hookTarget === 'shim' ? 'hqd-hook-shim.sh' : 'master-hook.sh'} registered in ${t.settingsJson}`);
|
|
283
|
+
log(` MCP: "${HQ_MCP_SERVER_NAME}" registered in ${t.claudeJson}`);
|
|
241
284
|
log(` registry: ${seeded.length} manifest entr${seeded.length === 1 ? 'y' : 'ies'} seeded`);
|
|
242
285
|
return touched;
|
|
243
286
|
}
|
|
@@ -367,12 +410,12 @@ function assertCodexHqRoot(hqRoot) {
|
|
|
367
410
|
throw new Error(`${hqRoot} is not an HQ checkout (missing ${p})`);
|
|
368
411
|
}
|
|
369
412
|
}
|
|
370
|
-
function hooksInstallFormat(hqRoot) {
|
|
413
|
+
function hooksInstallFormat(hqRoot, target) {
|
|
371
414
|
return {
|
|
372
415
|
...jsonFormat,
|
|
373
416
|
emptyDoc: () => ({}),
|
|
374
417
|
assertEntry(doc) {
|
|
375
|
-
if (!hasAllCodexHooks(doc, hqRoot))
|
|
418
|
+
if (!hasAllCodexHooks(doc, hqRoot, target))
|
|
376
419
|
throw new Error('HQ Codex hooks not present after write');
|
|
377
420
|
},
|
|
378
421
|
};
|
|
@@ -399,6 +442,7 @@ export function installGlobalCodex(opts) {
|
|
|
399
442
|
const hqPointer = claudeTargets(home).hqPointer;
|
|
400
443
|
const prior = readCodexState(t);
|
|
401
444
|
const regFile = opts.registryFile ?? registryFile(registryDir(home, env));
|
|
445
|
+
const manifest = mcpManifest(opts);
|
|
402
446
|
const touched = [];
|
|
403
447
|
const pointerOld = readOrNull(hqPointer);
|
|
404
448
|
const pointerNew = `${hqRoot}\n`;
|
|
@@ -406,11 +450,20 @@ export function installGlobalCodex(opts) {
|
|
|
406
450
|
const mdOld = readOrNull(t.agentsMd);
|
|
407
451
|
const mdNew = addAgentsMdBlock(mdOld, charter);
|
|
408
452
|
const legacy = classifyLegacySkills(t.skillsDir, hqRoot);
|
|
409
|
-
const
|
|
453
|
+
const legacyPath = path.join(t.skillsDir, LEGACY_SKILLS_NAME);
|
|
454
|
+
const legacyBackupPath = path.join(t.skillsDir, LEGACY_BACKUP_NAME);
|
|
455
|
+
if (legacy.kind === 'hq-dir' && fs.existsSync(legacyBackupPath)) {
|
|
456
|
+
throw new Error(`${legacyBackupPath} already exists; move it away before installing.`);
|
|
457
|
+
}
|
|
458
|
+
const skills = planSkills(hqRoot, t);
|
|
410
459
|
const hooksOld = readOrNull(t.hooksJson);
|
|
411
|
-
const tomlOld = readOrNull(t.configToml);
|
|
412
460
|
const hooksDoc = readConfigDoc(t.hooksJson, jsonFormat).doc;
|
|
413
|
-
const
|
|
461
|
+
const hookTarget = hookTargetFor(hqRoot, home, opts.daemonInstalled, env);
|
|
462
|
+
const hooksNext = addCodexHooks(hooksDoc, hqRoot, hookTarget);
|
|
463
|
+
const tomlOld = readOrNull(t.configToml);
|
|
464
|
+
const tomlDoc = readConfigDoc(t.configToml, codexConfigFormat).doc;
|
|
465
|
+
// Collision check runs here too, so a dry run surfaces a conflicting "hq" server.
|
|
466
|
+
const tomlNext = mergeCodexServer(HQ_MCP_SERVER_NAME, buildCodexServerDef(manifest, HQ_ANYWHERE_PACK, () => null, new Set()))(tomlDoc);
|
|
414
467
|
if (pointerOld !== pointerNew)
|
|
415
468
|
touched.push(hqPointer);
|
|
416
469
|
if (mdOld !== mdNew)
|
|
@@ -421,6 +474,8 @@ export function installGlobalCodex(opts) {
|
|
|
421
474
|
touched.push(path.join(t.skillsDir, `${SKILL_LINK_PREFIX}${s}`));
|
|
422
475
|
if (hooksNext !== hooksDoc)
|
|
423
476
|
touched.push(t.hooksJson);
|
|
477
|
+
if (tomlNext !== tomlDoc)
|
|
478
|
+
touched.push(t.configToml);
|
|
424
479
|
touched.push(regFile, t.stateFile);
|
|
425
480
|
if (opts.dryRun) {
|
|
426
481
|
log(`hq install --global --runtime codex --dry-run (HQ root ${hqRoot})`);
|
|
@@ -437,16 +492,14 @@ export function installGlobalCodex(opts) {
|
|
|
437
492
|
}
|
|
438
493
|
log(`\n--- ${t.hooksJson}`);
|
|
439
494
|
log(lineDiff(jsonFormat.serialize(hooksDoc), jsonFormat.serialize(hooksNext)));
|
|
495
|
+
log(`\n--- ${t.configToml}`);
|
|
496
|
+
log(lineDiff(codexConfigFormat.serialize(tomlDoc), codexConfigFormat.serialize(tomlNext)));
|
|
440
497
|
return touched;
|
|
441
498
|
}
|
|
442
|
-
if (legacy.kind === 'hq-dir' && fs.existsSync(path.join(t.skillsDir, LEGACY_BACKUP_NAME))) {
|
|
443
|
-
throw new Error(`${path.join(t.skillsDir, LEGACY_BACKUP_NAME)} already exists; move it away before installing.`);
|
|
444
|
-
}
|
|
445
499
|
writeFileAtomic(hqPointer, pointerNew);
|
|
446
500
|
writeFileAtomic(t.agentsMd, mdNew);
|
|
447
501
|
// Legacy mirror: replace only when HQ-owned; record enough to put it back.
|
|
448
502
|
let legacyRecord = prior?.legacy ?? { kind: 'none' };
|
|
449
|
-
const legacyPath = path.join(t.skillsDir, LEGACY_SKILLS_NAME);
|
|
450
503
|
if (legacy.kind === 'hq-symlink') {
|
|
451
504
|
fs.unlinkSync(legacyPath);
|
|
452
505
|
legacyRecord = { kind: 'symlink', target: legacy.target };
|
|
@@ -459,22 +512,24 @@ export function installGlobalCodex(opts) {
|
|
|
459
512
|
else if (legacy.kind === 'foreign')
|
|
460
513
|
log(legacyWarning(t.skillsDir));
|
|
461
514
|
fs.mkdirSync(t.skillsDir, { recursive: true });
|
|
462
|
-
for (const name of skills.replace)
|
|
463
|
-
fs.unlinkSync(path.join(t.skillsDir, `${SKILL_LINK_PREFIX}${name}`));
|
|
464
515
|
for (const name of skills.link) {
|
|
465
516
|
fs.symlinkSync(path.join(hqSkillsSource(hqRoot), name), path.join(t.skillsDir, `${SKILL_LINK_PREFIX}${name}`));
|
|
466
517
|
}
|
|
467
518
|
writeConfigAtomic({
|
|
468
519
|
target: t.hooksJson,
|
|
469
520
|
pack: HQ_ANYWHERE_PACK,
|
|
470
|
-
format: hooksInstallFormat(hqRoot),
|
|
471
|
-
merge: (doc) => addCodexHooks(doc, hqRoot),
|
|
521
|
+
format: hooksInstallFormat(hqRoot, hookTarget),
|
|
522
|
+
merge: (doc) => addCodexHooks(doc, hqRoot, hookTarget),
|
|
472
523
|
assertName: 'hooks',
|
|
473
524
|
env: { home },
|
|
474
525
|
});
|
|
526
|
+
registerCodexServer({ name: HQ_MCP_SERVER_NAME, manifest, pack: HQ_ANYWHERE_PACK, env: { home } });
|
|
475
527
|
let seeded = [];
|
|
528
|
+
let ownedSeeded = [];
|
|
476
529
|
try {
|
|
477
|
-
|
|
530
|
+
const result = seedRegistryFromHqRoot(hqRoot, { file: regFile });
|
|
531
|
+
seeded = result.added;
|
|
532
|
+
ownedSeeded = result.owned;
|
|
478
533
|
}
|
|
479
534
|
catch (e) {
|
|
480
535
|
log(`Registry not seeded from companies/manifest.yaml: ${e.message}`);
|
|
@@ -492,13 +547,13 @@ export function installGlobalCodex(opts) {
|
|
|
492
547
|
},
|
|
493
548
|
legacy: legacyRecord,
|
|
494
549
|
mcp: { name: HQ_MCP_SERVER_NAME, pack: HQ_ANYWHERE_PACK },
|
|
495
|
-
seeded: [...new Set([...(prior?.seeded ?? []), ...
|
|
550
|
+
seeded: [...new Set([...(prior?.seeded ?? []), ...ownedSeeded])].sort(),
|
|
496
551
|
};
|
|
497
552
|
writeFileAtomic(t.stateFile, `${JSON.stringify(state, null, 2)}\n`);
|
|
498
553
|
log(`HQ installed for Codex from ${hqRoot}.`);
|
|
499
554
|
log(` skills linked: ${skills.link.length} new, ${skills.already.length} already present, ${skills.skipped.length} skipped`);
|
|
500
|
-
log(` hooks: ${codexAdapterPath(hqRoot)} registered in ${t.hooksJson}`);
|
|
501
|
-
log(
|
|
555
|
+
log(` hooks: ${hookTarget === 'shim' ? shimPath(hqRoot) : codexAdapterPath(hqRoot)} registered in ${t.hooksJson}`);
|
|
556
|
+
log(` MCP: "${HQ_MCP_SERVER_NAME}" registered in ${t.configToml}`);
|
|
502
557
|
return touched;
|
|
503
558
|
}
|
|
504
559
|
/** Reverse every Codex install step; non-HQ entries are left untouched. */
|
package/dist/commands/logout.js
CHANGED
|
@@ -9,6 +9,8 @@ export function registerLogoutCommand(program) {
|
|
|
9
9
|
.description('Log out and clear cached credentials')
|
|
10
10
|
.action(async () => {
|
|
11
11
|
try {
|
|
12
|
+
const { clearMcpOAuthCaches } = await import('../lib/daemon/mcp-oauth.js');
|
|
13
|
+
clearMcpOAuthCaches();
|
|
12
14
|
const existing = loadCachedTokens();
|
|
13
15
|
if (!existing) {
|
|
14
16
|
console.log(chalk.yellow('No cached HQ session'));
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `hq mcp serve` (hq-anywhere US-011): a stdio MCP server that forwards every
|
|
3
|
+
* tool call to hqd over its socket.
|
|
4
|
+
*
|
|
5
|
+
* It keeps no state between requests, so an orphaned instance costs nothing
|
|
6
|
+
* (policy mcp-process-cleanup). src/index.ts routes `hq mcp serve` straight to
|
|
7
|
+
* {@link runMcpServe} without loading the command graph, which keeps start-up
|
|
8
|
+
* under 100ms. The MCP SDK is not loaded either: the stdio transport is
|
|
9
|
+
* newline-delimited JSON-RPC 2.0 and is handled here directly.
|
|
10
|
+
*
|
|
11
|
+
* When hqd is unreachable every tool call returns an isError result naming
|
|
12
|
+
* `hq daemon status`, and the server keeps running.
|
|
13
|
+
*/
|
|
14
|
+
export declare const MCP_PROTOCOL_VERSIONS: readonly ["2025-06-18", "2025-03-26", "2024-11-05"];
|
|
15
|
+
export interface McpServeOptions {
|
|
16
|
+
input?: NodeJS.ReadableStream;
|
|
17
|
+
output?: NodeJS.WritableStream;
|
|
18
|
+
env?: NodeJS.ProcessEnv;
|
|
19
|
+
home?: string;
|
|
20
|
+
cwd?: string;
|
|
21
|
+
/** Per-call hqd budget. Default 120s; policy checks and hq-mcp tools can be slow. */
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
}
|
|
24
|
+
/** HQ root for prompts: HQ_ROOT, else the ~/.hq/root pointer written by `hq install --global`. */
|
|
25
|
+
export declare function readHqRoot(env: NodeJS.ProcessEnv, home: string): string | null;
|
|
26
|
+
/** Run the server until input ends. Resolves when stdin closes. */
|
|
27
|
+
export declare function runMcpServe(opts?: McpServeOptions): Promise<void>;
|
|
28
|
+
//# sourceMappingURL=mcp-serve.d.ts.map
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `hq mcp serve` (hq-anywhere US-011): a stdio MCP server that forwards every
|
|
3
|
+
* tool call to hqd over its socket.
|
|
4
|
+
*
|
|
5
|
+
* It keeps no state between requests, so an orphaned instance costs nothing
|
|
6
|
+
* (policy mcp-process-cleanup). src/index.ts routes `hq mcp serve` straight to
|
|
7
|
+
* {@link runMcpServe} without loading the command graph, which keeps start-up
|
|
8
|
+
* under 100ms. The MCP SDK is not loaded either: the stdio transport is
|
|
9
|
+
* newline-delimited JSON-RPC 2.0 and is handled here directly.
|
|
10
|
+
*
|
|
11
|
+
* When hqd is unreachable every tool call returns an isError result naming
|
|
12
|
+
* `hq daemon status`, and the server keeps running.
|
|
13
|
+
*/
|
|
14
|
+
import * as fs from "node:fs";
|
|
15
|
+
import * as os from "node:os";
|
|
16
|
+
import * as path from "node:path";
|
|
17
|
+
import { CLI_VERSION } from "../cli-version.js";
|
|
18
|
+
import { HQ_ANYWHERE_RUNTIME_FLAG } from "./anywhere-runtime-flag.js";
|
|
19
|
+
import { anywhereRuntimeEnabledFromSnapshot } from "../lib/anywhere-runtime-fast-flag.js";
|
|
20
|
+
import { createLineSplitter, HqdClientError, hqdRequest, hqdSocketPath } from "../lib/daemon/protocol.js";
|
|
21
|
+
export const MCP_PROTOCOL_VERSIONS = ["2025-06-18", "2025-03-26", "2024-11-05"];
|
|
22
|
+
const UNREACHABLE_CODES = new Set(["ENOENT", "ECONNREFUSED", "ECONNRESET", "EPIPE", "ETIMEDOUT", "ESOCKET", "EACCES"]);
|
|
23
|
+
/** HQ root for prompts: HQ_ROOT, else the ~/.hq/root pointer written by `hq install --global`. */
|
|
24
|
+
export function readHqRoot(env, home) {
|
|
25
|
+
const fromEnv = env.HQ_ROOT?.trim();
|
|
26
|
+
if (fromEnv)
|
|
27
|
+
return fromEnv;
|
|
28
|
+
const dir = env.HQ_REGISTRY_DIR?.trim() ? path.resolve(env.HQ_REGISTRY_DIR.trim()) : path.join(home, ".hq");
|
|
29
|
+
try {
|
|
30
|
+
return fs.readFileSync(path.join(dir, "root"), "utf8").trim() || null;
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
if (err.code === "ENOENT")
|
|
34
|
+
return null;
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function toolError(code, message) {
|
|
39
|
+
const error = { code, message };
|
|
40
|
+
return { content: [{ type: "text", text: JSON.stringify({ error }, null, 2) }], structuredContent: { error }, isError: true };
|
|
41
|
+
}
|
|
42
|
+
/** Run the server until input ends. Resolves when stdin closes. */
|
|
43
|
+
export function runMcpServe(opts = {}) {
|
|
44
|
+
const input = opts.input ?? process.stdin;
|
|
45
|
+
const output = opts.output ?? process.stdout;
|
|
46
|
+
const env = opts.env ?? process.env;
|
|
47
|
+
const home = opts.home ?? os.homedir();
|
|
48
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
49
|
+
const socketPath = hqdSocketPath(home, env);
|
|
50
|
+
const timeoutMs = opts.timeoutMs ?? 120_000;
|
|
51
|
+
if (!anywhereRuntimeEnabledFromSnapshot(env)) {
|
|
52
|
+
process.stderr.write(`HQ Anywhere is not enabled. Enable the ${HQ_ANYWHERE_RUNTIME_FLAG} flag to use hq mcp serve.\n`);
|
|
53
|
+
process.exitCode = 1;
|
|
54
|
+
return Promise.resolve();
|
|
55
|
+
}
|
|
56
|
+
const send = (msg) => output.write(`${JSON.stringify({ jsonrpc: "2.0", ...msg })}\n`);
|
|
57
|
+
const reply = (id, result) => send({ id, result });
|
|
58
|
+
const replyError = (id, code, message) => send({ id, error: { code, message } });
|
|
59
|
+
const callTool = async (params) => {
|
|
60
|
+
const { MCP_TOOLS } = await import("../lib/mcp/tools.js");
|
|
61
|
+
const name = params.name;
|
|
62
|
+
if (typeof name !== "string" || !MCP_TOOLS.some((t) => t.name === name)) {
|
|
63
|
+
return toolError("unknown_tool", `unknown tool ${String(name)}`);
|
|
64
|
+
}
|
|
65
|
+
const args = params.arguments && typeof params.arguments === "object" ? params.arguments : {};
|
|
66
|
+
try {
|
|
67
|
+
return await hqdRequest("mcp.call", { name, arguments: args, cwd, timeoutMs }, { socketPath, timeoutMs });
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
if (!(err instanceof HqdClientError))
|
|
71
|
+
throw err;
|
|
72
|
+
if (UNREACHABLE_CODES.has(err.code)) {
|
|
73
|
+
return toolError("hqd_unreachable", `The HQ daemon (hqd) is not reachable at ${socketPath}. Run \`hq daemon status\` to check it, and \`hq daemon install\` if it is not installed. (${err.code})`);
|
|
74
|
+
}
|
|
75
|
+
return toolError(err.code, err.message);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const handle = async (msg) => {
|
|
79
|
+
const isRequest = msg.id !== undefined && msg.id !== null;
|
|
80
|
+
const id = msg.id ?? null;
|
|
81
|
+
const params = msg.params ?? {};
|
|
82
|
+
switch (msg.method) {
|
|
83
|
+
case "initialize": {
|
|
84
|
+
const requested = typeof params.protocolVersion === "string" ? params.protocolVersion : "";
|
|
85
|
+
const protocolVersion = MCP_PROTOCOL_VERSIONS.includes(requested) ? requested : MCP_PROTOCOL_VERSIONS[0];
|
|
86
|
+
reply(id, {
|
|
87
|
+
protocolVersion,
|
|
88
|
+
capabilities: { tools: { listChanged: false }, prompts: { listChanged: false } },
|
|
89
|
+
serverInfo: { name: "hq", version: CLI_VERSION },
|
|
90
|
+
instructions: "HQ tools, answered by the local HQ daemon. hq_resolve_company tells you which company the current repo belongs to; call it before company-scoped tools. If a tool returns hqd_unreachable, ask the user to run `hq daemon status`.",
|
|
91
|
+
});
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
case "ping":
|
|
95
|
+
reply(id, {});
|
|
96
|
+
return;
|
|
97
|
+
case "tools/list":
|
|
98
|
+
reply(id, { tools: (await import("../lib/mcp/tools.js")).MCP_TOOLS });
|
|
99
|
+
return;
|
|
100
|
+
case "tools/call":
|
|
101
|
+
reply(id, await callTool(params));
|
|
102
|
+
return;
|
|
103
|
+
case "prompts/list":
|
|
104
|
+
reply(id, { prompts: (await import("../lib/mcp/prompts.js")).MCP_PROMPTS });
|
|
105
|
+
return;
|
|
106
|
+
case "prompts/get": {
|
|
107
|
+
const { getPrompt, PromptNotFoundError } = await import("../lib/mcp/prompts.js");
|
|
108
|
+
const args = params.arguments && typeof params.arguments === "object" ? params.arguments : {};
|
|
109
|
+
try {
|
|
110
|
+
reply(id, getPrompt(String(params.name), args, readHqRoot(env, home)));
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
if (!(err instanceof PromptNotFoundError))
|
|
114
|
+
throw err;
|
|
115
|
+
replyError(id, -32602, err.message);
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
default:
|
|
120
|
+
// Notifications (initialized, cancelled, …) need no answer.
|
|
121
|
+
if (isRequest)
|
|
122
|
+
replyError(id, -32601, `method not found: ${String(msg.method)}`);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
return new Promise((resolve) => {
|
|
126
|
+
let inFlight = 0;
|
|
127
|
+
let ended = false;
|
|
128
|
+
const maybeDone = () => {
|
|
129
|
+
if (ended && inFlight === 0)
|
|
130
|
+
resolve();
|
|
131
|
+
};
|
|
132
|
+
const push = createLineSplitter((line) => {
|
|
133
|
+
let msg;
|
|
134
|
+
try {
|
|
135
|
+
msg = JSON.parse(line);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
replyError(null, -32700, "parse error");
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (!msg || typeof msg !== "object" || Array.isArray(msg)) {
|
|
142
|
+
replyError(null, -32600, "invalid request");
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
inFlight += 1;
|
|
146
|
+
handle(msg)
|
|
147
|
+
.catch((err) => {
|
|
148
|
+
if (msg.id !== undefined && msg.id !== null)
|
|
149
|
+
replyError(msg.id, -32603, err instanceof Error ? err.message : String(err));
|
|
150
|
+
else
|
|
151
|
+
process.stderr.write(`hq mcp serve: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
152
|
+
})
|
|
153
|
+
.finally(() => {
|
|
154
|
+
inFlight -= 1;
|
|
155
|
+
maybeDone();
|
|
156
|
+
});
|
|
157
|
+
}, () => replyError(null, -32600, "request line too large"), 16 * 1024 * 1024);
|
|
158
|
+
input.on("data", push);
|
|
159
|
+
input.on("end", () => {
|
|
160
|
+
ended = true;
|
|
161
|
+
maybeDone();
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=mcp-serve.js.map
|
|
@@ -389,6 +389,20 @@ export function registerMcpCommand(program) {
|
|
|
389
389
|
const mcp = program
|
|
390
390
|
.command('mcp')
|
|
391
391
|
.description('MCP pack observability (read-only) across Claude + Codex');
|
|
392
|
+
mcp
|
|
393
|
+
.command('serve')
|
|
394
|
+
.description('Run the HQ stdio MCP server; every tool call is answered by hqd (`hq daemon status`)')
|
|
395
|
+
.action(async () => {
|
|
396
|
+
const { runMcpServe } = await import('./mcp-serve.js');
|
|
397
|
+
await runMcpServe();
|
|
398
|
+
});
|
|
399
|
+
mcp
|
|
400
|
+
.command('login')
|
|
401
|
+
.description('Sign in to the hosted HQ MCP connector with browser OAuth')
|
|
402
|
+
.action(async () => {
|
|
403
|
+
const { loginHostedMcp } = await import('../lib/daemon/mcp-oauth.js');
|
|
404
|
+
await loginHostedMcp();
|
|
405
|
+
});
|
|
392
406
|
mcp
|
|
393
407
|
.command('status')
|
|
394
408
|
.alias('list')
|
package/dist/fast-core.js
CHANGED
|
@@ -39,11 +39,8 @@ export async function runFastCore(argv, parsed = parseFastCore(argv)) {
|
|
|
39
39
|
const useShell = entry.kind === "native" && Boolean(entry.familyFlagKey) && !familyEnabled;
|
|
40
40
|
let worktreeScopeFixEnabled;
|
|
41
41
|
if (entry.name === "repo-run-registry") {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
import("./native-fast-route.js"),
|
|
45
|
-
]);
|
|
46
|
-
worktreeScopeFixEnabled = await fastFlagEnabled(CORE_RRR_WORKTREE_SCOPE_FIX_FLAG, process.env, { numericEnvironmentOverride: false });
|
|
42
|
+
const { coreRrrWorktreeScopeFixFastFlagEnabled } = await import("./commands/core-rrr-worktree-scope-fix-fast-flag.js");
|
|
43
|
+
worktreeScopeFixEnabled = await coreRrrWorktreeScopeFixFastFlagEnabled(process.env);
|
|
47
44
|
}
|
|
48
45
|
const shouldInjectRoot = useShell
|
|
49
46
|
? entry.shellHqRoot === "live" || (entry.shellHqRoot === "reconcile-default" &&
|
|
@@ -57,7 +54,7 @@ export async function runFastCore(argv, parsed = parseFastCore(argv)) {
|
|
|
57
54
|
// its expected-user-error formatting remains authoritative.
|
|
58
55
|
let hqRoot;
|
|
59
56
|
if (shouldInjectRoot) {
|
|
60
|
-
const { resolveLiveRoot } = await import("./utils/hq-roots.js");
|
|
57
|
+
const { resolveLiveRoot } = await import("./utils/hq-roots-core.js");
|
|
61
58
|
try {
|
|
62
59
|
hqRoot = resolveLiveRoot(parsed.hqRootArg ? { hqRoot: parsed.hqRootArg } : {});
|
|
63
60
|
}
|
package/dist/fast-scaffold.js
CHANGED
|
@@ -175,7 +175,11 @@ export const FAST_SCAFFOLD_SCRIPTS = [
|
|
|
175
175
|
...["share-suggestion-state", "repo-run-registry", "detect-stale-review-base"].map((name) => ({
|
|
176
176
|
name,
|
|
177
177
|
kind: "native",
|
|
178
|
-
modulePath: "
|
|
178
|
+
modulePath: name === "share-suggestion-state"
|
|
179
|
+
? "./commands/core-share-suggestion-state-fast.js"
|
|
180
|
+
: name === "repo-run-registry"
|
|
181
|
+
? "./commands/core-repo-run-registry-fast.js"
|
|
182
|
+
: "./commands/core-detect-stale-review-base-fast.js",
|
|
179
183
|
exportName: name === "share-suggestion-state"
|
|
180
184
|
? "runShareSuggestionStateFast"
|
|
181
185
|
: name === "repo-run-registry"
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,9 @@ export declare const __test__: {
|
|
|
14
14
|
runCliEntry: typeof runCliEntry;
|
|
15
15
|
isNpmPackageEntry: typeof isNpmPackageEntry;
|
|
16
16
|
acquirePackageUseLease: (prefix: string, version: string, stateDirectory?: string) => Promise<import("./utils/package-use-lease.js").PackageUseLease>;
|
|
17
|
+
isMcpServeRequest: typeof isMcpServeRequest;
|
|
17
18
|
};
|
|
19
|
+
/** Exactly `hq mcp serve` with no further arguments; `--help` uses the command graph. */
|
|
20
|
+
declare function isMcpServeRequest(argv: readonly string[]): boolean;
|
|
18
21
|
export {};
|
|
19
22
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -77,6 +77,10 @@ function dispatchCliEntry() {
|
|
|
77
77
|
});
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
|
+
else if (isMcpServeRequest(process.argv)) {
|
|
81
|
+
// Keep the minimal startup route; runMcpServe applies the hq-flags gate.
|
|
82
|
+
void import("./commands/mcp-serve.js").then(({ runMcpServe }) => runMcpServe());
|
|
83
|
+
}
|
|
80
84
|
else if (parsedFastCore) {
|
|
81
85
|
// Hot relocated plumbing (`hq core hq-session`, `hq core checkpoint-stop-gate`)
|
|
82
86
|
// dispatches straight to the bundled-script runner, so it never pays for the
|
|
@@ -163,5 +167,10 @@ export const __test__ = {
|
|
|
163
167
|
const lease = await import("./utils/package-use-lease.js");
|
|
164
168
|
return lease.acquirePackageUseLease(prefix, version, stateDirectory);
|
|
165
169
|
},
|
|
170
|
+
isMcpServeRequest,
|
|
166
171
|
};
|
|
172
|
+
/** Exactly `hq mcp serve` with no further arguments; `--help` uses the command graph. */
|
|
173
|
+
function isMcpServeRequest(argv) {
|
|
174
|
+
return argv.length === 4 && argv[2] === "mcp" && argv[3] === "serve";
|
|
175
|
+
}
|
|
167
176
|
//# sourceMappingURL=index.js.map
|
package/dist/lanes-drain-hook.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
4
|
import { MESSAGE_REPLAY_STATE_NAME, readMessageReplayState, updateMessageReplayTranscript, writeMessageReplayState, } from "./lib/lanes/message-replay.js";
|
|
5
|
+
import { isMainModule } from "./utils/is-main-module.js";
|
|
6
6
|
function parseHookInput(text) {
|
|
7
7
|
if (text.trim() === "")
|
|
8
8
|
return {};
|
|
@@ -193,7 +193,7 @@ async function main() {
|
|
|
193
193
|
}
|
|
194
194
|
finish(await runLaneDrainHook(process.env, input, inputError));
|
|
195
195
|
}
|
|
196
|
-
if (
|
|
196
|
+
if (isMainModule(import.meta.url)) {
|
|
197
197
|
main().catch((err) => {
|
|
198
198
|
process.stderr.write(`hq-lanes-drain: hook_runner_failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
199
199
|
// A hook runner failure must not stop the provider's requested tool call.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { readLocalOverride, resolveFlag } from "@indigoai-us/hq-flags-client";
|
|
2
|
+
import { resolveProcessFlagCompanyContext } from "./process-flag-company-context.js";
|
|
3
|
+
import { readFlagSnapshotFile } from "./flag-snapshot-file.js";
|
|
4
|
+
import { HQ_ANYWHERE_RUNTIME_FLAG } from "./anywhere-runtime-flag-key.js";
|
|
5
|
+
/** Resolve the Anywhere gate from hq-flags without loading the CLI's large worker bundle. */
|
|
6
|
+
export function anywhereRuntimeEnabledFromSnapshot(env = process.env) {
|
|
7
|
+
const local = readLocalOverride(HQ_ANYWHERE_RUNTIME_FLAG, env);
|
|
8
|
+
if (local !== null)
|
|
9
|
+
return local;
|
|
10
|
+
const company = resolveProcessFlagCompanyContext(env);
|
|
11
|
+
const persisted = company.companyUid ? readFlagSnapshotFile(company.companyUid, { env }) : null;
|
|
12
|
+
const snapshot = persisted
|
|
13
|
+
? { version: 0, flags: persisted.flags }
|
|
14
|
+
: null;
|
|
15
|
+
return resolveFlag({
|
|
16
|
+
flagKey: HQ_ANYWHERE_RUNTIME_FLAG,
|
|
17
|
+
env,
|
|
18
|
+
snapshot,
|
|
19
|
+
companyIdentifiers: [company.companyUid, company.companySlug].filter((value) => Boolean(value)),
|
|
20
|
+
lookup: { fallback: false },
|
|
21
|
+
}).value;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=anywhere-runtime-fast-flag.js.map
|