hunter-harness 0.2.10 → 0.2.12
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 +309 -80
- 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) {
|
|
@@ -1230,6 +1230,9 @@ var HunterHarnessApiClient = class {
|
|
|
1230
1230
|
body
|
|
1231
1231
|
});
|
|
1232
1232
|
}
|
|
1233
|
+
async getProject(projectId, requestId) {
|
|
1234
|
+
return this.request("GET", "/api/v1/projects/" + encodeURIComponent(projectId), { requestId });
|
|
1235
|
+
}
|
|
1233
1236
|
async createProposalSession(projectId, body, requestId, idempotencyKey) {
|
|
1234
1237
|
return this.request("POST", "/api/v1/projects/" + encodeURIComponent(projectId) + "/proposal-sessions", { requestId, idempotencyKey, body });
|
|
1235
1238
|
}
|
|
@@ -1982,6 +1985,13 @@ function makeAdapter(spec) {
|
|
|
1982
1985
|
return projectBundleFiles(bundle, spec.skillsRoot, spec.agentsRoot);
|
|
1983
1986
|
},
|
|
1984
1987
|
contextIndex(context) {
|
|
1988
|
+
if (spec.name === "codebuddy") {
|
|
1989
|
+
return {
|
|
1990
|
+
instructions: spec.instructions,
|
|
1991
|
+
skills_root: spec.skillsRoot,
|
|
1992
|
+
rules: codebuddyRulePaths(context)
|
|
1993
|
+
};
|
|
1994
|
+
}
|
|
1985
1995
|
const rules = [];
|
|
1986
1996
|
if (spec.rulesRoot !== null && spec.ruleExt !== null) {
|
|
1987
1997
|
rules.push(`${spec.rulesRoot}/harness-general${spec.ruleExt}`);
|
|
@@ -2001,6 +2011,9 @@ function makeAdapter(spec) {
|
|
|
2001
2011
|
boundaries.push(spec.agentsRoot);
|
|
2002
2012
|
if (spec.rulesRoot !== null)
|
|
2003
2013
|
boundaries.push(spec.rulesRoot);
|
|
2014
|
+
if (spec.name === "codebuddy") {
|
|
2015
|
+
boundaries.push(".codebuddy/.rules", ".codebuddy/rules");
|
|
2016
|
+
}
|
|
2004
2017
|
const top = spec.skillsRoot.split("/")[0];
|
|
2005
2018
|
if (top !== void 0)
|
|
2006
2019
|
boundaries.push(top);
|
|
@@ -2053,6 +2066,17 @@ var ADAPTERS = {
|
|
|
2053
2066
|
function getAdapter(name) {
|
|
2054
2067
|
return ADAPTERS[name];
|
|
2055
2068
|
}
|
|
2069
|
+
function codebuddyRulePaths(context) {
|
|
2070
|
+
const names = context.profile === "java" ? ["harness-general", "harness-profile-java"] : ["harness-general"];
|
|
2071
|
+
const paths = [];
|
|
2072
|
+
if (context.codebuddySurface !== "cli") {
|
|
2073
|
+
paths.push(...names.map((name) => `.codebuddy/.rules/${name}.mdc`));
|
|
2074
|
+
}
|
|
2075
|
+
if (context.codebuddySurface !== "ide") {
|
|
2076
|
+
paths.push(...names.map((name) => `.codebuddy/rules/${name}.md`));
|
|
2077
|
+
}
|
|
2078
|
+
return paths.sort((left, right) => left.localeCompare(right));
|
|
2079
|
+
}
|
|
2056
2080
|
function getAdapters(names) {
|
|
2057
2081
|
return HARNESS_AGENT_ORDER.filter((n) => names.includes(n)).map(getAdapter);
|
|
2058
2082
|
}
|
|
@@ -2068,6 +2092,13 @@ function managedTargetsFor(adapter, bundle, context) {
|
|
|
2068
2092
|
if (context.profile === "java") {
|
|
2069
2093
|
records.push(ruleTarget("rules/harness-profile-java.mdc", ".cursor/rules/harness-profile-java.mdc", CURSOR_JAVA_RULES_CONTENT));
|
|
2070
2094
|
}
|
|
2095
|
+
} else if (adapter.name === "codebuddy") {
|
|
2096
|
+
for (const targetPath of codebuddyRulePaths(context)) {
|
|
2097
|
+
const isMdc = targetPath.endsWith(".mdc");
|
|
2098
|
+
const isJava = targetPath.includes("harness-profile-java");
|
|
2099
|
+
const content = isJava ? isMdc ? CURSOR_JAVA_RULES_CONTENT : HARNESS_JAVA_RULES_CONTENT : isMdc ? CURSOR_GENERAL_RULES_CONTENT : HARNESS_GENERAL_RULES_CONTENT;
|
|
2100
|
+
records.push(ruleTarget(`rules/${isJava ? "harness-profile-java" : "harness-general"}${isMdc ? ".mdc" : ".md"}`, targetPath, content));
|
|
2101
|
+
}
|
|
2071
2102
|
}
|
|
2072
2103
|
assertNoCaseCollisions(records.map((record) => record.target_path));
|
|
2073
2104
|
return records.sort((left, right) => left.target_path.localeCompare(right.target_path));
|
|
@@ -3704,6 +3735,10 @@ function assertPreviewAllowed(preview, skip) {
|
|
|
3704
3735
|
}
|
|
3705
3736
|
}
|
|
3706
3737
|
var CREDENTIALS_HINT = "\u53EF\u5728\u4EA4\u4E92\u6A21\u5F0F\u4E0B\u5F55\u5165\uFF0C\u6216\u5199\u5165 .harness/credentials.local.yaml\uFF08\u52FF\u63D0\u4EA4 git\uFF09";
|
|
3738
|
+
var STALE_BASELINE_MESSAGE = "\u670D\u52A1\u7AEF\u5DF2\u6709\u66F4\u65B0\u7684\u63A8\u9001\uFF0C\u8BF7\u5148 git pull \u540E\u6267\u884C npx hunter-harness update \u518D\u63A8";
|
|
3739
|
+
function staleBaselineError(code) {
|
|
3740
|
+
return new PushWorkflowError(STALE_BASELINE_MESSAGE, 5, code);
|
|
3741
|
+
}
|
|
3707
3742
|
function makePreview(baseline, files, confirmedProjectLocal, ignorePaths, deletedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
3708
3743
|
const filteredFiles = {};
|
|
3709
3744
|
for (const [path, content] of Object.entries(files)) {
|
|
@@ -3748,14 +3783,12 @@ async function pushProject(options) {
|
|
|
3748
3783
|
const profile = parseHarnessProfile(project.project.profiles[0]);
|
|
3749
3784
|
const installedPaths = profile === null ? /* @__PURE__ */ new Set() : new Set(await Promise.all(enabledHarnessAgents(project).map((agent) => managedBundleTargets(options.resourcesRoot, profile, agent))).then((targets) => targets.flatMap((target) => [...target])));
|
|
3750
3785
|
let preview = makePreview(baseline, await managedFiles(root, project), options.confirmedProjectLocal ?? [], installedPaths);
|
|
3751
|
-
const initialSkip = await resolveSensitiveScanSkip(preview, options);
|
|
3752
|
-
if (initialSkip.cancelled === true) {
|
|
3753
|
-
return { preview, proposalId: null, projectId: project.project.project_id, cancelled: true };
|
|
3754
|
-
}
|
|
3755
|
-
let sensitiveScanSkip = initialSkip.skip;
|
|
3756
|
-
let sensitiveScanSkipReason = initialSkip.reason;
|
|
3757
|
-
assertPreviewAllowed(preview, sensitiveScanSkip);
|
|
3758
3786
|
if (options.dryRun) {
|
|
3787
|
+
const drySkip = await resolveSensitiveScanSkip(preview, options);
|
|
3788
|
+
if (drySkip.cancelled === true) {
|
|
3789
|
+
return { preview, proposalId: null, projectId: project.project.project_id, cancelled: true };
|
|
3790
|
+
}
|
|
3791
|
+
assertPreviewAllowed(preview, drySkip.skip);
|
|
3759
3792
|
return { preview, proposalId: null, projectId: project.project.project_id };
|
|
3760
3793
|
}
|
|
3761
3794
|
const localCredentials = await readLocalCredentials(root);
|
|
@@ -3789,6 +3822,25 @@ async function pushProject(options) {
|
|
|
3789
3822
|
if (parsedServerUrl.protocol !== "https:") {
|
|
3790
3823
|
throw new PushWorkflowError("server_url must use HTTPS", 3, "SERVER_URL_INVALID");
|
|
3791
3824
|
}
|
|
3825
|
+
const client = new HunterHarnessApiClient({
|
|
3826
|
+
serverUrl: parsedServerUrl.toString(),
|
|
3827
|
+
token,
|
|
3828
|
+
...options.fetch === void 0 ? {} : { fetch: options.fetch }
|
|
3829
|
+
});
|
|
3830
|
+
const boundAtStart = project.project.project_id;
|
|
3831
|
+
if (boundAtStart !== null) {
|
|
3832
|
+
const remote = await client.getProject(boundAtStart, uuidV7());
|
|
3833
|
+
if (remote.latest_project_version !== baseline.complete_project_version) {
|
|
3834
|
+
throw staleBaselineError("PROJECT_VERSION_CONFLICT");
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
const initialSkip = await resolveSensitiveScanSkip(preview, options);
|
|
3838
|
+
if (initialSkip.cancelled === true) {
|
|
3839
|
+
return { preview, proposalId: null, projectId: project.project.project_id, cancelled: true };
|
|
3840
|
+
}
|
|
3841
|
+
let sensitiveScanSkip = initialSkip.skip;
|
|
3842
|
+
let sensitiveScanSkipReason = initialSkip.reason;
|
|
3843
|
+
assertPreviewAllowed(preview, sensitiveScanSkip);
|
|
3792
3844
|
if (options.confirmProposal !== void 0 && !await options.confirmProposal(preview)) {
|
|
3793
3845
|
return { preview, proposalId: null, projectId: project.project.project_id, cancelled: true };
|
|
3794
3846
|
}
|
|
@@ -3803,11 +3855,6 @@ async function pushProject(options) {
|
|
|
3803
3855
|
await atomicWriteJson(workflowPath, workflow);
|
|
3804
3856
|
preview = makePreview(baseline, await managedFiles(root, project), options.confirmedProjectLocal ?? [], installedPaths, workflow.created_at);
|
|
3805
3857
|
const requestId = workflow.request_id;
|
|
3806
|
-
const client = new HunterHarnessApiClient({
|
|
3807
|
-
serverUrl: parsedServerUrl.toString(),
|
|
3808
|
-
token,
|
|
3809
|
-
...options.fetch === void 0 ? {} : { fetch: options.fetch }
|
|
3810
|
-
});
|
|
3811
3858
|
if (project.project.project_id === null) {
|
|
3812
3859
|
const resolved = await client.resolveProject({
|
|
3813
3860
|
schema_version: 1,
|
|
@@ -3914,8 +3961,8 @@ async function pushProject(options) {
|
|
|
3914
3961
|
throw error;
|
|
3915
3962
|
}
|
|
3916
3963
|
if (error instanceof ApiError) {
|
|
3917
|
-
if (error.code === "STALE_PUSH") {
|
|
3918
|
-
throw
|
|
3964
|
+
if (error.code === "STALE_PUSH" || error.code === "PROJECT_VERSION_CONFLICT") {
|
|
3965
|
+
throw staleBaselineError(error.code);
|
|
3919
3966
|
}
|
|
3920
3967
|
if (error.code === "SENSITIVE_CONTENT_BLOCKED") {
|
|
3921
3968
|
const details = error.details;
|
|
@@ -4107,8 +4154,8 @@ async function rollbackLatestCommittedUpdate(projectRoot) {
|
|
|
4107
4154
|
const after = JSON.parse(await readFile11(join12(transactionRoot, "after", "manifest.json"), "utf8"));
|
|
4108
4155
|
for (const entry of after) {
|
|
4109
4156
|
const target = join12(projectRoot, entry.path);
|
|
4110
|
-
const
|
|
4111
|
-
if (
|
|
4157
|
+
const exists6 = await pathExists(target);
|
|
4158
|
+
if (exists6 !== entry.exists || exists6 && await sha256File(target) !== entry.hash) {
|
|
4112
4159
|
throw new Error("cannot rollback dirty path: " + entry.path);
|
|
4113
4160
|
}
|
|
4114
4161
|
}
|
|
@@ -4120,15 +4167,15 @@ async function rollbackLatestCommittedUpdate(projectRoot) {
|
|
|
4120
4167
|
}
|
|
4121
4168
|
seen.add(snapshot.path);
|
|
4122
4169
|
const target = join12(projectRoot, snapshot.path);
|
|
4123
|
-
const
|
|
4170
|
+
const exists6 = await pathExists(target);
|
|
4124
4171
|
if (snapshot.existed && snapshot.snapshot_name !== null) {
|
|
4125
4172
|
const content = await readFile11(join12(transactionRoot, "before", snapshot.snapshot_name));
|
|
4126
4173
|
operations.push({
|
|
4127
|
-
operation:
|
|
4174
|
+
operation: exists6 ? "modify" : "add",
|
|
4128
4175
|
path: snapshot.path,
|
|
4129
4176
|
content
|
|
4130
4177
|
});
|
|
4131
|
-
} else if (
|
|
4178
|
+
} else if (exists6) {
|
|
4132
4179
|
operations.push({ operation: "delete", path: snapshot.path });
|
|
4133
4180
|
}
|
|
4134
4181
|
}
|
|
@@ -4286,17 +4333,20 @@ async function updateProject(options) {
|
|
|
4286
4333
|
if (project.project.project_id === null) {
|
|
4287
4334
|
throw new UpdateWorkflowError("project is not bound to a server", 3, "PROJECT_NOT_BOUND");
|
|
4288
4335
|
}
|
|
4289
|
-
const serverUrl = options.serverUrl ?? project.server.url;
|
|
4290
4336
|
const tokenEnv = options.tokenEnv ?? project.server.token_env;
|
|
4291
|
-
if (serverUrl === null || serverUrl === void 0) {
|
|
4292
|
-
throw new UpdateWorkflowError("server_url is required", 3, "SERVER_URL_REQUIRED");
|
|
4293
|
-
}
|
|
4294
4337
|
if (!/^[A-Z_][A-Z0-9_]*$/.test(tokenEnv)) {
|
|
4295
4338
|
throw new UpdateWorkflowError("token_env is invalid", 3, "TOKEN_ENV_INVALID");
|
|
4296
4339
|
}
|
|
4297
|
-
const
|
|
4298
|
-
|
|
4299
|
-
|
|
4340
|
+
const local = await readLocalCredentials(root);
|
|
4341
|
+
const serverUrl = options.serverUrl ?? local?.server_url ?? project.server.url;
|
|
4342
|
+
if (serverUrl === null || serverUrl === void 0) {
|
|
4343
|
+
throw new UpdateWorkflowError("server_url is required", 3, "SERVER_URL_REQUIRED");
|
|
4344
|
+
}
|
|
4345
|
+
const envToken = options.env[tokenEnv]?.trim();
|
|
4346
|
+
const localToken = local?.token?.trim();
|
|
4347
|
+
const token = envToken !== void 0 && envToken !== "" ? envToken : localToken !== void 0 && localToken !== "" ? localToken : void 0;
|
|
4348
|
+
if (token === void 0) {
|
|
4349
|
+
throw new UpdateWorkflowError(`API token \u672A\u914D\u7F6E\uFF1A\u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF ${tokenEnv}\uFF0C\u6216\u5728 .harness/credentials.local.yaml \u914D\u7F6E token\uFF08\u53EF\u901A\u8FC7 npx hunter-harness push \u5F15\u5BFC\u5199\u5165\uFF09`, 8, "TOKEN_INVALID");
|
|
4300
4350
|
}
|
|
4301
4351
|
const requestId = uuidV7();
|
|
4302
4352
|
const baseline = await readBaseline(root);
|
|
@@ -4541,13 +4591,15 @@ function normalizeProfile(value) {
|
|
|
4541
4591
|
if (value === "2" || value === "java") return "java";
|
|
4542
4592
|
throw new InitConfigurationError("\u914D\u7F6E\u7C7B\u578B\u5FC5\u987B\u4E3A general \u6216 java");
|
|
4543
4593
|
}
|
|
4594
|
+
var ALL_AGENT_TOKENS = /* @__PURE__ */ new Set(["all", "5"]);
|
|
4544
4595
|
function parseAgentsInput(raw) {
|
|
4545
4596
|
const trimmed = raw.trim();
|
|
4546
4597
|
if (trimmed === "") return ["claude-code"];
|
|
4547
|
-
if (trimmed
|
|
4598
|
+
if (ALL_AGENT_TOKENS.has(trimmed)) return [...HARNESS_AGENT_ORDER];
|
|
4548
4599
|
const agents = [];
|
|
4549
4600
|
for (const token of trimmed.split(",")) {
|
|
4550
4601
|
const value = token.trim();
|
|
4602
|
+
if (ALL_AGENT_TOKENS.has(value)) return [...HARNESS_AGENT_ORDER];
|
|
4551
4603
|
const byIndex = AGENT_BY_INDEX[value];
|
|
4552
4604
|
if (byIndex !== void 0) {
|
|
4553
4605
|
agents.push(byIndex);
|
|
@@ -4672,14 +4724,118 @@ function serializeCliResult(result) {
|
|
|
4672
4724
|
return JSON.stringify(result) + "\n";
|
|
4673
4725
|
}
|
|
4674
4726
|
|
|
4727
|
+
// src/config/codebuddy-setup.ts
|
|
4728
|
+
import { mkdir as mkdir4, readFile as readFile14, readdir as readdir7, stat as stat4, writeFile as writeFile5 } from "node:fs/promises";
|
|
4729
|
+
import { basename as basename2, dirname as dirname4, extname, join as join15 } from "node:path";
|
|
4730
|
+
var MANAGED_RULE_NAMES = /* @__PURE__ */ new Set([
|
|
4731
|
+
"harness-general.md",
|
|
4732
|
+
"harness-general.mdc",
|
|
4733
|
+
"harness-profile-java.md",
|
|
4734
|
+
"harness-profile-java.mdc"
|
|
4735
|
+
]);
|
|
4736
|
+
var SENSITIVE_ASSIGNMENT = /(?:password|passwd|token|secret|access[_-]?key|private[_-]?key)\s*[:=]\s*[^\s#]+/i;
|
|
4737
|
+
var PRIVATE_KEY_BLOCK = /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/i;
|
|
4738
|
+
async function exists4(path) {
|
|
4739
|
+
try {
|
|
4740
|
+
await stat4(path);
|
|
4741
|
+
return true;
|
|
4742
|
+
} catch {
|
|
4743
|
+
return false;
|
|
4744
|
+
}
|
|
4745
|
+
}
|
|
4746
|
+
async function readJsonObject(path) {
|
|
4747
|
+
try {
|
|
4748
|
+
const parsed = JSON.parse(await readFile14(path, "utf8"));
|
|
4749
|
+
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
4750
|
+
} catch (error) {
|
|
4751
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return {};
|
|
4752
|
+
return null;
|
|
4753
|
+
}
|
|
4754
|
+
}
|
|
4755
|
+
async function inspectCodeBuddySetup(projectRoot) {
|
|
4756
|
+
const rulesRoot = join15(projectRoot, ".claude", "rules");
|
|
4757
|
+
let claudeRules = [];
|
|
4758
|
+
try {
|
|
4759
|
+
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();
|
|
4760
|
+
} catch (error) {
|
|
4761
|
+
if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) throw error;
|
|
4762
|
+
}
|
|
4763
|
+
const mcp = await readJsonObject(join15(projectRoot, ".mcp.json"));
|
|
4764
|
+
const servers = mcp?.mcpServers;
|
|
4765
|
+
const configured = servers !== null && typeof servers === "object" && !Array.isArray(servers) && Object.prototype.hasOwnProperty.call(servers, "codegraph");
|
|
4766
|
+
return {
|
|
4767
|
+
claudeRules,
|
|
4768
|
+
hasCodeGraphIndex: await exists4(join15(projectRoot, ".codegraph")),
|
|
4769
|
+
codeGraphConfigured: configured
|
|
4770
|
+
};
|
|
4771
|
+
}
|
|
4772
|
+
function destinationTargets(root, surface, name) {
|
|
4773
|
+
const stem = basename2(name, extname(name));
|
|
4774
|
+
const targets = [];
|
|
4775
|
+
if (surface !== "cli") targets.push(join15(root, ".codebuddy", ".rules", `${stem}.mdc`));
|
|
4776
|
+
if (surface !== "ide") targets.push(join15(root, ".codebuddy", "rules", `${stem}.md`));
|
|
4777
|
+
return targets;
|
|
4778
|
+
}
|
|
4779
|
+
async function applyCodeBuddySetup(options) {
|
|
4780
|
+
const result = {
|
|
4781
|
+
copied: [],
|
|
4782
|
+
preserved: [],
|
|
4783
|
+
skippedSensitive: [],
|
|
4784
|
+
mcpUpdated: false,
|
|
4785
|
+
warnings: []
|
|
4786
|
+
};
|
|
4787
|
+
if (options.syncClaudeRules) {
|
|
4788
|
+
const plan = await inspectCodeBuddySetup(options.projectRoot);
|
|
4789
|
+
for (const name of plan.claudeRules) {
|
|
4790
|
+
const source = join15(options.projectRoot, ".claude", "rules", name);
|
|
4791
|
+
const content = await readFile14(source, "utf8");
|
|
4792
|
+
if (SENSITIVE_ASSIGNMENT.test(content) || PRIVATE_KEY_BLOCK.test(content)) {
|
|
4793
|
+
result.skippedSensitive.push(name);
|
|
4794
|
+
continue;
|
|
4795
|
+
}
|
|
4796
|
+
for (const target of destinationTargets(options.projectRoot, options.surface, name)) {
|
|
4797
|
+
if (await exists4(target)) {
|
|
4798
|
+
result.preserved.push(target);
|
|
4799
|
+
continue;
|
|
4800
|
+
}
|
|
4801
|
+
await mkdir4(dirname4(target), { recursive: true });
|
|
4802
|
+
await writeFile5(target, content, { encoding: "utf8", flag: "wx" });
|
|
4803
|
+
result.copied.push(target);
|
|
4804
|
+
}
|
|
4805
|
+
}
|
|
4806
|
+
}
|
|
4807
|
+
if (options.configureCodeGraph) {
|
|
4808
|
+
const path = join15(options.projectRoot, ".mcp.json");
|
|
4809
|
+
const current = await readJsonObject(path);
|
|
4810
|
+
if (current === null) {
|
|
4811
|
+
result.warnings.push(".mcp.json \u4E0D\u662F\u6709\u6548 JSON\uFF0C\u5DF2\u4FDD\u7559\u539F\u6587\u4EF6\u5E76\u8DF3\u8FC7 CodeGraph MCP \u914D\u7F6E");
|
|
4812
|
+
} else {
|
|
4813
|
+
const currentServers = current.mcpServers;
|
|
4814
|
+
const servers = currentServers !== null && typeof currentServers === "object" && !Array.isArray(currentServers) ? currentServers : {};
|
|
4815
|
+
if (!Object.prototype.hasOwnProperty.call(servers, "codegraph")) {
|
|
4816
|
+
const next = {
|
|
4817
|
+
...current,
|
|
4818
|
+
mcpServers: {
|
|
4819
|
+
...servers,
|
|
4820
|
+
codegraph: { command: "codegraph", args: ["serve", "--mcp"] }
|
|
4821
|
+
}
|
|
4822
|
+
};
|
|
4823
|
+
await writeFile5(path, JSON.stringify(next, null, 2) + "\n", "utf8");
|
|
4824
|
+
result.mcpUpdated = true;
|
|
4825
|
+
}
|
|
4826
|
+
}
|
|
4827
|
+
}
|
|
4828
|
+
return result;
|
|
4829
|
+
}
|
|
4830
|
+
|
|
4675
4831
|
// src/commands/refresh.ts
|
|
4676
|
-
import { readFile as
|
|
4677
|
-
import { join as
|
|
4832
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
4833
|
+
import { join as join16 } from "node:path";
|
|
4678
4834
|
import { parse as parseYaml9 } from "yaml";
|
|
4679
4835
|
async function detectProject(root) {
|
|
4680
4836
|
let content;
|
|
4681
4837
|
try {
|
|
4682
|
-
content = await
|
|
4838
|
+
content = await readFile15(join16(root, ".harness", "project.yaml"), "utf8");
|
|
4683
4839
|
} catch (error) {
|
|
4684
4840
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
4685
4841
|
return { status: "absent" };
|
|
@@ -4707,8 +4863,14 @@ function refreshAgents(config) {
|
|
|
4707
4863
|
}));
|
|
4708
4864
|
return agents.length > 0 ? agents : ["claude-code"];
|
|
4709
4865
|
}
|
|
4710
|
-
function codebuddySurface(config) {
|
|
4711
|
-
|
|
4866
|
+
function codebuddySurface(config, override) {
|
|
4867
|
+
const value = override ?? config.adapter_options?.codebuddy?.surface ?? "both";
|
|
4868
|
+
if (value === "both" || value === "ide" || value === "cli") return value;
|
|
4869
|
+
throw new InitConfigurationError(
|
|
4870
|
+
"codebuddy surface \u5FC5\u987B\u4E3A both\u3001ide \u6216 cli",
|
|
4871
|
+
3,
|
|
4872
|
+
"CODEBUDDY_SURFACE_INVALID"
|
|
4873
|
+
);
|
|
4712
4874
|
}
|
|
4713
4875
|
function summarize(result) {
|
|
4714
4876
|
const items = [
|
|
@@ -4795,7 +4957,7 @@ async function runRefresh(options, dependencies) {
|
|
|
4795
4957
|
resourcesRoot: dependencies.resourcesRoot,
|
|
4796
4958
|
...targetProfile === void 0 ? {} : { profile: targetProfile },
|
|
4797
4959
|
agents: targetAgents,
|
|
4798
|
-
codebuddySurface: codebuddySurface(detection.config),
|
|
4960
|
+
codebuddySurface: codebuddySurface(detection.config, options.codebuddySurface),
|
|
4799
4961
|
dryRun: true,
|
|
4800
4962
|
forceManaged: options.forceManaged === true
|
|
4801
4963
|
});
|
|
@@ -4831,7 +4993,7 @@ async function runRefresh(options, dependencies) {
|
|
|
4831
4993
|
resourcesRoot: dependencies.resourcesRoot,
|
|
4832
4994
|
...targetProfile === void 0 ? {} : { profile: targetProfile },
|
|
4833
4995
|
agents: targetAgents,
|
|
4834
|
-
codebuddySurface: codebuddySurface(detection.config),
|
|
4996
|
+
codebuddySurface: codebuddySurface(detection.config, options.codebuddySurface),
|
|
4835
4997
|
dryRun,
|
|
4836
4998
|
forceManaged: options.forceManaged === true
|
|
4837
4999
|
});
|
|
@@ -4880,6 +5042,54 @@ var AGENT_LABELS = {
|
|
|
4880
5042
|
cursor: "Cursor",
|
|
4881
5043
|
codebuddy: "CodeBuddy"
|
|
4882
5044
|
};
|
|
5045
|
+
function agentMenuLines(installedProfiles) {
|
|
5046
|
+
const lines = HARNESS_AGENT_ORDER.map((agent, index) => {
|
|
5047
|
+
const profile = installedProfiles?.[agent];
|
|
5048
|
+
const suffix = profile === void 0 ? "" : `\uFF08\u5DF2\u5B89\u88C5\uFF1A${profile}\uFF09`;
|
|
5049
|
+
return ` ${index + 1}. ${AGENT_LABELS[agent]}${suffix}`;
|
|
5050
|
+
}).join("\n");
|
|
5051
|
+
return lines + "\n 5. \u5168\u90E8";
|
|
5052
|
+
}
|
|
5053
|
+
async function configureCodeBuddyExtras(agents, surface, options, dependencies) {
|
|
5054
|
+
if (!agents.includes("codebuddy")) return;
|
|
5055
|
+
const plan = await inspectCodeBuddySetup(dependencies.cwd);
|
|
5056
|
+
let syncClaudeRules = false;
|
|
5057
|
+
let configureCodeGraph = false;
|
|
5058
|
+
if (plan.claudeRules.length > 0) {
|
|
5059
|
+
syncClaudeRules = options.nonInteractive === true ? options.yes === true : /^(?:|y|yes)$/i.test((await dependencies.prompt(
|
|
5060
|
+
`\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`
|
|
5061
|
+
)).trim());
|
|
5062
|
+
}
|
|
5063
|
+
if (plan.hasCodeGraphIndex && !plan.codeGraphConfigured) {
|
|
5064
|
+
configureCodeGraph = options.nonInteractive === true ? options.yes === true : /^(?:|y|yes)$/i.test((await dependencies.prompt(
|
|
5065
|
+
"\u68C0\u6D4B\u5230 .codegraph \u7D22\u5F15\uFF0C\u662F\u5426\u5408\u5E76 CodeGraph MCP \u5230\u9879\u76EE .mcp.json\uFF1F[Y/n]\uFF1A"
|
|
5066
|
+
)).trim());
|
|
5067
|
+
}
|
|
5068
|
+
if (options.dryRun === true) {
|
|
5069
|
+
if (syncClaudeRules || configureCodeGraph) {
|
|
5070
|
+
dependencies.stdout("CodeBuddy \u9644\u52A0\u914D\u7F6E\u5904\u4E8E dry-run\uFF0C\u672A\u5199\u5165\u89C4\u5219\u6216 .mcp.json\u3002\n");
|
|
5071
|
+
}
|
|
5072
|
+
return;
|
|
5073
|
+
}
|
|
5074
|
+
const result = await applyCodeBuddySetup({
|
|
5075
|
+
projectRoot: dependencies.cwd,
|
|
5076
|
+
surface,
|
|
5077
|
+
syncClaudeRules,
|
|
5078
|
+
configureCodeGraph
|
|
5079
|
+
});
|
|
5080
|
+
if (result.copied.length > 0) {
|
|
5081
|
+
dependencies.stdout(`\u5DF2\u5B89\u5168\u590D\u5236 ${result.copied.length} \u4E2A CodeBuddy \u89C4\u5219\u6587\u4EF6\u3002
|
|
5082
|
+
`);
|
|
5083
|
+
}
|
|
5084
|
+
if (result.skippedSensitive.length > 0) {
|
|
5085
|
+
dependencies.stderr(
|
|
5086
|
+
`\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
|
|
5087
|
+
`
|
|
5088
|
+
);
|
|
5089
|
+
}
|
|
5090
|
+
if (result.mcpUpdated) dependencies.stdout("\u5DF2\u5408\u5E76 CodeGraph MCP \u5230 .mcp.json\u3002\n");
|
|
5091
|
+
for (const warning of result.warnings) dependencies.stderr(warning + "\n");
|
|
5092
|
+
}
|
|
4883
5093
|
async function runFirstInstall(options, dependencies) {
|
|
4884
5094
|
const requestId = uuidV7();
|
|
4885
5095
|
try {
|
|
@@ -4889,7 +5099,7 @@ async function runFirstInstall(options, dependencies) {
|
|
|
4889
5099
|
options,
|
|
4890
5100
|
options.nonInteractive === true ? {} : {
|
|
4891
5101
|
agents: () => dependencies.prompt(
|
|
4892
|
-
"\u8BF7\u9009\u62E9\u76EE\u6807 Agent\uFF08\u53EF\u591A\u9009\uFF0C\u4F7F\u7528\u9017\u53F7\u5206\u9694\uFF09\n
|
|
5102
|
+
"\u8BF7\u9009\u62E9\u76EE\u6807 Agent\uFF08\u53EF\u591A\u9009\uFF0C\u4F7F\u7528\u9017\u53F7\u5206\u9694\uFF09\n" + agentMenuLines() + "\n\u8BF7\u8F93\u5165\u7F16\u53F7 [1]: "
|
|
4893
5103
|
).then((answer) => answer.trim()),
|
|
4894
5104
|
profile: () => dependencies.prompt(
|
|
4895
5105
|
"\u8BF7\u9009\u62E9 Harness \u7C7B\u578B\uFF1A\n1. \u901A\u7528\uFF08\u9ED8\u8BA4\uFF09\n2. Java\n\u8BF7\u8F93\u5165 1 \u6216 2 [1]: "
|
|
@@ -4910,6 +5120,12 @@ async function runFirstInstall(options, dependencies) {
|
|
|
4910
5120
|
config,
|
|
4911
5121
|
dryRun: options.dryRun === true
|
|
4912
5122
|
});
|
|
5123
|
+
await configureCodeBuddyExtras(
|
|
5124
|
+
config.agents,
|
|
5125
|
+
config.codebuddy_surface,
|
|
5126
|
+
options,
|
|
5127
|
+
dependencies
|
|
5128
|
+
);
|
|
4913
5129
|
const output = {
|
|
4914
5130
|
schema_version: 1,
|
|
4915
5131
|
command: "configure",
|
|
@@ -4949,20 +5165,29 @@ async function runFirstInstall(options, dependencies) {
|
|
|
4949
5165
|
return exitCode;
|
|
4950
5166
|
}
|
|
4951
5167
|
}
|
|
4952
|
-
async function runExistingProject(options, dependencies, currentProfile) {
|
|
5168
|
+
async function runExistingProject(options, dependencies, currentProfile, currentSurface) {
|
|
4953
5169
|
const refreshOptions = {};
|
|
4954
5170
|
if (options.agents !== void 0) refreshOptions.agents = options.agents;
|
|
5171
|
+
if (options.codebuddySurface !== void 0) {
|
|
5172
|
+
refreshOptions.codebuddySurface = options.codebuddySurface;
|
|
5173
|
+
}
|
|
4955
5174
|
if (options.profile !== void 0) refreshOptions.profile = options.profile;
|
|
4956
5175
|
if (options.nonInteractive !== void 0) refreshOptions.nonInteractive = options.nonInteractive;
|
|
4957
5176
|
if (options.yes !== void 0) refreshOptions.yes = options.yes;
|
|
4958
5177
|
if (options.dryRun !== void 0) refreshOptions.dryRun = options.dryRun;
|
|
4959
5178
|
if (options.json !== void 0) refreshOptions.json = options.json;
|
|
4960
5179
|
if (options.forceManaged !== void 0) refreshOptions.forceManaged = options.forceManaged;
|
|
4961
|
-
if (options.nonInteractive === true) {
|
|
4962
|
-
return runRefresh(refreshOptions, dependencies);
|
|
4963
|
-
}
|
|
4964
5180
|
const installed = await readInstalledAgentConfiguration(dependencies.cwd);
|
|
4965
5181
|
const currentAgents = installed.agents.length > 0 ? installed.agents : ["claude-code"];
|
|
5182
|
+
if (options.nonInteractive === true) {
|
|
5183
|
+
const selectedAgents2 = options.agents === void 0 ? currentAgents : parseAgentsInput(options.agents);
|
|
5184
|
+
const code2 = await runRefresh(refreshOptions, dependencies);
|
|
5185
|
+
if (code2 === 0) {
|
|
5186
|
+
const surface = options.codebuddySurface === "ide" || options.codebuddySurface === "cli" || options.codebuddySurface === "both" ? options.codebuddySurface : currentSurface;
|
|
5187
|
+
await configureCodeBuddyExtras(selectedAgents2, surface, options, dependencies);
|
|
5188
|
+
}
|
|
5189
|
+
return code2;
|
|
5190
|
+
}
|
|
4966
5191
|
const currentLines = currentAgents.map(
|
|
4967
5192
|
(agent) => `- ${AGENT_LABELS[agent]}\uFF1A${installed.profiles[agent] ?? currentProfile}`
|
|
4968
5193
|
).join("\n");
|
|
@@ -4972,10 +5197,7 @@ async function runExistingProject(options, dependencies, currentProfile) {
|
|
|
4972
5197
|
`Hunter Harness \u5F53\u524D\u914D\u7F6E\uFF1A
|
|
4973
5198
|
${currentLines}
|
|
4974
5199
|
\u8BF7\u9009\u62E9\u672C\u6B21\u8981\u65B0\u589E\u6216\u5237\u65B0\u7684\u5DE5\u5177\uFF08\u53EF\u591A\u9009\uFF0C\u9017\u53F7\u5206\u9694\uFF1B\u672A\u9009\u62E9\u7684\u5DE5\u5177\u4FDD\u6301\u4E0D\u53D8\uFF09\uFF1A
|
|
4975
|
-
|
|
4976
|
-
2. Codex
|
|
4977
|
-
3. Cursor
|
|
4978
|
-
4. CodeBuddy
|
|
5200
|
+
` + agentMenuLines(installed.profiles) + `
|
|
4979
5201
|
\u8BF7\u8F93\u5165\u7F16\u53F7 [${defaultSelection}]\uFF0C\u6216\u8F93\u5165 0 \u53D6\u6D88\uFF1A`
|
|
4980
5202
|
);
|
|
4981
5203
|
if (answer.trim() === "0" || /^c/i.test(answer.trim())) return 2;
|
|
@@ -4997,7 +5219,13 @@ ${currentLines}
|
|
|
4997
5219
|
refreshOptions.profile = answer.trim() === "" ? defaultProfile : answer.trim();
|
|
4998
5220
|
}
|
|
4999
5221
|
refreshOptions.confirmed = true;
|
|
5000
|
-
|
|
5222
|
+
const selectedAgents = parseAgentsInput(refreshOptions.agents);
|
|
5223
|
+
const code = await runRefresh(refreshOptions, dependencies);
|
|
5224
|
+
if (code === 0) {
|
|
5225
|
+
const surface = options.codebuddySurface === "ide" || options.codebuddySurface === "cli" || options.codebuddySurface === "both" ? options.codebuddySurface : currentSurface;
|
|
5226
|
+
await configureCodeBuddyExtras(selectedAgents, surface, options, dependencies);
|
|
5227
|
+
}
|
|
5228
|
+
return code;
|
|
5001
5229
|
}
|
|
5002
5230
|
async function runConfigure(options, dependencies) {
|
|
5003
5231
|
const detection = await detectProject(dependencies.cwd);
|
|
@@ -5022,7 +5250,8 @@ async function runConfigure(options, dependencies) {
|
|
|
5022
5250
|
}
|
|
5023
5251
|
if (detection.status === "valid") {
|
|
5024
5252
|
const currentProfile = detection.config.project.profiles[0] ?? "general";
|
|
5025
|
-
|
|
5253
|
+
const currentSurface = detection.config.adapter_options?.codebuddy?.surface ?? "both";
|
|
5254
|
+
return runExistingProject(options, dependencies, currentProfile, currentSurface);
|
|
5026
5255
|
}
|
|
5027
5256
|
return runFirstInstall(options, dependencies);
|
|
5028
5257
|
}
|
|
@@ -5371,11 +5600,11 @@ async function runUpdate(options, dependencies) {
|
|
|
5371
5600
|
}
|
|
5372
5601
|
|
|
5373
5602
|
// src/commands/recovery.ts
|
|
5374
|
-
import { stat as
|
|
5375
|
-
import { join as
|
|
5376
|
-
async function
|
|
5603
|
+
import { stat as stat5 } from "node:fs/promises";
|
|
5604
|
+
import { join as join17 } from "node:path";
|
|
5605
|
+
async function exists5(path) {
|
|
5377
5606
|
try {
|
|
5378
|
-
await
|
|
5607
|
+
await stat5(path);
|
|
5379
5608
|
return true;
|
|
5380
5609
|
} catch {
|
|
5381
5610
|
return false;
|
|
@@ -5432,7 +5661,7 @@ async function runRecoveryMenuIfApplicable(options, dependencies) {
|
|
|
5432
5661
|
return 5;
|
|
5433
5662
|
}
|
|
5434
5663
|
}
|
|
5435
|
-
const initialized = await
|
|
5664
|
+
const initialized = await exists5(join17(dependencies.cwd, ".harness", "project.yaml"));
|
|
5436
5665
|
if (!initialized || options.nonInteractive === true || explicitConfigure(options)) {
|
|
5437
5666
|
return null;
|
|
5438
5667
|
}
|
|
@@ -5475,8 +5704,8 @@ async function runRecoveryMenuIfApplicable(options, dependencies) {
|
|
|
5475
5704
|
}
|
|
5476
5705
|
|
|
5477
5706
|
// src/workflow-data/resolve.ts
|
|
5478
|
-
import { cp, mkdir as
|
|
5479
|
-
import { dirname as
|
|
5707
|
+
import { cp, mkdir as mkdir5, readdir as readdir8, readFile as readFile16, rm as rm8, stat as stat6 } from "node:fs/promises";
|
|
5708
|
+
import { dirname as dirname5, join as join18, relative as relative2 } from "node:path";
|
|
5480
5709
|
import { fileURLToPath } from "node:url";
|
|
5481
5710
|
var WorkflowDataResolutionError = class extends Error {
|
|
5482
5711
|
code;
|
|
@@ -5490,7 +5719,7 @@ var WorkflowDataResolutionError = class extends Error {
|
|
|
5490
5719
|
};
|
|
5491
5720
|
async function pathExists3(path) {
|
|
5492
5721
|
try {
|
|
5493
|
-
await
|
|
5722
|
+
await stat6(path);
|
|
5494
5723
|
return true;
|
|
5495
5724
|
} catch {
|
|
5496
5725
|
return false;
|
|
@@ -5510,17 +5739,17 @@ function workflowPackageName(family, env) {
|
|
|
5510
5739
|
return `${scope}/workflow-${family}`;
|
|
5511
5740
|
}
|
|
5512
5741
|
async function listFilesRecursive(root, base = root) {
|
|
5513
|
-
const entries = await
|
|
5742
|
+
const entries = await readdir8(root, { withFileTypes: true });
|
|
5514
5743
|
const files = [];
|
|
5515
5744
|
for (const entry of entries) {
|
|
5516
|
-
const absolute =
|
|
5745
|
+
const absolute = join18(root, entry.name);
|
|
5517
5746
|
if (entry.isDirectory()) {
|
|
5518
5747
|
files.push(...await listFilesRecursive(absolute, base));
|
|
5519
5748
|
continue;
|
|
5520
5749
|
}
|
|
5521
5750
|
if (!entry.isFile()) continue;
|
|
5522
5751
|
const rel = relative2(base, absolute).replaceAll("\\", "/");
|
|
5523
|
-
files.push({ path: rel, content: await
|
|
5752
|
+
files.push({ path: rel, content: await readFile16(absolute, "utf8") });
|
|
5524
5753
|
}
|
|
5525
5754
|
return files;
|
|
5526
5755
|
}
|
|
@@ -5533,7 +5762,7 @@ async function verifyWorkflowPackageIntegrity(resourcesRoot) {
|
|
|
5533
5762
|
const manifest = await readWorkflowFamilyManifest(resourcesRoot);
|
|
5534
5763
|
const expected = manifest.content_sha256;
|
|
5535
5764
|
if (typeof expected !== "string" || expected.length === 0) return;
|
|
5536
|
-
const harnessRoot =
|
|
5765
|
+
const harnessRoot = join18(resourcesRoot, "harness");
|
|
5537
5766
|
if (!await pathExists3(harnessRoot)) {
|
|
5538
5767
|
throw new WorkflowDataResolutionError(
|
|
5539
5768
|
"\u5DE5\u4F5C\u6D41\u6570\u636E\u5305\u7F3A\u5C11 harness/\uFF0C\u65E0\u6CD5\u6821\u9A8C SHA-256",
|
|
@@ -5552,40 +5781,40 @@ async function verifyWorkflowPackageIntegrity(resourcesRoot) {
|
|
|
5552
5781
|
}
|
|
5553
5782
|
}
|
|
5554
5783
|
async function monorepoResourcesRoot() {
|
|
5555
|
-
const here =
|
|
5784
|
+
const here = dirname5(fileURLToPath(import.meta.url));
|
|
5556
5785
|
const candidates = [
|
|
5557
5786
|
// TypeScript source: packages/cli/src/workflow-data -> packages/workflow-data-harness.
|
|
5558
|
-
|
|
5787
|
+
join18(here, "../../../workflow-data-harness"),
|
|
5559
5788
|
// Bundled CLI: packages/cli/dist -> packages/workflow-data-harness.
|
|
5560
|
-
|
|
5789
|
+
join18(here, "../../workflow-data-harness"),
|
|
5561
5790
|
// Test/build layouts that preserve additional source directory levels.
|
|
5562
|
-
|
|
5791
|
+
join18(here, "../../../../packages/workflow-data-harness")
|
|
5563
5792
|
];
|
|
5564
5793
|
for (const candidate of candidates) {
|
|
5565
|
-
if (await pathExists3(
|
|
5794
|
+
if (await pathExists3(join18(candidate, "harness", "manifests"))) return candidate;
|
|
5566
5795
|
}
|
|
5567
5796
|
return null;
|
|
5568
5797
|
}
|
|
5569
5798
|
async function siblingWorkflowPackage(cwd) {
|
|
5570
|
-
const here =
|
|
5799
|
+
const here = dirname5(fileURLToPath(import.meta.url));
|
|
5571
5800
|
const candidates = [
|
|
5572
|
-
|
|
5801
|
+
join18(cwd, "node_modules", "@hunter-harness", "workflow-harness"),
|
|
5573
5802
|
// Published layout: node_modules/hunter-harness/dist/bin.js alongside the
|
|
5574
5803
|
// scoped workflow package in node_modules/@hunter-harness/workflow-harness.
|
|
5575
|
-
|
|
5804
|
+
join18(here, "..", "..", "@hunter-harness", "workflow-harness"),
|
|
5576
5805
|
// Monorepo bundled layout: packages/cli/dist/bin.js.
|
|
5577
|
-
|
|
5806
|
+
join18(here, "..", "..", "workflow-data-harness")
|
|
5578
5807
|
];
|
|
5579
5808
|
for (const candidate of candidates) {
|
|
5580
|
-
if (await pathExists3(
|
|
5809
|
+
if (await pathExists3(join18(candidate, "harness", "manifests"))) return candidate;
|
|
5581
5810
|
}
|
|
5582
5811
|
return null;
|
|
5583
5812
|
}
|
|
5584
5813
|
async function readCachedNpmPackageVersion(cacheRoot) {
|
|
5585
|
-
const manifestPath =
|
|
5814
|
+
const manifestPath = join18(cacheRoot, "package.json");
|
|
5586
5815
|
if (!await pathExists3(manifestPath)) return null;
|
|
5587
5816
|
try {
|
|
5588
|
-
const pkg = JSON.parse(await
|
|
5817
|
+
const pkg = JSON.parse(await readFile16(manifestPath, "utf8"));
|
|
5589
5818
|
return typeof pkg.version === "string" && pkg.version.length > 0 ? pkg.version : null;
|
|
5590
5819
|
} catch {
|
|
5591
5820
|
return null;
|
|
@@ -5607,13 +5836,13 @@ async function latestWorkflowCacheIsStale(cacheRoot, packageName, resolveManifes
|
|
|
5607
5836
|
}
|
|
5608
5837
|
}
|
|
5609
5838
|
async function materializeWorkflowPackageCache(cacheRoot, packageSpec, extract) {
|
|
5610
|
-
await
|
|
5611
|
-
const staging =
|
|
5839
|
+
await mkdir5(cacheRoot, { recursive: true });
|
|
5840
|
+
const staging = join18(cacheRoot, ".extract");
|
|
5612
5841
|
await rm8(staging, { recursive: true, force: true });
|
|
5613
|
-
await
|
|
5842
|
+
await mkdir5(staging, { recursive: true });
|
|
5614
5843
|
await extract(packageSpec, staging);
|
|
5615
|
-
const packageDir = await pathExists3(
|
|
5616
|
-
if (!await pathExists3(
|
|
5844
|
+
const packageDir = await pathExists3(join18(staging, "harness", "manifests")) ? staging : join18(staging, "package");
|
|
5845
|
+
if (!await pathExists3(join18(packageDir, "harness", "manifests"))) {
|
|
5617
5846
|
throw new WorkflowDataResolutionError(
|
|
5618
5847
|
"\u5DE5\u4F5C\u6D41\u6570\u636E\u5305\u5185\u5BB9\u65E0\u6548\uFF1A\u7F3A\u5C11 harness/manifests",
|
|
5619
5848
|
"WORKFLOW_DATA_INVALID",
|
|
@@ -5642,8 +5871,8 @@ async function resolveWorkflowResourcesRoot(options, argv = []) {
|
|
|
5642
5871
|
const packageName = workflowPackageName(family, options.env);
|
|
5643
5872
|
const packageSpec = version === "latest" ? packageName : `${packageName}@${version}`;
|
|
5644
5873
|
const cacheKey = packageSpec.replace("/", "+");
|
|
5645
|
-
const cacheRoot =
|
|
5646
|
-
if (await pathExists3(
|
|
5874
|
+
const cacheRoot = join18(options.cwd, ".harness", "cache", "workflow-packages", cacheKey);
|
|
5875
|
+
if (await pathExists3(join18(cacheRoot, "harness", "manifests"))) {
|
|
5647
5876
|
if (version === "latest") {
|
|
5648
5877
|
const stale = await latestWorkflowCacheIsStale(cacheRoot, packageName, options.pacoteManifest);
|
|
5649
5878
|
if (stale) {
|
|
@@ -5689,9 +5918,9 @@ function describeWorkflowDataFetchFailure(error, packageSpec) {
|
|
|
5689
5918
|
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
5919
|
}
|
|
5691
5920
|
async function readWorkflowFamilyManifest(resourcesRoot) {
|
|
5692
|
-
const manifestPath =
|
|
5921
|
+
const manifestPath = join18(resourcesRoot, "hunter-workflow-family.json");
|
|
5693
5922
|
if (!await pathExists3(manifestPath)) return {};
|
|
5694
|
-
return JSON.parse(await
|
|
5923
|
+
return JSON.parse(await readFile16(manifestPath, "utf8"));
|
|
5695
5924
|
}
|
|
5696
5925
|
|
|
5697
5926
|
// 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.12",
|
|
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": "*",
|