harnessed 4.6.0 → 4.8.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 +682 -433
- 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 +20 -3
- package/package.json +1 -1
- package/workflows/auto/SKILL.zh-Hans.md +129 -0
- package/workflows/capabilities.yaml +120 -0
- package/workflows/disciplines/doc-discipline.zh-Hans.yaml +49 -0
- package/workflows/disciplines/karpathy.yaml +5 -5
- package/workflows/disciplines/karpathy.zh-Hans.yaml +47 -0
- package/workflows/disciplines/operational.yaml +6 -6
- package/workflows/disciplines/operational.zh-Hans.yaml +79 -0
- package/workflows/disciplines/output-style.yaml +7 -7
- package/workflows/disciplines/output-style.zh-Hans.yaml +62 -0
- package/workflows/disciplines/priority.yaml +2 -2
- package/workflows/disciplines/priority.zh-Hans.yaml +28 -0
- package/workflows/discuss/auto/SKILL.zh-Hans.md +75 -0
- package/workflows/discuss/phase/SKILL.zh-Hans.md +73 -0
- package/workflows/discuss/strategic/SKILL.zh-Hans.md +78 -0
- package/workflows/discuss/subtask/SKILL.zh-Hans.md +79 -0
- package/workflows/plan/architecture/SKILL.zh-Hans.md +74 -0
- package/workflows/plan/auto/SKILL.zh-Hans.md +75 -0
- package/workflows/plan/phase/SKILL.zh-Hans.md +76 -0
- package/workflows/research/SKILL.zh-Hans.md +81 -0
- package/workflows/retro/SKILL.zh-Hans.md +71 -0
- package/workflows/role-prompts.zh-Hans.yaml +501 -0
- package/workflows/ship/auto/SKILL.zh-Hans.md +46 -0
- package/workflows/ship/preflight/SKILL.zh-Hans.md +38 -0
- package/workflows/task/auto/SKILL.zh-Hans.md +80 -0
- package/workflows/task/clarify/SKILL.zh-Hans.md +79 -0
- package/workflows/task/code/SKILL.zh-Hans.md +85 -0
- package/workflows/task/deliver/SKILL.zh-Hans.md +113 -0
- package/workflows/task/test/SKILL.zh-Hans.md +90 -0
- package/workflows/verify/auto/SKILL.zh-Hans.md +89 -0
- package/workflows/verify/code-review/SKILL.zh-Hans.md +71 -0
- package/workflows/verify/design/SKILL.zh-Hans.md +74 -0
- package/workflows/verify/multispec/SKILL.zh-Hans.md +88 -0
- package/workflows/verify/paranoid/SKILL.zh-Hans.md +74 -0
- package/workflows/verify/progress/SKILL.zh-Hans.md +69 -0
- package/workflows/verify/qa/SKILL.zh-Hans.md +76 -0
- package/workflows/verify/security/SKILL.zh-Hans.md +70 -0
- package/workflows/verify/simplify/SKILL.zh-Hans.md +70 -0
package/dist/cli.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import * as ajvFormatsNs from 'ajv-formats';
|
|
|
6
6
|
import { execFileSync, spawnSync, spawn, exec, execFile, execSync } from 'child_process';
|
|
7
7
|
import { existsSync, mkdirSync, renameSync, writeFileSync, readFileSync, readdirSync } from 'fs';
|
|
8
8
|
import { join, dirname, resolve, sep, relative } from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
9
10
|
import { homedir } from 'os';
|
|
10
11
|
import { createHash } from 'crypto';
|
|
11
12
|
import { readFile, readdir, unlink, writeFile, stat, rm, cp, mkdir, rename, appendFile, access } from 'fs/promises';
|
|
@@ -13,7 +14,6 @@ import { Value } from '@sinclair/typebox/value';
|
|
|
13
14
|
import lockfile from 'proper-lockfile';
|
|
14
15
|
import { Parser } from 'expr-eval';
|
|
15
16
|
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
16
|
-
import { fileURLToPath } from 'url';
|
|
17
17
|
import { promisify } from 'util';
|
|
18
18
|
import * as p from '@clack/prompts';
|
|
19
19
|
import { createPatch } from 'diff';
|
|
@@ -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.8.0",
|
|
42
42
|
description: "AI coding harness package manager + composition orchestrator",
|
|
43
43
|
type: "module",
|
|
44
44
|
license: "Apache-2.0",
|
|
@@ -852,10 +852,162 @@ var init_origin_check = __esm({
|
|
|
852
852
|
"src/cli/lib/origin-check.ts"() {
|
|
853
853
|
}
|
|
854
854
|
});
|
|
855
|
-
function
|
|
855
|
+
function mapToSupported(raw) {
|
|
856
|
+
if (!raw) return "en";
|
|
857
|
+
if (/^zh([^a-z]|$)/i.test(raw)) return "zh-Hans";
|
|
858
|
+
return "en";
|
|
859
|
+
}
|
|
860
|
+
function detectLocale() {
|
|
861
|
+
const raw = process.env.HARNESSED_LANG || process.env.LC_ALL || process.env.LANG || process.env.LANGUAGE || safeIntlLocale();
|
|
862
|
+
return mapToSupported(raw);
|
|
863
|
+
}
|
|
864
|
+
function safeIntlLocale() {
|
|
865
|
+
try {
|
|
866
|
+
return Intl.DateTimeFormat().resolvedOptions().locale;
|
|
867
|
+
} catch {
|
|
868
|
+
return void 0;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
function messagesDir() {
|
|
872
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
873
|
+
const candidates = [resolve(here, "..", "..", "messages"), resolve(here, "..", "messages")];
|
|
874
|
+
for (const c of candidates) {
|
|
875
|
+
try {
|
|
876
|
+
readFileSync(join(c, "en.json"), "utf8");
|
|
877
|
+
return c;
|
|
878
|
+
} catch {
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
return resolve(process.cwd(), "messages");
|
|
882
|
+
}
|
|
883
|
+
function loadLocale(locale) {
|
|
884
|
+
if (cache[locale]) return cache[locale];
|
|
885
|
+
const path = join(messagesDir(), `${locale}.json`);
|
|
886
|
+
try {
|
|
887
|
+
const raw = readFileSync(path, "utf8");
|
|
888
|
+
cache[locale] = JSON.parse(raw);
|
|
889
|
+
} catch {
|
|
890
|
+
cache[locale] = {};
|
|
891
|
+
}
|
|
892
|
+
return cache[locale];
|
|
893
|
+
}
|
|
894
|
+
function setLocale(locale) {
|
|
895
|
+
if (!locale) return;
|
|
896
|
+
const mapped = mapToSupported(locale);
|
|
897
|
+
if (SUPPORTED.has(mapped)) currentLocale = mapped;
|
|
898
|
+
}
|
|
899
|
+
function getLocale() {
|
|
900
|
+
if (currentLocale === null) currentLocale = detectLocale();
|
|
901
|
+
return currentLocale;
|
|
902
|
+
}
|
|
903
|
+
function t(key, params) {
|
|
904
|
+
const locale = getLocale();
|
|
905
|
+
const primary = loadLocale(locale);
|
|
906
|
+
let template = primary[key];
|
|
907
|
+
if (template === void 0 && locale !== "en") {
|
|
908
|
+
template = loadLocale("en")[key];
|
|
909
|
+
}
|
|
910
|
+
if (template === void 0) template = key;
|
|
911
|
+
if (!params) return template;
|
|
912
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_match, name) => {
|
|
913
|
+
const v = params[name];
|
|
914
|
+
return v === void 0 ? `{{${name}}}` : String(v);
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
var SUPPORTED, currentLocale, cache;
|
|
918
|
+
var init_i18n = __esm({
|
|
919
|
+
"src/i18n/index.ts"() {
|
|
920
|
+
SUPPORTED = /* @__PURE__ */ new Set(["en", "zh-Hans"]);
|
|
921
|
+
currentLocale = null;
|
|
922
|
+
cache = {};
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
function claudeDescriptor(home = homedir()) {
|
|
926
|
+
const claudeHome = join(home, ".claude");
|
|
927
|
+
return {
|
|
928
|
+
id: "claude",
|
|
929
|
+
homeDir: claudeHome,
|
|
930
|
+
stateRoot: join(claudeHome, "harnessed"),
|
|
931
|
+
settingsPath: join(claudeHome, "settings.json"),
|
|
932
|
+
skillsDir: join(claudeHome, "skills"),
|
|
933
|
+
commandsDir: join(claudeHome, "commands"),
|
|
934
|
+
pluginsRegistry: join(claudeHome, "plugins", "installed_plugins.json"),
|
|
935
|
+
// mcpConfigPath is a SIBLING of `.claude` (`<home>/.claude.json`), based on
|
|
936
|
+
// the same home base — not a child of homeDir.
|
|
937
|
+
mcpConfigPath: join(home, ".claude.json"),
|
|
938
|
+
// claude writes its env keys into JSON settings.json (capability present).
|
|
939
|
+
supportsEnvKeyWrite: true
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
function codexDescriptor(home = homedir()) {
|
|
943
|
+
const codexHome = join(home, ".codex");
|
|
944
|
+
const configToml = join(codexHome, "config.toml");
|
|
945
|
+
return {
|
|
946
|
+
id: "codex",
|
|
947
|
+
homeDir: codexHome,
|
|
948
|
+
stateRoot: join(codexHome, "harnessed"),
|
|
949
|
+
settingsPath: configToml,
|
|
950
|
+
// SHARED convention dir, NOT <homeDir>/skills.
|
|
951
|
+
skillsDir: join(home, ".agents", "skills"),
|
|
952
|
+
commandsDir: join(codexHome, "prompts"),
|
|
953
|
+
pluginsRegistry: null,
|
|
954
|
+
mcpConfigPath: configToml,
|
|
955
|
+
supportsEnvKeyWrite: false
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
function descriptorById(id, home) {
|
|
959
|
+
if (id === "claude") return claudeDescriptor(home);
|
|
960
|
+
if (id === "codex") return codexDescriptor(home);
|
|
961
|
+
return void 0;
|
|
962
|
+
}
|
|
963
|
+
function detectPlatform(home = homedir()) {
|
|
964
|
+
const base = claudeDescriptor(home);
|
|
856
965
|
const override = process.env.HARNESSED_ROOT_OVERRIDE;
|
|
857
|
-
if (override !== void 0 && override !== "") return override;
|
|
858
|
-
|
|
966
|
+
if (override !== void 0 && override !== "") return { ...base, stateRoot: override };
|
|
967
|
+
const envPlatform = process.env.HARNESSED_PLATFORM;
|
|
968
|
+
if (envPlatform !== void 0 && envPlatform !== "") {
|
|
969
|
+
const d = descriptorById(envPlatform, home);
|
|
970
|
+
if (d) return d;
|
|
971
|
+
}
|
|
972
|
+
try {
|
|
973
|
+
const pin = readFileSync(join(base.stateRoot, ".platform"), "utf8").trim();
|
|
974
|
+
const d = descriptorById(pin, home);
|
|
975
|
+
if (d) return d;
|
|
976
|
+
} catch {
|
|
977
|
+
}
|
|
978
|
+
try {
|
|
979
|
+
if (existsSync(base.homeDir)) return base;
|
|
980
|
+
const codex = codexDescriptor(home);
|
|
981
|
+
if (existsSync(codex.homeDir)) return codex;
|
|
982
|
+
} catch {
|
|
983
|
+
}
|
|
984
|
+
return base;
|
|
985
|
+
}
|
|
986
|
+
function getSettingsPath(home) {
|
|
987
|
+
return detectPlatform(home).settingsPath;
|
|
988
|
+
}
|
|
989
|
+
function getSkillsDir(home) {
|
|
990
|
+
return detectPlatform(home).skillsDir;
|
|
991
|
+
}
|
|
992
|
+
function getCommandsDir(home) {
|
|
993
|
+
return detectPlatform(home).commandsDir;
|
|
994
|
+
}
|
|
995
|
+
function getPluginsRegistry(home) {
|
|
996
|
+
return detectPlatform(home).pluginsRegistry;
|
|
997
|
+
}
|
|
998
|
+
function getMcpConfigPath(home) {
|
|
999
|
+
return detectPlatform(home).mcpConfigPath;
|
|
1000
|
+
}
|
|
1001
|
+
function harnessSkillsDirs(home) {
|
|
1002
|
+
const dirs = [claudeDescriptor(home).skillsDir, codexDescriptor(home).skillsDir];
|
|
1003
|
+
return [...new Set(dirs)];
|
|
1004
|
+
}
|
|
1005
|
+
var init_platform = __esm({
|
|
1006
|
+
"src/installers/lib/platform.ts"() {
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
function getHarnessedRoot() {
|
|
1010
|
+
return detectPlatform().stateRoot;
|
|
859
1011
|
}
|
|
860
1012
|
function harnessedSubdir(name) {
|
|
861
1013
|
return join(getHarnessedRoot(), name);
|
|
@@ -890,6 +1042,7 @@ function migrateLegacyHarnessedRoot() {
|
|
|
890
1042
|
}
|
|
891
1043
|
var init_harnessedRoot = __esm({
|
|
892
1044
|
"src/installers/lib/harnessedRoot.ts"() {
|
|
1045
|
+
init_platform();
|
|
893
1046
|
}
|
|
894
1047
|
});
|
|
895
1048
|
function getBackupRoot() {
|
|
@@ -1895,8 +2048,20 @@ var init_loadPhases = __esm({
|
|
|
1895
2048
|
};
|
|
1896
2049
|
}
|
|
1897
2050
|
});
|
|
1898
|
-
|
|
1899
|
-
|
|
2051
|
+
function resolveLocaleYaml(dir, baseName, locale = getLocale()) {
|
|
2052
|
+
if (locale !== "en") {
|
|
2053
|
+
const sibling = join(dir, `${baseName}.${locale}.yaml`);
|
|
2054
|
+
if (existsSync(sibling)) return sibling;
|
|
2055
|
+
}
|
|
2056
|
+
return join(dir, `${baseName}.yaml`);
|
|
2057
|
+
}
|
|
2058
|
+
var init_localeYaml = __esm({
|
|
2059
|
+
"src/i18n/localeYaml.ts"() {
|
|
2060
|
+
init_i18n();
|
|
2061
|
+
}
|
|
2062
|
+
});
|
|
2063
|
+
async function loadRolePrompts(workflowsDir, locale = getLocale()) {
|
|
2064
|
+
const path = resolveLocaleYaml(workflowsDir, "role-prompts", locale);
|
|
1900
2065
|
let raw;
|
|
1901
2066
|
try {
|
|
1902
2067
|
raw = await readFile(path, "utf8");
|
|
@@ -2056,7 +2221,7 @@ function nameToYamlHintPath(name) {
|
|
|
2056
2221
|
function shouldOverwriteFile(content) {
|
|
2057
2222
|
return HARNESSED_MARKER_RX.test(content) || V3_4_3_SIGNATURE_SUB_RX.test(content) || V3_4_3_SIGNATURE_MASTER_RX.test(content);
|
|
2058
2223
|
}
|
|
2059
|
-
async function writeAllCommands(slashNames, commandsDir, rolePrompts, capabilities, installedPlugins, installedUserSkills, writer, fileExists2 = existsSync,
|
|
2224
|
+
async function writeAllCommands(slashNames, commandsDir, rolePrompts, capabilities, installedPlugins, installedUserSkills, writer, fileExists2 = existsSync, readFileSync13 = (p5) => readFileSync(p5, "utf8")) {
|
|
2060
2225
|
const results = [];
|
|
2061
2226
|
const aggregatedWarnings = /* @__PURE__ */ new Set();
|
|
2062
2227
|
for (const name of slashNames) {
|
|
@@ -2075,7 +2240,7 @@ async function writeAllCommands(slashNames, commandsDir, rolePrompts, capabiliti
|
|
|
2075
2240
|
if (fileExists2(path)) {
|
|
2076
2241
|
let existing = "";
|
|
2077
2242
|
try {
|
|
2078
|
-
existing =
|
|
2243
|
+
existing = readFileSync13(path);
|
|
2079
2244
|
} catch {
|
|
2080
2245
|
existing = "";
|
|
2081
2246
|
}
|
|
@@ -2110,6 +2275,8 @@ async function writeAllCommands(slashNames, commandsDir, rolePrompts, capabiliti
|
|
|
2110
2275
|
var INTERACTIVE_COMMANDS, ORCHESTRATOR_COMMANDS, MARKER, LANG_DIRECTIVE, HARNESSED_MARKER_RX, V3_4_3_SIGNATURE_SUB_RX, V3_4_3_SIGNATURE_MASTER_RX;
|
|
2111
2276
|
var init_generateCommands = __esm({
|
|
2112
2277
|
"src/cli/lib/generateCommands.ts"() {
|
|
2278
|
+
init_i18n();
|
|
2279
|
+
init_localeYaml();
|
|
2113
2280
|
INTERACTIVE_COMMANDS = /* @__PURE__ */ new Set([
|
|
2114
2281
|
"discuss",
|
|
2115
2282
|
"discuss-strategic",
|
|
@@ -4373,7 +4540,7 @@ var init_check_mattpocock_skills = __esm({
|
|
|
4373
4540
|
}
|
|
4374
4541
|
});
|
|
4375
4542
|
function getUserClaudeJsonPath() {
|
|
4376
|
-
return
|
|
4543
|
+
return getMcpConfigPath();
|
|
4377
4544
|
}
|
|
4378
4545
|
async function readUserClaudeJson() {
|
|
4379
4546
|
const path = getUserClaudeJsonPath();
|
|
@@ -4399,21 +4566,20 @@ async function isMcpServerRegistered(name) {
|
|
|
4399
4566
|
return Object.hasOwn(servers, name);
|
|
4400
4567
|
}
|
|
4401
4568
|
async function isPluginRegistered(pluginName) {
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
if (
|
|
4409
|
-
|
|
4569
|
+
const registryPath = getPluginsRegistry();
|
|
4570
|
+
if (registryPath !== null) {
|
|
4571
|
+
try {
|
|
4572
|
+
const raw = await readFile(registryPath, "utf8");
|
|
4573
|
+
const parsed = JSON.parse(raw);
|
|
4574
|
+
const plugins = parsed.plugins;
|
|
4575
|
+
if (plugins && typeof plugins === "object") {
|
|
4576
|
+
if (Object.hasOwn(plugins, pluginName)) return true;
|
|
4577
|
+
if (Object.keys(plugins).some((k) => k.split("@")[0] === pluginName)) return true;
|
|
4578
|
+
}
|
|
4579
|
+
} catch {
|
|
4410
4580
|
}
|
|
4411
|
-
} catch {
|
|
4412
4581
|
}
|
|
4413
|
-
for (const path of [
|
|
4414
|
-
join(homedir(), ".claude", "settings.json"),
|
|
4415
|
-
join(homedir(), ".claude.json")
|
|
4416
|
-
]) {
|
|
4582
|
+
for (const path of [getSettingsPath(), getMcpConfigPath()]) {
|
|
4417
4583
|
try {
|
|
4418
4584
|
const raw = await readFile(path, "utf8");
|
|
4419
4585
|
const parsed = JSON.parse(raw);
|
|
@@ -4429,6 +4595,7 @@ async function isPluginRegistered(pluginName) {
|
|
|
4429
4595
|
}
|
|
4430
4596
|
var init_readClaudeConfig = __esm({
|
|
4431
4597
|
"src/installers/lib/readClaudeConfig.ts"() {
|
|
4598
|
+
init_platform();
|
|
4432
4599
|
}
|
|
4433
4600
|
});
|
|
4434
4601
|
|
|
@@ -4827,6 +4994,7 @@ var init_ccHookAdd2 = __esm({
|
|
|
4827
4994
|
init_confirm();
|
|
4828
4995
|
init_diff();
|
|
4829
4996
|
init_err();
|
|
4997
|
+
init_platform();
|
|
4830
4998
|
init_preflight();
|
|
4831
4999
|
init_state2();
|
|
4832
5000
|
installCcHookAdd = async (ctx) => {
|
|
@@ -4850,10 +5018,10 @@ var init_ccHookAdd2 = __esm({
|
|
|
4850
5018
|
const e = pre.errors[0] ?? err(ctx, "/", "preflight failed", "preflight");
|
|
4851
5019
|
return { ok: false, phase: "preflight", error: e };
|
|
4852
5020
|
}
|
|
4853
|
-
const
|
|
5021
|
+
const settingsPath = getSettingsPath();
|
|
4854
5022
|
let existing;
|
|
4855
5023
|
try {
|
|
4856
|
-
existing = await readFile(
|
|
5024
|
+
existing = await readFile(settingsPath, "utf8");
|
|
4857
5025
|
} catch {
|
|
4858
5026
|
existing = null;
|
|
4859
5027
|
}
|
|
@@ -4884,7 +5052,7 @@ var init_ccHookAdd2 = __esm({
|
|
|
4884
5052
|
const newText = `${JSON.stringify(settings, null, 2)}
|
|
4885
5053
|
`;
|
|
4886
5054
|
const plan = {
|
|
4887
|
-
files: [{ target:
|
|
5055
|
+
files: [{ target: settingsPath, scope: "HOME", oldText: existing ?? "", newText }]
|
|
4888
5056
|
};
|
|
4889
5057
|
if (!ctx.opts.quiet) process.stdout.write(renderDiff(plan, ctx));
|
|
4890
5058
|
const conf = await confirmAt("L3", { ...ctx});
|
|
@@ -4895,10 +5063,10 @@ var init_ccHookAdd2 = __esm({
|
|
|
4895
5063
|
if (ctx.opts.dryRun) return { aborted: true, reason: "user-cancel" };
|
|
4896
5064
|
const bk = await backup(plan, ctx);
|
|
4897
5065
|
if (!bk.ok) return { ok: false, phase: "preflight", error: bk.error };
|
|
4898
|
-
await writeFile(
|
|
5066
|
+
await writeFile(settingsPath, newText);
|
|
4899
5067
|
let verify;
|
|
4900
5068
|
try {
|
|
4901
|
-
verify = JSON.parse(await readFile(
|
|
5069
|
+
verify = JSON.parse(await readFile(settingsPath, "utf8"));
|
|
4902
5070
|
} catch (e) {
|
|
4903
5071
|
return {
|
|
4904
5072
|
ok: false,
|
|
@@ -4926,7 +5094,7 @@ var init_ccHookAdd2 = __esm({
|
|
|
4926
5094
|
};
|
|
4927
5095
|
}
|
|
4928
5096
|
await updateInstalled(ctx.cwd, ctx.manifest.metadata.name, "", "");
|
|
4929
|
-
return { ok: true, backupId: bk.backupId, appliedFiles: [
|
|
5097
|
+
return { ok: true, backupId: bk.backupId, appliedFiles: [settingsPath] };
|
|
4930
5098
|
};
|
|
4931
5099
|
}
|
|
4932
5100
|
});
|
|
@@ -4982,10 +5150,10 @@ async function spawnCmd(ctx, cmd, args, timeoutMs, opts) {
|
|
|
4982
5150
|
child.stderr?.setEncoding("utf8").on("data", (chunk) => {
|
|
4983
5151
|
stderr += chunk;
|
|
4984
5152
|
});
|
|
4985
|
-
return await new Promise((
|
|
5153
|
+
return await new Promise((resolve18) => {
|
|
4986
5154
|
const timer = setTimeout(() => {
|
|
4987
5155
|
child.kill("SIGKILL");
|
|
4988
|
-
|
|
5156
|
+
resolve18({
|
|
4989
5157
|
ok: false,
|
|
4990
5158
|
phase: "spawn",
|
|
4991
5159
|
error: {
|
|
@@ -5001,7 +5169,7 @@ async function spawnCmd(ctx, cmd, args, timeoutMs, opts) {
|
|
|
5001
5169
|
child.on("error", (err2) => {
|
|
5002
5170
|
clearTimeout(timer);
|
|
5003
5171
|
const bashMissing = triedBash && err2.code === "ENOENT";
|
|
5004
|
-
|
|
5172
|
+
resolve18({
|
|
5005
5173
|
ok: false,
|
|
5006
5174
|
phase: "spawn",
|
|
5007
5175
|
error: {
|
|
@@ -5016,7 +5184,7 @@ async function spawnCmd(ctx, cmd, args, timeoutMs, opts) {
|
|
|
5016
5184
|
});
|
|
5017
5185
|
child.on("close", (code) => {
|
|
5018
5186
|
clearTimeout(timer);
|
|
5019
|
-
|
|
5187
|
+
resolve18({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
|
|
5020
5188
|
});
|
|
5021
5189
|
});
|
|
5022
5190
|
}
|
|
@@ -5056,7 +5224,7 @@ async function detectNative(ctx) {
|
|
|
5056
5224
|
const indicators = INSTALLED_INDICATORS[name];
|
|
5057
5225
|
if (indicators) {
|
|
5058
5226
|
for (const ind of indicators) {
|
|
5059
|
-
const dir = join(
|
|
5227
|
+
const dir = join(getSkillsDir(), ind);
|
|
5060
5228
|
try {
|
|
5061
5229
|
await access(dir);
|
|
5062
5230
|
return true;
|
|
@@ -5075,8 +5243,8 @@ async function detectNative(ctx) {
|
|
|
5075
5243
|
}
|
|
5076
5244
|
if (method === "npx-skill-installer") {
|
|
5077
5245
|
const skillName = extractSkillName(cmd, name);
|
|
5078
|
-
for (const
|
|
5079
|
-
const skillMd = join(
|
|
5246
|
+
for (const skillsDir of harnessSkillsDirs()) {
|
|
5247
|
+
const skillMd = join(skillsDir, skillName, "SKILL.md");
|
|
5080
5248
|
try {
|
|
5081
5249
|
await access(skillMd);
|
|
5082
5250
|
return true;
|
|
@@ -5095,7 +5263,7 @@ async function detectNative(ctx) {
|
|
|
5095
5263
|
}
|
|
5096
5264
|
}
|
|
5097
5265
|
if (method === "npm-cli") {
|
|
5098
|
-
const skillDir = join(
|
|
5266
|
+
const skillDir = join(getSkillsDir(), name);
|
|
5099
5267
|
try {
|
|
5100
5268
|
await access(skillDir);
|
|
5101
5269
|
return true;
|
|
@@ -5129,6 +5297,7 @@ async function isAlreadyInstalled(ctx, opts = {}) {
|
|
|
5129
5297
|
var IDEMPOTENT_CHECK_TIMEOUT_MS, INSTALLED_INDICATORS;
|
|
5130
5298
|
var init_idempotent = __esm({
|
|
5131
5299
|
"src/installers/lib/idempotent.ts"() {
|
|
5300
|
+
init_platform();
|
|
5132
5301
|
init_readClaudeConfig();
|
|
5133
5302
|
init_spawn();
|
|
5134
5303
|
IDEMPOTENT_CHECK_TIMEOUT_MS = 1e4;
|
|
@@ -5139,7 +5308,7 @@ var init_idempotent = __esm({
|
|
|
5139
5308
|
}
|
|
5140
5309
|
});
|
|
5141
5310
|
function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
|
|
5142
|
-
return new Promise((
|
|
5311
|
+
return new Promise((resolve18) => {
|
|
5143
5312
|
const isWin = process.platform === "win32";
|
|
5144
5313
|
const child = isWin ? spawn("cmd.exe", ["/c", "claude", ...claudeArgs], { cwd, windowsHide: true }) : spawn("claude", claudeArgs, { cwd, shell: false });
|
|
5145
5314
|
let stderr = "";
|
|
@@ -5148,15 +5317,15 @@ function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
|
|
|
5148
5317
|
});
|
|
5149
5318
|
const timer = setTimeout(() => {
|
|
5150
5319
|
child.kill("SIGKILL");
|
|
5151
|
-
|
|
5320
|
+
resolve18({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
|
|
5152
5321
|
}, timeoutMs);
|
|
5153
5322
|
child.on("error", (e) => {
|
|
5154
5323
|
clearTimeout(timer);
|
|
5155
|
-
|
|
5324
|
+
resolve18({ exitCode: -1, stderr: `${stderr}${e.message}` });
|
|
5156
5325
|
});
|
|
5157
5326
|
child.on("close", (code) => {
|
|
5158
5327
|
clearTimeout(timer);
|
|
5159
|
-
|
|
5328
|
+
resolve18({ exitCode: code ?? -1, stderr });
|
|
5160
5329
|
});
|
|
5161
5330
|
});
|
|
5162
5331
|
}
|
|
@@ -5326,7 +5495,7 @@ ${newEntry}
|
|
|
5326
5495
|
}
|
|
5327
5496
|
});
|
|
5328
5497
|
function gitRevParseHead(cwd, timeoutMs = 1e4) {
|
|
5329
|
-
return new Promise((
|
|
5498
|
+
return new Promise((resolve18) => {
|
|
5330
5499
|
const isWin = process.platform === "win32";
|
|
5331
5500
|
const child = isWin ? spawn("cmd.exe", ["/c", "git", "rev-parse", "HEAD"], { cwd, windowsHide: true }) : spawn("git", ["rev-parse", "HEAD"], { cwd, shell: false });
|
|
5332
5501
|
let stdout2 = "";
|
|
@@ -5335,15 +5504,15 @@ function gitRevParseHead(cwd, timeoutMs = 1e4) {
|
|
|
5335
5504
|
});
|
|
5336
5505
|
const timer = setTimeout(() => {
|
|
5337
5506
|
child.kill("SIGKILL");
|
|
5338
|
-
|
|
5507
|
+
resolve18({ sha: "", exit: -1 });
|
|
5339
5508
|
}, timeoutMs);
|
|
5340
5509
|
child.on("error", () => {
|
|
5341
5510
|
clearTimeout(timer);
|
|
5342
|
-
|
|
5511
|
+
resolve18({ sha: "", exit: -1 });
|
|
5343
5512
|
});
|
|
5344
5513
|
child.on("close", (code) => {
|
|
5345
5514
|
clearTimeout(timer);
|
|
5346
|
-
|
|
5515
|
+
resolve18({ sha: stdout2.trim(), exit: code ?? -1 });
|
|
5347
5516
|
});
|
|
5348
5517
|
});
|
|
5349
5518
|
}
|
|
@@ -6284,8 +6453,14 @@ var init_install_base = __esm({
|
|
|
6284
6453
|
// src/checkpoint/nextStep.ts
|
|
6285
6454
|
var nextStep_exports = {};
|
|
6286
6455
|
__export(nextStep_exports, {
|
|
6456
|
+
resolveAutoTransition: () => resolveAutoTransition,
|
|
6287
6457
|
resolveNext: () => resolveNext
|
|
6288
6458
|
});
|
|
6459
|
+
function resolveAutoTransition(envelopeValue, env = process.env.HARNESSED_AUTO_TRANSITION) {
|
|
6460
|
+
if (env === "true") return true;
|
|
6461
|
+
if (env === "false") return false;
|
|
6462
|
+
return envelopeValue ?? true;
|
|
6463
|
+
}
|
|
6289
6464
|
function resolveNext(ledger, autoTransition) {
|
|
6290
6465
|
const sub = nextPending(ledger);
|
|
6291
6466
|
if (sub === null) return { next: "done" };
|
|
@@ -6784,73 +6959,9 @@ audited ${yamls.length} manifest${yamls.length === 1 ? "" : "s"} \u2014 ${findin
|
|
|
6784
6959
|
process.exit(errorCount > 0 ? 1 : 0);
|
|
6785
6960
|
});
|
|
6786
6961
|
}
|
|
6787
|
-
var SUPPORTED = /* @__PURE__ */ new Set(["en", "zh-Hans"]);
|
|
6788
|
-
var currentLocale = null;
|
|
6789
|
-
var cache = {};
|
|
6790
|
-
function mapToSupported(raw) {
|
|
6791
|
-
if (!raw) return "en";
|
|
6792
|
-
if (/^zh([^a-z]|$)/i.test(raw)) return "zh-Hans";
|
|
6793
|
-
return "en";
|
|
6794
|
-
}
|
|
6795
|
-
function detectLocale() {
|
|
6796
|
-
const raw = process.env.HARNESSED_LANG || process.env.LC_ALL || process.env.LANG || process.env.LANGUAGE || safeIntlLocale();
|
|
6797
|
-
return mapToSupported(raw);
|
|
6798
|
-
}
|
|
6799
|
-
function safeIntlLocale() {
|
|
6800
|
-
try {
|
|
6801
|
-
return Intl.DateTimeFormat().resolvedOptions().locale;
|
|
6802
|
-
} catch {
|
|
6803
|
-
return void 0;
|
|
6804
|
-
}
|
|
6805
|
-
}
|
|
6806
|
-
function messagesDir() {
|
|
6807
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
6808
|
-
const candidates = [resolve(here, "..", "..", "messages"), resolve(here, "..", "messages")];
|
|
6809
|
-
for (const c of candidates) {
|
|
6810
|
-
try {
|
|
6811
|
-
readFileSync(join(c, "en.json"), "utf8");
|
|
6812
|
-
return c;
|
|
6813
|
-
} catch {
|
|
6814
|
-
}
|
|
6815
|
-
}
|
|
6816
|
-
return resolve(process.cwd(), "messages");
|
|
6817
|
-
}
|
|
6818
|
-
function loadLocale(locale) {
|
|
6819
|
-
if (cache[locale]) return cache[locale];
|
|
6820
|
-
const path = join(messagesDir(), `${locale}.json`);
|
|
6821
|
-
try {
|
|
6822
|
-
const raw = readFileSync(path, "utf8");
|
|
6823
|
-
cache[locale] = JSON.parse(raw);
|
|
6824
|
-
} catch {
|
|
6825
|
-
cache[locale] = {};
|
|
6826
|
-
}
|
|
6827
|
-
return cache[locale];
|
|
6828
|
-
}
|
|
6829
|
-
function setLocale(locale) {
|
|
6830
|
-
if (!locale) return;
|
|
6831
|
-
const mapped = mapToSupported(locale);
|
|
6832
|
-
if (SUPPORTED.has(mapped)) currentLocale = mapped;
|
|
6833
|
-
}
|
|
6834
|
-
function getLocale() {
|
|
6835
|
-
if (currentLocale === null) currentLocale = detectLocale();
|
|
6836
|
-
return currentLocale;
|
|
6837
|
-
}
|
|
6838
|
-
function t(key, params) {
|
|
6839
|
-
const locale = getLocale();
|
|
6840
|
-
const primary = loadLocale(locale);
|
|
6841
|
-
let template = primary[key];
|
|
6842
|
-
if (template === void 0 && locale !== "en") {
|
|
6843
|
-
template = loadLocale("en")[key];
|
|
6844
|
-
}
|
|
6845
|
-
if (template === void 0) template = key;
|
|
6846
|
-
if (!params) return template;
|
|
6847
|
-
return template.replace(/\{\{(\w+)\}\}/g, (_match, name) => {
|
|
6848
|
-
const v = params[name];
|
|
6849
|
-
return v === void 0 ? `{{${name}}}` : String(v);
|
|
6850
|
-
});
|
|
6851
|
-
}
|
|
6852
6962
|
|
|
6853
6963
|
// src/cli/audit-log.ts
|
|
6964
|
+
init_i18n();
|
|
6854
6965
|
init_harnessedRoot();
|
|
6855
6966
|
function auditPath() {
|
|
6856
6967
|
return harnessedFile("audit.log");
|
|
@@ -6882,7 +6993,7 @@ function renderHumanTable(records) {
|
|
|
6882
6993
|
}
|
|
6883
6994
|
}
|
|
6884
6995
|
function pipeToJq(filterExpr, lines) {
|
|
6885
|
-
return new Promise((
|
|
6996
|
+
return new Promise((resolve18, reject) => {
|
|
6886
6997
|
const child = spawn("jq", [filterExpr], {
|
|
6887
6998
|
stdio: ["pipe", "inherit", "inherit"],
|
|
6888
6999
|
windowsHide: true
|
|
@@ -6891,12 +7002,12 @@ function pipeToJq(filterExpr, lines) {
|
|
|
6891
7002
|
const e = err2;
|
|
6892
7003
|
if (e.code === "ENOENT") {
|
|
6893
7004
|
console.error(t("audit_log.jq_missing"));
|
|
6894
|
-
|
|
7005
|
+
resolve18(1);
|
|
6895
7006
|
} else {
|
|
6896
7007
|
reject(err2);
|
|
6897
7008
|
}
|
|
6898
7009
|
});
|
|
6899
|
-
child.on("close", (code) =>
|
|
7010
|
+
child.on("close", (code) => resolve18(code ?? 0));
|
|
6900
7011
|
child.stdin.write(lines.join("\n"));
|
|
6901
7012
|
child.stdin.end();
|
|
6902
7013
|
});
|
|
@@ -6957,6 +7068,9 @@ function registerAuditLog(program2) {
|
|
|
6957
7068
|
}
|
|
6958
7069
|
);
|
|
6959
7070
|
}
|
|
7071
|
+
|
|
7072
|
+
// src/cli/backup-list.ts
|
|
7073
|
+
init_i18n();
|
|
6960
7074
|
init_backup();
|
|
6961
7075
|
function registerBackupList(program2) {
|
|
6962
7076
|
const backup2 = program2.command("backup").description("Backup snapshot operations");
|
|
@@ -7192,6 +7306,7 @@ function registerCompact(program2) {
|
|
|
7192
7306
|
}
|
|
7193
7307
|
|
|
7194
7308
|
// src/cli/doctor.ts
|
|
7309
|
+
init_i18n();
|
|
7195
7310
|
init_doctor_registry();
|
|
7196
7311
|
function registerDoctor(program2) {
|
|
7197
7312
|
program2.command("doctor").description(
|
|
@@ -7348,6 +7463,9 @@ function fireEntry(clause, master) {
|
|
|
7348
7463
|
if (isMaster) entry.is_master = true;
|
|
7349
7464
|
return entry;
|
|
7350
7465
|
}
|
|
7466
|
+
|
|
7467
|
+
// src/cli/gc.ts
|
|
7468
|
+
init_i18n();
|
|
7351
7469
|
init_backup();
|
|
7352
7470
|
var DURATION_RE = /^(\d+)([dhmw])$/;
|
|
7353
7471
|
function parseDuration(s) {
|
|
@@ -7438,6 +7556,7 @@ ${t("gc.invalid_duration.fix")}`
|
|
|
7438
7556
|
|
|
7439
7557
|
// src/cli/install.ts
|
|
7440
7558
|
init_package();
|
|
7559
|
+
init_i18n();
|
|
7441
7560
|
init_installers();
|
|
7442
7561
|
init_path_guard();
|
|
7443
7562
|
init_validate();
|
|
@@ -7458,24 +7577,26 @@ function registerInstall(program2) {
|
|
|
7458
7577
|
const { resolveAlias: resolveAlias2 } = await Promise.resolve().then(() => (init_aliases(), aliases_exports));
|
|
7459
7578
|
const resolvedName = resolveAlias2(name) ?? name;
|
|
7460
7579
|
checkPathSafe(resolvedName);
|
|
7461
|
-
const
|
|
7462
|
-
|
|
7580
|
+
const candidatePaths = ["tools", "skill-packs", "optional"].map(
|
|
7581
|
+
(dir) => resolve(getPackageRoot(), `manifests/${dir}/${resolvedName}.yaml`)
|
|
7582
|
+
);
|
|
7463
7583
|
let yamlSrc;
|
|
7464
|
-
let chosenPath
|
|
7465
|
-
|
|
7466
|
-
yamlSrc = await readFile(manifestPath, "utf8");
|
|
7467
|
-
} catch {
|
|
7584
|
+
let chosenPath;
|
|
7585
|
+
for (const p5 of candidatePaths) {
|
|
7468
7586
|
try {
|
|
7469
|
-
yamlSrc = await readFile(
|
|
7470
|
-
chosenPath =
|
|
7587
|
+
yamlSrc = await readFile(p5, "utf8");
|
|
7588
|
+
chosenPath = p5;
|
|
7589
|
+
break;
|
|
7471
7590
|
} 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
7591
|
}
|
|
7478
7592
|
}
|
|
7593
|
+
if (yamlSrc === void 0 || chosenPath === void 0) {
|
|
7594
|
+
console.error(
|
|
7595
|
+
`${t("install.manifest_not_found", { name: resolvedName })}
|
|
7596
|
+
${t("install.manifest_not_found.fix", { name: resolvedName })}`
|
|
7597
|
+
);
|
|
7598
|
+
process.exit(1);
|
|
7599
|
+
}
|
|
7479
7600
|
const v = validateManifestFile(yamlSrc, chosenPath);
|
|
7480
7601
|
if (!v.ok) {
|
|
7481
7602
|
for (const e of v.errors) console.error(`error: ${e.message} at ${e.path}`);
|
|
@@ -7535,74 +7656,248 @@ function registerLearn(program2) {
|
|
|
7535
7656
|
process.exit(0);
|
|
7536
7657
|
});
|
|
7537
7658
|
}
|
|
7538
|
-
var QA = [
|
|
7539
|
-
{ q: "\u2460 \u662F\u771F reusable surface \u8FD8\u662F\u4E34\u65F6 wrapper?", f: "q1_reusable_surface" },
|
|
7540
|
-
{ q: "\u2461 \u4E0A\u6E38\u540D\u5B57 fit \u9879\u76EE shape \u5417? \u6709\u73B0\u6709\u547D\u540D\u51B2\u7A81\u5417?", f: "q2_name_fit" },
|
|
7541
|
-
{ q: "\u2462 \u4E0E\u5DF2\u88C5\u914D\u7EC4\u4EF6\u6709 overlap surface \u5417?", f: "q3_overlap" },
|
|
7542
|
-
{ q: "\u2463 \u662F import \u6982\u5FF5 (\u53EF\u63A7) \u8FD8\u662F import \u522B\u4EBA\u4EA7\u54C1\u8EAB\u4EFD (\u9AD8\u8026\u5408)?", f: "q4_concept_vs_identity" },
|
|
7543
|
-
{ q: "\u2464 user \u4E0D\u77E5 upstream \u8FD8\u80FD\u7406\u89E3\u8BE5\u88C5\u914D\u5417?", f: "q5_user_understanding" }
|
|
7544
|
-
];
|
|
7545
|
-
function basename(upstream) {
|
|
7546
|
-
return (upstream.split("/").pop() ?? upstream).replace(/\.git$/, "");
|
|
7547
|
-
}
|
|
7548
|
-
function registerManifestAdd(program2) {
|
|
7549
|
-
program2.command("manifest-add <upstream>").description(
|
|
7550
|
-
"Add a new upstream adapter (EE-5 5-question merge gate; immediate by default \u2014 use --dry-run for preview)"
|
|
7551
|
-
).option("--category <cat>", "manifest category (skill-packs | tools)", "skill-packs").option("--name <name>", "short adapter name (defaults to <upstream> basename)").option("--dry-run", "preview only \u2014 do not write JSON (opt-in for advanced users)").option("--non-interactive", "CI/scripts \u2014 WARN-only dry-run").action(async (upstream, raw) => {
|
|
7552
|
-
const name = raw.name ?? basename(upstream);
|
|
7553
|
-
const category = raw.category ?? "skill-packs";
|
|
7554
|
-
const outPath = `manifests/${category}/${name}.ee5-answers.json`;
|
|
7555
|
-
if (raw.nonInteractive) {
|
|
7556
|
-
console.warn(t("manifest_add.non_interactive_warn"));
|
|
7557
|
-
console.log(t("manifest_add.dry_run_preview", { upstream, path: outPath }));
|
|
7558
|
-
process.exit(0);
|
|
7559
|
-
}
|
|
7560
|
-
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
7561
|
-
const payload = {
|
|
7562
|
-
upstream,
|
|
7563
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7564
|
-
author: process.env.USER ?? process.env.USERNAME ?? "unknown"
|
|
7565
|
-
};
|
|
7566
|
-
for (const { q, f } of QA) {
|
|
7567
|
-
const a = (await rl.question(`${q}
|
|
7568
|
-
> `)).trim();
|
|
7569
|
-
if (!a) {
|
|
7570
|
-
console.error(t("manifest_add.empty_answer"));
|
|
7571
|
-
rl.close();
|
|
7572
|
-
process.exit(1);
|
|
7573
|
-
}
|
|
7574
|
-
payload[f] = a;
|
|
7575
|
-
}
|
|
7576
|
-
rl.close();
|
|
7577
|
-
const dryRun = raw.dryRun === true;
|
|
7578
|
-
if (!dryRun) {
|
|
7579
|
-
writeFileSync(outPath, `${JSON.stringify(payload, null, 2)}
|
|
7580
|
-
`, "utf8");
|
|
7581
|
-
console.log(t("manifest_add.gate_passed_wrote", { path: outPath }));
|
|
7582
|
-
} else {
|
|
7583
|
-
console.log(t("manifest_add.gate_passed_dry_run", { path: outPath }));
|
|
7584
|
-
console.log(JSON.stringify(payload, null, 2));
|
|
7585
|
-
}
|
|
7586
|
-
process.exit(0);
|
|
7587
|
-
});
|
|
7588
|
-
}
|
|
7589
7659
|
|
|
7590
|
-
// src/cli/
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7660
|
+
// src/cli/lib/here.ts
|
|
7661
|
+
init_nextStep();
|
|
7662
|
+
init_i18n();
|
|
7663
|
+
init_harnessedRoot();
|
|
7664
|
+
|
|
7665
|
+
// src/cli/status.ts
|
|
7666
|
+
init_evidence();
|
|
7667
|
+
init_ledger();
|
|
7668
|
+
init_state();
|
|
7669
|
+
init_i18n();
|
|
7670
|
+
init_harnessedRoot();
|
|
7671
|
+
init_state2();
|
|
7672
|
+
function statusMarker(status) {
|
|
7673
|
+
switch (status) {
|
|
7674
|
+
case "done":
|
|
7675
|
+
return "\u2705 done";
|
|
7676
|
+
case "pending":
|
|
7677
|
+
return "\u23F3 pending";
|
|
7678
|
+
case "failed":
|
|
7679
|
+
return "\u2717 failed";
|
|
7680
|
+
case "skipped":
|
|
7681
|
+
return "\u2B1C skipped";
|
|
7682
|
+
case "rejected":
|
|
7683
|
+
return "\u{1F6AB} rejected";
|
|
7684
|
+
}
|
|
7685
|
+
}
|
|
7686
|
+
function evidenceLabel(entry) {
|
|
7687
|
+
switch (entry.evidence_status) {
|
|
7688
|
+
case "verified":
|
|
7689
|
+
return "evidence: verified";
|
|
7690
|
+
case "overridden":
|
|
7691
|
+
return "evidence: overridden (--force)";
|
|
7692
|
+
default:
|
|
7693
|
+
return "evidence: none_declared";
|
|
7694
|
+
}
|
|
7695
|
+
}
|
|
7696
|
+
async function buildRecoverLines(workflow, ledger) {
|
|
7697
|
+
const lines = [];
|
|
7698
|
+
if (!workflow || ledger.length === 0) {
|
|
7699
|
+
lines.push("no ledger \u2014 run gates + start");
|
|
7700
|
+
return lines;
|
|
7701
|
+
}
|
|
7702
|
+
lines.push(`workflow: ${workflow.phase} (${workflow.status}) started ${workflow.started_at}`);
|
|
7703
|
+
const next = nextPending(ledger);
|
|
7704
|
+
for (const e of ledger) {
|
|
7705
|
+
const marker = statusMarker(e.status);
|
|
7706
|
+
let suffix = "";
|
|
7707
|
+
if (e.status === "skipped") {
|
|
7708
|
+
suffix = e.reason ? ` (skipped: ${e.reason})` : "";
|
|
7709
|
+
} else if (e.status === "done") {
|
|
7710
|
+
suffix = ` ${evidenceLabel(e)}`;
|
|
7711
|
+
}
|
|
7712
|
+
const arrow = e.sub === next ? " \u2190 next" : "";
|
|
7713
|
+
lines.push(` ${e.sub} ${marker}${suffix}${arrow}`);
|
|
7714
|
+
}
|
|
7715
|
+
if (next) {
|
|
7716
|
+
lines.push(`\u2192 next: harnessed prompt ${next}`);
|
|
7717
|
+
} else {
|
|
7718
|
+
lines.push("\u2192 all subs resolved (no pending work)");
|
|
7719
|
+
}
|
|
7720
|
+
for (const e of ledger) {
|
|
7721
|
+
if (!e.evidence || e.evidence.length === 0) continue;
|
|
7722
|
+
const drift = await detectDrift(e.evidence);
|
|
7723
|
+
for (const d of drift) {
|
|
7724
|
+
const now = d.now === "" ? "missing" : `${d.now.slice(0, 7)}\u2026`;
|
|
7725
|
+
lines.push(`\u26A0 drift: ${d.path} sha256 changed (was ${d.was.slice(0, 7)}\u2026, now ${now})`);
|
|
7726
|
+
}
|
|
7727
|
+
}
|
|
7728
|
+
return lines;
|
|
7729
|
+
}
|
|
7730
|
+
function registerStatus(program2) {
|
|
7731
|
+
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) => {
|
|
7732
|
+
if (opts.recover) {
|
|
7733
|
+
const wf = await readCurrentWorkflow();
|
|
7734
|
+
const ledger = wf?.sub_progress ?? [];
|
|
7735
|
+
const lines = await buildRecoverLines(wf, ledger);
|
|
7736
|
+
for (const l of lines) console.log(l);
|
|
7737
|
+
return;
|
|
7738
|
+
}
|
|
7739
|
+
const state = await readState(process.cwd());
|
|
7740
|
+
const names = Object.keys(state.installed).sort();
|
|
7741
|
+
if (names.length === 0) {
|
|
7742
|
+
console.log(t("status.no_installs", { path: harnessedFile("state.json") }));
|
|
7743
|
+
} else {
|
|
7744
|
+
for (const n of names) {
|
|
7745
|
+
const e = state.installed[n];
|
|
7746
|
+
if (!e) continue;
|
|
7747
|
+
console.log(`${n} @ ${e.version} (installed ${e.installedAt})`);
|
|
7748
|
+
}
|
|
7749
|
+
console.log(t("status.summary_installs", { count: names.length }));
|
|
7750
|
+
}
|
|
7751
|
+
const lockPath = harnessedFile(".lock");
|
|
7752
|
+
try {
|
|
7753
|
+
const isLocked = await lockfile.check(getHarnessedRoot(), {
|
|
7754
|
+
lockfilePath: lockPath,
|
|
7755
|
+
stale: 1e4
|
|
7756
|
+
});
|
|
7757
|
+
if (isLocked) {
|
|
7758
|
+
const s = await stat(lockPath);
|
|
7759
|
+
const ageMs = Date.now() - s.mtime.getTime();
|
|
7760
|
+
const stale = ageMs > 1e4;
|
|
7761
|
+
console.log(
|
|
7762
|
+
t("status.lock_held", {
|
|
7763
|
+
since: s.mtime.toISOString(),
|
|
7764
|
+
staleSuffix: stale ? t("status.lock_held.stale_suffix") : ""
|
|
7765
|
+
})
|
|
7766
|
+
);
|
|
7767
|
+
console.log(t("status.lock_release_hint", { path: lockPath }));
|
|
7768
|
+
} else {
|
|
7769
|
+
console.log(t("status.lock_free"));
|
|
7770
|
+
}
|
|
7771
|
+
} catch {
|
|
7772
|
+
}
|
|
7773
|
+
});
|
|
7774
|
+
}
|
|
7775
|
+
|
|
7776
|
+
// src/cli/lib/here.ts
|
|
7777
|
+
function parseBareInvocation(argvRest) {
|
|
7778
|
+
const tokens = [];
|
|
7779
|
+
for (let i = 0; i < argvRest.length; i++) {
|
|
7780
|
+
const a = argvRest[i];
|
|
7781
|
+
if (a === "--lang") {
|
|
7782
|
+
i++;
|
|
7783
|
+
continue;
|
|
7784
|
+
}
|
|
7785
|
+
if (a?.startsWith("--lang=")) continue;
|
|
7786
|
+
if (a !== void 0) tokens.push(a);
|
|
7787
|
+
}
|
|
7788
|
+
if (tokens.length === 0) return { here: true, json: false };
|
|
7789
|
+
if (tokens.length === 1 && tokens[0] === "--json") return { here: true, json: true };
|
|
7790
|
+
return { here: false, json: false };
|
|
7791
|
+
}
|
|
7792
|
+
async function buildHereView(wf, ledger, auto) {
|
|
7793
|
+
if (!wf) {
|
|
7794
|
+
return {
|
|
7795
|
+
lines: [
|
|
7796
|
+
t("here.no_workflow", { path: harnessedFile("workflows.json") }),
|
|
7797
|
+
t("here.no_workflow.hint")
|
|
7798
|
+
],
|
|
7799
|
+
json: { active: false }
|
|
7800
|
+
};
|
|
7801
|
+
}
|
|
7802
|
+
const body = await buildRecoverLines(wf, ledger);
|
|
7803
|
+
const step = resolveNext(ledger, auto);
|
|
7804
|
+
const lines = [
|
|
7805
|
+
...body,
|
|
7806
|
+
`NEXT: ${step.next}`,
|
|
7807
|
+
...step.sub ? [`\u2192 run: harnessed prompt ${step.sub}`] : []
|
|
7808
|
+
];
|
|
7809
|
+
return {
|
|
7810
|
+
lines,
|
|
7811
|
+
json: {
|
|
7812
|
+
active: true,
|
|
7813
|
+
phase: wf.phase,
|
|
7814
|
+
status: wf.status,
|
|
7815
|
+
started_at: wf.started_at,
|
|
7816
|
+
next: step.next,
|
|
7817
|
+
sub: step.sub,
|
|
7818
|
+
hint: step.hint,
|
|
7819
|
+
sub_progress: ledger
|
|
7820
|
+
}
|
|
7821
|
+
};
|
|
7822
|
+
}
|
|
7823
|
+
async function runHere(opts) {
|
|
7824
|
+
const { readCurrentWorkflow: readCurrentWorkflow2 } = await Promise.resolve().then(() => (init_state(), state_exports));
|
|
7825
|
+
const wf = await readCurrentWorkflow2();
|
|
7826
|
+
const ledger = wf?.sub_progress ?? [];
|
|
7827
|
+
const auto = resolveAutoTransition(wf?.auto_transition);
|
|
7828
|
+
const view = await buildHereView(wf, ledger, auto);
|
|
7829
|
+
if (opts.json) {
|
|
7830
|
+
console.log(JSON.stringify(view.json, null, 2));
|
|
7831
|
+
} else {
|
|
7832
|
+
for (const l of view.lines) console.log(l);
|
|
7833
|
+
}
|
|
7834
|
+
process.exit(0);
|
|
7835
|
+
}
|
|
7836
|
+
|
|
7837
|
+
// src/cli/manifest-add.ts
|
|
7838
|
+
init_i18n();
|
|
7839
|
+
var QA = [
|
|
7840
|
+
{ q: "\u2460 \u662F\u771F reusable surface \u8FD8\u662F\u4E34\u65F6 wrapper?", f: "q1_reusable_surface" },
|
|
7841
|
+
{ q: "\u2461 \u4E0A\u6E38\u540D\u5B57 fit \u9879\u76EE shape \u5417? \u6709\u73B0\u6709\u547D\u540D\u51B2\u7A81\u5417?", f: "q2_name_fit" },
|
|
7842
|
+
{ q: "\u2462 \u4E0E\u5DF2\u88C5\u914D\u7EC4\u4EF6\u6709 overlap surface \u5417?", f: "q3_overlap" },
|
|
7843
|
+
{ q: "\u2463 \u662F import \u6982\u5FF5 (\u53EF\u63A7) \u8FD8\u662F import \u522B\u4EBA\u4EA7\u54C1\u8EAB\u4EFD (\u9AD8\u8026\u5408)?", f: "q4_concept_vs_identity" },
|
|
7844
|
+
{ q: "\u2464 user \u4E0D\u77E5 upstream \u8FD8\u80FD\u7406\u89E3\u8BE5\u88C5\u914D\u5417?", f: "q5_user_understanding" }
|
|
7845
|
+
];
|
|
7846
|
+
function basename(upstream) {
|
|
7847
|
+
return (upstream.split("/").pop() ?? upstream).replace(/\.git$/, "");
|
|
7848
|
+
}
|
|
7849
|
+
function registerManifestAdd(program2) {
|
|
7850
|
+
program2.command("manifest-add <upstream>").description(
|
|
7851
|
+
"Add a new upstream adapter (EE-5 5-question merge gate; immediate by default \u2014 use --dry-run for preview)"
|
|
7852
|
+
).option("--category <cat>", "manifest category (skill-packs | tools)", "skill-packs").option("--name <name>", "short adapter name (defaults to <upstream> basename)").option("--dry-run", "preview only \u2014 do not write JSON (opt-in for advanced users)").option("--non-interactive", "CI/scripts \u2014 WARN-only dry-run").action(async (upstream, raw) => {
|
|
7853
|
+
const name = raw.name ?? basename(upstream);
|
|
7854
|
+
const category = raw.category ?? "skill-packs";
|
|
7855
|
+
const outPath = `manifests/${category}/${name}.ee5-answers.json`;
|
|
7856
|
+
if (raw.nonInteractive) {
|
|
7857
|
+
console.warn(t("manifest_add.non_interactive_warn"));
|
|
7858
|
+
console.log(t("manifest_add.dry_run_preview", { upstream, path: outPath }));
|
|
7859
|
+
process.exit(0);
|
|
7860
|
+
}
|
|
7861
|
+
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
7862
|
+
const payload = {
|
|
7863
|
+
upstream,
|
|
7864
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7865
|
+
author: process.env.USER ?? process.env.USERNAME ?? "unknown"
|
|
7866
|
+
};
|
|
7867
|
+
for (const { q, f } of QA) {
|
|
7868
|
+
const a = (await rl.question(`${q}
|
|
7869
|
+
> `)).trim();
|
|
7870
|
+
if (!a) {
|
|
7871
|
+
console.error(t("manifest_add.empty_answer"));
|
|
7872
|
+
rl.close();
|
|
7873
|
+
process.exit(1);
|
|
7874
|
+
}
|
|
7875
|
+
payload[f] = a;
|
|
7876
|
+
}
|
|
7877
|
+
rl.close();
|
|
7878
|
+
const dryRun = raw.dryRun === true;
|
|
7879
|
+
if (!dryRun) {
|
|
7880
|
+
writeFileSync(outPath, `${JSON.stringify(payload, null, 2)}
|
|
7881
|
+
`, "utf8");
|
|
7882
|
+
console.log(t("manifest_add.gate_passed_wrote", { path: outPath }));
|
|
7883
|
+
} else {
|
|
7884
|
+
console.log(t("manifest_add.gate_passed_dry_run", { path: outPath }));
|
|
7885
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
7886
|
+
}
|
|
7887
|
+
process.exit(0);
|
|
7888
|
+
});
|
|
7889
|
+
}
|
|
7890
|
+
|
|
7891
|
+
// src/cli/next.ts
|
|
7892
|
+
function registerNext(program2) {
|
|
7893
|
+
program2.command("next").description(
|
|
7894
|
+
"Print the deterministic next-step contract (NEXT: auto|manual|done) for the active workflow"
|
|
7895
|
+
).action(async () => {
|
|
7896
|
+
const { readCurrentWorkflow: readCurrentWorkflow2 } = await Promise.resolve().then(() => (init_state(), state_exports));
|
|
7897
|
+
const { resolveNext: resolveNext2, resolveAutoTransition: resolveAutoTransition2 } = await Promise.resolve().then(() => (init_nextStep(), nextStep_exports));
|
|
7898
|
+
const current = await readCurrentWorkflow2();
|
|
7899
|
+
const ledger = current?.sub_progress ?? [];
|
|
7900
|
+
const auto = resolveAutoTransition2(current?.auto_transition);
|
|
7606
7901
|
const step = resolveNext2(ledger, auto);
|
|
7607
7902
|
console.log(`NEXT: ${step.next}`);
|
|
7608
7903
|
if (step.sub) console.log(`SUB: ${step.sub}`);
|
|
@@ -7612,6 +7907,8 @@ function registerNext(program2) {
|
|
|
7612
7907
|
}
|
|
7613
7908
|
|
|
7614
7909
|
// src/cli/prompt.ts
|
|
7910
|
+
init_i18n();
|
|
7911
|
+
init_localeYaml();
|
|
7615
7912
|
init_run();
|
|
7616
7913
|
init_generateCommands();
|
|
7617
7914
|
init_packagePath();
|
|
@@ -7657,16 +7954,17 @@ ${lines.join("\n")}
|
|
|
7657
7954
|
return "";
|
|
7658
7955
|
}
|
|
7659
7956
|
}
|
|
7660
|
-
async function buildDisciplinesSection(sub, packageRoot) {
|
|
7957
|
+
async function buildDisciplinesSection(sub, packageRoot, locale = getLocale()) {
|
|
7661
7958
|
try {
|
|
7662
7959
|
const workflowsDir = resolve(packageRoot, "workflows");
|
|
7960
|
+
const disciplinesDir = resolve(workflowsDir, "disciplines");
|
|
7663
7961
|
const applied = await loadSubArrayField(sub, packageRoot, "disciplines_applied");
|
|
7664
7962
|
const names = applied.filter((d) => d !== "language");
|
|
7665
7963
|
if (names.length === 0) return "";
|
|
7666
7964
|
const blocks = [];
|
|
7667
7965
|
for (const name of names) {
|
|
7668
7966
|
try {
|
|
7669
|
-
const dRaw = await readFile(
|
|
7967
|
+
const dRaw = await readFile(resolveLocaleYaml(disciplinesDir, name, locale), "utf8");
|
|
7670
7968
|
const dDoc = parse(dRaw);
|
|
7671
7969
|
const rules = Array.isArray(dDoc?.rules) ? dDoc.rules : [];
|
|
7672
7970
|
const descs = rules.map((r) => typeof r.description === "string" ? r.description.trim() : "").filter((s) => s.length > 0).map((s) => ` - ${s.replace(/\s+/g, " ")}`);
|
|
@@ -7812,6 +8110,9 @@ function registerReleasePreflight(program2) {
|
|
|
7812
8110
|
process.exit(failed ? 1 : 0);
|
|
7813
8111
|
});
|
|
7814
8112
|
}
|
|
8113
|
+
|
|
8114
|
+
// src/cli/research.ts
|
|
8115
|
+
init_i18n();
|
|
7815
8116
|
init_run();
|
|
7816
8117
|
init_packagePath();
|
|
7817
8118
|
init_run2();
|
|
@@ -7853,6 +8154,7 @@ function registerResearch(program2) {
|
|
|
7853
8154
|
}
|
|
7854
8155
|
|
|
7855
8156
|
// src/cli/resume.ts
|
|
8157
|
+
init_i18n();
|
|
7856
8158
|
function registerResume(program2) {
|
|
7857
8159
|
program2.command("resume").description(
|
|
7858
8160
|
"Reload checkpoint from paused workflow + print resume hint (D-03 \u2014 user invokes phase command manually)"
|
|
@@ -7917,6 +8219,9 @@ function registerRetro(program2) {
|
|
|
7917
8219
|
process.exit(0);
|
|
7918
8220
|
});
|
|
7919
8221
|
}
|
|
8222
|
+
|
|
8223
|
+
// src/cli/rollback.ts
|
|
8224
|
+
init_i18n();
|
|
7920
8225
|
init_backup();
|
|
7921
8226
|
function normalizeEol(buf, eol) {
|
|
7922
8227
|
const lf = buf.toString("utf8").replace(/\r\n/g, "\n");
|
|
@@ -7988,9 +8293,16 @@ ${t("rollback.metadata_unreadable.fix")}`
|
|
|
7988
8293
|
|
|
7989
8294
|
// src/cli.ts
|
|
7990
8295
|
init_run2();
|
|
8296
|
+
|
|
8297
|
+
// src/cli/setup.ts
|
|
8298
|
+
init_i18n();
|
|
8299
|
+
init_platform();
|
|
8300
|
+
|
|
8301
|
+
// src/cli/lib/capabilityResolver.ts
|
|
8302
|
+
init_platform();
|
|
7991
8303
|
function readInstalledPlugins(homedirOverride) {
|
|
7992
|
-
const
|
|
7993
|
-
|
|
8304
|
+
const path = getPluginsRegistry(homedirOverride);
|
|
8305
|
+
if (path === null) return /* @__PURE__ */ new Set();
|
|
7994
8306
|
let raw;
|
|
7995
8307
|
try {
|
|
7996
8308
|
raw = readFileSync(path, "utf8");
|
|
@@ -8015,8 +8327,7 @@ function readInstalledPlugins(homedirOverride) {
|
|
|
8015
8327
|
return out;
|
|
8016
8328
|
}
|
|
8017
8329
|
function readInstalledUserSkills(homedirOverride) {
|
|
8018
|
-
const
|
|
8019
|
-
const skillsRoot = join(home, ".claude", "skills");
|
|
8330
|
+
const skillsRoot = getSkillsDir(homedirOverride);
|
|
8020
8331
|
try {
|
|
8021
8332
|
const entries = readdirSync(skillsRoot, { withFileTypes: true });
|
|
8022
8333
|
const out = /* @__PURE__ */ new Set();
|
|
@@ -8093,55 +8404,57 @@ function renderSkillBody(body, capabilities, installedPlugins, installedUserSkil
|
|
|
8093
8404
|
}
|
|
8094
8405
|
|
|
8095
8406
|
// src/cli/lib/enableAgentTeamsInSettings.ts
|
|
8407
|
+
init_platform();
|
|
8408
|
+
|
|
8409
|
+
// src/cli/lib/settingsWriter.ts
|
|
8096
8410
|
init_harnessedRoot();
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
async function enableAgentTeamsInSettings() {
|
|
8101
|
-
const path = settingsPath();
|
|
8411
|
+
init_platform();
|
|
8412
|
+
async function mergeSettingsEnvKey(key, value, opts = {}) {
|
|
8413
|
+
const path = getSettingsPath();
|
|
8102
8414
|
let raw;
|
|
8103
8415
|
try {
|
|
8104
8416
|
raw = await readFile(path, "utf8");
|
|
8105
8417
|
} catch (err2) {
|
|
8106
8418
|
const code = err2.code;
|
|
8107
8419
|
if (code !== "ENOENT") {
|
|
8108
|
-
return {
|
|
8420
|
+
return { outcome: "warn", message: `read ${path} failed: ${err2.message}` };
|
|
8109
8421
|
}
|
|
8110
|
-
return
|
|
8422
|
+
return createFresh(path, key, value);
|
|
8111
8423
|
}
|
|
8112
8424
|
let data;
|
|
8113
8425
|
try {
|
|
8114
8426
|
const parsed = JSON.parse(raw);
|
|
8115
8427
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8116
|
-
return {
|
|
8428
|
+
return { outcome: "warn", message: `${path} is not a JSON object` };
|
|
8117
8429
|
}
|
|
8118
8430
|
data = parsed;
|
|
8119
8431
|
} catch (err2) {
|
|
8120
|
-
return {
|
|
8432
|
+
return { outcome: "warn", message: `${path} malformed JSON: ${err2.message}` };
|
|
8121
8433
|
}
|
|
8122
8434
|
const env = data.env ?? {};
|
|
8123
|
-
|
|
8124
|
-
|
|
8435
|
+
const existing = env[key];
|
|
8436
|
+
if (typeof existing === "string" && existing.length > 0 && opts.skipIfPresent?.(existing) === true) {
|
|
8437
|
+
return { outcome: "already", path, existing };
|
|
8125
8438
|
}
|
|
8126
|
-
const
|
|
8127
|
-
if (
|
|
8128
|
-
data.env = { ...env,
|
|
8439
|
+
const backup2 = await backupOriginal(raw);
|
|
8440
|
+
if (backup2.status === "warn") return { outcome: "warn", message: backup2.message };
|
|
8441
|
+
data.env = { ...env, [key]: value };
|
|
8129
8442
|
const writeErr = await atomicWrite(path, `${JSON.stringify(data, null, 2)}
|
|
8130
8443
|
`);
|
|
8131
|
-
if (writeErr) return {
|
|
8132
|
-
return {
|
|
8444
|
+
if (writeErr) return { outcome: "warn", message: writeErr };
|
|
8445
|
+
return { outcome: "merged", path, backupPath: backup2.path };
|
|
8133
8446
|
}
|
|
8134
|
-
async function
|
|
8135
|
-
const data = { env: {
|
|
8447
|
+
async function createFresh(path, key, value) {
|
|
8448
|
+
const data = { env: { [key]: value } };
|
|
8136
8449
|
try {
|
|
8137
|
-
await mkdir(
|
|
8450
|
+
await mkdir(dirname(path), { recursive: true });
|
|
8138
8451
|
} catch (err2) {
|
|
8139
|
-
return {
|
|
8452
|
+
return { outcome: "warn", message: `mkdir ~/.claude failed: ${err2.message}` };
|
|
8140
8453
|
}
|
|
8141
8454
|
const writeErr = await atomicWrite(path, `${JSON.stringify(data, null, 2)}
|
|
8142
8455
|
`);
|
|
8143
|
-
if (writeErr) return {
|
|
8144
|
-
return {
|
|
8456
|
+
if (writeErr) return { outcome: "warn", message: writeErr };
|
|
8457
|
+
return { outcome: "created", path };
|
|
8145
8458
|
}
|
|
8146
8459
|
async function backupOriginal(raw) {
|
|
8147
8460
|
const backupRoot = harnessedSubdir("backups");
|
|
@@ -8166,11 +8479,34 @@ async function atomicWrite(path, content) {
|
|
|
8166
8479
|
}
|
|
8167
8480
|
}
|
|
8168
8481
|
|
|
8169
|
-
// src/cli/lib/
|
|
8170
|
-
|
|
8171
|
-
function
|
|
8172
|
-
|
|
8482
|
+
// src/cli/lib/enableAgentTeamsInSettings.ts
|
|
8483
|
+
var ENV_KEY = "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS";
|
|
8484
|
+
async function enableAgentTeamsInSettings() {
|
|
8485
|
+
const platform = detectPlatform();
|
|
8486
|
+
if (!platform.supportsEnvKeyWrite) {
|
|
8487
|
+
return {
|
|
8488
|
+
status: "warn",
|
|
8489
|
+
message: `platform '${platform.id}' does not support env-key settings writes (capability-absent) \u2014 ${ENV_KEY} skipped`
|
|
8490
|
+
};
|
|
8491
|
+
}
|
|
8492
|
+
const r = await mergeSettingsEnvKey(ENV_KEY, "1", {
|
|
8493
|
+
skipIfPresent: (existing) => existing === "1"
|
|
8494
|
+
});
|
|
8495
|
+
switch (r.outcome) {
|
|
8496
|
+
case "created":
|
|
8497
|
+
return { status: "created", path: r.path };
|
|
8498
|
+
case "already":
|
|
8499
|
+
return { status: "already-enabled", path: r.path };
|
|
8500
|
+
case "merged":
|
|
8501
|
+
return { status: "enabled", path: r.path, backupPath: r.backupPath };
|
|
8502
|
+
case "warn":
|
|
8503
|
+
return { status: "warn", message: r.message };
|
|
8504
|
+
}
|
|
8173
8505
|
}
|
|
8506
|
+
|
|
8507
|
+
// src/cli/lib/enableUserLangInSettings.ts
|
|
8508
|
+
init_platform();
|
|
8509
|
+
var ENV_KEY2 = "HARNESSED_USER_LANG";
|
|
8174
8510
|
function detectUserLang(override) {
|
|
8175
8511
|
if (override) {
|
|
8176
8512
|
if (/^zh([^a-z]|$)/i.test(override)) return "zh-Hans";
|
|
@@ -8188,115 +8524,104 @@ function safeIntlLocale2() {
|
|
|
8188
8524
|
}
|
|
8189
8525
|
}
|
|
8190
8526
|
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}` };
|
|
8527
|
+
const platform = detectPlatform();
|
|
8528
|
+
if (!platform.supportsEnvKeyWrite) {
|
|
8529
|
+
return {
|
|
8530
|
+
status: "warn",
|
|
8531
|
+
message: `platform '${platform.id}' does not support env-key settings writes (capability-absent) \u2014 ${ENV_KEY2} skipped`
|
|
8532
|
+
};
|
|
8248
8533
|
}
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
|
|
8257
|
-
|
|
8534
|
+
const detected = detectUserLang(override);
|
|
8535
|
+
const r = await mergeSettingsEnvKey(ENV_KEY2, detected, {
|
|
8536
|
+
skipIfPresent: () => override === void 0
|
|
8537
|
+
});
|
|
8538
|
+
switch (r.outcome) {
|
|
8539
|
+
case "created":
|
|
8540
|
+
return { status: "created", path: r.path, detected };
|
|
8541
|
+
case "already":
|
|
8542
|
+
return { status: "already-set", path: r.path, existing: r.existing };
|
|
8543
|
+
case "merged":
|
|
8544
|
+
return { status: "enabled", path: r.path, backupPath: r.backupPath, detected };
|
|
8545
|
+
case "warn":
|
|
8546
|
+
return { status: "warn", message: r.message };
|
|
8258
8547
|
}
|
|
8259
8548
|
}
|
|
8260
8549
|
|
|
8261
8550
|
// src/cli/setup.ts
|
|
8262
8551
|
init_generateCommands();
|
|
8263
8552
|
init_packagePath();
|
|
8553
|
+
|
|
8554
|
+
// src/cli/lib/renderSkillTemplates.ts
|
|
8555
|
+
init_i18n();
|
|
8556
|
+
|
|
8557
|
+
// src/cli/lib/resolveSkillBody.ts
|
|
8558
|
+
init_i18n();
|
|
8559
|
+
function skillBodyFilename(locale) {
|
|
8560
|
+
return locale === "en" ? "SKILL.md" : `SKILL.${locale}.md`;
|
|
8561
|
+
}
|
|
8562
|
+
function resolveSkillBodyFilename(skillDir, locale) {
|
|
8563
|
+
const resolved = locale ?? getLocale();
|
|
8564
|
+
const candidate = skillBodyFilename(resolved);
|
|
8565
|
+
if (candidate === "SKILL.md") return "SKILL.md";
|
|
8566
|
+
return existsSync(join(skillDir, candidate)) ? candidate : "SKILL.md";
|
|
8567
|
+
}
|
|
8568
|
+
|
|
8569
|
+
// src/cli/lib/renderSkillTemplates.ts
|
|
8570
|
+
var LOCALE_SIBLINGS = ["zh-Hans"];
|
|
8264
8571
|
async function loadCapabilities(workflowsDir) {
|
|
8265
8572
|
const path = join(workflowsDir, "capabilities.yaml");
|
|
8266
8573
|
const raw = await readFile(path, "utf8");
|
|
8267
8574
|
const doc = parse(raw);
|
|
8268
8575
|
return doc?.capabilities ?? {};
|
|
8269
8576
|
}
|
|
8270
|
-
async function renderSkillFile(skillName, skillsBase, capabilities, installedPlugins, installedUserSkills) {
|
|
8271
|
-
const
|
|
8577
|
+
async function renderSkillFile(skillName, skillsBase, capabilities, installedPlugins, installedUserSkills, locale) {
|
|
8578
|
+
const dir = join(skillsBase, skillName);
|
|
8579
|
+
const resolved = locale ?? getLocale();
|
|
8580
|
+
const srcName = resolveSkillBodyFilename(dir, resolved);
|
|
8581
|
+
const srcPath = join(dir, srcName);
|
|
8582
|
+
const destPath = join(dir, "SKILL.md");
|
|
8272
8583
|
const result = {
|
|
8273
8584
|
name: skillName,
|
|
8274
|
-
skillPath,
|
|
8585
|
+
skillPath: destPath,
|
|
8275
8586
|
rendered: false,
|
|
8276
8587
|
warnings: []
|
|
8277
8588
|
};
|
|
8278
8589
|
let body;
|
|
8279
8590
|
try {
|
|
8280
|
-
body = await readFile(
|
|
8591
|
+
body = await readFile(srcPath, "utf8");
|
|
8281
8592
|
} catch (e) {
|
|
8282
8593
|
result.error = `read failed: ${e.message}`;
|
|
8283
8594
|
return result;
|
|
8284
8595
|
}
|
|
8285
8596
|
const rendered = renderSkillBody(body, capabilities, installedPlugins, installedUserSkills);
|
|
8286
|
-
|
|
8597
|
+
const localeBodySelected = srcName !== "SKILL.md";
|
|
8598
|
+
const needsWrite = localeBodySelected || rendered.body !== body;
|
|
8599
|
+
if (!needsWrite) {
|
|
8287
8600
|
result.warnings = rendered.warnings;
|
|
8288
8601
|
return result;
|
|
8289
8602
|
}
|
|
8290
8603
|
try {
|
|
8291
|
-
await writeFile(
|
|
8292
|
-
result.rendered =
|
|
8604
|
+
await writeFile(destPath, rendered.body, "utf8");
|
|
8605
|
+
result.rendered = rendered.body !== body;
|
|
8293
8606
|
result.warnings = rendered.warnings;
|
|
8294
8607
|
} catch (e) {
|
|
8295
8608
|
result.error = `write failed: ${e.message}`;
|
|
8609
|
+
return result;
|
|
8610
|
+
}
|
|
8611
|
+
if (localeBodySelected) {
|
|
8612
|
+
for (const loc of LOCALE_SIBLINGS) {
|
|
8613
|
+
const sibling = skillBodyFilename(loc);
|
|
8614
|
+
if (sibling === "SKILL.md") continue;
|
|
8615
|
+
try {
|
|
8616
|
+
await rm(join(dir, sibling), { force: true });
|
|
8617
|
+
} catch {
|
|
8618
|
+
}
|
|
8619
|
+
}
|
|
8296
8620
|
}
|
|
8297
8621
|
return result;
|
|
8298
8622
|
}
|
|
8299
|
-
async function renderAllSkills(skillNames, skillsBase, workflowsDir, homedirOverride) {
|
|
8623
|
+
async function renderAllSkills(skillNames, skillsBase, workflowsDir, homedirOverride, locale) {
|
|
8624
|
+
const resolvedLocale = locale ?? getLocale();
|
|
8300
8625
|
let capabilities = {};
|
|
8301
8626
|
try {
|
|
8302
8627
|
capabilities = await loadCapabilities(workflowsDir);
|
|
@@ -8314,8 +8639,8 @@ async function renderAllSkills(skillNames, skillsBase, workflowsDir, homedirOver
|
|
|
8314
8639
|
]
|
|
8315
8640
|
};
|
|
8316
8641
|
}
|
|
8317
|
-
const installedPlugins = readInstalledPlugins();
|
|
8318
|
-
const installedUserSkills = readInstalledUserSkills();
|
|
8642
|
+
const installedPlugins = readInstalledPlugins(homedirOverride);
|
|
8643
|
+
const installedUserSkills = readInstalledUserSkills(homedirOverride);
|
|
8319
8644
|
const results = [];
|
|
8320
8645
|
const warningSet = /* @__PURE__ */ new Set();
|
|
8321
8646
|
for (const name of skillNames) {
|
|
@@ -8324,7 +8649,8 @@ async function renderAllSkills(skillNames, skillsBase, workflowsDir, homedirOver
|
|
|
8324
8649
|
skillsBase,
|
|
8325
8650
|
capabilities,
|
|
8326
8651
|
installedPlugins,
|
|
8327
|
-
installedUserSkills
|
|
8652
|
+
installedUserSkills,
|
|
8653
|
+
resolvedLocale
|
|
8328
8654
|
);
|
|
8329
8655
|
results.push(r);
|
|
8330
8656
|
for (const w of r.warnings) warningSet.add(w);
|
|
@@ -8478,6 +8804,23 @@ async function runStepBInstall(manifestPaths, runOpts = {}) {
|
|
|
8478
8804
|
}
|
|
8479
8805
|
|
|
8480
8806
|
// src/cli/setup.ts
|
|
8807
|
+
var KNOWN_PLATFORMS = ["claude", "codex"];
|
|
8808
|
+
async function applyPlatformOption(platform) {
|
|
8809
|
+
if (!KNOWN_PLATFORMS.includes(platform)) {
|
|
8810
|
+
console.error(
|
|
8811
|
+
`--platform: unknown id '${platform}' (expected one of: ${KNOWN_PLATFORMS.join(" | ")})`
|
|
8812
|
+
);
|
|
8813
|
+
process.exit(1);
|
|
8814
|
+
}
|
|
8815
|
+
process.env.HARNESSED_PLATFORM = platform;
|
|
8816
|
+
const stateRoot = detectPlatform().stateRoot;
|
|
8817
|
+
try {
|
|
8818
|
+
await mkdir(stateRoot, { recursive: true });
|
|
8819
|
+
await writeFile(join(stateRoot, ".platform"), platform, "utf8");
|
|
8820
|
+
} catch (e) {
|
|
8821
|
+
console.warn(` [--platform] could not persist .platform pin (${e.message})`);
|
|
8822
|
+
}
|
|
8823
|
+
}
|
|
8481
8824
|
async function listBaseManifests2(pkgRoot) {
|
|
8482
8825
|
const out = [];
|
|
8483
8826
|
for (const d of ["manifests/tools", "manifests/skill-packs"]) {
|
|
@@ -8530,14 +8873,15 @@ function registerSetup(program2) {
|
|
|
8530
8873
|
).option("--dry-run", "preview only \u2014 do not write to disk (opt-in for advanced users)").option(
|
|
8531
8874
|
"--user-lang <code>",
|
|
8532
8875
|
"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(
|
|
8876
|
+
).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
8877
|
"--update-installed",
|
|
8535
8878
|
"force re-install already-installed plugins (excludes MCP servers); default: skip if installed"
|
|
8536
8879
|
).action(async (raw) => {
|
|
8537
8880
|
const dryRun = raw.dryRun === true;
|
|
8881
|
+
if (raw.platform !== void 0) await applyPlatformOption(raw.platform);
|
|
8538
8882
|
const pkgRoot = getPackageRoot();
|
|
8539
8883
|
const workflowsDir = resolve(pkgRoot, "workflows");
|
|
8540
|
-
const skillsBase =
|
|
8884
|
+
const skillsBase = getSkillsDir();
|
|
8541
8885
|
await warnIfAgentTeamsMissing();
|
|
8542
8886
|
let entries;
|
|
8543
8887
|
try {
|
|
@@ -8574,14 +8918,20 @@ function registerSetup(program2) {
|
|
|
8574
8918
|
}
|
|
8575
8919
|
console.log(t("setup.step_a_complete", { count: skillsInstalled, path: skillsBase }));
|
|
8576
8920
|
const skillNames = toInstall.map((wf) => wf.name);
|
|
8577
|
-
const rendered = await renderAllSkills(
|
|
8921
|
+
const rendered = await renderAllSkills(
|
|
8922
|
+
skillNames,
|
|
8923
|
+
skillsBase,
|
|
8924
|
+
workflowsDir,
|
|
8925
|
+
void 0,
|
|
8926
|
+
getLocale()
|
|
8927
|
+
);
|
|
8578
8928
|
if (rendered.aggregatedWarnings.length > 0) {
|
|
8579
8929
|
console.warn(t("setup.step_a_render.warnings_header"));
|
|
8580
8930
|
for (const w of rendered.aggregatedWarnings) {
|
|
8581
8931
|
console.warn(` - ${w}`);
|
|
8582
8932
|
}
|
|
8583
8933
|
}
|
|
8584
|
-
const commandsBase =
|
|
8934
|
+
const commandsBase = getCommandsDir();
|
|
8585
8935
|
try {
|
|
8586
8936
|
await mkdir(commandsBase, { recursive: true });
|
|
8587
8937
|
} catch (e) {
|
|
@@ -8677,115 +9027,8 @@ Force-update pass complete: ${b2.installed.length} installed / ${b2.alreadyInsta
|
|
|
8677
9027
|
});
|
|
8678
9028
|
}
|
|
8679
9029
|
|
|
8680
|
-
// src/cli/
|
|
8681
|
-
|
|
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
|
-
}
|
|
9030
|
+
// src/cli/uninstall.ts
|
|
9031
|
+
init_i18n();
|
|
8789
9032
|
init_harnessedRoot();
|
|
8790
9033
|
init_path_guard();
|
|
8791
9034
|
init_validate();
|
|
@@ -8804,10 +9047,10 @@ var uninstallCcHookAdd = async (ctx) => {
|
|
|
8804
9047
|
}
|
|
8805
9048
|
const abort = dryRunGate(ctx);
|
|
8806
9049
|
if (abort) return abort;
|
|
8807
|
-
const
|
|
9050
|
+
const settingsPath = join(homedir(), ".claude", "settings.json");
|
|
8808
9051
|
let existing;
|
|
8809
9052
|
try {
|
|
8810
|
-
existing = await readFile(
|
|
9053
|
+
existing = await readFile(settingsPath, "utf8");
|
|
8811
9054
|
} catch {
|
|
8812
9055
|
return { ok: true, removedPaths: [] };
|
|
8813
9056
|
}
|
|
@@ -8836,8 +9079,8 @@ var uninstallCcHookAdd = async (ctx) => {
|
|
|
8836
9079
|
if (settings.hooks?.[ev]?.length === before || before === settings.hooks?.[ev]?.length) ;
|
|
8837
9080
|
const newText = `${JSON.stringify(settings, null, 2)}
|
|
8838
9081
|
`;
|
|
8839
|
-
await writeFile(
|
|
8840
|
-
return { ok: true, removedPaths: [
|
|
9082
|
+
await writeFile(settingsPath, newText);
|
|
9083
|
+
return { ok: true, removedPaths: [settingsPath] };
|
|
8841
9084
|
};
|
|
8842
9085
|
|
|
8843
9086
|
// src/uninstallers/ccPluginMarketplace.ts
|
|
@@ -8976,7 +9219,7 @@ var uninstallNpmCli = async (ctx) => {
|
|
|
8976
9219
|
const m = install.cmd.match(/npm\s+(?:install|i)\s+(?:-g\s+)?(\S+)/);
|
|
8977
9220
|
const pkg = m?.[1] ?? ctx.manifest.metadata.upstream.source;
|
|
8978
9221
|
const isWin = process.platform === "win32";
|
|
8979
|
-
const result = await new Promise((
|
|
9222
|
+
const result = await new Promise((resolve18) => {
|
|
8980
9223
|
const child = isWin ? spawn("cmd.exe", ["/c", "npm", "uninstall", "-g", pkg], { windowsHide: true }) : spawn("npm", ["uninstall", "-g", pkg], { shell: false });
|
|
8981
9224
|
let stderr = "";
|
|
8982
9225
|
child.stderr?.setEncoding("utf8").on("data", (c) => {
|
|
@@ -8984,15 +9227,15 @@ var uninstallNpmCli = async (ctx) => {
|
|
|
8984
9227
|
});
|
|
8985
9228
|
const timer = setTimeout(() => {
|
|
8986
9229
|
child.kill("SIGKILL");
|
|
8987
|
-
|
|
9230
|
+
resolve18({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
8988
9231
|
}, 3e4);
|
|
8989
9232
|
child.on("error", (e) => {
|
|
8990
9233
|
clearTimeout(timer);
|
|
8991
|
-
|
|
9234
|
+
resolve18({ exitCode: -1, stderr: e.message });
|
|
8992
9235
|
});
|
|
8993
9236
|
child.on("close", (code) => {
|
|
8994
9237
|
clearTimeout(timer);
|
|
8995
|
-
|
|
9238
|
+
resolve18({ exitCode: code ?? -1, stderr });
|
|
8996
9239
|
});
|
|
8997
9240
|
});
|
|
8998
9241
|
if (result.exitCode !== 0) {
|
|
@@ -9058,9 +9301,9 @@ async function discoverCommandFiles(commandsDir) {
|
|
|
9058
9301
|
}
|
|
9059
9302
|
return owned;
|
|
9060
9303
|
}
|
|
9061
|
-
async function checkSettingsEnv(
|
|
9304
|
+
async function checkSettingsEnv(settingsPath) {
|
|
9062
9305
|
try {
|
|
9063
|
-
const raw = await readFile(
|
|
9306
|
+
const raw = await readFile(settingsPath, "utf8");
|
|
9064
9307
|
const data = JSON.parse(raw);
|
|
9065
9308
|
const env = data.env ?? {};
|
|
9066
9309
|
return {
|
|
@@ -9071,8 +9314,8 @@ async function checkSettingsEnv(settingsPath3) {
|
|
|
9071
9314
|
return { hasAgentTeams: false, hasUserLang: false };
|
|
9072
9315
|
}
|
|
9073
9316
|
}
|
|
9074
|
-
async function removeSettingsEnv(
|
|
9075
|
-
const raw = await readFile(
|
|
9317
|
+
async function removeSettingsEnv(settingsPath) {
|
|
9318
|
+
const raw = await readFile(settingsPath, "utf8");
|
|
9076
9319
|
const data = JSON.parse(raw);
|
|
9077
9320
|
const env = data.env ?? {};
|
|
9078
9321
|
let changed = false;
|
|
@@ -9087,14 +9330,14 @@ async function removeSettingsEnv(settingsPath3) {
|
|
|
9087
9330
|
if (!changed) return false;
|
|
9088
9331
|
if (Object.keys(env).length === 0) delete data.env;
|
|
9089
9332
|
else data.env = env;
|
|
9090
|
-
await writeFile(
|
|
9333
|
+
await writeFile(settingsPath, `${JSON.stringify(data, null, 2)}
|
|
9091
9334
|
`, "utf8");
|
|
9092
9335
|
return true;
|
|
9093
9336
|
}
|
|
9094
9337
|
async function runUnifiedUninstall(home, dryRun) {
|
|
9095
9338
|
const commandsDir = join(home, ".claude", "commands");
|
|
9096
9339
|
const skillsDir = join(home, ".claude", "skills");
|
|
9097
|
-
const
|
|
9340
|
+
const settingsPath = join(home, ".claude", "settings.json");
|
|
9098
9341
|
const harnessedRoot = getHarnessedRoot();
|
|
9099
9342
|
const pkgRoot = getPackageRoot();
|
|
9100
9343
|
let workflowNames = [];
|
|
@@ -9105,7 +9348,7 @@ async function runUnifiedUninstall(home, dryRun) {
|
|
|
9105
9348
|
} catch {
|
|
9106
9349
|
}
|
|
9107
9350
|
const commandFiles = await discoverCommandFiles(commandsDir);
|
|
9108
|
-
const settingsEnv = await checkSettingsEnv(
|
|
9351
|
+
const settingsEnv = await checkSettingsEnv(settingsPath);
|
|
9109
9352
|
const hasSettingsChanges = settingsEnv.hasAgentTeams || settingsEnv.hasUserLang;
|
|
9110
9353
|
const skillDirs = [];
|
|
9111
9354
|
for (const name of workflowNames) {
|
|
@@ -9163,9 +9406,9 @@ async function runUnifiedUninstall(home, dryRun) {
|
|
|
9163
9406
|
let removedSettings = false;
|
|
9164
9407
|
if (hasSettingsChanges) {
|
|
9165
9408
|
try {
|
|
9166
|
-
removedSettings = await removeSettingsEnv(
|
|
9409
|
+
removedSettings = await removeSettingsEnv(settingsPath);
|
|
9167
9410
|
} catch (e) {
|
|
9168
|
-
failures.push(`${
|
|
9411
|
+
failures.push(`${settingsPath}: ${e.message}`);
|
|
9169
9412
|
}
|
|
9170
9413
|
}
|
|
9171
9414
|
const normalizedRoot = resolve(harnessedRoot);
|
|
@@ -9362,6 +9605,7 @@ function registerWorkflows(program2) {
|
|
|
9362
9605
|
}
|
|
9363
9606
|
|
|
9364
9607
|
// src/cli.ts
|
|
9608
|
+
init_i18n();
|
|
9365
9609
|
init_harnessedRoot();
|
|
9366
9610
|
migrateLegacyHarnessedRoot();
|
|
9367
9611
|
var argv = process.argv;
|
|
@@ -9413,6 +9657,11 @@ registerLearn(program);
|
|
|
9413
9657
|
registerUpdate(program);
|
|
9414
9658
|
registerReleasePreflight(program);
|
|
9415
9659
|
registerRetro(program);
|
|
9416
|
-
|
|
9660
|
+
var bare = parseBareInvocation(process.argv.slice(2));
|
|
9661
|
+
if (bare.here) {
|
|
9662
|
+
void runHere({ json: bare.json });
|
|
9663
|
+
} else {
|
|
9664
|
+
program.parse(process.argv);
|
|
9665
|
+
}
|
|
9417
9666
|
//# sourceMappingURL=cli.mjs.map
|
|
9418
9667
|
//# sourceMappingURL=cli.mjs.map
|