harnessed 4.6.0 → 4.7.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/README.md +37 -3
- package/dist/cli.mjs +451 -295
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/manifests/optional/codegraph.yaml +6 -4
- package/manifests/optional/ecc.ee5-answers.json +13 -0
- package/manifests/optional/ecc.yaml +64 -0
- package/messages/en.json +4 -1
- package/messages/zh-Hans.json +4 -1
- package/package.json +1 -1
- package/workflows/capabilities.yaml +120 -0
package/dist/cli.mjs
CHANGED
|
@@ -38,7 +38,7 @@ var init_package = __esm({
|
|
|
38
38
|
"package.json"() {
|
|
39
39
|
package_default = {
|
|
40
40
|
name: "harnessed",
|
|
41
|
-
version: "4.
|
|
41
|
+
version: "4.7.0",
|
|
42
42
|
description: "AI coding harness package manager + composition orchestrator",
|
|
43
43
|
type: "module",
|
|
44
44
|
license: "Apache-2.0",
|
|
@@ -852,10 +852,92 @@ var init_origin_check = __esm({
|
|
|
852
852
|
"src/cli/lib/origin-check.ts"() {
|
|
853
853
|
}
|
|
854
854
|
});
|
|
855
|
-
function
|
|
855
|
+
function claudeDescriptor(home = homedir()) {
|
|
856
|
+
const claudeHome = join(home, ".claude");
|
|
857
|
+
return {
|
|
858
|
+
id: "claude",
|
|
859
|
+
homeDir: claudeHome,
|
|
860
|
+
stateRoot: join(claudeHome, "harnessed"),
|
|
861
|
+
settingsPath: join(claudeHome, "settings.json"),
|
|
862
|
+
skillsDir: join(claudeHome, "skills"),
|
|
863
|
+
commandsDir: join(claudeHome, "commands"),
|
|
864
|
+
pluginsRegistry: join(claudeHome, "plugins", "installed_plugins.json"),
|
|
865
|
+
// mcpConfigPath is a SIBLING of `.claude` (`<home>/.claude.json`), based on
|
|
866
|
+
// the same home base — not a child of homeDir.
|
|
867
|
+
mcpConfigPath: join(home, ".claude.json"),
|
|
868
|
+
// claude writes its env keys into JSON settings.json (capability present).
|
|
869
|
+
supportsEnvKeyWrite: true
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
function codexDescriptor(home = homedir()) {
|
|
873
|
+
const codexHome = join(home, ".codex");
|
|
874
|
+
const configToml = join(codexHome, "config.toml");
|
|
875
|
+
return {
|
|
876
|
+
id: "codex",
|
|
877
|
+
homeDir: codexHome,
|
|
878
|
+
stateRoot: join(codexHome, "harnessed"),
|
|
879
|
+
settingsPath: configToml,
|
|
880
|
+
// SHARED convention dir, NOT <homeDir>/skills.
|
|
881
|
+
skillsDir: join(home, ".agents", "skills"),
|
|
882
|
+
commandsDir: join(codexHome, "prompts"),
|
|
883
|
+
pluginsRegistry: null,
|
|
884
|
+
mcpConfigPath: configToml,
|
|
885
|
+
supportsEnvKeyWrite: false
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
function descriptorById(id, home) {
|
|
889
|
+
if (id === "claude") return claudeDescriptor(home);
|
|
890
|
+
if (id === "codex") return codexDescriptor(home);
|
|
891
|
+
return void 0;
|
|
892
|
+
}
|
|
893
|
+
function detectPlatform(home = homedir()) {
|
|
894
|
+
const base = claudeDescriptor(home);
|
|
856
895
|
const override = process.env.HARNESSED_ROOT_OVERRIDE;
|
|
857
|
-
if (override !== void 0 && override !== "") return override;
|
|
858
|
-
|
|
896
|
+
if (override !== void 0 && override !== "") return { ...base, stateRoot: override };
|
|
897
|
+
const envPlatform = process.env.HARNESSED_PLATFORM;
|
|
898
|
+
if (envPlatform !== void 0 && envPlatform !== "") {
|
|
899
|
+
const d = descriptorById(envPlatform, home);
|
|
900
|
+
if (d) return d;
|
|
901
|
+
}
|
|
902
|
+
try {
|
|
903
|
+
const pin = readFileSync(join(base.stateRoot, ".platform"), "utf8").trim();
|
|
904
|
+
const d = descriptorById(pin, home);
|
|
905
|
+
if (d) return d;
|
|
906
|
+
} catch {
|
|
907
|
+
}
|
|
908
|
+
try {
|
|
909
|
+
if (existsSync(base.homeDir)) return base;
|
|
910
|
+
const codex = codexDescriptor(home);
|
|
911
|
+
if (existsSync(codex.homeDir)) return codex;
|
|
912
|
+
} catch {
|
|
913
|
+
}
|
|
914
|
+
return base;
|
|
915
|
+
}
|
|
916
|
+
function getSettingsPath(home) {
|
|
917
|
+
return detectPlatform(home).settingsPath;
|
|
918
|
+
}
|
|
919
|
+
function getSkillsDir(home) {
|
|
920
|
+
return detectPlatform(home).skillsDir;
|
|
921
|
+
}
|
|
922
|
+
function getCommandsDir(home) {
|
|
923
|
+
return detectPlatform(home).commandsDir;
|
|
924
|
+
}
|
|
925
|
+
function getPluginsRegistry(home) {
|
|
926
|
+
return detectPlatform(home).pluginsRegistry;
|
|
927
|
+
}
|
|
928
|
+
function getMcpConfigPath(home) {
|
|
929
|
+
return detectPlatform(home).mcpConfigPath;
|
|
930
|
+
}
|
|
931
|
+
function harnessSkillsDirs(home) {
|
|
932
|
+
const dirs = [claudeDescriptor(home).skillsDir, codexDescriptor(home).skillsDir];
|
|
933
|
+
return [...new Set(dirs)];
|
|
934
|
+
}
|
|
935
|
+
var init_platform = __esm({
|
|
936
|
+
"src/installers/lib/platform.ts"() {
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
function getHarnessedRoot() {
|
|
940
|
+
return detectPlatform().stateRoot;
|
|
859
941
|
}
|
|
860
942
|
function harnessedSubdir(name) {
|
|
861
943
|
return join(getHarnessedRoot(), name);
|
|
@@ -890,6 +972,7 @@ function migrateLegacyHarnessedRoot() {
|
|
|
890
972
|
}
|
|
891
973
|
var init_harnessedRoot = __esm({
|
|
892
974
|
"src/installers/lib/harnessedRoot.ts"() {
|
|
975
|
+
init_platform();
|
|
893
976
|
}
|
|
894
977
|
});
|
|
895
978
|
function getBackupRoot() {
|
|
@@ -2056,7 +2139,7 @@ function nameToYamlHintPath(name) {
|
|
|
2056
2139
|
function shouldOverwriteFile(content) {
|
|
2057
2140
|
return HARNESSED_MARKER_RX.test(content) || V3_4_3_SIGNATURE_SUB_RX.test(content) || V3_4_3_SIGNATURE_MASTER_RX.test(content);
|
|
2058
2141
|
}
|
|
2059
|
-
async function writeAllCommands(slashNames, commandsDir, rolePrompts, capabilities, installedPlugins, installedUserSkills, writer, fileExists2 = existsSync,
|
|
2142
|
+
async function writeAllCommands(slashNames, commandsDir, rolePrompts, capabilities, installedPlugins, installedUserSkills, writer, fileExists2 = existsSync, readFileSync13 = (p5) => readFileSync(p5, "utf8")) {
|
|
2060
2143
|
const results = [];
|
|
2061
2144
|
const aggregatedWarnings = /* @__PURE__ */ new Set();
|
|
2062
2145
|
for (const name of slashNames) {
|
|
@@ -2075,7 +2158,7 @@ async function writeAllCommands(slashNames, commandsDir, rolePrompts, capabiliti
|
|
|
2075
2158
|
if (fileExists2(path)) {
|
|
2076
2159
|
let existing = "";
|
|
2077
2160
|
try {
|
|
2078
|
-
existing =
|
|
2161
|
+
existing = readFileSync13(path);
|
|
2079
2162
|
} catch {
|
|
2080
2163
|
existing = "";
|
|
2081
2164
|
}
|
|
@@ -4373,7 +4456,7 @@ var init_check_mattpocock_skills = __esm({
|
|
|
4373
4456
|
}
|
|
4374
4457
|
});
|
|
4375
4458
|
function getUserClaudeJsonPath() {
|
|
4376
|
-
return
|
|
4459
|
+
return getMcpConfigPath();
|
|
4377
4460
|
}
|
|
4378
4461
|
async function readUserClaudeJson() {
|
|
4379
4462
|
const path = getUserClaudeJsonPath();
|
|
@@ -4399,21 +4482,20 @@ async function isMcpServerRegistered(name) {
|
|
|
4399
4482
|
return Object.hasOwn(servers, name);
|
|
4400
4483
|
}
|
|
4401
4484
|
async function isPluginRegistered(pluginName) {
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
if (
|
|
4409
|
-
|
|
4485
|
+
const registryPath = getPluginsRegistry();
|
|
4486
|
+
if (registryPath !== null) {
|
|
4487
|
+
try {
|
|
4488
|
+
const raw = await readFile(registryPath, "utf8");
|
|
4489
|
+
const parsed = JSON.parse(raw);
|
|
4490
|
+
const plugins = parsed.plugins;
|
|
4491
|
+
if (plugins && typeof plugins === "object") {
|
|
4492
|
+
if (Object.hasOwn(plugins, pluginName)) return true;
|
|
4493
|
+
if (Object.keys(plugins).some((k) => k.split("@")[0] === pluginName)) return true;
|
|
4494
|
+
}
|
|
4495
|
+
} catch {
|
|
4410
4496
|
}
|
|
4411
|
-
} catch {
|
|
4412
4497
|
}
|
|
4413
|
-
for (const path of [
|
|
4414
|
-
join(homedir(), ".claude", "settings.json"),
|
|
4415
|
-
join(homedir(), ".claude.json")
|
|
4416
|
-
]) {
|
|
4498
|
+
for (const path of [getSettingsPath(), getMcpConfigPath()]) {
|
|
4417
4499
|
try {
|
|
4418
4500
|
const raw = await readFile(path, "utf8");
|
|
4419
4501
|
const parsed = JSON.parse(raw);
|
|
@@ -4429,6 +4511,7 @@ async function isPluginRegistered(pluginName) {
|
|
|
4429
4511
|
}
|
|
4430
4512
|
var init_readClaudeConfig = __esm({
|
|
4431
4513
|
"src/installers/lib/readClaudeConfig.ts"() {
|
|
4514
|
+
init_platform();
|
|
4432
4515
|
}
|
|
4433
4516
|
});
|
|
4434
4517
|
|
|
@@ -4827,6 +4910,7 @@ var init_ccHookAdd2 = __esm({
|
|
|
4827
4910
|
init_confirm();
|
|
4828
4911
|
init_diff();
|
|
4829
4912
|
init_err();
|
|
4913
|
+
init_platform();
|
|
4830
4914
|
init_preflight();
|
|
4831
4915
|
init_state2();
|
|
4832
4916
|
installCcHookAdd = async (ctx) => {
|
|
@@ -4850,10 +4934,10 @@ var init_ccHookAdd2 = __esm({
|
|
|
4850
4934
|
const e = pre.errors[0] ?? err(ctx, "/", "preflight failed", "preflight");
|
|
4851
4935
|
return { ok: false, phase: "preflight", error: e };
|
|
4852
4936
|
}
|
|
4853
|
-
const
|
|
4937
|
+
const settingsPath = getSettingsPath();
|
|
4854
4938
|
let existing;
|
|
4855
4939
|
try {
|
|
4856
|
-
existing = await readFile(
|
|
4940
|
+
existing = await readFile(settingsPath, "utf8");
|
|
4857
4941
|
} catch {
|
|
4858
4942
|
existing = null;
|
|
4859
4943
|
}
|
|
@@ -4884,7 +4968,7 @@ var init_ccHookAdd2 = __esm({
|
|
|
4884
4968
|
const newText = `${JSON.stringify(settings, null, 2)}
|
|
4885
4969
|
`;
|
|
4886
4970
|
const plan = {
|
|
4887
|
-
files: [{ target:
|
|
4971
|
+
files: [{ target: settingsPath, scope: "HOME", oldText: existing ?? "", newText }]
|
|
4888
4972
|
};
|
|
4889
4973
|
if (!ctx.opts.quiet) process.stdout.write(renderDiff(plan, ctx));
|
|
4890
4974
|
const conf = await confirmAt("L3", { ...ctx});
|
|
@@ -4895,10 +4979,10 @@ var init_ccHookAdd2 = __esm({
|
|
|
4895
4979
|
if (ctx.opts.dryRun) return { aborted: true, reason: "user-cancel" };
|
|
4896
4980
|
const bk = await backup(plan, ctx);
|
|
4897
4981
|
if (!bk.ok) return { ok: false, phase: "preflight", error: bk.error };
|
|
4898
|
-
await writeFile(
|
|
4982
|
+
await writeFile(settingsPath, newText);
|
|
4899
4983
|
let verify;
|
|
4900
4984
|
try {
|
|
4901
|
-
verify = JSON.parse(await readFile(
|
|
4985
|
+
verify = JSON.parse(await readFile(settingsPath, "utf8"));
|
|
4902
4986
|
} catch (e) {
|
|
4903
4987
|
return {
|
|
4904
4988
|
ok: false,
|
|
@@ -4926,7 +5010,7 @@ var init_ccHookAdd2 = __esm({
|
|
|
4926
5010
|
};
|
|
4927
5011
|
}
|
|
4928
5012
|
await updateInstalled(ctx.cwd, ctx.manifest.metadata.name, "", "");
|
|
4929
|
-
return { ok: true, backupId: bk.backupId, appliedFiles: [
|
|
5013
|
+
return { ok: true, backupId: bk.backupId, appliedFiles: [settingsPath] };
|
|
4930
5014
|
};
|
|
4931
5015
|
}
|
|
4932
5016
|
});
|
|
@@ -4982,10 +5066,10 @@ async function spawnCmd(ctx, cmd, args, timeoutMs, opts) {
|
|
|
4982
5066
|
child.stderr?.setEncoding("utf8").on("data", (chunk) => {
|
|
4983
5067
|
stderr += chunk;
|
|
4984
5068
|
});
|
|
4985
|
-
return await new Promise((
|
|
5069
|
+
return await new Promise((resolve18) => {
|
|
4986
5070
|
const timer = setTimeout(() => {
|
|
4987
5071
|
child.kill("SIGKILL");
|
|
4988
|
-
|
|
5072
|
+
resolve18({
|
|
4989
5073
|
ok: false,
|
|
4990
5074
|
phase: "spawn",
|
|
4991
5075
|
error: {
|
|
@@ -5001,7 +5085,7 @@ async function spawnCmd(ctx, cmd, args, timeoutMs, opts) {
|
|
|
5001
5085
|
child.on("error", (err2) => {
|
|
5002
5086
|
clearTimeout(timer);
|
|
5003
5087
|
const bashMissing = triedBash && err2.code === "ENOENT";
|
|
5004
|
-
|
|
5088
|
+
resolve18({
|
|
5005
5089
|
ok: false,
|
|
5006
5090
|
phase: "spawn",
|
|
5007
5091
|
error: {
|
|
@@ -5016,7 +5100,7 @@ async function spawnCmd(ctx, cmd, args, timeoutMs, opts) {
|
|
|
5016
5100
|
});
|
|
5017
5101
|
child.on("close", (code) => {
|
|
5018
5102
|
clearTimeout(timer);
|
|
5019
|
-
|
|
5103
|
+
resolve18({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
|
|
5020
5104
|
});
|
|
5021
5105
|
});
|
|
5022
5106
|
}
|
|
@@ -5056,7 +5140,7 @@ async function detectNative(ctx) {
|
|
|
5056
5140
|
const indicators = INSTALLED_INDICATORS[name];
|
|
5057
5141
|
if (indicators) {
|
|
5058
5142
|
for (const ind of indicators) {
|
|
5059
|
-
const dir = join(
|
|
5143
|
+
const dir = join(getSkillsDir(), ind);
|
|
5060
5144
|
try {
|
|
5061
5145
|
await access(dir);
|
|
5062
5146
|
return true;
|
|
@@ -5075,8 +5159,8 @@ async function detectNative(ctx) {
|
|
|
5075
5159
|
}
|
|
5076
5160
|
if (method === "npx-skill-installer") {
|
|
5077
5161
|
const skillName = extractSkillName(cmd, name);
|
|
5078
|
-
for (const
|
|
5079
|
-
const skillMd = join(
|
|
5162
|
+
for (const skillsDir of harnessSkillsDirs()) {
|
|
5163
|
+
const skillMd = join(skillsDir, skillName, "SKILL.md");
|
|
5080
5164
|
try {
|
|
5081
5165
|
await access(skillMd);
|
|
5082
5166
|
return true;
|
|
@@ -5095,7 +5179,7 @@ async function detectNative(ctx) {
|
|
|
5095
5179
|
}
|
|
5096
5180
|
}
|
|
5097
5181
|
if (method === "npm-cli") {
|
|
5098
|
-
const skillDir = join(
|
|
5182
|
+
const skillDir = join(getSkillsDir(), name);
|
|
5099
5183
|
try {
|
|
5100
5184
|
await access(skillDir);
|
|
5101
5185
|
return true;
|
|
@@ -5129,6 +5213,7 @@ async function isAlreadyInstalled(ctx, opts = {}) {
|
|
|
5129
5213
|
var IDEMPOTENT_CHECK_TIMEOUT_MS, INSTALLED_INDICATORS;
|
|
5130
5214
|
var init_idempotent = __esm({
|
|
5131
5215
|
"src/installers/lib/idempotent.ts"() {
|
|
5216
|
+
init_platform();
|
|
5132
5217
|
init_readClaudeConfig();
|
|
5133
5218
|
init_spawn();
|
|
5134
5219
|
IDEMPOTENT_CHECK_TIMEOUT_MS = 1e4;
|
|
@@ -5139,7 +5224,7 @@ var init_idempotent = __esm({
|
|
|
5139
5224
|
}
|
|
5140
5225
|
});
|
|
5141
5226
|
function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
|
|
5142
|
-
return new Promise((
|
|
5227
|
+
return new Promise((resolve18) => {
|
|
5143
5228
|
const isWin = process.platform === "win32";
|
|
5144
5229
|
const child = isWin ? spawn("cmd.exe", ["/c", "claude", ...claudeArgs], { cwd, windowsHide: true }) : spawn("claude", claudeArgs, { cwd, shell: false });
|
|
5145
5230
|
let stderr = "";
|
|
@@ -5148,15 +5233,15 @@ function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
|
|
|
5148
5233
|
});
|
|
5149
5234
|
const timer = setTimeout(() => {
|
|
5150
5235
|
child.kill("SIGKILL");
|
|
5151
|
-
|
|
5236
|
+
resolve18({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
|
|
5152
5237
|
}, timeoutMs);
|
|
5153
5238
|
child.on("error", (e) => {
|
|
5154
5239
|
clearTimeout(timer);
|
|
5155
|
-
|
|
5240
|
+
resolve18({ exitCode: -1, stderr: `${stderr}${e.message}` });
|
|
5156
5241
|
});
|
|
5157
5242
|
child.on("close", (code) => {
|
|
5158
5243
|
clearTimeout(timer);
|
|
5159
|
-
|
|
5244
|
+
resolve18({ exitCode: code ?? -1, stderr });
|
|
5160
5245
|
});
|
|
5161
5246
|
});
|
|
5162
5247
|
}
|
|
@@ -5326,7 +5411,7 @@ ${newEntry}
|
|
|
5326
5411
|
}
|
|
5327
5412
|
});
|
|
5328
5413
|
function gitRevParseHead(cwd, timeoutMs = 1e4) {
|
|
5329
|
-
return new Promise((
|
|
5414
|
+
return new Promise((resolve18) => {
|
|
5330
5415
|
const isWin = process.platform === "win32";
|
|
5331
5416
|
const child = isWin ? spawn("cmd.exe", ["/c", "git", "rev-parse", "HEAD"], { cwd, windowsHide: true }) : spawn("git", ["rev-parse", "HEAD"], { cwd, shell: false });
|
|
5332
5417
|
let stdout2 = "";
|
|
@@ -5335,15 +5420,15 @@ function gitRevParseHead(cwd, timeoutMs = 1e4) {
|
|
|
5335
5420
|
});
|
|
5336
5421
|
const timer = setTimeout(() => {
|
|
5337
5422
|
child.kill("SIGKILL");
|
|
5338
|
-
|
|
5423
|
+
resolve18({ sha: "", exit: -1 });
|
|
5339
5424
|
}, timeoutMs);
|
|
5340
5425
|
child.on("error", () => {
|
|
5341
5426
|
clearTimeout(timer);
|
|
5342
|
-
|
|
5427
|
+
resolve18({ sha: "", exit: -1 });
|
|
5343
5428
|
});
|
|
5344
5429
|
child.on("close", (code) => {
|
|
5345
5430
|
clearTimeout(timer);
|
|
5346
|
-
|
|
5431
|
+
resolve18({ sha: stdout2.trim(), exit: code ?? -1 });
|
|
5347
5432
|
});
|
|
5348
5433
|
});
|
|
5349
5434
|
}
|
|
@@ -6284,8 +6369,14 @@ var init_install_base = __esm({
|
|
|
6284
6369
|
// src/checkpoint/nextStep.ts
|
|
6285
6370
|
var nextStep_exports = {};
|
|
6286
6371
|
__export(nextStep_exports, {
|
|
6372
|
+
resolveAutoTransition: () => resolveAutoTransition,
|
|
6287
6373
|
resolveNext: () => resolveNext
|
|
6288
6374
|
});
|
|
6375
|
+
function resolveAutoTransition(envelopeValue, env = process.env.HARNESSED_AUTO_TRANSITION) {
|
|
6376
|
+
if (env === "true") return true;
|
|
6377
|
+
if (env === "false") return false;
|
|
6378
|
+
return envelopeValue ?? true;
|
|
6379
|
+
}
|
|
6289
6380
|
function resolveNext(ledger, autoTransition) {
|
|
6290
6381
|
const sub = nextPending(ledger);
|
|
6291
6382
|
if (sub === null) return { next: "done" };
|
|
@@ -6882,7 +6973,7 @@ function renderHumanTable(records) {
|
|
|
6882
6973
|
}
|
|
6883
6974
|
}
|
|
6884
6975
|
function pipeToJq(filterExpr, lines) {
|
|
6885
|
-
return new Promise((
|
|
6976
|
+
return new Promise((resolve18, reject) => {
|
|
6886
6977
|
const child = spawn("jq", [filterExpr], {
|
|
6887
6978
|
stdio: ["pipe", "inherit", "inherit"],
|
|
6888
6979
|
windowsHide: true
|
|
@@ -6891,12 +6982,12 @@ function pipeToJq(filterExpr, lines) {
|
|
|
6891
6982
|
const e = err2;
|
|
6892
6983
|
if (e.code === "ENOENT") {
|
|
6893
6984
|
console.error(t("audit_log.jq_missing"));
|
|
6894
|
-
|
|
6985
|
+
resolve18(1);
|
|
6895
6986
|
} else {
|
|
6896
6987
|
reject(err2);
|
|
6897
6988
|
}
|
|
6898
6989
|
});
|
|
6899
|
-
child.on("close", (code) =>
|
|
6990
|
+
child.on("close", (code) => resolve18(code ?? 0));
|
|
6900
6991
|
child.stdin.write(lines.join("\n"));
|
|
6901
6992
|
child.stdin.end();
|
|
6902
6993
|
});
|
|
@@ -7458,24 +7549,26 @@ function registerInstall(program2) {
|
|
|
7458
7549
|
const { resolveAlias: resolveAlias2 } = await Promise.resolve().then(() => (init_aliases(), aliases_exports));
|
|
7459
7550
|
const resolvedName = resolveAlias2(name) ?? name;
|
|
7460
7551
|
checkPathSafe(resolvedName);
|
|
7461
|
-
const
|
|
7462
|
-
|
|
7552
|
+
const candidatePaths = ["tools", "skill-packs", "optional"].map(
|
|
7553
|
+
(dir) => resolve(getPackageRoot(), `manifests/${dir}/${resolvedName}.yaml`)
|
|
7554
|
+
);
|
|
7463
7555
|
let yamlSrc;
|
|
7464
|
-
let chosenPath
|
|
7465
|
-
|
|
7466
|
-
yamlSrc = await readFile(manifestPath, "utf8");
|
|
7467
|
-
} catch {
|
|
7556
|
+
let chosenPath;
|
|
7557
|
+
for (const p5 of candidatePaths) {
|
|
7468
7558
|
try {
|
|
7469
|
-
yamlSrc = await readFile(
|
|
7470
|
-
chosenPath =
|
|
7559
|
+
yamlSrc = await readFile(p5, "utf8");
|
|
7560
|
+
chosenPath = p5;
|
|
7561
|
+
break;
|
|
7471
7562
|
} catch {
|
|
7472
|
-
console.error(
|
|
7473
|
-
`${t("install.manifest_not_found", { name: resolvedName })}
|
|
7474
|
-
${t("install.manifest_not_found.fix", { name: resolvedName })}`
|
|
7475
|
-
);
|
|
7476
|
-
process.exit(1);
|
|
7477
7563
|
}
|
|
7478
7564
|
}
|
|
7565
|
+
if (yamlSrc === void 0 || chosenPath === void 0) {
|
|
7566
|
+
console.error(
|
|
7567
|
+
`${t("install.manifest_not_found", { name: resolvedName })}
|
|
7568
|
+
${t("install.manifest_not_found.fix", { name: resolvedName })}`
|
|
7569
|
+
);
|
|
7570
|
+
process.exit(1);
|
|
7571
|
+
}
|
|
7479
7572
|
const v = validateManifestFile(yamlSrc, chosenPath);
|
|
7480
7573
|
if (!v.ok) {
|
|
7481
7574
|
for (const e of v.errors) console.error(`error: ${e.message} at ${e.path}`);
|
|
@@ -7535,6 +7628,181 @@ function registerLearn(program2) {
|
|
|
7535
7628
|
process.exit(0);
|
|
7536
7629
|
});
|
|
7537
7630
|
}
|
|
7631
|
+
|
|
7632
|
+
// src/cli/lib/here.ts
|
|
7633
|
+
init_nextStep();
|
|
7634
|
+
init_harnessedRoot();
|
|
7635
|
+
|
|
7636
|
+
// src/cli/status.ts
|
|
7637
|
+
init_evidence();
|
|
7638
|
+
init_ledger();
|
|
7639
|
+
init_state();
|
|
7640
|
+
init_harnessedRoot();
|
|
7641
|
+
init_state2();
|
|
7642
|
+
function statusMarker(status) {
|
|
7643
|
+
switch (status) {
|
|
7644
|
+
case "done":
|
|
7645
|
+
return "\u2705 done";
|
|
7646
|
+
case "pending":
|
|
7647
|
+
return "\u23F3 pending";
|
|
7648
|
+
case "failed":
|
|
7649
|
+
return "\u2717 failed";
|
|
7650
|
+
case "skipped":
|
|
7651
|
+
return "\u2B1C skipped";
|
|
7652
|
+
case "rejected":
|
|
7653
|
+
return "\u{1F6AB} rejected";
|
|
7654
|
+
}
|
|
7655
|
+
}
|
|
7656
|
+
function evidenceLabel(entry) {
|
|
7657
|
+
switch (entry.evidence_status) {
|
|
7658
|
+
case "verified":
|
|
7659
|
+
return "evidence: verified";
|
|
7660
|
+
case "overridden":
|
|
7661
|
+
return "evidence: overridden (--force)";
|
|
7662
|
+
default:
|
|
7663
|
+
return "evidence: none_declared";
|
|
7664
|
+
}
|
|
7665
|
+
}
|
|
7666
|
+
async function buildRecoverLines(workflow, ledger) {
|
|
7667
|
+
const lines = [];
|
|
7668
|
+
if (!workflow || ledger.length === 0) {
|
|
7669
|
+
lines.push("no ledger \u2014 run gates + start");
|
|
7670
|
+
return lines;
|
|
7671
|
+
}
|
|
7672
|
+
lines.push(`workflow: ${workflow.phase} (${workflow.status}) started ${workflow.started_at}`);
|
|
7673
|
+
const next = nextPending(ledger);
|
|
7674
|
+
for (const e of ledger) {
|
|
7675
|
+
const marker = statusMarker(e.status);
|
|
7676
|
+
let suffix = "";
|
|
7677
|
+
if (e.status === "skipped") {
|
|
7678
|
+
suffix = e.reason ? ` (skipped: ${e.reason})` : "";
|
|
7679
|
+
} else if (e.status === "done") {
|
|
7680
|
+
suffix = ` ${evidenceLabel(e)}`;
|
|
7681
|
+
}
|
|
7682
|
+
const arrow = e.sub === next ? " \u2190 next" : "";
|
|
7683
|
+
lines.push(` ${e.sub} ${marker}${suffix}${arrow}`);
|
|
7684
|
+
}
|
|
7685
|
+
if (next) {
|
|
7686
|
+
lines.push(`\u2192 next: harnessed prompt ${next}`);
|
|
7687
|
+
} else {
|
|
7688
|
+
lines.push("\u2192 all subs resolved (no pending work)");
|
|
7689
|
+
}
|
|
7690
|
+
for (const e of ledger) {
|
|
7691
|
+
if (!e.evidence || e.evidence.length === 0) continue;
|
|
7692
|
+
const drift = await detectDrift(e.evidence);
|
|
7693
|
+
for (const d of drift) {
|
|
7694
|
+
const now = d.now === "" ? "missing" : `${d.now.slice(0, 7)}\u2026`;
|
|
7695
|
+
lines.push(`\u26A0 drift: ${d.path} sha256 changed (was ${d.was.slice(0, 7)}\u2026, now ${now})`);
|
|
7696
|
+
}
|
|
7697
|
+
}
|
|
7698
|
+
return lines;
|
|
7699
|
+
}
|
|
7700
|
+
function registerStatus(program2) {
|
|
7701
|
+
program2.command("status").description("Show installed upstreams (from <harnessed-root>/state.json)").option("--recover", "structured recovery view of the active workflow ledger (v5.0 Spec 1 B)").action(async (opts) => {
|
|
7702
|
+
if (opts.recover) {
|
|
7703
|
+
const wf = await readCurrentWorkflow();
|
|
7704
|
+
const ledger = wf?.sub_progress ?? [];
|
|
7705
|
+
const lines = await buildRecoverLines(wf, ledger);
|
|
7706
|
+
for (const l of lines) console.log(l);
|
|
7707
|
+
return;
|
|
7708
|
+
}
|
|
7709
|
+
const state = await readState(process.cwd());
|
|
7710
|
+
const names = Object.keys(state.installed).sort();
|
|
7711
|
+
if (names.length === 0) {
|
|
7712
|
+
console.log(t("status.no_installs", { path: harnessedFile("state.json") }));
|
|
7713
|
+
} else {
|
|
7714
|
+
for (const n of names) {
|
|
7715
|
+
const e = state.installed[n];
|
|
7716
|
+
if (!e) continue;
|
|
7717
|
+
console.log(`${n} @ ${e.version} (installed ${e.installedAt})`);
|
|
7718
|
+
}
|
|
7719
|
+
console.log(t("status.summary_installs", { count: names.length }));
|
|
7720
|
+
}
|
|
7721
|
+
const lockPath = harnessedFile(".lock");
|
|
7722
|
+
try {
|
|
7723
|
+
const isLocked = await lockfile.check(getHarnessedRoot(), {
|
|
7724
|
+
lockfilePath: lockPath,
|
|
7725
|
+
stale: 1e4
|
|
7726
|
+
});
|
|
7727
|
+
if (isLocked) {
|
|
7728
|
+
const s = await stat(lockPath);
|
|
7729
|
+
const ageMs = Date.now() - s.mtime.getTime();
|
|
7730
|
+
const stale = ageMs > 1e4;
|
|
7731
|
+
console.log(
|
|
7732
|
+
t("status.lock_held", {
|
|
7733
|
+
since: s.mtime.toISOString(),
|
|
7734
|
+
staleSuffix: stale ? t("status.lock_held.stale_suffix") : ""
|
|
7735
|
+
})
|
|
7736
|
+
);
|
|
7737
|
+
console.log(t("status.lock_release_hint", { path: lockPath }));
|
|
7738
|
+
} else {
|
|
7739
|
+
console.log(t("status.lock_free"));
|
|
7740
|
+
}
|
|
7741
|
+
} catch {
|
|
7742
|
+
}
|
|
7743
|
+
});
|
|
7744
|
+
}
|
|
7745
|
+
|
|
7746
|
+
// src/cli/lib/here.ts
|
|
7747
|
+
function parseBareInvocation(argvRest) {
|
|
7748
|
+
const tokens = [];
|
|
7749
|
+
for (let i = 0; i < argvRest.length; i++) {
|
|
7750
|
+
const a = argvRest[i];
|
|
7751
|
+
if (a === "--lang") {
|
|
7752
|
+
i++;
|
|
7753
|
+
continue;
|
|
7754
|
+
}
|
|
7755
|
+
if (a?.startsWith("--lang=")) continue;
|
|
7756
|
+
if (a !== void 0) tokens.push(a);
|
|
7757
|
+
}
|
|
7758
|
+
if (tokens.length === 0) return { here: true, json: false };
|
|
7759
|
+
if (tokens.length === 1 && tokens[0] === "--json") return { here: true, json: true };
|
|
7760
|
+
return { here: false, json: false };
|
|
7761
|
+
}
|
|
7762
|
+
async function buildHereView(wf, ledger, auto) {
|
|
7763
|
+
if (!wf) {
|
|
7764
|
+
return {
|
|
7765
|
+
lines: [
|
|
7766
|
+
t("here.no_workflow", { path: harnessedFile("workflows.json") }),
|
|
7767
|
+
t("here.no_workflow.hint")
|
|
7768
|
+
],
|
|
7769
|
+
json: { active: false }
|
|
7770
|
+
};
|
|
7771
|
+
}
|
|
7772
|
+
const body = await buildRecoverLines(wf, ledger);
|
|
7773
|
+
const step = resolveNext(ledger, auto);
|
|
7774
|
+
const lines = [
|
|
7775
|
+
...body,
|
|
7776
|
+
`NEXT: ${step.next}`,
|
|
7777
|
+
...step.sub ? [`\u2192 run: harnessed prompt ${step.sub}`] : []
|
|
7778
|
+
];
|
|
7779
|
+
return {
|
|
7780
|
+
lines,
|
|
7781
|
+
json: {
|
|
7782
|
+
active: true,
|
|
7783
|
+
phase: wf.phase,
|
|
7784
|
+
status: wf.status,
|
|
7785
|
+
started_at: wf.started_at,
|
|
7786
|
+
next: step.next,
|
|
7787
|
+
sub: step.sub,
|
|
7788
|
+
hint: step.hint,
|
|
7789
|
+
sub_progress: ledger
|
|
7790
|
+
}
|
|
7791
|
+
};
|
|
7792
|
+
}
|
|
7793
|
+
async function runHere(opts) {
|
|
7794
|
+
const { readCurrentWorkflow: readCurrentWorkflow2 } = await Promise.resolve().then(() => (init_state(), state_exports));
|
|
7795
|
+
const wf = await readCurrentWorkflow2();
|
|
7796
|
+
const ledger = wf?.sub_progress ?? [];
|
|
7797
|
+
const auto = resolveAutoTransition(wf?.auto_transition);
|
|
7798
|
+
const view = await buildHereView(wf, ledger, auto);
|
|
7799
|
+
if (opts.json) {
|
|
7800
|
+
console.log(JSON.stringify(view.json, null, 2));
|
|
7801
|
+
} else {
|
|
7802
|
+
for (const l of view.lines) console.log(l);
|
|
7803
|
+
}
|
|
7804
|
+
process.exit(0);
|
|
7805
|
+
}
|
|
7538
7806
|
var QA = [
|
|
7539
7807
|
{ q: "\u2460 \u662F\u771F reusable surface \u8FD8\u662F\u4E34\u65F6 wrapper?", f: "q1_reusable_surface" },
|
|
7540
7808
|
{ q: "\u2461 \u4E0A\u6E38\u540D\u5B57 fit \u9879\u76EE shape \u5417? \u6709\u73B0\u6709\u547D\u540D\u51B2\u7A81\u5417?", f: "q2_name_fit" },
|
|
@@ -7588,21 +7856,15 @@ function registerManifestAdd(program2) {
|
|
|
7588
7856
|
}
|
|
7589
7857
|
|
|
7590
7858
|
// src/cli/next.ts
|
|
7591
|
-
function resolveAutoTransition(envelopeValue) {
|
|
7592
|
-
const env = process.env.HARNESSED_AUTO_TRANSITION;
|
|
7593
|
-
if (env === "true") return true;
|
|
7594
|
-
if (env === "false") return false;
|
|
7595
|
-
return envelopeValue ?? true;
|
|
7596
|
-
}
|
|
7597
7859
|
function registerNext(program2) {
|
|
7598
7860
|
program2.command("next").description(
|
|
7599
7861
|
"Print the deterministic next-step contract (NEXT: auto|manual|done) for the active workflow"
|
|
7600
7862
|
).action(async () => {
|
|
7601
7863
|
const { readCurrentWorkflow: readCurrentWorkflow2 } = await Promise.resolve().then(() => (init_state(), state_exports));
|
|
7602
|
-
const { resolveNext: resolveNext2 } = await Promise.resolve().then(() => (init_nextStep(), nextStep_exports));
|
|
7864
|
+
const { resolveNext: resolveNext2, resolveAutoTransition: resolveAutoTransition2 } = await Promise.resolve().then(() => (init_nextStep(), nextStep_exports));
|
|
7603
7865
|
const current = await readCurrentWorkflow2();
|
|
7604
7866
|
const ledger = current?.sub_progress ?? [];
|
|
7605
|
-
const auto =
|
|
7867
|
+
const auto = resolveAutoTransition2(current?.auto_transition);
|
|
7606
7868
|
const step = resolveNext2(ledger, auto);
|
|
7607
7869
|
console.log(`NEXT: ${step.next}`);
|
|
7608
7870
|
if (step.sub) console.log(`SUB: ${step.sub}`);
|
|
@@ -7988,9 +8250,13 @@ ${t("rollback.metadata_unreadable.fix")}`
|
|
|
7988
8250
|
|
|
7989
8251
|
// src/cli.ts
|
|
7990
8252
|
init_run2();
|
|
8253
|
+
init_platform();
|
|
8254
|
+
|
|
8255
|
+
// src/cli/lib/capabilityResolver.ts
|
|
8256
|
+
init_platform();
|
|
7991
8257
|
function readInstalledPlugins(homedirOverride) {
|
|
7992
|
-
const
|
|
7993
|
-
|
|
8258
|
+
const path = getPluginsRegistry(homedirOverride);
|
|
8259
|
+
if (path === null) return /* @__PURE__ */ new Set();
|
|
7994
8260
|
let raw;
|
|
7995
8261
|
try {
|
|
7996
8262
|
raw = readFileSync(path, "utf8");
|
|
@@ -8015,8 +8281,7 @@ function readInstalledPlugins(homedirOverride) {
|
|
|
8015
8281
|
return out;
|
|
8016
8282
|
}
|
|
8017
8283
|
function readInstalledUserSkills(homedirOverride) {
|
|
8018
|
-
const
|
|
8019
|
-
const skillsRoot = join(home, ".claude", "skills");
|
|
8284
|
+
const skillsRoot = getSkillsDir(homedirOverride);
|
|
8020
8285
|
try {
|
|
8021
8286
|
const entries = readdirSync(skillsRoot, { withFileTypes: true });
|
|
8022
8287
|
const out = /* @__PURE__ */ new Set();
|
|
@@ -8093,55 +8358,57 @@ function renderSkillBody(body, capabilities, installedPlugins, installedUserSkil
|
|
|
8093
8358
|
}
|
|
8094
8359
|
|
|
8095
8360
|
// src/cli/lib/enableAgentTeamsInSettings.ts
|
|
8361
|
+
init_platform();
|
|
8362
|
+
|
|
8363
|
+
// src/cli/lib/settingsWriter.ts
|
|
8096
8364
|
init_harnessedRoot();
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
async function enableAgentTeamsInSettings() {
|
|
8101
|
-
const path = settingsPath();
|
|
8365
|
+
init_platform();
|
|
8366
|
+
async function mergeSettingsEnvKey(key, value, opts = {}) {
|
|
8367
|
+
const path = getSettingsPath();
|
|
8102
8368
|
let raw;
|
|
8103
8369
|
try {
|
|
8104
8370
|
raw = await readFile(path, "utf8");
|
|
8105
8371
|
} catch (err2) {
|
|
8106
8372
|
const code = err2.code;
|
|
8107
8373
|
if (code !== "ENOENT") {
|
|
8108
|
-
return {
|
|
8374
|
+
return { outcome: "warn", message: `read ${path} failed: ${err2.message}` };
|
|
8109
8375
|
}
|
|
8110
|
-
return
|
|
8376
|
+
return createFresh(path, key, value);
|
|
8111
8377
|
}
|
|
8112
8378
|
let data;
|
|
8113
8379
|
try {
|
|
8114
8380
|
const parsed = JSON.parse(raw);
|
|
8115
8381
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8116
|
-
return {
|
|
8382
|
+
return { outcome: "warn", message: `${path} is not a JSON object` };
|
|
8117
8383
|
}
|
|
8118
8384
|
data = parsed;
|
|
8119
8385
|
} catch (err2) {
|
|
8120
|
-
return {
|
|
8386
|
+
return { outcome: "warn", message: `${path} malformed JSON: ${err2.message}` };
|
|
8121
8387
|
}
|
|
8122
8388
|
const env = data.env ?? {};
|
|
8123
|
-
|
|
8124
|
-
|
|
8389
|
+
const existing = env[key];
|
|
8390
|
+
if (typeof existing === "string" && existing.length > 0 && opts.skipIfPresent?.(existing) === true) {
|
|
8391
|
+
return { outcome: "already", path, existing };
|
|
8125
8392
|
}
|
|
8126
|
-
const
|
|
8127
|
-
if (
|
|
8128
|
-
data.env = { ...env,
|
|
8393
|
+
const backup2 = await backupOriginal(raw);
|
|
8394
|
+
if (backup2.status === "warn") return { outcome: "warn", message: backup2.message };
|
|
8395
|
+
data.env = { ...env, [key]: value };
|
|
8129
8396
|
const writeErr = await atomicWrite(path, `${JSON.stringify(data, null, 2)}
|
|
8130
8397
|
`);
|
|
8131
|
-
if (writeErr) return {
|
|
8132
|
-
return {
|
|
8398
|
+
if (writeErr) return { outcome: "warn", message: writeErr };
|
|
8399
|
+
return { outcome: "merged", path, backupPath: backup2.path };
|
|
8133
8400
|
}
|
|
8134
|
-
async function
|
|
8135
|
-
const data = { env: {
|
|
8401
|
+
async function createFresh(path, key, value) {
|
|
8402
|
+
const data = { env: { [key]: value } };
|
|
8136
8403
|
try {
|
|
8137
|
-
await mkdir(
|
|
8404
|
+
await mkdir(dirname(path), { recursive: true });
|
|
8138
8405
|
} catch (err2) {
|
|
8139
|
-
return {
|
|
8406
|
+
return { outcome: "warn", message: `mkdir ~/.claude failed: ${err2.message}` };
|
|
8140
8407
|
}
|
|
8141
8408
|
const writeErr = await atomicWrite(path, `${JSON.stringify(data, null, 2)}
|
|
8142
8409
|
`);
|
|
8143
|
-
if (writeErr) return {
|
|
8144
|
-
return {
|
|
8410
|
+
if (writeErr) return { outcome: "warn", message: writeErr };
|
|
8411
|
+
return { outcome: "created", path };
|
|
8145
8412
|
}
|
|
8146
8413
|
async function backupOriginal(raw) {
|
|
8147
8414
|
const backupRoot = harnessedSubdir("backups");
|
|
@@ -8166,11 +8433,34 @@ async function atomicWrite(path, content) {
|
|
|
8166
8433
|
}
|
|
8167
8434
|
}
|
|
8168
8435
|
|
|
8169
|
-
// src/cli/lib/
|
|
8170
|
-
|
|
8171
|
-
function
|
|
8172
|
-
|
|
8436
|
+
// src/cli/lib/enableAgentTeamsInSettings.ts
|
|
8437
|
+
var ENV_KEY = "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS";
|
|
8438
|
+
async function enableAgentTeamsInSettings() {
|
|
8439
|
+
const platform = detectPlatform();
|
|
8440
|
+
if (!platform.supportsEnvKeyWrite) {
|
|
8441
|
+
return {
|
|
8442
|
+
status: "warn",
|
|
8443
|
+
message: `platform '${platform.id}' does not support env-key settings writes (capability-absent) \u2014 ${ENV_KEY} skipped`
|
|
8444
|
+
};
|
|
8445
|
+
}
|
|
8446
|
+
const r = await mergeSettingsEnvKey(ENV_KEY, "1", {
|
|
8447
|
+
skipIfPresent: (existing) => existing === "1"
|
|
8448
|
+
});
|
|
8449
|
+
switch (r.outcome) {
|
|
8450
|
+
case "created":
|
|
8451
|
+
return { status: "created", path: r.path };
|
|
8452
|
+
case "already":
|
|
8453
|
+
return { status: "already-enabled", path: r.path };
|
|
8454
|
+
case "merged":
|
|
8455
|
+
return { status: "enabled", path: r.path, backupPath: r.backupPath };
|
|
8456
|
+
case "warn":
|
|
8457
|
+
return { status: "warn", message: r.message };
|
|
8458
|
+
}
|
|
8173
8459
|
}
|
|
8460
|
+
|
|
8461
|
+
// src/cli/lib/enableUserLangInSettings.ts
|
|
8462
|
+
init_platform();
|
|
8463
|
+
var ENV_KEY2 = "HARNESSED_USER_LANG";
|
|
8174
8464
|
function detectUserLang(override) {
|
|
8175
8465
|
if (override) {
|
|
8176
8466
|
if (/^zh([^a-z]|$)/i.test(override)) return "zh-Hans";
|
|
@@ -8188,73 +8478,26 @@ function safeIntlLocale2() {
|
|
|
8188
8478
|
}
|
|
8189
8479
|
}
|
|
8190
8480
|
async function enableUserLangInSettings(override) {
|
|
8191
|
-
const
|
|
8192
|
-
|
|
8193
|
-
|
|
8194
|
-
|
|
8195
|
-
|
|
8196
|
-
|
|
8197
|
-
const code = err2.code;
|
|
8198
|
-
if (code !== "ENOENT") {
|
|
8199
|
-
return { status: "warn", message: `read ${path} failed: ${err2.message}` };
|
|
8200
|
-
}
|
|
8201
|
-
return createFreshSettings2(path, detected);
|
|
8202
|
-
}
|
|
8203
|
-
let data;
|
|
8204
|
-
try {
|
|
8205
|
-
const parsed = JSON.parse(raw);
|
|
8206
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8207
|
-
return { status: "warn", message: `${path} is not a JSON object` };
|
|
8208
|
-
}
|
|
8209
|
-
data = parsed;
|
|
8210
|
-
} catch (err2) {
|
|
8211
|
-
return { status: "warn", message: `${path} malformed JSON: ${err2.message}` };
|
|
8212
|
-
}
|
|
8213
|
-
const env = data.env ?? {};
|
|
8214
|
-
const existing = env.HARNESSED_USER_LANG;
|
|
8215
|
-
if (typeof existing === "string" && existing.length > 0 && override === void 0) {
|
|
8216
|
-
return { status: "already-set", path, existing };
|
|
8217
|
-
}
|
|
8218
|
-
const backupPath = await backupOriginal2(raw);
|
|
8219
|
-
if (backupPath.status === "warn") return backupPath;
|
|
8220
|
-
data.env = { ...env, HARNESSED_USER_LANG: detected };
|
|
8221
|
-
const writeErr = await atomicWrite2(path, `${JSON.stringify(data, null, 2)}
|
|
8222
|
-
`);
|
|
8223
|
-
if (writeErr) return { status: "warn", message: writeErr };
|
|
8224
|
-
return { status: "enabled", path, backupPath: backupPath.path, detected };
|
|
8225
|
-
}
|
|
8226
|
-
async function createFreshSettings2(path, detected) {
|
|
8227
|
-
const data = { env: { HARNESSED_USER_LANG: detected } };
|
|
8228
|
-
try {
|
|
8229
|
-
await mkdir(join(homedir(), ".claude"), { recursive: true });
|
|
8230
|
-
} catch (err2) {
|
|
8231
|
-
return { status: "warn", message: `mkdir ~/.claude failed: ${err2.message}` };
|
|
8232
|
-
}
|
|
8233
|
-
const writeErr = await atomicWrite2(path, `${JSON.stringify(data, null, 2)}
|
|
8234
|
-
`);
|
|
8235
|
-
if (writeErr) return { status: "warn", message: writeErr };
|
|
8236
|
-
return { status: "created", path, detected };
|
|
8237
|
-
}
|
|
8238
|
-
async function backupOriginal2(raw) {
|
|
8239
|
-
const backupRoot = harnessedSubdir("backups");
|
|
8240
|
-
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-");
|
|
8241
|
-
const backupPath = join(backupRoot, `settings.json.${ts}.bak`);
|
|
8242
|
-
try {
|
|
8243
|
-
await mkdir(backupRoot, { recursive: true });
|
|
8244
|
-
await writeFile(backupPath, raw, "utf8");
|
|
8245
|
-
return { status: "ok", path: backupPath };
|
|
8246
|
-
} catch (err2) {
|
|
8247
|
-
return { status: "warn", message: `backup ${backupPath} failed: ${err2.message}` };
|
|
8481
|
+
const platform = detectPlatform();
|
|
8482
|
+
if (!platform.supportsEnvKeyWrite) {
|
|
8483
|
+
return {
|
|
8484
|
+
status: "warn",
|
|
8485
|
+
message: `platform '${platform.id}' does not support env-key settings writes (capability-absent) \u2014 ${ENV_KEY2} skipped`
|
|
8486
|
+
};
|
|
8248
8487
|
}
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
|
|
8257
|
-
|
|
8488
|
+
const detected = detectUserLang(override);
|
|
8489
|
+
const r = await mergeSettingsEnvKey(ENV_KEY2, detected, {
|
|
8490
|
+
skipIfPresent: () => override === void 0
|
|
8491
|
+
});
|
|
8492
|
+
switch (r.outcome) {
|
|
8493
|
+
case "created":
|
|
8494
|
+
return { status: "created", path: r.path, detected };
|
|
8495
|
+
case "already":
|
|
8496
|
+
return { status: "already-set", path: r.path, existing: r.existing };
|
|
8497
|
+
case "merged":
|
|
8498
|
+
return { status: "enabled", path: r.path, backupPath: r.backupPath, detected };
|
|
8499
|
+
case "warn":
|
|
8500
|
+
return { status: "warn", message: r.message };
|
|
8258
8501
|
}
|
|
8259
8502
|
}
|
|
8260
8503
|
|
|
@@ -8314,8 +8557,8 @@ async function renderAllSkills(skillNames, skillsBase, workflowsDir, homedirOver
|
|
|
8314
8557
|
]
|
|
8315
8558
|
};
|
|
8316
8559
|
}
|
|
8317
|
-
const installedPlugins = readInstalledPlugins();
|
|
8318
|
-
const installedUserSkills = readInstalledUserSkills();
|
|
8560
|
+
const installedPlugins = readInstalledPlugins(homedirOverride);
|
|
8561
|
+
const installedUserSkills = readInstalledUserSkills(homedirOverride);
|
|
8319
8562
|
const results = [];
|
|
8320
8563
|
const warningSet = /* @__PURE__ */ new Set();
|
|
8321
8564
|
for (const name of skillNames) {
|
|
@@ -8478,6 +8721,23 @@ async function runStepBInstall(manifestPaths, runOpts = {}) {
|
|
|
8478
8721
|
}
|
|
8479
8722
|
|
|
8480
8723
|
// src/cli/setup.ts
|
|
8724
|
+
var KNOWN_PLATFORMS = ["claude", "codex"];
|
|
8725
|
+
async function applyPlatformOption(platform) {
|
|
8726
|
+
if (!KNOWN_PLATFORMS.includes(platform)) {
|
|
8727
|
+
console.error(
|
|
8728
|
+
`--platform: unknown id '${platform}' (expected one of: ${KNOWN_PLATFORMS.join(" | ")})`
|
|
8729
|
+
);
|
|
8730
|
+
process.exit(1);
|
|
8731
|
+
}
|
|
8732
|
+
process.env.HARNESSED_PLATFORM = platform;
|
|
8733
|
+
const stateRoot = detectPlatform().stateRoot;
|
|
8734
|
+
try {
|
|
8735
|
+
await mkdir(stateRoot, { recursive: true });
|
|
8736
|
+
await writeFile(join(stateRoot, ".platform"), platform, "utf8");
|
|
8737
|
+
} catch (e) {
|
|
8738
|
+
console.warn(` [--platform] could not persist .platform pin (${e.message})`);
|
|
8739
|
+
}
|
|
8740
|
+
}
|
|
8481
8741
|
async function listBaseManifests2(pkgRoot) {
|
|
8482
8742
|
const out = [];
|
|
8483
8743
|
for (const d of ["manifests/tools", "manifests/skill-packs"]) {
|
|
@@ -8530,14 +8790,15 @@ function registerSetup(program2) {
|
|
|
8530
8790
|
).option("--dry-run", "preview only \u2014 do not write to disk (opt-in for advanced users)").option(
|
|
8531
8791
|
"--user-lang <code>",
|
|
8532
8792
|
"override detected OS locale for env.HARNESSED_USER_LANG (en | zh-Hans / zh-CN / zh-TW)"
|
|
8533
|
-
).option("--non-interactive", "skip all confirm prompts (CI / scripted setup)").option("--no-auto-install", "do not prompt to auto-install missing plugins (advisory only)").option(
|
|
8793
|
+
).option("--platform <id>", "target harness platform (claude | codex); default: auto-detect").option("--non-interactive", "skip all confirm prompts (CI / scripted setup)").option("--no-auto-install", "do not prompt to auto-install missing plugins (advisory only)").option(
|
|
8534
8794
|
"--update-installed",
|
|
8535
8795
|
"force re-install already-installed plugins (excludes MCP servers); default: skip if installed"
|
|
8536
8796
|
).action(async (raw) => {
|
|
8537
8797
|
const dryRun = raw.dryRun === true;
|
|
8798
|
+
if (raw.platform !== void 0) await applyPlatformOption(raw.platform);
|
|
8538
8799
|
const pkgRoot = getPackageRoot();
|
|
8539
8800
|
const workflowsDir = resolve(pkgRoot, "workflows");
|
|
8540
|
-
const skillsBase =
|
|
8801
|
+
const skillsBase = getSkillsDir();
|
|
8541
8802
|
await warnIfAgentTeamsMissing();
|
|
8542
8803
|
let entries;
|
|
8543
8804
|
try {
|
|
@@ -8581,7 +8842,7 @@ function registerSetup(program2) {
|
|
|
8581
8842
|
console.warn(` - ${w}`);
|
|
8582
8843
|
}
|
|
8583
8844
|
}
|
|
8584
|
-
const commandsBase =
|
|
8845
|
+
const commandsBase = getCommandsDir();
|
|
8585
8846
|
try {
|
|
8586
8847
|
await mkdir(commandsBase, { recursive: true });
|
|
8587
8848
|
} catch (e) {
|
|
@@ -8676,116 +8937,6 @@ Force-update pass complete: ${b2.installed.length} installed / ${b2.alreadyInsta
|
|
|
8676
8937
|
process.exit(0);
|
|
8677
8938
|
});
|
|
8678
8939
|
}
|
|
8679
|
-
|
|
8680
|
-
// src/cli/status.ts
|
|
8681
|
-
init_evidence();
|
|
8682
|
-
init_ledger();
|
|
8683
|
-
init_state();
|
|
8684
|
-
init_harnessedRoot();
|
|
8685
|
-
init_state2();
|
|
8686
|
-
function statusMarker(status) {
|
|
8687
|
-
switch (status) {
|
|
8688
|
-
case "done":
|
|
8689
|
-
return "\u2705 done";
|
|
8690
|
-
case "pending":
|
|
8691
|
-
return "\u23F3 pending";
|
|
8692
|
-
case "failed":
|
|
8693
|
-
return "\u2717 failed";
|
|
8694
|
-
case "skipped":
|
|
8695
|
-
return "\u2B1C skipped";
|
|
8696
|
-
case "rejected":
|
|
8697
|
-
return "\u{1F6AB} rejected";
|
|
8698
|
-
}
|
|
8699
|
-
}
|
|
8700
|
-
function evidenceLabel(entry) {
|
|
8701
|
-
switch (entry.evidence_status) {
|
|
8702
|
-
case "verified":
|
|
8703
|
-
return "evidence: verified";
|
|
8704
|
-
case "overridden":
|
|
8705
|
-
return "evidence: overridden (--force)";
|
|
8706
|
-
default:
|
|
8707
|
-
return "evidence: none_declared";
|
|
8708
|
-
}
|
|
8709
|
-
}
|
|
8710
|
-
async function buildRecoverLines(workflow, ledger) {
|
|
8711
|
-
const lines = [];
|
|
8712
|
-
if (!workflow || ledger.length === 0) {
|
|
8713
|
-
lines.push("no ledger \u2014 run gates + start");
|
|
8714
|
-
return lines;
|
|
8715
|
-
}
|
|
8716
|
-
lines.push(`workflow: ${workflow.phase} (${workflow.status}) started ${workflow.started_at}`);
|
|
8717
|
-
const next = nextPending(ledger);
|
|
8718
|
-
for (const e of ledger) {
|
|
8719
|
-
const marker = statusMarker(e.status);
|
|
8720
|
-
let suffix = "";
|
|
8721
|
-
if (e.status === "skipped") {
|
|
8722
|
-
suffix = e.reason ? ` (skipped: ${e.reason})` : "";
|
|
8723
|
-
} else if (e.status === "done") {
|
|
8724
|
-
suffix = ` ${evidenceLabel(e)}`;
|
|
8725
|
-
}
|
|
8726
|
-
const arrow = e.sub === next ? " \u2190 next" : "";
|
|
8727
|
-
lines.push(` ${e.sub} ${marker}${suffix}${arrow}`);
|
|
8728
|
-
}
|
|
8729
|
-
if (next) {
|
|
8730
|
-
lines.push(`\u2192 next: harnessed prompt ${next}`);
|
|
8731
|
-
} else {
|
|
8732
|
-
lines.push("\u2192 all subs resolved (no pending work)");
|
|
8733
|
-
}
|
|
8734
|
-
for (const e of ledger) {
|
|
8735
|
-
if (!e.evidence || e.evidence.length === 0) continue;
|
|
8736
|
-
const drift = await detectDrift(e.evidence);
|
|
8737
|
-
for (const d of drift) {
|
|
8738
|
-
const now = d.now === "" ? "missing" : `${d.now.slice(0, 7)}\u2026`;
|
|
8739
|
-
lines.push(`\u26A0 drift: ${d.path} sha256 changed (was ${d.was.slice(0, 7)}\u2026, now ${now})`);
|
|
8740
|
-
}
|
|
8741
|
-
}
|
|
8742
|
-
return lines;
|
|
8743
|
-
}
|
|
8744
|
-
function registerStatus(program2) {
|
|
8745
|
-
program2.command("status").description("Show installed upstreams (from <harnessed-root>/state.json)").option("--recover", "structured recovery view of the active workflow ledger (v5.0 Spec 1 B)").action(async (opts) => {
|
|
8746
|
-
if (opts.recover) {
|
|
8747
|
-
const wf = await readCurrentWorkflow();
|
|
8748
|
-
const ledger = wf?.sub_progress ?? [];
|
|
8749
|
-
const lines = await buildRecoverLines(wf, ledger);
|
|
8750
|
-
for (const l of lines) console.log(l);
|
|
8751
|
-
return;
|
|
8752
|
-
}
|
|
8753
|
-
const state = await readState(process.cwd());
|
|
8754
|
-
const names = Object.keys(state.installed).sort();
|
|
8755
|
-
if (names.length === 0) {
|
|
8756
|
-
console.log(t("status.no_installs", { path: harnessedFile("state.json") }));
|
|
8757
|
-
} else {
|
|
8758
|
-
for (const n of names) {
|
|
8759
|
-
const e = state.installed[n];
|
|
8760
|
-
if (!e) continue;
|
|
8761
|
-
console.log(`${n} @ ${e.version} (installed ${e.installedAt})`);
|
|
8762
|
-
}
|
|
8763
|
-
console.log(t("status.summary_installs", { count: names.length }));
|
|
8764
|
-
}
|
|
8765
|
-
const lockPath = harnessedFile(".lock");
|
|
8766
|
-
try {
|
|
8767
|
-
const isLocked = await lockfile.check(getHarnessedRoot(), {
|
|
8768
|
-
lockfilePath: lockPath,
|
|
8769
|
-
stale: 1e4
|
|
8770
|
-
});
|
|
8771
|
-
if (isLocked) {
|
|
8772
|
-
const s = await stat(lockPath);
|
|
8773
|
-
const ageMs = Date.now() - s.mtime.getTime();
|
|
8774
|
-
const stale = ageMs > 1e4;
|
|
8775
|
-
console.log(
|
|
8776
|
-
t("status.lock_held", {
|
|
8777
|
-
since: s.mtime.toISOString(),
|
|
8778
|
-
staleSuffix: stale ? t("status.lock_held.stale_suffix") : ""
|
|
8779
|
-
})
|
|
8780
|
-
);
|
|
8781
|
-
console.log(t("status.lock_release_hint", { path: lockPath }));
|
|
8782
|
-
} else {
|
|
8783
|
-
console.log(t("status.lock_free"));
|
|
8784
|
-
}
|
|
8785
|
-
} catch {
|
|
8786
|
-
}
|
|
8787
|
-
});
|
|
8788
|
-
}
|
|
8789
8940
|
init_harnessedRoot();
|
|
8790
8941
|
init_path_guard();
|
|
8791
8942
|
init_validate();
|
|
@@ -8804,10 +8955,10 @@ var uninstallCcHookAdd = async (ctx) => {
|
|
|
8804
8955
|
}
|
|
8805
8956
|
const abort = dryRunGate(ctx);
|
|
8806
8957
|
if (abort) return abort;
|
|
8807
|
-
const
|
|
8958
|
+
const settingsPath = join(homedir(), ".claude", "settings.json");
|
|
8808
8959
|
let existing;
|
|
8809
8960
|
try {
|
|
8810
|
-
existing = await readFile(
|
|
8961
|
+
existing = await readFile(settingsPath, "utf8");
|
|
8811
8962
|
} catch {
|
|
8812
8963
|
return { ok: true, removedPaths: [] };
|
|
8813
8964
|
}
|
|
@@ -8836,8 +8987,8 @@ var uninstallCcHookAdd = async (ctx) => {
|
|
|
8836
8987
|
if (settings.hooks?.[ev]?.length === before || before === settings.hooks?.[ev]?.length) ;
|
|
8837
8988
|
const newText = `${JSON.stringify(settings, null, 2)}
|
|
8838
8989
|
`;
|
|
8839
|
-
await writeFile(
|
|
8840
|
-
return { ok: true, removedPaths: [
|
|
8990
|
+
await writeFile(settingsPath, newText);
|
|
8991
|
+
return { ok: true, removedPaths: [settingsPath] };
|
|
8841
8992
|
};
|
|
8842
8993
|
|
|
8843
8994
|
// src/uninstallers/ccPluginMarketplace.ts
|
|
@@ -8976,7 +9127,7 @@ var uninstallNpmCli = async (ctx) => {
|
|
|
8976
9127
|
const m = install.cmd.match(/npm\s+(?:install|i)\s+(?:-g\s+)?(\S+)/);
|
|
8977
9128
|
const pkg = m?.[1] ?? ctx.manifest.metadata.upstream.source;
|
|
8978
9129
|
const isWin = process.platform === "win32";
|
|
8979
|
-
const result = await new Promise((
|
|
9130
|
+
const result = await new Promise((resolve18) => {
|
|
8980
9131
|
const child = isWin ? spawn("cmd.exe", ["/c", "npm", "uninstall", "-g", pkg], { windowsHide: true }) : spawn("npm", ["uninstall", "-g", pkg], { shell: false });
|
|
8981
9132
|
let stderr = "";
|
|
8982
9133
|
child.stderr?.setEncoding("utf8").on("data", (c) => {
|
|
@@ -8984,15 +9135,15 @@ var uninstallNpmCli = async (ctx) => {
|
|
|
8984
9135
|
});
|
|
8985
9136
|
const timer = setTimeout(() => {
|
|
8986
9137
|
child.kill("SIGKILL");
|
|
8987
|
-
|
|
9138
|
+
resolve18({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
8988
9139
|
}, 3e4);
|
|
8989
9140
|
child.on("error", (e) => {
|
|
8990
9141
|
clearTimeout(timer);
|
|
8991
|
-
|
|
9142
|
+
resolve18({ exitCode: -1, stderr: e.message });
|
|
8992
9143
|
});
|
|
8993
9144
|
child.on("close", (code) => {
|
|
8994
9145
|
clearTimeout(timer);
|
|
8995
|
-
|
|
9146
|
+
resolve18({ exitCode: code ?? -1, stderr });
|
|
8996
9147
|
});
|
|
8997
9148
|
});
|
|
8998
9149
|
if (result.exitCode !== 0) {
|
|
@@ -9058,9 +9209,9 @@ async function discoverCommandFiles(commandsDir) {
|
|
|
9058
9209
|
}
|
|
9059
9210
|
return owned;
|
|
9060
9211
|
}
|
|
9061
|
-
async function checkSettingsEnv(
|
|
9212
|
+
async function checkSettingsEnv(settingsPath) {
|
|
9062
9213
|
try {
|
|
9063
|
-
const raw = await readFile(
|
|
9214
|
+
const raw = await readFile(settingsPath, "utf8");
|
|
9064
9215
|
const data = JSON.parse(raw);
|
|
9065
9216
|
const env = data.env ?? {};
|
|
9066
9217
|
return {
|
|
@@ -9071,8 +9222,8 @@ async function checkSettingsEnv(settingsPath3) {
|
|
|
9071
9222
|
return { hasAgentTeams: false, hasUserLang: false };
|
|
9072
9223
|
}
|
|
9073
9224
|
}
|
|
9074
|
-
async function removeSettingsEnv(
|
|
9075
|
-
const raw = await readFile(
|
|
9225
|
+
async function removeSettingsEnv(settingsPath) {
|
|
9226
|
+
const raw = await readFile(settingsPath, "utf8");
|
|
9076
9227
|
const data = JSON.parse(raw);
|
|
9077
9228
|
const env = data.env ?? {};
|
|
9078
9229
|
let changed = false;
|
|
@@ -9087,14 +9238,14 @@ async function removeSettingsEnv(settingsPath3) {
|
|
|
9087
9238
|
if (!changed) return false;
|
|
9088
9239
|
if (Object.keys(env).length === 0) delete data.env;
|
|
9089
9240
|
else data.env = env;
|
|
9090
|
-
await writeFile(
|
|
9241
|
+
await writeFile(settingsPath, `${JSON.stringify(data, null, 2)}
|
|
9091
9242
|
`, "utf8");
|
|
9092
9243
|
return true;
|
|
9093
9244
|
}
|
|
9094
9245
|
async function runUnifiedUninstall(home, dryRun) {
|
|
9095
9246
|
const commandsDir = join(home, ".claude", "commands");
|
|
9096
9247
|
const skillsDir = join(home, ".claude", "skills");
|
|
9097
|
-
const
|
|
9248
|
+
const settingsPath = join(home, ".claude", "settings.json");
|
|
9098
9249
|
const harnessedRoot = getHarnessedRoot();
|
|
9099
9250
|
const pkgRoot = getPackageRoot();
|
|
9100
9251
|
let workflowNames = [];
|
|
@@ -9105,7 +9256,7 @@ async function runUnifiedUninstall(home, dryRun) {
|
|
|
9105
9256
|
} catch {
|
|
9106
9257
|
}
|
|
9107
9258
|
const commandFiles = await discoverCommandFiles(commandsDir);
|
|
9108
|
-
const settingsEnv = await checkSettingsEnv(
|
|
9259
|
+
const settingsEnv = await checkSettingsEnv(settingsPath);
|
|
9109
9260
|
const hasSettingsChanges = settingsEnv.hasAgentTeams || settingsEnv.hasUserLang;
|
|
9110
9261
|
const skillDirs = [];
|
|
9111
9262
|
for (const name of workflowNames) {
|
|
@@ -9163,9 +9314,9 @@ async function runUnifiedUninstall(home, dryRun) {
|
|
|
9163
9314
|
let removedSettings = false;
|
|
9164
9315
|
if (hasSettingsChanges) {
|
|
9165
9316
|
try {
|
|
9166
|
-
removedSettings = await removeSettingsEnv(
|
|
9317
|
+
removedSettings = await removeSettingsEnv(settingsPath);
|
|
9167
9318
|
} catch (e) {
|
|
9168
|
-
failures.push(`${
|
|
9319
|
+
failures.push(`${settingsPath}: ${e.message}`);
|
|
9169
9320
|
}
|
|
9170
9321
|
}
|
|
9171
9322
|
const normalizedRoot = resolve(harnessedRoot);
|
|
@@ -9413,6 +9564,11 @@ registerLearn(program);
|
|
|
9413
9564
|
registerUpdate(program);
|
|
9414
9565
|
registerReleasePreflight(program);
|
|
9415
9566
|
registerRetro(program);
|
|
9416
|
-
|
|
9567
|
+
var bare = parseBareInvocation(process.argv.slice(2));
|
|
9568
|
+
if (bare.here) {
|
|
9569
|
+
void runHere({ json: bare.json });
|
|
9570
|
+
} else {
|
|
9571
|
+
program.parse(process.argv);
|
|
9572
|
+
}
|
|
9417
9573
|
//# sourceMappingURL=cli.mjs.map
|
|
9418
9574
|
//# sourceMappingURL=cli.mjs.map
|