harnessed 3.3.0 → 3.4.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 +211 -194
- package/dist/cli.mjs +417 -138
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/workflows/disciplines/language.yaml +33 -13
package/dist/cli.mjs
CHANGED
|
@@ -11,12 +11,12 @@ import lockfile from 'proper-lockfile';
|
|
|
11
11
|
import { Command } from 'commander';
|
|
12
12
|
import { Ajv } from 'ajv';
|
|
13
13
|
import * as ajvFormatsNs from 'ajv-formats';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
14
15
|
import { createHash } from 'crypto';
|
|
15
16
|
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
16
17
|
import * as p from '@clack/prompts';
|
|
17
18
|
import { createPatch } from 'diff';
|
|
18
19
|
import pc from 'picocolors';
|
|
19
|
-
import { fileURLToPath } from 'url';
|
|
20
20
|
import { stdout, stdin } from 'process';
|
|
21
21
|
import * as readline from 'readline/promises';
|
|
22
22
|
|
|
@@ -498,7 +498,7 @@ function checkTokenBudget() {
|
|
|
498
498
|
const msg = `${items.length} skill(s) total ${total} tokens (under 1% / 2000 threshold)`;
|
|
499
499
|
return { name: "token budget", status: "pass", message: msg };
|
|
500
500
|
}
|
|
501
|
-
const top = [...items].sort((a, b) => b.tokens - a.tokens).slice(0, 3).map((
|
|
501
|
+
const top = [...items].sort((a, b) => b.tokens - a.tokens).slice(0, 3).map((t2) => `${t2.name}:${t2.tokens}`).join(", ");
|
|
502
502
|
const pct = (total / CONTEXT_WINDOW_TOKENS * 100).toFixed(2);
|
|
503
503
|
const message = `${items.length} skill(s) total ${total} tokens (${pct}% of 200000) \u2014 top: ${top}`;
|
|
504
504
|
return {
|
|
@@ -847,7 +847,7 @@ var init_resume = __esm({
|
|
|
847
847
|
|
|
848
848
|
// package.json
|
|
849
849
|
var package_default = {
|
|
850
|
-
version: "3.
|
|
850
|
+
version: "3.4.0"};
|
|
851
851
|
|
|
852
852
|
// src/manifest/errors.ts
|
|
853
853
|
function instancePathToKeyPath(instancePath) {
|
|
@@ -1600,6 +1600,71 @@ audited ${yamls.length} manifest${yamls.length === 1 ? "" : "s"} \u2014 ${findin
|
|
|
1600
1600
|
process.exit(errorCount > 0 ? 1 : 0);
|
|
1601
1601
|
});
|
|
1602
1602
|
}
|
|
1603
|
+
var SUPPORTED = /* @__PURE__ */ new Set(["en", "zh-Hans"]);
|
|
1604
|
+
var currentLocale = null;
|
|
1605
|
+
var cache = {};
|
|
1606
|
+
function mapToSupported(raw) {
|
|
1607
|
+
if (!raw) return "en";
|
|
1608
|
+
if (/^zh([^a-z]|$)/i.test(raw)) return "zh-Hans";
|
|
1609
|
+
return "en";
|
|
1610
|
+
}
|
|
1611
|
+
function detectLocale() {
|
|
1612
|
+
const raw = process.env.HARNESSED_LANG || process.env.LC_ALL || process.env.LANG || process.env.LANGUAGE || safeIntlLocale();
|
|
1613
|
+
return mapToSupported(raw);
|
|
1614
|
+
}
|
|
1615
|
+
function safeIntlLocale() {
|
|
1616
|
+
try {
|
|
1617
|
+
return Intl.DateTimeFormat().resolvedOptions().locale;
|
|
1618
|
+
} catch {
|
|
1619
|
+
return void 0;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
function messagesDir() {
|
|
1623
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
1624
|
+
const candidates = [resolve(here, "..", "..", "messages"), resolve(here, "..", "messages")];
|
|
1625
|
+
for (const c of candidates) {
|
|
1626
|
+
try {
|
|
1627
|
+
readFileSync(join(c, "en.json"), "utf8");
|
|
1628
|
+
return c;
|
|
1629
|
+
} catch {
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
return resolve(process.cwd(), "messages");
|
|
1633
|
+
}
|
|
1634
|
+
function loadLocale(locale) {
|
|
1635
|
+
if (cache[locale]) return cache[locale];
|
|
1636
|
+
const path = join(messagesDir(), `${locale}.json`);
|
|
1637
|
+
try {
|
|
1638
|
+
const raw = readFileSync(path, "utf8");
|
|
1639
|
+
cache[locale] = JSON.parse(raw);
|
|
1640
|
+
} catch {
|
|
1641
|
+
cache[locale] = {};
|
|
1642
|
+
}
|
|
1643
|
+
return cache[locale];
|
|
1644
|
+
}
|
|
1645
|
+
function setLocale(locale) {
|
|
1646
|
+
if (!locale) return;
|
|
1647
|
+
const mapped = mapToSupported(locale);
|
|
1648
|
+
if (SUPPORTED.has(mapped)) currentLocale = mapped;
|
|
1649
|
+
}
|
|
1650
|
+
function getLocale() {
|
|
1651
|
+
if (currentLocale === null) currentLocale = detectLocale();
|
|
1652
|
+
return currentLocale;
|
|
1653
|
+
}
|
|
1654
|
+
function t(key, params) {
|
|
1655
|
+
const locale = getLocale();
|
|
1656
|
+
const primary = loadLocale(locale);
|
|
1657
|
+
let template = primary[key];
|
|
1658
|
+
if (template === void 0 && locale !== "en") {
|
|
1659
|
+
template = loadLocale("en")[key];
|
|
1660
|
+
}
|
|
1661
|
+
if (template === void 0) template = key;
|
|
1662
|
+
if (!params) return template;
|
|
1663
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_match, name) => {
|
|
1664
|
+
const v = params[name];
|
|
1665
|
+
return v === void 0 ? `{{${name}}}` : String(v);
|
|
1666
|
+
});
|
|
1667
|
+
}
|
|
1603
1668
|
|
|
1604
1669
|
// src/cli/audit-log.ts
|
|
1605
1670
|
init_harnessedRoot();
|
|
@@ -1633,7 +1698,7 @@ function renderHumanTable(records) {
|
|
|
1633
1698
|
}
|
|
1634
1699
|
}
|
|
1635
1700
|
function pipeToJq(filterExpr, lines) {
|
|
1636
|
-
return new Promise((
|
|
1701
|
+
return new Promise((resolve12, reject) => {
|
|
1637
1702
|
const child = spawn("jq", [filterExpr], {
|
|
1638
1703
|
stdio: ["pipe", "inherit", "inherit"],
|
|
1639
1704
|
windowsHide: true
|
|
@@ -1641,13 +1706,13 @@ function pipeToJq(filterExpr, lines) {
|
|
|
1641
1706
|
child.on("error", (err2) => {
|
|
1642
1707
|
const e = err2;
|
|
1643
1708
|
if (e.code === "ENOENT") {
|
|
1644
|
-
console.error("
|
|
1645
|
-
|
|
1709
|
+
console.error(t("audit_log.jq_missing"));
|
|
1710
|
+
resolve12(1);
|
|
1646
1711
|
} else {
|
|
1647
1712
|
reject(err2);
|
|
1648
1713
|
}
|
|
1649
1714
|
});
|
|
1650
|
-
child.on("close", (code) =>
|
|
1715
|
+
child.on("close", (code) => resolve12(code ?? 0));
|
|
1651
1716
|
child.stdin.write(lines.join("\n"));
|
|
1652
1717
|
child.stdin.end();
|
|
1653
1718
|
});
|
|
@@ -1659,23 +1724,23 @@ function registerAuditLog(program2) {
|
|
|
1659
1724
|
async (opts) => {
|
|
1660
1725
|
const tailN = opts.tail !== void 0 ? Number(opts.tail) : 50;
|
|
1661
1726
|
if (Number.isNaN(tailN) || tailN < 1) {
|
|
1662
|
-
console.error("
|
|
1727
|
+
console.error(t("audit_log.tail_invalid"));
|
|
1663
1728
|
process.exit(2);
|
|
1664
1729
|
}
|
|
1665
1730
|
const headN = opts.head !== void 0 ? Number(opts.head) : void 0;
|
|
1666
1731
|
if (headN !== void 0 && (Number.isNaN(headN) || headN < 1)) {
|
|
1667
|
-
console.error("
|
|
1732
|
+
console.error(t("audit_log.head_invalid"));
|
|
1668
1733
|
process.exit(2);
|
|
1669
1734
|
}
|
|
1670
1735
|
const path = auditPath();
|
|
1671
1736
|
if (!existsSync(path)) {
|
|
1672
|
-
console.log(
|
|
1737
|
+
console.log(t("audit_log.no_records_file", { path }));
|
|
1673
1738
|
process.exit(0);
|
|
1674
1739
|
}
|
|
1675
1740
|
const raw = readFileSync(path, "utf8");
|
|
1676
1741
|
const lines = raw.split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
|
|
1677
1742
|
if (lines.length === 0) {
|
|
1678
|
-
console.log("
|
|
1743
|
+
console.log(t("audit_log.no_records_empty"));
|
|
1679
1744
|
process.exit(0);
|
|
1680
1745
|
}
|
|
1681
1746
|
let records = [];
|
|
@@ -1832,11 +1897,11 @@ function registerBackupList(program2) {
|
|
|
1832
1897
|
try {
|
|
1833
1898
|
dirs = (await readdir(root, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
1834
1899
|
} catch {
|
|
1835
|
-
console.log(
|
|
1900
|
+
console.log(t("backup.no_backups", { root }));
|
|
1836
1901
|
return;
|
|
1837
1902
|
}
|
|
1838
1903
|
if (dirs.length === 0) {
|
|
1839
|
-
console.log(
|
|
1904
|
+
console.log(t("backup.no_backups_empty", { root }));
|
|
1840
1905
|
return;
|
|
1841
1906
|
}
|
|
1842
1907
|
for (const ts of dirs) {
|
|
@@ -1851,8 +1916,7 @@ function registerBackupList(program2) {
|
|
|
1851
1916
|
console.log(`${ts} (metadata.json missing or unreadable)`);
|
|
1852
1917
|
}
|
|
1853
1918
|
}
|
|
1854
|
-
console.log(
|
|
1855
|
-
${dirs.length} snapshot${dirs.length === 1 ? "" : "s"} total`);
|
|
1919
|
+
console.log(t("backup.total_snapshots", { count: dirs.length }));
|
|
1856
1920
|
});
|
|
1857
1921
|
}
|
|
1858
1922
|
function checkNodeVersion() {
|
|
@@ -1998,7 +2062,7 @@ function registerDoctor(program2) {
|
|
|
1998
2062
|
if (r.status !== "pass" && r.fix) console.log(` fix: ${r.fix}`);
|
|
1999
2063
|
}
|
|
2000
2064
|
console.log(
|
|
2001
|
-
hasFail ? "
|
|
2065
|
+
hasFail ? t("doctor.summary.fail") : hasWarn ? t("doctor.summary.warn") : t("doctor.summary.pass")
|
|
2002
2066
|
);
|
|
2003
2067
|
}
|
|
2004
2068
|
process.exit(hasFail ? 1 : 0);
|
|
@@ -3005,7 +3069,7 @@ function registerExecuteTask(program2) {
|
|
|
3005
3069
|
"Run execute-task workflow (4-phase chain \u2192 ralph-loop COMPLETE; immediate by default \u2014 use --dry-run for preview)"
|
|
3006
3070
|
).requiredOption("--task <text>", "task description (required)").option("--workflow <name>", "workflow name", "execute-task").option("--dry-run", "preview only \u2014 do not spawn subagent (opt-in for advanced users)").option("--non-interactive", "CI / scripts").option("--model <model>", "subagent model: 'haiku' | 'sonnet' | 'opus'").option("--model-tier <tier>", "override: 'inherit' bypasses per-phase phase.model (B-10)").option("--max-iterations <n>", "ralph-loop max iter (default 20)", (v) => parseInt(v, 10)).action(async (raw) => {
|
|
3007
3071
|
if (!raw.task) {
|
|
3008
|
-
console.error("
|
|
3072
|
+
console.error(t("execute_task.require_task"));
|
|
3009
3073
|
process.exit(2);
|
|
3010
3074
|
}
|
|
3011
3075
|
const workflowName = raw.workflow ?? "execute-task";
|
|
@@ -3014,7 +3078,10 @@ function registerExecuteTask(program2) {
|
|
|
3014
3078
|
phases = loadPhases(`workflows/${workflowName}/phases.yaml`);
|
|
3015
3079
|
} catch (error) {
|
|
3016
3080
|
console.error(
|
|
3017
|
-
|
|
3081
|
+
t("execute_task.load_phases_failed", {
|
|
3082
|
+
workflow: workflowName,
|
|
3083
|
+
message: error.message
|
|
3084
|
+
})
|
|
3018
3085
|
);
|
|
3019
3086
|
process.exit(2);
|
|
3020
3087
|
}
|
|
@@ -3053,9 +3120,7 @@ function registerExecuteTask(program2) {
|
|
|
3053
3120
|
// apply-immediate default
|
|
3054
3121
|
});
|
|
3055
3122
|
} catch (err2) {
|
|
3056
|
-
console.warn(
|
|
3057
|
-
`\u26A0\uFE0F before-commit pre-flight skipped (${err2.message}); subagent will biome-check at commit time.`
|
|
3058
|
-
);
|
|
3123
|
+
console.warn(t("execute_task.precommit_skipped", { message: err2.message }));
|
|
3059
3124
|
}
|
|
3060
3125
|
const result = await runRouting(taskCtx, {
|
|
3061
3126
|
maxIterations: raw.maxIterations ?? 20,
|
|
@@ -3064,7 +3129,7 @@ function registerExecuteTask(program2) {
|
|
|
3064
3129
|
...fallbackPhaseId ? { fallbackPhaseId } : {}
|
|
3065
3130
|
});
|
|
3066
3131
|
if ("aborted" in result) {
|
|
3067
|
-
console.error(
|
|
3132
|
+
console.error(t("install.aborted", { reason: result.reason }));
|
|
3068
3133
|
process.exit(2);
|
|
3069
3134
|
}
|
|
3070
3135
|
if ("ok" in result && result.ok === false) {
|
|
@@ -3110,8 +3175,8 @@ function registerGc(program2) {
|
|
|
3110
3175
|
const olderMs = parseDuration(opts.olderThan ?? "30d");
|
|
3111
3176
|
if (olderMs == null) {
|
|
3112
3177
|
console.error(
|
|
3113
|
-
|
|
3114
|
-
|
|
3178
|
+
`${t("gc.invalid_duration", { value: opts.olderThan ?? "" })}
|
|
3179
|
+
${t("gc.invalid_duration.fix")}`
|
|
3115
3180
|
);
|
|
3116
3181
|
process.exit(1);
|
|
3117
3182
|
return;
|
|
@@ -3122,7 +3187,7 @@ function registerGc(program2) {
|
|
|
3122
3187
|
try {
|
|
3123
3188
|
dirs = (await readdir(root, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
3124
3189
|
} catch {
|
|
3125
|
-
console.log(
|
|
3190
|
+
console.log(t("gc.no_backups", { root }));
|
|
3126
3191
|
return;
|
|
3127
3192
|
}
|
|
3128
3193
|
const cutoff = Date.now() - olderMs;
|
|
@@ -3132,8 +3197,8 @@ function registerGc(program2) {
|
|
|
3132
3197
|
if (kept.has(ts)) continue;
|
|
3133
3198
|
const path = join(root, ts);
|
|
3134
3199
|
const iso = ts.replace(/T(\d{2})-(\d{2})-(\d{2})/, "T$1:$2:$3");
|
|
3135
|
-
const
|
|
3136
|
-
if (Number.isNaN(
|
|
3200
|
+
const t2 = Date.parse(iso);
|
|
3201
|
+
if (Number.isNaN(t2) || t2 > cutoff) continue;
|
|
3137
3202
|
let manifest = "(unknown)";
|
|
3138
3203
|
try {
|
|
3139
3204
|
const meta = JSON.parse(
|
|
@@ -3146,17 +3211,19 @@ function registerGc(program2) {
|
|
|
3146
3211
|
candidates.push({ ts, path, manifest, sizeKb });
|
|
3147
3212
|
}
|
|
3148
3213
|
if (candidates.length === 0) {
|
|
3149
|
-
console.log(
|
|
3214
|
+
console.log(
|
|
3215
|
+
t("gc.no_old_snapshots", { cutoff: opts.olderThan ?? "", keptCount: kept.size })
|
|
3216
|
+
);
|
|
3150
3217
|
return;
|
|
3151
3218
|
}
|
|
3152
3219
|
const totalKb = candidates.reduce((a, c) => a + c.sizeKb, 0);
|
|
3153
|
-
const
|
|
3154
|
-
console.log(
|
|
3220
|
+
const key = dryRun ? "gc.summary_will_delete" : "gc.summary_deleting";
|
|
3221
|
+
console.log(t(key, { count: candidates.length, sizeKb: totalKb }));
|
|
3155
3222
|
for (const c of candidates) {
|
|
3156
3223
|
console.log(` ${c.ts} ${c.manifest} (${c.sizeKb} KB)`);
|
|
3157
3224
|
if (!dryRun) await rm(c.path, { recursive: true, force: true });
|
|
3158
3225
|
}
|
|
3159
|
-
if (dryRun) console.log(
|
|
3226
|
+
if (dryRun) console.log(t("gc.dry_run_rerun_hint"));
|
|
3160
3227
|
});
|
|
3161
3228
|
}
|
|
3162
3229
|
async function confirmAt(level, ctx) {
|
|
@@ -3364,10 +3431,10 @@ var installCcHookAdd = async (ctx) => {
|
|
|
3364
3431
|
const e = pre.errors[0] ?? err(ctx, "/", "preflight failed", "preflight");
|
|
3365
3432
|
return { ok: false, phase: "preflight", error: e };
|
|
3366
3433
|
}
|
|
3367
|
-
const
|
|
3434
|
+
const settingsPath3 = join(homedir(), ".claude", "settings.json");
|
|
3368
3435
|
let existing;
|
|
3369
3436
|
try {
|
|
3370
|
-
existing = await readFile(
|
|
3437
|
+
existing = await readFile(settingsPath3, "utf8");
|
|
3371
3438
|
} catch {
|
|
3372
3439
|
existing = null;
|
|
3373
3440
|
}
|
|
@@ -3398,7 +3465,7 @@ var installCcHookAdd = async (ctx) => {
|
|
|
3398
3465
|
const newText = `${JSON.stringify(settings, null, 2)}
|
|
3399
3466
|
`;
|
|
3400
3467
|
const plan = {
|
|
3401
|
-
files: [{ target:
|
|
3468
|
+
files: [{ target: settingsPath3, scope: "HOME", oldText: existing ?? "", newText }]
|
|
3402
3469
|
};
|
|
3403
3470
|
process.stdout.write(renderDiff(plan, ctx));
|
|
3404
3471
|
const conf = await confirmAt("L3", { ...ctx});
|
|
@@ -3409,10 +3476,10 @@ var installCcHookAdd = async (ctx) => {
|
|
|
3409
3476
|
if (ctx.opts.dryRun) return { aborted: true, reason: "user-cancel" };
|
|
3410
3477
|
const bk = await backup(plan, ctx);
|
|
3411
3478
|
if (!bk.ok) return { ok: false, phase: "preflight", error: bk.error };
|
|
3412
|
-
await writeFile(
|
|
3479
|
+
await writeFile(settingsPath3, newText);
|
|
3413
3480
|
let verify;
|
|
3414
3481
|
try {
|
|
3415
|
-
verify = JSON.parse(await readFile(
|
|
3482
|
+
verify = JSON.parse(await readFile(settingsPath3, "utf8"));
|
|
3416
3483
|
} catch (e) {
|
|
3417
3484
|
return {
|
|
3418
3485
|
ok: false,
|
|
@@ -3440,7 +3507,7 @@ var installCcHookAdd = async (ctx) => {
|
|
|
3440
3507
|
};
|
|
3441
3508
|
}
|
|
3442
3509
|
await updateInstalled(ctx.cwd, ctx.manifest.metadata.name, "", "");
|
|
3443
|
-
return { ok: true, backupId: bk.backupId, appliedFiles: [
|
|
3510
|
+
return { ok: true, backupId: bk.backupId, appliedFiles: [settingsPath3] };
|
|
3444
3511
|
};
|
|
3445
3512
|
function getUserClaudeJsonPath() {
|
|
3446
3513
|
return join(homedir(), ".claude.json");
|
|
@@ -3476,7 +3543,7 @@ async function isPluginRegistered(pluginName) {
|
|
|
3476
3543
|
return Object.keys(plugins).some((k) => k.split("@")[0] === pluginName);
|
|
3477
3544
|
}
|
|
3478
3545
|
function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
|
|
3479
|
-
return new Promise((
|
|
3546
|
+
return new Promise((resolve12) => {
|
|
3480
3547
|
const isWin = process.platform === "win32";
|
|
3481
3548
|
const child = isWin ? spawn("cmd.exe", ["/c", "claude", ...claudeArgs], { cwd, windowsHide: true }) : spawn("claude", claudeArgs, { cwd, shell: false });
|
|
3482
3549
|
let stderr = "";
|
|
@@ -3485,15 +3552,15 @@ function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
|
|
|
3485
3552
|
});
|
|
3486
3553
|
const timer = setTimeout(() => {
|
|
3487
3554
|
child.kill("SIGKILL");
|
|
3488
|
-
|
|
3555
|
+
resolve12({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
|
|
3489
3556
|
}, timeoutMs);
|
|
3490
3557
|
child.on("error", (e) => {
|
|
3491
3558
|
clearTimeout(timer);
|
|
3492
|
-
|
|
3559
|
+
resolve12({ exitCode: -1, stderr: `${stderr}${e.message}` });
|
|
3493
3560
|
});
|
|
3494
3561
|
child.on("close", (code) => {
|
|
3495
3562
|
clearTimeout(timer);
|
|
3496
|
-
|
|
3563
|
+
resolve12({ exitCode: code ?? -1, stderr });
|
|
3497
3564
|
});
|
|
3498
3565
|
});
|
|
3499
3566
|
}
|
|
@@ -3672,10 +3739,10 @@ async function spawnCmd(ctx, cmd, args, timeoutMs) {
|
|
|
3672
3739
|
child.stderr?.setEncoding("utf8").on("data", (chunk) => {
|
|
3673
3740
|
stderr += chunk;
|
|
3674
3741
|
});
|
|
3675
|
-
return await new Promise((
|
|
3742
|
+
return await new Promise((resolve12) => {
|
|
3676
3743
|
const timer = setTimeout(() => {
|
|
3677
3744
|
child.kill("SIGKILL");
|
|
3678
|
-
|
|
3745
|
+
resolve12({
|
|
3679
3746
|
ok: false,
|
|
3680
3747
|
phase: "spawn",
|
|
3681
3748
|
error: {
|
|
@@ -3690,7 +3757,7 @@ async function spawnCmd(ctx, cmd, args, timeoutMs) {
|
|
|
3690
3757
|
}, effectiveTimeoutMs);
|
|
3691
3758
|
child.on("error", (err2) => {
|
|
3692
3759
|
clearTimeout(timer);
|
|
3693
|
-
|
|
3760
|
+
resolve12({
|
|
3694
3761
|
ok: false,
|
|
3695
3762
|
phase: "spawn",
|
|
3696
3763
|
error: {
|
|
@@ -3705,14 +3772,14 @@ async function spawnCmd(ctx, cmd, args, timeoutMs) {
|
|
|
3705
3772
|
});
|
|
3706
3773
|
child.on("close", (code) => {
|
|
3707
3774
|
clearTimeout(timer);
|
|
3708
|
-
|
|
3775
|
+
resolve12({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
|
|
3709
3776
|
});
|
|
3710
3777
|
});
|
|
3711
3778
|
}
|
|
3712
3779
|
|
|
3713
3780
|
// src/installers/gitCloneWithSetup.ts
|
|
3714
3781
|
function gitRevParseHead(cwd, timeoutMs = 1e4) {
|
|
3715
|
-
return new Promise((
|
|
3782
|
+
return new Promise((resolve12) => {
|
|
3716
3783
|
const isWin = process.platform === "win32";
|
|
3717
3784
|
const child = isWin ? spawn("cmd.exe", ["/c", "git", "rev-parse", "HEAD"], { cwd, windowsHide: true }) : spawn("git", ["rev-parse", "HEAD"], { cwd, shell: false });
|
|
3718
3785
|
let stdout2 = "";
|
|
@@ -3721,15 +3788,15 @@ function gitRevParseHead(cwd, timeoutMs = 1e4) {
|
|
|
3721
3788
|
});
|
|
3722
3789
|
const timer = setTimeout(() => {
|
|
3723
3790
|
child.kill("SIGKILL");
|
|
3724
|
-
|
|
3791
|
+
resolve12({ sha: "", exit: -1 });
|
|
3725
3792
|
}, timeoutMs);
|
|
3726
3793
|
child.on("error", () => {
|
|
3727
3794
|
clearTimeout(timer);
|
|
3728
|
-
|
|
3795
|
+
resolve12({ sha: "", exit: -1 });
|
|
3729
3796
|
});
|
|
3730
3797
|
child.on("close", (code) => {
|
|
3731
3798
|
clearTimeout(timer);
|
|
3732
|
-
|
|
3799
|
+
resolve12({ sha: stdout2.trim(), exit: code ?? -1 });
|
|
3733
3800
|
});
|
|
3734
3801
|
});
|
|
3735
3802
|
}
|
|
@@ -3740,11 +3807,11 @@ function extractCloneTarget(cmd) {
|
|
|
3740
3807
|
const tokens = tail.split(/\s+/);
|
|
3741
3808
|
let i = 0;
|
|
3742
3809
|
while (i < tokens.length) {
|
|
3743
|
-
const
|
|
3744
|
-
if (
|
|
3745
|
-
if (
|
|
3810
|
+
const t2 = tokens[i];
|
|
3811
|
+
if (t2 === void 0 || !t2.startsWith("-")) break;
|
|
3812
|
+
if (t2 === "--depth" || t2 === "--branch" || t2 === "-b") {
|
|
3746
3813
|
i += 2;
|
|
3747
|
-
} else if (
|
|
3814
|
+
} else if (t2.includes("=")) {
|
|
3748
3815
|
i += 1;
|
|
3749
3816
|
} else {
|
|
3750
3817
|
i += 2;
|
|
@@ -4479,8 +4546,8 @@ function registerInstall(program2) {
|
|
|
4479
4546
|
chosenPath = skillPackPath;
|
|
4480
4547
|
} catch {
|
|
4481
4548
|
console.error(
|
|
4482
|
-
|
|
4483
|
-
|
|
4549
|
+
`${t("install.manifest_not_found", { name: resolvedName })}
|
|
4550
|
+
${t("install.manifest_not_found.fix", { name: resolvedName })}`
|
|
4484
4551
|
);
|
|
4485
4552
|
process.exit(1);
|
|
4486
4553
|
}
|
|
@@ -4488,7 +4555,7 @@ function registerInstall(program2) {
|
|
|
4488
4555
|
const v = validateManifestFile(yamlSrc, chosenPath);
|
|
4489
4556
|
if (!v.ok) {
|
|
4490
4557
|
for (const e of v.errors) console.error(`error: ${e.message} at ${e.path}`);
|
|
4491
|
-
console.error(
|
|
4558
|
+
console.error(t("install.audit_hint"));
|
|
4492
4559
|
process.exit(1);
|
|
4493
4560
|
}
|
|
4494
4561
|
const dryRun = raw.dryRun === true;
|
|
@@ -4510,12 +4577,14 @@ function registerInstall(program2) {
|
|
|
4510
4577
|
}
|
|
4511
4578
|
const result = await runInstall(v.manifest, opts);
|
|
4512
4579
|
if ("aborted" in result) {
|
|
4513
|
-
console.error(
|
|
4580
|
+
console.error(t("install.aborted", { reason: result.reason }));
|
|
4514
4581
|
process.exit(2);
|
|
4515
4582
|
}
|
|
4516
4583
|
if (result.ok) {
|
|
4517
4584
|
const version = v.manifest.spec.install.method === "npm-cli" && "npm_version" in v.manifest.spec.install ? v.manifest.spec.install.npm_version : "";
|
|
4518
|
-
console.log(
|
|
4585
|
+
console.log(
|
|
4586
|
+
version ? t("install.success_with_version", { name: v.manifest.metadata.name, version }) : t("install.success", { name: v.manifest.metadata.name })
|
|
4587
|
+
);
|
|
4519
4588
|
process.exit(0);
|
|
4520
4589
|
}
|
|
4521
4590
|
console.error(formatError(result.error));
|
|
@@ -4613,10 +4682,8 @@ function registerManifestAdd(program2) {
|
|
|
4613
4682
|
const category = raw.category ?? "skill-packs";
|
|
4614
4683
|
const outPath = `manifests/${category}/${name}.ee5-answers.json`;
|
|
4615
4684
|
if (raw.nonInteractive) {
|
|
4616
|
-
console.warn(
|
|
4617
|
-
|
|
4618
|
-
);
|
|
4619
|
-
console.log(`[manifest-add] dry-run preview for upstream: ${upstream} \u2192 ${outPath}`);
|
|
4685
|
+
console.warn(t("manifest_add.non_interactive_warn"));
|
|
4686
|
+
console.log(t("manifest_add.dry_run_preview", { upstream, path: outPath }));
|
|
4620
4687
|
process.exit(0);
|
|
4621
4688
|
}
|
|
4622
4689
|
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
@@ -4629,7 +4696,7 @@ function registerManifestAdd(program2) {
|
|
|
4629
4696
|
const a = (await rl.question(`${q}
|
|
4630
4697
|
> `)).trim();
|
|
4631
4698
|
if (!a) {
|
|
4632
|
-
console.error("
|
|
4699
|
+
console.error(t("manifest_add.empty_answer"));
|
|
4633
4700
|
rl.close();
|
|
4634
4701
|
process.exit(1);
|
|
4635
4702
|
}
|
|
@@ -4640,9 +4707,9 @@ function registerManifestAdd(program2) {
|
|
|
4640
4707
|
if (!dryRun) {
|
|
4641
4708
|
writeFileSync(outPath, `${JSON.stringify(payload, null, 2)}
|
|
4642
4709
|
`, "utf8");
|
|
4643
|
-
console.log(
|
|
4710
|
+
console.log(t("manifest_add.gate_passed_wrote", { path: outPath }));
|
|
4644
4711
|
} else {
|
|
4645
|
-
console.log(
|
|
4712
|
+
console.log(t("manifest_add.gate_passed_dry_run", { path: outPath }));
|
|
4646
4713
|
console.log(JSON.stringify(payload, null, 2));
|
|
4647
4714
|
}
|
|
4648
4715
|
process.exit(0);
|
|
@@ -4655,7 +4722,7 @@ function registerResearch(program2) {
|
|
|
4655
4722
|
"Run research workflow (search category sub-routing \u2192 spawn \u2192 verbatim COMPLETE; immediate by default \u2014 use --dry-run for preview)"
|
|
4656
4723
|
).requiredOption("--query <text>", "research prompt (required)").option("--dry-run", "preview only \u2014 do not spawn subagent (opt-in for advanced users)").option("--non-interactive", "skip all prompts (CI / scripts)").option("--model <model>", "subagent model: 'haiku' | 'sonnet' | 'opus'").action(async (raw) => {
|
|
4657
4724
|
if (!raw.query) {
|
|
4658
|
-
console.error("
|
|
4725
|
+
console.error(t("research.require_query"));
|
|
4659
4726
|
process.exit(2);
|
|
4660
4727
|
}
|
|
4661
4728
|
const taskCtx = { task: raw.query, task_type: "search" };
|
|
@@ -4668,31 +4735,33 @@ function registerResearch(program2) {
|
|
|
4668
4735
|
...raw.model ? { agentOpts: { modelOverride: raw.model } } : {}
|
|
4669
4736
|
});
|
|
4670
4737
|
if ("aborted" in preview) {
|
|
4671
|
-
console.error(
|
|
4738
|
+
console.error(t("install.aborted", { reason: preview.reason }));
|
|
4672
4739
|
process.exit(2);
|
|
4673
4740
|
}
|
|
4674
4741
|
if ("ok" in preview && preview.ok === false) {
|
|
4675
4742
|
console.error(`error: ${preview.phase} \u2014 ${preview.error.message}`);
|
|
4676
4743
|
process.exit(1);
|
|
4677
4744
|
}
|
|
4678
|
-
console.log(`[dry-run] matched_rule: ${preview.matchedRule?.id ?? "(fallback supervisor)"}`);
|
|
4679
|
-
console.log(`[dry-run] query: ${raw.query}`);
|
|
4680
4745
|
console.log(
|
|
4681
|
-
"
|
|
4746
|
+
t("research.dry_run.matched_rule", {
|
|
4747
|
+
rule: preview.matchedRule?.id ?? "(fallback supervisor)"
|
|
4748
|
+
})
|
|
4682
4749
|
);
|
|
4750
|
+
console.log(t("research.dry_run.query", { query: raw.query }));
|
|
4751
|
+
console.log(t("research.dry_run.run_hint"));
|
|
4683
4752
|
process.exit(0);
|
|
4684
4753
|
}
|
|
4685
4754
|
const result = await runRouting(taskCtx, {
|
|
4686
4755
|
...raw.model ? { agentOpts: { modelOverride: raw.model } } : {}
|
|
4687
4756
|
});
|
|
4688
4757
|
if ("aborted" in result) {
|
|
4689
|
-
console.error(
|
|
4758
|
+
console.error(t("install.aborted", { reason: result.reason }));
|
|
4690
4759
|
process.exit(2);
|
|
4691
4760
|
}
|
|
4692
4761
|
if ("ok" in result && result.ok === false) {
|
|
4693
4762
|
console.error(`error: ${result.phase} \u2014 ${result.error.message}`);
|
|
4694
4763
|
if (result.phase === "install") {
|
|
4695
|
-
console.error(
|
|
4764
|
+
console.error(t("research.install_fix_hint"));
|
|
4696
4765
|
}
|
|
4697
4766
|
process.exit(1);
|
|
4698
4767
|
}
|
|
@@ -4714,12 +4783,11 @@ function registerResume(program2) {
|
|
|
4714
4783
|
return;
|
|
4715
4784
|
}
|
|
4716
4785
|
if (r.status === "no-paused-phase") {
|
|
4717
|
-
console.error(
|
|
4786
|
+
console.error(t("resume.fail", { error: r.error }));
|
|
4718
4787
|
process.exit(1);
|
|
4719
4788
|
}
|
|
4720
4789
|
if (r.status === "corrupt") {
|
|
4721
|
-
console.error(
|
|
4722
|
-
path: ${r.path}`);
|
|
4790
|
+
console.error(t("resume.corrupt", { error: r.error, path: r.path }));
|
|
4723
4791
|
process.exit(1);
|
|
4724
4792
|
}
|
|
4725
4793
|
if (r.cwdWarn) console.error(r.cwdWarn);
|
|
@@ -4748,8 +4816,8 @@ function registerRollback(program2) {
|
|
|
4748
4816
|
meta = JSON.parse(await readFile(metaPath, "utf8"));
|
|
4749
4817
|
} catch (err2) {
|
|
4750
4818
|
console.error(
|
|
4751
|
-
|
|
4752
|
-
|
|
4819
|
+
`${t("rollback.metadata_unreadable", { path: metaPath, message: err2.message })}
|
|
4820
|
+
${t("rollback.metadata_unreadable.fix")}`
|
|
4753
4821
|
);
|
|
4754
4822
|
process.exit(1);
|
|
4755
4823
|
return;
|
|
@@ -4772,16 +4840,186 @@ function registerRollback(program2) {
|
|
|
4772
4840
|
const sha1 = createHash("sha1").update(buf).digest("hex");
|
|
4773
4841
|
if (sha1 !== entry.sha1) {
|
|
4774
4842
|
console.error(
|
|
4775
|
-
|
|
4843
|
+
t("rollback.checksum_mismatch", {
|
|
4844
|
+
target: entry.target,
|
|
4845
|
+
expected: entry.sha1.slice(0, 12),
|
|
4846
|
+
actual: sha1.slice(0, 12)
|
|
4847
|
+
})
|
|
4776
4848
|
);
|
|
4777
4849
|
process.exit(1);
|
|
4778
4850
|
return;
|
|
4779
4851
|
}
|
|
4780
4852
|
await writeFile(entry.target, normalizeEol(buf, entry.eol));
|
|
4781
4853
|
}
|
|
4782
|
-
console.log(
|
|
4854
|
+
console.log(t("rollback.restored", { count: meta.files.length, timestamp }));
|
|
4783
4855
|
});
|
|
4784
4856
|
}
|
|
4857
|
+
|
|
4858
|
+
// src/cli/lib/enableAgentTeamsInSettings.ts
|
|
4859
|
+
init_harnessedRoot();
|
|
4860
|
+
function settingsPath() {
|
|
4861
|
+
return resolve(homedir(), ".claude", "settings.json");
|
|
4862
|
+
}
|
|
4863
|
+
async function enableAgentTeamsInSettings() {
|
|
4864
|
+
const path = settingsPath();
|
|
4865
|
+
let raw;
|
|
4866
|
+
try {
|
|
4867
|
+
raw = await readFile(path, "utf8");
|
|
4868
|
+
} catch (err2) {
|
|
4869
|
+
const code = err2.code;
|
|
4870
|
+
if (code !== "ENOENT") {
|
|
4871
|
+
return { status: "warn", message: `read ${path} failed: ${err2.message}` };
|
|
4872
|
+
}
|
|
4873
|
+
return createFreshSettings(path);
|
|
4874
|
+
}
|
|
4875
|
+
let data;
|
|
4876
|
+
try {
|
|
4877
|
+
const parsed = JSON.parse(raw);
|
|
4878
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
4879
|
+
return { status: "warn", message: `${path} is not a JSON object` };
|
|
4880
|
+
}
|
|
4881
|
+
data = parsed;
|
|
4882
|
+
} catch (err2) {
|
|
4883
|
+
return { status: "warn", message: `${path} malformed JSON: ${err2.message}` };
|
|
4884
|
+
}
|
|
4885
|
+
const env = data.env ?? {};
|
|
4886
|
+
if (env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS === "1") {
|
|
4887
|
+
return { status: "already-enabled", path };
|
|
4888
|
+
}
|
|
4889
|
+
const backupPath = await backupOriginal(raw);
|
|
4890
|
+
if (backupPath.status === "warn") return backupPath;
|
|
4891
|
+
data.env = { ...env, CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1" };
|
|
4892
|
+
const writeErr = await atomicWrite(path, `${JSON.stringify(data, null, 2)}
|
|
4893
|
+
`);
|
|
4894
|
+
if (writeErr) return { status: "warn", message: writeErr };
|
|
4895
|
+
return { status: "enabled", path, backupPath: backupPath.path };
|
|
4896
|
+
}
|
|
4897
|
+
async function createFreshSettings(path) {
|
|
4898
|
+
const data = { env: { CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1" } };
|
|
4899
|
+
try {
|
|
4900
|
+
await mkdir(join(homedir(), ".claude"), { recursive: true });
|
|
4901
|
+
} catch (err2) {
|
|
4902
|
+
return { status: "warn", message: `mkdir ~/.claude failed: ${err2.message}` };
|
|
4903
|
+
}
|
|
4904
|
+
const writeErr = await atomicWrite(path, `${JSON.stringify(data, null, 2)}
|
|
4905
|
+
`);
|
|
4906
|
+
if (writeErr) return { status: "warn", message: writeErr };
|
|
4907
|
+
return { status: "created", path };
|
|
4908
|
+
}
|
|
4909
|
+
async function backupOriginal(raw) {
|
|
4910
|
+
const backupRoot = harnessedSubdir("backups");
|
|
4911
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-");
|
|
4912
|
+
const backupPath = join(backupRoot, `settings.json.${ts}.bak`);
|
|
4913
|
+
try {
|
|
4914
|
+
await mkdir(backupRoot, { recursive: true });
|
|
4915
|
+
await writeFile(backupPath, raw, "utf8");
|
|
4916
|
+
return { status: "ok", path: backupPath };
|
|
4917
|
+
} catch (err2) {
|
|
4918
|
+
return { status: "warn", message: `backup ${backupPath} failed: ${err2.message}` };
|
|
4919
|
+
}
|
|
4920
|
+
}
|
|
4921
|
+
async function atomicWrite(path, content) {
|
|
4922
|
+
const tmpPath = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
4923
|
+
try {
|
|
4924
|
+
await writeFile(tmpPath, content, "utf8");
|
|
4925
|
+
await rename(tmpPath, path);
|
|
4926
|
+
return void 0;
|
|
4927
|
+
} catch (err2) {
|
|
4928
|
+
return `write ${path} failed: ${err2.message}`;
|
|
4929
|
+
}
|
|
4930
|
+
}
|
|
4931
|
+
|
|
4932
|
+
// src/cli/lib/enableUserLangInSettings.ts
|
|
4933
|
+
init_harnessedRoot();
|
|
4934
|
+
function settingsPath2() {
|
|
4935
|
+
return resolve(homedir(), ".claude", "settings.json");
|
|
4936
|
+
}
|
|
4937
|
+
function detectUserLang(override) {
|
|
4938
|
+
if (override) {
|
|
4939
|
+
if (/^zh([^a-z]|$)/i.test(override)) return "zh-Hans";
|
|
4940
|
+
return "en";
|
|
4941
|
+
}
|
|
4942
|
+
const raw = process.env.HARNESSED_LANG || process.env.LC_ALL || process.env.LANG || process.env.LANGUAGE || safeIntlLocale2() || "";
|
|
4943
|
+
if (/^zh([^a-z]|$)/i.test(raw)) return "zh-Hans";
|
|
4944
|
+
return "en";
|
|
4945
|
+
}
|
|
4946
|
+
function safeIntlLocale2() {
|
|
4947
|
+
try {
|
|
4948
|
+
return Intl.DateTimeFormat().resolvedOptions().locale;
|
|
4949
|
+
} catch {
|
|
4950
|
+
return void 0;
|
|
4951
|
+
}
|
|
4952
|
+
}
|
|
4953
|
+
async function enableUserLangInSettings(override) {
|
|
4954
|
+
const path = settingsPath2();
|
|
4955
|
+
const detected = detectUserLang(override);
|
|
4956
|
+
let raw;
|
|
4957
|
+
try {
|
|
4958
|
+
raw = await readFile(path, "utf8");
|
|
4959
|
+
} catch (err2) {
|
|
4960
|
+
const code = err2.code;
|
|
4961
|
+
if (code !== "ENOENT") {
|
|
4962
|
+
return { status: "warn", message: `read ${path} failed: ${err2.message}` };
|
|
4963
|
+
}
|
|
4964
|
+
return createFreshSettings2(path, detected);
|
|
4965
|
+
}
|
|
4966
|
+
let data;
|
|
4967
|
+
try {
|
|
4968
|
+
const parsed = JSON.parse(raw);
|
|
4969
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
4970
|
+
return { status: "warn", message: `${path} is not a JSON object` };
|
|
4971
|
+
}
|
|
4972
|
+
data = parsed;
|
|
4973
|
+
} catch (err2) {
|
|
4974
|
+
return { status: "warn", message: `${path} malformed JSON: ${err2.message}` };
|
|
4975
|
+
}
|
|
4976
|
+
const env = data.env ?? {};
|
|
4977
|
+
const existing = env.HARNESSED_USER_LANG;
|
|
4978
|
+
if (typeof existing === "string" && existing.length > 0 && override === void 0) {
|
|
4979
|
+
return { status: "already-set", path, existing };
|
|
4980
|
+
}
|
|
4981
|
+
const backupPath = await backupOriginal2(raw);
|
|
4982
|
+
if (backupPath.status === "warn") return backupPath;
|
|
4983
|
+
data.env = { ...env, HARNESSED_USER_LANG: detected };
|
|
4984
|
+
const writeErr = await atomicWrite2(path, `${JSON.stringify(data, null, 2)}
|
|
4985
|
+
`);
|
|
4986
|
+
if (writeErr) return { status: "warn", message: writeErr };
|
|
4987
|
+
return { status: "enabled", path, backupPath: backupPath.path, detected };
|
|
4988
|
+
}
|
|
4989
|
+
async function createFreshSettings2(path, detected) {
|
|
4990
|
+
const data = { env: { HARNESSED_USER_LANG: detected } };
|
|
4991
|
+
try {
|
|
4992
|
+
await mkdir(join(homedir(), ".claude"), { recursive: true });
|
|
4993
|
+
} catch (err2) {
|
|
4994
|
+
return { status: "warn", message: `mkdir ~/.claude failed: ${err2.message}` };
|
|
4995
|
+
}
|
|
4996
|
+
const writeErr = await atomicWrite2(path, `${JSON.stringify(data, null, 2)}
|
|
4997
|
+
`);
|
|
4998
|
+
if (writeErr) return { status: "warn", message: writeErr };
|
|
4999
|
+
return { status: "created", path, detected };
|
|
5000
|
+
}
|
|
5001
|
+
async function backupOriginal2(raw) {
|
|
5002
|
+
const backupRoot = harnessedSubdir("backups");
|
|
5003
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-");
|
|
5004
|
+
const backupPath = join(backupRoot, `settings.json.${ts}.bak`);
|
|
5005
|
+
try {
|
|
5006
|
+
await mkdir(backupRoot, { recursive: true });
|
|
5007
|
+
await writeFile(backupPath, raw, "utf8");
|
|
5008
|
+
return { status: "ok", path: backupPath };
|
|
5009
|
+
} catch (err2) {
|
|
5010
|
+
return { status: "warn", message: `backup ${backupPath} failed: ${err2.message}` };
|
|
5011
|
+
}
|
|
5012
|
+
}
|
|
5013
|
+
async function atomicWrite2(path, content) {
|
|
5014
|
+
const tmpPath = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
5015
|
+
try {
|
|
5016
|
+
await writeFile(tmpPath, content, "utf8");
|
|
5017
|
+
await rename(tmpPath, path);
|
|
5018
|
+
return void 0;
|
|
5019
|
+
} catch (err2) {
|
|
5020
|
+
return `write ${path} failed: ${err2.message}`;
|
|
5021
|
+
}
|
|
5022
|
+
}
|
|
4785
5023
|
init_checkAgentTeams();
|
|
4786
5024
|
var FLAT_LEGACY_DEPRECATED = /* @__PURE__ */ new Set(["plan-feature", "execute-task", "verify-work"]);
|
|
4787
5025
|
var FLAT_LEGACY_KEEP = /* @__PURE__ */ new Set(["research", "retro", "auto"]);
|
|
@@ -4952,7 +5190,10 @@ async function listBaseManifests2(pkgRoot) {
|
|
|
4952
5190
|
function registerSetup(program2) {
|
|
4953
5191
|
program2.command("setup").description(
|
|
4954
5192
|
"One-shot onboarding: install workflow skills + base manifests to ~/.claude/ (immediate by default \u2014 use --dry-run for preview)"
|
|
4955
|
-
).option("--dry-run", "preview only \u2014 do not write to disk (opt-in for advanced users)").
|
|
5193
|
+
).option("--dry-run", "preview only \u2014 do not write to disk (opt-in for advanced users)").option(
|
|
5194
|
+
"--user-lang <code>",
|
|
5195
|
+
"override detected OS locale for env.HARNESSED_USER_LANG (en | zh-Hans / zh-CN / zh-TW)"
|
|
5196
|
+
).action(async (raw) => {
|
|
4956
5197
|
const dryRun = raw.dryRun === true;
|
|
4957
5198
|
const pkgRoot = getPackageRoot();
|
|
4958
5199
|
const workflowsDir = resolve(pkgRoot, "workflows");
|
|
@@ -4962,7 +5203,7 @@ function registerSetup(program2) {
|
|
|
4962
5203
|
try {
|
|
4963
5204
|
entries = await readdir(workflowsDir);
|
|
4964
5205
|
} catch {
|
|
4965
|
-
console.error(
|
|
5206
|
+
console.error(t("setup.workflows_not_found", { path: workflowsDir }));
|
|
4966
5207
|
process.exit(1);
|
|
4967
5208
|
}
|
|
4968
5209
|
const { workflows: toInstall, deprecated } = await scanWorkflowsWithSkill(
|
|
@@ -4972,18 +5213,16 @@ function registerSetup(program2) {
|
|
|
4972
5213
|
const depBlock = renderDeprecationBlock(deprecated);
|
|
4973
5214
|
if (depBlock) console.log(depBlock);
|
|
4974
5215
|
if (toInstall.length === 0) {
|
|
4975
|
-
console.log("setup
|
|
5216
|
+
console.log(t("setup.nothing_to_install"));
|
|
4976
5217
|
process.exit(2);
|
|
4977
5218
|
}
|
|
4978
5219
|
if (dryRun) {
|
|
4979
|
-
console.log(
|
|
4980
|
-
`[dry-run] setup would install ${toInstall.length} workflow(s) to ${skillsBase}:`
|
|
4981
|
-
);
|
|
5220
|
+
console.log(t("setup.dry_run.header", { count: toInstall.length, path: skillsBase }));
|
|
4982
5221
|
for (const wf of toInstall) {
|
|
4983
5222
|
const masterTag = wf.isMaster ? " (master)" : "";
|
|
4984
5223
|
console.log(` ${wf.name} \u2192 ${join(skillsBase, wf.name)}${masterTag}`);
|
|
4985
5224
|
}
|
|
4986
|
-
console.log(
|
|
5225
|
+
console.log(t("setup.dry_run.run_hint"));
|
|
4987
5226
|
process.exit(0);
|
|
4988
5227
|
}
|
|
4989
5228
|
let skillsInstalled = 0;
|
|
@@ -4996,19 +5235,53 @@ function registerSetup(program2) {
|
|
|
4996
5235
|
console.log(` [A] installed ${wf.name} \u2192 ${dst}${masterTag}`);
|
|
4997
5236
|
skillsInstalled++;
|
|
4998
5237
|
} catch (e) {
|
|
4999
|
-
console.error(
|
|
5238
|
+
console.error(t("setup.copy_failed", { name: wf.name, message: e.message }));
|
|
5000
5239
|
process.exit(1);
|
|
5001
5240
|
}
|
|
5002
5241
|
}
|
|
5003
|
-
console.log(
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5242
|
+
console.log(t("setup.step_a_complete", { count: skillsInstalled, path: skillsBase }));
|
|
5243
|
+
const cResult = await enableAgentTeamsInSettings();
|
|
5244
|
+
if (cResult.status === "created") {
|
|
5245
|
+
console.log(t("setup.step_c.created", { path: cResult.path }));
|
|
5246
|
+
} else if (cResult.status === "already-enabled") {
|
|
5247
|
+
console.log(t("setup.step_c.already_enabled", { path: cResult.path }));
|
|
5248
|
+
} else if (cResult.status === "enabled") {
|
|
5249
|
+
console.log(
|
|
5250
|
+
t("setup.step_c.enabled_backup", {
|
|
5251
|
+
path: cResult.path,
|
|
5252
|
+
backupPath: cResult.backupPath
|
|
5253
|
+
})
|
|
5254
|
+
);
|
|
5255
|
+
} else {
|
|
5256
|
+
console.warn(t("setup.step_c.skipped", { message: cResult.message }));
|
|
5257
|
+
}
|
|
5258
|
+
const dResult = await enableUserLangInSettings(raw.userLang);
|
|
5259
|
+
if (dResult.status === "created") {
|
|
5260
|
+
console.log(t("setup.step_d.created", { path: dResult.path, lang: dResult.detected }));
|
|
5261
|
+
} else if (dResult.status === "already-set") {
|
|
5262
|
+
console.log(t("setup.step_d.already_set", { path: dResult.path, lang: dResult.existing }));
|
|
5263
|
+
} else if (dResult.status === "enabled") {
|
|
5264
|
+
console.log(
|
|
5265
|
+
t("setup.step_d.enabled_backup", {
|
|
5266
|
+
path: dResult.path,
|
|
5267
|
+
lang: dResult.detected,
|
|
5268
|
+
backupPath: dResult.backupPath
|
|
5269
|
+
})
|
|
5270
|
+
);
|
|
5271
|
+
} else {
|
|
5272
|
+
console.warn(t("setup.step_d.skipped", { message: dResult.message }));
|
|
5273
|
+
}
|
|
5007
5274
|
const manifestPaths = await listBaseManifests2(pkgRoot);
|
|
5008
5275
|
const b = await runStepBInstall(manifestPaths);
|
|
5009
5276
|
const stepBMs = (b.elapsedMs / 1e3).toFixed(1);
|
|
5010
5277
|
console.log(
|
|
5011
|
-
|
|
5278
|
+
t("setup.step_b_complete", {
|
|
5279
|
+
installed: b.installed.length,
|
|
5280
|
+
already: b.alreadyInstalled.length,
|
|
5281
|
+
skipped: b.skipped.length,
|
|
5282
|
+
failed: b.failed.length,
|
|
5283
|
+
seconds: stepBMs
|
|
5284
|
+
})
|
|
5012
5285
|
);
|
|
5013
5286
|
for (const n of b.installed) console.log(` [B] installed ${n}`);
|
|
5014
5287
|
for (const n of b.alreadyInstalled)
|
|
@@ -5018,41 +5291,33 @@ Step A complete: ${skillsInstalled} workflow skill(s) installed to ${skillsBase}
|
|
|
5018
5291
|
for (const n of b.skipped) console.log(` [B] skipped ${n}`);
|
|
5019
5292
|
for (const n of b.failed) console.error(` [B] failed ${n}`);
|
|
5020
5293
|
console.log(
|
|
5021
|
-
|
|
5022
|
-
|
|
5294
|
+
t("setup.complete", {
|
|
5295
|
+
skills: skillsInstalled,
|
|
5296
|
+
manifests: b.installed.length + b.alreadyInstalled.length
|
|
5297
|
+
})
|
|
5023
5298
|
);
|
|
5024
5299
|
if (b.alreadyInstalled.length > 0 || b.installed.length > 0) {
|
|
5025
|
-
console.log(
|
|
5026
|
-
`
|
|
5027
|
-
MCP servers configured. Run \`/mcp\` in Claude Code to verify each server's connection status. If a server shows disconnected, restart Claude Code or check the MCP command spec.`
|
|
5028
|
-
);
|
|
5300
|
+
console.log(t("setup.mcp_hint"));
|
|
5029
5301
|
}
|
|
5030
|
-
console.log(
|
|
5031
|
-
|
|
5032
|
-
);
|
|
5033
|
-
console.log(
|
|
5034
|
-
" workflows in <packageRoot>/workflows/ (Pure bundled, NOT user-dir override per D-01)"
|
|
5035
|
-
);
|
|
5302
|
+
console.log(t("setup.bundled_summary"));
|
|
5303
|
+
console.log(t("setup.bundled_location"));
|
|
5036
5304
|
process.exit(0);
|
|
5037
5305
|
});
|
|
5038
5306
|
}
|
|
5039
|
-
|
|
5040
|
-
// src/cli/status.ts
|
|
5041
5307
|
init_harnessedRoot();
|
|
5042
5308
|
function registerStatus(program2) {
|
|
5043
5309
|
program2.command("status").description("Show installed upstreams (from <harnessed-root>/state.json)").action(async () => {
|
|
5044
5310
|
const state = await readState(process.cwd());
|
|
5045
5311
|
const names = Object.keys(state.installed).sort();
|
|
5046
5312
|
if (names.length === 0) {
|
|
5047
|
-
console.log(
|
|
5313
|
+
console.log(t("status.no_installs", { path: harnessedFile("state.json") }));
|
|
5048
5314
|
} else {
|
|
5049
5315
|
for (const n of names) {
|
|
5050
5316
|
const e = state.installed[n];
|
|
5051
5317
|
if (!e) continue;
|
|
5052
5318
|
console.log(`${n} @ ${e.version} (installed ${e.installedAt})`);
|
|
5053
5319
|
}
|
|
5054
|
-
console.log(
|
|
5055
|
-
${names.length} install${names.length === 1 ? "" : "s"} recorded`);
|
|
5320
|
+
console.log(t("status.summary_installs", { count: names.length }));
|
|
5056
5321
|
}
|
|
5057
5322
|
const lockPath = harnessedFile(".lock");
|
|
5058
5323
|
try {
|
|
@@ -5064,18 +5329,20 @@ ${names.length} install${names.length === 1 ? "" : "s"} recorded`);
|
|
|
5064
5329
|
const s = await stat(lockPath);
|
|
5065
5330
|
const ageMs = Date.now() - s.mtime.getTime();
|
|
5066
5331
|
const stale = ageMs > 1e4;
|
|
5067
|
-
console.log(
|
|
5068
|
-
|
|
5069
|
-
|
|
5332
|
+
console.log(
|
|
5333
|
+
t("status.lock_held", {
|
|
5334
|
+
since: s.mtime.toISOString(),
|
|
5335
|
+
staleSuffix: stale ? t("status.lock_held.stale_suffix") : ""
|
|
5336
|
+
})
|
|
5337
|
+
);
|
|
5338
|
+
console.log(t("status.lock_release_hint", { path: lockPath }));
|
|
5070
5339
|
} else {
|
|
5071
|
-
console.log("
|
|
5340
|
+
console.log(t("status.lock_free"));
|
|
5072
5341
|
}
|
|
5073
5342
|
} catch {
|
|
5074
5343
|
}
|
|
5075
5344
|
});
|
|
5076
5345
|
}
|
|
5077
|
-
|
|
5078
|
-
// src/cli/uninstall.ts
|
|
5079
5346
|
init_path_guard();
|
|
5080
5347
|
|
|
5081
5348
|
// src/uninstallers/lib/runOrPreview.ts
|
|
@@ -5092,10 +5359,10 @@ var uninstallCcHookAdd = async (ctx) => {
|
|
|
5092
5359
|
}
|
|
5093
5360
|
const abort = dryRunGate(ctx);
|
|
5094
5361
|
if (abort) return abort;
|
|
5095
|
-
const
|
|
5362
|
+
const settingsPath3 = join(homedir(), ".claude", "settings.json");
|
|
5096
5363
|
let existing;
|
|
5097
5364
|
try {
|
|
5098
|
-
existing = await readFile(
|
|
5365
|
+
existing = await readFile(settingsPath3, "utf8");
|
|
5099
5366
|
} catch {
|
|
5100
5367
|
return { ok: true, removedPaths: [] };
|
|
5101
5368
|
}
|
|
@@ -5124,8 +5391,8 @@ var uninstallCcHookAdd = async (ctx) => {
|
|
|
5124
5391
|
if (settings.hooks?.[ev]?.length === before || before === settings.hooks?.[ev]?.length) ;
|
|
5125
5392
|
const newText = `${JSON.stringify(settings, null, 2)}
|
|
5126
5393
|
`;
|
|
5127
|
-
await writeFile(
|
|
5128
|
-
return { ok: true, removedPaths: [
|
|
5394
|
+
await writeFile(settingsPath3, newText);
|
|
5395
|
+
return { ok: true, removedPaths: [settingsPath3] };
|
|
5129
5396
|
};
|
|
5130
5397
|
|
|
5131
5398
|
// src/uninstallers/ccPluginMarketplace.ts
|
|
@@ -5167,11 +5434,11 @@ function extractCloneTarget2(cmd) {
|
|
|
5167
5434
|
const tokens = tail.split(/\s+/);
|
|
5168
5435
|
let i = 0;
|
|
5169
5436
|
while (i < tokens.length) {
|
|
5170
|
-
const
|
|
5171
|
-
if (
|
|
5172
|
-
if (
|
|
5437
|
+
const t2 = tokens[i];
|
|
5438
|
+
if (t2 === void 0 || !t2.startsWith("-")) break;
|
|
5439
|
+
if (t2 === "--depth" || t2 === "--branch" || t2 === "-b") {
|
|
5173
5440
|
i += 2;
|
|
5174
|
-
} else if (
|
|
5441
|
+
} else if (t2.includes("=")) {
|
|
5175
5442
|
i += 1;
|
|
5176
5443
|
} else {
|
|
5177
5444
|
i += 2;
|
|
@@ -5261,7 +5528,7 @@ var uninstallNpmCli = async (ctx) => {
|
|
|
5261
5528
|
const m = install.cmd.match(/npm\s+(?:install|i)\s+(?:-g\s+)?(\S+)/);
|
|
5262
5529
|
const pkg = m?.[1] ?? ctx.manifest.metadata.upstream.source;
|
|
5263
5530
|
const isWin = process.platform === "win32";
|
|
5264
|
-
const result = await new Promise((
|
|
5531
|
+
const result = await new Promise((resolve12) => {
|
|
5265
5532
|
const child = isWin ? spawn("cmd.exe", ["/c", "npm", "uninstall", "-g", pkg], { windowsHide: true }) : spawn("npm", ["uninstall", "-g", pkg], { shell: false });
|
|
5266
5533
|
let stderr = "";
|
|
5267
5534
|
child.stderr?.setEncoding("utf8").on("data", (c) => {
|
|
@@ -5269,15 +5536,15 @@ var uninstallNpmCli = async (ctx) => {
|
|
|
5269
5536
|
});
|
|
5270
5537
|
const timer = setTimeout(() => {
|
|
5271
5538
|
child.kill("SIGKILL");
|
|
5272
|
-
|
|
5539
|
+
resolve12({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
5273
5540
|
}, 3e4);
|
|
5274
5541
|
child.on("error", (e) => {
|
|
5275
5542
|
clearTimeout(timer);
|
|
5276
|
-
|
|
5543
|
+
resolve12({ exitCode: -1, stderr: e.message });
|
|
5277
5544
|
});
|
|
5278
5545
|
child.on("close", (code) => {
|
|
5279
5546
|
clearTimeout(timer);
|
|
5280
|
-
|
|
5547
|
+
resolve12({ exitCode: code ?? -1, stderr });
|
|
5281
5548
|
});
|
|
5282
5549
|
});
|
|
5283
5550
|
if (result.exitCode !== 0) {
|
|
@@ -5331,8 +5598,8 @@ function registerUninstall(program2) {
|
|
|
5331
5598
|
const yes = raw.yes === true || raw.nonInteractive === true;
|
|
5332
5599
|
if (yes && raw.dryRun) {
|
|
5333
5600
|
console.error(
|
|
5334
|
-
|
|
5335
|
-
|
|
5601
|
+
`${t("uninstall.yes_dryrun_conflict")}
|
|
5602
|
+
${t("uninstall.yes_dryrun_conflict.fix", { name })}`
|
|
5336
5603
|
);
|
|
5337
5604
|
process.exit(2);
|
|
5338
5605
|
}
|
|
@@ -5351,8 +5618,8 @@ function registerUninstall(program2) {
|
|
|
5351
5618
|
chosenPath = skillPackPath;
|
|
5352
5619
|
} catch {
|
|
5353
5620
|
console.error(
|
|
5354
|
-
|
|
5355
|
-
|
|
5621
|
+
`${t("install.manifest_not_found", { name: resolvedName })}
|
|
5622
|
+
${t("install.manifest_not_found.fix", { name: resolvedName })}`
|
|
5356
5623
|
);
|
|
5357
5624
|
process.exit(1);
|
|
5358
5625
|
}
|
|
@@ -5365,28 +5632,28 @@ function registerUninstall(program2) {
|
|
|
5365
5632
|
const method = v.manifest.spec.install.method;
|
|
5366
5633
|
const dryRun = raw.dryRun === true;
|
|
5367
5634
|
if (dryRun) {
|
|
5368
|
-
console.log(
|
|
5369
|
-
console.log(
|
|
5635
|
+
console.log(t("uninstall.dry_run.preview", { name: resolvedName, method }));
|
|
5636
|
+
console.log(t("uninstall.dry_run.run_hint"));
|
|
5370
5637
|
process.exit(2);
|
|
5371
5638
|
}
|
|
5372
5639
|
if (!yes) {
|
|
5373
5640
|
const answer = await p.confirm({
|
|
5374
|
-
message:
|
|
5641
|
+
message: t("uninstall.confirm.prompt", { name: resolvedName }),
|
|
5375
5642
|
initialValue: false
|
|
5376
5643
|
});
|
|
5377
5644
|
if (p.isCancel(answer) || answer === false) {
|
|
5378
|
-
console.error(
|
|
5645
|
+
console.error(t("uninstall.cancelled"));
|
|
5379
5646
|
process.exit(2);
|
|
5380
5647
|
}
|
|
5381
5648
|
}
|
|
5382
5649
|
const opts = { apply: true, dryRun: false, yes };
|
|
5383
5650
|
const result = await runUninstall(v.manifest, opts);
|
|
5384
5651
|
if ("aborted" in result) {
|
|
5385
|
-
console.error(
|
|
5652
|
+
console.error(t("install.aborted", { reason: result.reason }));
|
|
5386
5653
|
process.exit(2);
|
|
5387
5654
|
}
|
|
5388
5655
|
if (result.ok) {
|
|
5389
|
-
console.log(
|
|
5656
|
+
console.log(t("uninstall.completed", { name: resolvedName }));
|
|
5390
5657
|
process.exit(0);
|
|
5391
5658
|
}
|
|
5392
5659
|
console.error(`error: ${result.error}`);
|
|
@@ -5397,8 +5664,20 @@ function registerUninstall(program2) {
|
|
|
5397
5664
|
// src/cli.ts
|
|
5398
5665
|
init_harnessedRoot();
|
|
5399
5666
|
migrateLegacyHarnessedRoot();
|
|
5667
|
+
var argv = process.argv;
|
|
5668
|
+
for (let i = 2; i < argv.length; i++) {
|
|
5669
|
+
const a = argv[i];
|
|
5670
|
+
if (a === "--lang" && i + 1 < argv.length) {
|
|
5671
|
+
setLocale(argv[i + 1]);
|
|
5672
|
+
break;
|
|
5673
|
+
}
|
|
5674
|
+
if (a?.startsWith("--lang=")) {
|
|
5675
|
+
setLocale(a.slice("--lang=".length));
|
|
5676
|
+
break;
|
|
5677
|
+
}
|
|
5678
|
+
}
|
|
5400
5679
|
var program = new Command();
|
|
5401
|
-
program.name("harnessed").description("AI coding harness package manager + composition orchestrator").version(package_default.version);
|
|
5680
|
+
program.name("harnessed").description("AI coding harness package manager + composition orchestrator").version(package_default.version).option("--lang <code>", "output language: en | zh (auto-detect from $LANG / Intl if absent)");
|
|
5402
5681
|
registerInstall(program);
|
|
5403
5682
|
registerInstallBase(program);
|
|
5404
5683
|
registerResearch(program);
|