harnessed 1.0.0 → 1.0.1
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 +151 -110
- package/dist/cli.mjs +107 -38
- 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/dist/cli.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { Type } from '@sinclair/typebox';
|
|
|
6
6
|
import { Value } from '@sinclair/typebox/value';
|
|
7
7
|
import { LineCounter, parseDocument, parse, isSeq, isScalar } from 'yaml';
|
|
8
8
|
import { homedir } from 'os';
|
|
9
|
-
import { readFile, readdir, unlink, writeFile, stat, rm, access, mkdir, rename } from 'fs/promises';
|
|
9
|
+
import { readFile, readdir, unlink, writeFile, stat, rm, cp, access, mkdir, rename } from 'fs/promises';
|
|
10
10
|
import lockfile from 'proper-lockfile';
|
|
11
11
|
import { Command } from 'commander';
|
|
12
12
|
import { Ajv } from 'ajv';
|
|
@@ -16,6 +16,7 @@ import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
|
16
16
|
import * as p from '@clack/prompts';
|
|
17
17
|
import { createPatch } from 'diff';
|
|
18
18
|
import pc from 'picocolors';
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
19
20
|
import { stdout, stdin } from 'process';
|
|
20
21
|
import * as readline from 'readline/promises';
|
|
21
22
|
|
|
@@ -669,7 +670,7 @@ var init_resume = __esm({
|
|
|
669
670
|
|
|
670
671
|
// package.json
|
|
671
672
|
var package_default = {
|
|
672
|
-
version: "1.0.
|
|
673
|
+
version: "1.0.1"};
|
|
673
674
|
|
|
674
675
|
// src/manifest/errors.ts
|
|
675
676
|
function instancePathToKeyPath(instancePath) {
|
|
@@ -1450,7 +1451,7 @@ function renderHumanTable(records) {
|
|
|
1450
1451
|
}
|
|
1451
1452
|
}
|
|
1452
1453
|
function pipeToJq(filterExpr, lines) {
|
|
1453
|
-
return new Promise((
|
|
1454
|
+
return new Promise((resolve10, reject) => {
|
|
1454
1455
|
const child = spawn("jq", [filterExpr], {
|
|
1455
1456
|
stdio: ["pipe", "inherit", "inherit"],
|
|
1456
1457
|
windowsHide: true
|
|
@@ -1459,12 +1460,12 @@ function pipeToJq(filterExpr, lines) {
|
|
|
1459
1460
|
const e = err2;
|
|
1460
1461
|
if (e.code === "ENOENT") {
|
|
1461
1462
|
console.error("\u2717 jq not found in PATH \u2014 run: harnessed doctor");
|
|
1462
|
-
|
|
1463
|
+
resolve10(1);
|
|
1463
1464
|
} else {
|
|
1464
1465
|
reject(err2);
|
|
1465
1466
|
}
|
|
1466
1467
|
});
|
|
1467
|
-
child.on("close", (code) =>
|
|
1468
|
+
child.on("close", (code) => resolve10(code ?? 0));
|
|
1468
1469
|
child.stdin.write(lines.join("\n"));
|
|
1469
1470
|
child.stdin.end();
|
|
1470
1471
|
});
|
|
@@ -2893,7 +2894,7 @@ var installCcHookAdd = async (ctx) => {
|
|
|
2893
2894
|
return { ok: true, backupId: bk.backupId, appliedFiles: [settingsPath] };
|
|
2894
2895
|
};
|
|
2895
2896
|
function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
|
|
2896
|
-
return new Promise((
|
|
2897
|
+
return new Promise((resolve10) => {
|
|
2897
2898
|
const isWin = process.platform === "win32";
|
|
2898
2899
|
const child = isWin ? spawn("cmd.exe", ["/c", "claude", ...claudeArgs], { cwd, windowsHide: true }) : spawn("claude", claudeArgs, { cwd, shell: false });
|
|
2899
2900
|
let stderr = "";
|
|
@@ -2902,15 +2903,15 @@ function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
|
|
|
2902
2903
|
});
|
|
2903
2904
|
const timer = setTimeout(() => {
|
|
2904
2905
|
child.kill("SIGKILL");
|
|
2905
|
-
|
|
2906
|
+
resolve10({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
|
|
2906
2907
|
}, timeoutMs);
|
|
2907
2908
|
child.on("error", (e) => {
|
|
2908
2909
|
clearTimeout(timer);
|
|
2909
|
-
|
|
2910
|
+
resolve10({ exitCode: -1, stderr: `${stderr}${e.message}` });
|
|
2910
2911
|
});
|
|
2911
2912
|
child.on("close", (code) => {
|
|
2912
2913
|
clearTimeout(timer);
|
|
2913
|
-
|
|
2914
|
+
resolve10({ exitCode: code ?? -1, stderr });
|
|
2914
2915
|
});
|
|
2915
2916
|
});
|
|
2916
2917
|
}
|
|
@@ -3051,7 +3052,7 @@ ${newEntry}
|
|
|
3051
3052
|
)
|
|
3052
3053
|
};
|
|
3053
3054
|
}
|
|
3054
|
-
const vr = await new Promise((
|
|
3055
|
+
const vr = await new Promise((resolve10) => {
|
|
3055
3056
|
const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
|
|
3056
3057
|
let stderr = "";
|
|
3057
3058
|
child.stderr?.setEncoding("utf8").on("data", (c) => {
|
|
@@ -3059,15 +3060,15 @@ ${newEntry}
|
|
|
3059
3060
|
});
|
|
3060
3061
|
const timer = setTimeout(() => {
|
|
3061
3062
|
child.kill("SIGKILL");
|
|
3062
|
-
|
|
3063
|
+
resolve10({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
3063
3064
|
}, 15e3);
|
|
3064
3065
|
child.on("error", (e) => {
|
|
3065
3066
|
clearTimeout(timer);
|
|
3066
|
-
|
|
3067
|
+
resolve10({ exitCode: -1, stderr: e.message });
|
|
3067
3068
|
});
|
|
3068
3069
|
child.on("close", (code) => {
|
|
3069
3070
|
clearTimeout(timer);
|
|
3070
|
-
|
|
3071
|
+
resolve10({ exitCode: code ?? -1, stderr });
|
|
3071
3072
|
});
|
|
3072
3073
|
});
|
|
3073
3074
|
if (vr.exitCode !== 0) {
|
|
@@ -3123,10 +3124,10 @@ async function spawnCmd(ctx, cmd, args) {
|
|
|
3123
3124
|
child.stderr?.setEncoding("utf8").on("data", (chunk) => {
|
|
3124
3125
|
stderr += chunk;
|
|
3125
3126
|
});
|
|
3126
|
-
return await new Promise((
|
|
3127
|
+
return await new Promise((resolve10) => {
|
|
3127
3128
|
const timer = setTimeout(() => {
|
|
3128
3129
|
child.kill("SIGKILL");
|
|
3129
|
-
|
|
3130
|
+
resolve10({
|
|
3130
3131
|
ok: false,
|
|
3131
3132
|
phase: "spawn",
|
|
3132
3133
|
error: {
|
|
@@ -3141,7 +3142,7 @@ async function spawnCmd(ctx, cmd, args) {
|
|
|
3141
3142
|
}, timeoutMs);
|
|
3142
3143
|
child.on("error", (err2) => {
|
|
3143
3144
|
clearTimeout(timer);
|
|
3144
|
-
|
|
3145
|
+
resolve10({
|
|
3145
3146
|
ok: false,
|
|
3146
3147
|
phase: "spawn",
|
|
3147
3148
|
error: {
|
|
@@ -3156,14 +3157,14 @@ async function spawnCmd(ctx, cmd, args) {
|
|
|
3156
3157
|
});
|
|
3157
3158
|
child.on("close", (code) => {
|
|
3158
3159
|
clearTimeout(timer);
|
|
3159
|
-
|
|
3160
|
+
resolve10({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
|
|
3160
3161
|
});
|
|
3161
3162
|
});
|
|
3162
3163
|
}
|
|
3163
3164
|
|
|
3164
3165
|
// src/installers/gitCloneWithSetup.ts
|
|
3165
3166
|
function gitRevParseHead(cwd, timeoutMs = 1e4) {
|
|
3166
|
-
return new Promise((
|
|
3167
|
+
return new Promise((resolve10) => {
|
|
3167
3168
|
const isWin = process.platform === "win32";
|
|
3168
3169
|
const child = isWin ? spawn("cmd.exe", ["/c", "git", "rev-parse", "HEAD"], { cwd, windowsHide: true }) : spawn("git", ["rev-parse", "HEAD"], { cwd, shell: false });
|
|
3169
3170
|
let stdout2 = "";
|
|
@@ -3172,15 +3173,15 @@ function gitRevParseHead(cwd, timeoutMs = 1e4) {
|
|
|
3172
3173
|
});
|
|
3173
3174
|
const timer = setTimeout(() => {
|
|
3174
3175
|
child.kill("SIGKILL");
|
|
3175
|
-
|
|
3176
|
+
resolve10({ sha: "", exit: -1 });
|
|
3176
3177
|
}, timeoutMs);
|
|
3177
3178
|
child.on("error", () => {
|
|
3178
3179
|
clearTimeout(timer);
|
|
3179
|
-
|
|
3180
|
+
resolve10({ sha: "", exit: -1 });
|
|
3180
3181
|
});
|
|
3181
3182
|
child.on("close", (code) => {
|
|
3182
3183
|
clearTimeout(timer);
|
|
3183
|
-
|
|
3184
|
+
resolve10({ sha: stdout2.trim(), exit: code ?? -1 });
|
|
3184
3185
|
});
|
|
3185
3186
|
});
|
|
3186
3187
|
}
|
|
@@ -3517,7 +3518,7 @@ ${newEntry}
|
|
|
3517
3518
|
)
|
|
3518
3519
|
};
|
|
3519
3520
|
}
|
|
3520
|
-
const vr = await new Promise((
|
|
3521
|
+
const vr = await new Promise((resolve10) => {
|
|
3521
3522
|
const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
|
|
3522
3523
|
let stderr = "";
|
|
3523
3524
|
child.stderr?.setEncoding("utf8").on("data", (c) => {
|
|
@@ -3525,15 +3526,15 @@ ${newEntry}
|
|
|
3525
3526
|
});
|
|
3526
3527
|
const timer = setTimeout(() => {
|
|
3527
3528
|
child.kill("SIGKILL");
|
|
3528
|
-
|
|
3529
|
+
resolve10({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
3529
3530
|
}, 15e3);
|
|
3530
3531
|
child.on("error", (e) => {
|
|
3531
3532
|
clearTimeout(timer);
|
|
3532
|
-
|
|
3533
|
+
resolve10({ exitCode: -1, stderr: e.message });
|
|
3533
3534
|
});
|
|
3534
3535
|
child.on("close", (code) => {
|
|
3535
3536
|
clearTimeout(timer);
|
|
3536
|
-
|
|
3537
|
+
resolve10({ exitCode: code ?? -1, stderr });
|
|
3537
3538
|
});
|
|
3538
3539
|
});
|
|
3539
3540
|
if (vr.exitCode !== 0) {
|
|
@@ -3662,7 +3663,7 @@ ${newEntry}
|
|
|
3662
3663
|
)
|
|
3663
3664
|
};
|
|
3664
3665
|
}
|
|
3665
|
-
const vr = await new Promise((
|
|
3666
|
+
const vr = await new Promise((resolve10) => {
|
|
3666
3667
|
const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
|
|
3667
3668
|
let stderr = "";
|
|
3668
3669
|
child.stderr?.setEncoding("utf8").on("data", (c) => {
|
|
@@ -3670,15 +3671,15 @@ ${newEntry}
|
|
|
3670
3671
|
});
|
|
3671
3672
|
const timer = setTimeout(() => {
|
|
3672
3673
|
child.kill("SIGKILL");
|
|
3673
|
-
|
|
3674
|
+
resolve10({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
3674
3675
|
}, 15e3);
|
|
3675
3676
|
child.on("error", (e) => {
|
|
3676
3677
|
clearTimeout(timer);
|
|
3677
|
-
|
|
3678
|
+
resolve10({ exitCode: -1, stderr: e.message });
|
|
3678
3679
|
});
|
|
3679
3680
|
child.on("close", (code) => {
|
|
3680
3681
|
clearTimeout(timer);
|
|
3681
|
-
|
|
3682
|
+
resolve10({ exitCode: code ?? -1, stderr });
|
|
3682
3683
|
});
|
|
3683
3684
|
});
|
|
3684
3685
|
if (vr.exitCode !== 0) {
|
|
@@ -3957,6 +3958,16 @@ async function runInstall(manifest, opts) {
|
|
|
3957
3958
|
|
|
3958
3959
|
// src/cli/install.ts
|
|
3959
3960
|
init_path_guard();
|
|
3961
|
+
function getPackageRoot() {
|
|
3962
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
3963
|
+
const thisDir = dirname(thisFile);
|
|
3964
|
+
if (thisDir.endsWith("dist") || thisDir.replace(/\\/g, "/").endsWith("/dist")) {
|
|
3965
|
+
return resolve(thisDir, "..");
|
|
3966
|
+
}
|
|
3967
|
+
return resolve(thisDir, "..", "..", "..");
|
|
3968
|
+
}
|
|
3969
|
+
|
|
3970
|
+
// src/cli/install.ts
|
|
3960
3971
|
function formatError(e) {
|
|
3961
3972
|
const head = `error: ${e.message}`;
|
|
3962
3973
|
const where = e.path && e.path !== "/" ? `
|
|
@@ -3974,8 +3985,8 @@ function registerInstall(program2) {
|
|
|
3974
3985
|
const { resolveAlias: resolveAlias2 } = await Promise.resolve().then(() => (init_aliases(), aliases_exports));
|
|
3975
3986
|
const resolvedName = resolveAlias2(name) ?? name;
|
|
3976
3987
|
checkPathSafe(resolvedName);
|
|
3977
|
-
const manifestPath = resolve(
|
|
3978
|
-
const skillPackPath = resolve(
|
|
3988
|
+
const manifestPath = resolve(getPackageRoot(), `manifests/tools/${resolvedName}.yaml`);
|
|
3989
|
+
const skillPackPath = resolve(getPackageRoot(), `manifests/skill-packs/${resolvedName}.yaml`);
|
|
3979
3990
|
let yamlSrc;
|
|
3980
3991
|
let chosenPath = manifestPath;
|
|
3981
3992
|
try {
|
|
@@ -4059,7 +4070,7 @@ function registerInstallBase(program2) {
|
|
|
4059
4070
|
const installed = [];
|
|
4060
4071
|
const skipped = [];
|
|
4061
4072
|
const failed = [];
|
|
4062
|
-
for (const path of await listBaseManifests(
|
|
4073
|
+
for (const path of await listBaseManifests(getPackageRoot())) {
|
|
4063
4074
|
let yamlSrc;
|
|
4064
4075
|
try {
|
|
4065
4076
|
yamlSrc = await readFile(path, "utf8");
|
|
@@ -4277,6 +4288,63 @@ function registerRollback(program2) {
|
|
|
4277
4288
|
console.log(`restored ${meta.files.length} file(s) from ${timestamp}`);
|
|
4278
4289
|
});
|
|
4279
4290
|
}
|
|
4291
|
+
function registerSetup(program2) {
|
|
4292
|
+
program2.command("setup").description(
|
|
4293
|
+
"One-time onboarding: copy workflows/*/SKILL.md dirs to ~/.claude/skills/ (dry-run by default \u2014 pass --apply to execute)"
|
|
4294
|
+
).option("--apply", "execute the copy (default: dry-run preview only)").action(async (raw) => {
|
|
4295
|
+
const dryRun = raw.apply !== true;
|
|
4296
|
+
const workflowsDir = resolve(getPackageRoot(), "workflows");
|
|
4297
|
+
const skillsBase = resolve(homedir(), ".claude", "skills");
|
|
4298
|
+
let entries;
|
|
4299
|
+
try {
|
|
4300
|
+
entries = await readdir(workflowsDir);
|
|
4301
|
+
} catch {
|
|
4302
|
+
console.error(`error: workflows directory not found at ${workflowsDir}`);
|
|
4303
|
+
process.exit(1);
|
|
4304
|
+
}
|
|
4305
|
+
const toInstall = [];
|
|
4306
|
+
for (const entry of entries.sort()) {
|
|
4307
|
+
const src = join(workflowsDir, entry);
|
|
4308
|
+
try {
|
|
4309
|
+
const s = await stat(src);
|
|
4310
|
+
if (!s.isDirectory()) continue;
|
|
4311
|
+
await stat(join(src, "SKILL.md"));
|
|
4312
|
+
toInstall.push(entry);
|
|
4313
|
+
} catch {
|
|
4314
|
+
}
|
|
4315
|
+
}
|
|
4316
|
+
if (toInstall.length === 0) {
|
|
4317
|
+
console.log("setup: no workflow directories with SKILL.md found \u2014 nothing to install");
|
|
4318
|
+
process.exit(2);
|
|
4319
|
+
}
|
|
4320
|
+
if (dryRun) {
|
|
4321
|
+
console.log(
|
|
4322
|
+
`[dry-run] setup would install ${toInstall.length} workflow(s) to ${skillsBase}:`
|
|
4323
|
+
);
|
|
4324
|
+
for (const name of toInstall) {
|
|
4325
|
+
console.log(` ${name} \u2192 ${join(skillsBase, name)}`);
|
|
4326
|
+
}
|
|
4327
|
+
console.log(` run with --apply to execute`);
|
|
4328
|
+
process.exit(0);
|
|
4329
|
+
}
|
|
4330
|
+
let installed = 0;
|
|
4331
|
+
for (const name of toInstall) {
|
|
4332
|
+
const src = join(workflowsDir, name);
|
|
4333
|
+
const dst = join(skillsBase, name);
|
|
4334
|
+
try {
|
|
4335
|
+
await cp(src, dst, { recursive: true, force: true });
|
|
4336
|
+
console.log(` installed ${name} \u2192 ${dst}`);
|
|
4337
|
+
installed++;
|
|
4338
|
+
} catch (e) {
|
|
4339
|
+
console.error(` error: failed to copy ${name}: ${e.message}`);
|
|
4340
|
+
process.exit(1);
|
|
4341
|
+
}
|
|
4342
|
+
}
|
|
4343
|
+
console.log(`
|
|
4344
|
+
setup complete: ${installed} workflow(s) installed to ${skillsBase}`);
|
|
4345
|
+
process.exit(0);
|
|
4346
|
+
});
|
|
4347
|
+
}
|
|
4280
4348
|
function registerStatus(program2) {
|
|
4281
4349
|
program2.command("status").description("Show installed upstreams (from .harnessed/state.json)").action(async () => {
|
|
4282
4350
|
const state = await readState(process.cwd());
|
|
@@ -4498,7 +4566,7 @@ var uninstallNpmCli = async (ctx) => {
|
|
|
4498
4566
|
const m = install.cmd.match(/npm\s+(?:install|i)\s+(?:-g\s+)?(\S+)/);
|
|
4499
4567
|
const pkg = m?.[1] ?? ctx.manifest.metadata.upstream.source;
|
|
4500
4568
|
const isWin = process.platform === "win32";
|
|
4501
|
-
const result = await new Promise((
|
|
4569
|
+
const result = await new Promise((resolve10) => {
|
|
4502
4570
|
const child = isWin ? spawn("cmd.exe", ["/c", "npm", "uninstall", "-g", pkg], { windowsHide: true }) : spawn("npm", ["uninstall", "-g", pkg], { shell: false });
|
|
4503
4571
|
let stderr = "";
|
|
4504
4572
|
child.stderr?.setEncoding("utf8").on("data", (c) => {
|
|
@@ -4506,15 +4574,15 @@ var uninstallNpmCli = async (ctx) => {
|
|
|
4506
4574
|
});
|
|
4507
4575
|
const timer = setTimeout(() => {
|
|
4508
4576
|
child.kill("SIGKILL");
|
|
4509
|
-
|
|
4577
|
+
resolve10({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
4510
4578
|
}, 3e4);
|
|
4511
4579
|
child.on("error", (e) => {
|
|
4512
4580
|
clearTimeout(timer);
|
|
4513
|
-
|
|
4581
|
+
resolve10({ exitCode: -1, stderr: e.message });
|
|
4514
4582
|
});
|
|
4515
4583
|
child.on("close", (code) => {
|
|
4516
4584
|
clearTimeout(timer);
|
|
4517
|
-
|
|
4585
|
+
resolve10({ exitCode: code ?? -1, stderr });
|
|
4518
4586
|
});
|
|
4519
4587
|
});
|
|
4520
4588
|
if (result.exitCode !== 0) {
|
|
@@ -4576,8 +4644,8 @@ function registerUninstall(program2) {
|
|
|
4576
4644
|
const { resolveAlias: resolveAlias2 } = await Promise.resolve().then(() => (init_aliases(), aliases_exports));
|
|
4577
4645
|
const resolvedName = resolveAlias2(name) ?? name;
|
|
4578
4646
|
checkPathSafe(resolvedName);
|
|
4579
|
-
const manifestPath = resolve(
|
|
4580
|
-
const skillPackPath = resolve(
|
|
4647
|
+
const manifestPath = resolve(getPackageRoot(), `manifests/tools/${resolvedName}.yaml`);
|
|
4648
|
+
const skillPackPath = resolve(getPackageRoot(), `manifests/skill-packs/${resolvedName}.yaml`);
|
|
4581
4649
|
let yamlSrc;
|
|
4582
4650
|
let chosenPath = manifestPath;
|
|
4583
4651
|
try {
|
|
@@ -4648,6 +4716,7 @@ registerBackupList(program);
|
|
|
4648
4716
|
registerGc(program);
|
|
4649
4717
|
registerResume(program);
|
|
4650
4718
|
registerUninstall(program);
|
|
4719
|
+
registerSetup(program);
|
|
4651
4720
|
program.parse(process.argv);
|
|
4652
4721
|
//# sourceMappingURL=cli.mjs.map
|
|
4653
4722
|
//# sourceMappingURL=cli.mjs.map
|