harnessed 3.4.0 → 3.4.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # harnessed
2
2
 
3
- **English** | [中文](./README-cn.md)
3
+ **English** | [简体中文](./README-cn.md) | [繁體中文](./README-tw.md) | [日本語](./README-ja.md) | [한국어](./README-ko.md) | [Português (Brasil)](./README-pt-BR.md) | [Türkçe](./README-tr.md) | [Русский](./README-ru.md) | [Tiếng Việt](./README-vi.md) | [ไทย](./README-th.md)
4
4
 
5
5
  > AI coding harness package manager + composition orchestrator
6
6
  > Machine-executes the three-layer-stack collaboration methodology (gstack governance + GSD project manager + superpowers senior engineer + karpathy principles + mattpocock moves) as a runnable engine
@@ -15,7 +15,7 @@
15
15
 
16
16
  ## ✨ TL;DR
17
17
 
18
- Assembles the best open-source Claude Code ecosystem components, weaving them into a unified workflow via opinionated composition skills; does not vendor upstream code — manifests describe install/check, and composition skills orchestrate multi-upstream collaboration.
18
+ **Best-practice orchestration for Harness Engineering on Claude Code** — assembles the best open-source Claude Code ecosystem components, weaving them into a unified workflow via opinionated composition skills; does not vendor upstream code — manifests describe install/check, and composition skills orchestrate multi-upstream collaboration.
19
19
 
20
20
  ---
21
21
 
package/dist/cli.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { execSync, spawnSync, spawn } from 'child_process';
3
- import { existsSync, mkdirSync, renameSync, writeFileSync, readFileSync, appendFileSync, readdirSync } from 'fs';
3
+ import { existsSync, mkdirSync, renameSync, writeFileSync, readFileSync, readdirSync, appendFileSync } from 'fs';
4
4
  import { join, resolve, dirname, relative } from 'path';
5
5
  import { homedir } from 'os';
6
6
  import { Type } from '@sinclair/typebox';
@@ -847,7 +847,7 @@ var init_resume = __esm({
847
847
 
848
848
  // package.json
849
849
  var package_default = {
850
- version: "3.4.0"};
850
+ version: "3.4.2"};
851
851
 
852
852
  // src/manifest/errors.ts
853
853
  function instancePathToKeyPath(instancePath) {
@@ -5020,6 +5020,182 @@ async function atomicWrite2(path, content) {
5020
5020
  return `write ${path} failed: ${err2.message}`;
5021
5021
  }
5022
5022
  }
5023
+ function readInstalledPlugins(homedirOverride) {
5024
+ const home = homedir();
5025
+ const path = join(home, ".claude", "plugins", "installed_plugins.json");
5026
+ let raw;
5027
+ try {
5028
+ raw = readFileSync(path, "utf8");
5029
+ } catch {
5030
+ return /* @__PURE__ */ new Set();
5031
+ }
5032
+ let parsed;
5033
+ try {
5034
+ parsed = JSON.parse(raw);
5035
+ } catch {
5036
+ return /* @__PURE__ */ new Set();
5037
+ }
5038
+ if (!parsed || typeof parsed !== "object") return /* @__PURE__ */ new Set();
5039
+ const plugins = parsed.plugins;
5040
+ if (!plugins || typeof plugins !== "object") return /* @__PURE__ */ new Set();
5041
+ const out = /* @__PURE__ */ new Set();
5042
+ for (const key of Object.keys(plugins)) {
5043
+ const at = key.indexOf("@");
5044
+ if (at <= 0) continue;
5045
+ out.add(key.slice(0, at));
5046
+ }
5047
+ return out;
5048
+ }
5049
+ function readInstalledUserSkills(homedirOverride) {
5050
+ const home = homedir();
5051
+ const skillsRoot = join(home, ".claude", "skills");
5052
+ try {
5053
+ const entries = readdirSync(skillsRoot, { withFileTypes: true });
5054
+ const out = /* @__PURE__ */ new Set();
5055
+ for (const e of entries) if (e.isDirectory()) out.add(e.name);
5056
+ return out;
5057
+ } catch {
5058
+ return /* @__PURE__ */ new Set();
5059
+ }
5060
+ }
5061
+ function resolveCapabilityCmd(capability, installedPlugins, installedUserSkills) {
5062
+ const { cmd, install_type, plugin_id, skill_dir } = capability;
5063
+ if (!install_type) return { renderedCmd: cmd };
5064
+ const types = Array.isArray(install_type) ? install_type : [install_type];
5065
+ const uniqueTypes = [...new Set(types)];
5066
+ const missingHints = [];
5067
+ let anyDetected = false;
5068
+ for (const t2 of uniqueTypes) {
5069
+ if (t2 === "plugin") {
5070
+ if (!plugin_id) {
5071
+ missingHints.push(
5072
+ `install_type=plugin declared but no plugin_id (capabilities.yaml schema bug)`
5073
+ );
5074
+ continue;
5075
+ }
5076
+ if (installedPlugins.has(plugin_id)) {
5077
+ anyDetected = true;
5078
+ break;
5079
+ }
5080
+ missingHints.push(`plugin '${plugin_id}' (\`claude plugin install ${plugin_id}\`)`);
5081
+ } else {
5082
+ if (!skill_dir) {
5083
+ missingHints.push(
5084
+ `install_type=user-skill declared but no skill_dir (capabilities.yaml schema bug)`
5085
+ );
5086
+ continue;
5087
+ }
5088
+ if (installedUserSkills.has(skill_dir)) {
5089
+ anyDetected = true;
5090
+ break;
5091
+ }
5092
+ missingHints.push(
5093
+ `user-skill '${skill_dir}' under ~/.claude/skills/ (git clone the official repo; e.g. gstack: \`git clone https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup\`)`
5094
+ );
5095
+ }
5096
+ }
5097
+ if (anyDetected) return { renderedCmd: cmd };
5098
+ const prefix = uniqueTypes.length > 1 ? "[multi]" : `[${uniqueTypes[0]}]`;
5099
+ const joined = missingHints.join(" OR ");
5100
+ return {
5101
+ renderedCmd: cmd,
5102
+ warning: `${prefix} '${cmd}' backing missing \u2014 install either: ${joined}.`
5103
+ };
5104
+ }
5105
+ var CAPABILITY_CMD_TEMPLATE = /\{\{\s*capabilities\.([a-zA-Z0-9_-]+)\.cmd\s*\}\}/g;
5106
+ function renderSkillBody(body, capabilities, installedPlugins, installedUserSkills) {
5107
+ const warningsSet = /* @__PURE__ */ new Set();
5108
+ const out = body.replace(CAPABILITY_CMD_TEMPLATE, (match2, name) => {
5109
+ const cap = capabilities[name];
5110
+ if (!cap) {
5111
+ warningsSet.add(
5112
+ `capability '${name}' referenced in SKILL.md but not defined in capabilities.yaml`
5113
+ );
5114
+ return match2;
5115
+ }
5116
+ const { renderedCmd, warning } = resolveCapabilityCmd(
5117
+ cap,
5118
+ installedPlugins,
5119
+ installedUserSkills
5120
+ );
5121
+ if (warning) warningsSet.add(warning);
5122
+ return renderedCmd;
5123
+ });
5124
+ return { body: out, warnings: [...warningsSet] };
5125
+ }
5126
+
5127
+ // src/cli/lib/renderSkillTemplates.ts
5128
+ async function loadCapabilities(workflowsDir) {
5129
+ const path = join(workflowsDir, "capabilities.yaml");
5130
+ const raw = await readFile(path, "utf8");
5131
+ const doc = parse(raw);
5132
+ return doc?.capabilities ?? {};
5133
+ }
5134
+ async function renderSkillFile(skillName, skillsBase, capabilities, installedPlugins, installedUserSkills) {
5135
+ const skillPath = join(skillsBase, skillName, "SKILL.md");
5136
+ const result = {
5137
+ name: skillName,
5138
+ skillPath,
5139
+ rendered: false,
5140
+ warnings: []
5141
+ };
5142
+ let body;
5143
+ try {
5144
+ body = await readFile(skillPath, "utf8");
5145
+ } catch (e) {
5146
+ result.error = `read failed: ${e.message}`;
5147
+ return result;
5148
+ }
5149
+ const rendered = renderSkillBody(body, capabilities, installedPlugins, installedUserSkills);
5150
+ if (rendered.body === body) {
5151
+ result.warnings = rendered.warnings;
5152
+ return result;
5153
+ }
5154
+ try {
5155
+ await writeFile(skillPath, rendered.body, "utf8");
5156
+ result.rendered = true;
5157
+ result.warnings = rendered.warnings;
5158
+ } catch (e) {
5159
+ result.error = `write failed: ${e.message}`;
5160
+ }
5161
+ return result;
5162
+ }
5163
+ async function renderAllSkills(skillNames, skillsBase, workflowsDir, homedirOverride) {
5164
+ let capabilities = {};
5165
+ try {
5166
+ capabilities = await loadCapabilities(workflowsDir);
5167
+ } catch (e) {
5168
+ return {
5169
+ results: skillNames.map((name) => ({
5170
+ name,
5171
+ skillPath: join(skillsBase, name, "SKILL.md"),
5172
+ rendered: false,
5173
+ warnings: [],
5174
+ error: `capabilities.yaml load failed: ${e.message}`
5175
+ })),
5176
+ aggregatedWarnings: [
5177
+ `capabilities.yaml unreadable \u2014 SKILL.md placeholders left verbatim (${e.message})`
5178
+ ]
5179
+ };
5180
+ }
5181
+ const installedPlugins = readInstalledPlugins();
5182
+ const installedUserSkills = readInstalledUserSkills();
5183
+ const results = [];
5184
+ const warningSet = /* @__PURE__ */ new Set();
5185
+ for (const name of skillNames) {
5186
+ const r = await renderSkillFile(
5187
+ name,
5188
+ skillsBase,
5189
+ capabilities,
5190
+ installedPlugins,
5191
+ installedUserSkills
5192
+ );
5193
+ results.push(r);
5194
+ for (const w of r.warnings) warningSet.add(w);
5195
+ if (r.error) warningSet.add(`${name}: ${r.error}`);
5196
+ }
5197
+ return { results, aggregatedWarnings: [...warningSet] };
5198
+ }
5023
5199
  init_checkAgentTeams();
5024
5200
  var FLAT_LEGACY_DEPRECATED = /* @__PURE__ */ new Set(["plan-feature", "execute-task", "verify-work"]);
5025
5201
  var FLAT_LEGACY_KEEP = /* @__PURE__ */ new Set(["research", "retro", "auto"]);
@@ -5240,6 +5416,21 @@ function registerSetup(program2) {
5240
5416
  }
5241
5417
  }
5242
5418
  console.log(t("setup.step_a_complete", { count: skillsInstalled, path: skillsBase }));
5419
+ const skillNames = toInstall.map((wf) => wf.name);
5420
+ const rendered = await renderAllSkills(skillNames, skillsBase, workflowsDir);
5421
+ const renderedCount = rendered.results.filter((r) => r.rendered).length;
5422
+ console.log(
5423
+ t("setup.step_a_render.complete", {
5424
+ count: renderedCount,
5425
+ total: skillsInstalled
5426
+ })
5427
+ );
5428
+ if (rendered.aggregatedWarnings.length > 0) {
5429
+ console.warn(t("setup.step_a_render.warnings_header"));
5430
+ for (const w of rendered.aggregatedWarnings) {
5431
+ console.warn(` - ${w}`);
5432
+ }
5433
+ }
5243
5434
  const cResult = await enableAgentTeamsInSettings();
5244
5435
  if (cResult.status === "created") {
5245
5436
  console.log(t("setup.step_c.created", { path: cResult.path }));