hunter-harness 0.2.10 → 0.2.11
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 +3 -3
- package/dist/bin.js +253 -53
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -21,8 +21,8 @@ npx hunter-harness update
|
|
|
21
21
|
| Claude Code | `.claude/skills/` | `.claude/rules/*.md` | `.claude/agents/` |
|
|
22
22
|
| Codex | `.agents/skills/` | `AGENTS.md` | 不生成 |
|
|
23
23
|
| Cursor | `.cursor/skills/` | `.cursor/rules/*.mdc` | 不生成 |
|
|
24
|
-
| CodeBuddy `both` | `.codebuddy/skills/` | `CODEBUDDY.md` | `.codebuddy/agents/` |
|
|
24
|
+
| CodeBuddy `both` | `.codebuddy/skills/` | `CODEBUDDY.md` + `.codebuddy/.rules/*.mdc` + `.codebuddy/rules/*.md` | `.codebuddy/agents/` |
|
|
25
25
|
|
|
26
|
-
需要 Node.js
|
|
26
|
+
需要 Node.js 22.12 或更高版本。选择 CodeBuddy 时,可将已有 `.claude/rules` 非破坏性同步到 CodeBuddy,并在检测到 `.codegraph/` 时合并项目级 `.mcp.json`;疑似凭据内容会跳过。token 只通过 `--token-env` 指定的环境变量读取,不要写入项目文件或命令参数。
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
本包使用 MIT License。
|
package/dist/bin.js
CHANGED
|
@@ -1098,8 +1098,8 @@ async function assertNoSymlinks(root, relativePath) {
|
|
|
1098
1098
|
for (const segment of normalized.split("/")) {
|
|
1099
1099
|
current = join(current, segment);
|
|
1100
1100
|
try {
|
|
1101
|
-
const
|
|
1102
|
-
if (
|
|
1101
|
+
const stat7 = await lstat(current);
|
|
1102
|
+
if (stat7.isSymbolicLink()) {
|
|
1103
1103
|
throw new UnsafePathError("symbolic links are not managed");
|
|
1104
1104
|
}
|
|
1105
1105
|
} catch (error) {
|
|
@@ -1982,6 +1982,13 @@ function makeAdapter(spec) {
|
|
|
1982
1982
|
return projectBundleFiles(bundle, spec.skillsRoot, spec.agentsRoot);
|
|
1983
1983
|
},
|
|
1984
1984
|
contextIndex(context) {
|
|
1985
|
+
if (spec.name === "codebuddy") {
|
|
1986
|
+
return {
|
|
1987
|
+
instructions: spec.instructions,
|
|
1988
|
+
skills_root: spec.skillsRoot,
|
|
1989
|
+
rules: codebuddyRulePaths(context)
|
|
1990
|
+
};
|
|
1991
|
+
}
|
|
1985
1992
|
const rules = [];
|
|
1986
1993
|
if (spec.rulesRoot !== null && spec.ruleExt !== null) {
|
|
1987
1994
|
rules.push(`${spec.rulesRoot}/harness-general${spec.ruleExt}`);
|
|
@@ -2001,6 +2008,9 @@ function makeAdapter(spec) {
|
|
|
2001
2008
|
boundaries.push(spec.agentsRoot);
|
|
2002
2009
|
if (spec.rulesRoot !== null)
|
|
2003
2010
|
boundaries.push(spec.rulesRoot);
|
|
2011
|
+
if (spec.name === "codebuddy") {
|
|
2012
|
+
boundaries.push(".codebuddy/.rules", ".codebuddy/rules");
|
|
2013
|
+
}
|
|
2004
2014
|
const top = spec.skillsRoot.split("/")[0];
|
|
2005
2015
|
if (top !== void 0)
|
|
2006
2016
|
boundaries.push(top);
|
|
@@ -2053,6 +2063,17 @@ var ADAPTERS = {
|
|
|
2053
2063
|
function getAdapter(name) {
|
|
2054
2064
|
return ADAPTERS[name];
|
|
2055
2065
|
}
|
|
2066
|
+
function codebuddyRulePaths(context) {
|
|
2067
|
+
const names = context.profile === "java" ? ["harness-general", "harness-profile-java"] : ["harness-general"];
|
|
2068
|
+
const paths = [];
|
|
2069
|
+
if (context.codebuddySurface !== "cli") {
|
|
2070
|
+
paths.push(...names.map((name) => `.codebuddy/.rules/${name}.mdc`));
|
|
2071
|
+
}
|
|
2072
|
+
if (context.codebuddySurface !== "ide") {
|
|
2073
|
+
paths.push(...names.map((name) => `.codebuddy/rules/${name}.md`));
|
|
2074
|
+
}
|
|
2075
|
+
return paths.sort((left, right) => left.localeCompare(right));
|
|
2076
|
+
}
|
|
2056
2077
|
function getAdapters(names) {
|
|
2057
2078
|
return HARNESS_AGENT_ORDER.filter((n) => names.includes(n)).map(getAdapter);
|
|
2058
2079
|
}
|
|
@@ -2068,6 +2089,13 @@ function managedTargetsFor(adapter, bundle, context) {
|
|
|
2068
2089
|
if (context.profile === "java") {
|
|
2069
2090
|
records.push(ruleTarget("rules/harness-profile-java.mdc", ".cursor/rules/harness-profile-java.mdc", CURSOR_JAVA_RULES_CONTENT));
|
|
2070
2091
|
}
|
|
2092
|
+
} else if (adapter.name === "codebuddy") {
|
|
2093
|
+
for (const targetPath of codebuddyRulePaths(context)) {
|
|
2094
|
+
const isMdc = targetPath.endsWith(".mdc");
|
|
2095
|
+
const isJava = targetPath.includes("harness-profile-java");
|
|
2096
|
+
const content = isJava ? isMdc ? CURSOR_JAVA_RULES_CONTENT : HARNESS_JAVA_RULES_CONTENT : isMdc ? CURSOR_GENERAL_RULES_CONTENT : HARNESS_GENERAL_RULES_CONTENT;
|
|
2097
|
+
records.push(ruleTarget(`rules/${isJava ? "harness-profile-java" : "harness-general"}${isMdc ? ".mdc" : ".md"}`, targetPath, content));
|
|
2098
|
+
}
|
|
2071
2099
|
}
|
|
2072
2100
|
assertNoCaseCollisions(records.map((record) => record.target_path));
|
|
2073
2101
|
return records.sort((left, right) => left.target_path.localeCompare(right.target_path));
|
|
@@ -4107,8 +4135,8 @@ async function rollbackLatestCommittedUpdate(projectRoot) {
|
|
|
4107
4135
|
const after = JSON.parse(await readFile11(join12(transactionRoot, "after", "manifest.json"), "utf8"));
|
|
4108
4136
|
for (const entry of after) {
|
|
4109
4137
|
const target = join12(projectRoot, entry.path);
|
|
4110
|
-
const
|
|
4111
|
-
if (
|
|
4138
|
+
const exists6 = await pathExists(target);
|
|
4139
|
+
if (exists6 !== entry.exists || exists6 && await sha256File(target) !== entry.hash) {
|
|
4112
4140
|
throw new Error("cannot rollback dirty path: " + entry.path);
|
|
4113
4141
|
}
|
|
4114
4142
|
}
|
|
@@ -4120,15 +4148,15 @@ async function rollbackLatestCommittedUpdate(projectRoot) {
|
|
|
4120
4148
|
}
|
|
4121
4149
|
seen.add(snapshot.path);
|
|
4122
4150
|
const target = join12(projectRoot, snapshot.path);
|
|
4123
|
-
const
|
|
4151
|
+
const exists6 = await pathExists(target);
|
|
4124
4152
|
if (snapshot.existed && snapshot.snapshot_name !== null) {
|
|
4125
4153
|
const content = await readFile11(join12(transactionRoot, "before", snapshot.snapshot_name));
|
|
4126
4154
|
operations.push({
|
|
4127
|
-
operation:
|
|
4155
|
+
operation: exists6 ? "modify" : "add",
|
|
4128
4156
|
path: snapshot.path,
|
|
4129
4157
|
content
|
|
4130
4158
|
});
|
|
4131
|
-
} else if (
|
|
4159
|
+
} else if (exists6) {
|
|
4132
4160
|
operations.push({ operation: "delete", path: snapshot.path });
|
|
4133
4161
|
}
|
|
4134
4162
|
}
|
|
@@ -4672,14 +4700,118 @@ function serializeCliResult(result) {
|
|
|
4672
4700
|
return JSON.stringify(result) + "\n";
|
|
4673
4701
|
}
|
|
4674
4702
|
|
|
4703
|
+
// src/config/codebuddy-setup.ts
|
|
4704
|
+
import { mkdir as mkdir4, readFile as readFile14, readdir as readdir7, stat as stat4, writeFile as writeFile5 } from "node:fs/promises";
|
|
4705
|
+
import { basename as basename2, dirname as dirname4, extname, join as join15 } from "node:path";
|
|
4706
|
+
var MANAGED_RULE_NAMES = /* @__PURE__ */ new Set([
|
|
4707
|
+
"harness-general.md",
|
|
4708
|
+
"harness-general.mdc",
|
|
4709
|
+
"harness-profile-java.md",
|
|
4710
|
+
"harness-profile-java.mdc"
|
|
4711
|
+
]);
|
|
4712
|
+
var SENSITIVE_ASSIGNMENT = /(?:password|passwd|token|secret|access[_-]?key|private[_-]?key)\s*[:=]\s*[^\s#]+/i;
|
|
4713
|
+
var PRIVATE_KEY_BLOCK = /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/i;
|
|
4714
|
+
async function exists4(path) {
|
|
4715
|
+
try {
|
|
4716
|
+
await stat4(path);
|
|
4717
|
+
return true;
|
|
4718
|
+
} catch {
|
|
4719
|
+
return false;
|
|
4720
|
+
}
|
|
4721
|
+
}
|
|
4722
|
+
async function readJsonObject(path) {
|
|
4723
|
+
try {
|
|
4724
|
+
const parsed = JSON.parse(await readFile14(path, "utf8"));
|
|
4725
|
+
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
4726
|
+
} catch (error) {
|
|
4727
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return {};
|
|
4728
|
+
return null;
|
|
4729
|
+
}
|
|
4730
|
+
}
|
|
4731
|
+
async function inspectCodeBuddySetup(projectRoot) {
|
|
4732
|
+
const rulesRoot = join15(projectRoot, ".claude", "rules");
|
|
4733
|
+
let claudeRules = [];
|
|
4734
|
+
try {
|
|
4735
|
+
claudeRules = (await readdir7(rulesRoot, { withFileTypes: true })).filter((entry) => entry.isFile() && [".md", ".mdc"].includes(extname(entry.name).toLowerCase())).map((entry) => entry.name).filter((name) => !MANAGED_RULE_NAMES.has(name)).sort();
|
|
4736
|
+
} catch (error) {
|
|
4737
|
+
if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) throw error;
|
|
4738
|
+
}
|
|
4739
|
+
const mcp = await readJsonObject(join15(projectRoot, ".mcp.json"));
|
|
4740
|
+
const servers = mcp?.mcpServers;
|
|
4741
|
+
const configured = servers !== null && typeof servers === "object" && !Array.isArray(servers) && Object.prototype.hasOwnProperty.call(servers, "codegraph");
|
|
4742
|
+
return {
|
|
4743
|
+
claudeRules,
|
|
4744
|
+
hasCodeGraphIndex: await exists4(join15(projectRoot, ".codegraph")),
|
|
4745
|
+
codeGraphConfigured: configured
|
|
4746
|
+
};
|
|
4747
|
+
}
|
|
4748
|
+
function destinationTargets(root, surface, name) {
|
|
4749
|
+
const stem = basename2(name, extname(name));
|
|
4750
|
+
const targets = [];
|
|
4751
|
+
if (surface !== "cli") targets.push(join15(root, ".codebuddy", ".rules", `${stem}.mdc`));
|
|
4752
|
+
if (surface !== "ide") targets.push(join15(root, ".codebuddy", "rules", `${stem}.md`));
|
|
4753
|
+
return targets;
|
|
4754
|
+
}
|
|
4755
|
+
async function applyCodeBuddySetup(options) {
|
|
4756
|
+
const result = {
|
|
4757
|
+
copied: [],
|
|
4758
|
+
preserved: [],
|
|
4759
|
+
skippedSensitive: [],
|
|
4760
|
+
mcpUpdated: false,
|
|
4761
|
+
warnings: []
|
|
4762
|
+
};
|
|
4763
|
+
if (options.syncClaudeRules) {
|
|
4764
|
+
const plan = await inspectCodeBuddySetup(options.projectRoot);
|
|
4765
|
+
for (const name of plan.claudeRules) {
|
|
4766
|
+
const source = join15(options.projectRoot, ".claude", "rules", name);
|
|
4767
|
+
const content = await readFile14(source, "utf8");
|
|
4768
|
+
if (SENSITIVE_ASSIGNMENT.test(content) || PRIVATE_KEY_BLOCK.test(content)) {
|
|
4769
|
+
result.skippedSensitive.push(name);
|
|
4770
|
+
continue;
|
|
4771
|
+
}
|
|
4772
|
+
for (const target of destinationTargets(options.projectRoot, options.surface, name)) {
|
|
4773
|
+
if (await exists4(target)) {
|
|
4774
|
+
result.preserved.push(target);
|
|
4775
|
+
continue;
|
|
4776
|
+
}
|
|
4777
|
+
await mkdir4(dirname4(target), { recursive: true });
|
|
4778
|
+
await writeFile5(target, content, { encoding: "utf8", flag: "wx" });
|
|
4779
|
+
result.copied.push(target);
|
|
4780
|
+
}
|
|
4781
|
+
}
|
|
4782
|
+
}
|
|
4783
|
+
if (options.configureCodeGraph) {
|
|
4784
|
+
const path = join15(options.projectRoot, ".mcp.json");
|
|
4785
|
+
const current = await readJsonObject(path);
|
|
4786
|
+
if (current === null) {
|
|
4787
|
+
result.warnings.push(".mcp.json \u4E0D\u662F\u6709\u6548 JSON\uFF0C\u5DF2\u4FDD\u7559\u539F\u6587\u4EF6\u5E76\u8DF3\u8FC7 CodeGraph MCP \u914D\u7F6E");
|
|
4788
|
+
} else {
|
|
4789
|
+
const currentServers = current.mcpServers;
|
|
4790
|
+
const servers = currentServers !== null && typeof currentServers === "object" && !Array.isArray(currentServers) ? currentServers : {};
|
|
4791
|
+
if (!Object.prototype.hasOwnProperty.call(servers, "codegraph")) {
|
|
4792
|
+
const next = {
|
|
4793
|
+
...current,
|
|
4794
|
+
mcpServers: {
|
|
4795
|
+
...servers,
|
|
4796
|
+
codegraph: { command: "codegraph", args: ["serve", "--mcp"] }
|
|
4797
|
+
}
|
|
4798
|
+
};
|
|
4799
|
+
await writeFile5(path, JSON.stringify(next, null, 2) + "\n", "utf8");
|
|
4800
|
+
result.mcpUpdated = true;
|
|
4801
|
+
}
|
|
4802
|
+
}
|
|
4803
|
+
}
|
|
4804
|
+
return result;
|
|
4805
|
+
}
|
|
4806
|
+
|
|
4675
4807
|
// src/commands/refresh.ts
|
|
4676
|
-
import { readFile as
|
|
4677
|
-
import { join as
|
|
4808
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
4809
|
+
import { join as join16 } from "node:path";
|
|
4678
4810
|
import { parse as parseYaml9 } from "yaml";
|
|
4679
4811
|
async function detectProject(root) {
|
|
4680
4812
|
let content;
|
|
4681
4813
|
try {
|
|
4682
|
-
content = await
|
|
4814
|
+
content = await readFile15(join16(root, ".harness", "project.yaml"), "utf8");
|
|
4683
4815
|
} catch (error) {
|
|
4684
4816
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
4685
4817
|
return { status: "absent" };
|
|
@@ -4707,8 +4839,14 @@ function refreshAgents(config) {
|
|
|
4707
4839
|
}));
|
|
4708
4840
|
return agents.length > 0 ? agents : ["claude-code"];
|
|
4709
4841
|
}
|
|
4710
|
-
function codebuddySurface(config) {
|
|
4711
|
-
|
|
4842
|
+
function codebuddySurface(config, override) {
|
|
4843
|
+
const value = override ?? config.adapter_options?.codebuddy?.surface ?? "both";
|
|
4844
|
+
if (value === "both" || value === "ide" || value === "cli") return value;
|
|
4845
|
+
throw new InitConfigurationError(
|
|
4846
|
+
"codebuddy surface \u5FC5\u987B\u4E3A both\u3001ide \u6216 cli",
|
|
4847
|
+
3,
|
|
4848
|
+
"CODEBUDDY_SURFACE_INVALID"
|
|
4849
|
+
);
|
|
4712
4850
|
}
|
|
4713
4851
|
function summarize(result) {
|
|
4714
4852
|
const items = [
|
|
@@ -4795,7 +4933,7 @@ async function runRefresh(options, dependencies) {
|
|
|
4795
4933
|
resourcesRoot: dependencies.resourcesRoot,
|
|
4796
4934
|
...targetProfile === void 0 ? {} : { profile: targetProfile },
|
|
4797
4935
|
agents: targetAgents,
|
|
4798
|
-
codebuddySurface: codebuddySurface(detection.config),
|
|
4936
|
+
codebuddySurface: codebuddySurface(detection.config, options.codebuddySurface),
|
|
4799
4937
|
dryRun: true,
|
|
4800
4938
|
forceManaged: options.forceManaged === true
|
|
4801
4939
|
});
|
|
@@ -4831,7 +4969,7 @@ async function runRefresh(options, dependencies) {
|
|
|
4831
4969
|
resourcesRoot: dependencies.resourcesRoot,
|
|
4832
4970
|
...targetProfile === void 0 ? {} : { profile: targetProfile },
|
|
4833
4971
|
agents: targetAgents,
|
|
4834
|
-
codebuddySurface: codebuddySurface(detection.config),
|
|
4972
|
+
codebuddySurface: codebuddySurface(detection.config, options.codebuddySurface),
|
|
4835
4973
|
dryRun,
|
|
4836
4974
|
forceManaged: options.forceManaged === true
|
|
4837
4975
|
});
|
|
@@ -4880,6 +5018,46 @@ var AGENT_LABELS = {
|
|
|
4880
5018
|
cursor: "Cursor",
|
|
4881
5019
|
codebuddy: "CodeBuddy"
|
|
4882
5020
|
};
|
|
5021
|
+
async function configureCodeBuddyExtras(agents, surface, options, dependencies) {
|
|
5022
|
+
if (!agents.includes("codebuddy")) return;
|
|
5023
|
+
const plan = await inspectCodeBuddySetup(dependencies.cwd);
|
|
5024
|
+
let syncClaudeRules = false;
|
|
5025
|
+
let configureCodeGraph = false;
|
|
5026
|
+
if (plan.claudeRules.length > 0) {
|
|
5027
|
+
syncClaudeRules = options.nonInteractive === true ? options.yes === true : /^(?:|y|yes)$/i.test((await dependencies.prompt(
|
|
5028
|
+
`\u53D1\u73B0 ${plan.claudeRules.length} \u4E2A Claude \u81EA\u5B9A\u4E49\u89C4\u5219\uFF0C\u662F\u5426\u590D\u5236\u5230 CodeBuddy\uFF08\u4FDD\u7559\u6E90\u6587\u4EF6\u4E14\u4E0D\u8986\u76D6\u76EE\u6807\uFF09\uFF1F[Y/n]\uFF1A`
|
|
5029
|
+
)).trim());
|
|
5030
|
+
}
|
|
5031
|
+
if (plan.hasCodeGraphIndex && !plan.codeGraphConfigured) {
|
|
5032
|
+
configureCodeGraph = options.nonInteractive === true ? options.yes === true : /^(?:|y|yes)$/i.test((await dependencies.prompt(
|
|
5033
|
+
"\u68C0\u6D4B\u5230 .codegraph \u7D22\u5F15\uFF0C\u662F\u5426\u5408\u5E76 CodeGraph MCP \u5230\u9879\u76EE .mcp.json\uFF1F[Y/n]\uFF1A"
|
|
5034
|
+
)).trim());
|
|
5035
|
+
}
|
|
5036
|
+
if (options.dryRun === true) {
|
|
5037
|
+
if (syncClaudeRules || configureCodeGraph) {
|
|
5038
|
+
dependencies.stdout("CodeBuddy \u9644\u52A0\u914D\u7F6E\u5904\u4E8E dry-run\uFF0C\u672A\u5199\u5165\u89C4\u5219\u6216 .mcp.json\u3002\n");
|
|
5039
|
+
}
|
|
5040
|
+
return;
|
|
5041
|
+
}
|
|
5042
|
+
const result = await applyCodeBuddySetup({
|
|
5043
|
+
projectRoot: dependencies.cwd,
|
|
5044
|
+
surface,
|
|
5045
|
+
syncClaudeRules,
|
|
5046
|
+
configureCodeGraph
|
|
5047
|
+
});
|
|
5048
|
+
if (result.copied.length > 0) {
|
|
5049
|
+
dependencies.stdout(`\u5DF2\u5B89\u5168\u590D\u5236 ${result.copied.length} \u4E2A CodeBuddy \u89C4\u5219\u6587\u4EF6\u3002
|
|
5050
|
+
`);
|
|
5051
|
+
}
|
|
5052
|
+
if (result.skippedSensitive.length > 0) {
|
|
5053
|
+
dependencies.stderr(
|
|
5054
|
+
`\u68C0\u6D4B\u5230 ${result.skippedSensitive.length} \u4E2A\u7591\u4F3C\u542B\u51ED\u636E\u7684 Claude \u89C4\u5219\uFF0C\u5DF2\u4FDD\u7559\u539F\u5904\u4E14\u672A\u590D\u5236\u3002
|
|
5055
|
+
`
|
|
5056
|
+
);
|
|
5057
|
+
}
|
|
5058
|
+
if (result.mcpUpdated) dependencies.stdout("\u5DF2\u5408\u5E76 CodeGraph MCP \u5230 .mcp.json\u3002\n");
|
|
5059
|
+
for (const warning of result.warnings) dependencies.stderr(warning + "\n");
|
|
5060
|
+
}
|
|
4883
5061
|
async function runFirstInstall(options, dependencies) {
|
|
4884
5062
|
const requestId = uuidV7();
|
|
4885
5063
|
try {
|
|
@@ -4910,6 +5088,12 @@ async function runFirstInstall(options, dependencies) {
|
|
|
4910
5088
|
config,
|
|
4911
5089
|
dryRun: options.dryRun === true
|
|
4912
5090
|
});
|
|
5091
|
+
await configureCodeBuddyExtras(
|
|
5092
|
+
config.agents,
|
|
5093
|
+
config.codebuddy_surface,
|
|
5094
|
+
options,
|
|
5095
|
+
dependencies
|
|
5096
|
+
);
|
|
4913
5097
|
const output = {
|
|
4914
5098
|
schema_version: 1,
|
|
4915
5099
|
command: "configure",
|
|
@@ -4949,20 +5133,29 @@ async function runFirstInstall(options, dependencies) {
|
|
|
4949
5133
|
return exitCode;
|
|
4950
5134
|
}
|
|
4951
5135
|
}
|
|
4952
|
-
async function runExistingProject(options, dependencies, currentProfile) {
|
|
5136
|
+
async function runExistingProject(options, dependencies, currentProfile, currentSurface) {
|
|
4953
5137
|
const refreshOptions = {};
|
|
4954
5138
|
if (options.agents !== void 0) refreshOptions.agents = options.agents;
|
|
5139
|
+
if (options.codebuddySurface !== void 0) {
|
|
5140
|
+
refreshOptions.codebuddySurface = options.codebuddySurface;
|
|
5141
|
+
}
|
|
4955
5142
|
if (options.profile !== void 0) refreshOptions.profile = options.profile;
|
|
4956
5143
|
if (options.nonInteractive !== void 0) refreshOptions.nonInteractive = options.nonInteractive;
|
|
4957
5144
|
if (options.yes !== void 0) refreshOptions.yes = options.yes;
|
|
4958
5145
|
if (options.dryRun !== void 0) refreshOptions.dryRun = options.dryRun;
|
|
4959
5146
|
if (options.json !== void 0) refreshOptions.json = options.json;
|
|
4960
5147
|
if (options.forceManaged !== void 0) refreshOptions.forceManaged = options.forceManaged;
|
|
4961
|
-
if (options.nonInteractive === true) {
|
|
4962
|
-
return runRefresh(refreshOptions, dependencies);
|
|
4963
|
-
}
|
|
4964
5148
|
const installed = await readInstalledAgentConfiguration(dependencies.cwd);
|
|
4965
5149
|
const currentAgents = installed.agents.length > 0 ? installed.agents : ["claude-code"];
|
|
5150
|
+
if (options.nonInteractive === true) {
|
|
5151
|
+
const selectedAgents2 = options.agents === void 0 ? currentAgents : parseAgentsInput(options.agents);
|
|
5152
|
+
const code2 = await runRefresh(refreshOptions, dependencies);
|
|
5153
|
+
if (code2 === 0) {
|
|
5154
|
+
const surface = options.codebuddySurface === "ide" || options.codebuddySurface === "cli" || options.codebuddySurface === "both" ? options.codebuddySurface : currentSurface;
|
|
5155
|
+
await configureCodeBuddyExtras(selectedAgents2, surface, options, dependencies);
|
|
5156
|
+
}
|
|
5157
|
+
return code2;
|
|
5158
|
+
}
|
|
4966
5159
|
const currentLines = currentAgents.map(
|
|
4967
5160
|
(agent) => `- ${AGENT_LABELS[agent]}\uFF1A${installed.profiles[agent] ?? currentProfile}`
|
|
4968
5161
|
).join("\n");
|
|
@@ -4997,7 +5190,13 @@ ${currentLines}
|
|
|
4997
5190
|
refreshOptions.profile = answer.trim() === "" ? defaultProfile : answer.trim();
|
|
4998
5191
|
}
|
|
4999
5192
|
refreshOptions.confirmed = true;
|
|
5000
|
-
|
|
5193
|
+
const selectedAgents = parseAgentsInput(refreshOptions.agents);
|
|
5194
|
+
const code = await runRefresh(refreshOptions, dependencies);
|
|
5195
|
+
if (code === 0) {
|
|
5196
|
+
const surface = options.codebuddySurface === "ide" || options.codebuddySurface === "cli" || options.codebuddySurface === "both" ? options.codebuddySurface : currentSurface;
|
|
5197
|
+
await configureCodeBuddyExtras(selectedAgents, surface, options, dependencies);
|
|
5198
|
+
}
|
|
5199
|
+
return code;
|
|
5001
5200
|
}
|
|
5002
5201
|
async function runConfigure(options, dependencies) {
|
|
5003
5202
|
const detection = await detectProject(dependencies.cwd);
|
|
@@ -5022,7 +5221,8 @@ async function runConfigure(options, dependencies) {
|
|
|
5022
5221
|
}
|
|
5023
5222
|
if (detection.status === "valid") {
|
|
5024
5223
|
const currentProfile = detection.config.project.profiles[0] ?? "general";
|
|
5025
|
-
|
|
5224
|
+
const currentSurface = detection.config.adapter_options?.codebuddy?.surface ?? "both";
|
|
5225
|
+
return runExistingProject(options, dependencies, currentProfile, currentSurface);
|
|
5026
5226
|
}
|
|
5027
5227
|
return runFirstInstall(options, dependencies);
|
|
5028
5228
|
}
|
|
@@ -5371,11 +5571,11 @@ async function runUpdate(options, dependencies) {
|
|
|
5371
5571
|
}
|
|
5372
5572
|
|
|
5373
5573
|
// src/commands/recovery.ts
|
|
5374
|
-
import { stat as
|
|
5375
|
-
import { join as
|
|
5376
|
-
async function
|
|
5574
|
+
import { stat as stat5 } from "node:fs/promises";
|
|
5575
|
+
import { join as join17 } from "node:path";
|
|
5576
|
+
async function exists5(path) {
|
|
5377
5577
|
try {
|
|
5378
|
-
await
|
|
5578
|
+
await stat5(path);
|
|
5379
5579
|
return true;
|
|
5380
5580
|
} catch {
|
|
5381
5581
|
return false;
|
|
@@ -5432,7 +5632,7 @@ async function runRecoveryMenuIfApplicable(options, dependencies) {
|
|
|
5432
5632
|
return 5;
|
|
5433
5633
|
}
|
|
5434
5634
|
}
|
|
5435
|
-
const initialized = await
|
|
5635
|
+
const initialized = await exists5(join17(dependencies.cwd, ".harness", "project.yaml"));
|
|
5436
5636
|
if (!initialized || options.nonInteractive === true || explicitConfigure(options)) {
|
|
5437
5637
|
return null;
|
|
5438
5638
|
}
|
|
@@ -5475,8 +5675,8 @@ async function runRecoveryMenuIfApplicable(options, dependencies) {
|
|
|
5475
5675
|
}
|
|
5476
5676
|
|
|
5477
5677
|
// src/workflow-data/resolve.ts
|
|
5478
|
-
import { cp, mkdir as
|
|
5479
|
-
import { dirname as
|
|
5678
|
+
import { cp, mkdir as mkdir5, readdir as readdir8, readFile as readFile16, rm as rm8, stat as stat6 } from "node:fs/promises";
|
|
5679
|
+
import { dirname as dirname5, join as join18, relative as relative2 } from "node:path";
|
|
5480
5680
|
import { fileURLToPath } from "node:url";
|
|
5481
5681
|
var WorkflowDataResolutionError = class extends Error {
|
|
5482
5682
|
code;
|
|
@@ -5490,7 +5690,7 @@ var WorkflowDataResolutionError = class extends Error {
|
|
|
5490
5690
|
};
|
|
5491
5691
|
async function pathExists3(path) {
|
|
5492
5692
|
try {
|
|
5493
|
-
await
|
|
5693
|
+
await stat6(path);
|
|
5494
5694
|
return true;
|
|
5495
5695
|
} catch {
|
|
5496
5696
|
return false;
|
|
@@ -5510,17 +5710,17 @@ function workflowPackageName(family, env) {
|
|
|
5510
5710
|
return `${scope}/workflow-${family}`;
|
|
5511
5711
|
}
|
|
5512
5712
|
async function listFilesRecursive(root, base = root) {
|
|
5513
|
-
const entries = await
|
|
5713
|
+
const entries = await readdir8(root, { withFileTypes: true });
|
|
5514
5714
|
const files = [];
|
|
5515
5715
|
for (const entry of entries) {
|
|
5516
|
-
const absolute =
|
|
5716
|
+
const absolute = join18(root, entry.name);
|
|
5517
5717
|
if (entry.isDirectory()) {
|
|
5518
5718
|
files.push(...await listFilesRecursive(absolute, base));
|
|
5519
5719
|
continue;
|
|
5520
5720
|
}
|
|
5521
5721
|
if (!entry.isFile()) continue;
|
|
5522
5722
|
const rel = relative2(base, absolute).replaceAll("\\", "/");
|
|
5523
|
-
files.push({ path: rel, content: await
|
|
5723
|
+
files.push({ path: rel, content: await readFile16(absolute, "utf8") });
|
|
5524
5724
|
}
|
|
5525
5725
|
return files;
|
|
5526
5726
|
}
|
|
@@ -5533,7 +5733,7 @@ async function verifyWorkflowPackageIntegrity(resourcesRoot) {
|
|
|
5533
5733
|
const manifest = await readWorkflowFamilyManifest(resourcesRoot);
|
|
5534
5734
|
const expected = manifest.content_sha256;
|
|
5535
5735
|
if (typeof expected !== "string" || expected.length === 0) return;
|
|
5536
|
-
const harnessRoot =
|
|
5736
|
+
const harnessRoot = join18(resourcesRoot, "harness");
|
|
5537
5737
|
if (!await pathExists3(harnessRoot)) {
|
|
5538
5738
|
throw new WorkflowDataResolutionError(
|
|
5539
5739
|
"\u5DE5\u4F5C\u6D41\u6570\u636E\u5305\u7F3A\u5C11 harness/\uFF0C\u65E0\u6CD5\u6821\u9A8C SHA-256",
|
|
@@ -5552,40 +5752,40 @@ async function verifyWorkflowPackageIntegrity(resourcesRoot) {
|
|
|
5552
5752
|
}
|
|
5553
5753
|
}
|
|
5554
5754
|
async function monorepoResourcesRoot() {
|
|
5555
|
-
const here =
|
|
5755
|
+
const here = dirname5(fileURLToPath(import.meta.url));
|
|
5556
5756
|
const candidates = [
|
|
5557
5757
|
// TypeScript source: packages/cli/src/workflow-data -> packages/workflow-data-harness.
|
|
5558
|
-
|
|
5758
|
+
join18(here, "../../../workflow-data-harness"),
|
|
5559
5759
|
// Bundled CLI: packages/cli/dist -> packages/workflow-data-harness.
|
|
5560
|
-
|
|
5760
|
+
join18(here, "../../workflow-data-harness"),
|
|
5561
5761
|
// Test/build layouts that preserve additional source directory levels.
|
|
5562
|
-
|
|
5762
|
+
join18(here, "../../../../packages/workflow-data-harness")
|
|
5563
5763
|
];
|
|
5564
5764
|
for (const candidate of candidates) {
|
|
5565
|
-
if (await pathExists3(
|
|
5765
|
+
if (await pathExists3(join18(candidate, "harness", "manifests"))) return candidate;
|
|
5566
5766
|
}
|
|
5567
5767
|
return null;
|
|
5568
5768
|
}
|
|
5569
5769
|
async function siblingWorkflowPackage(cwd) {
|
|
5570
|
-
const here =
|
|
5770
|
+
const here = dirname5(fileURLToPath(import.meta.url));
|
|
5571
5771
|
const candidates = [
|
|
5572
|
-
|
|
5772
|
+
join18(cwd, "node_modules", "@hunter-harness", "workflow-harness"),
|
|
5573
5773
|
// Published layout: node_modules/hunter-harness/dist/bin.js alongside the
|
|
5574
5774
|
// scoped workflow package in node_modules/@hunter-harness/workflow-harness.
|
|
5575
|
-
|
|
5775
|
+
join18(here, "..", "..", "@hunter-harness", "workflow-harness"),
|
|
5576
5776
|
// Monorepo bundled layout: packages/cli/dist/bin.js.
|
|
5577
|
-
|
|
5777
|
+
join18(here, "..", "..", "workflow-data-harness")
|
|
5578
5778
|
];
|
|
5579
5779
|
for (const candidate of candidates) {
|
|
5580
|
-
if (await pathExists3(
|
|
5780
|
+
if (await pathExists3(join18(candidate, "harness", "manifests"))) return candidate;
|
|
5581
5781
|
}
|
|
5582
5782
|
return null;
|
|
5583
5783
|
}
|
|
5584
5784
|
async function readCachedNpmPackageVersion(cacheRoot) {
|
|
5585
|
-
const manifestPath =
|
|
5785
|
+
const manifestPath = join18(cacheRoot, "package.json");
|
|
5586
5786
|
if (!await pathExists3(manifestPath)) return null;
|
|
5587
5787
|
try {
|
|
5588
|
-
const pkg = JSON.parse(await
|
|
5788
|
+
const pkg = JSON.parse(await readFile16(manifestPath, "utf8"));
|
|
5589
5789
|
return typeof pkg.version === "string" && pkg.version.length > 0 ? pkg.version : null;
|
|
5590
5790
|
} catch {
|
|
5591
5791
|
return null;
|
|
@@ -5607,13 +5807,13 @@ async function latestWorkflowCacheIsStale(cacheRoot, packageName, resolveManifes
|
|
|
5607
5807
|
}
|
|
5608
5808
|
}
|
|
5609
5809
|
async function materializeWorkflowPackageCache(cacheRoot, packageSpec, extract) {
|
|
5610
|
-
await
|
|
5611
|
-
const staging =
|
|
5810
|
+
await mkdir5(cacheRoot, { recursive: true });
|
|
5811
|
+
const staging = join18(cacheRoot, ".extract");
|
|
5612
5812
|
await rm8(staging, { recursive: true, force: true });
|
|
5613
|
-
await
|
|
5813
|
+
await mkdir5(staging, { recursive: true });
|
|
5614
5814
|
await extract(packageSpec, staging);
|
|
5615
|
-
const packageDir = await pathExists3(
|
|
5616
|
-
if (!await pathExists3(
|
|
5815
|
+
const packageDir = await pathExists3(join18(staging, "harness", "manifests")) ? staging : join18(staging, "package");
|
|
5816
|
+
if (!await pathExists3(join18(packageDir, "harness", "manifests"))) {
|
|
5617
5817
|
throw new WorkflowDataResolutionError(
|
|
5618
5818
|
"\u5DE5\u4F5C\u6D41\u6570\u636E\u5305\u5185\u5BB9\u65E0\u6548\uFF1A\u7F3A\u5C11 harness/manifests",
|
|
5619
5819
|
"WORKFLOW_DATA_INVALID",
|
|
@@ -5642,8 +5842,8 @@ async function resolveWorkflowResourcesRoot(options, argv = []) {
|
|
|
5642
5842
|
const packageName = workflowPackageName(family, options.env);
|
|
5643
5843
|
const packageSpec = version === "latest" ? packageName : `${packageName}@${version}`;
|
|
5644
5844
|
const cacheKey = packageSpec.replace("/", "+");
|
|
5645
|
-
const cacheRoot =
|
|
5646
|
-
if (await pathExists3(
|
|
5845
|
+
const cacheRoot = join18(options.cwd, ".harness", "cache", "workflow-packages", cacheKey);
|
|
5846
|
+
if (await pathExists3(join18(cacheRoot, "harness", "manifests"))) {
|
|
5647
5847
|
if (version === "latest") {
|
|
5648
5848
|
const stale = await latestWorkflowCacheIsStale(cacheRoot, packageName, options.pacoteManifest);
|
|
5649
5849
|
if (stale) {
|
|
@@ -5689,9 +5889,9 @@ function describeWorkflowDataFetchFailure(error, packageSpec) {
|
|
|
5689
5889
|
return `\u65E0\u6CD5\u83B7\u53D6\u5DE5\u4F5C\u6D41\u6570\u636E\u5305 ${packageSpec}\uFF1A\u672C\u5730\u7F13\u5B58\u4E0D\u5B58\u5728\u4E14\u83B7\u53D6\u5931\u8D25\u3002\u53EF${hintRoot}\u3002\u539F\u56E0\uFF1A${code !== "" ? code + " " : ""}${detail}`;
|
|
5690
5890
|
}
|
|
5691
5891
|
async function readWorkflowFamilyManifest(resourcesRoot) {
|
|
5692
|
-
const manifestPath =
|
|
5892
|
+
const manifestPath = join18(resourcesRoot, "hunter-workflow-family.json");
|
|
5693
5893
|
if (!await pathExists3(manifestPath)) return {};
|
|
5694
|
-
return JSON.parse(await
|
|
5894
|
+
return JSON.parse(await readFile16(manifestPath, "utf8"));
|
|
5695
5895
|
}
|
|
5696
5896
|
|
|
5697
5897
|
// src/bin.ts
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
{
|
|
2
2
|
"name": "hunter-harness",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "Local-first, server-governed agent harness",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
8
|
-
"node": ">=
|
|
8
|
+
"node": ">=22.12.0"
|
|
9
9
|
},
|
|
10
10
|
"bin": {
|
|
11
11
|
"hunter-harness": "dist/bin.js"
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json && npm run bundle",
|
|
18
|
-
"bundle": "node ../../scripts/bundle-cli.mjs",
|
|
18
|
+
"bundle": "node ../../scripts/bundle-cli.mjs",
|
|
19
19
|
"typecheck": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json --noEmit",
|
|
20
20
|
"prepack": "npm run build"
|
|
21
21
|
},
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"zod": "4.4.3"
|
|
26
26
|
},
|
|
27
27
|
"optionalDependencies": {
|
|
28
|
-
"pacote": "
|
|
28
|
+
"pacote": "21.4.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@hunter-harness/contracts": "*",
|