@cardor/agent-harness-kit 1.10.2 → 1.10.3
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 +1 -9
- package/dist/cli.js +20 -129
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -184,15 +184,7 @@ For Claude Code and Codex CLI (not OpenCode), you'll also be asked whether to pe
|
|
|
184
184
|
|
|
185
185
|
Regardless of scope, `.harness/storage-state.json` is always written to the project — it records the *actual* current storage state (`scope`, `projectId`, `dbType`, `migratedAt`), separate from the *desired* state declared in the config file.
|
|
186
186
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
| Provider | Global agents dir | Global skills dir |
|
|
190
|
-
| ------------- | ---------------------------- | ----------------------------- |
|
|
191
|
-
| `claude-code` | `~/.claude/agents/` | `~/.claude/skills/` |
|
|
192
|
-
| `codex-cli` | `~/.codex/agents/` | `~/.agents/skills/` (separate namespace from `~/.codex/agents`) |
|
|
193
|
-
| `opencode` | `~/.config/opencode/agents/` | `~/.config/opencode/skills/` |
|
|
194
|
-
|
|
195
|
-
This sync is idempotent and non-destructive: it only creates files that are missing. If everything is already present, `ahk init` prints a message and skips; if only some files are missing, it creates just those and reports what was added. Files it detects as already customized/outdated are left untouched (same "preserve, don't overwrite" rule used for project-local agent files).
|
|
187
|
+
Agent and skill files always live in the project tree, regardless of storage scope — `--storage-scope` only affects where the harness DB lives.
|
|
196
188
|
|
|
197
189
|
```bash
|
|
198
190
|
ahk init
|
package/dist/cli.js
CHANGED
|
@@ -751,14 +751,6 @@ function writeSkills(cwd2, skillsDir) {
|
|
|
751
751
|
writeFileSync3(dest, readFileSync4(src, "utf8"), "utf8");
|
|
752
752
|
}
|
|
753
753
|
}
|
|
754
|
-
function writeSkill(skillsRoot, skillName) {
|
|
755
|
-
const src = join4(__dirname2, "skills", skillName, "SKILL.md");
|
|
756
|
-
const destDir = join4(skillsRoot, skillName);
|
|
757
|
-
const dest = join4(destDir, "SKILL.md");
|
|
758
|
-
if (existsSync3(dest)) return;
|
|
759
|
-
mkdirSync3(destDir, { recursive: true });
|
|
760
|
-
writeFileSync3(dest, readFileSync4(src, "utf8"), "utf8");
|
|
761
|
-
}
|
|
762
754
|
|
|
763
755
|
// src/core/materializer/claude-code.ts
|
|
764
756
|
var ClaudeCodeMaterializer = class {
|
|
@@ -1302,7 +1294,6 @@ import pc3 from "picocolors";
|
|
|
1302
1294
|
|
|
1303
1295
|
// src/core/doctor.ts
|
|
1304
1296
|
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
1305
|
-
import { homedir } from "os";
|
|
1306
1297
|
import { dirname as dirname6, join as join11 } from "path";
|
|
1307
1298
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
1308
1299
|
|
|
@@ -1465,55 +1456,6 @@ function checkSkills(cwd2, provider) {
|
|
|
1465
1456
|
const skillsDir = getProviderSkillsDir(provider);
|
|
1466
1457
|
return checkSkillsAtRoot(join11(cwd2, skillsDir));
|
|
1467
1458
|
}
|
|
1468
|
-
function getGlobalProviderAgentDir(provider, homeDir) {
|
|
1469
|
-
switch (provider) {
|
|
1470
|
-
case "claude-code":
|
|
1471
|
-
return { agentsDir: join11(homeDir, ".claude", "agents"), ext: ".md" };
|
|
1472
|
-
case "opencode":
|
|
1473
|
-
return { agentsDir: join11(homeDir, ".config", "opencode", "agents"), ext: ".md" };
|
|
1474
|
-
case "codex-cli":
|
|
1475
|
-
return { agentsDir: join11(homeDir, ".codex", "agents"), ext: ".toml" };
|
|
1476
|
-
default:
|
|
1477
|
-
return { agentsDir: join11(homeDir, ".claude", "agents"), ext: ".md" };
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1480
|
-
function getGlobalProviderSkillsDir(provider, homeDir) {
|
|
1481
|
-
switch (provider) {
|
|
1482
|
-
case "claude-code":
|
|
1483
|
-
return join11(homeDir, ".claude", "skills");
|
|
1484
|
-
case "opencode":
|
|
1485
|
-
return join11(homeDir, ".config", "opencode", "skills");
|
|
1486
|
-
case "codex-cli":
|
|
1487
|
-
return join11(homeDir, ".agents", "skills");
|
|
1488
|
-
default:
|
|
1489
|
-
return join11(homeDir, ".claude", "skills");
|
|
1490
|
-
}
|
|
1491
|
-
}
|
|
1492
|
-
async function getGlobalDoctorStatus(provider, config, homeDir = homedir()) {
|
|
1493
|
-
const projectName = config.project.name;
|
|
1494
|
-
const allowedPaths = (config.agents.explorer.allowedPaths ?? []).join(", ");
|
|
1495
|
-
const writablePaths = (config.agents.builder.writablePaths ?? []).join(", ");
|
|
1496
|
-
const models = {
|
|
1497
|
-
lead: config.agents.lead.model,
|
|
1498
|
-
explorer: config.agents.explorer.model,
|
|
1499
|
-
consultant: config.agents.consultant?.model,
|
|
1500
|
-
builder: config.agents.builder.model,
|
|
1501
|
-
reviewer: config.agents.reviewer.model
|
|
1502
|
-
};
|
|
1503
|
-
const { agentsDir, ext } = getGlobalProviderAgentDir(provider, homeDir);
|
|
1504
|
-
const agents = checkAgentFilesAtRoot(
|
|
1505
|
-
agentsDir,
|
|
1506
|
-
ext,
|
|
1507
|
-
provider,
|
|
1508
|
-
projectName,
|
|
1509
|
-
allowedPaths,
|
|
1510
|
-
writablePaths,
|
|
1511
|
-
models
|
|
1512
|
-
);
|
|
1513
|
-
const skillsDir = getGlobalProviderSkillsDir(provider, homeDir);
|
|
1514
|
-
const skills = checkSkillsAtRoot(skillsDir);
|
|
1515
|
-
return { agents, skills };
|
|
1516
|
-
}
|
|
1517
1459
|
async function getDoctorStatus(cwd2) {
|
|
1518
1460
|
const lib = await checkLibVersion();
|
|
1519
1461
|
let config;
|
|
@@ -1755,51 +1697,6 @@ import { join as join14 } from "path";
|
|
|
1755
1697
|
import * as p3 from "@clack/prompts";
|
|
1756
1698
|
import pc7 from "picocolors";
|
|
1757
1699
|
|
|
1758
|
-
// src/core/materializer/global-sync.ts
|
|
1759
|
-
import { homedir as homedir2 } from "os";
|
|
1760
|
-
async function syncGlobalAgentsAndSkills(config, provider, homeDir = homedir2()) {
|
|
1761
|
-
const status = await getGlobalDoctorStatus(provider, config, homeDir);
|
|
1762
|
-
const missingAgents = status.agents.filter((a) => a.status === "missing");
|
|
1763
|
-
const missingSkills = status.skills.filter((s) => s.status === "missing");
|
|
1764
|
-
if (missingAgents.length === 0 && missingSkills.length === 0) {
|
|
1765
|
-
return { alreadySynced: true, createdAgents: [], createdSkills: [] };
|
|
1766
|
-
}
|
|
1767
|
-
const projectName = config.project.name;
|
|
1768
|
-
const allowedPaths = (config.agents.explorer.allowedPaths ?? []).join(", ");
|
|
1769
|
-
const writablePaths = (config.agents.builder.writablePaths ?? []).join(", ");
|
|
1770
|
-
const models = {
|
|
1771
|
-
lead: config.agents.lead.model,
|
|
1772
|
-
explorer: config.agents.explorer.model,
|
|
1773
|
-
consultant: config.agents.consultant?.model,
|
|
1774
|
-
builder: config.agents.builder.model,
|
|
1775
|
-
reviewer: config.agents.reviewer.model
|
|
1776
|
-
};
|
|
1777
|
-
const createdAgents = [];
|
|
1778
|
-
if (missingAgents.length > 0) {
|
|
1779
|
-
const { agentsDir, ext } = getGlobalProviderAgentDir(provider, homeDir);
|
|
1780
|
-
for (const agent of missingAgents) {
|
|
1781
|
-
const name = agent.name;
|
|
1782
|
-
const content = generateExpectedAgentContent(name, provider, {
|
|
1783
|
-
projectName,
|
|
1784
|
-
allowedPaths,
|
|
1785
|
-
writablePaths,
|
|
1786
|
-
model: models[name]
|
|
1787
|
-
});
|
|
1788
|
-
writeAgentFile(agentsDir, `${name}${ext}`, content);
|
|
1789
|
-
createdAgents.push(name);
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1792
|
-
const createdSkills = [];
|
|
1793
|
-
if (missingSkills.length > 0) {
|
|
1794
|
-
const skillsDir = getGlobalProviderSkillsDir(provider, homeDir);
|
|
1795
|
-
for (const skill of missingSkills) {
|
|
1796
|
-
writeSkill(skillsDir, skill.name);
|
|
1797
|
-
createdSkills.push(skill.name);
|
|
1798
|
-
}
|
|
1799
|
-
}
|
|
1800
|
-
return { alreadySynced: false, createdAgents, createdSkills };
|
|
1801
|
-
}
|
|
1802
|
-
|
|
1803
1700
|
// src/schema/init.ts
|
|
1804
1701
|
import * as v from "valibot";
|
|
1805
1702
|
var initNameSchema = v.pipe(
|
|
@@ -2041,7 +1938,7 @@ async function runInit(cwd2, flags) {
|
|
|
2041
1938
|
const modelOverrides = {};
|
|
2042
1939
|
if (provider === "claude-code" || provider === "codex-cli") {
|
|
2043
1940
|
const wantsModelCustomization = await p3.confirm({
|
|
2044
|
-
message: "
|
|
1941
|
+
message: "Customize the model per agent?",
|
|
2045
1942
|
initialValue: false
|
|
2046
1943
|
});
|
|
2047
1944
|
if (p3.isCancel(wantsModelCustomization)) {
|
|
@@ -2052,7 +1949,7 @@ async function runInit(cwd2, flags) {
|
|
|
2052
1949
|
if (provider === "claude-code") {
|
|
2053
1950
|
for (const agent of AGENT_LABELS) {
|
|
2054
1951
|
const val = await p3.select({
|
|
2055
|
-
message: `
|
|
1952
|
+
message: `Model for ${agent.label}`,
|
|
2056
1953
|
options: [
|
|
2057
1954
|
{ value: "inherit", label: "inherit (default)" },
|
|
2058
1955
|
{ value: "haiku", label: "haiku" },
|
|
@@ -2071,8 +1968,8 @@ async function runInit(cwd2, flags) {
|
|
|
2071
1968
|
} else {
|
|
2072
1969
|
for (const agent of AGENT_LABELS) {
|
|
2073
1970
|
const val = await p3.text({
|
|
2074
|
-
message: `
|
|
2075
|
-
placeholder: "
|
|
1971
|
+
message: `Model for ${agent.label} (Codex does not validate this value)`,
|
|
1972
|
+
placeholder: "e.g. gpt-5 (empty or <3 chars = no override)"
|
|
2076
1973
|
});
|
|
2077
1974
|
if (p3.isCancel(val)) {
|
|
2078
1975
|
p3.cancel("Cancelled.");
|
|
@@ -2110,7 +2007,10 @@ async function runInit(cwd2, flags) {
|
|
|
2110
2007
|
message: "Storage scope",
|
|
2111
2008
|
options: [
|
|
2112
2009
|
{ value: "local", label: "Local \u2014 .harness/harness.db lives in this project" },
|
|
2113
|
-
{
|
|
2010
|
+
{
|
|
2011
|
+
value: "global",
|
|
2012
|
+
label: "Global \u2014 DB lives under ~/.harness/dbs/<projectId>/, outside the project"
|
|
2013
|
+
}
|
|
2114
2014
|
],
|
|
2115
2015
|
initialValue: "local"
|
|
2116
2016
|
});
|
|
@@ -2174,7 +2074,6 @@ async function runInit(cwd2, flags) {
|
|
|
2174
2074
|
firstTask = { title: taskTitle, description: taskDesc, acceptance };
|
|
2175
2075
|
}
|
|
2176
2076
|
let configExt = "ts";
|
|
2177
|
-
let globalSyncResult = null;
|
|
2178
2077
|
const spinner6 = p3.spinner();
|
|
2179
2078
|
spinner6.start("Scaffolding...");
|
|
2180
2079
|
try {
|
|
@@ -2208,9 +2107,6 @@ async function runInit(cwd2, flags) {
|
|
|
2208
2107
|
const db = await openDB(config, installDir);
|
|
2209
2108
|
await db.writeStorageState(installDir);
|
|
2210
2109
|
await materializer.scaffold(config, { cwd: installDir, firstTask });
|
|
2211
|
-
if (config.storage.scope === "global") {
|
|
2212
|
-
globalSyncResult = await syncGlobalAgentsAndSkills(config, provider);
|
|
2213
|
-
}
|
|
2214
2110
|
if (firstTask) {
|
|
2215
2111
|
const slug = slugify(firstTask.title);
|
|
2216
2112
|
await db.addTask({
|
|
@@ -2234,8 +2130,16 @@ async function runInit(cwd2, flags) {
|
|
|
2234
2130
|
console.log(pc7.green(`\u2713 agent-harness-kit.config.${configExt}`));
|
|
2235
2131
|
console.log(pc7.green("\u2713 AGENTS.md"));
|
|
2236
2132
|
console.log(pc7.green("\u2713 health.sh"));
|
|
2237
|
-
console.log(
|
|
2238
|
-
|
|
2133
|
+
console.log(
|
|
2134
|
+
pc7.green(
|
|
2135
|
+
storageScope === "global" ? "\u2713 ~/.harness/dbs/<projectId>/harness.db" : "\u2713 .harness/harness.db"
|
|
2136
|
+
)
|
|
2137
|
+
);
|
|
2138
|
+
console.log(
|
|
2139
|
+
pc7.green(
|
|
2140
|
+
storageScope === "global" ? "\u2713 ~/.harness/dbs/<projectId>/current.md" : "\u2713 .harness/current.md"
|
|
2141
|
+
)
|
|
2142
|
+
);
|
|
2239
2143
|
console.log(pc7.green("\u2713 .harness/storage-state.json"));
|
|
2240
2144
|
console.log(pc7.green(`\u2713 ${agentsDir}lead.md`));
|
|
2241
2145
|
console.log(pc7.green(`\u2713 ${agentsDir}explorer.md`));
|
|
@@ -2243,19 +2147,6 @@ async function runInit(cwd2, flags) {
|
|
|
2243
2147
|
console.log(pc7.green(`\u2713 ${agentsDir}reviewer.md`));
|
|
2244
2148
|
console.log(pc7.green(`\u2713 ${mcpFile}`));
|
|
2245
2149
|
console.log(pc7.green("\u2713 .gitignore entries added"));
|
|
2246
|
-
if (globalSyncResult) {
|
|
2247
|
-
console.log("");
|
|
2248
|
-
if (globalSyncResult.alreadySynced) {
|
|
2249
|
-
console.log(pc7.dim("\u2713 Global agents/skills already synced \u2014 skipped"));
|
|
2250
|
-
} else {
|
|
2251
|
-
for (const name2 of globalSyncResult.createdAgents) {
|
|
2252
|
-
console.log(pc7.green(`\u2713 ~global agent added: ${name2}`));
|
|
2253
|
-
}
|
|
2254
|
-
for (const name2 of globalSyncResult.createdSkills) {
|
|
2255
|
-
console.log(pc7.green(`\u2713 ~global skill added: ${name2}`));
|
|
2256
|
-
}
|
|
2257
|
-
}
|
|
2258
|
-
}
|
|
2259
2150
|
console.log("");
|
|
2260
2151
|
console.log(pc7.cyan("\u2192") + ` Edit ${pc7.cyan("health.sh")} with your project checks`);
|
|
2261
2152
|
console.log(pc7.cyan("\u2192") + ` ${pc7.cyan("ahk task add")} to queue work for agents`);
|
|
@@ -2314,7 +2205,7 @@ async function runMigrate(cwd2, opts) {
|
|
|
2314
2205
|
|
|
2315
2206
|
// src/commands/migrate-storage.ts
|
|
2316
2207
|
import { copyFileSync, existsSync as existsSync12, mkdirSync as mkdirSync8, rmSync, writeFileSync as writeFileSync9 } from "fs";
|
|
2317
|
-
import { homedir
|
|
2208
|
+
import { homedir } from "os";
|
|
2318
2209
|
import { dirname as dirname7, join as join15, resolve as resolve8 } from "path";
|
|
2319
2210
|
import pc9 from "picocolors";
|
|
2320
2211
|
function log5(msg) {
|
|
@@ -2351,7 +2242,7 @@ function copySqliteFile(srcPath, destPath) {
|
|
|
2351
2242
|
throw new Error(`Copy verification failed: ${destPath} does not exist after copy.`);
|
|
2352
2243
|
}
|
|
2353
2244
|
}
|
|
2354
|
-
async function runMigrateStorage(cwd2, opts, homeDir =
|
|
2245
|
+
async function runMigrateStorage(cwd2, opts, homeDir = homedir()) {
|
|
2355
2246
|
const config = await loadConfig(cwd2);
|
|
2356
2247
|
const storageDir = config.storage.dir;
|
|
2357
2248
|
const state = readStorageStateFile(cwd2, storageDir);
|