@indigoai-us/hq-cli 5.309.1 → 5.311.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 (99) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/assets/scaffold/core/scripts/hq-dm-bind.sh +585 -0
  3. package/assets/scaffold/core/scripts/jobs-validate.sh +1 -1
  4. package/assets/scaffold/core/scripts/work-mesh-live-bind-trusted.sh +152 -0
  5. package/dist/command-catalog.generated.d.ts +28 -2
  6. package/dist/command-catalog.generated.js +34 -2
  7. package/dist/commands/channels.d.ts +1 -9
  8. package/dist/commands/channels.js +2 -2
  9. package/dist/commands/company-transfer.d.ts +4 -28
  10. package/dist/commands/company-transfer.js +5 -49
  11. package/dist/commands/core-checkpoint-stop-gate-native.d.ts +34 -0
  12. package/dist/commands/core-checkpoint-stop-gate-native.js +713 -0
  13. package/dist/commands/core-checkpoint.d.ts +0 -24
  14. package/dist/commands/core-checkpoint.js +4 -4
  15. package/dist/commands/core-command-catalog.js +1 -1
  16. package/dist/commands/core-dm-bind-native.d.ts +32 -0
  17. package/dist/commands/core-dm-bind-native.js +668 -0
  18. package/dist/commands/core-hq-session-native.d.ts +39 -0
  19. package/dist/commands/core-hq-session-native.js +579 -0
  20. package/dist/commands/core-jobs-native.js +1 -4
  21. package/dist/commands/core-native-session-flag.d.ts +4 -0
  22. package/dist/commands/core-native-session-flag.js +7 -0
  23. package/dist/commands/core-native-utilities.d.ts +2 -1
  24. package/dist/commands/core-native-utilities.js +4 -2
  25. package/dist/commands/core-native-worker-skills-flag.d.ts +4 -0
  26. package/dist/commands/core-native-worker-skills-flag.js +7 -0
  27. package/dist/commands/core-work-mesh-bind-trusted-native.d.ts +32 -0
  28. package/dist/commands/core-work-mesh-bind-trusted-native.js +190 -0
  29. package/dist/commands/core-worker-lint-native.d.ts +4 -0
  30. package/dist/commands/core-worker-lint-native.js +160 -0
  31. package/dist/commands/core-worker-share-native.d.ts +5 -0
  32. package/dist/commands/core-worker-share-native.js +260 -0
  33. package/dist/commands/core-worker-skills-command.d.ts +8 -0
  34. package/dist/commands/core-worker-skills-command.js +33 -0
  35. package/dist/commands/core-worker-skills-runtime.d.ts +33 -0
  36. package/dist/commands/core-worker-skills-runtime.js +86 -0
  37. package/dist/commands/core.js +10 -2
  38. package/dist/commands/dm.d.ts +3 -7
  39. package/dist/commands/dm.js +3 -3
  40. package/dist/commands/integrations-import.d.ts +1 -1
  41. package/dist/commands/integrations-import.js +34 -19
  42. package/dist/commands/lanes-accounts.js +3 -1
  43. package/dist/commands/lanes-message.js +84 -10
  44. package/dist/commands/lanes-shared.d.ts +4 -1
  45. package/dist/commands/lanes.d.ts +4 -1
  46. package/dist/commands/lanes.js +48 -7
  47. package/dist/commands/personal-connector-settings-flag.d.ts +4 -0
  48. package/dist/commands/personal-connector-settings-flag.js +7 -0
  49. package/dist/commands/report.d.ts +1 -26
  50. package/dist/commands/report.js +6 -6
  51. package/dist/fast-core.js +15 -2
  52. package/dist/fast-scaffold.js +25 -2
  53. package/dist/index.js +8 -0
  54. package/dist/lib/bot/continuity.d.ts +2 -1
  55. package/dist/lib/bot/job-completions.d.ts +0 -2
  56. package/dist/lib/bot/job-completions.js +2 -2
  57. package/dist/lib/bot/local-config.d.ts +0 -10
  58. package/dist/lib/bot/local-config.js +3 -9
  59. package/dist/lib/bot/owner-context.d.ts +2 -6
  60. package/dist/lib/bot/owner-context.js +3 -3
  61. package/dist/lib/bot/runtime/windows.d.ts +0 -2
  62. package/dist/lib/bot/runtime/windows.js +1 -1
  63. package/dist/lib/bot/setup-explain.d.ts +0 -4
  64. package/dist/lib/bot/setup-explain.js +2 -2
  65. package/dist/lib/daemon/catalog.d.ts +3 -2
  66. package/dist/lib/daemon/hq-cloud.d.ts +2 -2
  67. package/dist/lib/daemon/hq-cloud.js +1 -1
  68. package/dist/lib/daemon/machine-type.d.ts +3 -4
  69. package/dist/lib/daemon/machine-type.js +2 -2
  70. package/dist/lib/daemon/remote-control.d.ts +0 -1
  71. package/dist/lib/daemon/remote-control.js +1 -1
  72. package/dist/lib/daemon/supervisor.d.ts +4 -9
  73. package/dist/lib/daemon/supervisor.js +5 -5
  74. package/dist/lib/lanes/admission-host.d.ts +3 -0
  75. package/dist/lib/lanes/admission-host.js +4 -0
  76. package/dist/lib/lanes/autopilot-registry.d.ts +62 -0
  77. package/dist/lib/lanes/autopilot-registry.js +192 -0
  78. package/dist/lib/lanes/autopilot.d.ts +2 -0
  79. package/dist/lib/lanes/autopilot.js +18 -5
  80. package/dist/lib/lanes/claude-balance.d.ts +6 -0
  81. package/dist/lib/lanes/claude-balance.js +17 -2
  82. package/dist/lib/lanes/keep-alive.d.ts +3 -0
  83. package/dist/lib/lanes/keep-alive.js +3 -0
  84. package/dist/lib/lanes/reconcile.js +14 -6
  85. package/dist/lib/lanes/spawn.js +2 -0
  86. package/dist/lib/mesh/live/daemon/credentials.d.ts +2 -17
  87. package/dist/lib/mesh/live/daemon/credentials.js +9 -9
  88. package/dist/lib/mesh/live/daemon/index.d.ts +5 -5
  89. package/dist/lib/mesh/live/daemon/index.js +2 -2
  90. package/dist/lib/mesh/live/daemon/state.d.ts +1 -1
  91. package/dist/lib/mesh/live/daemon/transcript-watch.d.ts +4 -11
  92. package/dist/lib/mesh/live/daemon/transcript-watch.js +5 -5
  93. package/dist/lib/work-context/company.d.ts +2 -25
  94. package/dist/lib/work-context/company.js +3 -3
  95. package/dist/lib/work-context/config.d.ts +3 -8
  96. package/dist/lib/work-context/config.js +2 -13
  97. package/dist/lib/work-context/state.d.ts +4 -13
  98. package/dist/lib/work-context/state.js +2 -30
  99. package/package.json +1 -1
@@ -0,0 +1,7 @@
1
+ /** Default-off rollout gate for native worker-scoped skill commands. */
2
+ export const CORE_NATIVE_WORKER_SKILLS_FLAG = "core-native-worker-skills";
3
+ export async function coreNativeWorkerSkillsEnabled() {
4
+ const { resolveProcessFlagGate } = await import("../lib/flag-registry.js");
5
+ return resolveProcessFlagGate(CORE_NATIVE_WORKER_SKILLS_FLAG, { fallback: false }, () => false);
6
+ }
7
+ //# sourceMappingURL=core-native-worker-skills-flag.js.map
@@ -0,0 +1,32 @@
1
+ /** Native implementation of core/scripts/work-mesh-live-bind-trusted.sh. */
2
+ import * as fs from "node:fs";
3
+ import type { Command } from "commander";
4
+ import { type UtilityOutput } from "./core-native-utilities.js";
5
+ export declare const WORK_MESH_BIND_TRUSTED_ASSET = "core/scripts/work-mesh-live-bind-trusted.sh";
6
+ export declare const WORK_MESH_BIND_TRUSTED_HELP = "hq-core: public\nUS-011 \u2014 write session meta + reconcile with observation.trustedContext.\nNo --trusted CLI flag in hq-cli; trusted bind is the observation field.\nset -euo pipefail\n\nROOT=\"${HQ_ROOT:-${CLAUDE_PROJECT_DIR:-}}\"\nif [ -z \"$ROOT\" ]; then\n";
7
+ export type WorkMeshBindDeps = {
8
+ root: string;
9
+ cwd: string;
10
+ env: NodeJS.ProcessEnv;
11
+ now: () => Date;
12
+ fs: typeof fs;
13
+ runHq: (args: string[], env: NodeJS.ProcessEnv) => {
14
+ code: number;
15
+ stdout: string;
16
+ stderr: string;
17
+ };
18
+ runHqSession: (root: string, args: string[], env: NodeJS.ProcessEnv) => {
19
+ code: number;
20
+ stdout: string;
21
+ stderr: string;
22
+ };
23
+ spawnHq: (args: string[], env: NodeJS.ProcessEnv) => void;
24
+ pid: number;
25
+ tmpDir: string;
26
+ ulid: (now: Date) => string;
27
+ };
28
+ export declare const createWorkMeshBindDeps: (root: string, cwd: string, env: NodeJS.ProcessEnv, now: () => Date) => WorkMeshBindDeps;
29
+ export declare function resolveWorkMeshBindSessionId(root: string, env: NodeJS.ProcessEnv, fileSystem?: typeof fs): string;
30
+ export declare function runWorkMeshBindTrusted(args: string[], deps: WorkMeshBindDeps): UtilityOutput;
31
+ export declare function registerCoreWorkMeshBindTrustedNative(core: Command): void;
32
+ //# sourceMappingURL=core-work-mesh-bind-trusted-native.d.ts.map
@@ -0,0 +1,190 @@
1
+ /** Native implementation of core/scripts/work-mesh-live-bind-trusted.sh. */
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import { spawn, spawnSync } from "node:child_process";
5
+ import { generateUlid } from "../lib/mesh/live/ulid.js";
6
+ import { resolveLiveRoot } from "../utils/hq-roots.js";
7
+ import { registerNativeUtility } from "./core-native-utilities.js";
8
+ export const WORK_MESH_BIND_TRUSTED_ASSET = "core/scripts/work-mesh-live-bind-trusted.sh";
9
+ export const WORK_MESH_BIND_TRUSTED_HELP = `hq-core: public
10
+ US-011 — write session meta + reconcile with observation.trustedContext.
11
+ No --trusted CLI flag in hq-cli; trusted bind is the observation field.
12
+ set -euo pipefail
13
+
14
+ ROOT="\${HQ_ROOT:-\${CLAUDE_PROJECT_DIR:-}}"
15
+ if [ -z "$ROOT" ]; then
16
+ `;
17
+ export const createWorkMeshBindDeps = (root, cwd, env, now) => ({
18
+ root, cwd, env, now, fs, pid: process.pid, tmpDir: env.TMPDIR || "/tmp", ulid: (date) => generateUlid(date.getTime()),
19
+ runHq: (args, childEnv) => {
20
+ const result = spawnSync("hq", args, { cwd, env: { ...childEnv, HQ_ROOT: root }, encoding: "utf8" });
21
+ return { code: result.status ?? 127, stdout: result.stdout ?? "", stderr: result.stderr ?? (result.error ? `${result.error.message}\n` : "") };
22
+ },
23
+ runHqSession: (scriptRoot, args, childEnv) => {
24
+ const result = spawnSync("bash", [path.join(scriptRoot, "core/scripts/hq-session.sh"), ...args], { cwd, env: childEnv, encoding: "utf8" });
25
+ return { code: result.status ?? 127, stdout: result.stdout ?? "", stderr: result.stderr ?? (result.error ? `${result.error.message}\n` : "") };
26
+ },
27
+ spawnHq: (args, childEnv) => {
28
+ const child = spawn("hq", args, { cwd, env: { ...childEnv, HQ_ROOT: root }, detached: true, stdio: "ignore" });
29
+ child.unref();
30
+ },
31
+ });
32
+ export function resolveWorkMeshBindSessionId(root, env, fileSystem = fs) {
33
+ for (const key of ["HQ_SESSION_ID", "CLAUDE_CODE_SESSION_ID", "CLAUDE_SESSION_ID", "CODEX_SESSION_ID", "CODEX_THREAD_ID"]) {
34
+ const value = (env[key] ?? "").replace(/\s/g, "");
35
+ if (value)
36
+ return value;
37
+ }
38
+ try {
39
+ const value = fileSystem.readFileSync(path.join(root, "workspace/sessions/.current"), "utf8").replace(/\s/g, "");
40
+ return value;
41
+ }
42
+ catch {
43
+ return "";
44
+ }
45
+ }
46
+ function hasExecutable(deps, command) {
47
+ for (const directory of (deps.env.PATH ?? "").split(path.delimiter)) {
48
+ if (!directory)
49
+ continue;
50
+ try {
51
+ const stat = deps.fs.statSync(path.join(directory, command));
52
+ if (stat.isFile() && (stat.mode & 0o111) !== 0)
53
+ return true;
54
+ }
55
+ catch { /* PATH entry is absent */ }
56
+ }
57
+ return false;
58
+ }
59
+ function workMeshJsonQuote(value) {
60
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t")}"`;
61
+ }
62
+ function workMeshObservationJson(observation) {
63
+ const identity = observation.identity;
64
+ const trusted = observation.trustedContext;
65
+ let json = `{"contractVersion":1,"clientOperationId":${workMeshJsonQuote(String(observation.clientOperationId))},"identity":{"sessionId":${workMeshJsonQuote(identity.sessionId)},"harness":${workMeshJsonQuote(identity.harness)},"adapterVersion":${workMeshJsonQuote(identity.adapterVersion)}},"cwd":${workMeshJsonQuote(String(observation.cwd))},"hqRoot":${workMeshJsonQuote(String(observation.hqRoot))}`;
66
+ if (trusted) {
67
+ json += ',"trustedContext":{';
68
+ let first = true;
69
+ for (const [key, value] of Object.entries(trusted)) {
70
+ if (!first)
71
+ json += ",";
72
+ first = false;
73
+ json += `${workMeshJsonQuote(key)}:${workMeshJsonQuote(value)}`;
74
+ }
75
+ json += "}";
76
+ }
77
+ return `${json}}`;
78
+ }
79
+ export function runWorkMeshBindTrusted(args, deps) {
80
+ let root = deps.env.HQ_ROOT || deps.env.CLAUDE_PROJECT_DIR || deps.root;
81
+ let company = "", project = "", task = "", sid = "", noReconcile = false;
82
+ const output = [];
83
+ for (let i = 0; i < args.length;) {
84
+ const arg = args[i];
85
+ if (["--company", "--company-slug", "--project", "--task", "--session", "--session-id", "--root"].includes(arg)) {
86
+ const value = args[i + 1] ?? "";
87
+ if (arg === "--company" || arg === "--company-slug")
88
+ company = value;
89
+ else if (arg === "--project")
90
+ project = value;
91
+ else if (arg === "--task")
92
+ task = value;
93
+ else if (arg === "--session" || arg === "--session-id")
94
+ sid = value;
95
+ else
96
+ root = value;
97
+ i += 2;
98
+ }
99
+ else if (arg === "--no-reconcile") {
100
+ noReconcile = true;
101
+ i += 1;
102
+ }
103
+ else if (arg === "-h" || arg === "--help")
104
+ return { code: 0, stdout: WORK_MESH_BIND_TRUSTED_HELP.replace(/^# ?/gm, ""), stderr: "" };
105
+ else
106
+ i += 1;
107
+ }
108
+ if (!sid) {
109
+ sid = resolveWorkMeshBindSessionId(root, deps.env, deps.fs);
110
+ if (!sid) {
111
+ try {
112
+ sid = deps.fs.readFileSync(path.join(root, "workspace/sessions/.current"), "utf8").replace(/\s/g, "");
113
+ }
114
+ catch {
115
+ sid = "";
116
+ }
117
+ }
118
+ }
119
+ const childEnv = { ...deps.env, HQ_ROOT: root };
120
+ const pin = sid ? ["--session-id", sid] : [];
121
+ let stderr = "";
122
+ for (const [key, value] of [["company_slug", company], ["project", project], ["task", task]]) {
123
+ if (!value)
124
+ continue;
125
+ const result = deps.runHqSession(root, [...pin, "set", key, value], deps.env);
126
+ output.push(result.stdout);
127
+ stderr += result.stderr;
128
+ if (result.code !== 0)
129
+ return { code: result.code, stdout: output.join(""), stderr };
130
+ }
131
+ if (noReconcile || !sid)
132
+ return { code: 0, stdout: output.join(""), stderr };
133
+ let harness = deps.env.HQ_HARNESS || deps.env.HQ_WORK_MESH_HARNESS || "claude-code";
134
+ if (["claude", "Claude", "ClaudeCode"].includes(harness))
135
+ harness = "claude-code";
136
+ else if (harness === "Codex")
137
+ harness = "codex";
138
+ else if (harness === "Grok")
139
+ harness = "grok";
140
+ const adapter = deps.env.HQ_ADAPTER_CONTRACT_VERSION || "1.0.0";
141
+ const clientOperationId = deps.ulid(deps.now());
142
+ const observation = {
143
+ contractVersion: 1,
144
+ clientOperationId,
145
+ identity: { sessionId: sid, harness, adapterVersion: adapter },
146
+ cwd: deps.env.PWD ?? "",
147
+ hqRoot: root,
148
+ };
149
+ if (company || project || task) {
150
+ const trusted = {};
151
+ if (company)
152
+ trusted.companySlug = company;
153
+ if (project)
154
+ trusted.project = project;
155
+ if (task)
156
+ trusted.task = task;
157
+ observation.trustedContext = trusted;
158
+ }
159
+ const directory = path.join(deps.tmpDir, "hq-work-mesh-obs");
160
+ try {
161
+ deps.fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
162
+ deps.fs.chmodSync(directory, 0o700);
163
+ }
164
+ catch { /* legacy mkdir/chmod are best effort */ }
165
+ const observationFile = path.join(directory, `${sid}.bind.${deps.pid}.json`);
166
+ try {
167
+ deps.fs.writeFileSync(observationFile, `${workMeshObservationJson(observation)}\n`);
168
+ }
169
+ catch { /* legacy printf redirection failure is non-fatal */ }
170
+ if (deps.env.HQ_WORK_MESH_RECONCILE_LOG) {
171
+ try {
172
+ deps.fs.appendFileSync(deps.env.HQ_WORK_MESH_RECONCILE_LOG, `reconcile-trusted ${observationFile}\n`);
173
+ }
174
+ catch { /* legacy log write is best effort */ }
175
+ }
176
+ if (deps.env.HQ_WORK_MESH_RECONCILE_STUB === "1")
177
+ return { code: 0, stdout: output.join(""), stderr };
178
+ if (hasExecutable(deps, "hq") && deps.fs.existsSync(observationFile)) {
179
+ deps.spawnHq(["mesh", "context", "reconcile", "--observation-file", observationFile, "--machine"], childEnv);
180
+ }
181
+ return { code: 0, stdout: output.join(""), stderr };
182
+ }
183
+ export function registerCoreWorkMeshBindTrustedNative(core) {
184
+ registerNativeUtility(core, {
185
+ name: "work-mesh-bind-trusted", asset: WORK_MESH_BIND_TRUSTED_ASSET, root: "live", summary: "Bind trusted session context and reconcile it with Work Mesh",
186
+ resolveRoot: (_cwd, hqRoot) => resolveLiveRoot({ hqRoot }),
187
+ run: (args, context) => runWorkMeshBindTrusted(args, createWorkMeshBindDeps(context.root, context.cwd, context.env, context.now)),
188
+ });
189
+ }
190
+ //# sourceMappingURL=core-work-mesh-bind-trusted-native.js.map
@@ -0,0 +1,4 @@
1
+ import { type WorkerSkillDependencies, type WorkerSkillResult } from "./core-worker-skills-runtime.js";
2
+ export declare const WORKER_SKILL_LINT_ASSET = "core/scripts/lint-shared-worker-skills.sh";
3
+ export declare function runWorkerSkillLint(args: string[], dependencies: WorkerSkillDependencies): WorkerSkillResult;
4
+ //# sourceMappingURL=core-worker-lint-native.d.ts.map
@@ -0,0 +1,160 @@
1
+ /** Native port of lint-shared-worker-skills.sh. */
2
+ import * as crypto from "node:crypto";
3
+ import * as path from "node:path";
4
+ import { errorCode, missingOrUnreadable, resolveWorkerCallerRoot, } from "./core-worker-skills-runtime.js";
5
+ export const WORKER_SKILL_LINT_ASSET = "core/scripts/lint-shared-worker-skills.sh";
6
+ const DEFAULT_ROOTS = ["core/workers", "core/packages"];
7
+ function joinFindPath(parent, child) {
8
+ return `${parent}${parent.endsWith("/") ? "" : "/"}${child}`;
9
+ }
10
+ function matchesFindPredicate(displayPath) {
11
+ // GNU/BSD find's `-path '*/skills/*.md'` glob lets `*` span `/`, but still
12
+ // requires a slash before `skills/`. In particular `skills/x.md` does not
13
+ // match while `./skills/x.md` and `workers/a/skills/x.md` do.
14
+ if (!/.+\/skills\/.*\.md$/.test(displayPath))
15
+ return false;
16
+ const name = displayPath.slice(displayPath.lastIndexOf("/") + 1);
17
+ if (name.startsWith("._"))
18
+ return false;
19
+ // Mirrors find's `-not -path '*/.*/*'`: a hidden directory is excluded only
20
+ // when it has a slash before and after the dot-prefixed component.
21
+ return !/\/\.[^/]*\//.test(displayPath);
22
+ }
23
+ function collectEntries(rootArg, cwd, fileSystem) {
24
+ const entries = [];
25
+ const visit = (displayPath) => {
26
+ const absolutePath = path.resolve(cwd, displayPath);
27
+ let info;
28
+ try {
29
+ info = fileSystem.lstatSync(absolutePath);
30
+ }
31
+ catch (error) {
32
+ if (missingOrUnreadable(error))
33
+ return;
34
+ throw error;
35
+ }
36
+ if (info.isDirectory()) {
37
+ let names;
38
+ try {
39
+ names = fileSystem.readdirSync(absolutePath);
40
+ }
41
+ catch (error) {
42
+ if (missingOrUnreadable(error))
43
+ return;
44
+ throw error;
45
+ }
46
+ for (const name of names)
47
+ visit(joinFindPath(displayPath, name));
48
+ return;
49
+ }
50
+ if ((info.isFile() || info.isSymbolicLink()) && matchesFindPredicate(displayPath)) {
51
+ entries.push({ displayPath, absolutePath, symlink: info.isSymbolicLink() });
52
+ }
53
+ };
54
+ visit(rootArg);
55
+ return entries;
56
+ }
57
+ function ownershipDomain(displayPath) {
58
+ const value = displayPath.startsWith("./") ? displayPath.slice(2) : displayPath;
59
+ if (value.startsWith("core/packages/")) {
60
+ const rest = value.slice("core/packages/".length);
61
+ return `core/packages/${rest.split("/", 1)[0]}`;
62
+ }
63
+ if (value.startsWith("companies/")) {
64
+ const rest = value.slice("companies/".length);
65
+ return `companies/${rest.split("/", 1)[0]}`;
66
+ }
67
+ if (value === "core/workers" || value.startsWith("core/workers/"))
68
+ return "core/workers";
69
+ return value.split("/", 1)[0] ?? "";
70
+ }
71
+ function readFileForHash(filePath, fileSystem) {
72
+ const content = fileSystem.readFileSync(filePath);
73
+ return Buffer.isBuffer(content) ? content : Buffer.from(content);
74
+ }
75
+ function brokenLink(filePath, fileSystem) {
76
+ try {
77
+ fileSystem.statSync(filePath);
78
+ return false;
79
+ }
80
+ catch (error) {
81
+ if (missingOrUnreadable(error))
82
+ return true;
83
+ throw error;
84
+ }
85
+ }
86
+ function sorted(entries) {
87
+ // The legacy command pipes `find` into `sort`; the project environment uses
88
+ // the C locale, so preserve its byte ordering instead of ICU collation.
89
+ return entries.sort((left, right) => left.displayPath < right.displayPath ? -1 : left.displayPath > right.displayPath ? 1 : 0);
90
+ }
91
+ function resultError(error) {
92
+ const code = errorCode(error);
93
+ if (code === "EACCES" || code === "EPERM") {
94
+ return { code: 1, stdout: "", stderr: `md5sum: failed to read file: ${error instanceof Error ? error.message : String(error)}\n` };
95
+ }
96
+ throw error;
97
+ }
98
+ export function runWorkerSkillLint(args, dependencies) {
99
+ const { root, fs: fileSystem } = resolveWorkerCallerRoot(dependencies);
100
+ const roots = args.length > 0 ? args : DEFAULT_ROOTS;
101
+ const entries = sorted(roots.flatMap((entry) => collectEntries(entry, root, fileSystem)));
102
+ if (entries.length === 0) {
103
+ return {
104
+ code: 0,
105
+ stdout: `OK: worker-scoped skills are single-sourced (0 skill entries under: ${roots.join(" ")})\n`,
106
+ stderr: "",
107
+ };
108
+ }
109
+ const broken = [];
110
+ const hashGroups = new Map();
111
+ try {
112
+ for (const entry of entries) {
113
+ if (entry.symlink) {
114
+ if (brokenLink(entry.absolutePath, fileSystem))
115
+ broken.push(entry.displayPath);
116
+ continue;
117
+ }
118
+ const digest = crypto.createHash("md5").update(readFileForHash(entry.absolutePath, fileSystem)).digest("hex");
119
+ const domain = ownershipDomain(entry.displayPath);
120
+ const key = `${domain}|${digest}`;
121
+ const group = hashGroups.get(key);
122
+ if (group)
123
+ group.paths.push(entry.displayPath);
124
+ else
125
+ hashGroups.set(key, { domain, paths: [entry.displayPath] });
126
+ }
127
+ }
128
+ catch (error) {
129
+ return resultError(error);
130
+ }
131
+ const duplicates = [...hashGroups.values()].filter((group) => group.paths.length >= 2);
132
+ const findings = duplicates.length + broken.length;
133
+ if (findings === 0) {
134
+ return {
135
+ code: 0,
136
+ stdout: `OK: worker-scoped skills are single-sourced (${entries.length} skill entries scanned under: ${roots.join(" ")})\n`,
137
+ stderr: "",
138
+ };
139
+ }
140
+ const lines = [];
141
+ if (duplicates.length > 0) {
142
+ lines.push("lint-shared-worker-skills: FAIL — duplicated worker skills (single-source these via _shared-skills/ + relative symlinks):");
143
+ }
144
+ for (const group of duplicates) {
145
+ lines.push(` DUPLICATE in domain '${group.domain}' (identical content, ${group.paths.length} copies — pick one canonical and symlink the rest):`);
146
+ // The shell prefixes the newline-joined group only once, so only the first
147
+ // path carries these spaces in the resulting stderr.
148
+ lines.push(` ${group.paths.join("\n")}`);
149
+ }
150
+ if (broken.length > 0) {
151
+ if (duplicates.length === 0)
152
+ lines.push("lint-shared-worker-skills: FAIL — broken shared-skill symlink(s):");
153
+ lines.push(" BROKEN (symlink target does not resolve):");
154
+ for (const filePath of broken)
155
+ lines.push(` ${filePath}`);
156
+ }
157
+ lines.push("", " Fix: keep ONE canonical file in the narrowest-scope _shared-skills/ store", " (core/workers/_shared-skills, core/packages/<pack>/workers/_shared-skills,", " or companies/<co>/workers/_shared-skills) and replace each duplicate with a", " relative symlink. Helper: hq core worker share", " Doc: core/knowledge/public/hq-core/shared-worker-skills.md");
158
+ return { code: 1, stdout: "", stderr: `${lines.join("\n")}\n` };
159
+ }
160
+ //# sourceMappingURL=core-worker-lint-native.js.map
@@ -0,0 +1,5 @@
1
+ import { type WorkerSkillDependencies, type WorkerSkillResult } from "./core-worker-skills-runtime.js";
2
+ export declare const WORKER_SKILL_SHARE_ASSET = "core/scripts/share-worker-skill.sh";
3
+ export declare const WORKER_SKILL_SHARE_USAGE = "usage: $0 [--apply] [--force] <scope-dir> <skill-name> <worker-skill-path>...\n";
4
+ export declare function runWorkerSkillShare(argv: string[], dependencies: WorkerSkillDependencies): WorkerSkillResult;
5
+ //# sourceMappingURL=core-worker-share-native.d.ts.map
@@ -0,0 +1,260 @@
1
+ /** Native port of share-worker-skill.sh. */
2
+ import * as path from "node:path";
3
+ import { resolveBundledAsset } from "../utils/hq-roots.js";
4
+ import { existsFollowingLinks, isDirectory, isSymlink, resolveWorkerCallerRoot, runGit, errorCode, } from "./core-worker-skills-runtime.js";
5
+ export const WORKER_SKILL_SHARE_ASSET = "core/scripts/share-worker-skill.sh";
6
+ export const WORKER_SKILL_SHARE_USAGE = "usage: $0 [--apply] [--force] <scope-dir> <skill-name> <worker-skill-path>...\n";
7
+ function error(message, code = 2) {
8
+ return { code, stdout: "", stderr: `${message}\n` };
9
+ }
10
+ function toolFailure(output, tool, target, caught, destination) {
11
+ const code = errorCode(caught);
12
+ const reason = code === "EACCES" || code === "EPERM" ? "Permission denied"
13
+ : code === "ENOENT" ? "No such file or directory"
14
+ : code === "ENOTDIR" ? "Not a directory"
15
+ : code === "EEXIST" ? "File exists"
16
+ : code === "ENOSPC" ? "No space left on device"
17
+ : code === "EXDEV" ? "Invalid cross-device link"
18
+ : caught instanceof Error ? caught.message : String(caught);
19
+ const message = tool === "mkdir" ? `mkdir: cannot create directory ‘${target}’: ${reason}`
20
+ : tool === "mv" ? `mv: cannot move '${target}' to '${destination ?? ""}': ${reason}`
21
+ : tool === "rm" ? `rm: cannot remove '${target}': ${reason}`
22
+ : `ln: failed to create symbolic link '${target}': ${reason}`;
23
+ return { code: 1, stdout: `${output.join("\n")}\n`, stderr: `${message}\n` };
24
+ }
25
+ function removeOneTrailingSlash(value) {
26
+ return value.endsWith("/") ? value.slice(0, -1) : value;
27
+ }
28
+ function stripOneDotSlash(value) {
29
+ return value.startsWith("./") ? value.slice(2) : value;
30
+ }
31
+ function isUnderScope(workerPath, scopeDir) {
32
+ const worker = stripOneDotSlash(workerPath);
33
+ const scope = stripOneDotSlash(scopeDir);
34
+ return worker.startsWith(`${scope}/`);
35
+ }
36
+ function relativePath(fromDirectory, toFile) {
37
+ const from = stripOneDotSlash(fromDirectory);
38
+ const to = stripOneDotSlash(toFile);
39
+ const fromParts = from.split("/");
40
+ const toParts = to.split("/");
41
+ let index = 0;
42
+ while (index < fromParts.length && index < toParts.length && fromParts[index] === toParts[index])
43
+ index += 1;
44
+ const up = "../".repeat(fromParts.length - index);
45
+ const down = toParts.slice(index).join("/");
46
+ return `${up}${down}`;
47
+ }
48
+ function isRegularFile(filePath, fileSystem) {
49
+ if (isSymlink(filePath, fileSystem))
50
+ return false;
51
+ try {
52
+ return fileSystem.statSync(filePath).isFile();
53
+ }
54
+ catch {
55
+ return false;
56
+ }
57
+ }
58
+ function readLink(filePath, fileSystem) {
59
+ return fileSystem.readlinkSync(filePath);
60
+ }
61
+ function sameBytes(leftPath, rightPath, fileSystem) {
62
+ try {
63
+ const left = fileSystem.readFileSync(leftPath);
64
+ const right = fileSystem.readFileSync(rightPath);
65
+ return Buffer.from(left).equals(Buffer.from(right));
66
+ }
67
+ catch {
68
+ // The shell sends cmp's diagnostics to /dev/null and treats every nonzero
69
+ // result as a drifted copy.
70
+ return false;
71
+ }
72
+ }
73
+ function unlinkIfPresent(filePath, fileSystem) {
74
+ try {
75
+ fileSystem.unlinkSync(filePath);
76
+ }
77
+ catch (caught) {
78
+ const code = caught && typeof caught === "object" && "code" in caught
79
+ ? caught.code
80
+ : undefined;
81
+ if (code !== "ENOENT")
82
+ throw caught;
83
+ }
84
+ }
85
+ function forceSymlink(target, filePath, fileSystem) {
86
+ unlinkIfPresent(filePath, fileSystem);
87
+ fileSystem.symlinkSync(target, filePath);
88
+ }
89
+ function runtimeScriptPath(dependencies) {
90
+ return dependencies.scriptPath ?? resolveBundledAsset(WORKER_SKILL_SHARE_ASSET);
91
+ }
92
+ export function runWorkerSkillShare(argv, dependencies) {
93
+ let apply = false;
94
+ let force = false;
95
+ const args = [];
96
+ for (const value of argv) {
97
+ if (value === "--apply")
98
+ apply = true;
99
+ else if (value === "--force")
100
+ force = true;
101
+ else if (value.startsWith("-"))
102
+ return error(`unknown flag: ${value}`);
103
+ else
104
+ args.push(value);
105
+ }
106
+ if (args.length < 3) {
107
+ return error(WORKER_SKILL_SHARE_USAGE.replace("$0", runtimeScriptPath(dependencies)).trimEnd());
108
+ }
109
+ const { root, env, fs: fileSystem } = resolveWorkerCallerRoot(dependencies);
110
+ const scopeDir = removeOneTrailingSlash(args[0]);
111
+ const skillName = args[1];
112
+ const workerPaths = args.slice(2);
113
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(skillName)) {
114
+ return error(`error: skill-name '${skillName}' is not a bare skill name (no slashes, no .md)`);
115
+ }
116
+ if (scopeDir === "" || !isDirectory(path.resolve(root, scopeDir), fileSystem)) {
117
+ return error(`error: scope dir does not exist: ${scopeDir}`);
118
+ }
119
+ const canonical = `${scopeDir}/_shared-skills/${skillName}.md`;
120
+ const canonicalPath = path.resolve(root, canonical);
121
+ for (const originalPath of workerPaths) {
122
+ const workerPath = stripOneDotSlash(originalPath);
123
+ if (!isUnderScope(workerPath, scopeDir)) {
124
+ return error(`error: worker path '${workerPath}' is not under scope dir '${scopeDir}'\n canonical must live in the narrowest scope covering all sharers;\n cross-scope symlinks are disallowed. Aborting (no changes made).`);
125
+ }
126
+ }
127
+ const output = [`Canonical: ${canonical}`, `Scope: ${scopeDir}`];
128
+ if (!apply)
129
+ output.push("(dry run — pass --apply to make changes)");
130
+ const note = (message) => { output.push(message); };
131
+ const would = (message) => {
132
+ note(apply ? ` did: ${message}` : ` would: ${message}`);
133
+ };
134
+ const canonicalIsReusable = existsFollowingLinks(canonicalPath, fileSystem) && !isSymlink(canonicalPath, fileSystem);
135
+ if (canonicalIsReusable) {
136
+ note("canonical already exists (regular file) — reusing it");
137
+ }
138
+ else {
139
+ const seed = workerPaths.find((workerPath) => isRegularFile(path.resolve(root, stripOneDotSlash(workerPath)), fileSystem));
140
+ if (seed === undefined) {
141
+ return {
142
+ ...error("error: no canonical exists and no worker path is a regular file to seed it from"),
143
+ stdout: `${output.join("\n")}\n`,
144
+ };
145
+ }
146
+ would(`mkdir -p ${scopeDir}/_shared-skills`);
147
+ would(`git mv ${seed} ${canonical} (promote first copy to canonical)`);
148
+ if (apply) {
149
+ try {
150
+ fileSystem.mkdirSync(path.resolve(root, `${scopeDir}/_shared-skills`), { recursive: true });
151
+ }
152
+ catch (caught) {
153
+ return toolFailure(output, "mkdir", `${scopeDir}/_shared-skills`, caught);
154
+ }
155
+ let moved;
156
+ try {
157
+ moved = runGit(["mv", seed, canonical], root, env, dependencies.runGit);
158
+ }
159
+ catch (caught) {
160
+ if (errorCode(caught) === undefined)
161
+ throw caught;
162
+ moved = { code: 127, stdout: "" };
163
+ }
164
+ if (moved.code !== 0) {
165
+ try {
166
+ fileSystem.renameSync(path.resolve(root, stripOneDotSlash(seed)), canonicalPath);
167
+ }
168
+ catch (caught) {
169
+ return toolFailure(output, "mv", seed, caught, canonical);
170
+ }
171
+ }
172
+ }
173
+ }
174
+ for (const originalPath of workerPaths) {
175
+ const workerPath = stripOneDotSlash(originalPath);
176
+ const targetDir = path.posix.dirname(workerPath);
177
+ const relative = relativePath(targetDir, canonical);
178
+ const absoluteWorkerPath = path.resolve(root, workerPath);
179
+ if (isSymlink(absoluteWorkerPath, fileSystem)) {
180
+ const current = readLink(absoluteWorkerPath, fileSystem);
181
+ if (current === relative) {
182
+ note(`ok: ${workerPath} already links to canonical`);
183
+ continue;
184
+ }
185
+ would(`relink ${workerPath} -> ${relative} (was: ${current})`);
186
+ if (apply) {
187
+ try {
188
+ forceSymlink(relative, absoluteWorkerPath, fileSystem);
189
+ }
190
+ catch (caught) {
191
+ return toolFailure(output, "ln", workerPath, caught);
192
+ }
193
+ }
194
+ continue;
195
+ }
196
+ if (existsFollowingLinks(absoluteWorkerPath, fileSystem)) {
197
+ if (sameBytes(absoluteWorkerPath, canonicalPath, fileSystem)) {
198
+ would(`replace identical copy ${workerPath} with symlink -> ${relative}`);
199
+ if (apply) {
200
+ try {
201
+ unlinkIfPresent(absoluteWorkerPath, fileSystem);
202
+ }
203
+ catch (caught) {
204
+ return toolFailure(output, "rm", workerPath, caught);
205
+ }
206
+ try {
207
+ fileSystem.symlinkSync(relative, absoluteWorkerPath);
208
+ }
209
+ catch (caught) {
210
+ return toolFailure(output, "ln", workerPath, caught);
211
+ }
212
+ }
213
+ }
214
+ else if (force) {
215
+ would(`FORCE replace DRIFTED copy ${workerPath} with symlink -> ${relative} (content discarded)`);
216
+ if (apply) {
217
+ try {
218
+ unlinkIfPresent(absoluteWorkerPath, fileSystem);
219
+ }
220
+ catch (caught) {
221
+ return toolFailure(output, "rm", workerPath, caught);
222
+ }
223
+ try {
224
+ fileSystem.symlinkSync(relative, absoluteWorkerPath);
225
+ }
226
+ catch (caught) {
227
+ return toolFailure(output, "ln", workerPath, caught);
228
+ }
229
+ }
230
+ }
231
+ else {
232
+ return {
233
+ code: 3,
234
+ stdout: `${output.join("\n")}\n`,
235
+ stderr: ` DRIFT: ${workerPath} differs from canonical — reconcile by hand, then re-run\n (diff ${workerPath} ${canonical}), or pass --force to accept canonical.\n`,
236
+ };
237
+ }
238
+ }
239
+ else {
240
+ would(`create symlink ${workerPath} -> ${relative}`);
241
+ if (apply) {
242
+ try {
243
+ fileSystem.mkdirSync(path.resolve(root, targetDir), { recursive: true });
244
+ }
245
+ catch (caught) {
246
+ return toolFailure(output, "mkdir", targetDir, caught);
247
+ }
248
+ try {
249
+ fileSystem.symlinkSync(relative, absoluteWorkerPath);
250
+ }
251
+ catch (caught) {
252
+ return toolFailure(output, "ln", workerPath, caught);
253
+ }
254
+ }
255
+ }
256
+ }
257
+ output.push("done.");
258
+ return { code: 0, stdout: `${output.join("\n")}\n`, stderr: "" };
259
+ }
260
+ //# sourceMappingURL=core-worker-share-native.js.map
@@ -0,0 +1,8 @@
1
+ export type WorkerSkillCommandName = "lint" | "share";
2
+ export type WorkerSkillCommandContext = {
3
+ cwd: string;
4
+ hqRoot?: string;
5
+ };
6
+ /** Keep the shell rollback route byte-transparent while the family flag is off. */
7
+ export declare function runWorkerSkillCommand(name: WorkerSkillCommandName, args: string[], context: WorkerSkillCommandContext): Promise<number>;
8
+ //# sourceMappingURL=core-worker-skills-command.d.ts.map