@indigoai-us/hq-cli 5.345.146 → 5.345.148
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 +45 -0
- package/README.md +14 -0
- package/assets/bot-workers/setup/worker.yaml +11 -0
- package/dist/command-catalog.generated.d.ts +326 -4
- package/dist/command-catalog.generated.js +403 -4
- package/dist/commands/agent-inbox.js +2 -1
- package/dist/commands/auth.js +4 -2
- package/dist/commands/bot.js +1 -1
- package/dist/commands/cloud.js +31 -1
- package/dist/commands/core-refresh-vault-access-native.d.ts +17 -0
- package/dist/commands/core-refresh-vault-access-native.js +214 -19
- package/dist/commands/core-work-mesh-bind-trusted-native.d.ts +9 -0
- package/dist/commands/core-work-mesh-bind-trusted-native.js +26 -19
- package/dist/commands/core.js +6 -0
- package/dist/commands/daemon.d.ts +7 -0
- package/dist/commands/daemon.js +37 -3
- package/dist/commands/dm.js +9 -2
- package/dist/commands/doctor.d.ts +11 -1
- package/dist/commands/doctor.js +56 -1
- package/dist/commands/files.js +10 -6
- package/dist/commands/install-global.d.ts +7 -0
- package/dist/commands/install-global.js +129 -35
- package/dist/commands/lanes-shared.d.ts +3 -1
- package/dist/commands/lanes.d.ts +4 -4
- package/dist/commands/lanes.js +380 -25
- package/dist/commands/login.js +4 -3
- package/dist/commands/pack-install.d.ts +2 -2
- package/dist/commands/pack-install.js +133 -35
- package/dist/commands/sync-manifest.d.ts +2 -9
- package/dist/commands/sync-manifest.js +4 -15
- package/dist/hook-engine-path.d.ts +4 -0
- package/dist/hook-engine-path.js +36 -0
- package/dist/hq-cloud-sync-runner/bin/sync-runner.js +2784 -2360
- package/dist/hq-cloud-sync-runner/package.json +1 -1
- package/dist/index.js +6 -0
- package/dist/lib/agent-kit/inbox-redact.d.ts +44 -0
- package/dist/lib/agent-kit/inbox-redact.js +361 -0
- package/dist/lib/agent-kit/inbox-state.js +11 -2
- package/dist/lib/agent-kit/mcp/tools.js +2 -1
- package/dist/lib/agent-kit/run/inbox.js +36 -7
- package/dist/lib/anywhere-runtime-flag-key.d.ts +3 -0
- package/dist/lib/anywhere-runtime-flag-key.js +9 -0
- package/dist/lib/bot/daemon.js +3 -0
- package/dist/lib/bot/runtime/index.js +3 -1
- package/dist/lib/company-skill-wrapper.js +6 -4
- package/dist/lib/daemon/install.d.ts +47 -1
- package/dist/lib/daemon/install.js +181 -15
- package/dist/lib/daemon/lock.d.ts +32 -0
- package/dist/lib/daemon/lock.js +55 -2
- package/dist/lib/doctor/__testing__/run-doctor.js +4 -0
- package/dist/lib/doctor/checks/anywhere.d.ts +3 -0
- package/dist/lib/doctor/checks/anywhere.js +23 -8
- package/dist/lib/doctor/checks/cli-path.d.ts +11 -0
- package/dist/lib/doctor/checks/cli-path.js +45 -0
- package/dist/lib/doctor/checks/hook-engine.d.ts +4 -0
- package/dist/lib/doctor/checks/hook-engine.js +16 -0
- package/dist/lib/doctor/checks/release-links.d.ts +23 -0
- package/dist/lib/doctor/checks/release-links.js +152 -0
- package/dist/lib/doctor/checks/sync-health.d.ts +7 -0
- package/dist/lib/doctor/checks/sync-health.js +38 -2
- package/dist/lib/doctor/checks/temp-homes.d.ts +19 -0
- package/dist/lib/doctor/checks/temp-homes.js +54 -0
- package/dist/lib/doctor/fix/apply.d.ts +90 -1
- package/dist/lib/doctor/fix/apply.js +176 -10
- package/dist/lib/doctor/fix/json-output.d.ts +4 -1
- package/dist/lib/doctor/fix/json-output.js +6 -0
- package/dist/lib/doctor/fix/remediation.d.ts +7 -1
- package/dist/lib/doctor/fix/remediation.js +23 -1
- package/dist/lib/doctor/registry.js +13 -0
- package/dist/lib/doctor/temp-home-sweeper.d.ts +149 -0
- package/dist/lib/doctor/temp-home-sweeper.js +418 -0
- package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
- package/dist/lib/flag-registry-worker-source.generated.js +1 -1
- package/dist/lib/flag-registry.js +9 -1
- package/dist/lib/flag-snapshot-file.js +8 -3
- package/dist/lib/hook-engine/resolve.d.ts +31 -0
- package/dist/lib/hook-engine/resolve.js +134 -0
- package/dist/lib/lanes/admission-host.d.ts +1 -0
- package/dist/lib/lanes/admission-host.js +8 -1
- package/dist/lib/lanes/admission.d.ts +4 -0
- package/dist/lib/lanes/admission.js +6 -2
- package/dist/lib/lanes/envelope-repair-dispatch.d.ts +5 -0
- package/dist/lib/lanes/envelope-repair-dispatch.js +12 -1
- package/dist/lib/lanes/index.d.ts +2 -2
- package/dist/lib/lanes/index.js +1 -1
- package/dist/lib/lanes/keep-alive.js +4 -1
- package/dist/lib/lanes/local-host-identity.d.ts +40 -0
- package/dist/lib/lanes/local-host-identity.js +314 -0
- package/dist/lib/lanes/reaper.d.ts +1 -1
- package/dist/lib/lanes/reaper.js +7 -0
- package/dist/lib/lanes/reconcile.d.ts +4 -0
- package/dist/lib/lanes/reconcile.js +16 -8
- package/dist/lib/lanes/spawn.d.ts +20 -0
- package/dist/lib/lanes/spawn.js +48 -2
- package/dist/lib/lanes/status-fields.js +6 -1
- package/dist/lib/lanes/store.js +8 -1
- package/dist/lib/lanes/throttle.d.ts +49 -6
- package/dist/lib/lanes/throttle.js +154 -17
- package/dist/lib/lanes/types.d.ts +22 -1
- package/dist/lib/lanes/types.js +27 -3
- package/dist/lib/lanes/watcher.js +27 -2
- package/dist/lib/lanes/worker-continuity.d.ts +2 -0
- package/dist/lib/lanes/worker-continuity.js +18 -11
- package/dist/lib/manifest-disabled.d.ts +13 -0
- package/dist/lib/manifest-disabled.js +19 -0
- package/dist/lib/service-manager/launchd.d.ts +1 -0
- package/dist/lib/service-manager/launchd.js +1 -1
- package/dist/lib/windows/user-path.d.ts +48 -0
- package/dist/lib/windows/user-path.js +114 -0
- package/dist/lib/work-context/auto-bind.d.ts +1 -1
- package/dist/lib/work-context/auto-bind.js +22 -3
- package/dist/lib/work-context/lane-bind.d.ts +31 -0
- package/dist/lib/work-context/lane-bind.js +101 -0
- package/dist/lib/work-context/organize.js +1 -0
- package/dist/lib/work-context/prd-sync.js +7 -0
- package/dist/lib/work-context/project.d.ts +36 -10
- package/dist/lib/work-context/project.js +130 -41
- package/dist/lib/work-context/reconcile.d.ts +6 -0
- package/dist/lib/work-context/reconcile.js +25 -5
- package/dist/lib/work-context/state.d.ts +6 -0
- package/dist/lib/work-context/state.js +29 -1
- package/dist/lib/workers-registry/index.d.ts +6 -0
- package/dist/lib/workers-registry/index.js +8 -0
- package/dist/utils/copy-tree.d.ts +61 -0
- package/dist/utils/copy-tree.js +249 -0
- package/dist/utils/environmental-error.js +7 -3
- package/dist/utils/headless.d.ts +2 -0
- package/dist/utils/headless.js +8 -0
- package/dist/utils/login-browser.d.ts +4 -0
- package/dist/utils/login-browser.js +122 -0
- package/dist/utils/npm-invocation.d.ts +27 -0
- package/dist/utils/npm-invocation.js +54 -0
- package/dist/utils/safe-link.d.ts +50 -0
- package/dist/utils/safe-link.js +300 -0
- package/dist/utils/team-upgrade.js +1 -7
- package/package.json +17 -5
- package/scripts/hook-engine-manifest.mjs +22 -0
- package/scripts/install-hook-engine.mjs +10 -0
package/dist/commands/files.js
CHANGED
|
@@ -267,7 +267,7 @@ export function registerFilesCommand(program) {
|
|
|
267
267
|
});
|
|
268
268
|
files
|
|
269
269
|
.command("unshare <prefix>")
|
|
270
|
-
.description("Remove a named file access grant, or remove an empty ACL row without --with.")
|
|
270
|
+
.description("Remove a named file access grant, or remove an empty ACL row or grantee-less entries without --with.")
|
|
271
271
|
.option("--with <principal>", "Person uid, agent uid, email address, group id, or '@all' to remove the grant")
|
|
272
272
|
.action(async (prefix, opts) => {
|
|
273
273
|
try {
|
|
@@ -328,17 +328,21 @@ export function registerFilesCommand(program) {
|
|
|
328
328
|
}
|
|
329
329
|
if (!res.ok) {
|
|
330
330
|
if (res.status === 403) {
|
|
331
|
-
console.error(chalk.red("Not authorized to remove this ACL row"));
|
|
332
|
-
|
|
333
|
-
company: files.opts().company,
|
|
334
|
-
});
|
|
331
|
+
console.error(chalk.red(body.error ?? "Not authorized to remove this ACL row"));
|
|
332
|
+
console.error(chalk.dim("Ask a company admin to remove this entry."));
|
|
335
333
|
}
|
|
336
334
|
else {
|
|
337
335
|
console.error(chalk.red(body.error ?? "Unable to remove ACL row"));
|
|
338
336
|
}
|
|
339
337
|
process.exit(1);
|
|
340
338
|
}
|
|
341
|
-
|
|
339
|
+
const removedCount = body.removedEntries;
|
|
340
|
+
const cleanupMessage = typeof removedCount === "number" && removedCount > 0
|
|
341
|
+
? `Removed ${removedCount} empty ${removedCount === 1 ? "entry" : "entries"} from '${canonicalPrefix}'. `
|
|
342
|
+
: "";
|
|
343
|
+
console.log(chalk.green(body.aclRemoved === false
|
|
344
|
+
? `${cleanupMessage || `Removed grantee-less entries from '${canonicalPrefix}'. `}Existing grants remain.`
|
|
345
|
+
: `${cleanupMessage || `Removed empty ACL pattern '${canonicalPrefix}'. `}It now follows its parent folder's access, so anyone who can see that parent can see this folder.`));
|
|
342
346
|
return;
|
|
343
347
|
}
|
|
344
348
|
const res = await vaultApiFetch({
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { AnywhereRuntimeDisabledError } from './anywhere-runtime-flag.js';
|
|
3
3
|
import { type RuntimeHookTrustResult } from '../utils/hook-trust.js';
|
|
4
|
+
import { type ShimPathCheck } from '../lib/windows/user-path.js';
|
|
4
5
|
import { type HookTarget } from '../lib/install/claude-targets.js';
|
|
5
6
|
export interface GlobalInstallOptions {
|
|
6
7
|
/** HQ checkout to install from (absolute). */
|
|
@@ -28,6 +29,8 @@ export interface GlobalInstallOptions {
|
|
|
28
29
|
marketplaceRepository?: string;
|
|
29
30
|
/** Test seam for the Claude Code CLI; production uses `claude`. */
|
|
30
31
|
runClaude?: (args: string[], home: string, env: NodeJS.ProcessEnv) => void;
|
|
32
|
+
/** Platform override for link creation (default process.platform). */
|
|
33
|
+
platform?: NodeJS.Platform;
|
|
31
34
|
}
|
|
32
35
|
/** True when an hq daemon unit (launchd plist or systemd unit) exists. */
|
|
33
36
|
export declare function isDaemonInstalled(home: string, platform?: NodeJS.Platform, queryTask?: (task: string) => boolean): boolean;
|
|
@@ -58,6 +61,8 @@ export interface GlobalUninstallOptions {
|
|
|
58
61
|
log?: (line: string) => void;
|
|
59
62
|
/** Test seam for the Claude Code CLI; production uses `claude`. */
|
|
60
63
|
runClaude?: (args: string[], home: string, env: NodeJS.ProcessEnv) => void;
|
|
64
|
+
/** Platform override for link creation (default process.platform). */
|
|
65
|
+
platform?: NodeJS.Platform;
|
|
61
66
|
}
|
|
62
67
|
/** Reverse every install step; non-HQ entries are left untouched. */
|
|
63
68
|
export declare function uninstallGlobalClaude(opts?: GlobalUninstallOptions): string[];
|
|
@@ -83,6 +88,8 @@ export interface GlobalCommandOpts {
|
|
|
83
88
|
cwd?: string;
|
|
84
89
|
/** Test seam for trusting the Codex hooks a global install registers. */
|
|
85
90
|
trustCodexHooks?: (cwd: string, keys: readonly string[]) => Promise<RuntimeHookTrustResult>;
|
|
91
|
+
/** Test seam: is the npm hq.cmd folder on the Windows PATH? Null off Windows. */
|
|
92
|
+
shimPathCheck?: () => ShimPathCheck | null;
|
|
86
93
|
}
|
|
87
94
|
export declare function resolveInstallRoot(explicit: string | undefined, options?: {
|
|
88
95
|
env?: NodeJS.ProcessEnv;
|
|
@@ -17,6 +17,7 @@ import { seedRegistryFromHqRoot } from './link.js';
|
|
|
17
17
|
import { resolveHqRoot } from './doctor.js';
|
|
18
18
|
import { anywhereRuntimeEnabled, AnywhereRuntimeDisabledError } from './anywhere-runtime-flag.js';
|
|
19
19
|
import { resolveHqMcpEndpoint } from '../lib/daemon/mcp-endpoint.js';
|
|
20
|
+
import { linkDirectory, removeLinkOnly, removeSkillEntry, skillCopySource } from '../utils/safe-link.js';
|
|
20
21
|
import { chatgptConnectorInstructions } from '../lib/install/chatgpt-targets.js';
|
|
21
22
|
import { anywhereRuntimeAllowed } from '../lib/anywhere-person-setting.js';
|
|
22
23
|
import { grokConnectorInstructions } from '../lib/install/grok-targets.js';
|
|
@@ -28,6 +29,7 @@ import { DAEMON_LAUNCHD_LABEL, DAEMON_SYSTEMD_UNIT, DAEMON_WINDOWS_TASK, SYSTEM_
|
|
|
28
29
|
import { readDaemonConfig } from '../lib/daemon/config.js';
|
|
29
30
|
import { daemonPaths } from '../lib/daemon/paths.js';
|
|
30
31
|
import { assertProviderWrappersInstallable, installProviderWrappers, providerWrapperInstallPaths, removeProviderWrappers } from '../lib/install/provider-wrappers.js';
|
|
32
|
+
import { checkShimOnPath, shimPathWarning } from '../lib/windows/user-path.js';
|
|
31
33
|
import { addAgentsMdBlock, addCodexHooks, classifyLegacySkills, codexAdapterPath, codexCharterSource, codexHookTrustKeys, codexTargets, hasAllCodexHooks, HQ_OWNED_MARKER, LEGACY_BACKUP_NAME, LEGACY_SKILLS_NAME, removeAgentsMdBlock, removeCodexHooks, } from '../lib/install/codex-targets.js';
|
|
32
34
|
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';
|
|
33
35
|
/** True when an hq daemon unit (launchd plist or systemd unit) exists. */
|
|
@@ -206,6 +208,22 @@ function mcpManifest(opts) {
|
|
|
206
208
|
}
|
|
207
209
|
return { type: 'stdio', command, args: [entry, 'mcp', 'serve'], env: { PATH: MCP_SUBPROCESS_PATH } };
|
|
208
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* The folder an HQ skill entry stands for: a link's resolved target, or the
|
|
213
|
+
* source recorded in a marked copy. Null for anything else.
|
|
214
|
+
*/
|
|
215
|
+
function skillEntryTarget(skillsDir, entry) {
|
|
216
|
+
const st = fs.lstatSync(entry);
|
|
217
|
+
if (st.isSymbolicLink())
|
|
218
|
+
return path.resolve(skillsDir, fs.readlinkSync(entry));
|
|
219
|
+
return skillCopySource(entry);
|
|
220
|
+
}
|
|
221
|
+
function reportSkillCopies(kinds, log) {
|
|
222
|
+
const copies = kinds.filter((k) => k === 'copy').length;
|
|
223
|
+
if (copies === 0)
|
|
224
|
+
return;
|
|
225
|
+
log(` ${copies} skill folder(s) were copied because Windows refused both a link and a junction. Copies do not follow HQ skill updates; turn on Developer Mode, then uninstall and install again to get links.`);
|
|
226
|
+
}
|
|
209
227
|
function skillLinkName(name) {
|
|
210
228
|
return name.startsWith(SKILL_LINK_PREFIX) ? name : `${SKILL_LINK_PREFIX}${name}`;
|
|
211
229
|
}
|
|
@@ -254,7 +272,7 @@ function planSkills(hqRoot, t) {
|
|
|
254
272
|
}
|
|
255
273
|
if (!st)
|
|
256
274
|
plan.link.push(name);
|
|
257
|
-
else if (
|
|
275
|
+
else if (skillEntryTarget(t.skillsDir, link) === target)
|
|
258
276
|
plan.already.push(name);
|
|
259
277
|
else
|
|
260
278
|
plan.skipped.push(name);
|
|
@@ -283,9 +301,8 @@ function hqSkillLinksForRoot(skillsDir, hqRoot) {
|
|
|
283
301
|
return names.filter((name) => {
|
|
284
302
|
const link = path.join(skillsDir, name);
|
|
285
303
|
try {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
return path.dirname(path.resolve(skillsDir, fs.readlinkSync(link))) === hqSkillsSource(hqRoot);
|
|
304
|
+
const target = skillEntryTarget(skillsDir, link);
|
|
305
|
+
return target !== null && path.dirname(target) === hqSkillsSource(hqRoot);
|
|
289
306
|
}
|
|
290
307
|
catch (error) {
|
|
291
308
|
if (error.code === 'ENOENT')
|
|
@@ -301,8 +318,8 @@ function hqCompanySkillLinksForRoot(skillsDir, hqRoot) {
|
|
|
301
318
|
const source = hqSkillsSource(hqRoot);
|
|
302
319
|
return hqSkillLinksForRoot(skillsDir, hqRoot).filter((link) => {
|
|
303
320
|
try {
|
|
304
|
-
const target =
|
|
305
|
-
if (path.dirname(target) !== source)
|
|
321
|
+
const target = skillEntryTarget(skillsDir, link);
|
|
322
|
+
if (target === null || path.dirname(target) !== source)
|
|
306
323
|
return false;
|
|
307
324
|
const sourceName = path.basename(target);
|
|
308
325
|
const separator = sourceName.indexOf(':');
|
|
@@ -492,22 +509,41 @@ export function installGlobalClaude(opts) {
|
|
|
492
509
|
throw error;
|
|
493
510
|
}
|
|
494
511
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
512
|
+
// Skill links go first: they are the step most likely to fail (Windows
|
|
513
|
+
// without symlink privilege), and a failure here must not leave ~/.hq/root
|
|
514
|
+
// or the CLAUDE.md block behind. On any error, undo this run's writes.
|
|
515
|
+
const skillsDirExisted = fs.existsSync(t.skillsDir);
|
|
516
|
+
const createdLinks = [];
|
|
517
|
+
const linkKinds = [];
|
|
518
|
+
try {
|
|
519
|
+
if (!pluginSource)
|
|
520
|
+
fs.mkdirSync(t.skillsDir, { recursive: true });
|
|
521
|
+
for (const name of skills.link) {
|
|
522
|
+
const link = path.join(t.skillsDir, skillLinkName(name));
|
|
523
|
+
linkKinds.push(linkDirectory(path.join(hqSkillsSource(hqRoot), name), link, { platform: opts.platform }));
|
|
524
|
+
createdLinks.push(link);
|
|
525
|
+
}
|
|
526
|
+
writeFileAtomic(t.hqPointer, pointerNew);
|
|
527
|
+
if (mdOld !== mdNew) {
|
|
528
|
+
if (mdNew === null) {
|
|
529
|
+
if (fs.existsSync(t.claudeMd))
|
|
530
|
+
fs.unlinkSync(t.claudeMd);
|
|
531
|
+
}
|
|
532
|
+
else
|
|
533
|
+
writeFileAtomic(t.claudeMd, mdNew);
|
|
500
534
|
}
|
|
501
|
-
else
|
|
502
|
-
writeFileAtomic(t.claudeMd, mdNew);
|
|
503
535
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
536
|
+
catch (error) {
|
|
537
|
+
rollbackLinkPhase({
|
|
538
|
+
createdLinks,
|
|
539
|
+
skillsDir: skillsDirExisted ? null : t.skillsDir,
|
|
540
|
+
files: [[t.hqPointer, pointerOld], [t.claudeMd, mdOld]],
|
|
541
|
+
log,
|
|
542
|
+
});
|
|
543
|
+
throw error;
|
|
508
544
|
}
|
|
509
545
|
for (const link of skillLinksToRemove)
|
|
510
|
-
|
|
546
|
+
removeSkillEntry(link, log);
|
|
511
547
|
if (pluginSource) {
|
|
512
548
|
// Migrate a prior direct install: remove only our old hook/MCP entries while
|
|
513
549
|
// retaining the original user settings for a later uninstall restore.
|
|
@@ -574,6 +610,7 @@ export function installGlobalClaude(opts) {
|
|
|
574
610
|
installProviderWrappers(providerBin, home);
|
|
575
611
|
log(`HQ installed for Claude Code from ${hqRoot}.`);
|
|
576
612
|
log(` skills linked: ${skills.link.length} new, ${skills.already.length} already present, ${skills.skipped.length} skipped`);
|
|
613
|
+
reportSkillCopies(linkKinds, log);
|
|
577
614
|
reportSkippedSkills(skills, log, false);
|
|
578
615
|
if (pluginSource)
|
|
579
616
|
log(` plugin: ${pluginSource.pluginId} installed from ${pluginSource.sourceDir}; direct hooks and MCP entries removed`);
|
|
@@ -584,6 +621,38 @@ export function installGlobalClaude(opts) {
|
|
|
584
621
|
log(` registry: ${seeded.length} manifest entr${seeded.length === 1 ? 'y' : 'ies'} seeded`);
|
|
585
622
|
return touched;
|
|
586
623
|
}
|
|
624
|
+
/**
|
|
625
|
+
* Undo the link phase of a failed install: remove only the links this run
|
|
626
|
+
* created (never recursing into them), put each file back to its pre-run
|
|
627
|
+
* bytes, and drop the skills folder if this run created it and it is empty.
|
|
628
|
+
* A copy that is kept instead of removed is reported through `log`.
|
|
629
|
+
*/
|
|
630
|
+
function rollbackLinkPhase(args) {
|
|
631
|
+
for (const link of [...args.createdLinks].reverse()) {
|
|
632
|
+
try {
|
|
633
|
+
removeSkillEntry(link, args.log);
|
|
634
|
+
}
|
|
635
|
+
catch { /* keep rolling back */ }
|
|
636
|
+
}
|
|
637
|
+
for (const [file, original] of args.files) {
|
|
638
|
+
try {
|
|
639
|
+
if (original === null) {
|
|
640
|
+
if (fs.existsSync(file))
|
|
641
|
+
fs.unlinkSync(file);
|
|
642
|
+
}
|
|
643
|
+
else if (readOrNull(file) !== original)
|
|
644
|
+
writeFileAtomic(file, original);
|
|
645
|
+
}
|
|
646
|
+
catch { /* keep rolling back */ }
|
|
647
|
+
}
|
|
648
|
+
if (args.skillsDir) {
|
|
649
|
+
try {
|
|
650
|
+
if (fs.readdirSync(args.skillsDir).length === 0)
|
|
651
|
+
fs.rmdirSync(args.skillsDir);
|
|
652
|
+
}
|
|
653
|
+
catch { /* keep rolling back */ }
|
|
654
|
+
}
|
|
655
|
+
}
|
|
587
656
|
/** Reverse every install step; non-HQ entries are left untouched. */
|
|
588
657
|
export function uninstallGlobalClaude(opts = {}) {
|
|
589
658
|
const home = resolveEnv({ home: opts.home }).home;
|
|
@@ -621,11 +690,11 @@ export function uninstallGlobalClaude(opts = {}) {
|
|
|
621
690
|
const links = [];
|
|
622
691
|
try {
|
|
623
692
|
for (const d of fs.readdirSync(t.skillsDir, { withFileTypes: true })) {
|
|
624
|
-
if (!d.name.startsWith(SKILL_LINK_PREFIX) || !d.isSymbolicLink())
|
|
693
|
+
if (!d.name.startsWith(SKILL_LINK_PREFIX) || !(d.isSymbolicLink() || d.isDirectory()))
|
|
625
694
|
continue;
|
|
626
695
|
const link = path.join(t.skillsDir, d.name);
|
|
627
|
-
const target =
|
|
628
|
-
if (path.dirname(target) === source)
|
|
696
|
+
const target = skillEntryTarget(t.skillsDir, link);
|
|
697
|
+
if (target !== null && path.dirname(target) === source)
|
|
629
698
|
links.push(link);
|
|
630
699
|
}
|
|
631
700
|
}
|
|
@@ -693,7 +762,7 @@ export function uninstallGlobalClaude(opts = {}) {
|
|
|
693
762
|
}
|
|
694
763
|
}
|
|
695
764
|
for (const link of links)
|
|
696
|
-
|
|
765
|
+
removeSkillEntry(link, log);
|
|
697
766
|
if (md !== null && mdNext !== md) {
|
|
698
767
|
if (mdNext === null || (mdNext === '' && state?.claudeMd.original == null))
|
|
699
768
|
fs.unlinkSync(t.claudeMd);
|
|
@@ -842,12 +911,34 @@ export function installGlobalCodex(opts) {
|
|
|
842
911
|
log(lineDiff(codexConfigFormat.serialize(tomlDoc), codexConfigFormat.serialize(tomlNext)));
|
|
843
912
|
return touched;
|
|
844
913
|
}
|
|
845
|
-
|
|
846
|
-
|
|
914
|
+
// Skill links go first so a link failure leaves no pointer or AGENTS.md
|
|
915
|
+
// block behind (see installGlobalClaude).
|
|
916
|
+
const skillsDirExisted = fs.existsSync(t.skillsDir);
|
|
917
|
+
const createdLinks = [];
|
|
918
|
+
const linkKinds = [];
|
|
919
|
+
try {
|
|
920
|
+
fs.mkdirSync(t.skillsDir, { recursive: true });
|
|
921
|
+
for (const name of skills.link) {
|
|
922
|
+
const link = path.join(t.skillsDir, skillLinkName(name));
|
|
923
|
+
linkKinds.push(linkDirectory(path.join(hqSkillsSource(hqRoot), name), link, { platform: opts.platform }));
|
|
924
|
+
createdLinks.push(link);
|
|
925
|
+
}
|
|
926
|
+
writeFileAtomic(hqPointer, pointerNew);
|
|
927
|
+
writeFileAtomic(t.agentsMd, mdNew);
|
|
928
|
+
}
|
|
929
|
+
catch (error) {
|
|
930
|
+
rollbackLinkPhase({
|
|
931
|
+
createdLinks,
|
|
932
|
+
skillsDir: skillsDirExisted ? null : t.skillsDir,
|
|
933
|
+
files: [[hqPointer, pointerOld], [t.agentsMd, mdOld]],
|
|
934
|
+
log,
|
|
935
|
+
});
|
|
936
|
+
throw error;
|
|
937
|
+
}
|
|
847
938
|
// Legacy mirror: replace only when HQ-owned; record enough to put it back.
|
|
848
939
|
let legacyRecord = prior?.legacy ?? { kind: 'none' };
|
|
849
940
|
if (legacy.kind === 'hq-symlink') {
|
|
850
|
-
|
|
941
|
+
removeLinkOnly(legacyPath);
|
|
851
942
|
legacyRecord = { kind: 'symlink', target: legacy.target };
|
|
852
943
|
}
|
|
853
944
|
else if (legacy.kind === 'hq-dir') {
|
|
@@ -857,12 +948,8 @@ export function installGlobalCodex(opts) {
|
|
|
857
948
|
}
|
|
858
949
|
else if (legacy.kind === 'foreign')
|
|
859
950
|
log(legacyWarning(t.skillsDir));
|
|
860
|
-
fs.mkdirSync(t.skillsDir, { recursive: true });
|
|
861
|
-
for (const name of skills.link) {
|
|
862
|
-
fs.symlinkSync(path.join(hqSkillsSource(hqRoot), name), path.join(t.skillsDir, skillLinkName(name)));
|
|
863
|
-
}
|
|
864
951
|
for (const link of companySkillLinksToRemove)
|
|
865
|
-
|
|
952
|
+
removeSkillEntry(link, log);
|
|
866
953
|
writeConfigAtomic({
|
|
867
954
|
target: t.hooksJson,
|
|
868
955
|
pack: HQ_ANYWHERE_PACK,
|
|
@@ -905,6 +992,7 @@ export function installGlobalCodex(opts) {
|
|
|
905
992
|
installProviderWrappers(providerBin, home);
|
|
906
993
|
log(`HQ installed for Codex from ${hqRoot}.`);
|
|
907
994
|
log(` skills linked: ${skills.link.length} new, ${skills.already.length} already present, ${skills.skipped.length} skipped`);
|
|
995
|
+
reportSkillCopies(linkKinds, log);
|
|
908
996
|
reportSkippedSkills(skills, log, false);
|
|
909
997
|
log(` hooks: ${hookTarget === 'shim' ? shimPath(hqRoot) : codexAdapterPath(hqRoot)} registered in ${t.hooksJson}`);
|
|
910
998
|
log(` MCP: "${HQ_MCP_SERVER_NAME}" registered in ${t.configToml}`);
|
|
@@ -943,11 +1031,11 @@ export function uninstallGlobalCodex(opts = {}) {
|
|
|
943
1031
|
const links = [];
|
|
944
1032
|
try {
|
|
945
1033
|
for (const d of fs.readdirSync(t.skillsDir, { withFileTypes: true })) {
|
|
946
|
-
if (!d.name.startsWith(SKILL_LINK_PREFIX) || !d.isSymbolicLink())
|
|
1034
|
+
if (!d.name.startsWith(SKILL_LINK_PREFIX) || !(d.isSymbolicLink() || d.isDirectory()))
|
|
947
1035
|
continue;
|
|
948
1036
|
const link = path.join(t.skillsDir, d.name);
|
|
949
|
-
const target =
|
|
950
|
-
if (path.dirname(target) === source)
|
|
1037
|
+
const target = skillEntryTarget(t.skillsDir, link);
|
|
1038
|
+
if (target !== null && path.dirname(target) === source)
|
|
951
1039
|
links.push(link);
|
|
952
1040
|
}
|
|
953
1041
|
}
|
|
@@ -1012,10 +1100,10 @@ export function uninstallGlobalCodex(opts = {}) {
|
|
|
1012
1100
|
}
|
|
1013
1101
|
}
|
|
1014
1102
|
for (const link of links)
|
|
1015
|
-
|
|
1103
|
+
removeSkillEntry(link, log);
|
|
1016
1104
|
if (restoreLegacy && state) {
|
|
1017
1105
|
if (state.legacy.kind === 'symlink')
|
|
1018
|
-
|
|
1106
|
+
linkDirectory(state.legacy.target, legacyPath, { platform: opts.platform, allowCopy: false });
|
|
1019
1107
|
else if (state.legacy.kind === 'dir' && fs.existsSync(state.legacy.backup))
|
|
1020
1108
|
fs.renameSync(state.legacy.backup, legacyPath);
|
|
1021
1109
|
}
|
|
@@ -1124,6 +1212,12 @@ export async function runInstallGlobal(opts) {
|
|
|
1124
1212
|
});
|
|
1125
1213
|
if (runtime === 'codex' && !opts.dryRun)
|
|
1126
1214
|
await trustInstalledCodexHooks(hqRoot, opts);
|
|
1215
|
+
if (!opts.dryRun) {
|
|
1216
|
+
const shimPath = (opts.shimPathCheck ?? (() => checkShimOnPath({ env })))();
|
|
1217
|
+
if (shimPath)
|
|
1218
|
+
for (const line of shimPathWarning(shimPath))
|
|
1219
|
+
(opts.log ?? console.log)(line);
|
|
1220
|
+
}
|
|
1127
1221
|
}
|
|
1128
1222
|
/**
|
|
1129
1223
|
* Codex skips untrusted hooks, so the hooks.json entries written above do
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { LaneCompanyRef, LanePlacementPolicy, LaneRecord, LanesConfig, QuestionAnswer, QuestionBody, QuestionRecord, SeniorRef, AccountHistoryEntry, LaneAccountSelection, PendingResume, QueuedReason, ResolvedLaneWorker, LivenessDeps, MachineIdentity, MonitorEvidenceReader, ReconcileHost, RunCommand, ThrottleDeps, withLaneAdmissionLock, withLaneHireLocks } from "../lib/lanes/index.js";
|
|
2
2
|
import type { OutpostIdentity } from "../lib/lanes/admission-host.js";
|
|
3
3
|
import type { OutpostDispatchResult } from "../lib/lanes/outpost-dispatch.js";
|
|
4
|
-
import type { CpuBusySource } from "../lib/lanes/throttle.js";
|
|
4
|
+
import type { CpuBusySource, MemoryHold } from "../lib/lanes/throttle.js";
|
|
5
5
|
import type { ProbeCommand } from "../lib/lanes/models.js";
|
|
6
6
|
import type { FocusGuardReason } from "../lib/lanes/focus-guard.js";
|
|
7
7
|
import type { KillProcessFn, ProveDetachedFn, SpawnProcessFn } from "../lib/lanes/spawn.js";
|
|
@@ -100,6 +100,8 @@ export interface AdmissionResult {
|
|
|
100
100
|
last_busy: number | null;
|
|
101
101
|
busy_source: CpuBusySource | undefined;
|
|
102
102
|
sampled_at: string | undefined;
|
|
103
|
+
/** Present when the memory gate holds: the signal, its value and the threshold. */
|
|
104
|
+
memory_hold?: MemoryHold;
|
|
103
105
|
};
|
|
104
106
|
}
|
|
105
107
|
export interface PrepareAccountMoveInput {
|
package/dist/commands/lanes.d.ts
CHANGED
|
@@ -136,12 +136,12 @@ export declare function casUpdateLane(hqRoot: string, laneId: string, updater: (
|
|
|
136
136
|
* reconcile pass resumes the lane once every question is answered.
|
|
137
137
|
*/
|
|
138
138
|
export declare function checkpointInstruction(worker: Pick<ResolvedLaneWorker, "approvalRequired" | "humanCheckpoints">): string;
|
|
139
|
-
export declare function composeWorkerBrief(worker: ResolvedLaneWorker, taskBrief: string): string;
|
|
139
|
+
export declare function composeWorkerBrief(worker: ResolvedLaneWorker, taskBrief: string, hqRoot: string): string;
|
|
140
140
|
/**
|
|
141
|
-
* A lane with no worker still carries the standing authorization paragraph
|
|
142
|
-
*
|
|
141
|
+
* A lane with no worker still carries the standing authorization paragraph,
|
|
142
|
+
* the foreground-only rule and the temp-home rule.
|
|
143
143
|
*/
|
|
144
|
-
export declare function composeGeneralBrief(taskBrief: string): string;
|
|
144
|
+
export declare function composeGeneralBrief(taskBrief: string, hqRoot: string): string;
|
|
145
145
|
export declare const DISPATCH_TERMINAL_REASON: "outpost_dispatch_failed";
|
|
146
146
|
/**
|
|
147
147
|
* The CAS updater a pickup failure applies. A lane that is already terminal
|