hunter-harness 0.2.6 → 0.2.7
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/bin.js +207 -76
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -1670,6 +1670,9 @@ function affectedPaths(operation) {
|
|
|
1670
1670
|
}
|
|
1671
1671
|
async function writeJournal(transactionRoot, journal) {
|
|
1672
1672
|
await atomicWriteJson(join3(transactionRoot, "journal.json"), journal);
|
|
1673
|
+
await writeStatus(transactionRoot, journal);
|
|
1674
|
+
}
|
|
1675
|
+
async function writeStatus(transactionRoot, journal) {
|
|
1673
1676
|
await atomicWriteJson(join3(transactionRoot, "status.json"), {
|
|
1674
1677
|
schema_version: 1,
|
|
1675
1678
|
transaction_id: journal.transaction_id,
|
|
@@ -1679,6 +1682,16 @@ async function writeJournal(transactionRoot, journal) {
|
|
|
1679
1682
|
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1680
1683
|
});
|
|
1681
1684
|
}
|
|
1685
|
+
function journalOperation(operation) {
|
|
1686
|
+
if (operation.operation === "rename") {
|
|
1687
|
+
return {
|
|
1688
|
+
operation: "rename",
|
|
1689
|
+
from_path: operation.from_path,
|
|
1690
|
+
to_path: operation.to_path
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
return { operation: operation.operation, path: operation.path };
|
|
1694
|
+
}
|
|
1682
1695
|
async function pruneOlderSuccessful(layout, currentId, kind) {
|
|
1683
1696
|
if (kind === void 0)
|
|
1684
1697
|
return;
|
|
@@ -1799,12 +1812,12 @@ async function runTransaction(projectRoot, rawOperations, options = {}) {
|
|
|
1799
1812
|
const snapshots = await snapshotPaths(projectRoot, transactionRoot, paths);
|
|
1800
1813
|
await stageOperations(transactionRoot, operations);
|
|
1801
1814
|
const journal = {
|
|
1802
|
-
schema_version:
|
|
1815
|
+
schema_version: 2,
|
|
1803
1816
|
transaction_id: transactionId,
|
|
1804
1817
|
...options.kind === void 0 ? {} : { kind: options.kind },
|
|
1805
1818
|
state: "prepared",
|
|
1806
1819
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1807
|
-
operations,
|
|
1820
|
+
operations: operations.map(journalOperation),
|
|
1808
1821
|
snapshots,
|
|
1809
1822
|
applied_count: 0,
|
|
1810
1823
|
failure: null
|
|
@@ -1820,7 +1833,7 @@ async function runTransaction(projectRoot, rawOperations, options = {}) {
|
|
|
1820
1833
|
}
|
|
1821
1834
|
await applyOperation(projectRoot, transactionRoot, operation, index, transactionId);
|
|
1822
1835
|
journal.applied_count = index + 1;
|
|
1823
|
-
await
|
|
1836
|
+
await writeStatus(transactionRoot, journal);
|
|
1824
1837
|
if (options.interruptAfterApply === journal.applied_count) {
|
|
1825
1838
|
journal.state = "interrupted";
|
|
1826
1839
|
journal.failure = "injected interruption";
|
|
@@ -2346,6 +2359,7 @@ async function initializeProject(options) {
|
|
|
2346
2359
|
}
|
|
2347
2360
|
manifests.push({
|
|
2348
2361
|
adapter: agent,
|
|
2362
|
+
profile,
|
|
2349
2363
|
bundle_version: bundle.manifest.bundle_version,
|
|
2350
2364
|
bundle_manifest_hash: bundleHash
|
|
2351
2365
|
});
|
|
@@ -2450,9 +2464,9 @@ async function initializeProject(options) {
|
|
|
2450
2464
|
return byTarget !== 0 ? byTarget : left.block_id.localeCompare(right.block_id);
|
|
2451
2465
|
});
|
|
2452
2466
|
const installedState = {
|
|
2453
|
-
schema_version:
|
|
2454
|
-
profile,
|
|
2467
|
+
schema_version: 4,
|
|
2455
2468
|
adapters: enabledAgents,
|
|
2469
|
+
profiles: Object.fromEntries(enabledAgents.map((agent) => [agent, profile])),
|
|
2456
2470
|
installed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2457
2471
|
manifests,
|
|
2458
2472
|
files: mergedTargets.map((target) => ({
|
|
@@ -2507,17 +2521,35 @@ async function readOptionalText(path) {
|
|
|
2507
2521
|
async function readInstalledState(root) {
|
|
2508
2522
|
const content = await readOptionalText(join6(root, INSTALLED_STATE_PATH));
|
|
2509
2523
|
if (content === "") {
|
|
2510
|
-
return {
|
|
2524
|
+
return {
|
|
2525
|
+
profile: null,
|
|
2526
|
+
schemaVersion: null,
|
|
2527
|
+
adapters: [],
|
|
2528
|
+
profiles: /* @__PURE__ */ new Map(),
|
|
2529
|
+
trusted: /* @__PURE__ */ new Map(),
|
|
2530
|
+
files: [],
|
|
2531
|
+
manifests: [],
|
|
2532
|
+
managedBlocks: []
|
|
2533
|
+
};
|
|
2511
2534
|
}
|
|
2512
2535
|
let parsed;
|
|
2513
2536
|
try {
|
|
2514
2537
|
parsed = JSON.parse(content);
|
|
2515
2538
|
} catch {
|
|
2516
|
-
return {
|
|
2539
|
+
return {
|
|
2540
|
+
profile: null,
|
|
2541
|
+
schemaVersion: null,
|
|
2542
|
+
adapters: [],
|
|
2543
|
+
profiles: /* @__PURE__ */ new Map(),
|
|
2544
|
+
trusted: /* @__PURE__ */ new Map(),
|
|
2545
|
+
files: [],
|
|
2546
|
+
manifests: [],
|
|
2547
|
+
managedBlocks: []
|
|
2548
|
+
};
|
|
2517
2549
|
}
|
|
2518
2550
|
const profile = parseHarnessProfile(parsed.profile);
|
|
2519
2551
|
const trusted = /* @__PURE__ */ new Map();
|
|
2520
|
-
if ((parsed.schema_version === 2 || parsed.schema_version === 3) && Array.isArray(parsed.files)) {
|
|
2552
|
+
if ((parsed.schema_version === 2 || parsed.schema_version === 3 || parsed.schema_version === 4) && Array.isArray(parsed.files)) {
|
|
2521
2553
|
for (const entry of parsed.files) {
|
|
2522
2554
|
if (entry !== null && typeof entry === "object" && "target_path" in entry && "sha256" in entry) {
|
|
2523
2555
|
const target = entry.target_path;
|
|
@@ -2529,8 +2561,42 @@ async function readInstalledState(root) {
|
|
|
2529
2561
|
}
|
|
2530
2562
|
}
|
|
2531
2563
|
const schemaVersion = typeof parsed.schema_version === "number" ? parsed.schema_version : null;
|
|
2532
|
-
const adapters = schemaVersion === 3 && Array.isArray(parsed.adapters) ? sortHarnessAgents(parsed.adapters.filter((value) => value === "claude-code" || value === "codex" || value === "cursor" || value === "codebuddy")) : schemaVersion === 1 || schemaVersion === 2 ? ["claude-code"] : [];
|
|
2533
|
-
|
|
2564
|
+
const adapters = (schemaVersion === 3 || schemaVersion === 4) && Array.isArray(parsed.adapters) ? sortHarnessAgents(parsed.adapters.filter((value) => value === "claude-code" || value === "codex" || value === "cursor" || value === "codebuddy")) : schemaVersion === 1 || schemaVersion === 2 ? ["claude-code"] : [];
|
|
2565
|
+
const profiles = /* @__PURE__ */ new Map();
|
|
2566
|
+
if (schemaVersion === 4 && parsed.profiles !== null && typeof parsed.profiles === "object" && !Array.isArray(parsed.profiles)) {
|
|
2567
|
+
for (const agent of adapters) {
|
|
2568
|
+
const value = parsed.profiles[agent];
|
|
2569
|
+
const agentProfile = parseHarnessProfile(value);
|
|
2570
|
+
if (agentProfile !== null)
|
|
2571
|
+
profiles.set(agent, agentProfile);
|
|
2572
|
+
}
|
|
2573
|
+
} else if (profile !== null) {
|
|
2574
|
+
for (const agent of adapters)
|
|
2575
|
+
profiles.set(agent, profile);
|
|
2576
|
+
}
|
|
2577
|
+
const files = Array.isArray(parsed.files) ? parsed.files.filter((entry) => entry !== null && typeof entry === "object" && typeof entry.target_path === "string" && typeof entry.source_path === "string" && typeof entry.sha256 === "string" && "owner" in entry) : [];
|
|
2578
|
+
const manifests = schemaVersion === 4 && Array.isArray(parsed.manifests) ? parsed.manifests.filter((entry) => entry !== null && typeof entry === "object" && typeof entry.adapter === "string" && typeof entry.profile === "string") : [];
|
|
2579
|
+
const managedBlocks = Array.isArray(parsed.managed_blocks) ? parsed.managed_blocks.filter((entry) => entry !== null && typeof entry === "object" && typeof entry.target_path === "string" && typeof entry.block_id === "string") : [];
|
|
2580
|
+
return {
|
|
2581
|
+
profile,
|
|
2582
|
+
schemaVersion,
|
|
2583
|
+
adapters,
|
|
2584
|
+
profiles,
|
|
2585
|
+
trusted,
|
|
2586
|
+
files,
|
|
2587
|
+
manifests,
|
|
2588
|
+
managedBlocks
|
|
2589
|
+
};
|
|
2590
|
+
}
|
|
2591
|
+
async function readInstalledAgentConfiguration(projectRoot) {
|
|
2592
|
+
const installed = await readInstalledState(resolve4(projectRoot));
|
|
2593
|
+
return {
|
|
2594
|
+
agents: installed.adapters,
|
|
2595
|
+
profiles: Object.fromEntries(installed.adapters.map((agent) => [
|
|
2596
|
+
agent,
|
|
2597
|
+
installed.profiles.get(agent) ?? installed.profile ?? "general"
|
|
2598
|
+
]))
|
|
2599
|
+
};
|
|
2534
2600
|
}
|
|
2535
2601
|
async function readContextIndexBundleHash(root) {
|
|
2536
2602
|
const content = await readOptionalText(join6(root, CONTEXT_INDEX_PATH));
|
|
@@ -2588,16 +2654,18 @@ function conflict(target, reason, oldSha, incomingSha) {
|
|
|
2588
2654
|
function sortByTarget(items) {
|
|
2589
2655
|
return [...items].sort((left, right) => left.target_path.localeCompare(right.target_path));
|
|
2590
2656
|
}
|
|
2591
|
-
async function reconcileContextIndex(root,
|
|
2657
|
+
async function reconcileContextIndex(root, agents, profiles, manifests, codebuddySurface2) {
|
|
2592
2658
|
const existing = await readOptionalText(join6(root, CONTEXT_INDEX_PATH));
|
|
2593
|
-
const context = { profile, codebuddySurface: codebuddySurface2 };
|
|
2594
2659
|
const record = {
|
|
2595
2660
|
schema_version: 2,
|
|
2596
2661
|
project: {
|
|
2597
2662
|
shared_instructions: "AGENTS.md",
|
|
2598
2663
|
adapters: Object.fromEntries(agents.map((agent) => [
|
|
2599
2664
|
agent,
|
|
2600
|
-
getAdapter(agent).contextIndex(
|
|
2665
|
+
getAdapter(agent).contextIndex({
|
|
2666
|
+
profile: profiles.get(agent) ?? "general",
|
|
2667
|
+
codebuddySurface: codebuddySurface2
|
|
2668
|
+
})
|
|
2601
2669
|
]))
|
|
2602
2670
|
},
|
|
2603
2671
|
knowledge: { index: ".harness/knowledge/index.json" },
|
|
@@ -2616,23 +2684,25 @@ async function reconcileContextIndex(root, profile, agents, manifests, codebuddy
|
|
|
2616
2684
|
content: next
|
|
2617
2685
|
};
|
|
2618
2686
|
}
|
|
2619
|
-
async function projectTransitionOperation(root,
|
|
2620
|
-
if (previousProfile === profile && oldAgents.length === agents.length && oldAgents.every((agent, index) => agent === agents[index]))
|
|
2621
|
-
return null;
|
|
2687
|
+
async function projectTransitionOperation(root, agents, profiles, codebuddySurface2) {
|
|
2622
2688
|
const path = ".harness/project.yaml";
|
|
2623
2689
|
const content = await readOptionalText(join6(root, path));
|
|
2624
2690
|
if (content === "")
|
|
2625
2691
|
return null;
|
|
2626
2692
|
const project = parseYaml3(content);
|
|
2693
|
+
const activeProfiles = [...new Set(agents.map((agent) => profiles.get(agent) ?? "general"))].sort();
|
|
2694
|
+
const next = stringifyYaml2({
|
|
2695
|
+
...project,
|
|
2696
|
+
project: { ...project.project, profiles: activeProfiles },
|
|
2697
|
+
adapters: { enabled: agents },
|
|
2698
|
+
...agents.includes("codebuddy") ? { adapter_options: { codebuddy: { surface: codebuddySurface2 } } } : { adapter_options: void 0 }
|
|
2699
|
+
}, { sortMapEntries: true });
|
|
2700
|
+
if (next === content)
|
|
2701
|
+
return null;
|
|
2627
2702
|
return {
|
|
2628
2703
|
operation: "modify",
|
|
2629
2704
|
path,
|
|
2630
|
-
content:
|
|
2631
|
-
...project,
|
|
2632
|
-
project: { ...project.project, profiles: [profile] },
|
|
2633
|
-
adapters: { enabled: agents },
|
|
2634
|
-
...agents.includes("codebuddy") ? { adapter_options: { codebuddy: { surface: codebuddySurface2 } } } : { adapter_options: void 0 }
|
|
2635
|
-
}, { sortMapEntries: true })
|
|
2705
|
+
content: next
|
|
2636
2706
|
};
|
|
2637
2707
|
}
|
|
2638
2708
|
function mergeTargets(targets) {
|
|
@@ -2691,30 +2761,42 @@ function stateWithoutInstalledAt(value) {
|
|
|
2691
2761
|
}
|
|
2692
2762
|
async function refreshProject(options) {
|
|
2693
2763
|
const root = resolve4(options.projectRoot);
|
|
2694
|
-
const profile = options.profile;
|
|
2695
2764
|
const installed = await readInstalledState(root);
|
|
2696
|
-
const previousProfile = installed.profile;
|
|
2697
|
-
const agents = sortHarnessAgents(options.agents);
|
|
2698
2765
|
const oldAgents = installed.adapters.length > 0 ? installed.adapters : ["claude-code"];
|
|
2766
|
+
const selectedAgents = sortHarnessAgents(options.agents);
|
|
2767
|
+
const selectedSet = new Set(selectedAgents);
|
|
2768
|
+
const agents = sortHarnessAgents([...oldAgents, ...selectedAgents]);
|
|
2769
|
+
const profiles = new Map(installed.profiles);
|
|
2770
|
+
if (options.profile !== void 0) {
|
|
2771
|
+
for (const agent of selectedAgents)
|
|
2772
|
+
profiles.set(agent, options.profile);
|
|
2773
|
+
}
|
|
2774
|
+
const profile = options.profile ?? selectedAgents.map((agent) => profiles.get(agent)).find((value) => value !== void 0) ?? installed.profile ?? "general";
|
|
2775
|
+
const previousProfile = selectedAgents.map((agent) => installed.profiles.get(agent)).find((value) => value !== void 0) ?? installed.profile;
|
|
2699
2776
|
const codebuddySurface2 = options.codebuddySurface ?? "both";
|
|
2700
|
-
const context = { profile, codebuddySurface: codebuddySurface2 };
|
|
2701
2777
|
const owned = [];
|
|
2702
2778
|
const manifests = [];
|
|
2703
2779
|
for (const agent of agents) {
|
|
2704
|
-
const
|
|
2780
|
+
const agentProfile = profiles.get(agent) ?? profile;
|
|
2781
|
+
profiles.set(agent, agentProfile);
|
|
2782
|
+
const bundle = await loadAgentBundle(options.resourcesRoot, agentProfile, agent);
|
|
2705
2783
|
manifests.push({
|
|
2706
2784
|
adapter: agent,
|
|
2785
|
+
profile: agentProfile,
|
|
2707
2786
|
bundle_version: bundle.manifest.bundle_version,
|
|
2708
2787
|
bundle_manifest_hash: sha256Bytes(canonicalJson(bundle.manifest.files))
|
|
2709
2788
|
});
|
|
2710
|
-
|
|
2711
|
-
|
|
2789
|
+
if (selectedSet.has(agent)) {
|
|
2790
|
+
const context = { profile: agentProfile, codebuddySurface: codebuddySurface2 };
|
|
2791
|
+
for (const target of managedTargetsFor(getAdapter(agent), bundle, context)) {
|
|
2792
|
+
owned.push({ ...target, owner: agent });
|
|
2793
|
+
}
|
|
2712
2794
|
}
|
|
2713
2795
|
}
|
|
2714
2796
|
const newManaged = mergeTargets(owned);
|
|
2715
2797
|
let trusted = installed.trusted;
|
|
2716
2798
|
let migrationOldPaths = null;
|
|
2717
|
-
if (installed.schemaVersion === 1) {
|
|
2799
|
+
if (installed.schemaVersion === 1 && selectedSet.has("claude-code")) {
|
|
2718
2800
|
const contextHash = await readContextIndexBundleHash(root);
|
|
2719
2801
|
if (contextHash !== null) {
|
|
2720
2802
|
const migrations = await loadMigrationManifests(options.resourcesRoot);
|
|
@@ -2738,12 +2820,18 @@ async function refreshProject(options) {
|
|
|
2738
2820
|
});
|
|
2739
2821
|
}
|
|
2740
2822
|
}
|
|
2741
|
-
} else
|
|
2742
|
-
const oldContext = { profile: previousProfile, codebuddySurface: codebuddySurface2 };
|
|
2823
|
+
} else {
|
|
2743
2824
|
const oldTargets = [];
|
|
2744
|
-
for (const agent of
|
|
2745
|
-
const
|
|
2746
|
-
|
|
2825
|
+
for (const agent of selectedAgents) {
|
|
2826
|
+
const oldProfile = installed.profiles.get(agent) ?? installed.profile;
|
|
2827
|
+
if (!oldAgents.includes(agent) || oldProfile === null || oldProfile === void 0) {
|
|
2828
|
+
continue;
|
|
2829
|
+
}
|
|
2830
|
+
const bundle = await loadAgentBundle(options.resourcesRoot, oldProfile, agent);
|
|
2831
|
+
oldTargets.push(...managedTargetsFor(getAdapter(agent), bundle, {
|
|
2832
|
+
profile: oldProfile,
|
|
2833
|
+
codebuddySurface: codebuddySurface2
|
|
2834
|
+
}));
|
|
2747
2835
|
}
|
|
2748
2836
|
oldOnly = oldTargets.filter((target) => !newTargetSet.has(target.target_path));
|
|
2749
2837
|
}
|
|
@@ -2753,7 +2841,7 @@ async function refreshProject(options) {
|
|
|
2753
2841
|
const unchanged = [];
|
|
2754
2842
|
const conflicts = [];
|
|
2755
2843
|
const ops = [];
|
|
2756
|
-
const newStateFiles =
|
|
2844
|
+
const newStateFiles = installed.files.filter((entry) => entry.owner === "shared" || !selectedSet.has(entry.owner));
|
|
2757
2845
|
for (const target of newManaged) {
|
|
2758
2846
|
const incoming = target.sha256;
|
|
2759
2847
|
const current = await fileHex(join6(root, target.target_path));
|
|
@@ -2801,41 +2889,50 @@ async function refreshProject(options) {
|
|
|
2801
2889
|
}
|
|
2802
2890
|
}
|
|
2803
2891
|
await reconcileMarkdownBlock(root, "AGENTS.md", AGENTS_CORE_BLOCK_ID, AGENTS_MANAGED_BLOCK_CONTENT, false, ops, conflicts, preserved);
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2892
|
+
if (selectedSet.has("claude-code")) {
|
|
2893
|
+
await reconcileMarkdownBlock(root, "CLAUDE.md", CLAUDE_BLOCK_ID, CLAUDE_MANAGED_BLOCK_CONTENT, false, ops, conflicts, preserved);
|
|
2894
|
+
}
|
|
2895
|
+
if (selectedSet.has("codebuddy")) {
|
|
2896
|
+
await reconcileMarkdownBlock(root, "CODEBUDDY.md", CODEBUDDY_BLOCK_ID, CODEBUDDY_MANAGED_BLOCK_CONTENT, false, ops, conflicts, preserved);
|
|
2897
|
+
}
|
|
2898
|
+
const projectOperation = await projectTransitionOperation(root, agents, profiles, codebuddySurface2);
|
|
2807
2899
|
if (projectOperation !== null)
|
|
2808
2900
|
ops.push(projectOperation);
|
|
2809
|
-
const contextOperation = await reconcileContextIndex(root,
|
|
2901
|
+
const contextOperation = await reconcileContextIndex(root, agents, profiles, manifests, codebuddySurface2);
|
|
2810
2902
|
if (contextOperation !== null)
|
|
2811
2903
|
ops.push(contextOperation);
|
|
2812
2904
|
const managedBlocks = [
|
|
2905
|
+
...installed.managedBlocks.filter((entry) => entry.owner !== "shared" && !selectedSet.has(entry.owner)),
|
|
2813
2906
|
{
|
|
2814
2907
|
owner: "shared",
|
|
2815
2908
|
target_path: "AGENTS.md",
|
|
2816
2909
|
block_id: AGENTS_CORE_BLOCK_ID,
|
|
2817
2910
|
content_sha256: createHash5("sha256").update(AGENTS_MANAGED_BLOCK_CONTENT).digest("hex")
|
|
2818
2911
|
},
|
|
2819
|
-
...
|
|
2912
|
+
...selectedSet.has("claude-code") ? [{
|
|
2820
2913
|
owner: "claude-code",
|
|
2821
2914
|
target_path: "CLAUDE.md",
|
|
2822
2915
|
block_id: CLAUDE_BLOCK_ID,
|
|
2823
2916
|
content_sha256: createHash5("sha256").update(CLAUDE_MANAGED_BLOCK_CONTENT).digest("hex")
|
|
2824
2917
|
}] : [],
|
|
2825
|
-
...
|
|
2918
|
+
...selectedSet.has("codebuddy") ? [{
|
|
2826
2919
|
owner: "codebuddy",
|
|
2827
2920
|
target_path: "CODEBUDDY.md",
|
|
2828
2921
|
block_id: CODEBUDDY_BLOCK_ID,
|
|
2829
2922
|
content_sha256: createHash5("sha256").update(CODEBUDDY_MANAGED_BLOCK_CONTENT).digest("hex")
|
|
2830
2923
|
}] : []
|
|
2831
2924
|
].sort((left, right) => left.target_path.localeCompare(right.target_path) || left.block_id.localeCompare(right.block_id));
|
|
2925
|
+
const filesByTarget = new Map(newStateFiles.map((entry) => [entry.target_path, entry]));
|
|
2832
2926
|
const installedState = {
|
|
2833
|
-
schema_version:
|
|
2834
|
-
profile,
|
|
2927
|
+
schema_version: 4,
|
|
2835
2928
|
adapters: agents,
|
|
2929
|
+
profiles: Object.fromEntries(agents.map((agent) => [
|
|
2930
|
+
agent,
|
|
2931
|
+
profiles.get(agent) ?? "general"
|
|
2932
|
+
])),
|
|
2836
2933
|
installed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2837
|
-
manifests,
|
|
2838
|
-
files:
|
|
2934
|
+
manifests: manifests.sort((left, right) => left.adapter.localeCompare(right.adapter)),
|
|
2935
|
+
files: [...filesByTarget.values()].sort((left, right) => left.target_path.localeCompare(right.target_path) || left.source_path.localeCompare(right.source_path)),
|
|
2839
2936
|
managed_blocks: managedBlocks
|
|
2840
2937
|
};
|
|
2841
2938
|
const existingState = await readOptionalText(join6(root, INSTALLED_STATE_PATH));
|
|
@@ -2853,7 +2950,10 @@ async function refreshProject(options) {
|
|
|
2853
2950
|
}
|
|
2854
2951
|
if (!options.dryRun) {
|
|
2855
2952
|
await runTransaction(root, ops, { kind: "refresh" });
|
|
2856
|
-
await pruneEmptyParentDirs(root, removed.map((item2) => item2.target_path), getAdapters(
|
|
2953
|
+
await pruneEmptyParentDirs(root, removed.map((item2) => item2.target_path), getAdapters(selectedAgents).flatMap((adapter) => adapter.pruneBoundaries({
|
|
2954
|
+
profile: profiles.get(adapter.name) ?? profile,
|
|
2955
|
+
codebuddySurface: codebuddySurface2
|
|
2956
|
+
})));
|
|
2857
2957
|
}
|
|
2858
2958
|
return {
|
|
2859
2959
|
profile,
|
|
@@ -4568,9 +4668,9 @@ async function detectProject(root) {
|
|
|
4568
4668
|
}
|
|
4569
4669
|
return { status: "valid", config: parsed.data };
|
|
4570
4670
|
}
|
|
4571
|
-
function parseProfile(value
|
|
4671
|
+
function parseProfile(value) {
|
|
4572
4672
|
if (value === void 0 || value === "") {
|
|
4573
|
-
return
|
|
4673
|
+
return void 0;
|
|
4574
4674
|
}
|
|
4575
4675
|
if (value === "1" || value === "general") return "general";
|
|
4576
4676
|
if (value === "2" || value === "java") return "java";
|
|
@@ -4655,7 +4755,7 @@ async function runRefresh(options, dependencies) {
|
|
|
4655
4755
|
let targetProfile;
|
|
4656
4756
|
let targetAgents;
|
|
4657
4757
|
try {
|
|
4658
|
-
targetProfile = parseProfile(options.profile
|
|
4758
|
+
targetProfile = parseProfile(options.profile);
|
|
4659
4759
|
targetAgents = options.agents === void 0 ? refreshAgents(detection.config) : parseAgentsInput(options.agents);
|
|
4660
4760
|
} catch (error) {
|
|
4661
4761
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -4664,12 +4764,12 @@ async function runRefresh(options, dependencies) {
|
|
|
4664
4764
|
return error instanceof InitConfigurationError ? error.exitCode : 3;
|
|
4665
4765
|
}
|
|
4666
4766
|
const dryRun = options.dryRun === true;
|
|
4667
|
-
if ((targetProfile !== currentProfile || targetAgents.some((agent, index) => agent !== refreshAgents(detection.config)[index]) || targetAgents.length !== refreshAgents(detection.config).length) && !dryRun) {
|
|
4767
|
+
if ((targetProfile !== void 0 && targetProfile !== currentProfile || targetAgents.some((agent, index) => agent !== refreshAgents(detection.config)[index]) || targetAgents.length !== refreshAgents(detection.config).length) && !dryRun) {
|
|
4668
4768
|
try {
|
|
4669
4769
|
const preview = await refreshProject({
|
|
4670
4770
|
projectRoot: dependencies.cwd,
|
|
4671
4771
|
resourcesRoot: dependencies.resourcesRoot,
|
|
4672
|
-
profile: targetProfile,
|
|
4772
|
+
...targetProfile === void 0 ? {} : { profile: targetProfile },
|
|
4673
4773
|
agents: targetAgents,
|
|
4674
4774
|
codebuddySurface: codebuddySurface(detection.config),
|
|
4675
4775
|
dryRun: true,
|
|
@@ -4694,7 +4794,7 @@ async function runRefresh(options, dependencies) {
|
|
|
4694
4794
|
return 2;
|
|
4695
4795
|
}
|
|
4696
4796
|
} else if (!options.yes && !dryRun) {
|
|
4697
|
-
const label = targetProfile === currentProfile ? `\u5237\u65B0\u5F53\u524D\u914D\u7F6E\uFF08${currentProfile}\uFF09` : `\
|
|
4797
|
+
const label = targetProfile === currentProfile ? `\u5237\u65B0\u5F53\u524D\u914D\u7F6E\uFF08${currentProfile}\uFF09` : targetProfile === void 0 ? "\u5237\u65B0\u6240\u9009\u5DE5\u5177\u7684\u5F53\u524D\u914D\u7F6E" : `\u66F4\u65B0\u6240\u9009\u5DE5\u5177\u914D\u7F6E\uFF1A${currentProfile} \u2192 ${targetProfile}`;
|
|
4698
4798
|
const answer = await dependencies.prompt(`${label}\uFF1F[y/N]\uFF1A`);
|
|
4699
4799
|
if (!/^(?:y|yes)$/i.test(answer.trim())) {
|
|
4700
4800
|
return 2;
|
|
@@ -4705,7 +4805,7 @@ async function runRefresh(options, dependencies) {
|
|
|
4705
4805
|
const result = await refreshProject({
|
|
4706
4806
|
projectRoot: dependencies.cwd,
|
|
4707
4807
|
resourcesRoot: dependencies.resourcesRoot,
|
|
4708
|
-
profile: targetProfile,
|
|
4808
|
+
...targetProfile === void 0 ? {} : { profile: targetProfile },
|
|
4709
4809
|
agents: targetAgents,
|
|
4710
4810
|
codebuddySurface: codebuddySurface(detection.config),
|
|
4711
4811
|
dryRun,
|
|
@@ -4720,7 +4820,7 @@ async function runRefresh(options, dependencies) {
|
|
|
4720
4820
|
if (result.removed.length > 0) parts.push(`\u5DF2\u5220\u9664 ${result.removed.length} \u4E2A`);
|
|
4721
4821
|
if (result.preserved.length > 0) parts.push(`\u5DF2\u4FDD\u7559 ${result.preserved.length} \u4E2A`);
|
|
4722
4822
|
if (result.unchanged.length > 0) parts.push(`\u65E0\u9700\u53D8\u66F4 ${result.unchanged.length} \u4E2A`);
|
|
4723
|
-
dependencies.stdout(`Harness \u5237\u65B0\uFF08${
|
|
4823
|
+
dependencies.stdout(`Harness \u5237\u65B0\uFF08${result.profile}\uFF09\uFF1A${parts.join("\uFF0C") || "\u6CA1\u6709\u53D8\u66F4"}\u3002
|
|
4724
4824
|
`);
|
|
4725
4825
|
}
|
|
4726
4826
|
return output.exit_code;
|
|
@@ -4750,9 +4850,12 @@ async function runRefresh(options, dependencies) {
|
|
|
4750
4850
|
}
|
|
4751
4851
|
|
|
4752
4852
|
// src/commands/configure.ts
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4853
|
+
var AGENT_LABELS = {
|
|
4854
|
+
"claude-code": "Claude Code",
|
|
4855
|
+
codex: "Codex",
|
|
4856
|
+
cursor: "Cursor",
|
|
4857
|
+
codebuddy: "CodeBuddy"
|
|
4858
|
+
};
|
|
4756
4859
|
async function runFirstInstall(options, dependencies) {
|
|
4757
4860
|
const requestId = uuidV7();
|
|
4758
4861
|
try {
|
|
@@ -4834,21 +4937,40 @@ async function runExistingProject(options, dependencies, currentProfile) {
|
|
|
4834
4937
|
if (options.nonInteractive === true) {
|
|
4835
4938
|
return runRefresh(refreshOptions, dependencies);
|
|
4836
4939
|
}
|
|
4837
|
-
const
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4940
|
+
const installed = await readInstalledAgentConfiguration(dependencies.cwd);
|
|
4941
|
+
const currentAgents = installed.agents.length > 0 ? installed.agents : ["claude-code"];
|
|
4942
|
+
const currentLines = currentAgents.map(
|
|
4943
|
+
(agent) => `- ${AGENT_LABELS[agent]}\uFF1A${installed.profiles[agent] ?? currentProfile}`
|
|
4944
|
+
).join("\n");
|
|
4945
|
+
if (refreshOptions.agents === void 0) {
|
|
4946
|
+
const defaultSelection = currentAgents.map((agent) => String(HARNESS_AGENT_ORDER.indexOf(agent) + 1)).join(",");
|
|
4947
|
+
const answer = await dependencies.prompt(
|
|
4948
|
+
`Hunter Harness \u5F53\u524D\u914D\u7F6E\uFF1A
|
|
4949
|
+
${currentLines}
|
|
4950
|
+
\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
|
|
4951
|
+
1. Claude Code
|
|
4952
|
+
2. Codex
|
|
4953
|
+
3. Cursor
|
|
4954
|
+
4. CodeBuddy
|
|
4955
|
+
\u8BF7\u8F93\u5165\u7F16\u53F7 [${defaultSelection}]\uFF0C\u6216\u8F93\u5165 0 \u53D6\u6D88\uFF1A`
|
|
4956
|
+
);
|
|
4957
|
+
if (answer.trim() === "0" || /^c/i.test(answer.trim())) return 2;
|
|
4958
|
+
refreshOptions.agents = answer.trim() === "" ? currentAgents.join(",") : answer.trim();
|
|
4959
|
+
}
|
|
4960
|
+
if (refreshOptions.profile === void 0) {
|
|
4961
|
+
const selected = parseAgentsInput(refreshOptions.agents);
|
|
4962
|
+
const selectedProfiles = new Set(selected.flatMap((agent) => {
|
|
4963
|
+
const profile = installed.profiles[agent];
|
|
4964
|
+
return profile === void 0 ? [] : [profile];
|
|
4965
|
+
}));
|
|
4966
|
+
const defaultProfile = selectedProfiles.size === 1 ? [...selectedProfiles][0] ?? currentProfile : currentProfile;
|
|
4967
|
+
const answer = await dependencies.prompt(
|
|
4968
|
+
`\u8BF7\u9009\u62E9\u6240\u9009\u5DE5\u5177\u4F7F\u7528\u7684 Harness \u914D\u7F6E\uFF1A
|
|
4969
|
+
1. \u901A\u7528
|
|
4970
|
+
2. Java
|
|
4971
|
+
\u8BF7\u8F93\u5165\u7F16\u53F7 [${defaultProfile === "java" ? "2" : "1"}]\uFF1A`
|
|
4972
|
+
);
|
|
4973
|
+
refreshOptions.profile = answer.trim() === "" ? defaultProfile : answer.trim();
|
|
4852
4974
|
}
|
|
4853
4975
|
refreshOptions.confirmed = true;
|
|
4854
4976
|
return runRefresh(refreshOptions, dependencies);
|
|
@@ -5405,8 +5527,12 @@ async function verifyWorkflowPackageIntegrity(resourcesRoot) {
|
|
|
5405
5527
|
async function monorepoResourcesRoot() {
|
|
5406
5528
|
const here = dirname4(fileURLToPath(import.meta.url));
|
|
5407
5529
|
const candidates = [
|
|
5408
|
-
|
|
5409
|
-
join17(here, "
|
|
5530
|
+
// TypeScript source: packages/cli/src/workflow-data -> packages/workflow-data-harness.
|
|
5531
|
+
join17(here, "../../../workflow-data-harness"),
|
|
5532
|
+
// Bundled CLI: packages/cli/dist -> packages/workflow-data-harness.
|
|
5533
|
+
join17(here, "../../workflow-data-harness"),
|
|
5534
|
+
// Test/build layouts that preserve additional source directory levels.
|
|
5535
|
+
join17(here, "../../../../packages/workflow-data-harness")
|
|
5410
5536
|
];
|
|
5411
5537
|
for (const candidate of candidates) {
|
|
5412
5538
|
if (await pathExists3(join17(candidate, "harness", "manifests"))) return candidate;
|
|
@@ -5414,9 +5540,14 @@ async function monorepoResourcesRoot() {
|
|
|
5414
5540
|
return null;
|
|
5415
5541
|
}
|
|
5416
5542
|
async function siblingWorkflowPackage(cwd) {
|
|
5543
|
+
const here = dirname4(fileURLToPath(import.meta.url));
|
|
5417
5544
|
const candidates = [
|
|
5418
5545
|
join17(cwd, "node_modules", "@hunter-harness", "workflow-harness"),
|
|
5419
|
-
|
|
5546
|
+
// Published layout: node_modules/hunter-harness/dist/bin.js alongside the
|
|
5547
|
+
// scoped workflow package in node_modules/@hunter-harness/workflow-harness.
|
|
5548
|
+
join17(here, "..", "..", "@hunter-harness", "workflow-harness"),
|
|
5549
|
+
// Monorepo bundled layout: packages/cli/dist/bin.js.
|
|
5550
|
+
join17(here, "..", "..", "workflow-data-harness")
|
|
5420
5551
|
];
|
|
5421
5552
|
for (const candidate of candidates) {
|
|
5422
5553
|
if (await pathExists3(join17(candidate, "harness", "manifests"))) return candidate;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hunter-harness",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "Local-first, server-governed agent harness",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -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 ../../
|
|
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
|
},
|