wildcrane-runner 0.6.17 → 0.6.19
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 +12 -0
- package/dist/akb/dist/kanban.mjs +569 -455
- package/dist/akb/package.json +1 -1
- package/dist/launcher.mjs +4927 -31
- package/dist/runner.mjs +1022 -652
- package/package.json +1 -1
package/dist/akb/dist/kanban.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// ai4kanban 0.11.
|
|
2
|
+
// ai4kanban 0.11.17 — built by runner/scripts/build.mjs. Do not edit.
|
|
3
3
|
import { createRequire as __ai4kanbanRequire } from 'node:module'
|
|
4
4
|
const require = __ai4kanbanRequire(import.meta.url)
|
|
5
5
|
var __create = Object.create;
|
|
@@ -1167,7 +1167,7 @@ var require_command = __commonJS({
|
|
|
1167
1167
|
"../cli/node_modules/commander/lib/command.js"(exports) {
|
|
1168
1168
|
var EventEmitter = __require("node:events").EventEmitter;
|
|
1169
1169
|
var childProcess = __require("node:child_process");
|
|
1170
|
-
var
|
|
1170
|
+
var path88 = __require("node:path");
|
|
1171
1171
|
var fs87 = __require("node:fs");
|
|
1172
1172
|
var process2 = __require("node:process");
|
|
1173
1173
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
@@ -2167,9 +2167,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2167
2167
|
let launchWithNode = false;
|
|
2168
2168
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
2169
2169
|
function findFile(baseDir, baseName) {
|
|
2170
|
-
const localBin =
|
|
2170
|
+
const localBin = path88.resolve(baseDir, baseName);
|
|
2171
2171
|
if (fs87.existsSync(localBin)) return localBin;
|
|
2172
|
-
if (sourceExt.includes(
|
|
2172
|
+
if (sourceExt.includes(path88.extname(baseName))) return void 0;
|
|
2173
2173
|
const foundExt = sourceExt.find(
|
|
2174
2174
|
(ext) => fs87.existsSync(`${localBin}${ext}`)
|
|
2175
2175
|
);
|
|
@@ -2187,17 +2187,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2187
2187
|
} catch {
|
|
2188
2188
|
resolvedScriptPath = this._scriptPath;
|
|
2189
2189
|
}
|
|
2190
|
-
executableDir =
|
|
2191
|
-
|
|
2190
|
+
executableDir = path88.resolve(
|
|
2191
|
+
path88.dirname(resolvedScriptPath),
|
|
2192
2192
|
executableDir
|
|
2193
2193
|
);
|
|
2194
2194
|
}
|
|
2195
2195
|
if (executableDir) {
|
|
2196
2196
|
let localFile = findFile(executableDir, executableFile);
|
|
2197
2197
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
2198
|
-
const legacyName =
|
|
2198
|
+
const legacyName = path88.basename(
|
|
2199
2199
|
this._scriptPath,
|
|
2200
|
-
|
|
2200
|
+
path88.extname(this._scriptPath)
|
|
2201
2201
|
);
|
|
2202
2202
|
if (legacyName !== this._name) {
|
|
2203
2203
|
localFile = findFile(
|
|
@@ -2208,7 +2208,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2208
2208
|
}
|
|
2209
2209
|
executableFile = localFile || executableFile;
|
|
2210
2210
|
}
|
|
2211
|
-
launchWithNode = sourceExt.includes(
|
|
2211
|
+
launchWithNode = sourceExt.includes(path88.extname(executableFile));
|
|
2212
2212
|
let proc;
|
|
2213
2213
|
if (process2.platform !== "win32") {
|
|
2214
2214
|
if (launchWithNode) {
|
|
@@ -3055,7 +3055,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3055
3055
|
* @return {Command}
|
|
3056
3056
|
*/
|
|
3057
3057
|
nameFromFilename(filename) {
|
|
3058
|
-
this._name =
|
|
3058
|
+
this._name = path88.basename(filename, path88.extname(filename));
|
|
3059
3059
|
return this;
|
|
3060
3060
|
}
|
|
3061
3061
|
/**
|
|
@@ -3069,9 +3069,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3069
3069
|
* @param {string} [path]
|
|
3070
3070
|
* @return {(string|null|Command)}
|
|
3071
3071
|
*/
|
|
3072
|
-
executableDir(
|
|
3073
|
-
if (
|
|
3074
|
-
this._executableDir =
|
|
3072
|
+
executableDir(path89) {
|
|
3073
|
+
if (path89 === void 0) return this._executableDir;
|
|
3074
|
+
this._executableDir = path89;
|
|
3075
3075
|
return this;
|
|
3076
3076
|
}
|
|
3077
3077
|
/**
|
|
@@ -3487,11 +3487,11 @@ function projectStateDir(board2, root = stateRootOf(board2)) {
|
|
|
3487
3487
|
function ensureProjectState(board2, root = stateRootOf(board2)) {
|
|
3488
3488
|
const dir = projectStateDir(board2, root);
|
|
3489
3489
|
if (!fs.existsSync(dir)) {
|
|
3490
|
-
const
|
|
3490
|
+
const legacy2 = legacyProjectStateDir(board2);
|
|
3491
3491
|
fs.mkdirSync(path2.dirname(dir), { recursive: true });
|
|
3492
3492
|
const temporary = fs.mkdtempSync(`${dir}.import-`);
|
|
3493
3493
|
try {
|
|
3494
|
-
if (fs.existsSync(
|
|
3494
|
+
if (fs.existsSync(legacy2)) fs.cpSync(legacy2, temporary, {
|
|
3495
3495
|
recursive: true,
|
|
3496
3496
|
filter: (source) => ![".lock", ".answering"].some((suffix) => path2.basename(source).endsWith(suffix))
|
|
3497
3497
|
});
|
|
@@ -3842,6 +3842,9 @@ async function refresh() {
|
|
|
3842
3842
|
}
|
|
3843
3843
|
const fresh = sessionFrom(await response.json(), now);
|
|
3844
3844
|
if (!fresh) return { ok: false, reason: "expired" };
|
|
3845
|
+
const current = readSession();
|
|
3846
|
+
if (!current || current.subject !== now.subject) return { ok: false, reason: "expired" };
|
|
3847
|
+
if (current.refreshToken !== now.refreshToken) return { ok: true, token: current.accessToken, session: current };
|
|
3845
3848
|
writeSession(fresh);
|
|
3846
3849
|
return { ok: true, token: fresh.accessToken, session: fresh };
|
|
3847
3850
|
} finally {
|
|
@@ -4515,13 +4518,13 @@ function parse(file, lenient = false) {
|
|
|
4515
4518
|
if (i >= lines.length) return null;
|
|
4516
4519
|
const required = lenient ? ["source_id"] : FIELDS;
|
|
4517
4520
|
if (required.some((field2) => !held4[field2])) return null;
|
|
4518
|
-
const
|
|
4519
|
-
const missed =
|
|
4521
|
+
const legacy2 = held4.source_type ? "" : held4.source || held4.platform || "";
|
|
4522
|
+
const missed = legacy2 && !matchSourceType(legacy2) ? metaPair("source", legacy2) : null;
|
|
4520
4523
|
return {
|
|
4521
4524
|
sourceId: held4.source_id,
|
|
4522
4525
|
title: held4.title ?? "",
|
|
4523
4526
|
summary: lines.slice(i + 1).join("\n").replace(/^\n+/, "").replace(/\s+$/, ""),
|
|
4524
|
-
sourceType: held4.source_type ? readSourceType(held4.source_type) : matchSourceType(
|
|
4527
|
+
sourceType: held4.source_type ? readSourceType(held4.source_type) : matchSourceType(legacy2),
|
|
4525
4528
|
meta: missed ? [missed, ...written] : written,
|
|
4526
4529
|
url: held4.url || "",
|
|
4527
4530
|
...held4.agent ? { agent: held4.agent } : {},
|
|
@@ -5379,8 +5382,8 @@ function renamedPlan(rel2) {
|
|
|
5379
5382
|
return found2.sort((a, b) => b.at - a.at)[0].rel;
|
|
5380
5383
|
}
|
|
5381
5384
|
function planFromText(text5) {
|
|
5382
|
-
const
|
|
5383
|
-
if (text5.startsWith(
|
|
5385
|
+
const legacy2 = `${legacyProjectStateDir(KANBAN)}/`;
|
|
5386
|
+
if (text5.startsWith(legacy2)) text5 = text5.slice(legacy2.length);
|
|
5384
5387
|
const local = `${path15.dirname(PLANS)}/`;
|
|
5385
5388
|
if (text5.startsWith(local)) text5 = text5.slice(local.length);
|
|
5386
5389
|
if (text5.startsWith(`${KANBAN}/`)) text5 = text5.slice(KANBAN.length + 1);
|
|
@@ -5514,7 +5517,7 @@ var init_package = __esm({
|
|
|
5514
5517
|
"../cli/package.json"() {
|
|
5515
5518
|
package_default = {
|
|
5516
5519
|
name: "wildcrane",
|
|
5517
|
-
version: "0.11.
|
|
5520
|
+
version: "0.11.17",
|
|
5518
5521
|
akbContract: 5,
|
|
5519
5522
|
description: "WildCrane's `akb` command \u2014 scaffold the Markdown board under docs/kanban/, put an agent on a card, and carry the flows the agent works by.",
|
|
5520
5523
|
keywords: [
|
|
@@ -6115,7 +6118,7 @@ var init_file_memo = __esm({
|
|
|
6115
6118
|
var AGENT_default;
|
|
6116
6119
|
var init_AGENT = __esm({
|
|
6117
6120
|
"../cli/src/agents/builder/AGENT.md"() {
|
|
6118
|
-
AGENT_default = "---\nname: builder\ndescription:
|
|
6121
|
+
AGENT_default = "---\nname: builder\ndescription: Implement and land approved software changes.\n---\n\n- **Autonomy**: Decide technical details within the approved scope and existing permissions. Diagnose build problems, fix them, verify the result, and continue; do not repeat the same failed attempt instead of investigating. Ask the user only for decisions or blockers only they can resolve.\n- **Approved UI previews**: Implement every screen in the approved previews, compare desktop and mobile layouts, and correct differences.\n- **Approved API previews**: Implement every approved endpoint and document it.\n- **Approved database previews**: Write migrations that match the approved structure.\n- **Completion**: Finish when changes have landed and checks pass, unless deployment or publication is the task's goal. Do not push, deploy, or publish merely to finish a task.\n- **Production steps**: Hand the user any steps or validation that require production access. Mention them only in the final response; create no follow-up tasks and ask no questions.\n- **Checks**: Nobody reviews the implementation after you. Run targeted checks for the affected paths and all checks the repository requires for the changes.\n";
|
|
6119
6122
|
}
|
|
6120
6123
|
});
|
|
6121
6124
|
|
|
@@ -7512,13 +7515,15 @@ var init_identity = __esm({
|
|
|
7512
7515
|
});
|
|
7513
7516
|
|
|
7514
7517
|
// ../cli/src/lib/agent/harnesses/types.ts
|
|
7518
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
7515
7519
|
import fs22 from "node:fs";
|
|
7516
7520
|
import path24 from "node:path";
|
|
7517
7521
|
function namesFlag(argv, flags) {
|
|
7518
7522
|
return argv.some((tok) => flags.some((flag) => tok === flag || tok.startsWith(`${flag}=`)));
|
|
7519
7523
|
}
|
|
7520
7524
|
function machineFile(name, text5) {
|
|
7521
|
-
const
|
|
7525
|
+
const suffix = createHash2("sha256").update(text5).digest("hex").slice(0, 16);
|
|
7526
|
+
const file = path24.join(machineHome(), "prompts", `${path24.parse(name).name}-${suffix}${path24.extname(name)}`);
|
|
7522
7527
|
try {
|
|
7523
7528
|
if (fs22.readFileSync(file, "utf8") === text5) return file;
|
|
7524
7529
|
} catch {
|
|
@@ -8353,10 +8358,10 @@ function findRollout(threadId) {
|
|
|
8353
8358
|
}
|
|
8354
8359
|
for (const name of names4) {
|
|
8355
8360
|
if (!name.startsWith("rollout-") || !name.endsWith(tail2)) continue;
|
|
8356
|
-
const
|
|
8361
|
+
const path88 = join(dir, name);
|
|
8357
8362
|
try {
|
|
8358
|
-
const at = statSync(
|
|
8359
|
-
if (!newest2 || at > newest2.at) newest2 = { path:
|
|
8363
|
+
const at = statSync(path88).mtimeMs;
|
|
8364
|
+
if (!newest2 || at > newest2.at) newest2 = { path: path88, at };
|
|
8360
8365
|
} catch {
|
|
8361
8366
|
}
|
|
8362
8367
|
}
|
|
@@ -8364,11 +8369,11 @@ function findRollout(threadId) {
|
|
|
8364
8369
|
return newest2?.path;
|
|
8365
8370
|
}
|
|
8366
8371
|
function readCodexRunFacts(threadId) {
|
|
8367
|
-
const
|
|
8368
|
-
if (!
|
|
8372
|
+
const path88 = findRollout(threadId);
|
|
8373
|
+
if (!path88) return void 0;
|
|
8369
8374
|
let raw;
|
|
8370
8375
|
try {
|
|
8371
|
-
raw = readFileSync(
|
|
8376
|
+
raw = readFileSync(path88, "utf8");
|
|
8372
8377
|
} catch {
|
|
8373
8378
|
return void 0;
|
|
8374
8379
|
}
|
|
@@ -8496,14 +8501,19 @@ function renderCompleted(item) {
|
|
|
8496
8501
|
const changes = Array.isArray(item.changes) ? item.changes : [];
|
|
8497
8502
|
return changes.map((raw) => {
|
|
8498
8503
|
const change2 = obj(raw);
|
|
8499
|
-
const
|
|
8500
|
-
return
|
|
8504
|
+
const path88 = str2(change2.path);
|
|
8505
|
+
return path88 ? `\u23FA ${str2(change2.kind) || "change"}(${path88})
|
|
8501
8506
|
` : "";
|
|
8502
8507
|
}).join("");
|
|
8503
8508
|
}
|
|
8504
8509
|
case "error":
|
|
8505
8510
|
return `[error] ${str2(item.message)}
|
|
8506
8511
|
`;
|
|
8512
|
+
case "mcp_tool_call": {
|
|
8513
|
+
const error = str2(obj(item.error).message);
|
|
8514
|
+
return error ? `[MCP ${str2(item.server)}.${str2(item.tool)}] ${error}
|
|
8515
|
+
` : "";
|
|
8516
|
+
}
|
|
8507
8517
|
default:
|
|
8508
8518
|
return "";
|
|
8509
8519
|
}
|
|
@@ -8617,16 +8627,13 @@ function createCodexStreamRenderer(carried = false) {
|
|
|
8617
8627
|
return renderCompleted(item);
|
|
8618
8628
|
}
|
|
8619
8629
|
case "turn.completed":
|
|
8630
|
+
failure = void 0;
|
|
8620
8631
|
total = codexUsage(ev.usage) ?? total;
|
|
8621
8632
|
return "";
|
|
8622
|
-
//
|
|
8623
|
-
// stream itself failing. Kept as the run's failure (#525), because `codex exec`
|
|
8624
|
-
// reports its reason on this stream and nowhere else, and a retry has to read it.
|
|
8625
|
-
// An item's own `error` is not one of them: a tool that failed is the agent's
|
|
8626
|
-
// problem to work around, and the turn around it may still finish.
|
|
8633
|
+
// 最终失败保留;连接提示只在没有后续成功完成时才作为失败。
|
|
8627
8634
|
case "turn.failed": {
|
|
8628
|
-
const said3 = str2(obj(ev.error).message);
|
|
8629
|
-
failure = said3
|
|
8635
|
+
const said3 = str2(obj(ev.error).message) || failure || "Codex \u6267\u884C\u5931\u8D25";
|
|
8636
|
+
failure = said3;
|
|
8630
8637
|
events.error(said3);
|
|
8631
8638
|
return `[error] ${said3}
|
|
8632
8639
|
`;
|
|
@@ -8634,8 +8641,9 @@ function createCodexStreamRenderer(carried = false) {
|
|
|
8634
8641
|
case "error": {
|
|
8635
8642
|
const said3 = str2(ev.message);
|
|
8636
8643
|
failure = said3 || failure;
|
|
8637
|
-
events.
|
|
8638
|
-
|
|
8644
|
+
events.log(`[Codex] ${said3}
|
|
8645
|
+
`);
|
|
8646
|
+
return `[Codex] ${said3}
|
|
8639
8647
|
`;
|
|
8640
8648
|
}
|
|
8641
8649
|
default:
|
|
@@ -8756,10 +8764,10 @@ function readKimiRunFacts(sessionId) {
|
|
|
8756
8764
|
const total = { input: 0, cacheCreation: 0, cacheRead: 0, output: 0 };
|
|
8757
8765
|
let model;
|
|
8758
8766
|
let holding = 0;
|
|
8759
|
-
for (const { path:
|
|
8767
|
+
for (const { path: path88, main } of wireFiles(entry.sessionDir)) {
|
|
8760
8768
|
let raw;
|
|
8761
8769
|
try {
|
|
8762
|
-
raw = readFileSync2(
|
|
8770
|
+
raw = readFileSync2(path88, "utf8");
|
|
8763
8771
|
} catch {
|
|
8764
8772
|
continue;
|
|
8765
8773
|
}
|
|
@@ -9040,8 +9048,8 @@ var init_tail = __esm({
|
|
|
9040
9048
|
import fs24 from "node:fs";
|
|
9041
9049
|
function whereHint(update2, title) {
|
|
9042
9050
|
const locations = Array.isArray(update2.locations) ? update2.locations : [];
|
|
9043
|
-
const
|
|
9044
|
-
return
|
|
9051
|
+
const path88 = str2(obj(locations[0]).path);
|
|
9052
|
+
return path88 && !title.includes(path88) ? hint(path88) : "";
|
|
9045
9053
|
}
|
|
9046
9054
|
function saidBy(update2) {
|
|
9047
9055
|
const content = Array.isArray(update2.content) ? update2.content : [];
|
|
@@ -9747,9 +9755,12 @@ var init_claude_code = __esm({
|
|
|
9747
9755
|
forkArgs(argv, resumeId, sessionId) {
|
|
9748
9756
|
return [...claudeStreamArgs(argv), "--resume", resumeId, "--fork-session", "--session-id", sessionId];
|
|
9749
9757
|
},
|
|
9750
|
-
//
|
|
9758
|
+
// 默认快照会忽略续跑传入的新规则;仅显式关闭快照的会话支持更新。
|
|
9751
9759
|
systemPrompt(text5, argv) {
|
|
9752
|
-
|
|
9760
|
+
if (namesFlag(argv, ["--append-system-prompt", "--append-system-prompt-file"])) return {};
|
|
9761
|
+
const snapshot = argv.flatMap((arg, i) => arg === "--system-prompt-snapshot" ? [argv[i + 1]] : arg.startsWith("--system-prompt-snapshot=") ? [arg.slice("--system-prompt-snapshot=".length)] : []).at(-1);
|
|
9762
|
+
if (namesFlag(argv, ["--resume", "-r", "--continue", "-c"]) && snapshot !== "off") return {};
|
|
9763
|
+
return { args: ["--append-system-prompt", text5] };
|
|
9753
9764
|
},
|
|
9754
9765
|
// What Claude Code takes: who pays for the run, where that run goes, a model id, and how
|
|
9755
9766
|
// hard that model thinks. The provider comes first because it decides the rest — which
|
|
@@ -9978,6 +9989,46 @@ var init_claude_code = __esm({
|
|
|
9978
9989
|
}
|
|
9979
9990
|
});
|
|
9980
9991
|
|
|
9992
|
+
// ../cli/src/lib/agent/proxy.ts
|
|
9993
|
+
import { execFileSync } from "node:child_process";
|
|
9994
|
+
function codexProxyEnv(env2 = process.env, platform = process.platform, read4 = () => execFileSync("/usr/sbin/scutil", ["--proxy"], { encoding: "utf8", timeout: 1e3, stdio: ["ignore", "pipe", "ignore"] })) {
|
|
9995
|
+
const out = { ...env2 };
|
|
9996
|
+
if (platform !== "darwin" || PROXY_KEYS.some((key) => env2[key] !== void 0)) return out;
|
|
9997
|
+
let raw;
|
|
9998
|
+
try {
|
|
9999
|
+
raw = read4();
|
|
10000
|
+
} catch {
|
|
10001
|
+
return out;
|
|
10002
|
+
}
|
|
10003
|
+
const value = (key) => new RegExp(`^\\s*${key}\\s*:\\s*(.*?)\\s*$`, "m").exec(raw)?.[1];
|
|
10004
|
+
for (const [name, key, scheme] of [["HTTP", "HTTP_PROXY", "http"], ["HTTPS", "HTTPS_PROXY", "http"], ["SOCKS", "ALL_PROXY", "socks5"]]) {
|
|
10005
|
+
if (value(`${name}Enable`) !== "1") continue;
|
|
10006
|
+
const host2 = value(`${name}Proxy`);
|
|
10007
|
+
const port = Number(value(`${name}Port`));
|
|
10008
|
+
if (!host2 || /[\s/@?#]/.test(host2) || !Number.isInteger(port) || port < 1 || port > 65535) continue;
|
|
10009
|
+
try {
|
|
10010
|
+
out[key] = new URL(`${scheme}://${host2.includes(":") && !host2.startsWith("[") ? `[${host2}]` : host2}:${port}`).href.replace(/\/$/, "");
|
|
10011
|
+
} catch {
|
|
10012
|
+
}
|
|
10013
|
+
}
|
|
10014
|
+
if (PROXY_KEYS.some((key) => out[key]) && env2.NO_PROXY === void 0 && env2.no_proxy === void 0) {
|
|
10015
|
+
const exceptions = /ExceptionsList\s*:\s*<array>\s*\{([^}]+)\}/.exec(raw)?.[1];
|
|
10016
|
+
const bypass = exceptions?.split("\n").flatMap((line) => {
|
|
10017
|
+
const host2 = /^\s*\d+\s*:\s*(\S+)\s*$/.exec(line)?.[1];
|
|
10018
|
+
return host2 ? [host2.startsWith("*.") ? host2.slice(1) : host2] : [];
|
|
10019
|
+
});
|
|
10020
|
+
if (bypass?.length) out.NO_PROXY = bypass.join(",");
|
|
10021
|
+
}
|
|
10022
|
+
return out;
|
|
10023
|
+
}
|
|
10024
|
+
var PROXY_KEYS;
|
|
10025
|
+
var init_proxy = __esm({
|
|
10026
|
+
"../cli/src/lib/agent/proxy.ts"() {
|
|
10027
|
+
"use strict";
|
|
10028
|
+
PROXY_KEYS = ["HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy", "ALL_PROXY", "all_proxy"];
|
|
10029
|
+
}
|
|
10030
|
+
});
|
|
10031
|
+
|
|
9981
10032
|
// ../cli/src/lib/agent/worktree.ts
|
|
9982
10033
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
9983
10034
|
import crypto3 from "node:crypto";
|
|
@@ -10430,6 +10481,7 @@ var init_codex = __esm({
|
|
|
10430
10481
|
init_paths();
|
|
10431
10482
|
init_wire();
|
|
10432
10483
|
init_locate();
|
|
10484
|
+
init_proxy();
|
|
10433
10485
|
init_models();
|
|
10434
10486
|
init_worktree();
|
|
10435
10487
|
init_transient();
|
|
@@ -10464,10 +10516,15 @@ var init_codex = __esm({
|
|
|
10464
10516
|
forkArgs(argv, resumeId, _sessionId, cwd) {
|
|
10465
10517
|
return [...codexExtraArgs(argv, cwd), "fork", resumeId];
|
|
10466
10518
|
},
|
|
10467
|
-
//
|
|
10468
|
-
// `developer_instructions` in the user's config.toml, which is then left out of board runs.
|
|
10519
|
+
// 自设系统指令保持原样;无法追加时由调用方明确提示。
|
|
10469
10520
|
systemPrompt(text5, argv) {
|
|
10470
|
-
|
|
10521
|
+
if (namesFlag(argv, ["developer_instructions", "model_instructions_file"])) return {};
|
|
10522
|
+
try {
|
|
10523
|
+
const config = fs26.readFileSync(path29.join(process.env.CODEX_HOME || home2(".codex"), "config.toml"), "utf8");
|
|
10524
|
+
if (/^\s*(?:developer_instructions|model_instructions_file)\s*=/m.test(config)) return {};
|
|
10525
|
+
} catch {
|
|
10526
|
+
}
|
|
10527
|
+
return { args: ["-c", `developer_instructions=${JSON.stringify(text5)}`] };
|
|
10471
10528
|
},
|
|
10472
10529
|
// What Codex takes, in the order Claude Code's dialog draws the same four: who pays for
|
|
10473
10530
|
// the run and where it goes, that endpoint's address, the key, a model, and how hard that
|
|
@@ -10595,7 +10652,7 @@ var init_codex = __esm({
|
|
|
10595
10652
|
// No `providerEnv` either: the pick already drops and sets OPENAI_API_KEY, and there is
|
|
10596
10653
|
// no second variable to drop. Codex reads no base URL from the environment — an exported
|
|
10597
10654
|
// OPENAI_BASE_URL moves nothing — and where a run goes is settled on its command line.
|
|
10598
|
-
env: () => (
|
|
10655
|
+
env: () => codexProxyEnv(),
|
|
10599
10656
|
// A completed turn carries its token counts and nothing else. The model comes from the
|
|
10600
10657
|
// session's rollout on disk instead, and the price is worked out from the two
|
|
10601
10658
|
// (agent/wire/codex-stream.ts). A model whose rates the board doesn't know shows no price.
|
|
@@ -10806,9 +10863,9 @@ Verify your work by running the code or tests. Keep answers brief and factual.`;
|
|
|
10806
10863
|
// dsh-acp's persona IS its system prompt, so the ask is added to the one in effect: the
|
|
10807
10864
|
// user's own `DSH_SYSTEM_PROMPT`, else the bridge's default. A `--persona` on the command
|
|
10808
10865
|
// line outranks both, and is left alone.
|
|
10809
|
-
systemPrompt(text5, argv) {
|
|
10866
|
+
systemPrompt(text5, argv, env2) {
|
|
10810
10867
|
if (namesFlag(argv, ["--persona"])) return {};
|
|
10811
|
-
return { env: { DSH_SYSTEM_PROMPT: `${
|
|
10868
|
+
return { env: { DSH_SYSTEM_PROMPT: `${env2.DSH_SYSTEM_PROMPT || DSH_PERSONA}
|
|
10812
10869
|
|
|
10813
10870
|
${text5}` } };
|
|
10814
10871
|
},
|
|
@@ -11226,12 +11283,12 @@ var init_opencode = __esm({
|
|
|
11226
11283
|
env: () => ({ ...process.env }),
|
|
11227
11284
|
// `instructions` files join the system prompt, and OpenCode adds this config's list to the
|
|
11228
11285
|
// user's own rather than replacing it.
|
|
11229
|
-
systemPrompt(text5) {
|
|
11286
|
+
systemPrompt(text5, _argv, env2) {
|
|
11230
11287
|
const file = machineFile("opencode-language.md", `${text5}
|
|
11231
11288
|
`);
|
|
11232
11289
|
let own = {};
|
|
11233
11290
|
try {
|
|
11234
|
-
own = obj2(JSON.parse(
|
|
11291
|
+
own = obj2(JSON.parse(env2.OPENCODE_CONFIG_CONTENT ?? "{}"));
|
|
11235
11292
|
} catch {
|
|
11236
11293
|
}
|
|
11237
11294
|
const instructions = [...Array.isArray(own.instructions) ? own.instructions : [], file];
|
|
@@ -11484,26 +11541,36 @@ var init_capabilities = __esm({
|
|
|
11484
11541
|
});
|
|
11485
11542
|
|
|
11486
11543
|
// ../cli/src/lib/agent/language.ts
|
|
11487
|
-
function
|
|
11488
|
-
|
|
11544
|
+
function runLanguage() {
|
|
11545
|
+
if (process.env[LANGUAGE_ENV] !== void 0) return languageCode(process.env[LANGUAGE_ENV]);
|
|
11546
|
+
if (process.env[LANGUAGE_LINE_ENV] !== void 0) return legacyLanguage(process.env[LANGUAGE_LINE_ENV]);
|
|
11547
|
+
return readLanguage();
|
|
11489
11548
|
}
|
|
11490
|
-
var LANGUAGE_LINE_ENV,
|
|
11549
|
+
var LANGUAGE_ENV, LANGUAGE_LINE_ENV, SYSTEM_LANGUAGE_ZH, SYSTEM_LANGUAGE_EN, systemLanguages, isSystemLanguage, legacy, languageCode, legacyLanguage, translating, systemLanguage;
|
|
11491
11550
|
var init_language = __esm({
|
|
11492
11551
|
"../cli/src/lib/agent/language.ts"() {
|
|
11493
11552
|
"use strict";
|
|
11494
11553
|
init_settings();
|
|
11554
|
+
LANGUAGE_ENV = "AI4KANBAN_LANGUAGE";
|
|
11495
11555
|
LANGUAGE_LINE_ENV = "AI4KANBAN_LANGUAGE_LINE";
|
|
11496
|
-
|
|
11497
|
-
|
|
11498
|
-
|
|
11499
|
-
|
|
11500
|
-
|
|
11501
|
-
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11556
|
+
SYSTEM_LANGUAGE_ZH = "\u7528\u4E2D\u6587\u56DE\u590D\u3002\u6210\u679C\u8BED\u8A00\u4F9D\u6B21\u9075\u5FAA\u7528\u6237\u660E\u786E\u8981\u6C42\u3001\u6210\u679C\u4E13\u7528\u6307\u4EE4\u3001\u65E2\u6709\u6210\u679C\u6216\u9879\u76EE\u60EF\u4F8B\uFF0C\u6700\u540E\u91C7\u7528\u672C\u6B21\u8BF7\u6C42\u7684\u8BED\u8A00\u3002\u4EE3\u7801\u3001\u547D\u4EE4\u3001\u6587\u4EF6\u540D\u548C\u5B57\u6BB5\u540D\u4FDD\u6301\u539F\u6587\u3002";
|
|
11557
|
+
SYSTEM_LANGUAGE_EN = "Reply in English. Artifact language follows explicit user requests, artifact instructions, existing or project conventions, then the current request's language. Keep code, commands, filenames, and field names unchanged.";
|
|
11558
|
+
systemLanguages = /* @__PURE__ */ new Set([
|
|
11559
|
+
SYSTEM_LANGUAGE_ZH,
|
|
11560
|
+
SYSTEM_LANGUAGE_EN,
|
|
11561
|
+
"\u65E0\u8BBA\u5BF9\u8BDD\u91CC\u7684\u5361\u7247\u3001\u4EE3\u7801\u548C\u8D34\u8FDB\u6765\u7684\u5185\u5BB9\u662F\u4EC0\u4E48\u8BED\u8A00\uFF0C\u56DE\u590D\u548C\u5199\u4E0B\u7684\u5185\u5BB9\u90FD\u7528\u4E2D\u6587\u3002\u4EE3\u7801\u3001\u547D\u4EE4\u3001\u6587\u4EF6\u540D\u548C\u5B57\u6BB5\u540D\u4FDD\u6301\u539F\u6587\u3002",
|
|
11562
|
+
"\u5BF9\u8BDD\u548C\u89E3\u91CA\u4F7F\u7528\u4E2D\u6587\uFF1B\u4EA7\u7269\u4F7F\u7528\u7528\u6237\u660E\u786E\u6307\u5B9A\u7684\u8BED\u8A00\uFF0C\u5426\u5219\u4F7F\u7528\u6240\u5C5E agent \u660E\u786E\u6307\u5B9A\u7684\u8BED\u8A00\uFF0C\u5747\u672A\u6307\u5B9A\u65F6\u4F7F\u7528\u4E2D\u6587\u3002\u4EE3\u7801\u3001\u547D\u4EE4\u3001\u6587\u4EF6\u540D\u548C\u5B57\u6BB5\u540D\u4FDD\u6301\u539F\u6587\u3002",
|
|
11563
|
+
"Use English for conversation and explanations. For artifacts, use the language explicitly specified by the user, otherwise the responsible agent, otherwise English. Keep code, commands, filenames, and field names unchanged."
|
|
11564
|
+
]);
|
|
11565
|
+
isSystemLanguage = (text5) => systemLanguages.has(text5);
|
|
11566
|
+
legacy = /* @__PURE__ */ new Map([
|
|
11567
|
+
["\u8BF7\u7528\u4E2D\u6587\u56DE\u590D\u548C\u5199\u4F5C\u3002", "zh"],
|
|
11568
|
+
["Reply and write in English.", "en"]
|
|
11569
|
+
]);
|
|
11570
|
+
languageCode = (value) => value === "zh" || value === "en" ? value : null;
|
|
11571
|
+
legacyLanguage = (line) => typeof line === "string" ? legacy.get(line) ?? null : null;
|
|
11572
|
+
translating = () => runLanguage() === "zh";
|
|
11573
|
+
systemLanguage = (language) => language === "zh" ? SYSTEM_LANGUAGE_ZH : language === "en" ? SYSTEM_LANGUAGE_EN : "";
|
|
11507
11574
|
}
|
|
11508
11575
|
});
|
|
11509
11576
|
|
|
@@ -11537,6 +11604,7 @@ var init_providers = __esm({
|
|
|
11537
11604
|
});
|
|
11538
11605
|
|
|
11539
11606
|
// ../cli/src/lib/agent/run-settings.ts
|
|
11607
|
+
import path31 from "node:path";
|
|
11540
11608
|
function isFilled(harness, { values, secretsSet }) {
|
|
11541
11609
|
return (key) => {
|
|
11542
11610
|
const setting = harness.settings.find((s) => s.key === key);
|
|
@@ -11606,15 +11674,39 @@ function effectiveValues(harness, held4) {
|
|
|
11606
11674
|
}
|
|
11607
11675
|
return out;
|
|
11608
11676
|
}
|
|
11609
|
-
function
|
|
11610
|
-
const
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
|
|
11677
|
+
function languageEnv(given) {
|
|
11678
|
+
const env2 = { ...given };
|
|
11679
|
+
if (env2.DSH_SYSTEM_PROMPT) {
|
|
11680
|
+
const parts = env2.DSH_SYSTEM_PROMPT.split("\n\n");
|
|
11681
|
+
while (isSystemLanguage(parts.at(-1) ?? "")) parts.pop();
|
|
11682
|
+
env2.DSH_SYSTEM_PROMPT = parts.join("\n\n");
|
|
11683
|
+
}
|
|
11684
|
+
try {
|
|
11685
|
+
const config = JSON.parse(env2.OPENCODE_CONFIG_CONTENT ?? "{}");
|
|
11686
|
+
if (Array.isArray(config.instructions)) {
|
|
11687
|
+
config.instructions = config.instructions.filter((file) => typeof file !== "string" || path31.dirname(file) !== path31.join(machineHome(), "prompts") || !/^opencode-language(?:-[a-f0-9]{16})?\.md$/.test(path31.basename(file)));
|
|
11688
|
+
env2.OPENCODE_CONFIG_CONTENT = JSON.stringify(config);
|
|
11689
|
+
}
|
|
11690
|
+
} catch {
|
|
11691
|
+
}
|
|
11692
|
+
return env2;
|
|
11693
|
+
}
|
|
11694
|
+
function withSystemLanguage(harness, argv, env2, language) {
|
|
11695
|
+
env2 = languageEnv(env2);
|
|
11696
|
+
const text5 = systemLanguage(language);
|
|
11697
|
+
if (!text5) return { argv, env: env2 };
|
|
11698
|
+
const added = argv.length ? harness.systemPrompt?.(text5, argv, env2) : void 0;
|
|
11699
|
+
if (!added?.args?.length && !Object.keys(added?.env ?? {}).length) {
|
|
11700
|
+
const note = language === "zh" ? "\u8BED\u8A00\u89C4\u5219\u672A\u80FD\u66F4\u65B0\uFF1A\u8FDE\u63A5\u5668\u3001\u5DF2\u6709\u4F1A\u8BDD\u6216\u81EA\u8BBE\u7CFB\u7EDF\u63D0\u793A\u8BCD\u4E0D\u652F\u6301\u6CE8\u5165\u3002\u8BF7\u81EA\u884C\u53E6\u5F00\u652F\u6301\u7684\u4F1A\u8BDD\u4EE5\u91C7\u7528\u65B0\u89C4\u5219\u3002" : "Language rules could not be updated: the connector, existing session, or custom system prompt prevents injection. Open a supported new session to apply them.";
|
|
11701
|
+
return { argv, env: env2, note };
|
|
11702
|
+
}
|
|
11703
|
+
const at = /^node(?:\.exe)?$/.test(path31.basename(argv[0])) && argv[1] && !argv[1].startsWith("-") ? 2 : 1;
|
|
11704
|
+
return { argv: [...argv.slice(0, at), ...added?.args ?? [], ...argv.slice(at)], env: { ...env2, ...added?.env } };
|
|
11614
11705
|
}
|
|
11615
11706
|
var init_run_settings = __esm({
|
|
11616
11707
|
"../cli/src/lib/agent/run-settings.ts"() {
|
|
11617
11708
|
"use strict";
|
|
11709
|
+
init_home();
|
|
11618
11710
|
init_argv();
|
|
11619
11711
|
init_env();
|
|
11620
11712
|
init_language();
|
|
@@ -11625,10 +11717,10 @@ var init_run_settings = __esm({
|
|
|
11625
11717
|
|
|
11626
11718
|
// ../cli/src/lib/agent/log.ts
|
|
11627
11719
|
import fs28 from "node:fs";
|
|
11628
|
-
import
|
|
11720
|
+
import path32 from "node:path";
|
|
11629
11721
|
function pruneLogs() {
|
|
11630
11722
|
try {
|
|
11631
|
-
const runs = fs28.readdirSync(SESSIONS_DIR).filter((f) => f.endsWith(".log") && !f.endsWith(".watch.log")).map((f) => ({ id: f.slice(0, -".log".length), t: fs28.statSync(
|
|
11723
|
+
const runs = fs28.readdirSync(SESSIONS_DIR).filter((f) => f.endsWith(".log") && !f.endsWith(".watch.log")).map((f) => ({ id: f.slice(0, -".log".length), t: fs28.statSync(path32.join(SESSIONS_DIR, f)).mtimeMs })).sort((a, b) => b.t - a.t);
|
|
11632
11724
|
const kept2 = new Set(runs.slice(0, KEEP_LOGS).map((r) => r.id));
|
|
11633
11725
|
const drop = new Set(runs.slice(KEEP_LOGS).map((r) => r.id));
|
|
11634
11726
|
for (const f of fs28.readdirSync(SESSIONS_DIR)) {
|
|
@@ -11637,7 +11729,7 @@ function pruneLogs() {
|
|
|
11637
11729
|
const stray = f.endsWith(".images") && !kept2.has(id) && !drop.has(id) && stale2(f);
|
|
11638
11730
|
if (!stray && !drop.has(id)) continue;
|
|
11639
11731
|
try {
|
|
11640
|
-
fs28.rmSync(
|
|
11732
|
+
fs28.rmSync(path32.join(SESSIONS_DIR, f), { recursive: true, force: true });
|
|
11641
11733
|
} catch {
|
|
11642
11734
|
}
|
|
11643
11735
|
}
|
|
@@ -11646,7 +11738,7 @@ function pruneLogs() {
|
|
|
11646
11738
|
}
|
|
11647
11739
|
function stale2(name) {
|
|
11648
11740
|
try {
|
|
11649
|
-
return Date.now() - fs28.statSync(
|
|
11741
|
+
return Date.now() - fs28.statSync(path32.join(SESSIONS_DIR, name)).mtimeMs > STRAY_MS;
|
|
11650
11742
|
} catch {
|
|
11651
11743
|
return false;
|
|
11652
11744
|
}
|
|
@@ -11783,9 +11875,9 @@ ${DURATION_MARKER} ${Math.max(0, ms)}
|
|
|
11783
11875
|
|
|
11784
11876
|
// ../cli/src/lib/agent/store.ts
|
|
11785
11877
|
import fs29 from "node:fs";
|
|
11786
|
-
import
|
|
11878
|
+
import path33 from "node:path";
|
|
11787
11879
|
function logPathIn(held4, sessionId) {
|
|
11788
|
-
return typeof held4 === "string" && held4.startsWith(SESSIONS_DIR +
|
|
11880
|
+
return typeof held4 === "string" && held4.startsWith(SESSIONS_DIR + path33.sep) ? held4 : logPathOf(sessionId);
|
|
11789
11881
|
}
|
|
11790
11882
|
function parseStore() {
|
|
11791
11883
|
let data;
|
|
@@ -12155,7 +12247,7 @@ function writeStore(store) {
|
|
|
12155
12247
|
marks: store.marks,
|
|
12156
12248
|
...store.refined ? { refined: store.refined } : {}
|
|
12157
12249
|
};
|
|
12158
|
-
fs29.mkdirSync(
|
|
12250
|
+
fs29.mkdirSync(path33.dirname(SESSIONS), { recursive: true });
|
|
12159
12251
|
const tmp = `${SESSIONS}.tmp`;
|
|
12160
12252
|
fs29.writeFileSync(tmp, JSON.stringify(kept2, null, 2) + "\n");
|
|
12161
12253
|
fs29.renameSync(tmp, SESSIONS);
|
|
@@ -12221,8 +12313,8 @@ var init_store = __esm({
|
|
|
12221
12313
|
init_log();
|
|
12222
12314
|
KEEP_RUNS = 100;
|
|
12223
12315
|
KEEP_DELIVERIES = 30;
|
|
12224
|
-
logPathOf = (sessionId) =>
|
|
12225
|
-
eventsPathOf = (sessionId) =>
|
|
12316
|
+
logPathOf = (sessionId) => path33.join(SESSIONS_DIR, `${sessionId}.log`);
|
|
12317
|
+
eventsPathOf = (sessionId) => path33.join(SESSIONS_DIR, `${sessionId}.events.jsonl`);
|
|
12226
12318
|
WAS_CLARIFY = /* @__PURE__ */ new Set(["auto-refine", "refine", "raise-questions"]);
|
|
12227
12319
|
RETIRED_ACTIONS = /* @__PURE__ */ new Set([
|
|
12228
12320
|
"channel",
|
|
@@ -12442,7 +12534,7 @@ var init_spec_agent_names = __esm({
|
|
|
12442
12534
|
const canonical = canonicalSpecAgent(name);
|
|
12443
12535
|
return [
|
|
12444
12536
|
canonical,
|
|
12445
|
-
...Object.entries(LEGACY_SPEC_AGENT_NAMES).filter(([, current]) => current === canonical).map(([
|
|
12537
|
+
...Object.entries(LEGACY_SPEC_AGENT_NAMES).filter(([, current]) => current === canonical).map(([legacy2]) => legacy2)
|
|
12446
12538
|
];
|
|
12447
12539
|
};
|
|
12448
12540
|
}
|
|
@@ -12470,7 +12562,7 @@ var init_types3 = __esm({
|
|
|
12470
12562
|
|
|
12471
12563
|
// ../cli/src/lib/agent/settings.ts
|
|
12472
12564
|
import fs30 from "node:fs";
|
|
12473
|
-
import
|
|
12565
|
+
import path34 from "node:path";
|
|
12474
12566
|
function configBlock(value) {
|
|
12475
12567
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
12476
12568
|
return value;
|
|
@@ -12495,7 +12587,7 @@ function adoptProjectNames(cfg) {
|
|
|
12495
12587
|
function adoptLegacyConfig() {
|
|
12496
12588
|
if (!LEGACY_UI_CONFIG || !fs30.existsSync(LEGACY_UI_CONFIG)) return;
|
|
12497
12589
|
if (!fs30.existsSync(UI_CONFIG)) {
|
|
12498
|
-
fs30.mkdirSync(
|
|
12590
|
+
fs30.mkdirSync(path34.dirname(UI_CONFIG), { recursive: true });
|
|
12499
12591
|
fs30.copyFileSync(LEGACY_UI_CONFIG, UI_CONFIG);
|
|
12500
12592
|
}
|
|
12501
12593
|
fs30.rmSync(LEGACY_UI_CONFIG, { force: true });
|
|
@@ -12588,7 +12680,7 @@ function writeConfig(change2) {
|
|
|
12588
12680
|
}
|
|
12589
12681
|
change2(cfg);
|
|
12590
12682
|
try {
|
|
12591
|
-
fs30.mkdirSync(
|
|
12683
|
+
fs30.mkdirSync(path34.dirname(UI_CONFIG), { recursive: true });
|
|
12592
12684
|
fs30.writeFileSync(UI_CONFIG, JSON.stringify(cfg, null, 2) + "\n");
|
|
12593
12685
|
forgetFileMemos();
|
|
12594
12686
|
return { ok: true };
|
|
@@ -12630,7 +12722,7 @@ function readEnvFile() {
|
|
|
12630
12722
|
function ensureIgnored() {
|
|
12631
12723
|
try {
|
|
12632
12724
|
if (!fs30.existsSync(KANBAN_GITIGNORE)) {
|
|
12633
|
-
fs30.mkdirSync(
|
|
12725
|
+
fs30.mkdirSync(path34.dirname(KANBAN_GITIGNORE), { recursive: true });
|
|
12634
12726
|
fs30.writeFileSync(KANBAN_GITIGNORE, IGNORE_BLOCK);
|
|
12635
12727
|
return { ok: true };
|
|
12636
12728
|
}
|
|
@@ -13215,12 +13307,12 @@ function openPlan(plan) {
|
|
|
13215
13307
|
const { harness } = resolved2;
|
|
13216
13308
|
const start = startableBinary(plan.argv[0] ?? "", harness, pinnedBy(resolved2.runtime.settings, harness));
|
|
13217
13309
|
const started = [start.binary, ...plan.argv.slice(1)];
|
|
13218
|
-
const { argv, env: planned } = withSystemLanguage(harness, started, runEnv2(resolved2, plan.cwd ?? REPO_ROOT),
|
|
13310
|
+
const { argv, env: planned, note } = withSystemLanguage(harness, started, runEnv2(resolved2, plan.cwd ?? REPO_ROOT), runLanguage());
|
|
13219
13311
|
const env2 = copyEnv(start.binary, planned);
|
|
13220
13312
|
return {
|
|
13221
13313
|
...plan,
|
|
13222
13314
|
argv,
|
|
13223
|
-
...start.note ? { startNote: start.note } : {},
|
|
13315
|
+
...[start.note, note].some(Boolean) ? { startNote: [start.note, note].filter(Boolean).join("\n") } : {},
|
|
13224
13316
|
env: env2,
|
|
13225
13317
|
// The folder and the binary: what a renderer needs to go looking for what the stream
|
|
13226
13318
|
// left out (agent/harnesses/types.ts). argv's first word is the command's own binary,
|
|
@@ -13442,7 +13534,7 @@ var init_slug = __esm({
|
|
|
13442
13534
|
|
|
13443
13535
|
// ../cli/src/lib/validate.ts
|
|
13444
13536
|
import fs32 from "node:fs";
|
|
13445
|
-
import
|
|
13537
|
+
import path35 from "node:path";
|
|
13446
13538
|
function moduleNames() {
|
|
13447
13539
|
if (!fs32.existsSync(MODULES_MD)) return null;
|
|
13448
13540
|
return moduleNamesFrom(fs32.readFileSync(MODULES_MD, "utf8"));
|
|
@@ -13479,7 +13571,7 @@ function dependencyCycle(id, dep) {
|
|
|
13479
13571
|
seen.add(at);
|
|
13480
13572
|
const found2 = locate(at);
|
|
13481
13573
|
if (!found2) return null;
|
|
13482
|
-
const file = found2.kind === "group" ?
|
|
13574
|
+
const file = found2.kind === "group" ? path35.join(found2.target, "root.md") : found2.target;
|
|
13483
13575
|
const nexts = parseFrontmatter(fs32.readFileSync(file, "utf8")).meta?.blocked_by ?? [];
|
|
13484
13576
|
if (found2.kind === "group") nexts.push(...groupMembers(found2.target));
|
|
13485
13577
|
for (const next of nexts) {
|
|
@@ -13492,7 +13584,7 @@ function dependencyCycle(id, dep) {
|
|
|
13492
13584
|
return rest && [id, ...rest];
|
|
13493
13585
|
}
|
|
13494
13586
|
function groupMembers(dir) {
|
|
13495
|
-
return walkMd(dir).filter((f) => f !==
|
|
13587
|
+
return walkMd(dir).filter((f) => f !== path35.join(dir, "root.md")).map((f) => idPrefix(path35.basename(path35.basename(f) === "root.md" ? path35.dirname(f) : f))).filter((n) => n !== null);
|
|
13496
13588
|
}
|
|
13497
13589
|
var LEVELS;
|
|
13498
13590
|
var init_validate = __esm({
|
|
@@ -13529,7 +13621,7 @@ var init_bundled2 = __esm({
|
|
|
13529
13621
|
|
|
13530
13622
|
// ../cli/src/lib/memory.ts
|
|
13531
13623
|
import fs33 from "node:fs";
|
|
13532
|
-
import
|
|
13624
|
+
import path36 from "node:path";
|
|
13533
13625
|
function scaffoldProjectMemory() {
|
|
13534
13626
|
const board2 = scaffoldMemoryDir(MEMORY, setOf(BOARD_MEMORY_FILES));
|
|
13535
13627
|
const planner = scaffoldMemoryDir(agentMemoryDir(PLANNER), setOf(PLANNER_MEMORY_FILES));
|
|
@@ -13540,7 +13632,7 @@ function scaffoldMemoryDir(dir, set) {
|
|
|
13540
13632
|
fs33.mkdirSync(dir, { recursive: true });
|
|
13541
13633
|
const made = [];
|
|
13542
13634
|
for (const [name, body] of Object.entries(set)) {
|
|
13543
|
-
const file =
|
|
13635
|
+
const file = path36.join(dir, name);
|
|
13544
13636
|
if (!fs33.existsSync(file)) {
|
|
13545
13637
|
fs33.writeFileSync(file, body);
|
|
13546
13638
|
made.push(name);
|
|
@@ -13600,16 +13692,16 @@ ${carried}`].filter(Boolean).join("\n\n")}
|
|
|
13600
13692
|
}
|
|
13601
13693
|
}
|
|
13602
13694
|
function adoptOneFileMemory(agent) {
|
|
13603
|
-
const
|
|
13695
|
+
const legacy2 = legacyAgentMemoryFile(agent);
|
|
13604
13696
|
try {
|
|
13605
|
-
if (!fs33.existsSync(
|
|
13606
|
-
const carried = stripHeading(fs33.readFileSync(
|
|
13697
|
+
if (!fs33.existsSync(legacy2)) return;
|
|
13698
|
+
const carried = stripHeading(fs33.readFileSync(legacy2, "utf8"));
|
|
13607
13699
|
const into = agentMemoryFile(agent, "redesign.md");
|
|
13608
13700
|
const kept2 = fs33.existsSync(into) ? stripHeading(fs33.readFileSync(into, "utf8")) : "";
|
|
13609
13701
|
writeLegacyAgentMemory(agent, "redesign.md", [kept2, carried].filter(Boolean).join("\n\n"));
|
|
13610
|
-
fs33.rmSync(
|
|
13702
|
+
fs33.rmSync(legacy2, { force: true });
|
|
13611
13703
|
} catch {
|
|
13612
|
-
warn2(`couldn't move ${
|
|
13704
|
+
warn2(`couldn't move ${legacy2} into ${agentMemoryDir(agent)}/ \u2014 reading what is there`);
|
|
13613
13705
|
}
|
|
13614
13706
|
}
|
|
13615
13707
|
function plannerModules() {
|
|
@@ -13631,7 +13723,7 @@ function memoryTarget(name, module = "") {
|
|
|
13631
13723
|
}
|
|
13632
13724
|
function scaffoldFile(file, name) {
|
|
13633
13725
|
if (fs33.existsSync(file) || !STARTERS[name]) return;
|
|
13634
|
-
fs33.mkdirSync(
|
|
13726
|
+
fs33.mkdirSync(path36.dirname(file), { recursive: true });
|
|
13635
13727
|
fs33.writeFileSync(file, STARTERS[name]);
|
|
13636
13728
|
}
|
|
13637
13729
|
function readTopics(file) {
|
|
@@ -13651,7 +13743,7 @@ function readTopics(file) {
|
|
|
13651
13743
|
}
|
|
13652
13744
|
function mergeInto(file, block) {
|
|
13653
13745
|
const before = fs33.existsSync(file) ? fs33.readFileSync(file, "utf8").replace(/\s+$/, "") : "";
|
|
13654
|
-
fs33.mkdirSync(
|
|
13746
|
+
fs33.mkdirSync(path36.dirname(file), { recursive: true });
|
|
13655
13747
|
fs33.writeFileSync(file, `${before ? `${before}
|
|
13656
13748
|
|
|
13657
13749
|
` : ""}${block}
|
|
@@ -13661,7 +13753,7 @@ function liftMemoryFile(from, into, topic) {
|
|
|
13661
13753
|
if (!fs33.existsSync(from)) return false;
|
|
13662
13754
|
const body = entriesOf2(fs33.readFileSync(from, "utf8"));
|
|
13663
13755
|
if (body && !topic && !fs33.existsSync(into)) {
|
|
13664
|
-
fs33.mkdirSync(
|
|
13756
|
+
fs33.mkdirSync(path36.dirname(into), { recursive: true });
|
|
13665
13757
|
fs33.renameSync(from, into);
|
|
13666
13758
|
return true;
|
|
13667
13759
|
}
|
|
@@ -13717,8 +13809,8 @@ function legacyModuleDirs() {
|
|
|
13717
13809
|
}
|
|
13718
13810
|
}
|
|
13719
13811
|
function renameProductFile() {
|
|
13720
|
-
const from =
|
|
13721
|
-
const to =
|
|
13812
|
+
const from = path36.join(MEMORY, "product.md");
|
|
13813
|
+
const to = path36.join(MEMORY, "project.md");
|
|
13722
13814
|
if (!fs33.existsSync(from)) return null;
|
|
13723
13815
|
try {
|
|
13724
13816
|
const was = fs33.readFileSync(from, "utf8").trim();
|
|
@@ -13735,22 +13827,22 @@ function renameProductFile() {
|
|
|
13735
13827
|
}
|
|
13736
13828
|
function migrateMemory() {
|
|
13737
13829
|
renameProductFile();
|
|
13738
|
-
const roots = PLANNER_MEMORY_FILES.filter((name) => fs33.existsSync(
|
|
13830
|
+
const roots = PLANNER_MEMORY_FILES.filter((name) => fs33.existsSync(path36.join(MEMORY, name)));
|
|
13739
13831
|
const modules = legacyModuleDirs();
|
|
13740
13832
|
const moved = [];
|
|
13741
13833
|
try {
|
|
13742
13834
|
if (roots.length || modules.length) {
|
|
13743
13835
|
scaffoldProjectMemory();
|
|
13744
13836
|
for (const name of PLANNER_MEMORY_FILES) {
|
|
13745
|
-
const from =
|
|
13837
|
+
const from = path36.join(MEMORY, name);
|
|
13746
13838
|
if (liftMemoryFile(from, agentMemoryFile(PLANNER, name), "")) moved.push(rel(from));
|
|
13747
13839
|
}
|
|
13748
13840
|
for (const module of modules) {
|
|
13749
|
-
const dir =
|
|
13841
|
+
const dir = path36.join(MEMORY, module);
|
|
13750
13842
|
for (const name of [...PLANNER_MEMORY_FILES, "readme.md"]) {
|
|
13751
|
-
const from =
|
|
13843
|
+
const from = path36.join(dir, name);
|
|
13752
13844
|
const readme = name === "readme.md";
|
|
13753
|
-
const into = readme ?
|
|
13845
|
+
const into = readme ? path36.join(MEMORY, name) : isModule(module) ? moduleMemoryFile(module, name) : agentMemoryFile(PLANNER, name);
|
|
13754
13846
|
if (liftMemoryFile(from, into, readme || !isModule(module) ? module : "")) moved.push(rel(from));
|
|
13755
13847
|
}
|
|
13756
13848
|
dropIfEmpty(dir);
|
|
@@ -13767,14 +13859,14 @@ function mergeDismissed() {
|
|
|
13767
13859
|
const root = agentMemoryDir(PLANNER);
|
|
13768
13860
|
let dirs;
|
|
13769
13861
|
try {
|
|
13770
|
-
dirs = [root, ...fs33.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) =>
|
|
13862
|
+
dirs = [root, ...fs33.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path36.join(root, e.name))];
|
|
13771
13863
|
} catch {
|
|
13772
13864
|
return [];
|
|
13773
13865
|
}
|
|
13774
13866
|
return dirs.flatMap((dir) => {
|
|
13775
|
-
const from =
|
|
13867
|
+
const from = path36.join(dir, "dismissed.md");
|
|
13776
13868
|
if (!fs33.existsSync(from)) return [];
|
|
13777
|
-
const into =
|
|
13869
|
+
const into = path36.join(dir, "rejected.md");
|
|
13778
13870
|
scaffoldFile(into, "rejected.md");
|
|
13779
13871
|
return liftMemoryFile(from, into, "") ? [rel(from)] : [];
|
|
13780
13872
|
});
|
|
@@ -13789,7 +13881,7 @@ function goalWords(text5) {
|
|
|
13789
13881
|
return text5.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").split(/\r?\n\s*\r?\n/).filter((p) => !SEEDED_GOAL_PARAGRAPHS.has(p.trim().replace(/\s+/g, " "))).join("\n\n").trim();
|
|
13790
13882
|
}
|
|
13791
13883
|
function retireGoal() {
|
|
13792
|
-
const goal =
|
|
13884
|
+
const goal = path36.join(MEMORY, "goal.md");
|
|
13793
13885
|
if (!fs33.existsSync(goal)) return null;
|
|
13794
13886
|
const words = goalWords(fs33.readFileSync(goal, "utf8"));
|
|
13795
13887
|
if (words) {
|
|
@@ -13816,7 +13908,7 @@ var init_memory = __esm({
|
|
|
13816
13908
|
init_spec_agent_names();
|
|
13817
13909
|
init_validate();
|
|
13818
13910
|
init_bundled2();
|
|
13819
|
-
isGlobalMemory = (name) => name in BUNDLED_MEMORY_FILES || fs33.existsSync(
|
|
13911
|
+
isGlobalMemory = (name) => name in BUNDLED_MEMORY_FILES || fs33.existsSync(path36.join(MEMORY, name, "MEMORY.md"));
|
|
13820
13912
|
PLANNER = "planner";
|
|
13821
13913
|
PLANNER_MEMORY_FILES = ["decisions.md", "rejected.md", "redesign.md"];
|
|
13822
13914
|
BOARD_MEMORY_FILES = ["readme.md", "project.md"];
|
|
@@ -13855,8 +13947,8 @@ edits here do not last.
|
|
|
13855
13947
|
setOf = (names4) => Object.fromEntries(names4.map((name) => [name, STARTERS[name]]));
|
|
13856
13948
|
RESERVED_MEMORY_DIR = "agents";
|
|
13857
13949
|
KEPT_MEMORY_FOLDERS = { "hyperframes-editor": "video-assets" };
|
|
13858
|
-
agentMemoryDir = (agent) =>
|
|
13859
|
-
agentMemoryFile = (agent, name) =>
|
|
13950
|
+
agentMemoryDir = (agent) => path36.join(AGENT_MEMORY, KEPT_MEMORY_FOLDERS[agent] ?? agent);
|
|
13951
|
+
agentMemoryFile = (agent, name) => path36.join(agentMemoryDir(agent), name);
|
|
13860
13952
|
writtenMemoryNames = (agent) => {
|
|
13861
13953
|
try {
|
|
13862
13954
|
return fs33.readdirSync(agentMemoryDir(agent), { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => e.name).sort();
|
|
@@ -13866,7 +13958,7 @@ edits here do not last.
|
|
|
13866
13958
|
};
|
|
13867
13959
|
memoryNamesOf = (agent) => agent === PLANNER ? PLANNER_MEMORY_FILES : writtenMemoryNames(agent);
|
|
13868
13960
|
agentMemoryFiles = (agent) => memoryNamesOf(agent).map((name) => agentMemoryFile(agent, name));
|
|
13869
|
-
legacyAgentMemoryFile = (agent) =>
|
|
13961
|
+
legacyAgentMemoryFile = (agent) => path36.join(AGENT_MEMORY, `${agent}.md`);
|
|
13870
13962
|
HEADINGS = {
|
|
13871
13963
|
"redesign.md": (agent) => `# What \`${agent}\` was corrected on`,
|
|
13872
13964
|
"decisions.md": (agent) => `# What the user chose for \`${agent}\``
|
|
@@ -13881,7 +13973,7 @@ edits here do not last.
|
|
|
13881
13973
|
moveMemory = (from, into) => {
|
|
13882
13974
|
try {
|
|
13883
13975
|
if (!fs33.existsSync(from) || fs33.existsSync(into)) return;
|
|
13884
|
-
fs33.mkdirSync(
|
|
13976
|
+
fs33.mkdirSync(path36.dirname(into), { recursive: true });
|
|
13885
13977
|
fs33.renameSync(from, into);
|
|
13886
13978
|
} catch {
|
|
13887
13979
|
warn2(`couldn't move ${from} to ${into} \u2014 reading what is there`);
|
|
@@ -13889,12 +13981,12 @@ edits here do not last.
|
|
|
13889
13981
|
};
|
|
13890
13982
|
RESERVED_MODULE_NAMES = PLANNER_MEMORY_FILES.map((name) => name.replace(/\.md$/, ""));
|
|
13891
13983
|
isModule = (name) => !!name && !RESERVED_MODULE_NAMES.includes(name);
|
|
13892
|
-
moduleMemoryFile = (module, name) =>
|
|
13984
|
+
moduleMemoryFile = (module, name) => path36.join(agentMemoryDir(PLANNER), module, name);
|
|
13893
13985
|
plannerCopies = (name) => [
|
|
13894
13986
|
agentMemoryFile(PLANNER, name),
|
|
13895
13987
|
...plannerModules().map((m) => moduleMemoryFile(m, name)).filter((file) => fs33.existsSync(file))
|
|
13896
13988
|
];
|
|
13897
|
-
memoryFile = (name, module = "") => BOARD_MEMORY_FILES.includes(name) ?
|
|
13989
|
+
memoryFile = (name, module = "") => BOARD_MEMORY_FILES.includes(name) ? path36.join(MEMORY, name) : isModule(module) ? moduleMemoryFile(module, name) : agentMemoryFile(PLANNER, name);
|
|
13898
13990
|
ENTRY_RE = /^\s*([-*]|#{2,6})\s/;
|
|
13899
13991
|
entriesOf2 = (text5) => {
|
|
13900
13992
|
const lines = text5.replace(/^---\n[\s\S]*?\n---\n/, "").split("\n");
|
|
@@ -14057,7 +14149,7 @@ var init_WORKFLOW7 = __esm({
|
|
|
14057
14149
|
|
|
14058
14150
|
// ../cli/src/lib/agent/workflow-file.ts
|
|
14059
14151
|
import fs34 from "node:fs";
|
|
14060
|
-
import
|
|
14152
|
+
import path37 from "node:path";
|
|
14061
14153
|
function agentLines(agents, indent2) {
|
|
14062
14154
|
return agents.flatMap(
|
|
14063
14155
|
(h) => !h.extra && !h.off ? [`${indent2}- ${scalar(h.agent)}`] : [
|
|
@@ -14116,12 +14208,12 @@ function boardWorkflowDefs() {
|
|
|
14116
14208
|
}
|
|
14117
14209
|
function writeWorkflowDef(def) {
|
|
14118
14210
|
const file = fileOf(def.id);
|
|
14119
|
-
fs34.mkdirSync(
|
|
14211
|
+
fs34.mkdirSync(path37.dirname(file), { recursive: true });
|
|
14120
14212
|
fs34.writeFileSync(file, workflowFileText(def));
|
|
14121
14213
|
forgetFileMemos();
|
|
14122
14214
|
}
|
|
14123
14215
|
function removeWorkflowDef(id) {
|
|
14124
|
-
fs34.rmSync(
|
|
14216
|
+
fs34.rmSync(path37.join(WORKFLOWS, id), { recursive: true, force: true });
|
|
14125
14217
|
forgetFileMemos();
|
|
14126
14218
|
}
|
|
14127
14219
|
var BUNDLED, WORKFLOW_FILE, scalar, plain, flowList, BUILTIN_DEFS, fileOf;
|
|
@@ -14158,13 +14250,13 @@ var init_workflow_file = __esm({
|
|
|
14158
14250
|
if ("problem" in read4) throw new Error(read4.problem);
|
|
14159
14251
|
return read4.def;
|
|
14160
14252
|
});
|
|
14161
|
-
fileOf = (id) =>
|
|
14253
|
+
fileOf = (id) => path37.join(WORKFLOWS, id, WORKFLOW_FILE);
|
|
14162
14254
|
}
|
|
14163
14255
|
});
|
|
14164
14256
|
|
|
14165
14257
|
// ../cli/src/lib/agent/workflows.ts
|
|
14166
14258
|
import fs35 from "node:fs";
|
|
14167
|
-
import
|
|
14259
|
+
import path38 from "node:path";
|
|
14168
14260
|
function workflowPurpose(flow) {
|
|
14169
14261
|
if (flow.builtIn) return builtinDescription(flow.id) || flow.name;
|
|
14170
14262
|
return boardWorkflowDefs().defs.find((d) => d.id === flow.id)?.description || flow.name;
|
|
@@ -14579,7 +14671,7 @@ function cardMeta(id) {
|
|
|
14579
14671
|
try {
|
|
14580
14672
|
const found2 = locate(id) ?? locateArchived(id);
|
|
14581
14673
|
if (!found2) return null;
|
|
14582
|
-
const file = found2.kind === "group" ?
|
|
14674
|
+
const file = found2.kind === "group" ? path38.join(found2.target, "root.md") : found2.target;
|
|
14583
14675
|
return parseFrontmatter(fs35.readFileSync(file, "utf8")).meta;
|
|
14584
14676
|
} catch {
|
|
14585
14677
|
return null;
|
|
@@ -14678,7 +14770,7 @@ var init_workflows = __esm({
|
|
|
14678
14770
|
previewAgents = (flow) => previewMembers(flow).filter((h) => !h.off);
|
|
14679
14771
|
landAgents = (flow) => flow.land.filter((h) => !h.off);
|
|
14680
14772
|
archivesOnApproval = (flow) => !!flow && !landAgents(flow).length;
|
|
14681
|
-
workflowRuleFile = (id) =>
|
|
14773
|
+
workflowRuleFile = (id) => path38.join(RULES, "workflows", `${id}.md`);
|
|
14682
14774
|
save2 = (change2) => writeConfig((cfg) => {
|
|
14683
14775
|
const block = configBlock(cfg.workflows);
|
|
14684
14776
|
change2(block);
|
|
@@ -14709,7 +14801,7 @@ var init_workflows = __esm({
|
|
|
14709
14801
|
});
|
|
14710
14802
|
|
|
14711
14803
|
// ../cli/src/lib/agents/index.ts
|
|
14712
|
-
import
|
|
14804
|
+
import path39 from "node:path";
|
|
14713
14805
|
function agentLines2(agent, language = readLanguage()) {
|
|
14714
14806
|
const said3 = agent.i18n[language];
|
|
14715
14807
|
return {
|
|
@@ -14756,7 +14848,7 @@ function agentFilesBlock(agent) {
|
|
|
14756
14848
|
"Read one only when the work calls for it:",
|
|
14757
14849
|
"",
|
|
14758
14850
|
...agent.files.map(
|
|
14759
|
-
(f) => dir ? `- \`${rel(
|
|
14851
|
+
(f) => dir ? `- \`${rel(path39.join(dir, f))}\`` : `- \`${f}\` \u2014 \`${rawMove(`agent-file ${agent.name} ${f}`)}\``
|
|
14760
14852
|
)
|
|
14761
14853
|
].join("\n");
|
|
14762
14854
|
}
|
|
@@ -14985,7 +15077,7 @@ var init_stages = __esm({
|
|
|
14985
15077
|
});
|
|
14986
15078
|
|
|
14987
15079
|
// ../cli/src/lib/agent/roles.ts
|
|
14988
|
-
import
|
|
15080
|
+
import path40 from "node:path";
|
|
14989
15081
|
function landRole(name) {
|
|
14990
15082
|
const agent = specAgentCatalog().agents.find((a) => a.name === name);
|
|
14991
15083
|
return agent ? { name, gloss: agent.description, memory: [] } : void 0;
|
|
@@ -15128,8 +15220,8 @@ var init_roles = __esm({
|
|
|
15128
15220
|
hiddenRole = (name) => roleNamed(name)?.hidden === true;
|
|
15129
15221
|
stageContractProblems = () => contractProblems([...agentNames(), ...roles().filter((role) => role.hidden).map((role) => role.name)]);
|
|
15130
15222
|
memoryOf = (role) => role.memory.flatMap((file) => {
|
|
15131
|
-
const at =
|
|
15132
|
-
return
|
|
15223
|
+
const at = path40.join(KANBAN, file);
|
|
15224
|
+
return path40.dirname(at) === agentMemoryDir(PLANNER) ? plannerCopies(path40.basename(at)) : [at];
|
|
15133
15225
|
}).map(rel);
|
|
15134
15226
|
ownMemoryOf = (name, memory) => memoryNamesOf(name).filter((file) => memory.includes(rel(agentMemoryFile(name, file))));
|
|
15135
15227
|
agentNames = () => agentRoster().map((entry) => entry.name);
|
|
@@ -15138,7 +15230,7 @@ var init_roles = __esm({
|
|
|
15138
15230
|
|
|
15139
15231
|
// ../cli/src/lib/agents/roster.ts
|
|
15140
15232
|
import fs36 from "node:fs";
|
|
15141
|
-
import
|
|
15233
|
+
import path41 from "node:path";
|
|
15142
15234
|
function customAgentsOn() {
|
|
15143
15235
|
const running2 = /* @__PURE__ */ new Set();
|
|
15144
15236
|
for (const flow of workflows()) {
|
|
@@ -15151,26 +15243,26 @@ function copyAgent(from, taken = /* @__PURE__ */ new Set()) {
|
|
|
15151
15243
|
const catalog = specAgentCatalog().agents;
|
|
15152
15244
|
const agent = catalog.find((a) => a.name === from);
|
|
15153
15245
|
if (!agent) return { ok: false, error: `"${from}" is not an agent on this board.` };
|
|
15154
|
-
const free = (name2) => !taken.has(name2) && !ROLE_NAMES.includes(name2) && !catalog.some((a) => a.name === name2) && ![AGENTS, LEGACY_AGENTS].some((root) => fs36.existsSync(
|
|
15246
|
+
const free = (name2) => !taken.has(name2) && !ROLE_NAMES.includes(name2) && !catalog.some((a) => a.name === name2) && ![AGENTS, LEGACY_AGENTS].some((root) => fs36.existsSync(path41.join(root, name2)));
|
|
15155
15247
|
const base = from.replace(/-\d+$/, "");
|
|
15156
15248
|
let name = "";
|
|
15157
15249
|
for (let n = 2; !name; n++) if (free(`${base}-${n}`)) name = `${base}-${n}`;
|
|
15158
15250
|
taken.add(name);
|
|
15159
|
-
const dir =
|
|
15251
|
+
const dir = path41.join(AGENTS, name);
|
|
15160
15252
|
try {
|
|
15161
15253
|
if (agent.dir) fs36.cpSync(agent.dir, dir, { recursive: true });
|
|
15162
15254
|
else {
|
|
15163
15255
|
for (const [key, text5] of Object.entries(BUNDLED_AGENT_FILES)) {
|
|
15164
15256
|
if (!key.startsWith(`${from}/`)) continue;
|
|
15165
|
-
const file2 =
|
|
15166
|
-
fs36.mkdirSync(
|
|
15257
|
+
const file2 = path41.join(dir, key.slice(from.length + 1));
|
|
15258
|
+
fs36.mkdirSync(path41.dirname(file2), { recursive: true });
|
|
15167
15259
|
fs36.writeFileSync(file2, text5);
|
|
15168
15260
|
}
|
|
15169
15261
|
}
|
|
15170
|
-
const file =
|
|
15262
|
+
const file = path41.join(dir, agent.dir ? path41.basename(agent.from) : AGENT_FILE2);
|
|
15171
15263
|
fs36.writeFileSync(file, fs36.readFileSync(file, "utf8").replace(/^name:.*$/m, `name: ${name}`));
|
|
15172
|
-
const rule =
|
|
15173
|
-
if (fs36.existsSync(rule)) fs36.copyFileSync(rule,
|
|
15264
|
+
const rule = path41.join(RULES, `${from}.md`);
|
|
15265
|
+
if (fs36.existsSync(rule)) fs36.copyFileSync(rule, path41.join(RULES, `${name}.md`));
|
|
15174
15266
|
if (fs36.existsSync(agentMemoryDir(from))) fs36.cpSync(agentMemoryDir(from), agentMemoryDir(name), { recursive: true });
|
|
15175
15267
|
} catch (err) {
|
|
15176
15268
|
fs36.rmSync(dir, { recursive: true, force: true });
|
|
@@ -15189,7 +15281,7 @@ function copyAgent(from, taken = /* @__PURE__ */ new Set()) {
|
|
|
15189
15281
|
function renameCopiedAfter(copied) {
|
|
15190
15282
|
const rename = (list) => list.replace(/[\w-]+/g, (name) => copied.get(name) ?? name);
|
|
15191
15283
|
for (const copy2 of copied.values()) {
|
|
15192
|
-
const file = [AGENT_FILE2, "SKILL.md"].map((f) =>
|
|
15284
|
+
const file = [AGENT_FILE2, "SKILL.md"].map((f) => path41.join(AGENTS, copy2, f)).find((f) => fs36.existsSync(f));
|
|
15193
15285
|
if (!file) continue;
|
|
15194
15286
|
const text5 = fs36.readFileSync(file, "utf8");
|
|
15195
15287
|
const next = text5.replace(/^(\s+after:\s*)\[(.*)\]\s*$/m, (_, key, list) => `${key}[${rename(list)}]`).replace(/^(\s+after:\s*\n)((?:\s+-\s+.*\n)+)/m, (_, key, items) => key + rename(items));
|
|
@@ -15205,7 +15297,7 @@ function deleteAgent(name) {
|
|
|
15205
15297
|
}
|
|
15206
15298
|
const removed = [];
|
|
15207
15299
|
try {
|
|
15208
|
-
for (const file of [agent.dir,
|
|
15300
|
+
for (const file of [agent.dir, path41.join(RULES, `${name}.md`), agentMemoryDir(name), legacyAgentMemoryFile(name)]) {
|
|
15209
15301
|
if (!fs36.existsSync(file)) continue;
|
|
15210
15302
|
fs36.rmSync(file, { recursive: true, force: true });
|
|
15211
15303
|
removed.push(rel(file));
|
|
@@ -15305,12 +15397,12 @@ var init_candidate = __esm({
|
|
|
15305
15397
|
// ../cli/src/lib/agent/outputs.ts
|
|
15306
15398
|
import fs37 from "node:fs";
|
|
15307
15399
|
import os5 from "node:os";
|
|
15308
|
-
import
|
|
15400
|
+
import path42 from "node:path";
|
|
15309
15401
|
function cardBody(cardId2) {
|
|
15310
15402
|
const found2 = locate(cardId2) ?? locateArchived(cardId2);
|
|
15311
15403
|
if (!found2) return "";
|
|
15312
15404
|
try {
|
|
15313
|
-
return fs37.readFileSync(found2.kind === "group" ?
|
|
15405
|
+
return fs37.readFileSync(found2.kind === "group" ? path42.join(found2.target, "root.md") : found2.target, "utf8");
|
|
15314
15406
|
} catch {
|
|
15315
15407
|
return "";
|
|
15316
15408
|
}
|
|
@@ -15366,17 +15458,17 @@ var init_outputs = __esm({
|
|
|
15366
15458
|
plannedTodos = (cardId2) => todos(cardId2).map((t) => t.text);
|
|
15367
15459
|
looksLikePath = (token3) => !/\s/.test(token3) && !token3.includes("://") && !token3.startsWith("-") && token3.includes("/");
|
|
15368
15460
|
resolveOutput = (file) => {
|
|
15369
|
-
if (file.startsWith("~/")) return
|
|
15370
|
-
if (
|
|
15371
|
-
if (file.startsWith(".assets/")) return
|
|
15372
|
-
return
|
|
15461
|
+
if (file.startsWith("~/")) return path42.join(os5.homedir(), file.slice(2));
|
|
15462
|
+
if (path42.isAbsolute(file)) return file;
|
|
15463
|
+
if (file.startsWith(".assets/")) return path42.join(ASSETS, file.slice(".assets/".length));
|
|
15464
|
+
return path42.join(REPO_ROOT, file);
|
|
15373
15465
|
};
|
|
15374
15466
|
}
|
|
15375
15467
|
});
|
|
15376
15468
|
|
|
15377
15469
|
// ../cli/src/lib/agent/commit-mode.ts
|
|
15378
15470
|
import fs38 from "node:fs";
|
|
15379
|
-
import
|
|
15471
|
+
import path43 from "node:path";
|
|
15380
15472
|
function recordedIds() {
|
|
15381
15473
|
try {
|
|
15382
15474
|
return new Set(
|
|
@@ -15497,7 +15589,7 @@ function snapshotReviewed(delivery) {
|
|
|
15497
15589
|
const diff = candidateDiff(candidateOf(delivery));
|
|
15498
15590
|
if (diff) {
|
|
15499
15591
|
try {
|
|
15500
|
-
fs38.mkdirSync(
|
|
15592
|
+
fs38.mkdirSync(path43.dirname(reviewedDiffPath(delivery.deliveryId)), { recursive: true });
|
|
15501
15593
|
fs38.writeFileSync(reviewedDiffPath(delivery.deliveryId), diff);
|
|
15502
15594
|
where = rel(reviewedDiffPath(delivery.deliveryId));
|
|
15503
15595
|
} catch {
|
|
@@ -15537,7 +15629,7 @@ var init_commit_mode = __esm({
|
|
|
15537
15629
|
});
|
|
15538
15630
|
MAX_SUBJECT = 72;
|
|
15539
15631
|
deliveryName = (delivery) => delivery.cardId === null ? `delivery ${delivery.deliveryId}` : `#${delivery.cardId}`;
|
|
15540
|
-
reviewedDiffPath = (deliveryId) =>
|
|
15632
|
+
reviewedDiffPath = (deliveryId) => path43.join(AKB_DIR, "reviewed", `${deliveryId}.diff`);
|
|
15541
15633
|
}
|
|
15542
15634
|
});
|
|
15543
15635
|
|
|
@@ -15572,7 +15664,7 @@ import { spawnSync as spawnSync4 } from "node:child_process";
|
|
|
15572
15664
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
15573
15665
|
import fs39 from "node:fs";
|
|
15574
15666
|
import os6 from "node:os";
|
|
15575
|
-
import
|
|
15667
|
+
import path44 from "node:path";
|
|
15576
15668
|
function markedInProc(entry, strict = false) {
|
|
15577
15669
|
const pids = [];
|
|
15578
15670
|
for (const name of fs39.readdirSync("/proc")) {
|
|
@@ -15655,7 +15747,7 @@ function stopWatchers(sessions, onlyPid, stillOwns = () => true, startedBefore)
|
|
|
15655
15747
|
const match = /^"?[^\n]*?\bnode(?:\.exe)?"?\s+(?:"[^\n"]+\.mjs"|\S+\.mjs)\s+__watch\s+(\S+)(?:\s|$)/.exec(proc.command);
|
|
15656
15748
|
const args2 = `${proc.command} `;
|
|
15657
15749
|
const names4 = (flag, value) => args2.includes(` ${flag} ${value} `) || args2.includes(` ${flag} "${value}" `);
|
|
15658
|
-
const board2 = names4("--board", KANBAN) || KANBAN ===
|
|
15750
|
+
const board2 = names4("--board", KANBAN) || KANBAN === path44.join(REPO_ROOT, "docs", "kanban") && names4("--dir", REPO_ROOT);
|
|
15659
15751
|
if (!match || known2 && !known2.has(match[1]) || !board2 || !stillOwns()) continue;
|
|
15660
15752
|
if (WINDOWS3) {
|
|
15661
15753
|
if (killTreeOnWindows(proc.pid)) stopped2.push(proc.pid);
|
|
@@ -16002,7 +16094,7 @@ var init_runner = __esm({
|
|
|
16002
16094
|
|
|
16003
16095
|
// ../cli/src/lib/agent/rules.ts
|
|
16004
16096
|
import fs40 from "node:fs";
|
|
16005
|
-
import
|
|
16097
|
+
import path45 from "node:path";
|
|
16006
16098
|
function ruleFile(agent) {
|
|
16007
16099
|
if (!agent || hiddenRole(agent)) return "";
|
|
16008
16100
|
try {
|
|
@@ -16133,7 +16225,7 @@ var init_rules2 = __esm({
|
|
|
16133
16225
|
init_runner();
|
|
16134
16226
|
init_workflows();
|
|
16135
16227
|
init_types3();
|
|
16136
|
-
rulePath = (agent) =>
|
|
16228
|
+
rulePath = (agent) => path45.join(RULES, `${agent}.md`);
|
|
16137
16229
|
notOnTheRoster = (name) => `"${name}" is not an agent on this board. It has: ${agentNames().join(", ")}.`;
|
|
16138
16230
|
flowForRequest = (req) => req.refineRound !== void 0 && REFINE_ACTIONS.has(req.action) ? flowByCommand("refine") : flowByAction(req.action);
|
|
16139
16231
|
ruleOwnerSays = (owner) => `\`${owner.name}\` agent carries one rule of its own${owner.role ? ", on every flow it runs" : ""}`;
|
|
@@ -16219,7 +16311,7 @@ var init_source = __esm({
|
|
|
16219
16311
|
|
|
16220
16312
|
// ../cli/src/lib/card-sources.ts
|
|
16221
16313
|
import fs41 from "node:fs";
|
|
16222
|
-
import
|
|
16314
|
+
import path46 from "node:path";
|
|
16223
16315
|
function planById(id) {
|
|
16224
16316
|
const found2 = readPlan(`plans/${id}-.md`);
|
|
16225
16317
|
return found2?.text.trim() ? found2 : null;
|
|
@@ -16229,7 +16321,7 @@ function cardLink(id) {
|
|
|
16229
16321
|
const found2 = open3 ?? locateArchived(id);
|
|
16230
16322
|
if (!found2) return null;
|
|
16231
16323
|
try {
|
|
16232
|
-
const file = found2.kind === "group" ?
|
|
16324
|
+
const file = found2.kind === "group" ? path46.join(found2.target, "root.md") : found2.target;
|
|
16233
16325
|
const title = parseFrontmatter(fs41.readFileSync(file, "utf8")).meta?.title ?? "";
|
|
16234
16326
|
return { kind: "card", ref: String(id), title, ...open3 ? {} : { archived: true } };
|
|
16235
16327
|
} catch {
|
|
@@ -16255,7 +16347,7 @@ function link(ref, items) {
|
|
|
16255
16347
|
if (ref.kind === "plan") return planLink(ref.id);
|
|
16256
16348
|
if (ref.kind === "card") return cardLink(ref.id);
|
|
16257
16349
|
if (ref.kind === "url") return urlLink(ref.url);
|
|
16258
|
-
const item = items().find((signal2) =>
|
|
16350
|
+
const item = items().find((signal2) => path46.basename(signal2.relPath) === ref.file);
|
|
16259
16351
|
return item ? itemLink(item) : null;
|
|
16260
16352
|
}
|
|
16261
16353
|
function cardSources(meta, body) {
|
|
@@ -16297,11 +16389,11 @@ var init_card_sources = __esm({
|
|
|
16297
16389
|
|
|
16298
16390
|
// ../cli/src/lib/view/read.ts
|
|
16299
16391
|
import fs42 from "node:fs";
|
|
16300
|
-
import
|
|
16392
|
+
import path47 from "node:path";
|
|
16301
16393
|
function readCard(file, relFromTodo) {
|
|
16302
|
-
const id = idPrefix(
|
|
16394
|
+
const id = idPrefix(path47.basename(relFromTodo));
|
|
16303
16395
|
if (id === null) {
|
|
16304
|
-
const parts = relFromTodo.split(
|
|
16396
|
+
const parts = relFromTodo.split(path47.sep);
|
|
16305
16397
|
const folderId = idPrefix(parts[parts.length - 2] || "");
|
|
16306
16398
|
if (folderId === null) return null;
|
|
16307
16399
|
return buildCard(folderId, file, relFromTodo);
|
|
@@ -16312,7 +16404,7 @@ function buildCard(id, file, relFromTodo) {
|
|
|
16312
16404
|
const text5 = fs42.readFileSync(file, "utf8");
|
|
16313
16405
|
const { meta, body } = parseFrontmatter(text5);
|
|
16314
16406
|
if (!meta) return null;
|
|
16315
|
-
const relPath = relFromTodo.split(
|
|
16407
|
+
const relPath = relFromTodo.split(path47.sep).join("/");
|
|
16316
16408
|
const flow = workflowFor(meta.workflow);
|
|
16317
16409
|
const plan = !!meta.workflow && archivesOnApproval(flow);
|
|
16318
16410
|
const stages = cardStages(meta, flow).length;
|
|
@@ -16342,10 +16434,10 @@ function buildCard(id, file, relFromTodo) {
|
|
|
16342
16434
|
};
|
|
16343
16435
|
}
|
|
16344
16436
|
function readGroup2(folderName) {
|
|
16345
|
-
const groupDir =
|
|
16346
|
-
const rootFile =
|
|
16437
|
+
const groupDir = path47.join(TODO, folderName);
|
|
16438
|
+
const rootFile = path47.join(groupDir, "root.md");
|
|
16347
16439
|
if (!fs42.existsSync(rootFile)) return null;
|
|
16348
|
-
const root = readCard(rootFile,
|
|
16440
|
+
const root = readCard(rootFile, path47.join(folderName, "root.md"));
|
|
16349
16441
|
if (!root) return null;
|
|
16350
16442
|
root.isGroup = true;
|
|
16351
16443
|
const { total, resolved: resolved2 } = subtaskLines(root.body);
|
|
@@ -16353,11 +16445,11 @@ function readGroup2(folderName) {
|
|
|
16353
16445
|
const subCards = [];
|
|
16354
16446
|
const recurse = (dir, relDir) => {
|
|
16355
16447
|
for (const entry of fs42.readdirSync(dir, { withFileTypes: true })) {
|
|
16356
|
-
const childRel =
|
|
16448
|
+
const childRel = path47.join(relDir, entry.name);
|
|
16357
16449
|
if (entry.isDirectory()) {
|
|
16358
|
-
recurse(
|
|
16450
|
+
recurse(path47.join(dir, entry.name), childRel);
|
|
16359
16451
|
} else if (entry.name.endsWith(".md") && entry.name !== "README.md" && entry.name !== "root.md") {
|
|
16360
|
-
const c = readCard(
|
|
16452
|
+
const c = readCard(path47.join(dir, entry.name), childRel);
|
|
16361
16453
|
if (c) {
|
|
16362
16454
|
c.parent = { id: root.id, title: root.title };
|
|
16363
16455
|
subCards.push(c);
|
|
@@ -16378,12 +16470,12 @@ function readGroup2(folderName) {
|
|
|
16378
16470
|
return { root, subCards };
|
|
16379
16471
|
}
|
|
16380
16472
|
function standaloneCards(dirRel) {
|
|
16381
|
-
const dir = dirRel ?
|
|
16473
|
+
const dir = dirRel ? path47.join(TODO, dirRel) : TODO;
|
|
16382
16474
|
if (!fs42.existsSync(dir)) return [];
|
|
16383
16475
|
const cards = [];
|
|
16384
16476
|
for (const entry of fs42.readdirSync(dir, { withFileTypes: true })) {
|
|
16385
16477
|
if (entry.isFile() && entry.name.endsWith(".md") && entry.name !== "README.md") {
|
|
16386
|
-
const c = readCard(
|
|
16478
|
+
const c = readCard(path47.join(dir, entry.name), path47.join(dirRel, entry.name));
|
|
16387
16479
|
if (c) cards.push(c);
|
|
16388
16480
|
}
|
|
16389
16481
|
}
|
|
@@ -16427,7 +16519,7 @@ function findCard(id) {
|
|
|
16427
16519
|
}
|
|
16428
16520
|
function attachSources(card) {
|
|
16429
16521
|
try {
|
|
16430
|
-
const { meta, body } = parseFrontmatter(fs42.readFileSync(
|
|
16522
|
+
const { meta, body } = parseFrontmatter(fs42.readFileSync(path47.join(TODO, card.relPath), "utf8"));
|
|
16431
16523
|
const sources = meta ? cardSources(meta, body) : [];
|
|
16432
16524
|
if (sources.length) card.sources = sources;
|
|
16433
16525
|
} catch {
|
|
@@ -16616,7 +16708,7 @@ var init_review = __esm({
|
|
|
16616
16708
|
|
|
16617
16709
|
// ../cli/src/lib/agent/deliveries.ts
|
|
16618
16710
|
import fs43 from "node:fs";
|
|
16619
|
-
import
|
|
16711
|
+
import path48 from "node:path";
|
|
16620
16712
|
function writeAudit(delivery, runs) {
|
|
16621
16713
|
const known2 = new Map(readAudit(delivery.deliveryId)?.sessions.map((s) => [s.sessionId, s]) ?? []);
|
|
16622
16714
|
const sessions = delivery.sessions.flatMap((id) => {
|
|
@@ -16690,7 +16782,7 @@ function allAudits() {
|
|
|
16690
16782
|
const out = [];
|
|
16691
16783
|
for (const name of names4) {
|
|
16692
16784
|
if (!name.endsWith(".json")) continue;
|
|
16693
|
-
const audit = wholeAudit(
|
|
16785
|
+
const audit = wholeAudit(path48.join(DELIVERIES, name));
|
|
16694
16786
|
if (audit) out.push(audit);
|
|
16695
16787
|
}
|
|
16696
16788
|
return out;
|
|
@@ -16801,11 +16893,11 @@ function resumeRefusal(delivery, store) {
|
|
|
16801
16893
|
);
|
|
16802
16894
|
}
|
|
16803
16895
|
if (!worktreeExists(delivery.worktree)) {
|
|
16804
|
-
const
|
|
16896
|
+
const path88 = delivery.worktree ?? "";
|
|
16805
16897
|
return refusal(
|
|
16806
16898
|
"deliveryWorktreeGone",
|
|
16807
|
-
`delivery ${id}'s worktree ${
|
|
16808
|
-
{ id, task, path:
|
|
16899
|
+
`delivery ${id}'s worktree ${path88} is gone, so there is nothing to carry on. ${rebuild}`,
|
|
16900
|
+
{ id, task, path: path88, command: discard }
|
|
16809
16901
|
);
|
|
16810
16902
|
}
|
|
16811
16903
|
if (!branchExists(delivery.branch)) {
|
|
@@ -16948,7 +17040,7 @@ function carryOnFrom(deliveryId) {
|
|
|
16948
17040
|
function approvedRequirements(cardId2) {
|
|
16949
17041
|
const found2 = locate(cardId2);
|
|
16950
17042
|
if (!found2) return "";
|
|
16951
|
-
const file = found2.kind === "group" ?
|
|
17043
|
+
const file = found2.kind === "group" ? path48.join(found2.target, "root.md") : found2.target;
|
|
16952
17044
|
let text5;
|
|
16953
17045
|
try {
|
|
16954
17046
|
text5 = fs43.readFileSync(file, "utf8");
|
|
@@ -16981,7 +17073,7 @@ function cardMeta2(cardId2) {
|
|
|
16981
17073
|
try {
|
|
16982
17074
|
const found2 = locate(cardId2);
|
|
16983
17075
|
if (!found2) return null;
|
|
16984
|
-
const file = found2.kind === "group" ?
|
|
17076
|
+
const file = found2.kind === "group" ? path48.join(found2.target, "root.md") : found2.target;
|
|
16985
17077
|
return parseFrontmatter(fs43.readFileSync(file, "utf8")).meta;
|
|
16986
17078
|
} catch {
|
|
16987
17079
|
return null;
|
|
@@ -17241,7 +17333,7 @@ var init_deliveries = __esm({
|
|
|
17241
17333
|
init_store();
|
|
17242
17334
|
init_types3();
|
|
17243
17335
|
init_worktree();
|
|
17244
|
-
auditPath = (deliveryId) =>
|
|
17336
|
+
auditPath = (deliveryId) => path48.join(DELIVERIES, `${deliveryId}.json`);
|
|
17245
17337
|
activeAudits = () => allAudits().filter((a) => a.status === "active");
|
|
17246
17338
|
auditRow = (audit) => readDeliveryRow({ ...audit, sessions: audit.sessions.map((s) => s.sessionId) });
|
|
17247
17339
|
lastSessionRunning = (audit) => audit.sessions[audit.sessions.length - 1]?.status === "running";
|
|
@@ -17271,9 +17363,9 @@ var init_deliveries = __esm({
|
|
|
17271
17363
|
|
|
17272
17364
|
// ../cli/src/lib/agent/landing-lock.ts
|
|
17273
17365
|
import fs44 from "node:fs";
|
|
17274
|
-
import
|
|
17366
|
+
import path49 from "node:path";
|
|
17275
17367
|
function lockLanding() {
|
|
17276
|
-
const dir =
|
|
17368
|
+
const dir = path49.join(SESSIONS_DIR, ".landing.lock");
|
|
17277
17369
|
try {
|
|
17278
17370
|
const entries = fs44.readdirSync(dir);
|
|
17279
17371
|
const token3 = entries.length === 1 ? entries[0] : "";
|
|
@@ -17291,7 +17383,7 @@ function lockLanding() {
|
|
|
17291
17383
|
}
|
|
17292
17384
|
function renewLandingLock() {
|
|
17293
17385
|
const now = /* @__PURE__ */ new Date();
|
|
17294
|
-
fs44.utimesSync(
|
|
17386
|
+
fs44.utimesSync(path49.join(SESSIONS_DIR, ".landing.lock"), now, now);
|
|
17295
17387
|
}
|
|
17296
17388
|
var landingLimits;
|
|
17297
17389
|
var init_landing_lock = __esm({
|
|
@@ -17369,7 +17461,7 @@ var init_retry = __esm({
|
|
|
17369
17461
|
|
|
17370
17462
|
// ../cli/src/lib/agent/landing.ts
|
|
17371
17463
|
import fs45 from "node:fs";
|
|
17372
|
-
import
|
|
17464
|
+
import path50 from "node:path";
|
|
17373
17465
|
function waitingOut(delivery, now = Date.now()) {
|
|
17374
17466
|
const landing = delivery.landing;
|
|
17375
17467
|
if (!landing) return false;
|
|
@@ -17927,7 +18019,7 @@ var init_landing = __esm({
|
|
|
17927
18019
|
try {
|
|
17928
18020
|
return fs45.realpathSync(dir);
|
|
17929
18021
|
} catch {
|
|
17930
|
-
return
|
|
18022
|
+
return path50.resolve(dir);
|
|
17931
18023
|
}
|
|
17932
18024
|
};
|
|
17933
18025
|
return real2(a) === real2(b);
|
|
@@ -17937,7 +18029,7 @@ var init_landing = __esm({
|
|
|
17937
18029
|
|
|
17938
18030
|
// ../cli/src/lib/agent/pictures.ts
|
|
17939
18031
|
import fs46 from "node:fs";
|
|
17940
|
-
import
|
|
18032
|
+
import path51 from "node:path";
|
|
17941
18033
|
function claimRunPictures(box2, sessionId, names4 = []) {
|
|
17942
18034
|
if (!box2 || !freeBox(box2) || names4.length === 0) return [];
|
|
17943
18035
|
const from = pictureBox(box2);
|
|
@@ -17948,7 +18040,7 @@ function claimRunPictures(box2, sessionId, names4 = []) {
|
|
|
17948
18040
|
} catch {
|
|
17949
18041
|
return [];
|
|
17950
18042
|
}
|
|
17951
|
-
return names4.filter(pictureName).map((name) =>
|
|
18043
|
+
return names4.filter(pictureName).map((name) => path51.join(to, name)).filter((file) => fs46.existsSync(file));
|
|
17952
18044
|
}
|
|
17953
18045
|
function returnRunPictures(sessionId, box2) {
|
|
17954
18046
|
if (!box2 || !boxId(box2)) return;
|
|
@@ -17964,7 +18056,7 @@ function moveRunPictures(from, to, files2 = []) {
|
|
|
17964
18056
|
} catch {
|
|
17965
18057
|
return files2;
|
|
17966
18058
|
}
|
|
17967
|
-
return files2.map((file) =>
|
|
18059
|
+
return files2.map((file) => path51.join(pictureBox(to), path51.basename(file)));
|
|
17968
18060
|
}
|
|
17969
18061
|
var pictureName, boxId, freeBox, pictureBox;
|
|
17970
18062
|
var init_pictures = __esm({
|
|
@@ -17973,14 +18065,14 @@ var init_pictures = __esm({
|
|
|
17973
18065
|
init_paths();
|
|
17974
18066
|
pictureName = (name) => /^[0-9a-f-]{36}\.[a-z0-9]{2,5}$/.test(name);
|
|
17975
18067
|
boxId = (box2) => /^[0-9a-f-]{36}$/.test(box2);
|
|
17976
|
-
freeBox = (box2) => boxId(box2) && !fs46.existsSync(
|
|
17977
|
-
pictureBox = (box2) =>
|
|
18068
|
+
freeBox = (box2) => boxId(box2) && !fs46.existsSync(path51.join(SESSIONS_DIR, `${box2}.log`));
|
|
18069
|
+
pictureBox = (box2) => path51.join(SESSIONS_DIR, `${box2}.images`);
|
|
17978
18070
|
}
|
|
17979
18071
|
});
|
|
17980
18072
|
|
|
17981
18073
|
// ../cli/src/lib/memories.ts
|
|
17982
18074
|
import fs47 from "node:fs";
|
|
17983
|
-
import
|
|
18075
|
+
import path52 from "node:path";
|
|
17984
18076
|
function readMemory(text5, builtIn, folder) {
|
|
17985
18077
|
const { meta, body } = splitFrontmatter(text5);
|
|
17986
18078
|
if (meta === null) return null;
|
|
@@ -18000,7 +18092,7 @@ function readMemory(text5, builtIn, folder) {
|
|
|
18000
18092
|
rules: body.trim(),
|
|
18001
18093
|
text: text5,
|
|
18002
18094
|
builtIn,
|
|
18003
|
-
dir:
|
|
18095
|
+
dir: path52.join(MEMORY, folder)
|
|
18004
18096
|
};
|
|
18005
18097
|
}
|
|
18006
18098
|
function globalMemories() {
|
|
@@ -18016,7 +18108,7 @@ function globalMemories() {
|
|
|
18016
18108
|
}
|
|
18017
18109
|
for (const name of folders2) {
|
|
18018
18110
|
if (name in BUNDLED_MEMORY_FILES || name === RESERVED_MEMORY_DIR) continue;
|
|
18019
|
-
const file =
|
|
18111
|
+
const file = path52.join(MEMORY, name, GLOBAL_MEMORY_FILE);
|
|
18020
18112
|
if (!fs47.existsSync(file)) continue;
|
|
18021
18113
|
const memory = readMemory(fs47.readFileSync(file, "utf8"), false, name);
|
|
18022
18114
|
if (memory) out.push(memory);
|
|
@@ -18075,10 +18167,10 @@ var init_facts = __esm({
|
|
|
18075
18167
|
|
|
18076
18168
|
// ../cli/src/lib/view/archive.ts
|
|
18077
18169
|
import fs48 from "node:fs";
|
|
18078
|
-
import
|
|
18170
|
+
import path53 from "node:path";
|
|
18079
18171
|
function archivedId(file) {
|
|
18080
|
-
const own = idPrefix(
|
|
18081
|
-
return own !== null ? own : idPrefix(
|
|
18172
|
+
const own = idPrefix(path53.basename(file));
|
|
18173
|
+
return own !== null ? own : idPrefix(path53.basename(path53.dirname(file)));
|
|
18082
18174
|
}
|
|
18083
18175
|
function readFile(file) {
|
|
18084
18176
|
const id = archivedId(file);
|
|
@@ -18091,14 +18183,14 @@ function readFile(file) {
|
|
|
18091
18183
|
title: meta.title,
|
|
18092
18184
|
archived: meta.archived,
|
|
18093
18185
|
rejected: meta.rejected,
|
|
18094
|
-
relPath: rel(file).split(
|
|
18186
|
+
relPath: rel(file).split(path53.sep).join("/")
|
|
18095
18187
|
},
|
|
18096
18188
|
body: body.replace(/^\n+/, "").replace(/\s+$/, ""),
|
|
18097
18189
|
meta
|
|
18098
18190
|
};
|
|
18099
18191
|
}
|
|
18100
18192
|
function readArchive() {
|
|
18101
|
-
const relPath = rel(ARCHIVE).split(
|
|
18193
|
+
const relPath = rel(ARCHIVE).split(path53.sep).join("/");
|
|
18102
18194
|
if (!fs48.existsSync(ARCHIVE)) return { relPath, cards: [] };
|
|
18103
18195
|
const cards = walkMd(ARCHIVE).map(readFile).filter((found2) => found2 !== null).map((found2) => found2.row);
|
|
18104
18196
|
cards.sort((a, b) => b.id - a.id);
|
|
@@ -18114,7 +18206,7 @@ var init_archive = __esm({
|
|
|
18114
18206
|
});
|
|
18115
18207
|
|
|
18116
18208
|
// ../cli/src/lib/agent/prompts.ts
|
|
18117
|
-
import
|
|
18209
|
+
import path54 from "node:path";
|
|
18118
18210
|
function resumePrompt(deliveryId, cardId2, action) {
|
|
18119
18211
|
if (!deliveryId) return RESUME_PROMPT;
|
|
18120
18212
|
if (action === "conflict") {
|
|
@@ -18158,7 +18250,7 @@ function buildAsk(rawReq, notes = []) {
|
|
|
18158
18250
|
}
|
|
18159
18251
|
function cardFileOf(id) {
|
|
18160
18252
|
const found2 = id === void 0 ? null : locate(id);
|
|
18161
|
-
return found2 ? found2.kind === "group" ?
|
|
18253
|
+
return found2 ? found2.kind === "group" ? path54.join(found2.target, "root.md") : found2.target : "";
|
|
18162
18254
|
}
|
|
18163
18255
|
function realPaths(text5, req) {
|
|
18164
18256
|
if (req.action !== "scheduled" && req.action !== "sub") return text5;
|
|
@@ -18251,7 +18343,7 @@ function frozenRules(req) {
|
|
|
18251
18343
|
function archivedCardFile(id) {
|
|
18252
18344
|
const found2 = locateArchived(id);
|
|
18253
18345
|
if (!found2) return `${rel(ARCHIVE)}/`;
|
|
18254
|
-
return rel(found2.kind === "group" ?
|
|
18346
|
+
return rel(found2.kind === "group" ? path54.join(found2.target, "root.md") : found2.target);
|
|
18255
18347
|
}
|
|
18256
18348
|
function shippedLines(cardId2) {
|
|
18257
18349
|
const delivery = endedDelivery(`${cardId2}`);
|
|
@@ -18422,7 +18514,7 @@ function actionPrompt(req, command, notes) {
|
|
|
18422
18514
|
case "spec": {
|
|
18423
18515
|
const agent = findSpecAgent(req.specAgent ?? "");
|
|
18424
18516
|
const found2 = req.id === void 0 ? null : locate(req.id);
|
|
18425
|
-
const cardFile3 = found2 ? rel(found2.kind === "group" ?
|
|
18517
|
+
const cardFile3 = found2 ? rel(found2.kind === "group" ? path54.join(found2.target, "root.md") : found2.target) : `task #${req.id}`;
|
|
18426
18518
|
const own = agent ? specAgentInstructions(agent) : null;
|
|
18427
18519
|
if (own) notes.push(...own.notes);
|
|
18428
18520
|
const memory = agent ? agentMemoryBlock(agent) : "";
|
|
@@ -18609,7 +18701,7 @@ var init_prompts = __esm({
|
|
|
18609
18701
|
// ../cli/src/lib/agent/sessions.ts
|
|
18610
18702
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
18611
18703
|
import fs49 from "node:fs";
|
|
18612
|
-
import
|
|
18704
|
+
import path55 from "node:path";
|
|
18613
18705
|
function reap(runs, reaped = [], restore = []) {
|
|
18614
18706
|
let changed = false;
|
|
18615
18707
|
const now = Date.now();
|
|
@@ -18780,7 +18872,7 @@ function leftBoardOnLanding(id) {
|
|
|
18780
18872
|
function archivedRejected(id) {
|
|
18781
18873
|
const filed = locateArchived(id);
|
|
18782
18874
|
if (!filed) return null;
|
|
18783
|
-
const file = filed.kind === "group" ?
|
|
18875
|
+
const file = filed.kind === "group" ? path55.join(filed.target, "root.md") : filed.target;
|
|
18784
18876
|
try {
|
|
18785
18877
|
return !!parseFrontmatter(fs49.readFileSync(file, "utf8")).meta?.rejected;
|
|
18786
18878
|
} catch {
|
|
@@ -19039,7 +19131,7 @@ function openRun(req, prompt, notes = [], sessionId = randomUUID6(), said3) {
|
|
|
19039
19131
|
return { run: record, spec };
|
|
19040
19132
|
}
|
|
19041
19133
|
function planningSession(cardId2, cwd = REPO_ROOT) {
|
|
19042
|
-
const here = (dir) => !dir ||
|
|
19134
|
+
const here = (dir) => !dir || path55.resolve(dir) === cwd;
|
|
19043
19135
|
const ended = readRuns().filter((r) => r.status === "done").sort((a, b) => b.startedAt - a.startedAt);
|
|
19044
19136
|
const of = (run3, fork) => {
|
|
19045
19137
|
if (!here(run3.cwd)) return { none: "elsewhere" };
|
|
@@ -19108,7 +19200,9 @@ function requestOf(record) {
|
|
|
19108
19200
|
pictures: record.pictures
|
|
19109
19201
|
};
|
|
19110
19202
|
}
|
|
19111
|
-
async function openResume(id,
|
|
19203
|
+
async function openResume(id, options) {
|
|
19204
|
+
const build = typeof options === "object" ? options : void 0;
|
|
19205
|
+
const requireSession = options === true || !!build;
|
|
19112
19206
|
const restore = [];
|
|
19113
19207
|
const runs = withRuns((all2) => {
|
|
19114
19208
|
reap(all2, [], restore);
|
|
@@ -19122,14 +19216,19 @@ async function openResume(id, requireSession = false) {
|
|
|
19122
19216
|
if (prev.action === "sub") {
|
|
19123
19217
|
return refusal("subRunResume", "a sub-run cannot be continued on its own \u2014 the run that started it starts it again");
|
|
19124
19218
|
}
|
|
19125
|
-
|
|
19219
|
+
const delivery = build ? findDelivery(build.deliveryId) : void 0;
|
|
19220
|
+
if (build && (prev.action !== "implement" || prev.deliveryId !== build.deliveryId || delivery?.status !== "active" || delivery.landing)) {
|
|
19221
|
+
return refusal("deliveryTakenOver", "\u539F\u6784\u5EFA\u4E0D\u5C5E\u4E8E\u5F53\u524D\u5F85\u7EED\u8DD1\u7684\u4EA4\u4ED8\uFF0C\u4E0D\u4F1A\u542F\u52A8\u65B0\u4F1A\u8BDD\u3002", { id: build.deliveryId });
|
|
19222
|
+
}
|
|
19223
|
+
const completedBuild = prev.status === "done" && prev.action === "implement" && delivery?.status === "active" && !delivery.landing && prev.deliveryId === delivery.deliveryId;
|
|
19224
|
+
if (!canPickUp(prev) && !completedBuild) return refusal("runNotResumable", "only a failed, interrupted or stopped run can be continued");
|
|
19126
19225
|
if (prev.action === "create" && !prev.createdCardIds?.length) {
|
|
19127
19226
|
const refused = await preflightCreation(prev);
|
|
19128
19227
|
if (refused) return refused;
|
|
19129
19228
|
}
|
|
19130
19229
|
const retrying = lacksSession(prev);
|
|
19131
|
-
if (requireSession && !resumeSessionId(prev)) {
|
|
19132
|
-
return refusal("runNoSession", "
|
|
19230
|
+
if (requireSession && (!resumeSessionId(prev) || !resumesUnder(prev.harness))) {
|
|
19231
|
+
return refusal("runNoSession", "\u539F\u6784\u5EFA\u6CA1\u6709\u53EF\u6062\u590D\u7684\u4F1A\u8BDD\u6807\u8BC6\uFF0C\u6216\u539F Agent \u4E0D\u652F\u6301\u7EED\u8DD1\uFF1B\u672C\u6B21\u672A\u542F\u52A8\u65B0\u4F1A\u8BDD\u3002");
|
|
19133
19232
|
}
|
|
19134
19233
|
const ask2 = retrying && prev.status !== "stopped" ? retryAsk(prev) : void 0;
|
|
19135
19234
|
if (retrying && !ask2) return refusal("runNoSession", "that run never reported a session id to continue by");
|
|
@@ -19163,7 +19262,13 @@ async function openResume(id, requireSession = false) {
|
|
|
19163
19262
|
const agent = prev.harness || "the agent that started it";
|
|
19164
19263
|
return refusal("runForeign", `this version can't continue a conversation ${agent} opened`, { agent });
|
|
19165
19264
|
}
|
|
19166
|
-
|
|
19265
|
+
const opened = await resumeHeld(prev, plan, sessionId, resuming, fresh);
|
|
19266
|
+
if (!("error" in opened) && build) {
|
|
19267
|
+
opened.spec.notes = [...opened.spec.notes ?? [], "\u7EE7\u7EED\u539F\u6784\u5EFA\u4F1A\u8BDD\uFF1B\u82E5\u539F\u4F1A\u8BDD\u4E0D\u53EF\u7528\uFF0C\u672C\u6B21\u4E0D\u4F1A\u65B0\u5EFA\u4F1A\u8BDD\u3002"];
|
|
19268
|
+
if (build.notes?.trim()) opened.spec.prompt = [opened.spec.prompt || resumePrompt(prev.deliveryId, prev.cardId, prev.action), build.notes.trim()].join("\n\n");
|
|
19269
|
+
writeSpec(opened.spec);
|
|
19270
|
+
}
|
|
19271
|
+
return opened;
|
|
19167
19272
|
}
|
|
19168
19273
|
async function planRetry2(prev, ask2, sessionId, cwd) {
|
|
19169
19274
|
const runtime = prev.runtime && runtimeById(prev.runtime) ? prev.runtime : void 0;
|
|
@@ -19531,7 +19636,7 @@ function dropSpec(sessionId) {
|
|
|
19531
19636
|
}
|
|
19532
19637
|
async function acquireIndexLock(giveUp) {
|
|
19533
19638
|
const until = Date.now() + INDEX_WAIT_MS;
|
|
19534
|
-
const holder =
|
|
19639
|
+
const holder = path55.join(INDEX_LOCK2, "holder");
|
|
19535
19640
|
for (; ; ) {
|
|
19536
19641
|
if (giveUp()) return null;
|
|
19537
19642
|
try {
|
|
@@ -19541,7 +19646,7 @@ async function acquireIndexLock(giveUp) {
|
|
|
19541
19646
|
} catch (err) {
|
|
19542
19647
|
const code = err.code;
|
|
19543
19648
|
if (code === "ENOENT") {
|
|
19544
|
-
fs49.mkdirSync(
|
|
19649
|
+
fs49.mkdirSync(path55.dirname(INDEX_LOCK2), { recursive: true });
|
|
19545
19650
|
continue;
|
|
19546
19651
|
}
|
|
19547
19652
|
if (code !== "EEXIST") throw err;
|
|
@@ -19648,8 +19753,8 @@ var init_sessions = __esm({
|
|
|
19648
19753
|
conflict: "implementing",
|
|
19649
19754
|
hook: "implementing"
|
|
19650
19755
|
};
|
|
19651
|
-
specPathOf = (sessionId) =>
|
|
19652
|
-
asksPathOf = (sessionId) =>
|
|
19756
|
+
specPathOf = (sessionId) => path55.join(SESSIONS_DIR, `${sessionId}.plan.json`);
|
|
19757
|
+
asksPathOf = (sessionId) => path55.join(SESSIONS_DIR, `${sessionId}.asks.json`);
|
|
19653
19758
|
carriesSession = (r) => !!(r.resumedFrom || r.continues && !r.continues.fork);
|
|
19654
19759
|
canPickUp = (r) => (r.status === "error" || r.status === "interrupted" || r.status === "stopped") && !(r.action === "create" && readRuns().some((next) => next.resumedFrom === r.sessionId));
|
|
19655
19760
|
neverStarted = (r) => r.unspawned === true || !!r.errorWhy?.some((why) => why.kind === "notInstalled");
|
|
@@ -19784,7 +19889,7 @@ var init_questions = __esm({
|
|
|
19784
19889
|
|
|
19785
19890
|
// ../cli/src/lib/agent/workflow-cards.ts
|
|
19786
19891
|
import fs50 from "node:fs";
|
|
19787
|
-
import
|
|
19892
|
+
import path56 from "node:path";
|
|
19788
19893
|
function cardsOnWorkflow(id) {
|
|
19789
19894
|
const wanted = id || DEFAULT_WORKFLOW;
|
|
19790
19895
|
const ids = [];
|
|
@@ -19795,8 +19900,8 @@ function cardsOnWorkflow(id) {
|
|
|
19795
19900
|
return [];
|
|
19796
19901
|
}
|
|
19797
19902
|
for (const file of files2) {
|
|
19798
|
-
const base =
|
|
19799
|
-
const card = base === "root.md" ? idPrefix(
|
|
19903
|
+
const base = path56.basename(file);
|
|
19904
|
+
const card = base === "root.md" ? idPrefix(path56.basename(path56.dirname(file))) : idPrefix(base);
|
|
19800
19905
|
if (card === null) continue;
|
|
19801
19906
|
let meta;
|
|
19802
19907
|
try {
|
|
@@ -19859,7 +19964,7 @@ var init_workflow_cards = __esm({
|
|
|
19859
19964
|
|
|
19860
19965
|
// ../cli/src/lib/readme.ts
|
|
19861
19966
|
import fs51 from "node:fs";
|
|
19862
|
-
import
|
|
19967
|
+
import path57 from "node:path";
|
|
19863
19968
|
function stripReadmeRefs(target) {
|
|
19864
19969
|
if (!fs51.existsSync(README)) return [];
|
|
19865
19970
|
const needle = target.kind === "group" ? `](${target.rel}/` : `](${target.rel})`;
|
|
@@ -19890,7 +19995,7 @@ function stripReadmeRefs(target) {
|
|
|
19890
19995
|
}
|
|
19891
19996
|
function addReadmeRef(id, title, relPath) {
|
|
19892
19997
|
if (!fs51.existsSync(README)) return false;
|
|
19893
|
-
const link2 = relPath.split(
|
|
19998
|
+
const link2 = relPath.split(path57.sep).join("/");
|
|
19894
19999
|
const bullet = `- [#${id} ${title}](${link2})`;
|
|
19895
20000
|
const heading = `## ${TASKS_HEADING}`;
|
|
19896
20001
|
let lines = fs51.readFileSync(README, "utf8").split("\n");
|
|
@@ -19917,8 +20022,8 @@ function addReadmeRef(id, title, relPath) {
|
|
|
19917
20022
|
}
|
|
19918
20023
|
function repointReadmeLink(id, oldRel, newRel, title) {
|
|
19919
20024
|
if (!fs51.existsSync(README)) return false;
|
|
19920
|
-
const oldLink = oldRel.split(
|
|
19921
|
-
const newLink = newRel.split(
|
|
20025
|
+
const oldLink = oldRel.split(path57.sep).join("/");
|
|
20026
|
+
const newLink = newRel.split(path57.sep).join("/");
|
|
19922
20027
|
const linkRe = new RegExp(`\\[#${id}\\b[^\\]]*\\]\\(${escapeRegex(oldLink)}\\)`);
|
|
19923
20028
|
const lines = fs51.readFileSync(README, "utf8").split("\n");
|
|
19924
20029
|
let changed = false;
|
|
@@ -19948,13 +20053,13 @@ var init_readme = __esm({
|
|
|
19948
20053
|
|
|
19949
20054
|
// ../cli/src/lib/reconcile.ts
|
|
19950
20055
|
import fs52 from "node:fs";
|
|
19951
|
-
import
|
|
20056
|
+
import path58 from "node:path";
|
|
19952
20057
|
function liveIds() {
|
|
19953
20058
|
const ids = /* @__PURE__ */ new Set();
|
|
19954
20059
|
for (const file of walkMd(TODO)) {
|
|
19955
|
-
const base =
|
|
20060
|
+
const base = path58.basename(file);
|
|
19956
20061
|
if (base === "README.md") continue;
|
|
19957
|
-
const id = base === "root.md" ? idPrefix(
|
|
20062
|
+
const id = base === "root.md" ? idPrefix(path58.basename(path58.dirname(file))) : idPrefix(base);
|
|
19958
20063
|
if (id != null) ids.add(id);
|
|
19959
20064
|
}
|
|
19960
20065
|
return ids;
|
|
@@ -19962,16 +20067,16 @@ function liveIds() {
|
|
|
19962
20067
|
function indexableCards() {
|
|
19963
20068
|
const out = [];
|
|
19964
20069
|
for (const dir of walkDirs(TODO)) {
|
|
19965
|
-
const id = idPrefix(
|
|
19966
|
-
if (id != null && fs52.existsSync(
|
|
19967
|
-
out.push({ id, rel:
|
|
20070
|
+
const id = idPrefix(path58.basename(dir));
|
|
20071
|
+
if (id != null && fs52.existsSync(path58.join(dir, "root.md"))) {
|
|
20072
|
+
out.push({ id, rel: path58.join(path58.relative(TODO, dir), "root.md").split(path58.sep).join("/") });
|
|
19968
20073
|
}
|
|
19969
20074
|
}
|
|
19970
20075
|
for (const file of walkMd(TODO)) {
|
|
19971
|
-
const base =
|
|
20076
|
+
const base = path58.basename(file);
|
|
19972
20077
|
if (base === "README.md" || base === "root.md") continue;
|
|
19973
|
-
const relTODO =
|
|
19974
|
-
const segs = relTODO.split(
|
|
20078
|
+
const relTODO = path58.relative(TODO, file);
|
|
20079
|
+
const segs = relTODO.split(path58.sep);
|
|
19975
20080
|
if (segs.length !== 1) continue;
|
|
19976
20081
|
const id = idPrefix(base);
|
|
19977
20082
|
if (id != null) out.push({ id, rel: segs.join("/") });
|
|
@@ -19989,13 +20094,13 @@ function readmeComplaints(fix) {
|
|
|
19989
20094
|
const id = Number(m[1]);
|
|
19990
20095
|
const linkPath = m[2];
|
|
19991
20096
|
indexed.add(id);
|
|
19992
|
-
if (fs52.existsSync(
|
|
20097
|
+
if (fs52.existsSync(path58.join(TODO, linkPath))) continue;
|
|
19993
20098
|
const found2 = locate(id);
|
|
19994
20099
|
if (!found2) {
|
|
19995
20100
|
said3.push(`README links #${id} \u2192 ${linkPath}, but no card with that id exists (removed by hand?). Drop the entry or restore the file.`);
|
|
19996
20101
|
continue;
|
|
19997
20102
|
}
|
|
19998
|
-
const want = (found2.kind === "group" ?
|
|
20103
|
+
const want = (found2.kind === "group" ? path58.join(found2.rel, "root.md") : found2.rel).split(path58.sep).join("/");
|
|
19999
20104
|
if (want === linkPath) continue;
|
|
20000
20105
|
if (!fix) {
|
|
20001
20106
|
said3.push(`README links #${id} \u2192 ${linkPath}, which isn't there any more; the card is at ${want}.`);
|
|
@@ -20017,9 +20122,9 @@ function crossRefComplaints() {
|
|
|
20017
20122
|
const said3 = [];
|
|
20018
20123
|
const live = liveIds();
|
|
20019
20124
|
for (const file of walkMd(TODO)) {
|
|
20020
|
-
const base =
|
|
20125
|
+
const base = path58.basename(file);
|
|
20021
20126
|
if (base === "README.md") continue;
|
|
20022
|
-
const ownerId = base === "root.md" ? idPrefix(
|
|
20127
|
+
const ownerId = base === "root.md" ? idPrefix(path58.basename(path58.dirname(file))) : idPrefix(base);
|
|
20023
20128
|
if (ownerId == null) continue;
|
|
20024
20129
|
const { meta } = parseFrontmatter(fs52.readFileSync(file, "utf8"));
|
|
20025
20130
|
if (!meta) continue;
|
|
@@ -20059,7 +20164,7 @@ var init_reconcile = __esm({
|
|
|
20059
20164
|
|
|
20060
20165
|
// ../cli/src/commands/card.ts
|
|
20061
20166
|
import fs53 from "node:fs";
|
|
20062
|
-
import
|
|
20167
|
+
import path59 from "node:path";
|
|
20063
20168
|
function defaultBody() {
|
|
20064
20169
|
return [
|
|
20065
20170
|
"<\u7528\u4E00\u5C0F\u6BB5\u8BDD\u5148\u8BB2\u53D1\u751F\u4E86\u4EC0\u4E48\u3001\u7528\u6237\u9047\u5230\u4EC0\u4E48\uFF0C\u518D\u8BB2\u8FD9\u5F20\u5361\u8981\u8FBE\u5230\u7684\u7ED3\u679C\u3002>",
|
|
@@ -20113,13 +20218,13 @@ function validateCreate(opts) {
|
|
|
20113
20218
|
}
|
|
20114
20219
|
function cmdCreate(opts) {
|
|
20115
20220
|
const { title, priority, blocked_by, related, modules, triage, workflow, source, questions, written } = validateCreate(opts);
|
|
20116
|
-
if (opts.id === void 0) die("
|
|
20221
|
+
if (opts.id === void 0) die("\u8BF7\u901A\u8FC7 `akb raw create` \u521B\u5EFA\u5361\u7247\uFF0C\u7531 Cloud \u5206\u914D\u7F16\u53F7", { kind: "needs-cloud" });
|
|
20117
20222
|
const start = opts.id;
|
|
20118
20223
|
const slug2 = slugify(opts.slug !== void 0 ? opts.slug : title);
|
|
20119
20224
|
const fileRel = `${start}-${slug2}.md`;
|
|
20120
|
-
const file =
|
|
20225
|
+
const file = path59.join(TODO, fileRel);
|
|
20121
20226
|
const held4 = locate(start) ?? locateArchived(start);
|
|
20122
|
-
if (held4 &&
|
|
20227
|
+
if (held4 && path59.basename(held4.target) === `${start}-${slug2}.md`) {
|
|
20123
20228
|
say(start);
|
|
20124
20229
|
say(` ${rel(held4.target)} was already written`);
|
|
20125
20230
|
return { id: start, ids: [start], title, file: rel(held4.target), indexed: false };
|
|
@@ -20168,7 +20273,7 @@ function cmdUpdate(id, flags) {
|
|
|
20168
20273
|
if (creating) die(creating, { kind: "card-being-created", id });
|
|
20169
20274
|
const found2 = locate(id);
|
|
20170
20275
|
if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
|
|
20171
|
-
const file = found2.kind === "group" ?
|
|
20276
|
+
const file = found2.kind === "group" ? path59.join(found2.target, "root.md") : found2.target;
|
|
20172
20277
|
const { meta, body } = parseFrontmatter(fs53.readFileSync(file, "utf8"));
|
|
20173
20278
|
if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
|
|
20174
20279
|
if (flags.blockedBy !== void 0 && flags.addBlockedBy !== void 0) die("--blocked-by replaces the list and --add-blocked-by appends to it \u2014 pass one");
|
|
@@ -20249,26 +20354,26 @@ function cmdUpdate(id, flags) {
|
|
|
20249
20354
|
meta.status = "todo";
|
|
20250
20355
|
changes.push("status\u2192todo (open questions)");
|
|
20251
20356
|
}
|
|
20252
|
-
const curRel =
|
|
20357
|
+
const curRel = path59.relative(TODO, file);
|
|
20253
20358
|
const isSubtask = found2.kind === "file" && enclosingGroupRoot(file) !== null;
|
|
20254
|
-
let base =
|
|
20359
|
+
let base = path59.basename(file);
|
|
20255
20360
|
if (flags.slug !== void 0) {
|
|
20256
20361
|
if (found2.kind === "group") die("renaming a group root by script is not supported");
|
|
20257
20362
|
base = `${id}-${slugify(flags.slug)}.md`;
|
|
20258
20363
|
}
|
|
20259
|
-
const destRel =
|
|
20260
|
-
const dest =
|
|
20364
|
+
const destRel = path59.join(path59.dirname(curRel), base);
|
|
20365
|
+
const dest = path59.join(TODO, destRel);
|
|
20261
20366
|
const moving = dest !== file;
|
|
20262
20367
|
if (moving && fs53.existsSync(dest)) die(`${rel(dest)} already exists`);
|
|
20263
20368
|
fs53.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
|
|
20264
20369
|
if (moving) fs53.renameSync(file, dest);
|
|
20265
20370
|
if (isSubtask) {
|
|
20266
20371
|
if (moving || changes.includes("title")) repointReadmeLink(id, curRel, destRel, meta.title);
|
|
20267
|
-
if (moving) changes.push(`renamed \u2192 ${destRel.split(
|
|
20372
|
+
if (moving) changes.push(`renamed \u2192 ${destRel.split(path59.sep).join("/")}`);
|
|
20268
20373
|
} else if (moving) {
|
|
20269
20374
|
stripReadmeRefs({ kind: "file", rel: curRel });
|
|
20270
20375
|
addReadmeRef(id, meta.title, destRel);
|
|
20271
|
-
changes.push(`renamed \u2192 ${destRel.split(
|
|
20376
|
+
changes.push(`renamed \u2192 ${destRel.split(path59.sep).join("/")}`);
|
|
20272
20377
|
} else if (changes.includes("title")) {
|
|
20273
20378
|
stripReadmeRefs({ kind: "file", rel: curRel });
|
|
20274
20379
|
addReadmeRef(id, meta.title, curRel);
|
|
@@ -20280,7 +20385,7 @@ function cmdUpdateQuestions(id, input) {
|
|
|
20280
20385
|
const ops = readQuestionOps(input.ops ?? []);
|
|
20281
20386
|
const found2 = locate(id);
|
|
20282
20387
|
if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
|
|
20283
|
-
const file = found2.kind === "group" ?
|
|
20388
|
+
const file = found2.kind === "group" ? path59.join(found2.target, "root.md") : found2.target;
|
|
20284
20389
|
const { meta, body } = parseFrontmatter(fs53.readFileSync(file, "utf8"));
|
|
20285
20390
|
if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
|
|
20286
20391
|
if (insideRun() && ops.some((op) => op.kind === "skip" || op.kind === "unskip")) {
|
|
@@ -20429,9 +20534,9 @@ var init_config_template = __esm({
|
|
|
20429
20534
|
|
|
20430
20535
|
// ../cli/src/lib/agent/local.ts
|
|
20431
20536
|
import fs54 from "node:fs";
|
|
20432
|
-
import
|
|
20537
|
+
import path60 from "node:path";
|
|
20433
20538
|
function takeLocalModels(board2) {
|
|
20434
|
-
const file =
|
|
20539
|
+
const file = path60.join(board2, ".local.json");
|
|
20435
20540
|
let held4;
|
|
20436
20541
|
try {
|
|
20437
20542
|
held4 = configBlock(JSON.parse(fs54.readFileSync(file, "utf8")));
|
|
@@ -20486,7 +20591,7 @@ var init_check2 = __esm({
|
|
|
20486
20591
|
});
|
|
20487
20592
|
|
|
20488
20593
|
// ../cli/src/lib/signals/identity.ts
|
|
20489
|
-
import { createHash as
|
|
20594
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
20490
20595
|
function host(url) {
|
|
20491
20596
|
try {
|
|
20492
20597
|
return new URL(url).hostname.replace(/^www\./, "");
|
|
@@ -20508,13 +20613,13 @@ var init_identity2 = __esm({
|
|
|
20508
20613
|
"../cli/src/lib/signals/identity.ts"() {
|
|
20509
20614
|
"use strict";
|
|
20510
20615
|
DERIVED = "derived-";
|
|
20511
|
-
derivedSourceId = (seed) => `${DERIVED}${
|
|
20616
|
+
derivedSourceId = (seed) => `${DERIVED}${createHash3("sha256").update(seed).digest("hex").slice(0, 16)}`;
|
|
20512
20617
|
}
|
|
20513
20618
|
});
|
|
20514
20619
|
|
|
20515
20620
|
// ../cli/src/lib/signals/migrate.ts
|
|
20516
20621
|
import fs55 from "node:fs";
|
|
20517
|
-
import
|
|
20622
|
+
import path61 from "node:path";
|
|
20518
20623
|
function readHandled() {
|
|
20519
20624
|
let text5;
|
|
20520
20625
|
try {
|
|
@@ -20536,7 +20641,7 @@ function readHandled() {
|
|
|
20536
20641
|
}
|
|
20537
20642
|
function lift(from, into) {
|
|
20538
20643
|
fs55.mkdirSync(into, { recursive: true });
|
|
20539
|
-
fs55.renameSync(from,
|
|
20644
|
+
fs55.renameSync(from, path61.join(into, freeName(into, path61.basename(from))));
|
|
20540
20645
|
}
|
|
20541
20646
|
function repoint(dir, from, into) {
|
|
20542
20647
|
let entries;
|
|
@@ -20547,7 +20652,7 @@ function repoint(dir, from, into) {
|
|
|
20547
20652
|
}
|
|
20548
20653
|
for (const entry of entries) {
|
|
20549
20654
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
20550
|
-
const file =
|
|
20655
|
+
const file = path61.join(dir, entry.name);
|
|
20551
20656
|
const text5 = fs55.readFileSync(file, "utf8");
|
|
20552
20657
|
if (text5.includes(from)) fs55.writeFileSync(file, text5.split(from).join(into));
|
|
20553
20658
|
}
|
|
@@ -20560,17 +20665,17 @@ function dropIfEmpty2(dir) {
|
|
|
20560
20665
|
}
|
|
20561
20666
|
function liftInbox() {
|
|
20562
20667
|
if (!fs55.existsSync(SIGNALS_OLD_INBOX)) return;
|
|
20563
|
-
const oldFiles =
|
|
20668
|
+
const oldFiles = path61.join(SIGNALS_OLD_INBOX, "files");
|
|
20564
20669
|
if (fs55.existsSync(oldFiles)) {
|
|
20565
20670
|
for (const entry of fs55.readdirSync(oldFiles, { withFileTypes: true })) {
|
|
20566
|
-
if (entry.isFile()) lift(
|
|
20671
|
+
if (entry.isFile()) lift(path61.join(oldFiles, entry.name), SIGNALS_FILES);
|
|
20567
20672
|
}
|
|
20568
20673
|
dropIfEmpty2(oldFiles);
|
|
20569
20674
|
}
|
|
20570
20675
|
const held4 = new Set(readFolder(TRIAGE).map((signal2) => signal2.sourceId));
|
|
20571
20676
|
for (const entry of fs55.readdirSync(SIGNALS_OLD_INBOX, { withFileTypes: true })) {
|
|
20572
20677
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
20573
|
-
const from =
|
|
20678
|
+
const from = path61.join(SIGNALS_OLD_INBOX, entry.name);
|
|
20574
20679
|
const item = parse(from);
|
|
20575
20680
|
if (item && held4.has(item.sourceId)) {
|
|
20576
20681
|
fs55.rmSync(from, { force: true });
|
|
@@ -20579,7 +20684,7 @@ function liftInbox() {
|
|
|
20579
20684
|
if (item) held4.add(item.sourceId);
|
|
20580
20685
|
lift(from, TRIAGE);
|
|
20581
20686
|
}
|
|
20582
|
-
const wasAt = asWritten(
|
|
20687
|
+
const wasAt = asWritten(path61.join(SIGNALS_OLD_INBOX, "files"));
|
|
20583
20688
|
const nowAt = asWritten(SIGNALS_FILES);
|
|
20584
20689
|
for (const dir of [TRIAGE, SIGNALS_ARCHIVED, SIGNALS_DISMISSED]) repoint(dir, wasAt, nowAt);
|
|
20585
20690
|
dropIfEmpty2(SIGNALS_OLD_INBOX);
|
|
@@ -20605,7 +20710,7 @@ function liftHandled() {
|
|
|
20605
20710
|
"---",
|
|
20606
20711
|
""
|
|
20607
20712
|
];
|
|
20608
|
-
fs55.writeFileSync(
|
|
20713
|
+
fs55.writeFileSync(path61.join(SIGNALS_DISMISSED, name), `${front.join("\n")}
|
|
20609
20714
|
`);
|
|
20610
20715
|
}
|
|
20611
20716
|
fs55.rmSync(SIGNALS_OLD_HANDLED, { force: true });
|
|
@@ -20626,14 +20731,14 @@ var init_migrate = __esm({
|
|
|
20626
20731
|
init_yaml();
|
|
20627
20732
|
init_inbox();
|
|
20628
20733
|
HANDLED_LINE = /^-\s+(.+?)\s+—\s+(.+)$/;
|
|
20629
|
-
asWritten = (dir) => `${rel(dir).split(
|
|
20734
|
+
asWritten = (dir) => `${rel(dir).split(path61.sep).join("/")}/`;
|
|
20630
20735
|
}
|
|
20631
20736
|
});
|
|
20632
20737
|
|
|
20633
20738
|
// ../cli/src/lib/signals/add.ts
|
|
20634
20739
|
import fs56 from "node:fs";
|
|
20635
|
-
import
|
|
20636
|
-
import { createHash as
|
|
20740
|
+
import path62 from "node:path";
|
|
20741
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
20637
20742
|
function linkTitle(url) {
|
|
20638
20743
|
let at;
|
|
20639
20744
|
try {
|
|
@@ -20645,7 +20750,7 @@ function linkTitle(url) {
|
|
|
20645
20750
|
return oneLine(where) || url.slice(0, TITLE_MAX);
|
|
20646
20751
|
}
|
|
20647
20752
|
function asText(file) {
|
|
20648
|
-
const ext =
|
|
20753
|
+
const ext = path62.extname(file.name).toLowerCase();
|
|
20649
20754
|
if (!file.type.startsWith("text/") && file.type !== "application/json" && !TEXT_EXTENSIONS.has(ext)) return null;
|
|
20650
20755
|
try {
|
|
20651
20756
|
const text5 = new TextDecoder("utf-8", { fatal: true }).decode(file.data);
|
|
@@ -20655,10 +20760,10 @@ function asText(file) {
|
|
|
20655
20760
|
}
|
|
20656
20761
|
}
|
|
20657
20762
|
function fileName2(name, data) {
|
|
20658
|
-
const base =
|
|
20659
|
-
const ext =
|
|
20660
|
-
const word =
|
|
20661
|
-
const hash2 =
|
|
20763
|
+
const base = path62.basename(name);
|
|
20764
|
+
const ext = path62.extname(base).toLowerCase().slice(0, 12);
|
|
20765
|
+
const word = path62.basename(base, path62.extname(base)).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40) || "file";
|
|
20766
|
+
const hash2 = createHash4("sha256").update(data).digest("hex").slice(0, 8);
|
|
20662
20767
|
return `${word}-${hash2}${ext}`;
|
|
20663
20768
|
}
|
|
20664
20769
|
function addToInbox(drop) {
|
|
@@ -20666,9 +20771,9 @@ function addToInbox(drop) {
|
|
|
20666
20771
|
const typed = (drop.text ?? "").trim();
|
|
20667
20772
|
const file = drop.file;
|
|
20668
20773
|
if (!typed && !file) return { ok: false, error: "nothing to add \u2014 paste a link or some text, or drop a file." };
|
|
20669
|
-
if (file && file.data.length === 0) return { ok: false, error: `${
|
|
20774
|
+
if (file && file.data.length === 0) return { ok: false, error: `${path62.basename(file.name)} is empty.` };
|
|
20670
20775
|
if (file && file.data.length > MAX_BYTES) {
|
|
20671
|
-
return { ok: false, error: `${
|
|
20776
|
+
return { ok: false, error: `${path62.basename(file.name)} is over ${MAX_BYTES / 1024 / 1024} MB \u2014 too big for the board.` };
|
|
20672
20777
|
}
|
|
20673
20778
|
const read4 = describe(typed, file);
|
|
20674
20779
|
const keep2 = read4.keep;
|
|
@@ -20705,7 +20810,7 @@ function describe(typed, file) {
|
|
|
20705
20810
|
return only ? [only] : [];
|
|
20706
20811
|
};
|
|
20707
20812
|
if (file) {
|
|
20708
|
-
const name =
|
|
20813
|
+
const name = path62.basename(file.name);
|
|
20709
20814
|
const text5 = asText(file);
|
|
20710
20815
|
const note = typed ? `
|
|
20711
20816
|
|
|
@@ -20717,7 +20822,7 @@ ${typed}` : "";
|
|
|
20717
20822
|
const at = keepAt(file);
|
|
20718
20823
|
return {
|
|
20719
20824
|
incoming: {
|
|
20720
|
-
title:
|
|
20825
|
+
title: path62.basename(name, path62.extname(name)) || name,
|
|
20721
20826
|
summary: `${name} \u2014 ${boardRel2(at)}${note}`,
|
|
20722
20827
|
...named2
|
|
20723
20828
|
},
|
|
@@ -20755,19 +20860,19 @@ var init_add = __esm({
|
|
|
20755
20860
|
MAX_BYTES = 25 * 1024 * 1024;
|
|
20756
20861
|
TITLE_MAX = 120;
|
|
20757
20862
|
TEXT_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".markdown", ".txt", ".text", ".csv", ".json", ".yml", ".yaml", ".log"]);
|
|
20758
|
-
boardRel2 = (file) => rel(file).split(
|
|
20863
|
+
boardRel2 = (file) => rel(file).split(path62.sep).join("/");
|
|
20759
20864
|
oneLine = (text5) => {
|
|
20760
20865
|
const first = text5.split("\n").find((line) => line.trim()) ?? "";
|
|
20761
20866
|
const clean = first.replace(/^#+\s*/, "").trim();
|
|
20762
20867
|
return clean.length > TITLE_MAX ? `${clean.slice(0, TITLE_MAX - 1).trimEnd()}\u2026` : clean;
|
|
20763
20868
|
};
|
|
20764
|
-
keepAt = (file) =>
|
|
20869
|
+
keepAt = (file) => path62.join(SIGNALS_FILES, fileName2(file.name, file.data));
|
|
20765
20870
|
}
|
|
20766
20871
|
});
|
|
20767
20872
|
|
|
20768
20873
|
// ../cli/src/lib/recurring.ts
|
|
20769
20874
|
import fs57 from "node:fs";
|
|
20770
|
-
import
|
|
20875
|
+
import path63 from "node:path";
|
|
20771
20876
|
function migratePruneMemoryCard() {
|
|
20772
20877
|
const dir = recurringDir();
|
|
20773
20878
|
let names4;
|
|
@@ -20778,7 +20883,7 @@ function migratePruneMemoryCard() {
|
|
|
20778
20883
|
}
|
|
20779
20884
|
const name = names4.find((entry) => entry.endsWith(`-${PRUNE_SLUG}.md`));
|
|
20780
20885
|
if (!name) return null;
|
|
20781
|
-
const file =
|
|
20886
|
+
const file = path63.join(dir, name);
|
|
20782
20887
|
try {
|
|
20783
20888
|
const { meta } = parseFrontmatter(fs57.readFileSync(file, "utf8"));
|
|
20784
20889
|
const cadence = meta?.cadence;
|
|
@@ -20795,11 +20900,11 @@ function legacyCards() {
|
|
|
20795
20900
|
for (const entry of fs57.readdirSync(dir, { withFileTypes: true })) {
|
|
20796
20901
|
const id = idPrefix(entry.name);
|
|
20797
20902
|
if (id === null) continue;
|
|
20798
|
-
const full =
|
|
20903
|
+
const full = path63.join(dir, entry.name);
|
|
20799
20904
|
const slug2 = entry.name.replace(/\.md$/, "").replace(/^\d+-?/, "");
|
|
20800
20905
|
if (entry.isFile() && entry.name.endsWith(".md")) cards.push({ id, slug: slug2, file: full });
|
|
20801
|
-
else if (entry.isDirectory() && fs57.existsSync(
|
|
20802
|
-
cards.push({ id, slug: slug2, file:
|
|
20906
|
+
else if (entry.isDirectory() && fs57.existsSync(path63.join(full, "root.md"))) {
|
|
20907
|
+
cards.push({ id, slug: slug2, file: path63.join(full, "root.md"), folder: full });
|
|
20803
20908
|
}
|
|
20804
20909
|
}
|
|
20805
20910
|
return cards.sort((a, b) => a.id - b.id);
|
|
@@ -20808,8 +20913,8 @@ function mergeInto2(from, to, skip = []) {
|
|
|
20808
20913
|
fs57.mkdirSync(to, { recursive: true });
|
|
20809
20914
|
for (const entry of fs57.readdirSync(from, { withFileTypes: true })) {
|
|
20810
20915
|
if (skip.includes(entry.name)) continue;
|
|
20811
|
-
const source =
|
|
20812
|
-
const target =
|
|
20916
|
+
const source = path63.join(from, entry.name);
|
|
20917
|
+
const target = path63.join(to, entry.name);
|
|
20813
20918
|
if (!fs57.existsSync(target)) fs57.renameSync(source, target);
|
|
20814
20919
|
else if (entry.isDirectory() && fs57.statSync(target).isDirectory()) mergeInto2(source, target);
|
|
20815
20920
|
}
|
|
@@ -20819,9 +20924,9 @@ function moveSideFolders() {
|
|
|
20819
20924
|
const dir = recurringDir();
|
|
20820
20925
|
for (const entry of fs57.readdirSync(dir, { withFileTypes: true })) {
|
|
20821
20926
|
if (!entry.isDirectory() || idPrefix(entry.name) !== null) continue;
|
|
20822
|
-
const from =
|
|
20927
|
+
const from = path63.join(dir, entry.name);
|
|
20823
20928
|
if (isEmpty(from)) fs57.rmSync(from, { recursive: true, force: true });
|
|
20824
|
-
else mergeInto2(from,
|
|
20929
|
+
else mergeInto2(from, path63.join(AGENTS, entry.name));
|
|
20825
20930
|
}
|
|
20826
20931
|
}
|
|
20827
20932
|
function sections(body) {
|
|
@@ -20871,7 +20976,7 @@ function agentName(card, text5) {
|
|
|
20871
20976
|
const known2 = /* @__PURE__ */ new Set([...specAgentCatalog().agents.map((a) => a.name), ...ROLE_NAMES]);
|
|
20872
20977
|
for (const name of [slug2, `${slug2}-${card.id}`]) {
|
|
20873
20978
|
if (!AGENT_NAME.test(name)) continue;
|
|
20874
|
-
const file =
|
|
20979
|
+
const file = path63.join(AGENTS, name, AGENT_FILE3);
|
|
20875
20980
|
const written = fs57.existsSync(file) ? fs57.readFileSync(file, "utf8") : null;
|
|
20876
20981
|
if (written === text5(name)) return name;
|
|
20877
20982
|
if (written === null && !known2.has(name)) return name;
|
|
@@ -20889,7 +20994,7 @@ ${q.options.map((o) => `- ${o}`).join("\n")}` : "";
|
|
|
20889
20994
|
function removeCard(card) {
|
|
20890
20995
|
dropCrossRefs(card.id);
|
|
20891
20996
|
const target = card.folder ?? card.file;
|
|
20892
|
-
stripReadmeRefs({ kind: card.folder ? "group" : "file", rel:
|
|
20997
|
+
stripReadmeRefs({ kind: card.folder ? "group" : "file", rel: path63.relative(TODO, target).split(path63.sep).join("/") });
|
|
20893
20998
|
fs57.rmSync(target, { recursive: true, force: true });
|
|
20894
20999
|
}
|
|
20895
21000
|
function migrateCard(card) {
|
|
@@ -20903,8 +21008,8 @@ function migrateCard(card) {
|
|
|
20903
21008
|
if (!flow) throw new Error("this board has no workflow to put it in");
|
|
20904
21009
|
const text5 = (name2) => agentText(name2, meta.title || card.slug, body);
|
|
20905
21010
|
const name = agentName(card, text5);
|
|
20906
|
-
const dir =
|
|
20907
|
-
const file =
|
|
21011
|
+
const dir = path63.join(AGENTS, name);
|
|
21012
|
+
const file = path63.join(dir, AGENT_FILE3);
|
|
20908
21013
|
const madeDir = !fs57.existsSync(dir);
|
|
20909
21014
|
const wrote = !fs57.existsSync(file);
|
|
20910
21015
|
try {
|
|
@@ -20971,7 +21076,7 @@ var init_recurring = __esm({
|
|
|
20971
21076
|
PRUNE_SLUG = "prune-the-memory";
|
|
20972
21077
|
FETCH_SLUG = "fetch-triage-items";
|
|
20973
21078
|
AGENT_FILE3 = "AGENT.md";
|
|
20974
|
-
recurringDir = () =>
|
|
21079
|
+
recurringDir = () => path63.join(TODO, LEGACY_RECURRING);
|
|
20975
21080
|
isEmpty = (dir) => fs57.readdirSync(dir).every((name) => name === ".DS_Store");
|
|
20976
21081
|
SECTION = /^##\s+(.+?)\s*$/;
|
|
20977
21082
|
}
|
|
@@ -21176,7 +21281,7 @@ var init_scheduled = __esm({
|
|
|
21176
21281
|
|
|
21177
21282
|
// ../cli/src/lib/card-age.ts
|
|
21178
21283
|
import fs59 from "node:fs";
|
|
21179
|
-
import
|
|
21284
|
+
import path64 from "node:path";
|
|
21180
21285
|
function staleAfterIn(config) {
|
|
21181
21286
|
const days = Number(LINE.exec(config)?.[1]);
|
|
21182
21287
|
return Number.isInteger(days) && days > 0 ? days : STALE_AFTER_DEFAULT;
|
|
@@ -21186,14 +21291,14 @@ function staleAfter() {
|
|
|
21186
21291
|
return staleAfterIn(fs59.readFileSync(CONFIG, "utf8"));
|
|
21187
21292
|
}
|
|
21188
21293
|
function todoPathspec(top) {
|
|
21189
|
-
const from =
|
|
21190
|
-
if (!from || from.startsWith("..") ||
|
|
21191
|
-
return from.split(
|
|
21294
|
+
const from = path64.relative(real(top), real(TODO));
|
|
21295
|
+
if (!from || from.startsWith("..") || path64.isAbsolute(from)) return null;
|
|
21296
|
+
return from.split(path64.sep).join("/");
|
|
21192
21297
|
}
|
|
21193
21298
|
function cardFile2(spec, printed) {
|
|
21194
21299
|
const p = unquotePath(printed);
|
|
21195
21300
|
if (!p.startsWith(`${spec}/`)) return null;
|
|
21196
|
-
return
|
|
21301
|
+
return path64.join(TODO, ...p.slice(spec.length + 1).split("/"));
|
|
21197
21302
|
}
|
|
21198
21303
|
function dirtyCards(top, spec) {
|
|
21199
21304
|
const out = git(["status", "--porcelain", "--untracked-files=all", "--", spec], top);
|
|
@@ -21242,7 +21347,7 @@ var init_card_age = __esm({
|
|
|
21242
21347
|
try {
|
|
21243
21348
|
return fs59.realpathSync(p);
|
|
21244
21349
|
} catch {
|
|
21245
|
-
return
|
|
21350
|
+
return path64.resolve(p);
|
|
21246
21351
|
}
|
|
21247
21352
|
};
|
|
21248
21353
|
unquotePath = (line) => {
|
|
@@ -21278,7 +21383,7 @@ var init_card_holds = __esm({
|
|
|
21278
21383
|
|
|
21279
21384
|
// ../cli/src/commands/list.ts
|
|
21280
21385
|
import fs60 from "node:fs";
|
|
21281
|
-
import
|
|
21386
|
+
import path65 from "node:path";
|
|
21282
21387
|
function summaryLine(body) {
|
|
21283
21388
|
const lines = body.split("\n");
|
|
21284
21389
|
let i = 0;
|
|
@@ -21294,12 +21399,12 @@ function summaryLine(body) {
|
|
|
21294
21399
|
function openRows() {
|
|
21295
21400
|
const rows = [];
|
|
21296
21401
|
for (const file of walkMd(TODO)) {
|
|
21297
|
-
const base =
|
|
21402
|
+
const base = path65.basename(file);
|
|
21298
21403
|
if (base === "README.md") continue;
|
|
21299
21404
|
const isRoot = base === "root.md";
|
|
21300
|
-
const id = isRoot ? idPrefix(
|
|
21405
|
+
const id = isRoot ? idPrefix(path65.basename(path65.dirname(file))) : idPrefix(base);
|
|
21301
21406
|
if (id == null) continue;
|
|
21302
|
-
if (isLegacyRecurring(
|
|
21407
|
+
if (isLegacyRecurring(path65.relative(TODO, file))) continue;
|
|
21303
21408
|
const { meta, body } = parseFrontmatter(fs60.readFileSync(file, "utf8"));
|
|
21304
21409
|
rows.push({
|
|
21305
21410
|
id,
|
|
@@ -21344,12 +21449,12 @@ function archivedFiles() {
|
|
|
21344
21449
|
const files2 = /* @__PURE__ */ new Map();
|
|
21345
21450
|
if (!fs60.existsSync(ARCHIVE)) return files2;
|
|
21346
21451
|
for (const file of walkMd(ARCHIVE)) {
|
|
21347
|
-
const id = idPrefix(
|
|
21452
|
+
const id = idPrefix(path65.basename(file));
|
|
21348
21453
|
if (id !== null) files2.set(id, file);
|
|
21349
21454
|
}
|
|
21350
21455
|
for (const dir of walkDirs(ARCHIVE)) {
|
|
21351
|
-
const id = idPrefix(
|
|
21352
|
-
const root =
|
|
21456
|
+
const id = idPrefix(path65.basename(dir));
|
|
21457
|
+
const root = path65.join(dir, "root.md");
|
|
21353
21458
|
if (id !== null && fs60.existsSync(root)) files2.set(id, root);
|
|
21354
21459
|
}
|
|
21355
21460
|
return files2;
|
|
@@ -21462,13 +21567,13 @@ var init_list = __esm({
|
|
|
21462
21567
|
|
|
21463
21568
|
// ../cli/src/commands/misc.ts
|
|
21464
21569
|
import fs61 from "node:fs";
|
|
21465
|
-
import
|
|
21570
|
+
import path66 from "node:path";
|
|
21466
21571
|
function extractOldMeta(text5, file) {
|
|
21467
21572
|
const grab = (re) => {
|
|
21468
21573
|
const m = text5.match(re);
|
|
21469
21574
|
return m ? m[1].trim() : null;
|
|
21470
21575
|
};
|
|
21471
|
-
const title = grab(/^#\s+(.+)$/m) || slugify(
|
|
21576
|
+
const title = grab(/^#\s+(.+)$/m) || slugify(path66.basename(file, ".md").replace(/^\d+-/, "")).replace(/-/g, " ");
|
|
21472
21577
|
const norm = (raw) => {
|
|
21473
21578
|
const v = (raw || "").toLowerCase();
|
|
21474
21579
|
return LEVELS.includes(v) ? v : "med";
|
|
@@ -21495,7 +21600,7 @@ function stripOldHeader(text5) {
|
|
|
21495
21600
|
}
|
|
21496
21601
|
function cmdMigrate(opts) {
|
|
21497
21602
|
const dry = opts.dryRun === true;
|
|
21498
|
-
const files2 = walkMd(TODO).filter((f) =>
|
|
21603
|
+
const files2 = walkMd(TODO).filter((f) => path66.basename(f) !== "README.md");
|
|
21499
21604
|
let changed = 0;
|
|
21500
21605
|
let skipped = 0;
|
|
21501
21606
|
for (const file of files2) {
|
|
@@ -21546,11 +21651,11 @@ var init_group_close = __esm({
|
|
|
21546
21651
|
|
|
21547
21652
|
// ../cli/src/commands/remove.ts
|
|
21548
21653
|
import fs63 from "node:fs";
|
|
21549
|
-
import
|
|
21654
|
+
import path67 from "node:path";
|
|
21550
21655
|
function leavingIds(id, found2) {
|
|
21551
21656
|
const ids = /* @__PURE__ */ new Set([id]);
|
|
21552
21657
|
if (found2.kind === "group") {
|
|
21553
|
-
const names4 = [...walkMd(found2.target), ...walkDirs(found2.target)].map((f) =>
|
|
21658
|
+
const names4 = [...walkMd(found2.target), ...walkDirs(found2.target)].map((f) => path67.basename(f));
|
|
21554
21659
|
for (const name of names4) {
|
|
21555
21660
|
const n = idPrefix(name);
|
|
21556
21661
|
if (n !== null) ids.add(n);
|
|
@@ -21564,7 +21669,7 @@ function findMentions(ids) {
|
|
|
21564
21669
|
for (const dir of [TODO, MEMORY]) {
|
|
21565
21670
|
if (!fs63.existsSync(dir)) continue;
|
|
21566
21671
|
for (const file of walkMd(dir)) {
|
|
21567
|
-
if (
|
|
21672
|
+
if (path67.basename(file) === "README.md") continue;
|
|
21568
21673
|
const lines = fs63.readFileSync(file, "utf8").split("\n");
|
|
21569
21674
|
const fmEnd = frontmatterEnd(lines);
|
|
21570
21675
|
lines.forEach((line, i) => {
|
|
@@ -21581,12 +21686,12 @@ function findMentions(ids) {
|
|
|
21581
21686
|
return hits;
|
|
21582
21687
|
}
|
|
21583
21688
|
function leavingCards(id, found2) {
|
|
21584
|
-
const own = found2.kind === "group" ?
|
|
21689
|
+
const own = found2.kind === "group" ? path67.join(found2.target, "root.md") : found2.target;
|
|
21585
21690
|
const cards = [{ id, file: own }];
|
|
21586
21691
|
if (found2.kind !== "group") return cards;
|
|
21587
21692
|
for (const file of walkMd(found2.target)) {
|
|
21588
21693
|
if (file === own) continue;
|
|
21589
|
-
const n = idPrefix(
|
|
21694
|
+
const n = idPrefix(path67.basename(file));
|
|
21590
21695
|
if (n !== null) cards.push({ id: n, file });
|
|
21591
21696
|
}
|
|
21592
21697
|
return cards;
|
|
@@ -21614,7 +21719,7 @@ function unfinishedDelivery(id) {
|
|
|
21614
21719
|
}[gap];
|
|
21615
21720
|
return `#${id} is not finished: ${what}.`;
|
|
21616
21721
|
}
|
|
21617
|
-
const missing = planDeliverables(card.body).filter((src) => src.startsWith(".assets/")).find((src) => !fs63.existsSync(
|
|
21722
|
+
const missing = planDeliverables(card.body).filter((src) => src.startsWith(".assets/")).find((src) => !fs63.existsSync(path67.join(ASSETS, src.slice(".assets/".length))));
|
|
21618
21723
|
return missing ? `#${id} is not finished: ${missing} is not on this machine.` : null;
|
|
21619
21724
|
}
|
|
21620
21725
|
function agentDecisions(text5) {
|
|
@@ -21641,7 +21746,7 @@ function removeCard2(id, metric, options) {
|
|
|
21641
21746
|
const unfinished = metric === "completed" ? unfinishedDelivery(id) : null;
|
|
21642
21747
|
if (unfinished) die(unfinished, { kind: "plan-delivery-unfinished" });
|
|
21643
21748
|
const dest = options.cleanupDiscarded && locateArchived(id) ? null : archiveDest(found2);
|
|
21644
|
-
const cardFile3 = found2.kind === "group" ?
|
|
21749
|
+
const cardFile3 = found2.kind === "group" ? path67.join(found2.target, "root.md") : found2.target;
|
|
21645
21750
|
const cardText = fs63.existsSync(cardFile3) ? fs63.readFileSync(cardFile3, "utf8") : "";
|
|
21646
21751
|
const { meta: cardMeta3 } = parseFrontmatter(cardText);
|
|
21647
21752
|
const alsoRemoved = found2.kind === "group" ? walkMd(found2.target).filter((f) => f !== cardFile3).map((f) => rel(f)).sort() : [];
|
|
@@ -21725,7 +21830,7 @@ function removeCard2(id, metric, options) {
|
|
|
21725
21830
|
};
|
|
21726
21831
|
}
|
|
21727
21832
|
function closeGroup(rootFile) {
|
|
21728
|
-
const rootId = idPrefix(
|
|
21833
|
+
const rootId = idPrefix(path67.basename(path67.dirname(rootFile)));
|
|
21729
21834
|
if (rootId === null) return null;
|
|
21730
21835
|
if (runnerProject(KANBAN)) return null;
|
|
21731
21836
|
if (!groupCloses(rootFile)) return null;
|
|
@@ -21789,7 +21894,7 @@ function cleanupDiscardedCards(sessionId) {
|
|
|
21789
21894
|
if (card.pending) throw new Error(`Discarding #${card.id} did not finish. Retry the discard.`);
|
|
21790
21895
|
while (locate(card.id)) cmdRemove(card.id, "rejected", { discard: true, cleanupDiscarded: true });
|
|
21791
21896
|
dropCrossRefs(card.id);
|
|
21792
|
-
stripReadmeRefs({ kind: "file", rel:
|
|
21897
|
+
stripReadmeRefs({ kind: "file", rel: path67.relative(TODO, path67.resolve(REPO_ROOT, card.path)).split(path67.sep).join("/") });
|
|
21793
21898
|
}
|
|
21794
21899
|
return discarded.map((c) => c.id);
|
|
21795
21900
|
});
|
|
@@ -21876,7 +21981,7 @@ var init_memory_file = __esm({
|
|
|
21876
21981
|
|
|
21877
21982
|
// ../cli/src/commands/rule.ts
|
|
21878
21983
|
import fs64 from "node:fs";
|
|
21879
|
-
import
|
|
21984
|
+
import path68 from "node:path";
|
|
21880
21985
|
function cmdRule(askedName, flags) {
|
|
21881
21986
|
const agent = askedName.trim();
|
|
21882
21987
|
if (!agentRoster().some((a) => a.name === agent)) {
|
|
@@ -21885,7 +21990,7 @@ function cmdRule(askedName, flags) {
|
|
|
21885
21990
|
const rule = readRuleText(flags.file, flags.text);
|
|
21886
21991
|
const res = setAgentRule(agent, rule);
|
|
21887
21992
|
if (!res.ok) die(res.error);
|
|
21888
|
-
const file = rel(
|
|
21993
|
+
const file = rel(path68.join(RULES, `${agent}.md`));
|
|
21889
21994
|
say(rule ? `wrote the \`${agent}\` rule (${file})` : `cleared the \`${agent}\` rule`);
|
|
21890
21995
|
return { agent, file, cleared: !rule };
|
|
21891
21996
|
}
|
|
@@ -22021,13 +22126,13 @@ var init_storyboard = __esm({
|
|
|
22021
22126
|
|
|
22022
22127
|
// ../cli/src/lib/spec-contract.ts
|
|
22023
22128
|
import fs65 from "node:fs";
|
|
22024
|
-
import
|
|
22129
|
+
import path69 from "node:path";
|
|
22025
22130
|
function snapshotSpecs() {
|
|
22026
22131
|
const out = /* @__PURE__ */ new Map();
|
|
22027
22132
|
if (!fs65.existsSync(TODO)) return out;
|
|
22028
22133
|
for (const file of walkMd(TODO)) {
|
|
22029
|
-
if (isLegacyRecurring(
|
|
22030
|
-
const id = idPrefix(
|
|
22134
|
+
if (isLegacyRecurring(path69.relative(TODO, file))) continue;
|
|
22135
|
+
const id = idPrefix(path69.basename(file) === "root.md" ? path69.basename(path69.dirname(file)) : path69.basename(file));
|
|
22031
22136
|
if (id !== null) out.set(file, { id, text: fs65.readFileSync(file, "utf8") });
|
|
22032
22137
|
}
|
|
22033
22138
|
return out;
|
|
@@ -22035,7 +22140,7 @@ function snapshotSpecs() {
|
|
|
22035
22140
|
function enclosingGroups(file) {
|
|
22036
22141
|
const ids = [];
|
|
22037
22142
|
for (let root = enclosingGroupRoot(file); root; root = enclosingGroupRoot(root)) {
|
|
22038
|
-
const id = idPrefix(
|
|
22143
|
+
const id = idPrefix(path69.basename(path69.dirname(root)));
|
|
22039
22144
|
if (id !== null) ids.push(id);
|
|
22040
22145
|
}
|
|
22041
22146
|
return ids;
|
|
@@ -22205,8 +22310,8 @@ function validateSpec(file, text5, id) {
|
|
|
22205
22310
|
function inside(dir, name) {
|
|
22206
22311
|
try {
|
|
22207
22312
|
const root = fs65.realpathSync(dir);
|
|
22208
|
-
const real2 = fs65.realpathSync(
|
|
22209
|
-
return real2.startsWith(root +
|
|
22313
|
+
const real2 = fs65.realpathSync(path69.join(dir, name));
|
|
22314
|
+
return real2.startsWith(root + path69.sep) && fs65.statSync(real2).isFile() ? real2 : null;
|
|
22210
22315
|
} catch {
|
|
22211
22316
|
return null;
|
|
22212
22317
|
}
|
|
@@ -22224,7 +22329,7 @@ function variantsProblem(raw, src, id) {
|
|
|
22224
22329
|
if (files2.length < 2 || !files2.every((f) => typeof f === "string" && f)) return `variants needs at least two labels, each naming a file. ${shape}`;
|
|
22225
22330
|
if (!files2.includes(src)) return `src="${src}" is not one of the variants. Set src to the version shown first.`;
|
|
22226
22331
|
if (id === void 0) return null;
|
|
22227
|
-
const dir =
|
|
22332
|
+
const dir = path69.join(ASSETS, String(id));
|
|
22228
22333
|
for (const f of files2) {
|
|
22229
22334
|
const parts = f.split("/");
|
|
22230
22335
|
if (parts.length !== 3 || parts[0] !== ".assets" || parts[1] !== String(id) || !inside(dir, parts[2])) {
|
|
@@ -22234,7 +22339,7 @@ function variantsProblem(raw, src, id) {
|
|
|
22234
22339
|
return null;
|
|
22235
22340
|
}
|
|
22236
22341
|
function compareProblem(attrs, id) {
|
|
22237
|
-
const dir =
|
|
22342
|
+
const dir = path69.join(ASSETS, String(id));
|
|
22238
22343
|
const src = (message) => ({ rule: "compare-src", message });
|
|
22239
22344
|
const shots = (message) => ({ rule: "compare-shots", message });
|
|
22240
22345
|
for (const side of ["a", "b"]) {
|
|
@@ -22274,7 +22379,7 @@ function compareProblem(attrs, id) {
|
|
|
22274
22379
|
}
|
|
22275
22380
|
function validateStoryboards(file, id, text5) {
|
|
22276
22381
|
const errors = [];
|
|
22277
|
-
const dir =
|
|
22382
|
+
const dir = path69.join(ASSETS, String(id));
|
|
22278
22383
|
for (const marker of storyboardMarkers(text5)) {
|
|
22279
22384
|
const named2 = assetName(marker.src, id, ["json"]);
|
|
22280
22385
|
const add = (message) => errors.push({ file: rel(file), line: marker.line, rule: "storyboard-src", message });
|
|
@@ -22424,7 +22529,7 @@ var init_mockups = __esm({
|
|
|
22424
22529
|
|
|
22425
22530
|
// ../cli/src/commands/spec-write.ts
|
|
22426
22531
|
import fs66 from "node:fs";
|
|
22427
|
-
import
|
|
22532
|
+
import path70 from "node:path";
|
|
22428
22533
|
function cmdSpecWrite(id, askedName, flags) {
|
|
22429
22534
|
const agent = findSpecAgent(askedName);
|
|
22430
22535
|
if (!agent) die(notAnAgent(askedName), { kind: "no-such-spec-agent", specAgent: askedName });
|
|
@@ -22432,7 +22537,7 @@ function cmdSpecWrite(id, askedName, flags) {
|
|
|
22432
22537
|
const section = readSection(flags.file, flags.text);
|
|
22433
22538
|
const found2 = locate(id);
|
|
22434
22539
|
if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
|
|
22435
|
-
const file = found2.kind === "group" ?
|
|
22540
|
+
const file = found2.kind === "group" ? path70.join(found2.target, "root.md") : found2.target;
|
|
22436
22541
|
const { meta, body } = parseFrontmatter(fs66.readFileSync(file, "utf8"));
|
|
22437
22542
|
if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
|
|
22438
22543
|
const { body: next, replaced } = splice(body, name, section);
|
|
@@ -23023,7 +23128,7 @@ __export(board_exports, {
|
|
|
23023
23128
|
});
|
|
23024
23129
|
import crypto4 from "node:crypto";
|
|
23025
23130
|
import fs68 from "node:fs";
|
|
23026
|
-
import
|
|
23131
|
+
import path71 from "node:path";
|
|
23027
23132
|
async function dispatch(move2, cmd, args, opts, cli) {
|
|
23028
23133
|
const ctx = ctxOf(cmd, cli.program);
|
|
23029
23134
|
const data = await runAction(ctx, {}, async () => {
|
|
@@ -23092,7 +23197,7 @@ async function cloudCard(task, opts) {
|
|
|
23092
23197
|
const file = creationReceipt(`${checked.value}:${requestId}`, slot);
|
|
23093
23198
|
const journal = readCreation(file, said3);
|
|
23094
23199
|
if (!journal.made) {
|
|
23095
|
-
const key = `akb-${
|
|
23200
|
+
const key = `akb-${path71.basename(file, ".json").slice(0, 40)}`;
|
|
23096
23201
|
const made = await createProjectTask(checked.value, journal.said, key);
|
|
23097
23202
|
if (!made.ok) throw new BoardError(creationError(`Cloud did not make the task: ${made.error}`, triage), { kind: "refused" });
|
|
23098
23203
|
journal.made = made.value;
|
|
@@ -23238,15 +23343,15 @@ var init_board2 = __esm({
|
|
|
23238
23343
|
|
|
23239
23344
|
// ../cli/src/lib/board-cli.ts
|
|
23240
23345
|
import fs69 from "node:fs";
|
|
23241
|
-
import
|
|
23346
|
+
import path72 from "node:path";
|
|
23242
23347
|
function useBoard(found2, dirNamed) {
|
|
23243
|
-
if (!found2.named && found2.board ===
|
|
23348
|
+
if (!found2.named && found2.board === path72.join(found2.root, "docs", "kanban")) setBoardRoot(found2.root, dirNamed);
|
|
23244
23349
|
else setBoardDir(found2.board, found2.root);
|
|
23245
23350
|
useProjectState();
|
|
23246
23351
|
}
|
|
23247
23352
|
function whole(found2, move2) {
|
|
23248
23353
|
if (move2 === MAKES_A_BOARD) return found2;
|
|
23249
|
-
if (!fs69.existsSync(
|
|
23354
|
+
if (!fs69.existsSync(path72.join(found2.board, "todo"))) {
|
|
23250
23355
|
die(`the board in ${found2.board} has no todo/ \u2014 run \`init\` to add what is missing.`, {
|
|
23251
23356
|
kind: "board-incomplete",
|
|
23252
23357
|
dir: found2.root
|
|
@@ -23255,13 +23360,13 @@ function whole(found2, move2) {
|
|
|
23255
23360
|
return found2;
|
|
23256
23361
|
}
|
|
23257
23362
|
function findBoardUpward(from, move2) {
|
|
23258
|
-
let dir =
|
|
23363
|
+
let dir = path72.resolve(from);
|
|
23259
23364
|
for (; ; ) {
|
|
23260
|
-
if (hasBoard(dir)) return { root: dir, board:
|
|
23365
|
+
if (hasBoard(dir)) return { root: dir, board: path72.join(dir, "docs", "kanban") };
|
|
23261
23366
|
if (isBoardDir(dir)) return { root: projectRootOf(dir), board: dir };
|
|
23262
|
-
const kept2 =
|
|
23263
|
-
if (move2 !== MAKES_A_BOARD && fs69.existsSync(
|
|
23264
|
-
const up =
|
|
23367
|
+
const kept2 = path72.join(dir, ".akb", "kanban");
|
|
23368
|
+
if (move2 !== MAKES_A_BOARD && fs69.existsSync(path72.join(kept2, "todo"))) return { root: dir, board: kept2 };
|
|
23369
|
+
const up = path72.dirname(dir);
|
|
23265
23370
|
if (up === dir) return null;
|
|
23266
23371
|
dir = up;
|
|
23267
23372
|
}
|
|
@@ -23269,7 +23374,7 @@ function findBoardUpward(from, move2) {
|
|
|
23269
23374
|
function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
|
|
23270
23375
|
const named2 = board2 ?? boardFromEnv();
|
|
23271
23376
|
if (named2) {
|
|
23272
|
-
const folder =
|
|
23377
|
+
const folder = path72.resolve(cwd, named2);
|
|
23273
23378
|
if (!fs69.existsSync(folder) && move2 !== MAKES_A_BOARD) {
|
|
23274
23379
|
die(`no such folder: ${folder}`, { kind: "no-such-folder", dir: folder });
|
|
23275
23380
|
}
|
|
@@ -23282,24 +23387,24 @@ function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
|
|
|
23282
23387
|
return { root: projectRootOf(folder), board: folder, named: true };
|
|
23283
23388
|
}
|
|
23284
23389
|
if (dir !== null) {
|
|
23285
|
-
const root =
|
|
23390
|
+
const root = path72.resolve(dir);
|
|
23286
23391
|
if (!fs69.existsSync(root)) die(`no such folder: ${root}`, { kind: "no-such-folder", dir: root });
|
|
23287
|
-
const kept2 =
|
|
23288
|
-
if (!hasBoard(root) && move2 !== MAKES_A_BOARD && fs69.existsSync(
|
|
23392
|
+
const kept2 = path72.join(root, ".akb", "kanban");
|
|
23393
|
+
if (!hasBoard(root) && move2 !== MAKES_A_BOARD && fs69.existsSync(path72.join(kept2, "todo"))) return whole({ root, board: kept2 }, move2);
|
|
23289
23394
|
if (!hasBoard(root) && move2 !== MAKES_A_BOARD) {
|
|
23290
23395
|
die(`no board in ${root} \u2014 it has no docs/kanban/. ${installHint}\u3002`, {
|
|
23291
23396
|
kind: "no-board",
|
|
23292
23397
|
dir: root
|
|
23293
23398
|
});
|
|
23294
23399
|
}
|
|
23295
|
-
return whole({ root, board:
|
|
23400
|
+
return whole({ root, board: path72.join(root, "docs", "kanban") }, move2);
|
|
23296
23401
|
}
|
|
23297
23402
|
const found2 = findBoardUpward(cwd, move2);
|
|
23298
23403
|
if (found2) return whole(found2, move2);
|
|
23299
|
-
if (move2 === MAKES_A_BOARD) return { root:
|
|
23404
|
+
if (move2 === MAKES_A_BOARD) return { root: path72.resolve(cwd), board: path72.join(path72.resolve(cwd), "docs", "kanban") };
|
|
23300
23405
|
die(
|
|
23301
|
-
`no board here. Looked in ${
|
|
23302
|
-
{ kind: "no-board", dir:
|
|
23406
|
+
`no board here. Looked in ${path72.resolve(cwd)} and every folder above it for docs/kanban/. ${installHint}\uFF0C\u6216\u7528 --board \u6307\u5B9A\u5DF2\u6709\u770B\u677F\u3002`,
|
|
23407
|
+
{ kind: "no-board", dir: path72.resolve(cwd) }
|
|
23303
23408
|
);
|
|
23304
23409
|
}
|
|
23305
23410
|
function editDistance(a, b) {
|
|
@@ -23381,8 +23486,8 @@ var init_board_cli = __esm({
|
|
|
23381
23486
|
init_io();
|
|
23382
23487
|
init_pointer();
|
|
23383
23488
|
MAKES_A_BOARD = "init";
|
|
23384
|
-
hasBoard = (dir) => fs69.existsSync(
|
|
23385
|
-
isBoardDir = (dir) => fs69.existsSync(
|
|
23489
|
+
hasBoard = (dir) => fs69.existsSync(path72.join(dir, "docs", "kanban"));
|
|
23490
|
+
isBoardDir = (dir) => fs69.existsSync(path72.join(dir, "todo")) && fs69.existsSync(path72.join(dir, "config.md"));
|
|
23386
23491
|
boardFromEnv = () => {
|
|
23387
23492
|
const named2 = process.env.AI4KANBAN_BOARD?.trim();
|
|
23388
23493
|
return named2 ? named2 : null;
|
|
@@ -23418,7 +23523,7 @@ var init_board3 = __esm({
|
|
|
23418
23523
|
var card_chat_default;
|
|
23419
23524
|
var init_card_chat = __esm({
|
|
23420
23525
|
"../cli/src/guide/card-chat.md"() {
|
|
23421
|
-
card_chat_default = "# Answer a card's conversation\n\n- **Plain words**: answer directly
|
|
23526
|
+
card_chat_default = "# Answer a card's conversation\n\n- **Plain words**: answer directly using everyday words.\n- **Use judgment**: use context to decide when to answer, revise the card or record an answer, and handle all intents in the message without reconfirming clear decisions.\n- **Apply changes**: follow `akb card revise <id> --print \"<the change>\"` for revisions and `akb card resolve <id> --print` for answers to open questions, then validate the card; reuse flows already in context.\n- **Stay on this card**: revise this card's requirements or outcome; suggest a new task for independent work, and change no other card.\n- **Build from review**: edit drafts and previews here, never project code; when asked to build, direct the user to approve on the review page or answer remaining open questions first.\n- **Questions**: follow `akb guide update-questions` for decisions that need the user.\n- **Report results**: briefly state actual changes or failures, identifying recorded answers; pure answers need no change report.\n- **Write no memory**: neither this conversation nor any flow it starts may write memory, regardless of that flow's own instructions.\n- **Hide machinery**: name actions by their UI labels and keep commands and internal instructions out of replies.\n";
|
|
23422
23527
|
}
|
|
23423
23528
|
});
|
|
23424
23529
|
|
|
@@ -23442,7 +23547,7 @@ var init_describe_project = __esm({
|
|
|
23442
23547
|
var discuss_goal_default;
|
|
23443
23548
|
var init_discuss_goal = __esm({
|
|
23444
23549
|
"../cli/src/guide/discuss-goal.md"() {
|
|
23445
|
-
discuss_goal_default = "# \
|
|
23550
|
+
discuss_goal_default = "# Discuss a goal\n\nTalk with the user as you would without extra context; any work before actual building is allowed.\n\n- **Build boundary**: when actual building is needed, save or update the goal with `akb raw goal` (see `akb raw goal --help`), then stop; an existing build continues in its current session under the project's build requirements.\n- **Missing information**: ask the user for information needed to form a goal.\n\n## Goals\n\n- **Goal format**: each goal has a title, an outcome of at most 150 words, and a `## Done when` section listing verifiable acceptance criteria, without technical design.\n- **Independent deliverables**: save separate deliverables as separate goals; remove a replaced goal with `akb raw goal drop --id <goal>`.\n- **Use judgment**: settle outcome decisions you can and explain them; ask only for decisions the user must make that change the result, with a recommendation.\n- **Plain words**: answer directly using everyday words.\n- **Hide machinery**: omit plans, files, paths, commands, tools, and connectors from replies; refer to actions by their button labels.\n";
|
|
23446
23551
|
}
|
|
23447
23552
|
});
|
|
23448
23553
|
|
|
@@ -23650,7 +23755,7 @@ var init_guide = __esm({
|
|
|
23650
23755
|
|
|
23651
23756
|
// ../cli/src/lib/agent/approve.ts
|
|
23652
23757
|
import fs75 from "node:fs";
|
|
23653
|
-
import
|
|
23758
|
+
import path78 from "node:path";
|
|
23654
23759
|
function approvalRequest(req) {
|
|
23655
23760
|
const id = req.id;
|
|
23656
23761
|
const card = id === void 0 ? null : findCard(id);
|
|
@@ -23668,7 +23773,7 @@ function advancePhase(id) {
|
|
|
23668
23773
|
const card = findCard(id);
|
|
23669
23774
|
const found2 = locate(id);
|
|
23670
23775
|
if (!card || !found2 || !awaitingApproval(card)) return { error: `#${id} is no longer waiting on approval.` };
|
|
23671
|
-
const file = found2.kind === "group" ?
|
|
23776
|
+
const file = found2.kind === "group" ? path78.join(found2.target, "root.md") : found2.target;
|
|
23672
23777
|
const { meta, body } = parseFrontmatter(fs75.readFileSync(file, "utf8"));
|
|
23673
23778
|
if (!meta) return { error: `#${id} has no frontmatter` };
|
|
23674
23779
|
meta.phase = card.stage + 1;
|
|
@@ -23699,6 +23804,12 @@ async function startRun(req) {
|
|
|
23699
23804
|
const cardId2 = Number.isInteger(req.id) ? req.id : null;
|
|
23700
23805
|
const short2 = workflowRefusal(req) ?? await scheduledRefusal(req);
|
|
23701
23806
|
if (short2) return short2;
|
|
23807
|
+
const delivery = req.action === "implement" ? deliveryFor(req) : void 0;
|
|
23808
|
+
if (delivery?.landing) return refusal("deliveryHeld", "\u6784\u5EFA\u5DF2\u5B8C\u6210\uFF0C\u7B49\u5F85\u4EA4\u4ED8\u843D\u5730\uFF0C\u4E0D\u4F1A\u91CD\u65B0\u6784\u5EFA\u3002");
|
|
23809
|
+
if (delivery && req.session !== "new") {
|
|
23810
|
+
const previous = readRuns().filter((r) => r.deliveryId === delivery.deliveryId && r.action === "implement").reverse().sort((a, b) => b.startedAt - a.startedAt)[0];
|
|
23811
|
+
if (previous) return startResume(previous.sessionId, { deliveryId: delivery.deliveryId, notes: req.notes });
|
|
23812
|
+
}
|
|
23702
23813
|
const moved = req.action === "approve" ? advancePhase(cardId2) : null;
|
|
23703
23814
|
if (moved) return moved;
|
|
23704
23815
|
return open2(req, sessionId);
|
|
@@ -23771,8 +23882,8 @@ function open2(req, sessionId) {
|
|
|
23771
23882
|
markSpawned(run2.sessionId, pid);
|
|
23772
23883
|
return { run: run2, spawned: pid !== void 0 };
|
|
23773
23884
|
}
|
|
23774
|
-
async function startResume(id,
|
|
23775
|
-
const opened = await openResume(id,
|
|
23885
|
+
async function startResume(id, options) {
|
|
23886
|
+
const opened = await openResume(id, options);
|
|
23776
23887
|
if ("error" in opened) return opened;
|
|
23777
23888
|
const pid = spawnWatcher(opened.run.sessionId);
|
|
23778
23889
|
markSpawned(opened.run.sessionId, pid);
|
|
@@ -23787,6 +23898,7 @@ var init_start = __esm({
|
|
|
23787
23898
|
init_launch();
|
|
23788
23899
|
init_pictures();
|
|
23789
23900
|
init_deliveries();
|
|
23901
|
+
init_store();
|
|
23790
23902
|
init_prompts();
|
|
23791
23903
|
init_workflows();
|
|
23792
23904
|
init_sessions();
|
|
@@ -23798,10 +23910,10 @@ var init_start = __esm({
|
|
|
23798
23910
|
|
|
23799
23911
|
// ../cli/src/lib/agent/task-chats.ts
|
|
23800
23912
|
import fs76 from "node:fs";
|
|
23801
|
-
import
|
|
23913
|
+
import path79 from "node:path";
|
|
23802
23914
|
function taskChatFile(task, home3) {
|
|
23803
23915
|
if (!TASK.test(task)) throw new Error(`${task} is not a task id.`);
|
|
23804
|
-
return
|
|
23916
|
+
return path79.join(taskChatsDir(home3), `${task}.jsonl`);
|
|
23805
23917
|
}
|
|
23806
23918
|
function readTaskChat(task, home3) {
|
|
23807
23919
|
let text5;
|
|
@@ -23827,13 +23939,13 @@ var init_task_chats = __esm({
|
|
|
23827
23939
|
"use strict";
|
|
23828
23940
|
init_home();
|
|
23829
23941
|
TASK = /^[\w-]{1,100}$/;
|
|
23830
|
-
taskChatsDir = (home3 = machineHome()) =>
|
|
23942
|
+
taskChatsDir = (home3 = machineHome()) => path79.join(home3, "chats");
|
|
23831
23943
|
}
|
|
23832
23944
|
});
|
|
23833
23945
|
|
|
23834
23946
|
// ../cli/src/lib/agent/memory-review.ts
|
|
23835
23947
|
import fs77 from "node:fs";
|
|
23836
|
-
import
|
|
23948
|
+
import path80 from "node:path";
|
|
23837
23949
|
function taskChatsToReview(tasks) {
|
|
23838
23950
|
const out = [];
|
|
23839
23951
|
for (const ref of [...new Set(tasks)]) {
|
|
@@ -23855,7 +23967,7 @@ function readArchived2(id) {
|
|
|
23855
23967
|
const found2 = Number.isInteger(id) && id > 0 ? locateArchived(id) : null;
|
|
23856
23968
|
if (!found2) return null;
|
|
23857
23969
|
try {
|
|
23858
|
-
const file = found2.kind === "group" ?
|
|
23970
|
+
const file = found2.kind === "group" ? path80.join(found2.target, "root.md") : found2.target;
|
|
23859
23971
|
const { meta, body } = parseFrontmatter(fs77.readFileSync(file, "utf8"));
|
|
23860
23972
|
if (!meta) return null;
|
|
23861
23973
|
return { id, title: meta.title, modules: meta.modules ?? [], rejected: meta.rejected, body };
|
|
@@ -23926,9 +24038,9 @@ var init_memory_review = __esm({
|
|
|
23926
24038
|
|
|
23927
24039
|
// ../cli/src/lib/agent/dismissal-review.ts
|
|
23928
24040
|
import fs78 from "node:fs";
|
|
23929
|
-
import
|
|
24041
|
+
import path81 from "node:path";
|
|
23930
24042
|
function rejectionsToReview() {
|
|
23931
|
-
const dir =
|
|
24043
|
+
const dir = path81.join(ARCHIVE, REJECTIONS_TO_REVIEW);
|
|
23932
24044
|
if (!fs78.existsSync(dir)) return [];
|
|
23933
24045
|
const found2 = [];
|
|
23934
24046
|
for (const file of walkMd(dir)) {
|
|
@@ -23991,7 +24103,7 @@ var init_dismissal_review = __esm({
|
|
|
23991
24103
|
|
|
23992
24104
|
// ../cli/src/lib/agent/flow.ts
|
|
23993
24105
|
import fs79 from "node:fs";
|
|
23994
|
-
import
|
|
24106
|
+
import path82 from "node:path";
|
|
23995
24107
|
function readCard2(id, home3 = "board") {
|
|
23996
24108
|
const found2 = home3 === "archive" ? locateArchived(id) : locate(id);
|
|
23997
24109
|
if (!found2) {
|
|
@@ -24000,7 +24112,7 @@ function readCard2(id, home3 = "board") {
|
|
|
24000
24112
|
{ kind: "card-not-found", id }
|
|
24001
24113
|
);
|
|
24002
24114
|
}
|
|
24003
|
-
const file = found2.kind === "group" ?
|
|
24115
|
+
const file = found2.kind === "group" ? path82.join(found2.target, "root.md") : found2.target;
|
|
24004
24116
|
let text5;
|
|
24005
24117
|
try {
|
|
24006
24118
|
text5 = fs79.readFileSync(file, "utf8");
|
|
@@ -24127,7 +24239,7 @@ function workspaceField(delivery) {
|
|
|
24127
24239
|
if (!delivery?.worktree) return [];
|
|
24128
24240
|
return field("workspace", [
|
|
24129
24241
|
`write code in ${delivery.worktree} \u2014 this delivery's own worktree, on branch ${delivery.branch}.`,
|
|
24130
|
-
|
|
24242
|
+
path82.resolve(process.cwd()).startsWith(worktreeDir(delivery.worktree)) ? `it is your working folder already; the board's own files are NOT in it and never go on that branch.` : `cd into it first \u2014 you are in the project checkout, and code written here is not part of this build. The board's own files are NOT in it and never go on that branch.`,
|
|
24131
24243
|
`every board command names the project's own copy: \`${boardCommandFor()} <command>\`.`,
|
|
24132
24244
|
`${rel(REPO_ROOT)} is the project \u2014 the card, the memory files and the docs are changed there, not here.`
|
|
24133
24245
|
]);
|
|
@@ -24499,11 +24611,6 @@ function printFlow(rawReq, program2 = "akb") {
|
|
|
24499
24611
|
say("");
|
|
24500
24612
|
say(rule);
|
|
24501
24613
|
}
|
|
24502
|
-
const language = languageNote();
|
|
24503
|
-
if (language) {
|
|
24504
|
-
say("");
|
|
24505
|
-
say(language);
|
|
24506
|
-
}
|
|
24507
24614
|
return {
|
|
24508
24615
|
mode: "print",
|
|
24509
24616
|
action: req.action,
|
|
@@ -24591,7 +24698,7 @@ var init_flow = __esm({
|
|
|
24591
24698
|
});
|
|
24592
24699
|
|
|
24593
24700
|
// ../cli/src/lib/agent/refine.ts
|
|
24594
|
-
import { createHash as
|
|
24701
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
24595
24702
|
function markBoard() {
|
|
24596
24703
|
try {
|
|
24597
24704
|
return new Map(allCards().map((card) => [card.id, wroteOf(card)]));
|
|
@@ -24728,7 +24835,7 @@ var init_refine2 = __esm({
|
|
|
24728
24835
|
};
|
|
24729
24836
|
MARKER2 = /^<!--\s*agent\s*-->$/;
|
|
24730
24837
|
asMoved = (body) => body.split("\n").map((line) => line.trimEnd()).filter((line) => line.trim() && !MARKER2.test(line.trim())).sort();
|
|
24731
|
-
wroteOf = (card) =>
|
|
24838
|
+
wroteOf = (card) => createHash6("sha1").update(
|
|
24732
24839
|
JSON.stringify([
|
|
24733
24840
|
card.status,
|
|
24734
24841
|
card.relPath,
|
|
@@ -24932,7 +25039,7 @@ async function watchRun(sessionId, resume = startResume) {
|
|
|
24932
25039
|
if (active2.startNote) log.write(`[board] ${active2.startNote}
|
|
24933
25040
|
`);
|
|
24934
25041
|
const basePrompt = record.formatRepair ? contractRepairPrompt(requestOf(record), record.formatRepair.errors) : record.resumedFrom ? spec.prompt || resumePrompt(record.deliveryId, record.cardId, record.action) : spec.prompt;
|
|
24935
|
-
const prompt =
|
|
25042
|
+
const prompt = [basePrompt, discardedCardsPrompt(record.discardedCards)].filter(Boolean).join("\n\n");
|
|
24936
25043
|
const before = markBoard();
|
|
24937
25044
|
const sources = snapshotSpecs();
|
|
24938
25045
|
const wasBroken = new Set(boardComplaints());
|
|
@@ -24944,8 +25051,8 @@ async function watchRun(sessionId, resume = startResume) {
|
|
|
24944
25051
|
const silenceMs = silenceFor * 6e4;
|
|
24945
25052
|
void refreshCatalog();
|
|
24946
25053
|
const client = active2.client;
|
|
24947
|
-
const restartBase = !client || record.formatRepair ? void 0 : record.resumedFrom ? restartPrompt(requestOf(record), record.deliveryId) : record.continues && !record.continues.fork ? spec.prompt : void 0;
|
|
24948
|
-
const restart = restartBase ?
|
|
25054
|
+
const restartBase = !client || record.formatRepair || record.action === "implement" && record.resumedFrom ? void 0 : record.resumedFrom ? restartPrompt(requestOf(record), record.deliveryId) : record.continues && !record.continues.fork ? spec.prompt : void 0;
|
|
25055
|
+
const restart = restartBase ? [restartBase, discardedCardsPrompt(record.discardedCards)].filter(Boolean).join("\n\n") : void 0;
|
|
24949
25056
|
const stdinPrompt = client ? void 0 : active2.stdinPrompt;
|
|
24950
25057
|
const stdio = [client || stdinPrompt ? "pipe" : "ignore", "pipe", "pipe"];
|
|
24951
25058
|
let child;
|
|
@@ -25480,7 +25587,6 @@ var init_watch = __esm({
|
|
|
25480
25587
|
init_creation();
|
|
25481
25588
|
init_wire();
|
|
25482
25589
|
init_prompts();
|
|
25483
|
-
init_language();
|
|
25484
25590
|
init_resolve();
|
|
25485
25591
|
init_runner();
|
|
25486
25592
|
init_retry();
|
|
@@ -25990,7 +26096,7 @@ async function testAgent(args) {
|
|
|
25990
26096
|
|
|
25991
26097
|
// ../cli/src/commands/cloud.ts
|
|
25992
26098
|
import fs71 from "node:fs";
|
|
25993
|
-
import
|
|
26099
|
+
import path74 from "node:path";
|
|
25994
26100
|
|
|
25995
26101
|
// ../cli/src/lib/cloud/avatar.ts
|
|
25996
26102
|
init_session();
|
|
@@ -26039,10 +26145,10 @@ init_home();
|
|
|
26039
26145
|
init_config();
|
|
26040
26146
|
init_session();
|
|
26041
26147
|
import fs70 from "node:fs";
|
|
26042
|
-
import
|
|
26148
|
+
import path73 from "node:path";
|
|
26043
26149
|
var FRESH_MS = 10 * 6e4;
|
|
26044
26150
|
var GRACE_MS = 7 * 24 * 60 * 6e4;
|
|
26045
|
-
var heldFile = () =>
|
|
26151
|
+
var heldFile = () => path73.join(machineHome(), "pro.json");
|
|
26046
26152
|
function forgetPro() {
|
|
26047
26153
|
fs70.rmSync(heldFile(), { force: true });
|
|
26048
26154
|
}
|
|
@@ -26221,7 +26327,7 @@ async function cloudImage(opts, program2) {
|
|
|
26221
26327
|
const prompt = opts.prompt.trim();
|
|
26222
26328
|
if (!prompt) usage("Missing the prompt.", line);
|
|
26223
26329
|
const references = opts.refs.map((file) => {
|
|
26224
|
-
const type = REF_TYPES[
|
|
26330
|
+
const type = REF_TYPES[path74.extname(file).toLowerCase()];
|
|
26225
26331
|
if (!type) usage(`--ref ${file}: use a PNG, JPEG or WebP file.`, line);
|
|
26226
26332
|
try {
|
|
26227
26333
|
return `data:${type};base64,${fs71.readFileSync(file).toString("base64")}`;
|
|
@@ -26330,7 +26436,7 @@ async function generate(g) {
|
|
|
26330
26436
|
refuse(g.failed, g.fallback);
|
|
26331
26437
|
}
|
|
26332
26438
|
const out = g.outFor?.(res, g.out) ?? g.out;
|
|
26333
|
-
fs71.mkdirSync(
|
|
26439
|
+
fs71.mkdirSync(path74.dirname(path74.resolve(out)), { recursive: true });
|
|
26334
26440
|
fs71.writeFileSync(out, Buffer.from(await res.arrayBuffer()));
|
|
26335
26441
|
say(`${out} (${g.label(res)})`);
|
|
26336
26442
|
return { out };
|
|
@@ -26339,22 +26445,22 @@ async function generate(g) {
|
|
|
26339
26445
|
// ../cli/src/commands/cloud-import.ts
|
|
26340
26446
|
import crypto5 from "node:crypto";
|
|
26341
26447
|
import fs73 from "node:fs";
|
|
26342
|
-
import
|
|
26448
|
+
import path76 from "node:path";
|
|
26343
26449
|
|
|
26344
26450
|
// ../cloud/src/board-files.ts
|
|
26345
26451
|
var ROOTS = ["todo/", ".archive/", "memory/", "workflows/", "triage/", "assets/", "rules/"];
|
|
26346
26452
|
var SETTINGS = ["modules.md", "config.md"];
|
|
26347
|
-
var isSettingsPath = (
|
|
26453
|
+
var isSettingsPath = (path88) => SETTINGS.includes(path88) || /^rules\/(?:workflows\/)?[^/]+\.md$/.test(path88);
|
|
26348
26454
|
var BOARD_FILE_LIMIT = 256 * 1024;
|
|
26349
|
-
function isBoardPath(
|
|
26350
|
-
if (!
|
|
26351
|
-
if (
|
|
26352
|
-
if (
|
|
26353
|
-
if (
|
|
26354
|
-
if (SETTINGS.includes(
|
|
26355
|
-
if (
|
|
26356
|
-
if (
|
|
26357
|
-
return ROOTS.some((root) =>
|
|
26455
|
+
function isBoardPath(path88) {
|
|
26456
|
+
if (!path88 || path88.length > 500 || path88.startsWith("/") || path88.includes("\\") || path88.includes("\0")) return false;
|
|
26457
|
+
if (path88.split("/").some((part) => !part || part === "." || part === "..")) return false;
|
|
26458
|
+
if (path88 === "todo/README.md" || path88.split("/").includes(".sync")) return false;
|
|
26459
|
+
if (path88.startsWith("workflows/")) return true;
|
|
26460
|
+
if (SETTINGS.includes(path88)) return true;
|
|
26461
|
+
if (path88.startsWith("rules/")) return isSettingsPath(path88);
|
|
26462
|
+
if (path88.startsWith("assets/")) return path88.endsWith(".json");
|
|
26463
|
+
return ROOTS.some((root) => path88.startsWith(root)) && path88.endsWith(".md");
|
|
26358
26464
|
}
|
|
26359
26465
|
var workflowFolder = (id) => id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
26360
26466
|
|
|
@@ -26459,7 +26565,7 @@ init_bundled();
|
|
|
26459
26565
|
init_bundled();
|
|
26460
26566
|
init_parse();
|
|
26461
26567
|
import fs72 from "node:fs";
|
|
26462
|
-
import
|
|
26568
|
+
import path75 from "node:path";
|
|
26463
26569
|
function sharedMemory(board2) {
|
|
26464
26570
|
const declared = /* @__PURE__ */ new Map();
|
|
26465
26571
|
const take = (folder, text5, builtIn) => {
|
|
@@ -26471,7 +26577,7 @@ function sharedMemory(board2) {
|
|
|
26471
26577
|
const at = /^([^/]+)\/AGENT\.md$/.exec(key);
|
|
26472
26578
|
if (at) take(at[1], text5, true);
|
|
26473
26579
|
}
|
|
26474
|
-
const root =
|
|
26580
|
+
const root = path75.join(board2, "agents");
|
|
26475
26581
|
let folders2 = [];
|
|
26476
26582
|
try {
|
|
26477
26583
|
folders2 = fs72.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
|
|
@@ -26480,7 +26586,7 @@ function sharedMemory(board2) {
|
|
|
26480
26586
|
for (const folder of folders2.sort()) {
|
|
26481
26587
|
let text5 = null;
|
|
26482
26588
|
try {
|
|
26483
|
-
text5 = fs72.readFileSync(
|
|
26589
|
+
text5 = fs72.readFileSync(path75.join(root, folder, "AGENT.md"), "utf8");
|
|
26484
26590
|
} catch {
|
|
26485
26591
|
}
|
|
26486
26592
|
take(folder, text5, false);
|
|
@@ -26576,17 +26682,17 @@ function files(dir) {
|
|
|
26576
26682
|
const out = [];
|
|
26577
26683
|
for (const entry of fs73.readdirSync(dir, { withFileTypes: true })) {
|
|
26578
26684
|
if (entry.name.startsWith(".")) continue;
|
|
26579
|
-
const full =
|
|
26685
|
+
const full = path76.join(dir, entry.name);
|
|
26580
26686
|
if (entry.isDirectory()) out.push(...files(full));
|
|
26581
26687
|
else if (entry.isFile()) out.push(full);
|
|
26582
26688
|
}
|
|
26583
26689
|
return out;
|
|
26584
26690
|
}
|
|
26585
|
-
var posix2 = (p) => p.split(
|
|
26691
|
+
var posix2 = (p) => p.split(path76.sep).join("/");
|
|
26586
26692
|
function openCards() {
|
|
26587
26693
|
const out = [];
|
|
26588
26694
|
for (const file of walkMd(TODO)) {
|
|
26589
|
-
const rel2 = posix2(
|
|
26695
|
+
const rel2 = posix2(path76.relative(TODO, file));
|
|
26590
26696
|
if (rel2 === "README.md") continue;
|
|
26591
26697
|
const parts = rel2.split("/");
|
|
26592
26698
|
const text5 = read(file);
|
|
@@ -26620,14 +26726,14 @@ function bodyProblems(text5) {
|
|
|
26620
26726
|
return out;
|
|
26621
26727
|
}
|
|
26622
26728
|
function packWorkflow(flow) {
|
|
26623
|
-
const own =
|
|
26729
|
+
const own = path76.join(WORKFLOWS, flow.id);
|
|
26624
26730
|
const out = {};
|
|
26625
|
-
for (const file of files(own)) out[posix2(
|
|
26731
|
+
for (const file of files(own)) out[posix2(path76.relative(own, file))] = read(file);
|
|
26626
26732
|
const named2 = [...flow.previews, ...flow.land, ...flow.scheduled].map((h) => h.agent).filter((a) => !LAND_ROLES.includes(a));
|
|
26627
26733
|
for (const agent of new Set(named2)) {
|
|
26628
|
-
const dir =
|
|
26629
|
-
if (fs73.existsSync(
|
|
26630
|
-
for (const file of files(dir)) out[`agents/${agent}/${posix2(
|
|
26734
|
+
const dir = path76.join(AGENTS, agent);
|
|
26735
|
+
if (fs73.existsSync(path76.join(dir, "AGENT.md"))) {
|
|
26736
|
+
for (const file of files(dir)) out[`agents/${agent}/${posix2(path76.relative(dir, file))}`] = read(file);
|
|
26631
26737
|
continue;
|
|
26632
26738
|
}
|
|
26633
26739
|
for (const [key, text5] of Object.entries(BUNDLED_AGENT_FILES)) {
|
|
@@ -26679,8 +26785,8 @@ function draftsOf(text5) {
|
|
|
26679
26785
|
const at = /^\.(assets|mockups)\/(.+)$/.exec(name);
|
|
26680
26786
|
if (!at) continue;
|
|
26681
26787
|
const base = at[1] === "assets" ? ASSETS : MOCKUPS;
|
|
26682
|
-
const file =
|
|
26683
|
-
if (file.startsWith(base +
|
|
26788
|
+
const file = path76.resolve(base, at[2]);
|
|
26789
|
+
if (file.startsWith(base + path76.sep) && fs73.statSync(file, { throwIfNoEntry: false })?.isFile()) out.set(name, file);
|
|
26684
26790
|
}
|
|
26685
26791
|
return [...out].map(([name, file]) => ({ name, file }));
|
|
26686
26792
|
}
|
|
@@ -26714,7 +26820,7 @@ async function uploadDrafts(task, drafts) {
|
|
|
26714
26820
|
const assets = {};
|
|
26715
26821
|
for (const { name, file } of drafts) {
|
|
26716
26822
|
if (!card.body.includes(name)) continue;
|
|
26717
|
-
const ext =
|
|
26823
|
+
const ext = path76.extname(file).toLowerCase();
|
|
26718
26824
|
const type = TYPES[ext] ?? "application/octet-stream";
|
|
26719
26825
|
const data = fs73.readFileSync(file);
|
|
26720
26826
|
const artifact = await api("POST", `/v1/tasks/${task}/artifacts`, { type: kindOf(ext), name: name.slice(0, 200) });
|
|
@@ -26762,7 +26868,7 @@ async function run(asked, dryRun) {
|
|
|
26762
26868
|
const board2 = crypto5.createHash("sha256").update(fs73.realpathSync(KANBAN)).digest("hex").slice(0, 16);
|
|
26763
26869
|
const cards = openCards();
|
|
26764
26870
|
const problems = [];
|
|
26765
|
-
const where = (file) => posix2(
|
|
26871
|
+
const where = (file) => posix2(path76.relative(KANBAN, file));
|
|
26766
26872
|
const own = workflows().filter((w) => !w.builtIn);
|
|
26767
26873
|
const packed = /* @__PURE__ */ new Map();
|
|
26768
26874
|
for (const flow of own) {
|
|
@@ -26906,7 +27012,7 @@ function leftLocal(own) {
|
|
|
26906
27012
|
const triage = count4(TRIAGE);
|
|
26907
27013
|
if (triage) out.push(`triage/ (${triage})`);
|
|
26908
27014
|
if (fs73.existsSync(DELIVERIES)) out.push("deliveries/");
|
|
26909
|
-
if (fs73.existsSync(
|
|
27015
|
+
if (fs73.existsSync(path76.join(KANBAN, "metrics.csv"))) out.push("metrics.csv");
|
|
26910
27016
|
const named2 = new Set(own.flatMap((w) => [...w.previews, ...w.land, ...w.scheduled].map((h) => h.agent)));
|
|
26911
27017
|
if (fs73.existsSync(AGENTS)) {
|
|
26912
27018
|
for (const e of fs73.readdirSync(AGENTS, { withFileTypes: true })) {
|
|
@@ -26926,9 +27032,9 @@ init_config();
|
|
|
26926
27032
|
init_session();
|
|
26927
27033
|
init_io();
|
|
26928
27034
|
init_paths();
|
|
26929
|
-
import { createHash as
|
|
27035
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
26930
27036
|
import fs74 from "node:fs";
|
|
26931
|
-
import
|
|
27037
|
+
import path77 from "node:path";
|
|
26932
27038
|
var SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
26933
27039
|
var CAPTURED = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
26934
27040
|
var MAX_BYTES3 = 50 * 1024 * 1024;
|
|
@@ -27027,8 +27133,8 @@ This template has no preview.`);
|
|
|
27027
27133
|
const written = [];
|
|
27028
27134
|
for (const p of previews) {
|
|
27029
27135
|
const bytes3 = Buffer.from(await (await ask(`/${at}/previews/${route(p.path)}`, {}, WITHOUT)).arrayBuffer());
|
|
27030
|
-
const file =
|
|
27031
|
-
const out =
|
|
27136
|
+
const file = path77.basename(p.path);
|
|
27137
|
+
const out = path77.join(opts.to, file.startsWith(`${name}-`) ? file : `${name}-${file}`);
|
|
27032
27138
|
fs74.mkdirSync(opts.to, { recursive: true });
|
|
27033
27139
|
fs74.writeFileSync(out, bytes3);
|
|
27034
27140
|
written.push(out);
|
|
@@ -27043,15 +27149,15 @@ async function templateGet(name, opts, program2) {
|
|
|
27043
27149
|
const { template } = await asJson(await ask(`/${at}`, {}, WITHOUT));
|
|
27044
27150
|
const fetched = [];
|
|
27045
27151
|
for (const f of template.files) {
|
|
27046
|
-
const out =
|
|
27047
|
-
if (!out.startsWith(
|
|
27152
|
+
const out = path77.resolve(to, f.path);
|
|
27153
|
+
if (!out.startsWith(path77.resolve(to) + path77.sep)) refuse2(T.corrupt, WITHOUT);
|
|
27048
27154
|
const bytes3 = Buffer.from(await (await ask(`/${at}/files/${route(f.path)}`, {}, WITHOUT)).arrayBuffer());
|
|
27049
|
-
if (bytes3.byteLength !== f.size ||
|
|
27155
|
+
if (bytes3.byteLength !== f.size || createHash5("sha256").update(bytes3).digest("hex") !== f.sha256) {
|
|
27050
27156
|
refuse2(T.corrupt, WITHOUT);
|
|
27051
27157
|
}
|
|
27052
27158
|
fetched.push({ out, bytes: bytes3 });
|
|
27053
27159
|
}
|
|
27054
|
-
const taken = fetched.filter((f) => fs74.existsSync(f.out)).map((f) =>
|
|
27160
|
+
const taken = fetched.filter((f) => fs74.existsSync(f.out)).map((f) => path77.relative(to, f.out));
|
|
27055
27161
|
if (taken.length > 0) {
|
|
27056
27162
|
refuse2({
|
|
27057
27163
|
zh: `\u76EE\u6807\u6587\u4EF6\u5939\u5DF2\u6709\u540C\u540D\u6587\u4EF6\uFF1A${taken.join("\u3001")}\u3002\u8BF7\u6362\u4E00\u4E2A\u6587\u4EF6\u5939\uFF0C\u6216\u5148\u79FB\u8D70\u8FD9\u4E9B\u6587\u4EF6\u3002`,
|
|
@@ -27059,7 +27165,7 @@ async function templateGet(name, opts, program2) {
|
|
|
27059
27165
|
});
|
|
27060
27166
|
}
|
|
27061
27167
|
for (const f of fetched) {
|
|
27062
|
-
fs74.mkdirSync(
|
|
27168
|
+
fs74.mkdirSync(path77.dirname(f.out), { recursive: true });
|
|
27063
27169
|
fs74.writeFileSync(f.out, f.bytes);
|
|
27064
27170
|
}
|
|
27065
27171
|
say(`\u5DF2\u53D6\u56DE ${name}\uFF1A${fetched.length} \u4E2A\u6587\u4EF6\uFF0C\u4F4D\u4E8E ${to}
|
|
@@ -27078,7 +27184,7 @@ async function templateSource(name, opts) {
|
|
|
27078
27184
|
say(`evidence: ${e.path}`);
|
|
27079
27185
|
continue;
|
|
27080
27186
|
}
|
|
27081
|
-
const out =
|
|
27187
|
+
const out = path77.join(opts.to, `${name}-${path77.basename(e.path)}`);
|
|
27082
27188
|
fs74.mkdirSync(opts.to, { recursive: true });
|
|
27083
27189
|
fs74.writeFileSync(out, Buffer.from(await (await ask(`/${at}/evidence/${route(e.path)}`)).arrayBuffer()));
|
|
27084
27190
|
written.push(out);
|
|
@@ -27087,16 +27193,16 @@ async function templateSource(name, opts) {
|
|
|
27087
27193
|
return { name, source, evidence: written.length > 0 ? written : evidence.map((e) => e.path) };
|
|
27088
27194
|
}
|
|
27089
27195
|
async function templatePublish(dir, opts) {
|
|
27090
|
-
const root =
|
|
27091
|
-
const manifest =
|
|
27196
|
+
const root = path77.resolve(dir);
|
|
27197
|
+
const manifest = path77.join(root, "template.md");
|
|
27092
27198
|
if (!fs74.existsSync(manifest)) invalid("\u6587\u4EF6\u5939\u9876\u5C42\u7F3A\u5C11 template.md\u3002", "template.md is missing from the folder\u2019s top level.");
|
|
27093
27199
|
const meta = readTemplateMeta(fs74.readFileSync(manifest, "utf8"));
|
|
27094
27200
|
const files2 = walk2(root);
|
|
27095
|
-
const total = files2.reduce((sum, f) => sum + fs74.statSync(
|
|
27201
|
+
const total = files2.reduce((sum, f) => sum + fs74.statSync(path77.join(root, f)).size, 0);
|
|
27096
27202
|
if (total > MAX_BYTES3) invalid("\u6A21\u677F\u6587\u4EF6\u5408\u8BA1\u8D85\u8FC7 50 MB\u3002", "the template\u2019s files exceed 50 MB.");
|
|
27097
27203
|
for (const extra of [...opts.preview, ...opts.evidence]) {
|
|
27098
27204
|
if (!fs74.existsSync(extra)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${extra}\u3002`, `${extra} does not exist.`);
|
|
27099
|
-
if (
|
|
27205
|
+
if (path77.resolve(extra).startsWith(root + path77.sep)) {
|
|
27100
27206
|
invalid(
|
|
27101
27207
|
`${extra} \u5728\u6A21\u677F\u6587\u4EF6\u5939\u5185\uFF1B\u9884\u89C8\u56FE\u4E0E\u6765\u6E90\u51ED\u8BC1\u9700\u653E\u5728\u6A21\u677F\u6587\u4EF6\u5939\u4E4B\u5916\u3002`,
|
|
27102
27208
|
`${extra} is inside the template folder; keep previews and evidence outside it.`
|
|
@@ -27108,10 +27214,10 @@ async function templatePublish(dir, opts) {
|
|
|
27108
27214
|
}
|
|
27109
27215
|
const form = new FormData();
|
|
27110
27216
|
form.set("meta", JSON.stringify({ ...meta, replace: !!opts.replace }));
|
|
27111
|
-
const add = (kind, name, file) => form.append(`${kind}:${name}`, new Blob([fs74.readFileSync(file)]),
|
|
27112
|
-
for (const f of files2) add("files", f,
|
|
27113
|
-
for (const f of opts.preview) add("previews",
|
|
27114
|
-
for (const f of opts.evidence) add("evidence",
|
|
27217
|
+
const add = (kind, name, file) => form.append(`${kind}:${name}`, new Blob([fs74.readFileSync(file)]), path77.basename(name));
|
|
27218
|
+
for (const f of files2) add("files", f, path77.join(root, f));
|
|
27219
|
+
for (const f of opts.preview) add("previews", path77.basename(f), f);
|
|
27220
|
+
for (const f of opts.evidence) add("evidence", path77.basename(f), f);
|
|
27115
27221
|
const done2 = await asJson(await ask("", { method: "POST", body: form }));
|
|
27116
27222
|
say(
|
|
27117
27223
|
done2.replaced ? `\u5DF2\u66FF\u6362 ${done2.name}\uFF1A${files2.length} \u4E2A\u6587\u4EF6\u3002
|
|
@@ -27127,7 +27233,7 @@ Deleted ${name} from the catalog.`);
|
|
|
27127
27233
|
return { name, deleted: true };
|
|
27128
27234
|
}
|
|
27129
27235
|
async function templateShowcase(name, dir) {
|
|
27130
|
-
const file =
|
|
27236
|
+
const file = path77.join(dir, "showcase.json");
|
|
27131
27237
|
if (!fs74.existsSync(file)) invalid(`${dir} \u4E2D\u7F3A\u5C11 showcase.json\u3002`, `showcase.json is missing from ${dir}.`);
|
|
27132
27238
|
let meta;
|
|
27133
27239
|
try {
|
|
@@ -27143,7 +27249,7 @@ async function templateShowcase(name, dir) {
|
|
|
27143
27249
|
const form = new FormData();
|
|
27144
27250
|
form.set("meta", JSON.stringify(meta));
|
|
27145
27251
|
for (const p of new Set(named2)) {
|
|
27146
|
-
const at =
|
|
27252
|
+
const at = path77.join(dir, p);
|
|
27147
27253
|
if (!fs74.existsSync(at)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${at}\u3002`, `${at} does not exist.`);
|
|
27148
27254
|
form.append(`media:${p}`, new Blob([fs74.readFileSync(at)]), p);
|
|
27149
27255
|
}
|
|
@@ -27154,7 +27260,7 @@ Updated ${name}'s showcase: ${done2.files} files.`);
|
|
|
27154
27260
|
}
|
|
27155
27261
|
function walk2(root, sub = "") {
|
|
27156
27262
|
const out = [];
|
|
27157
|
-
for (const entry of fs74.readdirSync(
|
|
27263
|
+
for (const entry of fs74.readdirSync(path77.join(root, sub), { withFileTypes: true })) {
|
|
27158
27264
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
27159
27265
|
const rel2 = sub ? `${sub}/${entry.name}` : entry.name;
|
|
27160
27266
|
if (entry.isDirectory()) out.push(...walk2(root, rel2));
|
|
@@ -27439,7 +27545,7 @@ init_opencode();
|
|
|
27439
27545
|
init_locate();
|
|
27440
27546
|
init_store();
|
|
27441
27547
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
27442
|
-
import
|
|
27548
|
+
import path83 from "node:path";
|
|
27443
27549
|
var AGENTS2 = {
|
|
27444
27550
|
claude: "claude-code",
|
|
27445
27551
|
codex: "codex",
|
|
@@ -27454,10 +27560,10 @@ var AGENTS2 = {
|
|
|
27454
27560
|
var RUNTIMES = /* @__PURE__ */ new Set(["node", "bun", "deno"]);
|
|
27455
27561
|
function agentOf2(args) {
|
|
27456
27562
|
const words = args.trim().split(/\s+/);
|
|
27457
|
-
const first =
|
|
27563
|
+
const first = path83.basename(words[0] ?? "");
|
|
27458
27564
|
const script = RUNTIMES.has(first) ? words[1] ?? "" : words[0] ?? "";
|
|
27459
27565
|
if (script.includes("/@anthropic-ai/claude-code/")) return "claude-code";
|
|
27460
|
-
return AGENTS2[
|
|
27566
|
+
return AGENTS2[path83.basename(script).replace(/\.(m?js|cjs|exe)$/, "")];
|
|
27461
27567
|
}
|
|
27462
27568
|
function processChain() {
|
|
27463
27569
|
if (process.platform === "win32") return [];
|
|
@@ -27522,7 +27628,7 @@ init_read();
|
|
|
27522
27628
|
init_rules();
|
|
27523
27629
|
init_board();
|
|
27524
27630
|
import fs81 from "node:fs";
|
|
27525
|
-
import
|
|
27631
|
+
import path84 from "node:path";
|
|
27526
27632
|
var FOLLOW_MS = 400;
|
|
27527
27633
|
async function cmdStartRun(action, args, opts, program2 = "akb") {
|
|
27528
27634
|
const { req, follow: follow2, print } = readRequest(action, args, opts);
|
|
@@ -27568,6 +27674,14 @@ async function cmdStartRun(action, args, opts, program2 = "akb") {
|
|
|
27568
27674
|
}
|
|
27569
27675
|
sayBeforeStart(req, program2);
|
|
27570
27676
|
sayIfHeld(req, program2);
|
|
27677
|
+
const landing = runnable.action === "implement" && runnable.id !== void 0 ? activeDelivery(runnable.id) : void 0;
|
|
27678
|
+
if (landing?.landing) {
|
|
27679
|
+
const previous = readRuns().filter((r) => r.deliveryId === landing.deliveryId).reverse().sort((a, b) => b.startedAt - a.startedAt)[0];
|
|
27680
|
+
if (!previous) die("\u6784\u5EFA\u5DF2\u5B8C\u6210\uFF0C\u7B49\u5F85\u4EA4\u4ED8\u843D\u5730\uFF1B\u6CA1\u6709\u53EF\u8DDF\u968F\u7684\u8FD0\u884C\u8BB0\u5F55\u3002", { kind: "run-refused", action });
|
|
27681
|
+
say(`\u4EA4\u4ED8 ${landing.deliveryId} \u5DF2\u5B8C\u6210\u6784\u5EFA\uFF0C\u7EE7\u7EED\u843D\u5730\u3002`);
|
|
27682
|
+
if (follow2) return followStarted(previous.sessionId, program2, opts.events === true);
|
|
27683
|
+
return { sessionId: previous.sessionId, action, cardId: previous.cardId };
|
|
27684
|
+
}
|
|
27571
27685
|
const started = action === "refine" ? await startRefinement(req) : await startRun(runnable);
|
|
27572
27686
|
if ("error" in started) die(started.error, { kind: "run-refused", action });
|
|
27573
27687
|
const { run: run2, spawned } = started;
|
|
@@ -27814,7 +27928,7 @@ async function cmdSubStart(words, opts) {
|
|
|
27814
27928
|
let prompt = words.join(" ");
|
|
27815
27929
|
if (opts.file) {
|
|
27816
27930
|
try {
|
|
27817
|
-
prompt = fs81.readFileSync(
|
|
27931
|
+
prompt = fs81.readFileSync(path84.resolve(opts.file), "utf8");
|
|
27818
27932
|
} catch {
|
|
27819
27933
|
die(`--file: cannot read ${opts.file}`, { kind: "run-refused" });
|
|
27820
27934
|
}
|
|
@@ -28116,11 +28230,11 @@ init_yaml();
|
|
|
28116
28230
|
init_settings2();
|
|
28117
28231
|
init_paths();
|
|
28118
28232
|
import fs82 from "node:fs";
|
|
28119
|
-
import
|
|
28233
|
+
import path85 from "node:path";
|
|
28120
28234
|
var TOKEN_KEY = "TRIAGE_ENDPOINT_TOKEN";
|
|
28121
28235
|
var ENDPOINT_SETTING = "Triage endpoint";
|
|
28122
28236
|
var LINE2 = /^- \*\*Triage endpoint\*\*\s*[—-]\s*(.+)$/m;
|
|
28123
|
-
var boardRel3 = (file) => rel(file).split(
|
|
28237
|
+
var boardRel3 = (file) => rel(file).split(path85.sep).join("/");
|
|
28124
28238
|
function signalEndpoint() {
|
|
28125
28239
|
let text5;
|
|
28126
28240
|
try {
|
|
@@ -28865,7 +28979,7 @@ function declareAgent(program2, cli) {
|
|
|
28865
28979
|
|
|
28866
28980
|
// ../cli/src/lib/cli/setup.ts
|
|
28867
28981
|
import fs85 from "node:fs";
|
|
28868
|
-
import
|
|
28982
|
+
import path87 from "node:path";
|
|
28869
28983
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
28870
28984
|
|
|
28871
28985
|
// ../cli/src/commands/update.ts
|
|
@@ -28880,7 +28994,7 @@ init_memory();
|
|
|
28880
28994
|
init_yaml2();
|
|
28881
28995
|
init_version();
|
|
28882
28996
|
import fs84 from "node:fs";
|
|
28883
|
-
import
|
|
28997
|
+
import path86 from "node:path";
|
|
28884
28998
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
28885
28999
|
var NAME = "wildcrane";
|
|
28886
29000
|
var REPO = "https://github.com/ai4kanban/ai4kanban";
|
|
@@ -28930,9 +29044,9 @@ async function cmdUpdate2(ctx) {
|
|
|
28930
29044
|
return { updated: ctx.dir, from, to: SKILL_VERSION };
|
|
28931
29045
|
}
|
|
28932
29046
|
async function repairBoard(root, report3, named2) {
|
|
28933
|
-
const
|
|
28934
|
-
const kept2 =
|
|
28935
|
-
const board2 = named2 ?? (fs84.existsSync(
|
|
29047
|
+
const legacy2 = path86.join(root, "docs", "kanban");
|
|
29048
|
+
const kept2 = path86.join(root, ".akb", "kanban");
|
|
29049
|
+
const board2 = named2 ?? (fs84.existsSync(legacy2) ? legacy2 : kept2);
|
|
28936
29050
|
if (!fs84.existsSync(board2)) {
|
|
28937
29051
|
report3.notes.push("\u672A\u627E\u5230\u770B\u677F\uFF0C\u8BF7\u901A\u8FC7\u63D2\u4EF6\u8FDE\u63A5\u4E91\u7AEF\u9879\u76EE\u3002");
|
|
28938
29052
|
return false;
|
|
@@ -28964,14 +29078,14 @@ function moveRuntimes(root, board2, report3) {
|
|
|
28964
29078
|
}
|
|
28965
29079
|
}
|
|
28966
29080
|
function moveLegacyMemory(board2, report3) {
|
|
28967
|
-
const memory =
|
|
29081
|
+
const memory = path86.join(board2, "memory");
|
|
28968
29082
|
const moved = [];
|
|
28969
29083
|
for (const name of MEMORY_FILES) {
|
|
28970
|
-
const from =
|
|
29084
|
+
const from = path86.join(board2, name);
|
|
28971
29085
|
const st = statOf(from);
|
|
28972
29086
|
if (!st || !st.isFile()) continue;
|
|
28973
29087
|
fs84.mkdirSync(memory, { recursive: true });
|
|
28974
|
-
const to =
|
|
29088
|
+
const to = path86.join(memory, name);
|
|
28975
29089
|
const there = read3(to);
|
|
28976
29090
|
if (there === null) {
|
|
28977
29091
|
fs84.renameSync(from, to);
|
|
@@ -28986,19 +29100,19 @@ function moveLegacyMemory(board2, report3) {
|
|
|
28986
29100
|
if (moved.length) report3.did.push(`moved the memory set into docs/kanban/memory/: ${moved.join(", ")}`);
|
|
28987
29101
|
}
|
|
28988
29102
|
function dropRecordFile(board2) {
|
|
28989
|
-
const file =
|
|
29103
|
+
const file = path86.join(board2, "record.csv");
|
|
28990
29104
|
if (!fs84.existsSync(file)) return null;
|
|
28991
29105
|
fs84.rmSync(file);
|
|
28992
29106
|
return "removed docs/kanban/record.csv (the planning scores it fed are gone)";
|
|
28993
29107
|
}
|
|
28994
29108
|
function dropModuleGoals(board2, report3) {
|
|
28995
|
-
const memory =
|
|
29109
|
+
const memory = path86.join(board2, "memory");
|
|
28996
29110
|
if (!fs84.existsSync(memory)) return;
|
|
28997
|
-
const root = read3(
|
|
29111
|
+
const root = read3(path86.join(memory, "goal.md"));
|
|
28998
29112
|
let dropped = 0;
|
|
28999
29113
|
for (const entry of fs84.readdirSync(memory, { withFileTypes: true })) {
|
|
29000
29114
|
if (!entry.isDirectory()) continue;
|
|
29001
|
-
const file =
|
|
29115
|
+
const file = path86.join(memory, entry.name, "goal.md");
|
|
29002
29116
|
const text5 = read3(file);
|
|
29003
29117
|
if (text5 === null) continue;
|
|
29004
29118
|
if (text5 === root || text5.includes(UNFILLED) || !goalWords(text5)) {
|
|
@@ -29024,10 +29138,10 @@ var PLAN_LEADS = {
|
|
|
29024
29138
|
var GONE_LEADS = ["software-planner", "planner"];
|
|
29025
29139
|
function planLeads(board2) {
|
|
29026
29140
|
const leads = { ...PLAN_LEADS };
|
|
29027
|
-
const root =
|
|
29141
|
+
const root = path86.join(board2, "workflows");
|
|
29028
29142
|
for (const entry of fs84.existsSync(root) ? fs84.readdirSync(root, { withFileTypes: true }) : []) {
|
|
29029
29143
|
if (!entry.isDirectory()) continue;
|
|
29030
|
-
const meta = splitFrontmatter(read3(
|
|
29144
|
+
const meta = splitFrontmatter(read3(path86.join(root, entry.name, "WORKFLOW.md")) ?? "").meta;
|
|
29031
29145
|
const stages = meta === null ? void 0 : parseYamlBlock(meta).stages;
|
|
29032
29146
|
const plan = stages && typeof stages === "object" && !Array.isArray(stages) ? stages.plan : void 0;
|
|
29033
29147
|
const lead = plan && typeof plan === "object" && !Array.isArray(plan) && typeof plan.lead === "string" ? plan.lead.trim() : "";
|
|
@@ -29036,30 +29150,30 @@ function planLeads(board2) {
|
|
|
29036
29150
|
return leads;
|
|
29037
29151
|
}
|
|
29038
29152
|
function movePlanRules(board2, report3) {
|
|
29039
|
-
const rules =
|
|
29153
|
+
const rules = path86.join(board2, "rules");
|
|
29040
29154
|
const moved = [];
|
|
29041
29155
|
for (const [id, leads] of Object.entries(planLeads(board2))) {
|
|
29042
29156
|
for (const lead of leads) {
|
|
29043
|
-
const from =
|
|
29157
|
+
const from = path86.join(rules, `${lead}.md`);
|
|
29044
29158
|
const rule = read3(from)?.trim();
|
|
29045
29159
|
if (!rule) continue;
|
|
29046
|
-
const to =
|
|
29160
|
+
const to = path86.join(rules, "workflows", `${id}.md`);
|
|
29047
29161
|
const there = read3(to)?.trim() ?? "";
|
|
29048
29162
|
if (!there.includes(rule)) {
|
|
29049
|
-
fs84.mkdirSync(
|
|
29163
|
+
fs84.mkdirSync(path86.dirname(to), { recursive: true });
|
|
29050
29164
|
fs84.writeFileSync(to, `${[there, rule].filter(Boolean).join("\n\n")}
|
|
29051
29165
|
`);
|
|
29052
29166
|
moved.push(`${lead} \u2192 ${id}`);
|
|
29053
29167
|
}
|
|
29054
29168
|
}
|
|
29055
29169
|
}
|
|
29056
|
-
for (const lead of GONE_LEADS) fs84.rmSync(
|
|
29170
|
+
for (const lead of GONE_LEADS) fs84.rmSync(path86.join(rules, `${lead}.md`), { force: true });
|
|
29057
29171
|
if (moved.length) report3.did.push(`made each plan agent's rule its workflow's card rule (docs/kanban/rules/workflows/): ${moved.join(", ")}`);
|
|
29058
29172
|
}
|
|
29059
29173
|
function noteOldWorkflows(board2, report3) {
|
|
29060
29174
|
const old = (dir, file, keys) => {
|
|
29061
|
-
const root =
|
|
29062
|
-
return (fs84.existsSync(root) ? fs84.readdirSync(root, { withFileTypes: true }) : []).filter((e) => e.isDirectory() && keys.test(splitFrontmatter(read3(
|
|
29175
|
+
const root = path86.join(board2, dir);
|
|
29176
|
+
return (fs84.existsSync(root) ? fs84.readdirSync(root, { withFileTypes: true }) : []).filter((e) => e.isDirectory() && keys.test(splitFrontmatter(read3(path86.join(root, e.name, file)) ?? "").meta ?? "")).map((e) => `docs/kanban/${dir}/${e.name}/${file}`);
|
|
29063
29177
|
};
|
|
29064
29178
|
const files2 = [...old("workflows", "WORKFLOW.md", /^(stages|delivers):/m), ...old("agents", "AGENT.md", /^\s+(hook|lead):/m)];
|
|
29065
29179
|
if (files2.length) {
|
|
@@ -29069,7 +29183,7 @@ function noteOldWorkflows(board2, report3) {
|
|
|
29069
29183
|
}
|
|
29070
29184
|
}
|
|
29071
29185
|
function checkConfig(board2, report3) {
|
|
29072
|
-
const current = read3(
|
|
29186
|
+
const current = read3(path86.join(board2, "config.md"));
|
|
29073
29187
|
if (!current) return;
|
|
29074
29188
|
const added = missingConfigKeys(current);
|
|
29075
29189
|
if (added.length) {
|
|
@@ -29079,7 +29193,7 @@ function checkConfig(board2, report3) {
|
|
|
29079
29193
|
}
|
|
29080
29194
|
}
|
|
29081
29195
|
function checkModules(board2, report3) {
|
|
29082
|
-
const map2 = read3(
|
|
29196
|
+
const map2 = read3(path86.join(board2, "modules.md"));
|
|
29083
29197
|
if (map2 && map2.includes(UNFILLED)) {
|
|
29084
29198
|
report3.notes.push(
|
|
29085
29199
|
"docs/kanban/modules.md is still blank \u2014 write it from the repo (`akb guide module-map`)"
|
|
@@ -29133,14 +29247,14 @@ function declareSetup(program2, cli) {
|
|
|
29133
29247
|
program2.version(SKILL_VERSION, "-v, --version", "print this version").addHelpText("beforeAll", spelled(cli.program));
|
|
29134
29248
|
const dirOption = (cmd) => cmd.option("--dir <path>", "the project to work on (default: the current folder)").option("--json", "answer as one JSON object instead of prose");
|
|
29135
29249
|
const where = (cmd) => {
|
|
29136
|
-
const dir =
|
|
29250
|
+
const dir = path87.resolve(String(cmd.optsWithGlobals().dir ?? cli.cwd));
|
|
29137
29251
|
if (!fs85.existsSync(dir)) throw new BoardError(`no such folder: ${dir}`, { kind: "no-such-folder", dir });
|
|
29138
29252
|
return dir;
|
|
29139
29253
|
};
|
|
29140
29254
|
dirOption(program2.command("update")).summary("\u4FEE\u590D\u5DF2\u6709\u770B\u677F\u5E76\u68C0\u67E5 CLI \u65B0\u7248\u672C").description("\u4FEE\u590D\u5DF2\u6709\u770B\u677F\u5E76\u63D0\u793A CLI \u5347\u7EA7\u547D\u4EE4\u3002\u6CA1\u6709\u770B\u677F\u65F6\uFF0C\u8BF7\u901A\u8FC7\u63D2\u4EF6\u8FDE\u63A5\u4E91\u7AEF\u9879\u76EE\u3002").action(async function() {
|
|
29141
29255
|
const dir = where(this);
|
|
29142
29256
|
const named2 = this.optsWithGlobals().board ?? process.env.AI4KANBAN_BOARD?.trim();
|
|
29143
|
-
const board2 = named2 ?
|
|
29257
|
+
const board2 = named2 ? path87.resolve(cli.cwd, named2) : null;
|
|
29144
29258
|
await runAction(ctxOf(this, cli.program), {}, () => cmdUpdate2({ dir, program: cli.program, board: board2 }));
|
|
29145
29259
|
});
|
|
29146
29260
|
program2.command("__rules", { hidden: true }).action(function() {
|