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/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.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((resolve8, reject) => {
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
- resolve8(1);
1463
+ resolve10(1);
1463
1464
  } else {
1464
1465
  reject(err2);
1465
1466
  }
1466
1467
  });
1467
- child.on("close", (code) => resolve8(code ?? 0));
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((resolve8) => {
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
- resolve8({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
2906
+ resolve10({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
2906
2907
  }, timeoutMs);
2907
2908
  child.on("error", (e) => {
2908
2909
  clearTimeout(timer);
2909
- resolve8({ exitCode: -1, stderr: `${stderr}${e.message}` });
2910
+ resolve10({ exitCode: -1, stderr: `${stderr}${e.message}` });
2910
2911
  });
2911
2912
  child.on("close", (code) => {
2912
2913
  clearTimeout(timer);
2913
- resolve8({ exitCode: code ?? -1, stderr });
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((resolve8) => {
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
- resolve8({ exitCode: -1, stderr: `${stderr}[timeout]` });
3063
+ resolve10({ exitCode: -1, stderr: `${stderr}[timeout]` });
3063
3064
  }, 15e3);
3064
3065
  child.on("error", (e) => {
3065
3066
  clearTimeout(timer);
3066
- resolve8({ exitCode: -1, stderr: e.message });
3067
+ resolve10({ exitCode: -1, stderr: e.message });
3067
3068
  });
3068
3069
  child.on("close", (code) => {
3069
3070
  clearTimeout(timer);
3070
- resolve8({ exitCode: code ?? -1, stderr });
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((resolve8) => {
3127
+ return await new Promise((resolve10) => {
3127
3128
  const timer = setTimeout(() => {
3128
3129
  child.kill("SIGKILL");
3129
- resolve8({
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
- resolve8({
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
- resolve8({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
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((resolve8) => {
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
- resolve8({ sha: "", exit: -1 });
3176
+ resolve10({ sha: "", exit: -1 });
3176
3177
  }, timeoutMs);
3177
3178
  child.on("error", () => {
3178
3179
  clearTimeout(timer);
3179
- resolve8({ sha: "", exit: -1 });
3180
+ resolve10({ sha: "", exit: -1 });
3180
3181
  });
3181
3182
  child.on("close", (code) => {
3182
3183
  clearTimeout(timer);
3183
- resolve8({ sha: stdout2.trim(), exit: code ?? -1 });
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((resolve8) => {
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
- resolve8({ exitCode: -1, stderr: `${stderr}[timeout]` });
3529
+ resolve10({ exitCode: -1, stderr: `${stderr}[timeout]` });
3529
3530
  }, 15e3);
3530
3531
  child.on("error", (e) => {
3531
3532
  clearTimeout(timer);
3532
- resolve8({ exitCode: -1, stderr: e.message });
3533
+ resolve10({ exitCode: -1, stderr: e.message });
3533
3534
  });
3534
3535
  child.on("close", (code) => {
3535
3536
  clearTimeout(timer);
3536
- resolve8({ exitCode: code ?? -1, stderr });
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((resolve8) => {
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
- resolve8({ exitCode: -1, stderr: `${stderr}[timeout]` });
3674
+ resolve10({ exitCode: -1, stderr: `${stderr}[timeout]` });
3674
3675
  }, 15e3);
3675
3676
  child.on("error", (e) => {
3676
3677
  clearTimeout(timer);
3677
- resolve8({ exitCode: -1, stderr: e.message });
3678
+ resolve10({ exitCode: -1, stderr: e.message });
3678
3679
  });
3679
3680
  child.on("close", (code) => {
3680
3681
  clearTimeout(timer);
3681
- resolve8({ exitCode: code ?? -1, stderr });
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(process.cwd(), `manifests/tools/${resolvedName}.yaml`);
3978
- const skillPackPath = resolve(process.cwd(), `manifests/skill-packs/${resolvedName}.yaml`);
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(process.cwd())) {
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((resolve8) => {
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
- resolve8({ exitCode: -1, stderr: `${stderr}[timeout]` });
4577
+ resolve10({ exitCode: -1, stderr: `${stderr}[timeout]` });
4510
4578
  }, 3e4);
4511
4579
  child.on("error", (e) => {
4512
4580
  clearTimeout(timer);
4513
- resolve8({ exitCode: -1, stderr: e.message });
4581
+ resolve10({ exitCode: -1, stderr: e.message });
4514
4582
  });
4515
4583
  child.on("close", (code) => {
4516
4584
  clearTimeout(timer);
4517
- resolve8({ exitCode: code ?? -1, stderr });
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(process.cwd(), `manifests/tools/${resolvedName}.yaml`);
4580
- const skillPackPath = resolve(process.cwd(), `manifests/skill-packs/${resolvedName}.yaml`);
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