wildcrane-runner 0.6.13 → 0.6.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/akb/dist/kanban.mjs +995 -883
- package/dist/akb/package.json +1 -1
- package/dist/launcher.mjs +2 -2
- package/dist/runner.mjs +21 -6
- 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.11 — 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,8 +1167,8 @@ 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
|
|
1171
|
-
var
|
|
1170
|
+
var path87 = __require("node:path");
|
|
1171
|
+
var fs87 = __require("node:fs");
|
|
1172
1172
|
var process2 = __require("node:process");
|
|
1173
1173
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
1174
1174
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -2149,7 +2149,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2149
2149
|
* @param {string} subcommandName
|
|
2150
2150
|
*/
|
|
2151
2151
|
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
|
|
2152
|
-
if (
|
|
2152
|
+
if (fs87.existsSync(executableFile)) return;
|
|
2153
2153
|
const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
|
|
2154
2154
|
const executableMissing = `'${executableFile}' does not exist
|
|
2155
2155
|
- if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
@@ -2167,11 +2167,11 @@ 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 =
|
|
2171
|
-
if (
|
|
2172
|
-
if (sourceExt.includes(
|
|
2170
|
+
const localBin = path87.resolve(baseDir, baseName);
|
|
2171
|
+
if (fs87.existsSync(localBin)) return localBin;
|
|
2172
|
+
if (sourceExt.includes(path87.extname(baseName))) return void 0;
|
|
2173
2173
|
const foundExt = sourceExt.find(
|
|
2174
|
-
(ext) =>
|
|
2174
|
+
(ext) => fs87.existsSync(`${localBin}${ext}`)
|
|
2175
2175
|
);
|
|
2176
2176
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
2177
2177
|
return void 0;
|
|
@@ -2183,21 +2183,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2183
2183
|
if (this._scriptPath) {
|
|
2184
2184
|
let resolvedScriptPath;
|
|
2185
2185
|
try {
|
|
2186
|
-
resolvedScriptPath =
|
|
2186
|
+
resolvedScriptPath = fs87.realpathSync(this._scriptPath);
|
|
2187
2187
|
} catch {
|
|
2188
2188
|
resolvedScriptPath = this._scriptPath;
|
|
2189
2189
|
}
|
|
2190
|
-
executableDir =
|
|
2191
|
-
|
|
2190
|
+
executableDir = path87.resolve(
|
|
2191
|
+
path87.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 = path87.basename(
|
|
2199
2199
|
this._scriptPath,
|
|
2200
|
-
|
|
2200
|
+
path87.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(path87.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 = path87.basename(filename, path87.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(path88) {
|
|
3073
|
+
if (path88 === void 0) return this._executableDir;
|
|
3074
|
+
this._executableDir = path88;
|
|
3075
3075
|
return this;
|
|
3076
3076
|
}
|
|
3077
3077
|
/**
|
|
@@ -5477,7 +5477,7 @@ var init_package = __esm({
|
|
|
5477
5477
|
"../cli/package.json"() {
|
|
5478
5478
|
package_default = {
|
|
5479
5479
|
name: "wildcrane",
|
|
5480
|
-
version: "0.11.
|
|
5480
|
+
version: "0.11.11",
|
|
5481
5481
|
akbContract: 5,
|
|
5482
5482
|
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.",
|
|
5483
5483
|
keywords: [
|
|
@@ -8316,10 +8316,10 @@ function findRollout(threadId) {
|
|
|
8316
8316
|
}
|
|
8317
8317
|
for (const name of names4) {
|
|
8318
8318
|
if (!name.startsWith("rollout-") || !name.endsWith(tail2)) continue;
|
|
8319
|
-
const
|
|
8319
|
+
const path87 = join(dir, name);
|
|
8320
8320
|
try {
|
|
8321
|
-
const at = statSync(
|
|
8322
|
-
if (!newest2 || at > newest2.at) newest2 = { path:
|
|
8321
|
+
const at = statSync(path87).mtimeMs;
|
|
8322
|
+
if (!newest2 || at > newest2.at) newest2 = { path: path87, at };
|
|
8323
8323
|
} catch {
|
|
8324
8324
|
}
|
|
8325
8325
|
}
|
|
@@ -8327,11 +8327,11 @@ function findRollout(threadId) {
|
|
|
8327
8327
|
return newest2?.path;
|
|
8328
8328
|
}
|
|
8329
8329
|
function readCodexRunFacts(threadId) {
|
|
8330
|
-
const
|
|
8331
|
-
if (!
|
|
8330
|
+
const path87 = findRollout(threadId);
|
|
8331
|
+
if (!path87) return void 0;
|
|
8332
8332
|
let raw;
|
|
8333
8333
|
try {
|
|
8334
|
-
raw = readFileSync(
|
|
8334
|
+
raw = readFileSync(path87, "utf8");
|
|
8335
8335
|
} catch {
|
|
8336
8336
|
return void 0;
|
|
8337
8337
|
}
|
|
@@ -8459,8 +8459,8 @@ function renderCompleted(item) {
|
|
|
8459
8459
|
const changes = Array.isArray(item.changes) ? item.changes : [];
|
|
8460
8460
|
return changes.map((raw) => {
|
|
8461
8461
|
const change2 = obj(raw);
|
|
8462
|
-
const
|
|
8463
|
-
return
|
|
8462
|
+
const path87 = str2(change2.path);
|
|
8463
|
+
return path87 ? `\u23FA ${str2(change2.kind) || "change"}(${path87})
|
|
8464
8464
|
` : "";
|
|
8465
8465
|
}).join("");
|
|
8466
8466
|
}
|
|
@@ -8719,10 +8719,10 @@ function readKimiRunFacts(sessionId) {
|
|
|
8719
8719
|
const total = { input: 0, cacheCreation: 0, cacheRead: 0, output: 0 };
|
|
8720
8720
|
let model;
|
|
8721
8721
|
let holding = 0;
|
|
8722
|
-
for (const { path:
|
|
8722
|
+
for (const { path: path87, main } of wireFiles(entry.sessionDir)) {
|
|
8723
8723
|
let raw;
|
|
8724
8724
|
try {
|
|
8725
|
-
raw = readFileSync2(
|
|
8725
|
+
raw = readFileSync2(path87, "utf8");
|
|
8726
8726
|
} catch {
|
|
8727
8727
|
continue;
|
|
8728
8728
|
}
|
|
@@ -9003,8 +9003,8 @@ var init_tail = __esm({
|
|
|
9003
9003
|
import fs24 from "node:fs";
|
|
9004
9004
|
function whereHint(update2, title) {
|
|
9005
9005
|
const locations = Array.isArray(update2.locations) ? update2.locations : [];
|
|
9006
|
-
const
|
|
9007
|
-
return
|
|
9006
|
+
const path87 = str2(obj(locations[0]).path);
|
|
9007
|
+
return path87 && !title.includes(path87) ? hint(path87) : "";
|
|
9008
9008
|
}
|
|
9009
9009
|
function saidBy(update2) {
|
|
9010
9010
|
const content = Array.isArray(update2.content) ? update2.content : [];
|
|
@@ -9947,27 +9947,40 @@ import crypto3 from "node:crypto";
|
|
|
9947
9947
|
import fs25 from "node:fs";
|
|
9948
9948
|
import os4 from "node:os";
|
|
9949
9949
|
import path28 from "node:path";
|
|
9950
|
+
function spawnGit(args, options) {
|
|
9951
|
+
const out = spawnSync3("git", args, {
|
|
9952
|
+
...options,
|
|
9953
|
+
encoding: "utf8",
|
|
9954
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
9955
|
+
timeout: gitLimits.timeoutMs,
|
|
9956
|
+
killSignal: "SIGKILL"
|
|
9957
|
+
});
|
|
9958
|
+
if (out.error?.code === "ETIMEDOUT") throw new GitTimeoutError(`Git ${args[0]} timed out after ${gitLimits.timeoutMs / 1e3}s; the command was killed.`);
|
|
9959
|
+
return out;
|
|
9960
|
+
}
|
|
9950
9961
|
function git(args, cwd = REPO_ROOT) {
|
|
9951
9962
|
try {
|
|
9952
|
-
const out =
|
|
9963
|
+
const out = spawnGit(args, { cwd, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true });
|
|
9953
9964
|
if (out.error || out.status !== 0) return null;
|
|
9954
9965
|
return out.stdout;
|
|
9955
|
-
} catch {
|
|
9966
|
+
} catch (error) {
|
|
9967
|
+
if (error instanceof GitTimeoutError) throw error;
|
|
9956
9968
|
return null;
|
|
9957
9969
|
}
|
|
9958
9970
|
}
|
|
9959
9971
|
function gitDiff(args, cwd = REPO_ROOT) {
|
|
9960
9972
|
try {
|
|
9961
|
-
const out =
|
|
9973
|
+
const out = spawnGit(args, { cwd, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true });
|
|
9962
9974
|
if (out.error || out.status !== 0 && out.status !== 1) return null;
|
|
9963
9975
|
return out.stdout;
|
|
9964
|
-
} catch {
|
|
9976
|
+
} catch (error) {
|
|
9977
|
+
if (error instanceof GitTimeoutError) throw error;
|
|
9965
9978
|
return null;
|
|
9966
9979
|
}
|
|
9967
9980
|
}
|
|
9968
9981
|
function tryGit(args, cwd = REPO_ROOT, env2) {
|
|
9969
9982
|
try {
|
|
9970
|
-
const out =
|
|
9983
|
+
const out = spawnGit(args, {
|
|
9971
9984
|
cwd,
|
|
9972
9985
|
encoding: "utf8",
|
|
9973
9986
|
maxBuffer: MAX_OUTPUT,
|
|
@@ -9978,6 +9991,7 @@ function tryGit(args, cwd = REPO_ROOT, env2) {
|
|
|
9978
9991
|
if (out.status !== 0) return { ok: false, why: (out.stderr || out.stdout || "").trim() };
|
|
9979
9992
|
return { ok: true };
|
|
9980
9993
|
} catch (e) {
|
|
9994
|
+
if (e instanceof GitTimeoutError) throw e;
|
|
9981
9995
|
return { ok: false, why: e instanceof Error ? e.message : String(e) };
|
|
9982
9996
|
}
|
|
9983
9997
|
}
|
|
@@ -9986,7 +10000,7 @@ function boardManagedPaths() {
|
|
|
9986
10000
|
}
|
|
9987
10001
|
function outsideBoardForAdd(cwd = REPO_ROOT) {
|
|
9988
10002
|
const kept2 = boardManagedPaths().filter(
|
|
9989
|
-
(p) =>
|
|
10003
|
+
(p) => spawnGit(["check-ignore", "-q", p], { cwd, windowsHide: true }).status !== 0
|
|
9990
10004
|
);
|
|
9991
10005
|
return ["--", ".", ...kept2.map((p) => `:(exclude)${p}`)];
|
|
9992
10006
|
}
|
|
@@ -10106,10 +10120,10 @@ function treeMark(base, cwd = REPO_ROOT) {
|
|
|
10106
10120
|
const index = path28.join(ensureAkbDir(), `index-${process.pid}-${Date.now()}`);
|
|
10107
10121
|
try {
|
|
10108
10122
|
const env2 = { ...process.env, GIT_INDEX_FILE: index };
|
|
10109
|
-
const run2 = (args) =>
|
|
10123
|
+
const run2 = (args) => spawnGit(args, { cwd, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true, env: env2 }).status === 0;
|
|
10110
10124
|
if (!run2(["read-tree", base])) return null;
|
|
10111
10125
|
if (!run2(["add", "-A", ...outsideBoardForAdd(cwd)])) return null;
|
|
10112
|
-
const tree =
|
|
10126
|
+
const tree = spawnGit(["write-tree"], {
|
|
10113
10127
|
cwd,
|
|
10114
10128
|
encoding: "utf8",
|
|
10115
10129
|
maxBuffer: MAX_OUTPUT,
|
|
@@ -10117,7 +10131,8 @@ function treeMark(base, cwd = REPO_ROOT) {
|
|
|
10117
10131
|
env: env2
|
|
10118
10132
|
});
|
|
10119
10133
|
return tree.status === 0 ? tree.stdout.trim() || null : null;
|
|
10120
|
-
} catch {
|
|
10134
|
+
} catch (error) {
|
|
10135
|
+
if (error instanceof GitTimeoutError) throw error;
|
|
10121
10136
|
return null;
|
|
10122
10137
|
} finally {
|
|
10123
10138
|
try {
|
|
@@ -10128,23 +10143,26 @@ function treeMark(base, cwd = REPO_ROOT) {
|
|
|
10128
10143
|
}
|
|
10129
10144
|
function reverseApplies(patch2, ref, cwd = REPO_ROOT) {
|
|
10130
10145
|
if (!patch2.trim()) return false;
|
|
10131
|
-
const
|
|
10146
|
+
const folder = fs25.mkdtempSync(path28.join(os4.tmpdir(), `akb-apply-${process.pid}-`));
|
|
10147
|
+
const index = path28.join(folder, "index");
|
|
10148
|
+
const patchFile = path28.join(folder, "patch");
|
|
10132
10149
|
const env2 = { GIT_INDEX_FILE: index };
|
|
10133
10150
|
try {
|
|
10151
|
+
fs25.writeFileSync(patchFile, patch2);
|
|
10134
10152
|
if (!tryGit(["read-tree", ref], cwd, env2).ok) return false;
|
|
10135
|
-
const out =
|
|
10153
|
+
const out = spawnGit(["apply", "--cached", "--reverse", "--check", patchFile], {
|
|
10136
10154
|
cwd,
|
|
10137
|
-
input: patch2,
|
|
10138
10155
|
encoding: "utf8",
|
|
10139
10156
|
maxBuffer: MAX_OUTPUT,
|
|
10140
10157
|
windowsHide: true,
|
|
10141
10158
|
env: { ...process.env, ...env2 }
|
|
10142
10159
|
});
|
|
10143
10160
|
return !out.error && out.status === 0;
|
|
10144
|
-
} catch {
|
|
10161
|
+
} catch (error) {
|
|
10162
|
+
if (error instanceof GitTimeoutError) throw error;
|
|
10145
10163
|
return false;
|
|
10146
10164
|
} finally {
|
|
10147
|
-
fs25.rmSync(
|
|
10165
|
+
fs25.rmSync(folder, { recursive: true, force: true });
|
|
10148
10166
|
}
|
|
10149
10167
|
}
|
|
10150
10168
|
function squashOnto(dir, base, message) {
|
|
@@ -10226,12 +10244,15 @@ function moveBranchRef(branch, commit, expect) {
|
|
|
10226
10244
|
if (branchTip(branch) !== expect) return { moved: true };
|
|
10227
10245
|
return { ok: false, error: `couldn't move ${branch} to ${commit.slice(0, 12)}: ${done2.why ?? "git refused"}` };
|
|
10228
10246
|
}
|
|
10229
|
-
var MAX_OUTPUT, scripted, posix, outsideBoard, deliveryBranch, deliveryWorktree, worktreeDir, inGitRepo, headCommit, worktreeExists, branchExists, pruneWorktreeMetadata, branchTip, isAncestor, changedPaths, branchPatch, lastCommitTouching, continueRebase, abortRebase, conflictedPaths, headingKind;
|
|
10247
|
+
var MAX_OUTPUT, gitLimits, GitTimeoutError, scripted, posix, outsideBoard, deliveryBranch, deliveryWorktree, worktreeDir, inGitRepo, headCommit, worktreeExists, branchExists, pruneWorktreeMetadata, branchTip, isAncestor, changedPaths, branchPatch, lastCommitTouching, continueRebase, abortRebase, conflictedPaths, headingKind;
|
|
10230
10248
|
var init_worktree = __esm({
|
|
10231
10249
|
"../cli/src/lib/agent/worktree.ts"() {
|
|
10232
10250
|
"use strict";
|
|
10233
10251
|
init_paths();
|
|
10234
10252
|
MAX_OUTPUT = 64 * 1024 * 1024;
|
|
10253
|
+
gitLimits = { timeoutMs: 3e4 };
|
|
10254
|
+
GitTimeoutError = class extends Error {
|
|
10255
|
+
};
|
|
10235
10256
|
scripted = (args, cwd) => tryGit(["-c", "core.hooksPath=/dev/null", ...args], cwd, { GIT_EDITOR: "true", GIT_SEQUENCE_EDITOR: "true" });
|
|
10236
10257
|
posix = (p) => p.split(path28.sep).join("/");
|
|
10237
10258
|
outsideBoard = () => [
|
|
@@ -10248,7 +10269,7 @@ var init_worktree = __esm({
|
|
|
10248
10269
|
branchExists = (branch) => !!branch && git(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`]) !== null;
|
|
10249
10270
|
pruneWorktreeMetadata = () => void git(["worktree", "prune"]);
|
|
10250
10271
|
branchTip = (branch) => git(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`])?.trim() || null;
|
|
10251
|
-
isAncestor = (older, newer, cwd = REPO_ROOT) =>
|
|
10272
|
+
isAncestor = (older, newer, cwd = REPO_ROOT) => spawnGit(["merge-base", "--is-ancestor", older, newer], { cwd, windowsHide: true }).status === 0;
|
|
10252
10273
|
changedPaths = (base, branch, cwd = REPO_ROOT) => {
|
|
10253
10274
|
const out = git(["diff", "--name-only", base, branch, ...outsideBoard()], cwd);
|
|
10254
10275
|
return out === null ? null : out.split("\n").filter(Boolean);
|
|
@@ -15546,6 +15567,252 @@ var init_complete = __esm({
|
|
|
15546
15567
|
}
|
|
15547
15568
|
});
|
|
15548
15569
|
|
|
15570
|
+
// ../cli/src/lib/agent/stop.ts
|
|
15571
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
15572
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
15573
|
+
import fs39 from "node:fs";
|
|
15574
|
+
import os6 from "node:os";
|
|
15575
|
+
import path43 from "node:path";
|
|
15576
|
+
function markedInProc(entry, strict = false) {
|
|
15577
|
+
const pids = [];
|
|
15578
|
+
for (const name of fs39.readdirSync("/proc")) {
|
|
15579
|
+
if (!/^\d+$/.test(name)) continue;
|
|
15580
|
+
try {
|
|
15581
|
+
if (fs39.readFileSync(`/proc/${name}/environ`, "utf8").split("\0").includes(entry)) pids.push(Number(name));
|
|
15582
|
+
} catch (error) {
|
|
15583
|
+
if (strict && !["ENOENT", "ESRCH"].includes(error.code ?? "")) {
|
|
15584
|
+
const stat = fs39.statSync(`/proc/${name}`, { throwIfNoEntry: false });
|
|
15585
|
+
if (stat?.uid === process.getuid?.()) throw error;
|
|
15586
|
+
}
|
|
15587
|
+
}
|
|
15588
|
+
}
|
|
15589
|
+
return pids;
|
|
15590
|
+
}
|
|
15591
|
+
function markedInPs(entry, roots) {
|
|
15592
|
+
const ps = spawnSync4("ps", ["-A", "-E", "-ww", "-o", "pid=,ppid=,command="], {
|
|
15593
|
+
encoding: "utf8",
|
|
15594
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
15595
|
+
timeout: 5e3
|
|
15596
|
+
});
|
|
15597
|
+
if (ps.status !== 0 || !ps.stdout) throw new Error("\u65E0\u6CD5\u67E5\u8BE2\u8FDB\u7A0B\u6811\u3002");
|
|
15598
|
+
const children = /* @__PURE__ */ new Map();
|
|
15599
|
+
const listed2 = /* @__PURE__ */ new Set();
|
|
15600
|
+
const found2 = /* @__PURE__ */ new Set();
|
|
15601
|
+
for (const line of ps.stdout.split("\n")) {
|
|
15602
|
+
const m = /^\s*(\d+)\s+(\d+)\s(.*)$/.exec(line);
|
|
15603
|
+
if (!m) continue;
|
|
15604
|
+
const pid = Number(m[1]);
|
|
15605
|
+
const ppid = Number(m[2]);
|
|
15606
|
+
listed2.add(pid);
|
|
15607
|
+
children.set(ppid, [...children.get(ppid) ?? [], pid]);
|
|
15608
|
+
if (`${m[3]} `.includes(` ${entry} `)) found2.add(pid);
|
|
15609
|
+
}
|
|
15610
|
+
const queue = [...found2, ...roots.filter((pid) => listed2.has(pid))];
|
|
15611
|
+
for (const pid of queue) {
|
|
15612
|
+
found2.add(pid);
|
|
15613
|
+
for (const child of children.get(pid) ?? []) if (!found2.has(child)) queue.push(child);
|
|
15614
|
+
}
|
|
15615
|
+
return [...found2];
|
|
15616
|
+
}
|
|
15617
|
+
function marked(mark, roots = []) {
|
|
15618
|
+
if (WINDOWS3 || !mark.value) return [];
|
|
15619
|
+
const entry = `${mark.name}=${mark.value}`;
|
|
15620
|
+
try {
|
|
15621
|
+
const pids = process.platform === "linux" ? markedInProc(entry) : markedInPs(entry, roots);
|
|
15622
|
+
return pids.filter((pid) => pid !== process.pid);
|
|
15623
|
+
} catch {
|
|
15624
|
+
return [];
|
|
15625
|
+
}
|
|
15626
|
+
}
|
|
15627
|
+
function signal(pids, sig) {
|
|
15628
|
+
for (const pid of pids) {
|
|
15629
|
+
try {
|
|
15630
|
+
process.kill(pid, sig);
|
|
15631
|
+
} catch {
|
|
15632
|
+
}
|
|
15633
|
+
}
|
|
15634
|
+
}
|
|
15635
|
+
function stopWatchers(sessions, onlyPid, stillOwns = () => true, startedBefore) {
|
|
15636
|
+
if (sessions ? !sessions.length : onlyPid === void 0) return [];
|
|
15637
|
+
const command = WINDOWS3 ? "powershell.exe" : "ps";
|
|
15638
|
+
const args = WINDOWS3 ? ["-NoProfile", "-NonInteractive", "-Command", "Get-CimInstance Win32_Process | Select-Object ProcessId,CommandLine,@{Name='StartedAt';Expression={([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds()}} | ConvertTo-Json -Compress"] : ["-A", "-ww", "-o", "pid=,lstart=,command="];
|
|
15639
|
+
const out = spawnSync4(command, args, { encoding: "utf8", timeout: 5e3, maxBuffer: 16 * 1024 * 1024, windowsHide: true, env: { ...process.env, LC_ALL: "C" } });
|
|
15640
|
+
if (out.status !== 0) return [];
|
|
15641
|
+
let procs;
|
|
15642
|
+
try {
|
|
15643
|
+
procs = WINDOWS3 ? [].concat(JSON.parse(out.stdout)).map((p) => ({ pid: p.ProcessId, command: p.CommandLine ?? "", startedAt: p.StartedAt })) : out.stdout.split("\n").flatMap((line) => {
|
|
15644
|
+
const m = /^\s*(\d+)\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d{4})\s+(.*)$/.exec(line);
|
|
15645
|
+
return m ? [{ pid: Number(m[1]), command: m[3], startedAt: Date.parse(m[2]) }] : [];
|
|
15646
|
+
});
|
|
15647
|
+
} catch {
|
|
15648
|
+
return [];
|
|
15649
|
+
}
|
|
15650
|
+
const known2 = sessions && new Set(sessions);
|
|
15651
|
+
const stopped2 = [];
|
|
15652
|
+
for (const proc of procs) {
|
|
15653
|
+
if (proc.pid === process.pid || onlyPid !== void 0 && proc.pid !== onlyPid) continue;
|
|
15654
|
+
if (startedBefore !== void 0 && (!Number.isFinite(proc.startedAt) || proc.startedAt > startedBefore)) continue;
|
|
15655
|
+
const match = /^"?[^\n]*?\bnode(?:\.exe)?"?\s+(?:"[^\n"]+\.mjs"|\S+\.mjs)\s+__watch\s+(\S+)(?:\s|$)/.exec(proc.command);
|
|
15656
|
+
const args2 = `${proc.command} `;
|
|
15657
|
+
const names4 = (flag, value) => args2.includes(` ${flag} ${value} `) || args2.includes(` ${flag} "${value}" `);
|
|
15658
|
+
const board2 = names4("--board", KANBAN) || KANBAN === path43.join(REPO_ROOT, "docs", "kanban") && names4("--dir", REPO_ROOT);
|
|
15659
|
+
if (!match || known2 && !known2.has(match[1]) || !board2 || !stillOwns()) continue;
|
|
15660
|
+
if (WINDOWS3) {
|
|
15661
|
+
if (killTreeOnWindows(proc.pid)) stopped2.push(proc.pid);
|
|
15662
|
+
} else {
|
|
15663
|
+
try {
|
|
15664
|
+
process.kill(-proc.pid, "SIGKILL");
|
|
15665
|
+
stopped2.push(proc.pid);
|
|
15666
|
+
} catch {
|
|
15667
|
+
}
|
|
15668
|
+
}
|
|
15669
|
+
}
|
|
15670
|
+
return stopped2;
|
|
15671
|
+
}
|
|
15672
|
+
function startedUnder(list, root, since) {
|
|
15673
|
+
const self = list.procs.find((p) => p.pid === root);
|
|
15674
|
+
const from = since ?? self?.startedAt;
|
|
15675
|
+
if (from === void 0 || list.bootedAt > from) return [];
|
|
15676
|
+
if (self && self.startedAt > from) return [];
|
|
15677
|
+
const found2 = self ? [root] : [];
|
|
15678
|
+
const seen = /* @__PURE__ */ new Set([root]);
|
|
15679
|
+
const queue = [{ pid: root, startedAt: from }];
|
|
15680
|
+
for (const parent of queue) {
|
|
15681
|
+
for (const p of list.procs) {
|
|
15682
|
+
if (p.ppid !== parent.pid || seen.has(p.pid) || p.startedAt < parent.startedAt) continue;
|
|
15683
|
+
seen.add(p.pid);
|
|
15684
|
+
found2.push(p.pid);
|
|
15685
|
+
queue.push(p);
|
|
15686
|
+
}
|
|
15687
|
+
}
|
|
15688
|
+
return found2.reverse();
|
|
15689
|
+
}
|
|
15690
|
+
function readProcList(out) {
|
|
15691
|
+
const procs = [];
|
|
15692
|
+
let bootedAt;
|
|
15693
|
+
for (const line of out.split(/\r?\n/)) {
|
|
15694
|
+
const boot = /^boot (\d+)$/.exec(line.trim());
|
|
15695
|
+
if (boot) bootedAt = Number(boot[1]);
|
|
15696
|
+
const m = /^(\d+) (\d+) (\d+)$/.exec(line.trim());
|
|
15697
|
+
if (m) procs.push({ pid: Number(m[1]), ppid: Number(m[2]), startedAt: Number(m[3]) });
|
|
15698
|
+
}
|
|
15699
|
+
return bootedAt !== void 0 && procs.length ? { procs, bootedAt } : void 0;
|
|
15700
|
+
}
|
|
15701
|
+
function procsOnWindows() {
|
|
15702
|
+
const ps = spawnSync4("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", LIST_PROCS], {
|
|
15703
|
+
encoding: "utf8",
|
|
15704
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
15705
|
+
timeout: 5e3,
|
|
15706
|
+
windowsHide: true
|
|
15707
|
+
});
|
|
15708
|
+
return ps.status === 0 && ps.stdout ? readProcList(ps.stdout) : void 0;
|
|
15709
|
+
}
|
|
15710
|
+
function killUnderOnWindows(pid, since) {
|
|
15711
|
+
if (!WINDOWS3 || !pid) return;
|
|
15712
|
+
const list = procsOnWindows();
|
|
15713
|
+
if (!list) return;
|
|
15714
|
+
const pids = startedUnder(list, pid, since).filter((p) => p !== process.pid);
|
|
15715
|
+
if (!pids.length) return;
|
|
15716
|
+
spawnSync4("taskkill", ["/F", ...pids.flatMap((p) => ["/PID", String(p)])], { stdio: "ignore", windowsHide: true, timeout: 1e4 });
|
|
15717
|
+
}
|
|
15718
|
+
function killMarked(mark, agent) {
|
|
15719
|
+
if (WINDOWS3) {
|
|
15720
|
+
if (agent) killUnderOnWindows(agent.pid, agent.since);
|
|
15721
|
+
return;
|
|
15722
|
+
}
|
|
15723
|
+
signal(marked(mark), "SIGKILL");
|
|
15724
|
+
}
|
|
15725
|
+
function killTreeOnWindows(pid) {
|
|
15726
|
+
if (!WINDOWS3 || !pid) return false;
|
|
15727
|
+
const out = spawnSync4("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true, timeout: 1e4 });
|
|
15728
|
+
return out.status === 0;
|
|
15729
|
+
}
|
|
15730
|
+
function later(ms, fn) {
|
|
15731
|
+
const t = setTimeout(fn, ms);
|
|
15732
|
+
if (typeof t.unref === "function") t.unref();
|
|
15733
|
+
}
|
|
15734
|
+
function endAgent(child, mark, graceMs) {
|
|
15735
|
+
const term = (sig) => {
|
|
15736
|
+
try {
|
|
15737
|
+
child.kill(sig);
|
|
15738
|
+
} catch {
|
|
15739
|
+
}
|
|
15740
|
+
};
|
|
15741
|
+
if (killTreeOnWindows(child.pid)) return;
|
|
15742
|
+
killUnderOnWindows(child.pid);
|
|
15743
|
+
const seen = new Set(marked(mark, child.pid ? [child.pid] : []));
|
|
15744
|
+
term("SIGTERM");
|
|
15745
|
+
signal([...seen].filter((pid) => pid !== child.pid), "SIGTERM");
|
|
15746
|
+
const round = (left) => {
|
|
15747
|
+
const pids = marked(mark, [...seen]);
|
|
15748
|
+
signal(pids, "SIGKILL");
|
|
15749
|
+
for (const pid of pids) seen.add(pid);
|
|
15750
|
+
if (pids.length && left > 1) later(KILL_ROUND_MS, () => round(left - 1));
|
|
15751
|
+
};
|
|
15752
|
+
later(graceMs, () => {
|
|
15753
|
+
term("SIGKILL");
|
|
15754
|
+
round(KILL_ROUNDS);
|
|
15755
|
+
});
|
|
15756
|
+
}
|
|
15757
|
+
function killRunning() {
|
|
15758
|
+
for (const [child, mark] of running) {
|
|
15759
|
+
if (killTreeOnWindows(child.pid)) continue;
|
|
15760
|
+
killUnderOnWindows(child.pid);
|
|
15761
|
+
signal(marked(mark, child.pid ? [child.pid] : []), "SIGKILL");
|
|
15762
|
+
try {
|
|
15763
|
+
child.kill("SIGKILL");
|
|
15764
|
+
} catch {
|
|
15765
|
+
}
|
|
15766
|
+
}
|
|
15767
|
+
running.clear();
|
|
15768
|
+
}
|
|
15769
|
+
function unlisten() {
|
|
15770
|
+
for (const [sig, fn] of listening) process.removeListener(sig, fn);
|
|
15771
|
+
listening.clear();
|
|
15772
|
+
}
|
|
15773
|
+
function onQuit(sig) {
|
|
15774
|
+
killRunning();
|
|
15775
|
+
unlisten();
|
|
15776
|
+
if (process.listenerCount(sig) === 0) process.exit(128 + os6.constants.signals[sig]);
|
|
15777
|
+
}
|
|
15778
|
+
function trackAgent(child, mark) {
|
|
15779
|
+
if (!child.pid) return;
|
|
15780
|
+
running.set(child, mark);
|
|
15781
|
+
child.once("close", () => {
|
|
15782
|
+
running.delete(child);
|
|
15783
|
+
if (!running.size) unlisten();
|
|
15784
|
+
});
|
|
15785
|
+
for (const sig of QUIT_SIGNALS) {
|
|
15786
|
+
if (listening.has(sig) || sig === "SIGINT" && interruptIsTheCallers) continue;
|
|
15787
|
+
const fn = () => onQuit(sig);
|
|
15788
|
+
listening.set(sig, fn);
|
|
15789
|
+
process.on(sig, fn);
|
|
15790
|
+
}
|
|
15791
|
+
}
|
|
15792
|
+
var runMark, stopMark, markEnv, WINDOWS3, KILL_ROUND_MS, KILL_ROUNDS, LIST_PROCS, running, QUIT_SIGNALS, listening, interruptIsTheCallers;
|
|
15793
|
+
var init_stop = __esm({
|
|
15794
|
+
"../cli/src/lib/agent/stop.ts"() {
|
|
15795
|
+
"use strict";
|
|
15796
|
+
init_env();
|
|
15797
|
+
init_paths();
|
|
15798
|
+
runMark = (sessionId) => ({ name: RUN_ENV, value: sessionId });
|
|
15799
|
+
stopMark = () => ({ name: STOP_ENV, value: randomUUID5() });
|
|
15800
|
+
markEnv = (env2, mark) => ({ ...env2, [mark.name]: mark.value });
|
|
15801
|
+
WINDOWS3 = process.platform === "win32";
|
|
15802
|
+
KILL_ROUND_MS = 300;
|
|
15803
|
+
KILL_ROUNDS = 3;
|
|
15804
|
+
LIST_PROCS = [
|
|
15805
|
+
"$ErrorActionPreference = 'Stop'",
|
|
15806
|
+
"Get-CimInstance Win32_Process | ForEach-Object { if ($_.CreationDate) { '{0} {1} {2}' -f $_.ProcessId, $_.ParentProcessId, ([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds() } }",
|
|
15807
|
+
"'boot {0}' -f ([DateTimeOffset](Get-CimInstance Win32_OperatingSystem).LastBootUpTime).ToUnixTimeMilliseconds()"
|
|
15808
|
+
].join("; ");
|
|
15809
|
+
running = /* @__PURE__ */ new Map();
|
|
15810
|
+
QUIT_SIGNALS = ["SIGTERM", "SIGINT", "SIGHUP"];
|
|
15811
|
+
listening = /* @__PURE__ */ new Map();
|
|
15812
|
+
interruptIsTheCallers = false;
|
|
15813
|
+
}
|
|
15814
|
+
});
|
|
15815
|
+
|
|
15549
15816
|
// ../cli/src/lib/agent/pause.ts
|
|
15550
15817
|
function deliveryState(delivery, questions) {
|
|
15551
15818
|
const landing = delivery.landing;
|
|
@@ -15734,12 +16001,12 @@ var init_runner = __esm({
|
|
|
15734
16001
|
});
|
|
15735
16002
|
|
|
15736
16003
|
// ../cli/src/lib/agent/rules.ts
|
|
15737
|
-
import
|
|
15738
|
-
import
|
|
16004
|
+
import fs40 from "node:fs";
|
|
16005
|
+
import path44 from "node:path";
|
|
15739
16006
|
function ruleFile(agent) {
|
|
15740
16007
|
if (!agent || hiddenRole(agent)) return "";
|
|
15741
16008
|
try {
|
|
15742
|
-
return
|
|
16009
|
+
return fs40.readFileSync(rulePath(agent), "utf8").trim();
|
|
15743
16010
|
} catch {
|
|
15744
16011
|
return "";
|
|
15745
16012
|
}
|
|
@@ -15754,11 +16021,11 @@ function setAgentRule(agent, text5) {
|
|
|
15754
16021
|
const rule = String(text5 ?? "").trim();
|
|
15755
16022
|
try {
|
|
15756
16023
|
if (!rule) {
|
|
15757
|
-
|
|
16024
|
+
fs40.rmSync(rulePath(agent), { force: true });
|
|
15758
16025
|
return { ok: true };
|
|
15759
16026
|
}
|
|
15760
|
-
|
|
15761
|
-
|
|
16027
|
+
fs40.mkdirSync(RULES, { recursive: true });
|
|
16028
|
+
fs40.writeFileSync(rulePath(agent), `${rule}
|
|
15762
16029
|
`);
|
|
15763
16030
|
return { ok: true };
|
|
15764
16031
|
} catch (err) {
|
|
@@ -15806,7 +16073,7 @@ function migrateFlowRules() {
|
|
|
15806
16073
|
adoptRenamedRules();
|
|
15807
16074
|
let here;
|
|
15808
16075
|
try {
|
|
15809
|
-
here = new Set(
|
|
16076
|
+
here = new Set(fs40.readdirSync(RULES));
|
|
15810
16077
|
} catch {
|
|
15811
16078
|
return [];
|
|
15812
16079
|
}
|
|
@@ -15817,9 +16084,9 @@ function migrateFlowRules() {
|
|
|
15817
16084
|
if (!from.length) continue;
|
|
15818
16085
|
const parts = [ruleFile(owner.name), ...from.map(ruleFile)].filter(Boolean);
|
|
15819
16086
|
try {
|
|
15820
|
-
if (parts.length)
|
|
16087
|
+
if (parts.length) fs40.writeFileSync(rulePath(owner.name), `${parts.join("\n\n")}
|
|
15821
16088
|
`);
|
|
15822
|
-
for (const flow of from)
|
|
16089
|
+
for (const flow of from) fs40.rmSync(rulePath(flow), { force: true });
|
|
15823
16090
|
} catch (err) {
|
|
15824
16091
|
notes.push(`could not move the ${from.join(", ")} rule(s) onto \`${owner.name}\`: ${String(err)}`);
|
|
15825
16092
|
continue;
|
|
@@ -15833,7 +16100,7 @@ function migrateFlowRules() {
|
|
|
15833
16100
|
function adoptRenamedRules() {
|
|
15834
16101
|
let here;
|
|
15835
16102
|
try {
|
|
15836
|
-
here =
|
|
16103
|
+
here = fs40.readdirSync(RULES);
|
|
15837
16104
|
} catch {
|
|
15838
16105
|
return;
|
|
15839
16106
|
}
|
|
@@ -15843,13 +16110,13 @@ function adoptRenamedRules() {
|
|
|
15843
16110
|
const now = RENAMED_ROLES[was] ?? canonicalSpecAgent(was);
|
|
15844
16111
|
if (now === was) continue;
|
|
15845
16112
|
try {
|
|
15846
|
-
if (!
|
|
16113
|
+
if (!fs40.existsSync(rulePath(now))) fs40.renameSync(rulePath(was), rulePath(now));
|
|
15847
16114
|
else if (FOLDED_SPEC_AGENTS.has(was)) {
|
|
15848
16115
|
const kept2 = ruleFile(now);
|
|
15849
16116
|
const carried = ruleFile(was);
|
|
15850
|
-
if (carried && !kept2.includes(carried))
|
|
16117
|
+
if (carried && !kept2.includes(carried)) fs40.writeFileSync(rulePath(now), `${[kept2, carried].filter(Boolean).join("\n\n")}
|
|
15851
16118
|
`);
|
|
15852
|
-
|
|
16119
|
+
fs40.rmSync(rulePath(was), { force: true });
|
|
15853
16120
|
}
|
|
15854
16121
|
} catch {
|
|
15855
16122
|
}
|
|
@@ -15866,7 +16133,7 @@ var init_rules2 = __esm({
|
|
|
15866
16133
|
init_runner();
|
|
15867
16134
|
init_workflows();
|
|
15868
16135
|
init_types3();
|
|
15869
|
-
rulePath = (agent) =>
|
|
16136
|
+
rulePath = (agent) => path44.join(RULES, `${agent}.md`);
|
|
15870
16137
|
notOnTheRoster = (name) => `"${name}" is not an agent on this board. It has: ${agentNames().join(", ")}.`;
|
|
15871
16138
|
flowForRequest = (req) => req.refineRound !== void 0 && REFINE_ACTIONS.has(req.action) ? flowByCommand("refine") : flowByAction(req.action);
|
|
15872
16139
|
ruleOwnerSays = (owner) => `\`${owner.name}\` agent carries one rule of its own${owner.role ? ", on every flow it runs" : ""}`;
|
|
@@ -15951,8 +16218,8 @@ var init_source = __esm({
|
|
|
15951
16218
|
});
|
|
15952
16219
|
|
|
15953
16220
|
// ../cli/src/lib/card-sources.ts
|
|
15954
|
-
import
|
|
15955
|
-
import
|
|
16221
|
+
import fs41 from "node:fs";
|
|
16222
|
+
import path45 from "node:path";
|
|
15956
16223
|
function planById(id) {
|
|
15957
16224
|
const found2 = readPlan(`plans/${id}-.md`);
|
|
15958
16225
|
return found2?.text.trim() ? found2 : null;
|
|
@@ -15962,8 +16229,8 @@ function cardLink(id) {
|
|
|
15962
16229
|
const found2 = open3 ?? locateArchived(id);
|
|
15963
16230
|
if (!found2) return null;
|
|
15964
16231
|
try {
|
|
15965
|
-
const file = found2.kind === "group" ?
|
|
15966
|
-
const title = parseFrontmatter(
|
|
16232
|
+
const file = found2.kind === "group" ? path45.join(found2.target, "root.md") : found2.target;
|
|
16233
|
+
const title = parseFrontmatter(fs41.readFileSync(file, "utf8")).meta?.title ?? "";
|
|
15967
16234
|
return { kind: "card", ref: String(id), title, ...open3 ? {} : { archived: true } };
|
|
15968
16235
|
} catch {
|
|
15969
16236
|
return null;
|
|
@@ -15988,7 +16255,7 @@ function link(ref, items) {
|
|
|
15988
16255
|
if (ref.kind === "plan") return planLink(ref.id);
|
|
15989
16256
|
if (ref.kind === "card") return cardLink(ref.id);
|
|
15990
16257
|
if (ref.kind === "url") return urlLink(ref.url);
|
|
15991
|
-
const item = items().find((signal2) =>
|
|
16258
|
+
const item = items().find((signal2) => path45.basename(signal2.relPath) === ref.file);
|
|
15992
16259
|
return item ? itemLink(item) : null;
|
|
15993
16260
|
}
|
|
15994
16261
|
function cardSources(meta, body) {
|
|
@@ -16029,12 +16296,12 @@ var init_card_sources = __esm({
|
|
|
16029
16296
|
});
|
|
16030
16297
|
|
|
16031
16298
|
// ../cli/src/lib/view/read.ts
|
|
16032
|
-
import
|
|
16033
|
-
import
|
|
16299
|
+
import fs42 from "node:fs";
|
|
16300
|
+
import path46 from "node:path";
|
|
16034
16301
|
function readCard(file, relFromTodo) {
|
|
16035
|
-
const id = idPrefix(
|
|
16302
|
+
const id = idPrefix(path46.basename(relFromTodo));
|
|
16036
16303
|
if (id === null) {
|
|
16037
|
-
const parts = relFromTodo.split(
|
|
16304
|
+
const parts = relFromTodo.split(path46.sep);
|
|
16038
16305
|
const folderId = idPrefix(parts[parts.length - 2] || "");
|
|
16039
16306
|
if (folderId === null) return null;
|
|
16040
16307
|
return buildCard(folderId, file, relFromTodo);
|
|
@@ -16042,10 +16309,10 @@ function readCard(file, relFromTodo) {
|
|
|
16042
16309
|
return buildCard(id, file, relFromTodo);
|
|
16043
16310
|
}
|
|
16044
16311
|
function buildCard(id, file, relFromTodo) {
|
|
16045
|
-
const text5 =
|
|
16312
|
+
const text5 = fs42.readFileSync(file, "utf8");
|
|
16046
16313
|
const { meta, body } = parseFrontmatter(text5);
|
|
16047
16314
|
if (!meta) return null;
|
|
16048
|
-
const relPath = relFromTodo.split(
|
|
16315
|
+
const relPath = relFromTodo.split(path46.sep).join("/");
|
|
16049
16316
|
const flow = workflowFor(meta.workflow);
|
|
16050
16317
|
const plan = !!meta.workflow && archivesOnApproval(flow);
|
|
16051
16318
|
const stages = cardStages(meta, flow).length;
|
|
@@ -16054,7 +16321,7 @@ function buildCard(id, file, relFromTodo) {
|
|
|
16054
16321
|
// Derived from the file exactly as it is on disk (lib/board/revision.ts), so a card
|
|
16055
16322
|
// read here and a card read by the contract agree on what version this is.
|
|
16056
16323
|
revision: revisionOf(text5),
|
|
16057
|
-
updatedAt:
|
|
16324
|
+
updatedAt: fs42.statSync(file).mtime.toISOString(),
|
|
16058
16325
|
relPath,
|
|
16059
16326
|
title: meta.title,
|
|
16060
16327
|
priority: meta.priority,
|
|
@@ -16075,22 +16342,22 @@ function buildCard(id, file, relFromTodo) {
|
|
|
16075
16342
|
};
|
|
16076
16343
|
}
|
|
16077
16344
|
function readGroup2(folderName) {
|
|
16078
|
-
const groupDir =
|
|
16079
|
-
const rootFile =
|
|
16080
|
-
if (!
|
|
16081
|
-
const root = readCard(rootFile,
|
|
16345
|
+
const groupDir = path46.join(TODO, folderName);
|
|
16346
|
+
const rootFile = path46.join(groupDir, "root.md");
|
|
16347
|
+
if (!fs42.existsSync(rootFile)) return null;
|
|
16348
|
+
const root = readCard(rootFile, path46.join(folderName, "root.md"));
|
|
16082
16349
|
if (!root) return null;
|
|
16083
16350
|
root.isGroup = true;
|
|
16084
16351
|
const { total, resolved: resolved2 } = subtaskLines(root.body);
|
|
16085
16352
|
root.subtaskLines = { total, resolved: resolved2 };
|
|
16086
16353
|
const subCards = [];
|
|
16087
16354
|
const recurse = (dir, relDir) => {
|
|
16088
|
-
for (const entry of
|
|
16089
|
-
const childRel =
|
|
16355
|
+
for (const entry of fs42.readdirSync(dir, { withFileTypes: true })) {
|
|
16356
|
+
const childRel = path46.join(relDir, entry.name);
|
|
16090
16357
|
if (entry.isDirectory()) {
|
|
16091
|
-
recurse(
|
|
16358
|
+
recurse(path46.join(dir, entry.name), childRel);
|
|
16092
16359
|
} else if (entry.name.endsWith(".md") && entry.name !== "README.md" && entry.name !== "root.md") {
|
|
16093
|
-
const c = readCard(
|
|
16360
|
+
const c = readCard(path46.join(dir, entry.name), childRel);
|
|
16094
16361
|
if (c) {
|
|
16095
16362
|
c.parent = { id: root.id, title: root.title };
|
|
16096
16363
|
subCards.push(c);
|
|
@@ -16111,12 +16378,12 @@ function readGroup2(folderName) {
|
|
|
16111
16378
|
return { root, subCards };
|
|
16112
16379
|
}
|
|
16113
16380
|
function standaloneCards(dirRel) {
|
|
16114
|
-
const dir = dirRel ?
|
|
16115
|
-
if (!
|
|
16381
|
+
const dir = dirRel ? path46.join(TODO, dirRel) : TODO;
|
|
16382
|
+
if (!fs42.existsSync(dir)) return [];
|
|
16116
16383
|
const cards = [];
|
|
16117
|
-
for (const entry of
|
|
16384
|
+
for (const entry of fs42.readdirSync(dir, { withFileTypes: true })) {
|
|
16118
16385
|
if (entry.isFile() && entry.name.endsWith(".md") && entry.name !== "README.md") {
|
|
16119
|
-
const c = readCard(
|
|
16386
|
+
const c = readCard(path46.join(dir, entry.name), path46.join(dirRel, entry.name));
|
|
16120
16387
|
if (c) cards.push(c);
|
|
16121
16388
|
}
|
|
16122
16389
|
}
|
|
@@ -16128,7 +16395,7 @@ function collectCards() {
|
|
|
16128
16395
|
const top = standaloneCards("");
|
|
16129
16396
|
board2.push(...top);
|
|
16130
16397
|
every.push(...top);
|
|
16131
|
-
for (const entry of
|
|
16398
|
+
for (const entry of fs42.readdirSync(TODO, { withFileTypes: true })) {
|
|
16132
16399
|
if (!entry.isDirectory()) continue;
|
|
16133
16400
|
if (isGroupFolder(entry.name)) {
|
|
16134
16401
|
const g = readGroup2(entry.name);
|
|
@@ -16160,7 +16427,7 @@ function findCard(id) {
|
|
|
16160
16427
|
}
|
|
16161
16428
|
function attachSources(card) {
|
|
16162
16429
|
try {
|
|
16163
|
-
const { meta, body } = parseFrontmatter(
|
|
16430
|
+
const { meta, body } = parseFrontmatter(fs42.readFileSync(path46.join(TODO, card.relPath), "utf8"));
|
|
16164
16431
|
const sources = meta ? cardSources(meta, body) : [];
|
|
16165
16432
|
if (sources.length) card.sources = sources;
|
|
16166
16433
|
} catch {
|
|
@@ -16348,8 +16615,8 @@ var init_review = __esm({
|
|
|
16348
16615
|
});
|
|
16349
16616
|
|
|
16350
16617
|
// ../cli/src/lib/agent/deliveries.ts
|
|
16351
|
-
import
|
|
16352
|
-
import
|
|
16618
|
+
import fs43 from "node:fs";
|
|
16619
|
+
import path47 from "node:path";
|
|
16353
16620
|
function writeAudit(delivery, runs) {
|
|
16354
16621
|
const known2 = new Map(readAudit(delivery.deliveryId)?.sessions.map((s) => [s.sessionId, s]) ?? []);
|
|
16355
16622
|
const sessions = delivery.sessions.flatMap((id) => {
|
|
@@ -16377,10 +16644,10 @@ function writeAudit(delivery, runs) {
|
|
|
16377
16644
|
}
|
|
16378
16645
|
function writeAuditFile(record) {
|
|
16379
16646
|
try {
|
|
16380
|
-
|
|
16647
|
+
fs43.mkdirSync(DELIVERIES, { recursive: true });
|
|
16381
16648
|
const tmp = `${auditPath(record.deliveryId)}.tmp`;
|
|
16382
|
-
|
|
16383
|
-
|
|
16649
|
+
fs43.writeFileSync(tmp, JSON.stringify(record, null, 2) + "\n");
|
|
16650
|
+
fs43.renameSync(tmp, auditPath(record.deliveryId));
|
|
16384
16651
|
return true;
|
|
16385
16652
|
} catch {
|
|
16386
16653
|
return false;
|
|
@@ -16388,7 +16655,7 @@ function writeAuditFile(record) {
|
|
|
16388
16655
|
}
|
|
16389
16656
|
function readAudit(deliveryId) {
|
|
16390
16657
|
try {
|
|
16391
|
-
const data = JSON.parse(
|
|
16658
|
+
const data = JSON.parse(fs43.readFileSync(auditPath(deliveryId), "utf8"));
|
|
16392
16659
|
return { sessions: Array.isArray(data?.sessions) ? data.sessions : [] };
|
|
16393
16660
|
} catch {
|
|
16394
16661
|
return null;
|
|
@@ -16403,7 +16670,7 @@ function syncAudit(deliveryId, just) {
|
|
|
16403
16670
|
}
|
|
16404
16671
|
function wholeAudit(file) {
|
|
16405
16672
|
try {
|
|
16406
|
-
const data = JSON.parse(
|
|
16673
|
+
const data = JSON.parse(fs43.readFileSync(file, "utf8"));
|
|
16407
16674
|
return typeof data?.deliveryId === "string" && Array.isArray(data.sessions) ? data : null;
|
|
16408
16675
|
} catch {
|
|
16409
16676
|
return null;
|
|
@@ -16416,14 +16683,14 @@ function activeAudit(file) {
|
|
|
16416
16683
|
function allAudits() {
|
|
16417
16684
|
let names4;
|
|
16418
16685
|
try {
|
|
16419
|
-
names4 =
|
|
16686
|
+
names4 = fs43.readdirSync(DELIVERIES);
|
|
16420
16687
|
} catch {
|
|
16421
16688
|
return [];
|
|
16422
16689
|
}
|
|
16423
16690
|
const out = [];
|
|
16424
16691
|
for (const name of names4) {
|
|
16425
16692
|
if (!name.endsWith(".json")) continue;
|
|
16426
|
-
const audit = wholeAudit(
|
|
16693
|
+
const audit = wholeAudit(path47.join(DELIVERIES, name));
|
|
16427
16694
|
if (audit) out.push(audit);
|
|
16428
16695
|
}
|
|
16429
16696
|
return out;
|
|
@@ -16534,11 +16801,11 @@ function resumeRefusal(delivery, store) {
|
|
|
16534
16801
|
);
|
|
16535
16802
|
}
|
|
16536
16803
|
if (!worktreeExists(delivery.worktree)) {
|
|
16537
|
-
const
|
|
16804
|
+
const path87 = delivery.worktree ?? "";
|
|
16538
16805
|
return refusal(
|
|
16539
16806
|
"deliveryWorktreeGone",
|
|
16540
|
-
`delivery ${id}'s worktree ${
|
|
16541
|
-
{ id, task, path:
|
|
16807
|
+
`delivery ${id}'s worktree ${path87} is gone, so there is nothing to carry on. ${rebuild}`,
|
|
16808
|
+
{ id, task, path: path87, command: discard }
|
|
16542
16809
|
);
|
|
16543
16810
|
}
|
|
16544
16811
|
if (!branchExists(delivery.branch)) {
|
|
@@ -16591,11 +16858,12 @@ function tidyCheckout(deliveryId) {
|
|
|
16591
16858
|
const store = readStore();
|
|
16592
16859
|
const row = store.deliveries.find((d) => d.deliveryId === deliveryId) ?? auditOnly(deliveryId);
|
|
16593
16860
|
if (!row || row.status === "active") return NOTHING;
|
|
16594
|
-
if (!row.worktree && !row.branch) return NOTHING;
|
|
16595
|
-
if (isResumable(row)) return { removed: false, kept: true };
|
|
16596
16861
|
if (store.runs.some((r) => r.deliveryId === deliveryId && runIsLive(r))) {
|
|
16597
16862
|
return { removed: false, kept: false, error: `a run of delivery ${deliveryId} is still going`, reason: "deliveryRunGoing", args: { id: deliveryId } };
|
|
16598
16863
|
}
|
|
16864
|
+
if (row.status !== "finished" || Date.now() - (row.endedAt ?? 0) > 3e4) stopWatchers(row.sessions);
|
|
16865
|
+
if (!row.worktree && !row.branch) return NOTHING;
|
|
16866
|
+
if (isResumable(row)) return { removed: false, kept: true };
|
|
16599
16867
|
const removed = removeWorktree(row.worktree, row.branch, true);
|
|
16600
16868
|
if (!removed.ok) return { removed: false, kept: false, error: removed.error };
|
|
16601
16869
|
forgetCheckout(deliveryId);
|
|
@@ -16618,7 +16886,10 @@ function forgetCheckout(deliveryId) {
|
|
|
16618
16886
|
function sweepCheckouts() {
|
|
16619
16887
|
const seen = /* @__PURE__ */ new Set();
|
|
16620
16888
|
const complaints = [];
|
|
16621
|
-
const
|
|
16889
|
+
const store = readStore();
|
|
16890
|
+
const rows = [...store.deliveries, ...allAudits().flatMap((a) => auditRow(a) ?? [])];
|
|
16891
|
+
const ended = rows.filter((row) => row.status !== "active" && (row.status !== "finished" || Date.now() - (row.endedAt ?? 0) > 3e4) && !store.runs.some((r) => r.deliveryId === row.deliveryId && runIsLive(r)));
|
|
16892
|
+
stopWatchers([...new Set(ended.flatMap((row) => row.sessions))]);
|
|
16622
16893
|
for (const row of rows) {
|
|
16623
16894
|
if (seen.has(row.deliveryId) || row.status === "active" || !row.worktree) continue;
|
|
16624
16895
|
seen.add(row.deliveryId);
|
|
@@ -16671,10 +16942,10 @@ function carryOnFrom(deliveryId) {
|
|
|
16671
16942
|
function approvedRequirements(cardId2) {
|
|
16672
16943
|
const found2 = locate(cardId2);
|
|
16673
16944
|
if (!found2) return "";
|
|
16674
|
-
const file = found2.kind === "group" ?
|
|
16945
|
+
const file = found2.kind === "group" ? path47.join(found2.target, "root.md") : found2.target;
|
|
16675
16946
|
let text5;
|
|
16676
16947
|
try {
|
|
16677
|
-
text5 =
|
|
16948
|
+
text5 = fs43.readFileSync(file, "utf8");
|
|
16678
16949
|
} catch {
|
|
16679
16950
|
return "";
|
|
16680
16951
|
}
|
|
@@ -16704,8 +16975,8 @@ function cardMeta2(cardId2) {
|
|
|
16704
16975
|
try {
|
|
16705
16976
|
const found2 = locate(cardId2);
|
|
16706
16977
|
if (!found2) return null;
|
|
16707
|
-
const file = found2.kind === "group" ?
|
|
16708
|
-
return parseFrontmatter(
|
|
16978
|
+
const file = found2.kind === "group" ? path47.join(found2.target, "root.md") : found2.target;
|
|
16979
|
+
return parseFrontmatter(fs43.readFileSync(file, "utf8")).meta;
|
|
16709
16980
|
} catch {
|
|
16710
16981
|
return null;
|
|
16711
16982
|
}
|
|
@@ -16954,6 +17225,7 @@ var init_deliveries = __esm({
|
|
|
16954
17225
|
init_complete();
|
|
16955
17226
|
init_outputs();
|
|
16956
17227
|
init_env();
|
|
17228
|
+
init_stop();
|
|
16957
17229
|
init_flows();
|
|
16958
17230
|
init_pause();
|
|
16959
17231
|
init_rules2();
|
|
@@ -16963,7 +17235,7 @@ var init_deliveries = __esm({
|
|
|
16963
17235
|
init_store();
|
|
16964
17236
|
init_types3();
|
|
16965
17237
|
init_worktree();
|
|
16966
|
-
auditPath = (deliveryId) =>
|
|
17238
|
+
auditPath = (deliveryId) => path47.join(DELIVERIES, `${deliveryId}.json`);
|
|
16967
17239
|
activeAudits = () => allAudits().filter((a) => a.status === "active");
|
|
16968
17240
|
auditRow = (audit) => readDeliveryRow({ ...audit, sessions: audit.sessions.map((s) => s.sessionId) });
|
|
16969
17241
|
lastSessionRunning = (audit) => audit.sessions[audit.sessions.length - 1]?.status === "running";
|
|
@@ -16991,9 +17263,44 @@ var init_deliveries = __esm({
|
|
|
16991
17263
|
}
|
|
16992
17264
|
});
|
|
16993
17265
|
|
|
16994
|
-
// ../cli/src/lib/agent/
|
|
16995
|
-
|
|
16996
|
-
|
|
17266
|
+
// ../cli/src/lib/agent/landing-lock.ts
|
|
17267
|
+
import fs44 from "node:fs";
|
|
17268
|
+
import path48 from "node:path";
|
|
17269
|
+
function lockLanding() {
|
|
17270
|
+
const dir = path48.join(SESSIONS_DIR, ".landing.lock");
|
|
17271
|
+
try {
|
|
17272
|
+
const entries = fs44.readdirSync(dir);
|
|
17273
|
+
const token3 = entries.length === 1 ? entries[0] : "";
|
|
17274
|
+
const match = /^(\d+)-[a-f0-9-]{36}$/.exec(token3);
|
|
17275
|
+
const stat = fs44.statSync(dir);
|
|
17276
|
+
if (match && Date.now() - stat.mtimeMs > landingLimits.staleMs) {
|
|
17277
|
+
stopWatchers(void 0, Number(match[1]), () => {
|
|
17278
|
+
const now = fs44.readdirSync(dir);
|
|
17279
|
+
return now.length === 1 && now[0] === token3 && Date.now() - fs44.statSync(dir).mtimeMs > landingLimits.staleMs;
|
|
17280
|
+
}, stat.birthtimeMs || stat.mtimeMs);
|
|
17281
|
+
}
|
|
17282
|
+
} catch {
|
|
17283
|
+
}
|
|
17284
|
+
return tryLock(dir);
|
|
17285
|
+
}
|
|
17286
|
+
function renewLandingLock() {
|
|
17287
|
+
const now = /* @__PURE__ */ new Date();
|
|
17288
|
+
fs44.utimesSync(path48.join(SESSIONS_DIR, ".landing.lock"), now, now);
|
|
17289
|
+
}
|
|
17290
|
+
var landingLimits;
|
|
17291
|
+
var init_landing_lock = __esm({
|
|
17292
|
+
"../cli/src/lib/agent/landing-lock.ts"() {
|
|
17293
|
+
"use strict";
|
|
17294
|
+
init_lock();
|
|
17295
|
+
init_paths();
|
|
17296
|
+
init_stop();
|
|
17297
|
+
landingLimits = { staleMs: 5 * 6e4 };
|
|
17298
|
+
}
|
|
17299
|
+
});
|
|
17300
|
+
|
|
17301
|
+
// ../cli/src/lib/agent/answers.ts
|
|
17302
|
+
function answerOutcome(delivery) {
|
|
17303
|
+
return pendingAnswers(delivery).some((a) => a.outcome === "changed") ? "changed" : "unchanged";
|
|
16997
17304
|
}
|
|
16998
17305
|
function takeUnchanged(delivery) {
|
|
16999
17306
|
if (!pendingAnswers(delivery).some((a) => a.outcome === "unchanged")) return;
|
|
@@ -17055,8 +17362,8 @@ var init_retry = __esm({
|
|
|
17055
17362
|
});
|
|
17056
17363
|
|
|
17057
17364
|
// ../cli/src/lib/agent/landing.ts
|
|
17058
|
-
import
|
|
17059
|
-
import
|
|
17365
|
+
import fs45 from "node:fs";
|
|
17366
|
+
import path49 from "node:path";
|
|
17060
17367
|
function waitingOut(delivery, now = Date.now()) {
|
|
17061
17368
|
const landing = delivery.landing;
|
|
17062
17369
|
if (!landing) return false;
|
|
@@ -17192,8 +17499,9 @@ function noteQueue(held4) {
|
|
|
17192
17499
|
}
|
|
17193
17500
|
async function advanceLanding() {
|
|
17194
17501
|
let release;
|
|
17502
|
+
let current;
|
|
17195
17503
|
try {
|
|
17196
|
-
release =
|
|
17504
|
+
release = lockLanding();
|
|
17197
17505
|
if (!release) return null;
|
|
17198
17506
|
const owed = await owedRestart();
|
|
17199
17507
|
if (owed) return owed;
|
|
@@ -17208,11 +17516,19 @@ async function advanceLanding() {
|
|
|
17208
17516
|
return null;
|
|
17209
17517
|
}
|
|
17210
17518
|
tried.add(picked.deliveryId);
|
|
17519
|
+
current = picked;
|
|
17520
|
+
renewLandingLock();
|
|
17211
17521
|
const step = await landStep(picked);
|
|
17212
17522
|
if (step.start) return step.start;
|
|
17213
17523
|
if (!step.done) return null;
|
|
17214
17524
|
}
|
|
17215
|
-
} catch {
|
|
17525
|
+
} catch (error) {
|
|
17526
|
+
if (current && error instanceof GitTimeoutError) {
|
|
17527
|
+
giveUpSlot(current, error.message);
|
|
17528
|
+
patchLanding(current.deliveryId, (landing) => {
|
|
17529
|
+
landing.retryAt = Date.now() + 3e4;
|
|
17530
|
+
});
|
|
17531
|
+
}
|
|
17216
17532
|
return null;
|
|
17217
17533
|
} finally {
|
|
17218
17534
|
release?.();
|
|
@@ -17529,7 +17845,7 @@ async function settleAlreadyLanded(delivery) {
|
|
|
17529
17845
|
return true;
|
|
17530
17846
|
}
|
|
17531
17847
|
function repairLanding() {
|
|
17532
|
-
const release =
|
|
17848
|
+
const release = lockLanding();
|
|
17533
17849
|
if (!release) return [];
|
|
17534
17850
|
try {
|
|
17535
17851
|
return repairIdleLanding();
|
|
@@ -17562,7 +17878,7 @@ var init_landing = __esm({
|
|
|
17562
17878
|
init_execution();
|
|
17563
17879
|
init_board();
|
|
17564
17880
|
init_io();
|
|
17565
|
-
|
|
17881
|
+
init_landing_lock();
|
|
17566
17882
|
init_paths();
|
|
17567
17883
|
init_answers();
|
|
17568
17884
|
init_commit_mode();
|
|
@@ -17603,9 +17919,9 @@ var init_landing = __esm({
|
|
|
17603
17919
|
sameDir = (a, b) => {
|
|
17604
17920
|
const real2 = (dir) => {
|
|
17605
17921
|
try {
|
|
17606
|
-
return
|
|
17922
|
+
return fs45.realpathSync(dir);
|
|
17607
17923
|
} catch {
|
|
17608
|
-
return
|
|
17924
|
+
return path49.resolve(dir);
|
|
17609
17925
|
}
|
|
17610
17926
|
};
|
|
17611
17927
|
return real2(a) === real2(b);
|
|
@@ -17614,35 +17930,35 @@ var init_landing = __esm({
|
|
|
17614
17930
|
});
|
|
17615
17931
|
|
|
17616
17932
|
// ../cli/src/lib/agent/pictures.ts
|
|
17617
|
-
import
|
|
17618
|
-
import
|
|
17933
|
+
import fs46 from "node:fs";
|
|
17934
|
+
import path50 from "node:path";
|
|
17619
17935
|
function claimRunPictures(box2, sessionId, names4 = []) {
|
|
17620
17936
|
if (!box2 || !freeBox(box2) || names4.length === 0) return [];
|
|
17621
17937
|
const from = pictureBox(box2);
|
|
17622
17938
|
const to = pictureBox(sessionId);
|
|
17623
17939
|
try {
|
|
17624
|
-
|
|
17625
|
-
|
|
17940
|
+
fs46.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
17941
|
+
fs46.renameSync(from, to);
|
|
17626
17942
|
} catch {
|
|
17627
17943
|
return [];
|
|
17628
17944
|
}
|
|
17629
|
-
return names4.filter(pictureName).map((name) =>
|
|
17945
|
+
return names4.filter(pictureName).map((name) => path50.join(to, name)).filter((file) => fs46.existsSync(file));
|
|
17630
17946
|
}
|
|
17631
17947
|
function returnRunPictures(sessionId, box2) {
|
|
17632
17948
|
if (!box2 || !boxId(box2)) return;
|
|
17633
17949
|
try {
|
|
17634
|
-
|
|
17950
|
+
fs46.renameSync(pictureBox(sessionId), pictureBox(box2));
|
|
17635
17951
|
} catch {
|
|
17636
17952
|
}
|
|
17637
17953
|
}
|
|
17638
17954
|
function moveRunPictures(from, to, files2 = []) {
|
|
17639
17955
|
if (files2.length === 0) return [];
|
|
17640
17956
|
try {
|
|
17641
|
-
|
|
17957
|
+
fs46.renameSync(pictureBox(from), pictureBox(to));
|
|
17642
17958
|
} catch {
|
|
17643
17959
|
return files2;
|
|
17644
17960
|
}
|
|
17645
|
-
return files2.map((file) =>
|
|
17961
|
+
return files2.map((file) => path50.join(pictureBox(to), path50.basename(file)));
|
|
17646
17962
|
}
|
|
17647
17963
|
var pictureName, boxId, freeBox, pictureBox;
|
|
17648
17964
|
var init_pictures = __esm({
|
|
@@ -17651,14 +17967,14 @@ var init_pictures = __esm({
|
|
|
17651
17967
|
init_paths();
|
|
17652
17968
|
pictureName = (name) => /^[0-9a-f-]{36}\.[a-z0-9]{2,5}$/.test(name);
|
|
17653
17969
|
boxId = (box2) => /^[0-9a-f-]{36}$/.test(box2);
|
|
17654
|
-
freeBox = (box2) => boxId(box2) && !
|
|
17655
|
-
pictureBox = (box2) =>
|
|
17970
|
+
freeBox = (box2) => boxId(box2) && !fs46.existsSync(path50.join(SESSIONS_DIR, `${box2}.log`));
|
|
17971
|
+
pictureBox = (box2) => path50.join(SESSIONS_DIR, `${box2}.images`);
|
|
17656
17972
|
}
|
|
17657
17973
|
});
|
|
17658
17974
|
|
|
17659
17975
|
// ../cli/src/lib/memories.ts
|
|
17660
|
-
import
|
|
17661
|
-
import
|
|
17976
|
+
import fs47 from "node:fs";
|
|
17977
|
+
import path51 from "node:path";
|
|
17662
17978
|
function readMemory(text5, builtIn, folder) {
|
|
17663
17979
|
const { meta, body } = splitFrontmatter(text5);
|
|
17664
17980
|
if (meta === null) return null;
|
|
@@ -17678,7 +17994,7 @@ function readMemory(text5, builtIn, folder) {
|
|
|
17678
17994
|
rules: body.trim(),
|
|
17679
17995
|
text: text5,
|
|
17680
17996
|
builtIn,
|
|
17681
|
-
dir:
|
|
17997
|
+
dir: path51.join(MEMORY, folder)
|
|
17682
17998
|
};
|
|
17683
17999
|
}
|
|
17684
18000
|
function globalMemories() {
|
|
@@ -17689,14 +18005,14 @@ function globalMemories() {
|
|
|
17689
18005
|
}
|
|
17690
18006
|
let folders2 = [];
|
|
17691
18007
|
try {
|
|
17692
|
-
folders2 =
|
|
18008
|
+
folders2 = fs47.readdirSync(MEMORY, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
17693
18009
|
} catch {
|
|
17694
18010
|
}
|
|
17695
18011
|
for (const name of folders2) {
|
|
17696
18012
|
if (name in BUNDLED_MEMORY_FILES || name === RESERVED_MEMORY_DIR) continue;
|
|
17697
|
-
const file =
|
|
17698
|
-
if (!
|
|
17699
|
-
const memory = readMemory(
|
|
18013
|
+
const file = path51.join(MEMORY, name, GLOBAL_MEMORY_FILE);
|
|
18014
|
+
if (!fs47.existsSync(file)) continue;
|
|
18015
|
+
const memory = readMemory(fs47.readFileSync(file, "utf8"), false, name);
|
|
17700
18016
|
if (memory) out.push(memory);
|
|
17701
18017
|
}
|
|
17702
18018
|
return out;
|
|
@@ -17752,16 +18068,16 @@ var init_facts = __esm({
|
|
|
17752
18068
|
});
|
|
17753
18069
|
|
|
17754
18070
|
// ../cli/src/lib/view/archive.ts
|
|
17755
|
-
import
|
|
17756
|
-
import
|
|
18071
|
+
import fs48 from "node:fs";
|
|
18072
|
+
import path52 from "node:path";
|
|
17757
18073
|
function archivedId(file) {
|
|
17758
|
-
const own = idPrefix(
|
|
17759
|
-
return own !== null ? own : idPrefix(
|
|
18074
|
+
const own = idPrefix(path52.basename(file));
|
|
18075
|
+
return own !== null ? own : idPrefix(path52.basename(path52.dirname(file)));
|
|
17760
18076
|
}
|
|
17761
18077
|
function readFile(file) {
|
|
17762
18078
|
const id = archivedId(file);
|
|
17763
18079
|
if (id === null) return null;
|
|
17764
|
-
const { meta, body } = parseFrontmatter(
|
|
18080
|
+
const { meta, body } = parseFrontmatter(fs48.readFileSync(file, "utf8"));
|
|
17765
18081
|
if (!meta) return null;
|
|
17766
18082
|
return {
|
|
17767
18083
|
row: {
|
|
@@ -17769,15 +18085,15 @@ function readFile(file) {
|
|
|
17769
18085
|
title: meta.title,
|
|
17770
18086
|
archived: meta.archived,
|
|
17771
18087
|
rejected: meta.rejected,
|
|
17772
|
-
relPath: rel(file).split(
|
|
18088
|
+
relPath: rel(file).split(path52.sep).join("/")
|
|
17773
18089
|
},
|
|
17774
18090
|
body: body.replace(/^\n+/, "").replace(/\s+$/, ""),
|
|
17775
18091
|
meta
|
|
17776
18092
|
};
|
|
17777
18093
|
}
|
|
17778
18094
|
function readArchive() {
|
|
17779
|
-
const relPath = rel(ARCHIVE).split(
|
|
17780
|
-
if (!
|
|
18095
|
+
const relPath = rel(ARCHIVE).split(path52.sep).join("/");
|
|
18096
|
+
if (!fs48.existsSync(ARCHIVE)) return { relPath, cards: [] };
|
|
17781
18097
|
const cards = walkMd(ARCHIVE).map(readFile).filter((found2) => found2 !== null).map((found2) => found2.row);
|
|
17782
18098
|
cards.sort((a, b) => b.id - a.id);
|
|
17783
18099
|
return { relPath, cards };
|
|
@@ -17792,7 +18108,7 @@ var init_archive = __esm({
|
|
|
17792
18108
|
});
|
|
17793
18109
|
|
|
17794
18110
|
// ../cli/src/lib/agent/prompts.ts
|
|
17795
|
-
import
|
|
18111
|
+
import path53 from "node:path";
|
|
17796
18112
|
function resumePrompt(deliveryId, cardId2, action) {
|
|
17797
18113
|
if (!deliveryId) return RESUME_PROMPT;
|
|
17798
18114
|
if (action === "conflict") {
|
|
@@ -17836,7 +18152,7 @@ function buildAsk(rawReq, notes = []) {
|
|
|
17836
18152
|
}
|
|
17837
18153
|
function cardFileOf(id) {
|
|
17838
18154
|
const found2 = id === void 0 ? null : locate(id);
|
|
17839
|
-
return found2 ? found2.kind === "group" ?
|
|
18155
|
+
return found2 ? found2.kind === "group" ? path53.join(found2.target, "root.md") : found2.target : "";
|
|
17840
18156
|
}
|
|
17841
18157
|
function realPaths(text5, req) {
|
|
17842
18158
|
if (req.action !== "scheduled" && req.action !== "sub") return text5;
|
|
@@ -17929,7 +18245,7 @@ function frozenRules(req) {
|
|
|
17929
18245
|
function archivedCardFile(id) {
|
|
17930
18246
|
const found2 = locateArchived(id);
|
|
17931
18247
|
if (!found2) return `${rel(ARCHIVE)}/`;
|
|
17932
|
-
return rel(found2.kind === "group" ?
|
|
18248
|
+
return rel(found2.kind === "group" ? path53.join(found2.target, "root.md") : found2.target);
|
|
17933
18249
|
}
|
|
17934
18250
|
function shippedLines(cardId2) {
|
|
17935
18251
|
const delivery = endedDelivery(`${cardId2}`);
|
|
@@ -18100,7 +18416,7 @@ function actionPrompt(req, command, notes) {
|
|
|
18100
18416
|
case "spec": {
|
|
18101
18417
|
const agent = findSpecAgent(req.specAgent ?? "");
|
|
18102
18418
|
const found2 = req.id === void 0 ? null : locate(req.id);
|
|
18103
|
-
const cardFile3 = found2 ? rel(found2.kind === "group" ?
|
|
18419
|
+
const cardFile3 = found2 ? rel(found2.kind === "group" ? path53.join(found2.target, "root.md") : found2.target) : `task #${req.id}`;
|
|
18104
18420
|
const own = agent ? specAgentInstructions(agent) : null;
|
|
18105
18421
|
if (own) notes.push(...own.notes);
|
|
18106
18422
|
const memory = agent ? agentMemoryBlock(agent) : "";
|
|
@@ -18211,290 +18527,83 @@ ${shared}` : ""
|
|
|
18211
18527
|
return [
|
|
18212
18528
|
`${kb}. Task ${req.id} ${named2} has been approved: write the previews that were waiting on that Approve from the card as it stands.`,
|
|
18213
18529
|
req.notes ? `Extra notes: ${req.notes}` : "",
|
|
18214
|
-
`Don't ask me questions with human-in-the-loop. Leave any questions as open questions.`
|
|
18215
|
-
].filter(Boolean).join(" ");
|
|
18216
|
-
case "resolve":
|
|
18217
|
-
return [
|
|
18218
|
-
`${kb}. Apply my answers to the open questions on task ${req.id} ${named2} following \`akb guide resolve\`.`,
|
|
18219
|
-
carriedNote(req),
|
|
18220
|
-
answeredNote(req.id, command),
|
|
18221
|
-
req.notes ? `Extra notes: ${req.notes}` : ""
|
|
18222
|
-
].filter(Boolean).join(" ");
|
|
18223
|
-
}
|
|
18224
|
-
}
|
|
18225
|
-
function discardedCardsPrompt(cards = []) {
|
|
18226
|
-
if (!cards.length) return "";
|
|
18227
|
-
return "The user discarded the cards listed below. Their absence is intentional. Do not restore or recreate them, including under new IDs or filenames. Continue the remaining work from the current board, preserving the other cards.\n\n" + cards.map((c) => `- #${c.id}: ${c.path}`).join("\n");
|
|
18228
|
-
}
|
|
18229
|
-
var RESUME_PROMPT, DELIVERY_RESUME, DELIVERY_RESUME_CARD, RESTARTABLE, RESTART_LEAD, restartable, NO_IMPLEMENT, BOARD_FOLDER, CARD_FILE, PROJECT_FOLDER, PLANNING_FOR, SPEC_SELECTOR_FOR, reflectedCards;
|
|
18230
|
-
var init_prompts = __esm({
|
|
18231
|
-
"../cli/src/lib/agent/prompts.ts"() {
|
|
18232
|
-
"use strict";
|
|
18233
|
-
init_cards();
|
|
18234
|
-
init_memory();
|
|
18235
|
-
init_memories();
|
|
18236
|
-
init_paths();
|
|
18237
|
-
init_agents();
|
|
18238
|
-
init_command();
|
|
18239
|
-
init_deliveries();
|
|
18240
|
-
init_env();
|
|
18241
|
-
init_facts();
|
|
18242
|
-
init_outputs();
|
|
18243
|
-
init_archive();
|
|
18244
|
-
init_flows();
|
|
18245
|
-
init_resolve();
|
|
18246
|
-
init_runner();
|
|
18247
|
-
init_workflows();
|
|
18248
|
-
init_catalog2();
|
|
18249
|
-
init_roles();
|
|
18250
|
-
init_rules2();
|
|
18251
|
-
init_settings2();
|
|
18252
|
-
init_types3();
|
|
18253
|
-
RESUME_PROMPT = [
|
|
18254
|
-
`Continue. The previous run ended before it finished the task.`,
|
|
18255
|
-
`Pick up where you left off and carry it through.`
|
|
18256
|
-
].join(" ");
|
|
18257
|
-
DELIVERY_RESUME = [
|
|
18258
|
-
`Continue delivery %s. The previous run ended before the delivery finished.`,
|
|
18259
|
-
`Re-enter the flow from the top and check each step's precondition before you do it \u2014 work that is already done is done, so do not repeat it.`
|
|
18260
|
-
].join(" ");
|
|
18261
|
-
DELIVERY_RESUME_CARD = `Build the card as the delivery holds it, not as the file reads now: \`%c\` prints the approved copy.`;
|
|
18262
|
-
RESTARTABLE = /* @__PURE__ */ new Set([
|
|
18263
|
-
"implement",
|
|
18264
|
-
"clarify",
|
|
18265
|
-
"resolve",
|
|
18266
|
-
"approve",
|
|
18267
|
-
"archive",
|
|
18268
|
-
"spec",
|
|
18269
|
-
"conflict",
|
|
18270
|
-
"scheduled"
|
|
18271
|
-
]);
|
|
18272
|
-
RESTART_LEAD = [
|
|
18273
|
-
`The session you were continuing is gone, and this one holds nothing of it.`,
|
|
18274
|
-
`Do the task below from the top, checking each step's precondition before you do it \u2014 work an earlier run already finished is done, so don't repeat it.`
|
|
18275
|
-
].join(" ");
|
|
18276
|
-
restartable = (run2) => (run2.cardId !== null || !!run2.deliveryId) && RESTARTABLE.has(run2.action);
|
|
18277
|
-
NO_IMPLEMENT = `(Unless the request explicitly asks for implementation, don't implement it; revising the card's drafts is not implementing.)`;
|
|
18278
|
-
BOARD_FOLDER = "<board folder>";
|
|
18279
|
-
CARD_FILE = "<card file>";
|
|
18280
|
-
PROJECT_FOLDER = "<project folder>";
|
|
18281
|
-
PLANNING_FOR = /* @__PURE__ */ new Set(["create", "clarify", "resolve", "approve", "edit"]);
|
|
18282
|
-
SPEC_SELECTOR_FOR = /* @__PURE__ */ new Set(["clarify", "resolve", "approve", "edit"]);
|
|
18283
|
-
reflectedCards = (req) => req.cards ?? (req.id !== void 0 ? [req.id] : []);
|
|
18284
|
-
}
|
|
18285
|
-
});
|
|
18286
|
-
|
|
18287
|
-
// ../cli/src/lib/agent/stop.ts
|
|
18288
|
-
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
18289
|
-
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
18290
|
-
import fs47 from "node:fs";
|
|
18291
|
-
import os6 from "node:os";
|
|
18292
|
-
function markedInProc(entry, strict = false) {
|
|
18293
|
-
const pids = [];
|
|
18294
|
-
for (const name of fs47.readdirSync("/proc")) {
|
|
18295
|
-
if (!/^\d+$/.test(name)) continue;
|
|
18296
|
-
try {
|
|
18297
|
-
if (fs47.readFileSync(`/proc/${name}/environ`, "utf8").split("\0").includes(entry)) pids.push(Number(name));
|
|
18298
|
-
} catch (error) {
|
|
18299
|
-
if (strict && !["ENOENT", "ESRCH"].includes(error.code ?? "")) {
|
|
18300
|
-
const stat = fs47.statSync(`/proc/${name}`, { throwIfNoEntry: false });
|
|
18301
|
-
if (stat?.uid === process.getuid?.()) throw error;
|
|
18302
|
-
}
|
|
18303
|
-
}
|
|
18304
|
-
}
|
|
18305
|
-
return pids;
|
|
18306
|
-
}
|
|
18307
|
-
function markedInPs(entry, roots) {
|
|
18308
|
-
const ps = spawnSync4("ps", ["-A", "-E", "-ww", "-o", "pid=,ppid=,command="], {
|
|
18309
|
-
encoding: "utf8",
|
|
18310
|
-
maxBuffer: 64 * 1024 * 1024,
|
|
18311
|
-
timeout: 5e3
|
|
18312
|
-
});
|
|
18313
|
-
if (ps.status !== 0 || !ps.stdout) throw new Error("\u65E0\u6CD5\u67E5\u8BE2\u8FDB\u7A0B\u6811\u3002");
|
|
18314
|
-
const children = /* @__PURE__ */ new Map();
|
|
18315
|
-
const listed2 = /* @__PURE__ */ new Set();
|
|
18316
|
-
const found2 = /* @__PURE__ */ new Set();
|
|
18317
|
-
for (const line of ps.stdout.split("\n")) {
|
|
18318
|
-
const m = /^\s*(\d+)\s+(\d+)\s(.*)$/.exec(line);
|
|
18319
|
-
if (!m) continue;
|
|
18320
|
-
const pid = Number(m[1]);
|
|
18321
|
-
const ppid = Number(m[2]);
|
|
18322
|
-
listed2.add(pid);
|
|
18323
|
-
children.set(ppid, [...children.get(ppid) ?? [], pid]);
|
|
18324
|
-
if (`${m[3]} `.includes(` ${entry} `)) found2.add(pid);
|
|
18325
|
-
}
|
|
18326
|
-
const queue = [...found2, ...roots.filter((pid) => listed2.has(pid))];
|
|
18327
|
-
for (const pid of queue) {
|
|
18328
|
-
found2.add(pid);
|
|
18329
|
-
for (const child of children.get(pid) ?? []) if (!found2.has(child)) queue.push(child);
|
|
18330
|
-
}
|
|
18331
|
-
return [...found2];
|
|
18332
|
-
}
|
|
18333
|
-
function marked(mark, roots = []) {
|
|
18334
|
-
if (WINDOWS3 || !mark.value) return [];
|
|
18335
|
-
const entry = `${mark.name}=${mark.value}`;
|
|
18336
|
-
try {
|
|
18337
|
-
const pids = process.platform === "linux" ? markedInProc(entry) : markedInPs(entry, roots);
|
|
18338
|
-
return pids.filter((pid) => pid !== process.pid);
|
|
18339
|
-
} catch {
|
|
18340
|
-
return [];
|
|
18341
|
-
}
|
|
18342
|
-
}
|
|
18343
|
-
function signal(pids, sig) {
|
|
18344
|
-
for (const pid of pids) {
|
|
18345
|
-
try {
|
|
18346
|
-
process.kill(pid, sig);
|
|
18347
|
-
} catch {
|
|
18348
|
-
}
|
|
18349
|
-
}
|
|
18350
|
-
}
|
|
18351
|
-
function startedUnder(list, root, since) {
|
|
18352
|
-
const self = list.procs.find((p) => p.pid === root);
|
|
18353
|
-
const from = since ?? self?.startedAt;
|
|
18354
|
-
if (from === void 0 || list.bootedAt > from) return [];
|
|
18355
|
-
if (self && self.startedAt > from) return [];
|
|
18356
|
-
const found2 = self ? [root] : [];
|
|
18357
|
-
const seen = /* @__PURE__ */ new Set([root]);
|
|
18358
|
-
const queue = [{ pid: root, startedAt: from }];
|
|
18359
|
-
for (const parent of queue) {
|
|
18360
|
-
for (const p of list.procs) {
|
|
18361
|
-
if (p.ppid !== parent.pid || seen.has(p.pid) || p.startedAt < parent.startedAt) continue;
|
|
18362
|
-
seen.add(p.pid);
|
|
18363
|
-
found2.push(p.pid);
|
|
18364
|
-
queue.push(p);
|
|
18365
|
-
}
|
|
18366
|
-
}
|
|
18367
|
-
return found2.reverse();
|
|
18368
|
-
}
|
|
18369
|
-
function readProcList(out) {
|
|
18370
|
-
const procs = [];
|
|
18371
|
-
let bootedAt;
|
|
18372
|
-
for (const line of out.split(/\r?\n/)) {
|
|
18373
|
-
const boot = /^boot (\d+)$/.exec(line.trim());
|
|
18374
|
-
if (boot) bootedAt = Number(boot[1]);
|
|
18375
|
-
const m = /^(\d+) (\d+) (\d+)$/.exec(line.trim());
|
|
18376
|
-
if (m) procs.push({ pid: Number(m[1]), ppid: Number(m[2]), startedAt: Number(m[3]) });
|
|
18377
|
-
}
|
|
18378
|
-
return bootedAt !== void 0 && procs.length ? { procs, bootedAt } : void 0;
|
|
18379
|
-
}
|
|
18380
|
-
function procsOnWindows() {
|
|
18381
|
-
const ps = spawnSync4("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", LIST_PROCS], {
|
|
18382
|
-
encoding: "utf8",
|
|
18383
|
-
maxBuffer: 64 * 1024 * 1024,
|
|
18384
|
-
timeout: 5e3,
|
|
18385
|
-
windowsHide: true
|
|
18386
|
-
});
|
|
18387
|
-
return ps.status === 0 && ps.stdout ? readProcList(ps.stdout) : void 0;
|
|
18388
|
-
}
|
|
18389
|
-
function killUnderOnWindows(pid, since) {
|
|
18390
|
-
if (!WINDOWS3 || !pid) return;
|
|
18391
|
-
const list = procsOnWindows();
|
|
18392
|
-
if (!list) return;
|
|
18393
|
-
const pids = startedUnder(list, pid, since).filter((p) => p !== process.pid);
|
|
18394
|
-
if (!pids.length) return;
|
|
18395
|
-
spawnSync4("taskkill", ["/F", ...pids.flatMap((p) => ["/PID", String(p)])], { stdio: "ignore", windowsHide: true, timeout: 1e4 });
|
|
18396
|
-
}
|
|
18397
|
-
function killMarked(mark, agent) {
|
|
18398
|
-
if (WINDOWS3) {
|
|
18399
|
-
if (agent) killUnderOnWindows(agent.pid, agent.since);
|
|
18400
|
-
return;
|
|
18401
|
-
}
|
|
18402
|
-
signal(marked(mark), "SIGKILL");
|
|
18403
|
-
}
|
|
18404
|
-
function killTreeOnWindows(pid) {
|
|
18405
|
-
if (!WINDOWS3 || !pid) return false;
|
|
18406
|
-
const out = spawnSync4("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true, timeout: 1e4 });
|
|
18407
|
-
return out.status === 0;
|
|
18408
|
-
}
|
|
18409
|
-
function later(ms, fn) {
|
|
18410
|
-
const t = setTimeout(fn, ms);
|
|
18411
|
-
if (typeof t.unref === "function") t.unref();
|
|
18412
|
-
}
|
|
18413
|
-
function endAgent(child, mark, graceMs) {
|
|
18414
|
-
const term = (sig) => {
|
|
18415
|
-
try {
|
|
18416
|
-
child.kill(sig);
|
|
18417
|
-
} catch {
|
|
18418
|
-
}
|
|
18419
|
-
};
|
|
18420
|
-
if (killTreeOnWindows(child.pid)) return;
|
|
18421
|
-
killUnderOnWindows(child.pid);
|
|
18422
|
-
const seen = new Set(marked(mark, child.pid ? [child.pid] : []));
|
|
18423
|
-
term("SIGTERM");
|
|
18424
|
-
signal([...seen].filter((pid) => pid !== child.pid), "SIGTERM");
|
|
18425
|
-
const round = (left) => {
|
|
18426
|
-
const pids = marked(mark, [...seen]);
|
|
18427
|
-
signal(pids, "SIGKILL");
|
|
18428
|
-
for (const pid of pids) seen.add(pid);
|
|
18429
|
-
if (pids.length && left > 1) later(KILL_ROUND_MS, () => round(left - 1));
|
|
18430
|
-
};
|
|
18431
|
-
later(graceMs, () => {
|
|
18432
|
-
term("SIGKILL");
|
|
18433
|
-
round(KILL_ROUNDS);
|
|
18434
|
-
});
|
|
18435
|
-
}
|
|
18436
|
-
function killRunning() {
|
|
18437
|
-
for (const [child, mark] of running) {
|
|
18438
|
-
if (killTreeOnWindows(child.pid)) continue;
|
|
18439
|
-
killUnderOnWindows(child.pid);
|
|
18440
|
-
signal(marked(mark, child.pid ? [child.pid] : []), "SIGKILL");
|
|
18441
|
-
try {
|
|
18442
|
-
child.kill("SIGKILL");
|
|
18443
|
-
} catch {
|
|
18444
|
-
}
|
|
18530
|
+
`Don't ask me questions with human-in-the-loop. Leave any questions as open questions.`
|
|
18531
|
+
].filter(Boolean).join(" ");
|
|
18532
|
+
case "resolve":
|
|
18533
|
+
return [
|
|
18534
|
+
`${kb}. Apply my answers to the open questions on task ${req.id} ${named2} following \`akb guide resolve\`.`,
|
|
18535
|
+
carriedNote(req),
|
|
18536
|
+
answeredNote(req.id, command),
|
|
18537
|
+
req.notes ? `Extra notes: ${req.notes}` : ""
|
|
18538
|
+
].filter(Boolean).join(" ");
|
|
18445
18539
|
}
|
|
18446
|
-
running.clear();
|
|
18447
|
-
}
|
|
18448
|
-
function unlisten() {
|
|
18449
|
-
for (const [sig, fn] of listening) process.removeListener(sig, fn);
|
|
18450
|
-
listening.clear();
|
|
18451
|
-
}
|
|
18452
|
-
function onQuit(sig) {
|
|
18453
|
-
killRunning();
|
|
18454
|
-
unlisten();
|
|
18455
|
-
if (process.listenerCount(sig) === 0) process.exit(128 + os6.constants.signals[sig]);
|
|
18456
18540
|
}
|
|
18457
|
-
function
|
|
18458
|
-
if (!
|
|
18459
|
-
|
|
18460
|
-
child.once("close", () => {
|
|
18461
|
-
running.delete(child);
|
|
18462
|
-
if (!running.size) unlisten();
|
|
18463
|
-
});
|
|
18464
|
-
for (const sig of QUIT_SIGNALS) {
|
|
18465
|
-
if (listening.has(sig) || sig === "SIGINT" && interruptIsTheCallers) continue;
|
|
18466
|
-
const fn = () => onQuit(sig);
|
|
18467
|
-
listening.set(sig, fn);
|
|
18468
|
-
process.on(sig, fn);
|
|
18469
|
-
}
|
|
18541
|
+
function discardedCardsPrompt(cards = []) {
|
|
18542
|
+
if (!cards.length) return "";
|
|
18543
|
+
return "The user discarded the cards listed below. Their absence is intentional. Do not restore or recreate them, including under new IDs or filenames. Continue the remaining work from the current board, preserving the other cards.\n\n" + cards.map((c) => `- #${c.id}: ${c.path}`).join("\n");
|
|
18470
18544
|
}
|
|
18471
|
-
var
|
|
18472
|
-
var
|
|
18473
|
-
"../cli/src/lib/agent/
|
|
18545
|
+
var RESUME_PROMPT, DELIVERY_RESUME, DELIVERY_RESUME_CARD, RESTARTABLE, RESTART_LEAD, restartable, NO_IMPLEMENT, BOARD_FOLDER, CARD_FILE, PROJECT_FOLDER, PLANNING_FOR, SPEC_SELECTOR_FOR, reflectedCards;
|
|
18546
|
+
var init_prompts = __esm({
|
|
18547
|
+
"../cli/src/lib/agent/prompts.ts"() {
|
|
18474
18548
|
"use strict";
|
|
18549
|
+
init_cards();
|
|
18550
|
+
init_memory();
|
|
18551
|
+
init_memories();
|
|
18552
|
+
init_paths();
|
|
18553
|
+
init_agents();
|
|
18554
|
+
init_command();
|
|
18555
|
+
init_deliveries();
|
|
18475
18556
|
init_env();
|
|
18476
|
-
|
|
18477
|
-
|
|
18478
|
-
|
|
18479
|
-
|
|
18480
|
-
|
|
18481
|
-
|
|
18482
|
-
|
|
18483
|
-
|
|
18484
|
-
|
|
18485
|
-
|
|
18486
|
-
|
|
18487
|
-
|
|
18488
|
-
|
|
18489
|
-
|
|
18490
|
-
|
|
18557
|
+
init_facts();
|
|
18558
|
+
init_outputs();
|
|
18559
|
+
init_archive();
|
|
18560
|
+
init_flows();
|
|
18561
|
+
init_resolve();
|
|
18562
|
+
init_runner();
|
|
18563
|
+
init_workflows();
|
|
18564
|
+
init_catalog2();
|
|
18565
|
+
init_roles();
|
|
18566
|
+
init_rules2();
|
|
18567
|
+
init_settings2();
|
|
18568
|
+
init_types3();
|
|
18569
|
+
RESUME_PROMPT = [
|
|
18570
|
+
`Continue. The previous run ended before it finished the task.`,
|
|
18571
|
+
`Pick up where you left off and carry it through.`
|
|
18572
|
+
].join(" ");
|
|
18573
|
+
DELIVERY_RESUME = [
|
|
18574
|
+
`Continue delivery %s. The previous run ended before the delivery finished.`,
|
|
18575
|
+
`Re-enter the flow from the top and check each step's precondition before you do it \u2014 work that is already done is done, so do not repeat it.`
|
|
18576
|
+
].join(" ");
|
|
18577
|
+
DELIVERY_RESUME_CARD = `Build the card as the delivery holds it, not as the file reads now: \`%c\` prints the approved copy.`;
|
|
18578
|
+
RESTARTABLE = /* @__PURE__ */ new Set([
|
|
18579
|
+
"implement",
|
|
18580
|
+
"clarify",
|
|
18581
|
+
"resolve",
|
|
18582
|
+
"approve",
|
|
18583
|
+
"archive",
|
|
18584
|
+
"spec",
|
|
18585
|
+
"conflict",
|
|
18586
|
+
"scheduled"
|
|
18587
|
+
]);
|
|
18588
|
+
RESTART_LEAD = [
|
|
18589
|
+
`The session you were continuing is gone, and this one holds nothing of it.`,
|
|
18590
|
+
`Do the task below from the top, checking each step's precondition before you do it \u2014 work an earlier run already finished is done, so don't repeat it.`
|
|
18591
|
+
].join(" ");
|
|
18592
|
+
restartable = (run2) => (run2.cardId !== null || !!run2.deliveryId) && RESTARTABLE.has(run2.action);
|
|
18593
|
+
NO_IMPLEMENT = `(Unless the request explicitly asks for implementation, don't implement it; revising the card's drafts is not implementing.)`;
|
|
18594
|
+
BOARD_FOLDER = "<board folder>";
|
|
18595
|
+
CARD_FILE = "<card file>";
|
|
18596
|
+
PROJECT_FOLDER = "<project folder>";
|
|
18597
|
+
PLANNING_FOR = /* @__PURE__ */ new Set(["create", "clarify", "resolve", "approve", "edit"]);
|
|
18598
|
+
SPEC_SELECTOR_FOR = /* @__PURE__ */ new Set(["clarify", "resolve", "approve", "edit"]);
|
|
18599
|
+
reflectedCards = (req) => req.cards ?? (req.id !== void 0 ? [req.id] : []);
|
|
18491
18600
|
}
|
|
18492
18601
|
});
|
|
18493
18602
|
|
|
18494
18603
|
// ../cli/src/lib/agent/sessions.ts
|
|
18495
18604
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
18496
|
-
import
|
|
18497
|
-
import
|
|
18605
|
+
import fs49 from "node:fs";
|
|
18606
|
+
import path54 from "node:path";
|
|
18498
18607
|
function reap(runs, reaped = [], restore = []) {
|
|
18499
18608
|
let changed = false;
|
|
18500
18609
|
const now = Date.now();
|
|
@@ -18514,7 +18623,7 @@ function reap(runs, reaped = [], restore = []) {
|
|
|
18514
18623
|
}
|
|
18515
18624
|
function stampDuration(run2, endedAt) {
|
|
18516
18625
|
try {
|
|
18517
|
-
|
|
18626
|
+
fs49.appendFileSync(run2.logPath, durationLine(endedAt - run2.startedAt));
|
|
18518
18627
|
} catch {
|
|
18519
18628
|
}
|
|
18520
18629
|
}
|
|
@@ -18528,7 +18637,7 @@ function cardNow(cardId2) {
|
|
|
18528
18637
|
try {
|
|
18529
18638
|
const file = cardFile(cardId2);
|
|
18530
18639
|
if (!file) return null;
|
|
18531
|
-
const { meta } = parseFrontmatter(
|
|
18640
|
+
const { meta } = parseFrontmatter(fs49.readFileSync(file, "utf8"));
|
|
18532
18641
|
if (!meta) return null;
|
|
18533
18642
|
return { status: meta.status || "todo", questions: openOf(meta.questions).length, title: meta.title };
|
|
18534
18643
|
} catch {
|
|
@@ -18665,9 +18774,9 @@ function leftBoardOnLanding(id) {
|
|
|
18665
18774
|
function archivedRejected(id) {
|
|
18666
18775
|
const filed = locateArchived(id);
|
|
18667
18776
|
if (!filed) return null;
|
|
18668
|
-
const file = filed.kind === "group" ?
|
|
18777
|
+
const file = filed.kind === "group" ? path54.join(filed.target, "root.md") : filed.target;
|
|
18669
18778
|
try {
|
|
18670
|
-
return !!parseFrontmatter(
|
|
18779
|
+
return !!parseFrontmatter(fs49.readFileSync(file, "utf8")).meta?.rejected;
|
|
18671
18780
|
} catch {
|
|
18672
18781
|
return false;
|
|
18673
18782
|
}
|
|
@@ -18924,7 +19033,7 @@ function openRun(req, prompt, notes = [], sessionId = randomUUID6(), said3) {
|
|
|
18924
19033
|
return { run: record, spec };
|
|
18925
19034
|
}
|
|
18926
19035
|
function planningSession(cardId2, cwd = REPO_ROOT) {
|
|
18927
|
-
const here = (dir) => !dir ||
|
|
19036
|
+
const here = (dir) => !dir || path54.resolve(dir) === cwd;
|
|
18928
19037
|
const ended = readRuns().filter((r) => r.status === "done").sort((a, b) => b.startedAt - a.startedAt);
|
|
18929
19038
|
const of = (run3, fork) => {
|
|
18930
19039
|
if (!here(run3.cwd)) return { none: "elsewhere" };
|
|
@@ -19146,8 +19255,8 @@ async function resumeHeld(prev, plan, sessionId, resuming, fresh) {
|
|
|
19146
19255
|
clearAsks(prev.sessionId);
|
|
19147
19256
|
try {
|
|
19148
19257
|
if (prev.action !== "create") {
|
|
19149
|
-
|
|
19150
|
-
|
|
19258
|
+
fs49.unlinkSync(prev.logPath);
|
|
19259
|
+
fs49.rmSync(eventsPathOf(prev.sessionId), { force: true });
|
|
19151
19260
|
}
|
|
19152
19261
|
} catch {
|
|
19153
19262
|
}
|
|
@@ -19171,8 +19280,8 @@ function forgetRun(sessionId) {
|
|
|
19171
19280
|
if (!gone) return;
|
|
19172
19281
|
clearAsks(sessionId);
|
|
19173
19282
|
try {
|
|
19174
|
-
|
|
19175
|
-
|
|
19283
|
+
fs49.unlinkSync(gone.logPath);
|
|
19284
|
+
fs49.rmSync(eventsPathOf(gone.sessionId), { force: true });
|
|
19176
19285
|
} catch {
|
|
19177
19286
|
}
|
|
19178
19287
|
}
|
|
@@ -19201,14 +19310,14 @@ function noteSpecRefusals(sessionId, refused) {
|
|
|
19201
19310
|
}
|
|
19202
19311
|
function clearAsks(sessionId) {
|
|
19203
19312
|
try {
|
|
19204
|
-
|
|
19313
|
+
fs49.unlinkSync(asksPathOf(sessionId));
|
|
19205
19314
|
} catch {
|
|
19206
19315
|
}
|
|
19207
19316
|
}
|
|
19208
19317
|
function readAsks(sessionId) {
|
|
19209
19318
|
let data;
|
|
19210
19319
|
try {
|
|
19211
|
-
data = JSON.parse(
|
|
19320
|
+
data = JSON.parse(fs49.readFileSync(asksPathOf(sessionId), "utf8"));
|
|
19212
19321
|
} catch {
|
|
19213
19322
|
return { asks: [], refines: [], refused: [] };
|
|
19214
19323
|
}
|
|
@@ -19235,10 +19344,10 @@ function readAsks(sessionId) {
|
|
|
19235
19344
|
return { asks: asks.filter((a) => !discarded.has(a.cardId)), refines: refines.filter((a) => !discarded.has(a.cardId)), refused };
|
|
19236
19345
|
}
|
|
19237
19346
|
function writeAsks(sessionId, file) {
|
|
19238
|
-
|
|
19347
|
+
fs49.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
19239
19348
|
const tmp = `${asksPathOf(sessionId)}.tmp`;
|
|
19240
|
-
|
|
19241
|
-
|
|
19349
|
+
fs49.writeFileSync(tmp, JSON.stringify(file, null, 2) + "\n");
|
|
19350
|
+
fs49.renameSync(tmp, asksPathOf(sessionId));
|
|
19242
19351
|
}
|
|
19243
19352
|
function markSpawned(sessionId, pid) {
|
|
19244
19353
|
patch(sessionId, (r) => {
|
|
@@ -19326,7 +19435,10 @@ async function cancelDelivery(id) {
|
|
|
19326
19435
|
if (!id.trim()) return { ok: false, ...refusal("deliveryUnnamed", "name the delivery to cancel", { action: "cancel" }) };
|
|
19327
19436
|
const delivery = namedDelivery(id);
|
|
19328
19437
|
if (!delivery) return { ok: false, ...unknownDelivery(id) };
|
|
19329
|
-
if (delivery.status !== "active")
|
|
19438
|
+
if (delivery.status !== "active") {
|
|
19439
|
+
tidyCheckout(delivery.deliveryId);
|
|
19440
|
+
return { ok: true, deliveryId: delivery.deliveryId };
|
|
19441
|
+
}
|
|
19330
19442
|
endDelivery(delivery.deliveryId, "cancelled");
|
|
19331
19443
|
const live = readRuns().find((r) => r.status === "running" && r.deliveryId === delivery.deliveryId && r.action !== "sub");
|
|
19332
19444
|
if (live) await stopRun(live.sessionId);
|
|
@@ -19392,12 +19504,12 @@ async function discardDelivery(id) {
|
|
|
19392
19504
|
return { ok: true, deliveryId: delivery.deliveryId };
|
|
19393
19505
|
}
|
|
19394
19506
|
function writeSpec(spec) {
|
|
19395
|
-
|
|
19396
|
-
|
|
19507
|
+
fs49.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
19508
|
+
fs49.writeFileSync(specPathOf(spec.sessionId), JSON.stringify(spec, null, 2) + "\n");
|
|
19397
19509
|
}
|
|
19398
19510
|
function readSpec(sessionId) {
|
|
19399
19511
|
try {
|
|
19400
|
-
const spec = JSON.parse(
|
|
19512
|
+
const spec = JSON.parse(fs49.readFileSync(specPathOf(sessionId), "utf8"));
|
|
19401
19513
|
return spec && Array.isArray(spec.plan?.argv) ? spec : null;
|
|
19402
19514
|
} catch {
|
|
19403
19515
|
return null;
|
|
@@ -19405,28 +19517,28 @@ function readSpec(sessionId) {
|
|
|
19405
19517
|
}
|
|
19406
19518
|
function dropSpec(sessionId) {
|
|
19407
19519
|
try {
|
|
19408
|
-
|
|
19520
|
+
fs49.unlinkSync(specPathOf(sessionId));
|
|
19409
19521
|
} catch {
|
|
19410
19522
|
}
|
|
19411
19523
|
}
|
|
19412
19524
|
async function acquireIndexLock(giveUp) {
|
|
19413
19525
|
const until = Date.now() + INDEX_WAIT_MS;
|
|
19414
|
-
const holder =
|
|
19526
|
+
const holder = path54.join(INDEX_LOCK2, "holder");
|
|
19415
19527
|
for (; ; ) {
|
|
19416
19528
|
if (giveUp()) return null;
|
|
19417
19529
|
try {
|
|
19418
|
-
|
|
19419
|
-
|
|
19420
|
-
return () =>
|
|
19530
|
+
fs49.mkdirSync(INDEX_LOCK2, { recursive: false });
|
|
19531
|
+
fs49.writeFileSync(holder, String(process.pid));
|
|
19532
|
+
return () => fs49.rmSync(INDEX_LOCK2, { recursive: true, force: true });
|
|
19421
19533
|
} catch (err) {
|
|
19422
19534
|
const code = err.code;
|
|
19423
19535
|
if (code === "ENOENT") {
|
|
19424
|
-
|
|
19536
|
+
fs49.mkdirSync(path54.dirname(INDEX_LOCK2), { recursive: true });
|
|
19425
19537
|
continue;
|
|
19426
19538
|
}
|
|
19427
19539
|
if (code !== "EEXIST") throw err;
|
|
19428
19540
|
if (!pidAlive(Number(readHolder(holder)))) {
|
|
19429
|
-
|
|
19541
|
+
fs49.rmSync(INDEX_LOCK2, { recursive: true, force: true });
|
|
19430
19542
|
continue;
|
|
19431
19543
|
}
|
|
19432
19544
|
if (Date.now() > until) return null;
|
|
@@ -19436,7 +19548,7 @@ async function acquireIndexLock(giveUp) {
|
|
|
19436
19548
|
}
|
|
19437
19549
|
function readHolder(file) {
|
|
19438
19550
|
try {
|
|
19439
|
-
return
|
|
19551
|
+
return fs49.readFileSync(file, "utf8").trim();
|
|
19440
19552
|
} catch {
|
|
19441
19553
|
return String(process.pid);
|
|
19442
19554
|
}
|
|
@@ -19527,8 +19639,8 @@ var init_sessions = __esm({
|
|
|
19527
19639
|
conflict: "implementing",
|
|
19528
19640
|
hook: "implementing"
|
|
19529
19641
|
};
|
|
19530
|
-
specPathOf = (sessionId) =>
|
|
19531
|
-
asksPathOf = (sessionId) =>
|
|
19642
|
+
specPathOf = (sessionId) => path54.join(SESSIONS_DIR, `${sessionId}.plan.json`);
|
|
19643
|
+
asksPathOf = (sessionId) => path54.join(SESSIONS_DIR, `${sessionId}.asks.json`);
|
|
19532
19644
|
carriesSession = (r) => !!(r.resumedFrom || r.continues && !r.continues.fork);
|
|
19533
19645
|
canPickUp = (r) => (r.status === "error" || r.status === "interrupted" || r.status === "stopped") && !(r.action === "create" && readRuns().some((next) => next.resumedFrom === r.sessionId));
|
|
19534
19646
|
neverStarted = (r) => r.unspawned === true || !!r.errorWhy?.some((why) => why.kind === "notInstalled");
|
|
@@ -19662,8 +19774,8 @@ var init_questions = __esm({
|
|
|
19662
19774
|
});
|
|
19663
19775
|
|
|
19664
19776
|
// ../cli/src/lib/agent/workflow-cards.ts
|
|
19665
|
-
import
|
|
19666
|
-
import
|
|
19777
|
+
import fs50 from "node:fs";
|
|
19778
|
+
import path55 from "node:path";
|
|
19667
19779
|
function cardsOnWorkflow(id) {
|
|
19668
19780
|
const wanted = id || DEFAULT_WORKFLOW;
|
|
19669
19781
|
const ids = [];
|
|
@@ -19674,12 +19786,12 @@ function cardsOnWorkflow(id) {
|
|
|
19674
19786
|
return [];
|
|
19675
19787
|
}
|
|
19676
19788
|
for (const file of files2) {
|
|
19677
|
-
const base =
|
|
19678
|
-
const card = base === "root.md" ? idPrefix(
|
|
19789
|
+
const base = path55.basename(file);
|
|
19790
|
+
const card = base === "root.md" ? idPrefix(path55.basename(path55.dirname(file))) : idPrefix(base);
|
|
19679
19791
|
if (card === null) continue;
|
|
19680
19792
|
let meta;
|
|
19681
19793
|
try {
|
|
19682
|
-
meta = parseFrontmatter(
|
|
19794
|
+
meta = parseFrontmatter(fs50.readFileSync(file, "utf8")).meta;
|
|
19683
19795
|
} catch {
|
|
19684
19796
|
continue;
|
|
19685
19797
|
}
|
|
@@ -19737,12 +19849,12 @@ var init_workflow_cards = __esm({
|
|
|
19737
19849
|
});
|
|
19738
19850
|
|
|
19739
19851
|
// ../cli/src/lib/readme.ts
|
|
19740
|
-
import
|
|
19741
|
-
import
|
|
19852
|
+
import fs51 from "node:fs";
|
|
19853
|
+
import path56 from "node:path";
|
|
19742
19854
|
function stripReadmeRefs(target) {
|
|
19743
|
-
if (!
|
|
19855
|
+
if (!fs51.existsSync(README)) return [];
|
|
19744
19856
|
const needle = target.kind === "group" ? `](${target.rel}/` : `](${target.rel})`;
|
|
19745
|
-
const lines =
|
|
19857
|
+
const lines = fs51.readFileSync(README, "utf8").split("\n");
|
|
19746
19858
|
const out = [];
|
|
19747
19859
|
const removed = [];
|
|
19748
19860
|
let i = 0;
|
|
@@ -19764,20 +19876,20 @@ function stripReadmeRefs(target) {
|
|
|
19764
19876
|
out.push(line);
|
|
19765
19877
|
i++;
|
|
19766
19878
|
}
|
|
19767
|
-
if (removed.length)
|
|
19879
|
+
if (removed.length) fs51.writeFileSync(README, out.join("\n"));
|
|
19768
19880
|
return removed;
|
|
19769
19881
|
}
|
|
19770
19882
|
function addReadmeRef(id, title, relPath) {
|
|
19771
|
-
if (!
|
|
19772
|
-
const link2 = relPath.split(
|
|
19883
|
+
if (!fs51.existsSync(README)) return false;
|
|
19884
|
+
const link2 = relPath.split(path56.sep).join("/");
|
|
19773
19885
|
const bullet = `- [#${id} ${title}](${link2})`;
|
|
19774
19886
|
const heading = `## ${TASKS_HEADING}`;
|
|
19775
|
-
let lines =
|
|
19887
|
+
let lines = fs51.readFileSync(README, "utf8").split("\n");
|
|
19776
19888
|
const hi = lines.findIndex((l) => l.trim().toLowerCase() === heading.toLowerCase());
|
|
19777
19889
|
if (hi === -1) {
|
|
19778
19890
|
while (lines.length && lines[lines.length - 1].trim() === "") lines.pop();
|
|
19779
19891
|
lines.push("", heading, "", bullet);
|
|
19780
|
-
|
|
19892
|
+
fs51.writeFileSync(README, lines.join("\n") + "\n");
|
|
19781
19893
|
return true;
|
|
19782
19894
|
}
|
|
19783
19895
|
let end2 = hi + 1;
|
|
@@ -19791,15 +19903,15 @@ function addReadmeRef(id, title, relPath) {
|
|
|
19791
19903
|
const at = lastBullet !== -1 ? lastBullet + 1 : lines[hi + 1] === "" ? hi + 2 : hi + 1;
|
|
19792
19904
|
lines.splice(at, 0, bullet);
|
|
19793
19905
|
}
|
|
19794
|
-
|
|
19906
|
+
fs51.writeFileSync(README, lines.join("\n"));
|
|
19795
19907
|
return true;
|
|
19796
19908
|
}
|
|
19797
19909
|
function repointReadmeLink(id, oldRel, newRel, title) {
|
|
19798
|
-
if (!
|
|
19799
|
-
const oldLink = oldRel.split(
|
|
19800
|
-
const newLink = newRel.split(
|
|
19910
|
+
if (!fs51.existsSync(README)) return false;
|
|
19911
|
+
const oldLink = oldRel.split(path56.sep).join("/");
|
|
19912
|
+
const newLink = newRel.split(path56.sep).join("/");
|
|
19801
19913
|
const linkRe = new RegExp(`\\[#${id}\\b[^\\]]*\\]\\(${escapeRegex(oldLink)}\\)`);
|
|
19802
|
-
const lines =
|
|
19914
|
+
const lines = fs51.readFileSync(README, "utf8").split("\n");
|
|
19803
19915
|
let changed = false;
|
|
19804
19916
|
for (let i = 0; i < lines.length; i++) {
|
|
19805
19917
|
if (!lines[i].includes(`](${oldLink})`)) continue;
|
|
@@ -19809,7 +19921,7 @@ function repointReadmeLink(id, oldRel, newRel, title) {
|
|
|
19809
19921
|
changed = true;
|
|
19810
19922
|
}
|
|
19811
19923
|
}
|
|
19812
|
-
if (changed)
|
|
19924
|
+
if (changed) fs51.writeFileSync(README, lines.join("\n"));
|
|
19813
19925
|
return changed;
|
|
19814
19926
|
}
|
|
19815
19927
|
var isTableRow, isBulletStart, bulletIndent, TASKS_HEADING, escapeRegex;
|
|
@@ -19826,14 +19938,14 @@ var init_readme = __esm({
|
|
|
19826
19938
|
});
|
|
19827
19939
|
|
|
19828
19940
|
// ../cli/src/lib/reconcile.ts
|
|
19829
|
-
import
|
|
19830
|
-
import
|
|
19941
|
+
import fs52 from "node:fs";
|
|
19942
|
+
import path57 from "node:path";
|
|
19831
19943
|
function liveIds() {
|
|
19832
19944
|
const ids = /* @__PURE__ */ new Set();
|
|
19833
19945
|
for (const file of walkMd(TODO)) {
|
|
19834
|
-
const base =
|
|
19946
|
+
const base = path57.basename(file);
|
|
19835
19947
|
if (base === "README.md") continue;
|
|
19836
|
-
const id = base === "root.md" ? idPrefix(
|
|
19948
|
+
const id = base === "root.md" ? idPrefix(path57.basename(path57.dirname(file))) : idPrefix(base);
|
|
19837
19949
|
if (id != null) ids.add(id);
|
|
19838
19950
|
}
|
|
19839
19951
|
return ids;
|
|
@@ -19841,16 +19953,16 @@ function liveIds() {
|
|
|
19841
19953
|
function indexableCards() {
|
|
19842
19954
|
const out = [];
|
|
19843
19955
|
for (const dir of walkDirs(TODO)) {
|
|
19844
|
-
const id = idPrefix(
|
|
19845
|
-
if (id != null &&
|
|
19846
|
-
out.push({ id, rel:
|
|
19956
|
+
const id = idPrefix(path57.basename(dir));
|
|
19957
|
+
if (id != null && fs52.existsSync(path57.join(dir, "root.md"))) {
|
|
19958
|
+
out.push({ id, rel: path57.join(path57.relative(TODO, dir), "root.md").split(path57.sep).join("/") });
|
|
19847
19959
|
}
|
|
19848
19960
|
}
|
|
19849
19961
|
for (const file of walkMd(TODO)) {
|
|
19850
|
-
const base =
|
|
19962
|
+
const base = path57.basename(file);
|
|
19851
19963
|
if (base === "README.md" || base === "root.md") continue;
|
|
19852
|
-
const relTODO =
|
|
19853
|
-
const segs = relTODO.split(
|
|
19964
|
+
const relTODO = path57.relative(TODO, file);
|
|
19965
|
+
const segs = relTODO.split(path57.sep);
|
|
19854
19966
|
if (segs.length !== 1) continue;
|
|
19855
19967
|
const id = idPrefix(base);
|
|
19856
19968
|
if (id != null) out.push({ id, rel: segs.join("/") });
|
|
@@ -19859,7 +19971,7 @@ function indexableCards() {
|
|
|
19859
19971
|
}
|
|
19860
19972
|
function readmeComplaints(fix) {
|
|
19861
19973
|
const said3 = [];
|
|
19862
|
-
const lines =
|
|
19974
|
+
const lines = fs52.readFileSync(README, "utf8").split("\n");
|
|
19863
19975
|
const indexed = /* @__PURE__ */ new Set();
|
|
19864
19976
|
let fixed = 0;
|
|
19865
19977
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -19868,13 +19980,13 @@ function readmeComplaints(fix) {
|
|
|
19868
19980
|
const id = Number(m[1]);
|
|
19869
19981
|
const linkPath = m[2];
|
|
19870
19982
|
indexed.add(id);
|
|
19871
|
-
if (
|
|
19983
|
+
if (fs52.existsSync(path57.join(TODO, linkPath))) continue;
|
|
19872
19984
|
const found2 = locate(id);
|
|
19873
19985
|
if (!found2) {
|
|
19874
19986
|
said3.push(`README links #${id} \u2192 ${linkPath}, but no card with that id exists (removed by hand?). Drop the entry or restore the file.`);
|
|
19875
19987
|
continue;
|
|
19876
19988
|
}
|
|
19877
|
-
const want = (found2.kind === "group" ?
|
|
19989
|
+
const want = (found2.kind === "group" ? path57.join(found2.rel, "root.md") : found2.rel).split(path57.sep).join("/");
|
|
19878
19990
|
if (want === linkPath) continue;
|
|
19879
19991
|
if (!fix) {
|
|
19880
19992
|
said3.push(`README links #${id} \u2192 ${linkPath}, which isn't there any more; the card is at ${want}.`);
|
|
@@ -19884,7 +19996,7 @@ function readmeComplaints(fix) {
|
|
|
19884
19996
|
said3.push(`README link #${id} pointed at missing ${linkPath} \u2192 repointed to ${want}.`);
|
|
19885
19997
|
fixed++;
|
|
19886
19998
|
}
|
|
19887
|
-
if (fixed)
|
|
19999
|
+
if (fixed) fs52.writeFileSync(README, lines.join("\n"));
|
|
19888
20000
|
for (const c of indexableCards()) {
|
|
19889
20001
|
if (!indexed.has(c.id)) {
|
|
19890
20002
|
said3.push(`card ${c.rel} (#${c.id}) is not in the README index. Add it under "## ${TASKS_HEADING}".`);
|
|
@@ -19896,11 +20008,11 @@ function crossRefComplaints() {
|
|
|
19896
20008
|
const said3 = [];
|
|
19897
20009
|
const live = liveIds();
|
|
19898
20010
|
for (const file of walkMd(TODO)) {
|
|
19899
|
-
const base =
|
|
20011
|
+
const base = path57.basename(file);
|
|
19900
20012
|
if (base === "README.md") continue;
|
|
19901
|
-
const ownerId = base === "root.md" ? idPrefix(
|
|
20013
|
+
const ownerId = base === "root.md" ? idPrefix(path57.basename(path57.dirname(file))) : idPrefix(base);
|
|
19902
20014
|
if (ownerId == null) continue;
|
|
19903
|
-
const { meta } = parseFrontmatter(
|
|
20015
|
+
const { meta } = parseFrontmatter(fs52.readFileSync(file, "utf8"));
|
|
19904
20016
|
if (!meta) continue;
|
|
19905
20017
|
for (const field2 of ["blocked_by", "related"]) {
|
|
19906
20018
|
for (const ref of meta[field2] || []) {
|
|
@@ -19913,11 +20025,11 @@ function crossRefComplaints() {
|
|
|
19913
20025
|
return said3;
|
|
19914
20026
|
}
|
|
19915
20027
|
function reconcileBoard() {
|
|
19916
|
-
if (!
|
|
20028
|
+
if (!fs52.existsSync(README)) return;
|
|
19917
20029
|
for (const line of [...readmeComplaints(true), ...crossRefComplaints()]) warn2(line);
|
|
19918
20030
|
}
|
|
19919
20031
|
function boardComplaints() {
|
|
19920
|
-
if (!
|
|
20032
|
+
if (!fs52.existsSync(README)) return [];
|
|
19921
20033
|
try {
|
|
19922
20034
|
return [...readmeComplaints(false), ...crossRefComplaints()];
|
|
19923
20035
|
} catch {
|
|
@@ -19937,8 +20049,8 @@ var init_reconcile = __esm({
|
|
|
19937
20049
|
});
|
|
19938
20050
|
|
|
19939
20051
|
// ../cli/src/commands/card.ts
|
|
19940
|
-
import
|
|
19941
|
-
import
|
|
20052
|
+
import fs53 from "node:fs";
|
|
20053
|
+
import path58 from "node:path";
|
|
19942
20054
|
function defaultBody() {
|
|
19943
20055
|
return [
|
|
19944
20056
|
"<\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>",
|
|
@@ -19964,7 +20076,7 @@ function bodyFromFile(opts) {
|
|
|
19964
20076
|
if (opts.body === false) die("pass --body-file or --no-body, not both");
|
|
19965
20077
|
let text5;
|
|
19966
20078
|
try {
|
|
19967
|
-
text5 =
|
|
20079
|
+
text5 = fs53.readFileSync(opts.bodyFile, "utf8");
|
|
19968
20080
|
} catch {
|
|
19969
20081
|
die(`can't read ${opts.bodyFile} \u2014 write the body to a file, then pass its path`, { kind: "needs-input" });
|
|
19970
20082
|
}
|
|
@@ -19996,20 +20108,20 @@ function cmdCreate(opts) {
|
|
|
19996
20108
|
const start = opts.id;
|
|
19997
20109
|
const slug2 = slugify(opts.slug !== void 0 ? opts.slug : title);
|
|
19998
20110
|
const fileRel = `${start}-${slug2}.md`;
|
|
19999
|
-
const file =
|
|
20111
|
+
const file = path58.join(TODO, fileRel);
|
|
20000
20112
|
const held4 = locate(start) ?? locateArchived(start);
|
|
20001
|
-
if (held4 &&
|
|
20113
|
+
if (held4 && path58.basename(held4.target) === `${start}-${slug2}.md`) {
|
|
20002
20114
|
say(start);
|
|
20003
20115
|
say(` ${rel(held4.target)} was already written`);
|
|
20004
20116
|
return { id: start, ids: [start], title, file: rel(held4.target), indexed: false };
|
|
20005
20117
|
}
|
|
20006
20118
|
if (held4) die(`#${start} is already a card here (${rel(held4.target)})`, { kind: "id-taken", id: start });
|
|
20007
|
-
if (
|
|
20119
|
+
if (fs53.existsSync(file)) die(`${rel(file)} already exists \u2014 pick a different --slug`);
|
|
20008
20120
|
countBoardEvent(triage ? "cards_created_proposed" : "cards_created_asked");
|
|
20009
20121
|
const meta = { title, priority, status: "todo", blocked_by, related, modules, workflow, triage, source, questions };
|
|
20010
20122
|
const scaffolded = !written && opts.body !== false;
|
|
20011
20123
|
const body = written ?? (scaffolded ? defaultBody() : "");
|
|
20012
|
-
|
|
20124
|
+
fs53.writeFileSync(file, serializeFrontmatter(meta) + "\n\n" + body);
|
|
20013
20125
|
const indexed = addReadmeRef(start, title, fileRel);
|
|
20014
20126
|
say(start);
|
|
20015
20127
|
say(
|
|
@@ -20047,8 +20159,8 @@ function cmdUpdate(id, flags) {
|
|
|
20047
20159
|
if (creating) die(creating, { kind: "card-being-created", id });
|
|
20048
20160
|
const found2 = locate(id);
|
|
20049
20161
|
if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
|
|
20050
|
-
const file = found2.kind === "group" ?
|
|
20051
|
-
const { meta, body } = parseFrontmatter(
|
|
20162
|
+
const file = found2.kind === "group" ? path58.join(found2.target, "root.md") : found2.target;
|
|
20163
|
+
const { meta, body } = parseFrontmatter(fs53.readFileSync(file, "utf8"));
|
|
20052
20164
|
if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
|
|
20053
20165
|
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");
|
|
20054
20166
|
if (flags.related !== void 0 && flags.addRelated !== void 0) die("--related replaces the list and --add-related appends to it \u2014 pass one");
|
|
@@ -20128,26 +20240,26 @@ function cmdUpdate(id, flags) {
|
|
|
20128
20240
|
meta.status = "todo";
|
|
20129
20241
|
changes.push("status\u2192todo (open questions)");
|
|
20130
20242
|
}
|
|
20131
|
-
const curRel =
|
|
20243
|
+
const curRel = path58.relative(TODO, file);
|
|
20132
20244
|
const isSubtask = found2.kind === "file" && enclosingGroupRoot(file) !== null;
|
|
20133
|
-
let base =
|
|
20245
|
+
let base = path58.basename(file);
|
|
20134
20246
|
if (flags.slug !== void 0) {
|
|
20135
20247
|
if (found2.kind === "group") die("renaming a group root by script is not supported");
|
|
20136
20248
|
base = `${id}-${slugify(flags.slug)}.md`;
|
|
20137
20249
|
}
|
|
20138
|
-
const destRel =
|
|
20139
|
-
const dest =
|
|
20250
|
+
const destRel = path58.join(path58.dirname(curRel), base);
|
|
20251
|
+
const dest = path58.join(TODO, destRel);
|
|
20140
20252
|
const moving = dest !== file;
|
|
20141
|
-
if (moving &&
|
|
20142
|
-
|
|
20143
|
-
if (moving)
|
|
20253
|
+
if (moving && fs53.existsSync(dest)) die(`${rel(dest)} already exists`);
|
|
20254
|
+
fs53.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
|
|
20255
|
+
if (moving) fs53.renameSync(file, dest);
|
|
20144
20256
|
if (isSubtask) {
|
|
20145
20257
|
if (moving || changes.includes("title")) repointReadmeLink(id, curRel, destRel, meta.title);
|
|
20146
|
-
if (moving) changes.push(`renamed \u2192 ${destRel.split(
|
|
20258
|
+
if (moving) changes.push(`renamed \u2192 ${destRel.split(path58.sep).join("/")}`);
|
|
20147
20259
|
} else if (moving) {
|
|
20148
20260
|
stripReadmeRefs({ kind: "file", rel: curRel });
|
|
20149
20261
|
addReadmeRef(id, meta.title, destRel);
|
|
20150
|
-
changes.push(`renamed \u2192 ${destRel.split(
|
|
20262
|
+
changes.push(`renamed \u2192 ${destRel.split(path58.sep).join("/")}`);
|
|
20151
20263
|
} else if (changes.includes("title")) {
|
|
20152
20264
|
stripReadmeRefs({ kind: "file", rel: curRel });
|
|
20153
20265
|
addReadmeRef(id, meta.title, curRel);
|
|
@@ -20159,8 +20271,8 @@ function cmdUpdateQuestions(id, input) {
|
|
|
20159
20271
|
const ops = readQuestionOps(input.ops ?? []);
|
|
20160
20272
|
const found2 = locate(id);
|
|
20161
20273
|
if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
|
|
20162
|
-
const file = found2.kind === "group" ?
|
|
20163
|
-
const { meta, body } = parseFrontmatter(
|
|
20274
|
+
const file = found2.kind === "group" ? path58.join(found2.target, "root.md") : found2.target;
|
|
20275
|
+
const { meta, body } = parseFrontmatter(fs53.readFileSync(file, "utf8"));
|
|
20164
20276
|
if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
|
|
20165
20277
|
if (insideRun() && ops.some((op) => op.kind === "skip" || op.kind === "unskip")) {
|
|
20166
20278
|
die("only the user skips or reopens a question \u2014 a run leaves questions for them to answer");
|
|
@@ -20233,7 +20345,7 @@ function cmdUpdateQuestions(id, input) {
|
|
|
20233
20345
|
meta.status = "todo";
|
|
20234
20346
|
changes.push("status\u2192todo (open questions)");
|
|
20235
20347
|
}
|
|
20236
|
-
|
|
20348
|
+
fs53.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
|
|
20237
20349
|
for (const kind of closed2) countBoardEvent(kind);
|
|
20238
20350
|
settleDelivery2(id, skipped, unskipped);
|
|
20239
20351
|
say(
|
|
@@ -20307,13 +20419,13 @@ var init_config_template = __esm({
|
|
|
20307
20419
|
});
|
|
20308
20420
|
|
|
20309
20421
|
// ../cli/src/lib/agent/local.ts
|
|
20310
|
-
import
|
|
20311
|
-
import
|
|
20422
|
+
import fs54 from "node:fs";
|
|
20423
|
+
import path59 from "node:path";
|
|
20312
20424
|
function takeLocalModels(board2) {
|
|
20313
|
-
const file =
|
|
20425
|
+
const file = path59.join(board2, ".local.json");
|
|
20314
20426
|
let held4;
|
|
20315
20427
|
try {
|
|
20316
|
-
held4 = configBlock(JSON.parse(
|
|
20428
|
+
held4 = configBlock(JSON.parse(fs54.readFileSync(file, "utf8")));
|
|
20317
20429
|
} catch {
|
|
20318
20430
|
return {};
|
|
20319
20431
|
}
|
|
@@ -20331,7 +20443,7 @@ function takeLocalModels(board2) {
|
|
|
20331
20443
|
}
|
|
20332
20444
|
if (!Object.keys(agents).length) return out;
|
|
20333
20445
|
delete held4.agents;
|
|
20334
|
-
|
|
20446
|
+
fs54.writeFileSync(file, JSON.stringify(held4, null, 2) + "\n");
|
|
20335
20447
|
return out;
|
|
20336
20448
|
}
|
|
20337
20449
|
var LOCAL_IGNORE_LINE;
|
|
@@ -20392,12 +20504,12 @@ var init_identity2 = __esm({
|
|
|
20392
20504
|
});
|
|
20393
20505
|
|
|
20394
20506
|
// ../cli/src/lib/signals/migrate.ts
|
|
20395
|
-
import
|
|
20396
|
-
import
|
|
20507
|
+
import fs55 from "node:fs";
|
|
20508
|
+
import path60 from "node:path";
|
|
20397
20509
|
function readHandled() {
|
|
20398
20510
|
let text5;
|
|
20399
20511
|
try {
|
|
20400
|
-
text5 =
|
|
20512
|
+
text5 = fs55.readFileSync(SIGNALS_OLD_HANDLED, "utf8");
|
|
20401
20513
|
} catch {
|
|
20402
20514
|
return [];
|
|
20403
20515
|
}
|
|
@@ -20414,57 +20526,57 @@ function readHandled() {
|
|
|
20414
20526
|
return held4;
|
|
20415
20527
|
}
|
|
20416
20528
|
function lift(from, into) {
|
|
20417
|
-
|
|
20418
|
-
|
|
20529
|
+
fs55.mkdirSync(into, { recursive: true });
|
|
20530
|
+
fs55.renameSync(from, path60.join(into, freeName(into, path60.basename(from))));
|
|
20419
20531
|
}
|
|
20420
20532
|
function repoint(dir, from, into) {
|
|
20421
20533
|
let entries;
|
|
20422
20534
|
try {
|
|
20423
|
-
entries =
|
|
20535
|
+
entries = fs55.readdirSync(dir, { withFileTypes: true });
|
|
20424
20536
|
} catch {
|
|
20425
20537
|
return;
|
|
20426
20538
|
}
|
|
20427
20539
|
for (const entry of entries) {
|
|
20428
20540
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
20429
|
-
const file =
|
|
20430
|
-
const text5 =
|
|
20431
|
-
if (text5.includes(from))
|
|
20541
|
+
const file = path60.join(dir, entry.name);
|
|
20542
|
+
const text5 = fs55.readFileSync(file, "utf8");
|
|
20543
|
+
if (text5.includes(from)) fs55.writeFileSync(file, text5.split(from).join(into));
|
|
20432
20544
|
}
|
|
20433
20545
|
}
|
|
20434
20546
|
function dropIfEmpty2(dir) {
|
|
20435
20547
|
try {
|
|
20436
|
-
|
|
20548
|
+
fs55.rmdirSync(dir);
|
|
20437
20549
|
} catch {
|
|
20438
20550
|
}
|
|
20439
20551
|
}
|
|
20440
20552
|
function liftInbox() {
|
|
20441
|
-
if (!
|
|
20442
|
-
const oldFiles =
|
|
20443
|
-
if (
|
|
20444
|
-
for (const entry of
|
|
20445
|
-
if (entry.isFile()) lift(
|
|
20553
|
+
if (!fs55.existsSync(SIGNALS_OLD_INBOX)) return;
|
|
20554
|
+
const oldFiles = path60.join(SIGNALS_OLD_INBOX, "files");
|
|
20555
|
+
if (fs55.existsSync(oldFiles)) {
|
|
20556
|
+
for (const entry of fs55.readdirSync(oldFiles, { withFileTypes: true })) {
|
|
20557
|
+
if (entry.isFile()) lift(path60.join(oldFiles, entry.name), SIGNALS_FILES);
|
|
20446
20558
|
}
|
|
20447
20559
|
dropIfEmpty2(oldFiles);
|
|
20448
20560
|
}
|
|
20449
20561
|
const held4 = new Set(readFolder(TRIAGE).map((signal2) => signal2.sourceId));
|
|
20450
|
-
for (const entry of
|
|
20562
|
+
for (const entry of fs55.readdirSync(SIGNALS_OLD_INBOX, { withFileTypes: true })) {
|
|
20451
20563
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
20452
|
-
const from =
|
|
20564
|
+
const from = path60.join(SIGNALS_OLD_INBOX, entry.name);
|
|
20453
20565
|
const item = parse(from);
|
|
20454
20566
|
if (item && held4.has(item.sourceId)) {
|
|
20455
|
-
|
|
20567
|
+
fs55.rmSync(from, { force: true });
|
|
20456
20568
|
continue;
|
|
20457
20569
|
}
|
|
20458
20570
|
if (item) held4.add(item.sourceId);
|
|
20459
20571
|
lift(from, TRIAGE);
|
|
20460
20572
|
}
|
|
20461
|
-
const wasAt = asWritten(
|
|
20573
|
+
const wasAt = asWritten(path60.join(SIGNALS_OLD_INBOX, "files"));
|
|
20462
20574
|
const nowAt = asWritten(SIGNALS_FILES);
|
|
20463
20575
|
for (const dir of [TRIAGE, SIGNALS_ARCHIVED, SIGNALS_DISMISSED]) repoint(dir, wasAt, nowAt);
|
|
20464
20576
|
dropIfEmpty2(SIGNALS_OLD_INBOX);
|
|
20465
20577
|
}
|
|
20466
20578
|
function liftHandled() {
|
|
20467
|
-
if (!
|
|
20579
|
+
if (!fs55.existsSync(SIGNALS_OLD_HANDLED)) return;
|
|
20468
20580
|
const lines = readHandled();
|
|
20469
20581
|
const held4 = /* @__PURE__ */ new Set([
|
|
20470
20582
|
...readFolder(TRIAGE).map((signal2) => signal2.sourceId),
|
|
@@ -20474,7 +20586,7 @@ function liftHandled() {
|
|
|
20474
20586
|
for (const { sourceId, when } of lines) {
|
|
20475
20587
|
if (held4.has(sourceId)) continue;
|
|
20476
20588
|
held4.add(sourceId);
|
|
20477
|
-
|
|
20589
|
+
fs55.mkdirSync(SIGNALS_DISMISSED, { recursive: true });
|
|
20478
20590
|
const name = freeName(SIGNALS_DISMISSED, fileName("", sourceId));
|
|
20479
20591
|
const front = [
|
|
20480
20592
|
"---",
|
|
@@ -20484,13 +20596,13 @@ function liftHandled() {
|
|
|
20484
20596
|
"---",
|
|
20485
20597
|
""
|
|
20486
20598
|
];
|
|
20487
|
-
|
|
20599
|
+
fs55.writeFileSync(path60.join(SIGNALS_DISMISSED, name), `${front.join("\n")}
|
|
20488
20600
|
`);
|
|
20489
20601
|
}
|
|
20490
|
-
|
|
20602
|
+
fs55.rmSync(SIGNALS_OLD_HANDLED, { force: true });
|
|
20491
20603
|
}
|
|
20492
20604
|
function migrateTriage() {
|
|
20493
|
-
if (!
|
|
20605
|
+
if (!fs55.existsSync(SIGNALS_OLD_INBOX) && !fs55.existsSync(SIGNALS_OLD_HANDLED)) return;
|
|
20494
20606
|
try {
|
|
20495
20607
|
liftInbox();
|
|
20496
20608
|
liftHandled();
|
|
@@ -20505,13 +20617,13 @@ var init_migrate = __esm({
|
|
|
20505
20617
|
init_yaml();
|
|
20506
20618
|
init_inbox();
|
|
20507
20619
|
HANDLED_LINE = /^-\s+(.+?)\s+—\s+(.+)$/;
|
|
20508
|
-
asWritten = (dir) => `${rel(dir).split(
|
|
20620
|
+
asWritten = (dir) => `${rel(dir).split(path60.sep).join("/")}/`;
|
|
20509
20621
|
}
|
|
20510
20622
|
});
|
|
20511
20623
|
|
|
20512
20624
|
// ../cli/src/lib/signals/add.ts
|
|
20513
|
-
import
|
|
20514
|
-
import
|
|
20625
|
+
import fs56 from "node:fs";
|
|
20626
|
+
import path61 from "node:path";
|
|
20515
20627
|
import { createHash as createHash3 } from "node:crypto";
|
|
20516
20628
|
function linkTitle(url) {
|
|
20517
20629
|
let at;
|
|
@@ -20524,7 +20636,7 @@ function linkTitle(url) {
|
|
|
20524
20636
|
return oneLine(where) || url.slice(0, TITLE_MAX);
|
|
20525
20637
|
}
|
|
20526
20638
|
function asText(file) {
|
|
20527
|
-
const ext =
|
|
20639
|
+
const ext = path61.extname(file.name).toLowerCase();
|
|
20528
20640
|
if (!file.type.startsWith("text/") && file.type !== "application/json" && !TEXT_EXTENSIONS.has(ext)) return null;
|
|
20529
20641
|
try {
|
|
20530
20642
|
const text5 = new TextDecoder("utf-8", { fatal: true }).decode(file.data);
|
|
@@ -20534,9 +20646,9 @@ function asText(file) {
|
|
|
20534
20646
|
}
|
|
20535
20647
|
}
|
|
20536
20648
|
function fileName2(name, data) {
|
|
20537
|
-
const base =
|
|
20538
|
-
const ext =
|
|
20539
|
-
const word =
|
|
20649
|
+
const base = path61.basename(name);
|
|
20650
|
+
const ext = path61.extname(base).toLowerCase().slice(0, 12);
|
|
20651
|
+
const word = path61.basename(base, path61.extname(base)).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40) || "file";
|
|
20540
20652
|
const hash2 = createHash3("sha256").update(data).digest("hex").slice(0, 8);
|
|
20541
20653
|
return `${word}-${hash2}${ext}`;
|
|
20542
20654
|
}
|
|
@@ -20545,9 +20657,9 @@ function addToInbox(drop) {
|
|
|
20545
20657
|
const typed = (drop.text ?? "").trim();
|
|
20546
20658
|
const file = drop.file;
|
|
20547
20659
|
if (!typed && !file) return { ok: false, error: "nothing to add \u2014 paste a link or some text, or drop a file." };
|
|
20548
|
-
if (file && file.data.length === 0) return { ok: false, error: `${
|
|
20660
|
+
if (file && file.data.length === 0) return { ok: false, error: `${path61.basename(file.name)} is empty.` };
|
|
20549
20661
|
if (file && file.data.length > MAX_BYTES) {
|
|
20550
|
-
return { ok: false, error: `${
|
|
20662
|
+
return { ok: false, error: `${path61.basename(file.name)} is over ${MAX_BYTES / 1024 / 1024} MB \u2014 too big for the board.` };
|
|
20551
20663
|
}
|
|
20552
20664
|
const read4 = describe(typed, file);
|
|
20553
20665
|
const keep2 = read4.keep;
|
|
@@ -20569,8 +20681,8 @@ ${incoming.summary}`);
|
|
|
20569
20681
|
if (seen.status === "pending" || seen.status === "archived") return { ok: false, error: `${sayHit(seen)}.` };
|
|
20570
20682
|
try {
|
|
20571
20683
|
if (keep2) {
|
|
20572
|
-
|
|
20573
|
-
|
|
20684
|
+
fs56.mkdirSync(SIGNALS_FILES, { recursive: true });
|
|
20685
|
+
fs56.writeFileSync(keep2.at, keep2.data);
|
|
20574
20686
|
}
|
|
20575
20687
|
return { ok: true, signal: writeSignal({ ...incoming, sourceId }, formatStamp(/* @__PURE__ */ new Date()), drop.slug) };
|
|
20576
20688
|
} catch (e) {
|
|
@@ -20584,7 +20696,7 @@ function describe(typed, file) {
|
|
|
20584
20696
|
return only ? [only] : [];
|
|
20585
20697
|
};
|
|
20586
20698
|
if (file) {
|
|
20587
|
-
const name =
|
|
20699
|
+
const name = path61.basename(file.name);
|
|
20588
20700
|
const text5 = asText(file);
|
|
20589
20701
|
const note = typed ? `
|
|
20590
20702
|
|
|
@@ -20596,7 +20708,7 @@ ${typed}` : "";
|
|
|
20596
20708
|
const at = keepAt(file);
|
|
20597
20709
|
return {
|
|
20598
20710
|
incoming: {
|
|
20599
|
-
title:
|
|
20711
|
+
title: path61.basename(name, path61.extname(name)) || name,
|
|
20600
20712
|
summary: `${name} \u2014 ${boardRel2(at)}${note}`,
|
|
20601
20713
|
...named2
|
|
20602
20714
|
},
|
|
@@ -20634,73 +20746,73 @@ var init_add = __esm({
|
|
|
20634
20746
|
MAX_BYTES = 25 * 1024 * 1024;
|
|
20635
20747
|
TITLE_MAX = 120;
|
|
20636
20748
|
TEXT_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".markdown", ".txt", ".text", ".csv", ".json", ".yml", ".yaml", ".log"]);
|
|
20637
|
-
boardRel2 = (file) => rel(file).split(
|
|
20749
|
+
boardRel2 = (file) => rel(file).split(path61.sep).join("/");
|
|
20638
20750
|
oneLine = (text5) => {
|
|
20639
20751
|
const first = text5.split("\n").find((line) => line.trim()) ?? "";
|
|
20640
20752
|
const clean = first.replace(/^#+\s*/, "").trim();
|
|
20641
20753
|
return clean.length > TITLE_MAX ? `${clean.slice(0, TITLE_MAX - 1).trimEnd()}\u2026` : clean;
|
|
20642
20754
|
};
|
|
20643
|
-
keepAt = (file) =>
|
|
20755
|
+
keepAt = (file) => path61.join(SIGNALS_FILES, fileName2(file.name, file.data));
|
|
20644
20756
|
}
|
|
20645
20757
|
});
|
|
20646
20758
|
|
|
20647
20759
|
// ../cli/src/lib/recurring.ts
|
|
20648
|
-
import
|
|
20649
|
-
import
|
|
20760
|
+
import fs57 from "node:fs";
|
|
20761
|
+
import path62 from "node:path";
|
|
20650
20762
|
function migratePruneMemoryCard() {
|
|
20651
20763
|
const dir = recurringDir();
|
|
20652
20764
|
let names4;
|
|
20653
20765
|
try {
|
|
20654
|
-
names4 =
|
|
20766
|
+
names4 = fs57.readdirSync(dir);
|
|
20655
20767
|
} catch {
|
|
20656
20768
|
return null;
|
|
20657
20769
|
}
|
|
20658
20770
|
const name = names4.find((entry) => entry.endsWith(`-${PRUNE_SLUG}.md`));
|
|
20659
20771
|
if (!name) return null;
|
|
20660
|
-
const file =
|
|
20772
|
+
const file = path62.join(dir, name);
|
|
20661
20773
|
try {
|
|
20662
|
-
const { meta } = parseFrontmatter(
|
|
20774
|
+
const { meta } = parseFrontmatter(fs57.readFileSync(file, "utf8"));
|
|
20663
20775
|
const cadence = meta?.cadence;
|
|
20664
20776
|
if (typeof cadence === "string") adoptMemoryPruneCadence(cadence);
|
|
20665
20777
|
} catch {
|
|
20666
20778
|
}
|
|
20667
|
-
|
|
20779
|
+
fs57.rmSync(file, { force: true });
|
|
20668
20780
|
return rel(file);
|
|
20669
20781
|
}
|
|
20670
20782
|
function legacyCards() {
|
|
20671
20783
|
const dir = recurringDir();
|
|
20672
|
-
if (!
|
|
20784
|
+
if (!fs57.existsSync(dir)) return [];
|
|
20673
20785
|
const cards = [];
|
|
20674
|
-
for (const entry of
|
|
20786
|
+
for (const entry of fs57.readdirSync(dir, { withFileTypes: true })) {
|
|
20675
20787
|
const id = idPrefix(entry.name);
|
|
20676
20788
|
if (id === null) continue;
|
|
20677
|
-
const full =
|
|
20789
|
+
const full = path62.join(dir, entry.name);
|
|
20678
20790
|
const slug2 = entry.name.replace(/\.md$/, "").replace(/^\d+-?/, "");
|
|
20679
20791
|
if (entry.isFile() && entry.name.endsWith(".md")) cards.push({ id, slug: slug2, file: full });
|
|
20680
|
-
else if (entry.isDirectory() &&
|
|
20681
|
-
cards.push({ id, slug: slug2, file:
|
|
20792
|
+
else if (entry.isDirectory() && fs57.existsSync(path62.join(full, "root.md"))) {
|
|
20793
|
+
cards.push({ id, slug: slug2, file: path62.join(full, "root.md"), folder: full });
|
|
20682
20794
|
}
|
|
20683
20795
|
}
|
|
20684
20796
|
return cards.sort((a, b) => a.id - b.id);
|
|
20685
20797
|
}
|
|
20686
20798
|
function mergeInto2(from, to, skip = []) {
|
|
20687
|
-
|
|
20688
|
-
for (const entry of
|
|
20799
|
+
fs57.mkdirSync(to, { recursive: true });
|
|
20800
|
+
for (const entry of fs57.readdirSync(from, { withFileTypes: true })) {
|
|
20689
20801
|
if (skip.includes(entry.name)) continue;
|
|
20690
|
-
const source =
|
|
20691
|
-
const target =
|
|
20692
|
-
if (!
|
|
20693
|
-
else if (entry.isDirectory() &&
|
|
20802
|
+
const source = path62.join(from, entry.name);
|
|
20803
|
+
const target = path62.join(to, entry.name);
|
|
20804
|
+
if (!fs57.existsSync(target)) fs57.renameSync(source, target);
|
|
20805
|
+
else if (entry.isDirectory() && fs57.statSync(target).isDirectory()) mergeInto2(source, target);
|
|
20694
20806
|
}
|
|
20695
|
-
if (isEmpty(from))
|
|
20807
|
+
if (isEmpty(from)) fs57.rmSync(from, { recursive: true, force: true });
|
|
20696
20808
|
}
|
|
20697
20809
|
function moveSideFolders() {
|
|
20698
20810
|
const dir = recurringDir();
|
|
20699
|
-
for (const entry of
|
|
20811
|
+
for (const entry of fs57.readdirSync(dir, { withFileTypes: true })) {
|
|
20700
20812
|
if (!entry.isDirectory() || idPrefix(entry.name) !== null) continue;
|
|
20701
|
-
const from =
|
|
20702
|
-
if (isEmpty(from))
|
|
20703
|
-
else mergeInto2(from,
|
|
20813
|
+
const from = path62.join(dir, entry.name);
|
|
20814
|
+
if (isEmpty(from)) fs57.rmSync(from, { recursive: true, force: true });
|
|
20815
|
+
else mergeInto2(from, path62.join(AGENTS, entry.name));
|
|
20704
20816
|
}
|
|
20705
20817
|
}
|
|
20706
20818
|
function sections(body) {
|
|
@@ -20750,8 +20862,8 @@ function agentName(card, text5) {
|
|
|
20750
20862
|
const known2 = /* @__PURE__ */ new Set([...specAgentCatalog().agents.map((a) => a.name), ...ROLE_NAMES]);
|
|
20751
20863
|
for (const name of [slug2, `${slug2}-${card.id}`]) {
|
|
20752
20864
|
if (!AGENT_NAME.test(name)) continue;
|
|
20753
|
-
const file =
|
|
20754
|
-
const written =
|
|
20865
|
+
const file = path62.join(AGENTS, name, AGENT_FILE3);
|
|
20866
|
+
const written = fs57.existsSync(file) ? fs57.readFileSync(file, "utf8") : null;
|
|
20755
20867
|
if (written === text5(name)) return name;
|
|
20756
20868
|
if (written === null && !known2.has(name)) return name;
|
|
20757
20869
|
}
|
|
@@ -20768,11 +20880,11 @@ ${q.options.map((o) => `- ${o}`).join("\n")}` : "";
|
|
|
20768
20880
|
function removeCard(card) {
|
|
20769
20881
|
dropCrossRefs(card.id);
|
|
20770
20882
|
const target = card.folder ?? card.file;
|
|
20771
|
-
stripReadmeRefs({ kind: card.folder ? "group" : "file", rel:
|
|
20772
|
-
|
|
20883
|
+
stripReadmeRefs({ kind: card.folder ? "group" : "file", rel: path62.relative(TODO, target).split(path62.sep).join("/") });
|
|
20884
|
+
fs57.rmSync(target, { recursive: true, force: true });
|
|
20773
20885
|
}
|
|
20774
20886
|
function migrateCard(card) {
|
|
20775
|
-
const { meta, body } = parseFrontmatter(
|
|
20887
|
+
const { meta, body } = parseFrontmatter(fs57.readFileSync(card.file, "utf8"));
|
|
20776
20888
|
if (!meta) throw new Error("its frontmatter cannot be read");
|
|
20777
20889
|
const raw = meta;
|
|
20778
20890
|
const said3 = (key) => typeof raw[key] === "string" ? raw[key].trim() : "";
|
|
@@ -20782,19 +20894,19 @@ function migrateCard(card) {
|
|
|
20782
20894
|
if (!flow) throw new Error("this board has no workflow to put it in");
|
|
20783
20895
|
const text5 = (name2) => agentText(name2, meta.title || card.slug, body);
|
|
20784
20896
|
const name = agentName(card, text5);
|
|
20785
|
-
const dir =
|
|
20786
|
-
const file =
|
|
20787
|
-
const madeDir = !
|
|
20788
|
-
const wrote = !
|
|
20897
|
+
const dir = path62.join(AGENTS, name);
|
|
20898
|
+
const file = path62.join(dir, AGENT_FILE3);
|
|
20899
|
+
const madeDir = !fs57.existsSync(dir);
|
|
20900
|
+
const wrote = !fs57.existsSync(file);
|
|
20789
20901
|
try {
|
|
20790
|
-
|
|
20791
|
-
if (wrote)
|
|
20902
|
+
fs57.mkdirSync(dir, { recursive: true });
|
|
20903
|
+
if (wrote) fs57.writeFileSync(file, text5(name));
|
|
20792
20904
|
const adopted = adoptWorkflowScheduled(flow.id, name, { cadence, lastRun });
|
|
20793
20905
|
if (!adopted.ok) throw new Error(adopted.error);
|
|
20794
20906
|
questionsToTriage(meta, name);
|
|
20795
20907
|
} catch (e) {
|
|
20796
|
-
if (madeDir)
|
|
20797
|
-
else if (wrote)
|
|
20908
|
+
if (madeDir) fs57.rmSync(dir, { recursive: true, force: true });
|
|
20909
|
+
else if (wrote) fs57.rmSync(file, { force: true });
|
|
20798
20910
|
throw e;
|
|
20799
20911
|
}
|
|
20800
20912
|
if (card.folder) mergeInto2(card.folder, dir, ["root.md"]);
|
|
@@ -20804,7 +20916,7 @@ function migrateCard(card) {
|
|
|
20804
20916
|
function migrateRecurringCards() {
|
|
20805
20917
|
const done2 = { agents: [], removed: [], failed: [] };
|
|
20806
20918
|
const dir = recurringDir();
|
|
20807
|
-
if (!
|
|
20919
|
+
if (!fs57.existsSync(dir)) return done2;
|
|
20808
20920
|
migratePruneMemoryCard();
|
|
20809
20921
|
moveSideFolders();
|
|
20810
20922
|
for (const card of legacyCards()) {
|
|
@@ -20817,7 +20929,7 @@ function migrateRecurringCards() {
|
|
|
20817
20929
|
done2.failed.push({ card: rel(card.file), why: e instanceof Error ? e.message : String(e) });
|
|
20818
20930
|
}
|
|
20819
20931
|
}
|
|
20820
|
-
if (isEmpty(dir))
|
|
20932
|
+
if (isEmpty(dir)) fs57.rmSync(dir, { recursive: true, force: true });
|
|
20821
20933
|
return done2;
|
|
20822
20934
|
}
|
|
20823
20935
|
function recurringMigrationLines(done2) {
|
|
@@ -20850,14 +20962,14 @@ var init_recurring = __esm({
|
|
|
20850
20962
|
PRUNE_SLUG = "prune-the-memory";
|
|
20851
20963
|
FETCH_SLUG = "fetch-triage-items";
|
|
20852
20964
|
AGENT_FILE3 = "AGENT.md";
|
|
20853
|
-
recurringDir = () =>
|
|
20854
|
-
isEmpty = (dir) =>
|
|
20965
|
+
recurringDir = () => path62.join(TODO, LEGACY_RECURRING);
|
|
20966
|
+
isEmpty = (dir) => fs57.readdirSync(dir).every((name) => name === ".DS_Store");
|
|
20855
20967
|
SECTION = /^##\s+(.+?)\s*$/;
|
|
20856
20968
|
}
|
|
20857
20969
|
});
|
|
20858
20970
|
|
|
20859
20971
|
// ../cli/src/commands/init.ts
|
|
20860
|
-
import
|
|
20972
|
+
import fs58 from "node:fs";
|
|
20861
20973
|
function boardReadme() {
|
|
20862
20974
|
return `# Board
|
|
20863
20975
|
|
|
@@ -20870,20 +20982,20 @@ _(none)_
|
|
|
20870
20982
|
`;
|
|
20871
20983
|
}
|
|
20872
20984
|
function writeConfigIfMissing() {
|
|
20873
|
-
if (
|
|
20874
|
-
|
|
20875
|
-
|
|
20985
|
+
if (fs58.existsSync(CONFIG)) return false;
|
|
20986
|
+
fs58.mkdirSync(KANBAN, { recursive: true });
|
|
20987
|
+
fs58.writeFileSync(CONFIG, CONFIG_TEMPLATE);
|
|
20876
20988
|
return true;
|
|
20877
20989
|
}
|
|
20878
20990
|
function writeModulesIfMissing() {
|
|
20879
|
-
if (
|
|
20880
|
-
|
|
20881
|
-
|
|
20991
|
+
if (fs58.existsSync(MODULES_MD)) return false;
|
|
20992
|
+
fs58.mkdirSync(KANBAN, { recursive: true });
|
|
20993
|
+
fs58.writeFileSync(MODULES_MD, MODULES_TEMPLATE);
|
|
20882
20994
|
return true;
|
|
20883
20995
|
}
|
|
20884
20996
|
function writeGitignoreIfMissing() {
|
|
20885
|
-
|
|
20886
|
-
const text5 =
|
|
20997
|
+
fs58.mkdirSync(KANBAN, { recursive: true });
|
|
20998
|
+
const text5 = fs58.existsSync(KANBAN_GITIGNORE) ? fs58.readFileSync(KANBAN_GITIGNORE, "utf8") : "";
|
|
20887
20999
|
const has = (rule) => text5.split("\n").some((line) => line.trim() === rule.line);
|
|
20888
21000
|
const missing = IGNORE_RULES.filter((rule) => !has(rule));
|
|
20889
21001
|
if (!missing.length) return false;
|
|
@@ -20891,37 +21003,37 @@ function writeGitignoreIfMissing() {
|
|
|
20891
21003
|
${rule.line}
|
|
20892
21004
|
`).join("");
|
|
20893
21005
|
const separator = !text5 || text5.endsWith("\n") ? "" : "\n";
|
|
20894
|
-
|
|
21006
|
+
fs58.writeFileSync(KANBAN_GITIGNORE, `${text5}${separator}${block}`);
|
|
20895
21007
|
return true;
|
|
20896
21008
|
}
|
|
20897
21009
|
function writeSkeletonIfMissing() {
|
|
20898
21010
|
const added = [];
|
|
20899
|
-
if (!
|
|
20900
|
-
|
|
21011
|
+
if (!fs58.existsSync(TODO)) {
|
|
21012
|
+
fs58.mkdirSync(TODO, { recursive: true });
|
|
20901
21013
|
added.push(`${rel(TODO)}/`);
|
|
20902
21014
|
}
|
|
20903
|
-
if (!
|
|
20904
|
-
|
|
21015
|
+
if (!fs58.existsSync(README)) {
|
|
21016
|
+
fs58.writeFileSync(README, boardReadme());
|
|
20905
21017
|
added.push(rel(README));
|
|
20906
21018
|
}
|
|
20907
21019
|
return added;
|
|
20908
21020
|
}
|
|
20909
21021
|
function retireReleases() {
|
|
20910
21022
|
const done2 = [];
|
|
20911
|
-
if (
|
|
20912
|
-
|
|
21023
|
+
if (fs58.existsSync(RELEASES)) {
|
|
21024
|
+
fs58.rmSync(RELEASES);
|
|
20913
21025
|
done2.push(`deleted ${rel(RELEASES)}`);
|
|
20914
21026
|
}
|
|
20915
21027
|
let cards = 0;
|
|
20916
21028
|
for (const dir of [TODO, ARCHIVE]) {
|
|
20917
|
-
if (!
|
|
21029
|
+
if (!fs58.existsSync(dir)) continue;
|
|
20918
21030
|
for (const file of walkMd(dir)) {
|
|
20919
|
-
const text5 =
|
|
21031
|
+
const text5 = fs58.readFileSync(file, "utf8");
|
|
20920
21032
|
const end2 = text5.startsWith("---\n") ? text5.indexOf("\n---", 3) : -1;
|
|
20921
21033
|
if (end2 < 0) continue;
|
|
20922
21034
|
const head = text5.slice(0, end2).replace(/\nrelease:[^\n]*/, "");
|
|
20923
21035
|
if (head.length === end2) continue;
|
|
20924
|
-
|
|
21036
|
+
fs58.writeFileSync(file, head + text5.slice(end2));
|
|
20925
21037
|
cards++;
|
|
20926
21038
|
}
|
|
20927
21039
|
}
|
|
@@ -20929,7 +21041,7 @@ function retireReleases() {
|
|
|
20929
21041
|
return done2.join(", ");
|
|
20930
21042
|
}
|
|
20931
21043
|
function cmdInit() {
|
|
20932
|
-
if (
|
|
21044
|
+
if (fs58.existsSync(KANBAN)) {
|
|
20933
21045
|
const added = [
|
|
20934
21046
|
...writeSkeletonIfMissing(),
|
|
20935
21047
|
writeConfigIfMissing() && rel(CONFIG),
|
|
@@ -20964,8 +21076,8 @@ function cmdInit() {
|
|
|
20964
21076
|
}
|
|
20965
21077
|
return { board: rel(KANBAN), created: false, added, memory_paths: scaffolded.map((s) => rel(s.dir)) };
|
|
20966
21078
|
}
|
|
20967
|
-
|
|
20968
|
-
|
|
21079
|
+
fs58.mkdirSync(TODO, { recursive: true });
|
|
21080
|
+
fs58.writeFileSync(README, boardReadme());
|
|
20969
21081
|
writeConfigIfMissing();
|
|
20970
21082
|
scaffoldProjectMemory();
|
|
20971
21083
|
writeModulesIfMissing();
|
|
@@ -21054,25 +21166,25 @@ var init_scheduled = __esm({
|
|
|
21054
21166
|
});
|
|
21055
21167
|
|
|
21056
21168
|
// ../cli/src/lib/card-age.ts
|
|
21057
|
-
import
|
|
21058
|
-
import
|
|
21169
|
+
import fs59 from "node:fs";
|
|
21170
|
+
import path63 from "node:path";
|
|
21059
21171
|
function staleAfterIn(config) {
|
|
21060
21172
|
const days = Number(LINE.exec(config)?.[1]);
|
|
21061
21173
|
return Number.isInteger(days) && days > 0 ? days : STALE_AFTER_DEFAULT;
|
|
21062
21174
|
}
|
|
21063
21175
|
function staleAfter() {
|
|
21064
|
-
if (!
|
|
21065
|
-
return staleAfterIn(
|
|
21176
|
+
if (!fs59.existsSync(CONFIG)) return STALE_AFTER_DEFAULT;
|
|
21177
|
+
return staleAfterIn(fs59.readFileSync(CONFIG, "utf8"));
|
|
21066
21178
|
}
|
|
21067
21179
|
function todoPathspec(top) {
|
|
21068
|
-
const from =
|
|
21069
|
-
if (!from || from.startsWith("..") ||
|
|
21070
|
-
return from.split(
|
|
21180
|
+
const from = path63.relative(real(top), real(TODO));
|
|
21181
|
+
if (!from || from.startsWith("..") || path63.isAbsolute(from)) return null;
|
|
21182
|
+
return from.split(path63.sep).join("/");
|
|
21071
21183
|
}
|
|
21072
21184
|
function cardFile2(spec, printed) {
|
|
21073
21185
|
const p = unquotePath(printed);
|
|
21074
21186
|
if (!p.startsWith(`${spec}/`)) return null;
|
|
21075
|
-
return
|
|
21187
|
+
return path63.join(TODO, ...p.slice(spec.length + 1).split("/"));
|
|
21076
21188
|
}
|
|
21077
21189
|
function dirtyCards(top, spec) {
|
|
21078
21190
|
const out = git(["status", "--porcelain", "--untracked-files=all", "--", spec], top);
|
|
@@ -21119,9 +21231,9 @@ var init_card_age = __esm({
|
|
|
21119
21231
|
gitTop = () => git(["rev-parse", "--show-toplevel"])?.trim() || null;
|
|
21120
21232
|
real = (p) => {
|
|
21121
21233
|
try {
|
|
21122
|
-
return
|
|
21234
|
+
return fs59.realpathSync(p);
|
|
21123
21235
|
} catch {
|
|
21124
|
-
return
|
|
21236
|
+
return path63.resolve(p);
|
|
21125
21237
|
}
|
|
21126
21238
|
};
|
|
21127
21239
|
unquotePath = (line) => {
|
|
@@ -21156,8 +21268,8 @@ var init_card_holds = __esm({
|
|
|
21156
21268
|
});
|
|
21157
21269
|
|
|
21158
21270
|
// ../cli/src/commands/list.ts
|
|
21159
|
-
import
|
|
21160
|
-
import
|
|
21271
|
+
import fs60 from "node:fs";
|
|
21272
|
+
import path64 from "node:path";
|
|
21161
21273
|
function summaryLine(body) {
|
|
21162
21274
|
const lines = body.split("\n");
|
|
21163
21275
|
let i = 0;
|
|
@@ -21173,13 +21285,13 @@ function summaryLine(body) {
|
|
|
21173
21285
|
function openRows() {
|
|
21174
21286
|
const rows = [];
|
|
21175
21287
|
for (const file of walkMd(TODO)) {
|
|
21176
|
-
const base =
|
|
21288
|
+
const base = path64.basename(file);
|
|
21177
21289
|
if (base === "README.md") continue;
|
|
21178
21290
|
const isRoot = base === "root.md";
|
|
21179
|
-
const id = isRoot ? idPrefix(
|
|
21291
|
+
const id = isRoot ? idPrefix(path64.basename(path64.dirname(file))) : idPrefix(base);
|
|
21180
21292
|
if (id == null) continue;
|
|
21181
|
-
if (isLegacyRecurring(
|
|
21182
|
-
const { meta, body } = parseFrontmatter(
|
|
21293
|
+
if (isLegacyRecurring(path64.relative(TODO, file))) continue;
|
|
21294
|
+
const { meta, body } = parseFrontmatter(fs60.readFileSync(file, "utf8"));
|
|
21183
21295
|
rows.push({
|
|
21184
21296
|
id,
|
|
21185
21297
|
file,
|
|
@@ -21221,15 +21333,15 @@ function cmdStale(rows, all2, scope, mod) {
|
|
|
21221
21333
|
}
|
|
21222
21334
|
function archivedFiles() {
|
|
21223
21335
|
const files2 = /* @__PURE__ */ new Map();
|
|
21224
|
-
if (!
|
|
21336
|
+
if (!fs60.existsSync(ARCHIVE)) return files2;
|
|
21225
21337
|
for (const file of walkMd(ARCHIVE)) {
|
|
21226
|
-
const id = idPrefix(
|
|
21338
|
+
const id = idPrefix(path64.basename(file));
|
|
21227
21339
|
if (id !== null) files2.set(id, file);
|
|
21228
21340
|
}
|
|
21229
21341
|
for (const dir of walkDirs(ARCHIVE)) {
|
|
21230
|
-
const id = idPrefix(
|
|
21231
|
-
const root =
|
|
21232
|
-
if (id !== null &&
|
|
21342
|
+
const id = idPrefix(path64.basename(dir));
|
|
21343
|
+
const root = path64.join(dir, "root.md");
|
|
21344
|
+
if (id !== null && fs60.existsSync(root)) files2.set(id, root);
|
|
21233
21345
|
}
|
|
21234
21346
|
return files2;
|
|
21235
21347
|
}
|
|
@@ -21340,14 +21452,14 @@ var init_list = __esm({
|
|
|
21340
21452
|
});
|
|
21341
21453
|
|
|
21342
21454
|
// ../cli/src/commands/misc.ts
|
|
21343
|
-
import
|
|
21344
|
-
import
|
|
21455
|
+
import fs61 from "node:fs";
|
|
21456
|
+
import path65 from "node:path";
|
|
21345
21457
|
function extractOldMeta(text5, file) {
|
|
21346
21458
|
const grab = (re) => {
|
|
21347
21459
|
const m = text5.match(re);
|
|
21348
21460
|
return m ? m[1].trim() : null;
|
|
21349
21461
|
};
|
|
21350
|
-
const title = grab(/^#\s+(.+)$/m) || slugify(
|
|
21462
|
+
const title = grab(/^#\s+(.+)$/m) || slugify(path65.basename(file, ".md").replace(/^\d+-/, "")).replace(/-/g, " ");
|
|
21351
21463
|
const norm = (raw) => {
|
|
21352
21464
|
const v = (raw || "").toLowerCase();
|
|
21353
21465
|
return LEVELS.includes(v) ? v : "med";
|
|
@@ -21374,11 +21486,11 @@ function stripOldHeader(text5) {
|
|
|
21374
21486
|
}
|
|
21375
21487
|
function cmdMigrate(opts) {
|
|
21376
21488
|
const dry = opts.dryRun === true;
|
|
21377
|
-
const files2 = walkMd(TODO).filter((f) =>
|
|
21489
|
+
const files2 = walkMd(TODO).filter((f) => path65.basename(f) !== "README.md");
|
|
21378
21490
|
let changed = 0;
|
|
21379
21491
|
let skipped = 0;
|
|
21380
21492
|
for (const file of files2) {
|
|
21381
|
-
const text5 =
|
|
21493
|
+
const text5 = fs61.readFileSync(file, "utf8");
|
|
21382
21494
|
if (text5.trimStart().startsWith("---")) {
|
|
21383
21495
|
skipped++;
|
|
21384
21496
|
continue;
|
|
@@ -21387,7 +21499,7 @@ function cmdMigrate(opts) {
|
|
|
21387
21499
|
const out = serializeFrontmatter(meta) + "\n\n" + stripOldHeader(text5).replace(/^\n+/, "");
|
|
21388
21500
|
if (dry) say(`would migrate ${rel(file)} (priority=${meta.priority})`);
|
|
21389
21501
|
else {
|
|
21390
|
-
|
|
21502
|
+
fs61.writeFileSync(file, out.endsWith("\n") ? out : out + "\n");
|
|
21391
21503
|
say(`migrated ${rel(file)}`);
|
|
21392
21504
|
}
|
|
21393
21505
|
changed++;
|
|
@@ -21408,10 +21520,10 @@ var init_misc = __esm({
|
|
|
21408
21520
|
});
|
|
21409
21521
|
|
|
21410
21522
|
// ../cli/src/lib/group-close.ts
|
|
21411
|
-
import
|
|
21523
|
+
import fs62 from "node:fs";
|
|
21412
21524
|
function groupCloses(rootFile) {
|
|
21413
21525
|
try {
|
|
21414
|
-
return groupCloseOf(
|
|
21526
|
+
return groupCloseOf(fs62.readFileSync(rootFile, "utf8"));
|
|
21415
21527
|
} catch {
|
|
21416
21528
|
return false;
|
|
21417
21529
|
}
|
|
@@ -21424,12 +21536,12 @@ var init_group_close = __esm({
|
|
|
21424
21536
|
});
|
|
21425
21537
|
|
|
21426
21538
|
// ../cli/src/commands/remove.ts
|
|
21427
|
-
import
|
|
21428
|
-
import
|
|
21539
|
+
import fs63 from "node:fs";
|
|
21540
|
+
import path66 from "node:path";
|
|
21429
21541
|
function leavingIds(id, found2) {
|
|
21430
21542
|
const ids = /* @__PURE__ */ new Set([id]);
|
|
21431
21543
|
if (found2.kind === "group") {
|
|
21432
|
-
const names4 = [...walkMd(found2.target), ...walkDirs(found2.target)].map((f) =>
|
|
21544
|
+
const names4 = [...walkMd(found2.target), ...walkDirs(found2.target)].map((f) => path66.basename(f));
|
|
21433
21545
|
for (const name of names4) {
|
|
21434
21546
|
const n = idPrefix(name);
|
|
21435
21547
|
if (n !== null) ids.add(n);
|
|
@@ -21441,10 +21553,10 @@ function findMentions(ids) {
|
|
|
21441
21553
|
const hits = [];
|
|
21442
21554
|
const re = new RegExp(ids.map((id) => `#${id}(?!\\d)`).join("|"));
|
|
21443
21555
|
for (const dir of [TODO, MEMORY]) {
|
|
21444
|
-
if (!
|
|
21556
|
+
if (!fs63.existsSync(dir)) continue;
|
|
21445
21557
|
for (const file of walkMd(dir)) {
|
|
21446
|
-
if (
|
|
21447
|
-
const lines =
|
|
21558
|
+
if (path66.basename(file) === "README.md") continue;
|
|
21559
|
+
const lines = fs63.readFileSync(file, "utf8").split("\n");
|
|
21448
21560
|
const fmEnd = frontmatterEnd(lines);
|
|
21449
21561
|
lines.forEach((line, i) => {
|
|
21450
21562
|
if (!re.test(line)) return;
|
|
@@ -21460,12 +21572,12 @@ function findMentions(ids) {
|
|
|
21460
21572
|
return hits;
|
|
21461
21573
|
}
|
|
21462
21574
|
function leavingCards(id, found2) {
|
|
21463
|
-
const own = found2.kind === "group" ?
|
|
21575
|
+
const own = found2.kind === "group" ? path66.join(found2.target, "root.md") : found2.target;
|
|
21464
21576
|
const cards = [{ id, file: own }];
|
|
21465
21577
|
if (found2.kind !== "group") return cards;
|
|
21466
21578
|
for (const file of walkMd(found2.target)) {
|
|
21467
21579
|
if (file === own) continue;
|
|
21468
|
-
const n = idPrefix(
|
|
21580
|
+
const n = idPrefix(path66.basename(file));
|
|
21469
21581
|
if (n !== null) cards.push({ id: n, file });
|
|
21470
21582
|
}
|
|
21471
21583
|
return cards;
|
|
@@ -21474,10 +21586,10 @@ function stampLeaving(cards, rejection) {
|
|
|
21474
21586
|
const day = formatDay();
|
|
21475
21587
|
const stamp4 = rejection && !rejection.discard ? { reason: rejection.reason, at: (/* @__PURE__ */ new Date()).toISOString(), by: insideRun() ? "agent" : "user" } : rejection && { reason: rejection.reason };
|
|
21476
21588
|
for (const card of cards) {
|
|
21477
|
-
if (!
|
|
21478
|
-
const text5 =
|
|
21589
|
+
if (!fs63.existsSync(card.file)) continue;
|
|
21590
|
+
const text5 = fs63.readFileSync(card.file, "utf8");
|
|
21479
21591
|
const stamped = stampLeavingIn(text5, day, stamp4);
|
|
21480
|
-
if (stamped !== text5)
|
|
21592
|
+
if (stamped !== text5) fs63.writeFileSync(card.file, stamped);
|
|
21481
21593
|
}
|
|
21482
21594
|
}
|
|
21483
21595
|
function unfinishedDelivery(id) {
|
|
@@ -21493,7 +21605,7 @@ function unfinishedDelivery(id) {
|
|
|
21493
21605
|
}[gap];
|
|
21494
21606
|
return `#${id} is not finished: ${what}.`;
|
|
21495
21607
|
}
|
|
21496
|
-
const missing = planDeliverables(card.body).filter((src) => src.startsWith(".assets/")).find((src) => !
|
|
21608
|
+
const missing = planDeliverables(card.body).filter((src) => src.startsWith(".assets/")).find((src) => !fs63.existsSync(path66.join(ASSETS, src.slice(".assets/".length))));
|
|
21497
21609
|
return missing ? `#${id} is not finished: ${missing} is not on this machine.` : null;
|
|
21498
21610
|
}
|
|
21499
21611
|
function agentDecisions(text5) {
|
|
@@ -21520,8 +21632,8 @@ function removeCard2(id, metric, options) {
|
|
|
21520
21632
|
const unfinished = metric === "completed" ? unfinishedDelivery(id) : null;
|
|
21521
21633
|
if (unfinished) die(unfinished, { kind: "plan-delivery-unfinished" });
|
|
21522
21634
|
const dest = options.cleanupDiscarded && locateArchived(id) ? null : archiveDest(found2);
|
|
21523
|
-
const cardFile3 = found2.kind === "group" ?
|
|
21524
|
-
const cardText =
|
|
21635
|
+
const cardFile3 = found2.kind === "group" ? path66.join(found2.target, "root.md") : found2.target;
|
|
21636
|
+
const cardText = fs63.existsSync(cardFile3) ? fs63.readFileSync(cardFile3, "utf8") : "";
|
|
21525
21637
|
const { meta: cardMeta3 } = parseFrontmatter(cardText);
|
|
21526
21638
|
const alsoRemoved = found2.kind === "group" ? walkMd(found2.target).filter((f) => f !== cardFile3).map((f) => rel(f)).sort() : [];
|
|
21527
21639
|
const leftIds = leavingIds(id, found2);
|
|
@@ -21552,10 +21664,10 @@ function removeCard2(id, metric, options) {
|
|
|
21552
21664
|
const groupRoot = found2.kind === "file" && !options.closing ? enclosingGroupRoot(found2.target) : null;
|
|
21553
21665
|
if (dest) {
|
|
21554
21666
|
stampLeaving(leaving, metric === "rejected" ? { reason: options.reason?.trim() ?? "", discard: options.discard === true } : null);
|
|
21555
|
-
|
|
21556
|
-
|
|
21667
|
+
fs63.mkdirSync(ARCHIVE, { recursive: true });
|
|
21668
|
+
fs63.renameSync(found2.target, dest);
|
|
21557
21669
|
} else {
|
|
21558
|
-
|
|
21670
|
+
fs63.rmSync(found2.target, { recursive: true, force: true });
|
|
21559
21671
|
}
|
|
21560
21672
|
const removedRefs = stripReadmeRefs(found2);
|
|
21561
21673
|
let marked2 = null;
|
|
@@ -21604,14 +21716,14 @@ function removeCard2(id, metric, options) {
|
|
|
21604
21716
|
};
|
|
21605
21717
|
}
|
|
21606
21718
|
function closeGroup(rootFile) {
|
|
21607
|
-
const rootId = idPrefix(
|
|
21719
|
+
const rootId = idPrefix(path66.basename(path66.dirname(rootFile)));
|
|
21608
21720
|
if (rootId === null) return null;
|
|
21609
21721
|
if (runnerProject(KANBAN)) return null;
|
|
21610
21722
|
if (!groupCloses(rootFile)) return null;
|
|
21611
21723
|
say(`
|
|
21612
21724
|
every subtask line on #${rootId} is resolved \u2014 closing the group:`);
|
|
21613
21725
|
try {
|
|
21614
|
-
const { meta, body } = parseFrontmatter(
|
|
21726
|
+
const { meta, body } = parseFrontmatter(fs63.readFileSync(rootFile, "utf8"));
|
|
21615
21727
|
const res = cmdRemove(rootId, "completed", { closing: true });
|
|
21616
21728
|
return {
|
|
21617
21729
|
id: rootId,
|
|
@@ -21668,7 +21780,7 @@ function cleanupDiscardedCards(sessionId) {
|
|
|
21668
21780
|
if (card.pending) throw new Error(`Discarding #${card.id} did not finish. Retry the discard.`);
|
|
21669
21781
|
while (locate(card.id)) cmdRemove(card.id, "rejected", { discard: true, cleanupDiscarded: true });
|
|
21670
21782
|
dropCrossRefs(card.id);
|
|
21671
|
-
stripReadmeRefs({ kind: "file", rel:
|
|
21783
|
+
stripReadmeRefs({ kind: "file", rel: path66.relative(TODO, path66.resolve(REPO_ROOT, card.path)).split(path66.sep).join("/") });
|
|
21672
21784
|
}
|
|
21673
21785
|
return discarded.map((c) => c.id);
|
|
21674
21786
|
});
|
|
@@ -21754,8 +21866,8 @@ var init_memory_file = __esm({
|
|
|
21754
21866
|
});
|
|
21755
21867
|
|
|
21756
21868
|
// ../cli/src/commands/rule.ts
|
|
21757
|
-
import
|
|
21758
|
-
import
|
|
21869
|
+
import fs64 from "node:fs";
|
|
21870
|
+
import path67 from "node:path";
|
|
21759
21871
|
function cmdRule(askedName, flags) {
|
|
21760
21872
|
const agent = askedName.trim();
|
|
21761
21873
|
if (!agentRoster().some((a) => a.name === agent)) {
|
|
@@ -21764,7 +21876,7 @@ function cmdRule(askedName, flags) {
|
|
|
21764
21876
|
const rule = readRuleText(flags.file, flags.text);
|
|
21765
21877
|
const res = setAgentRule(agent, rule);
|
|
21766
21878
|
if (!res.ok) die(res.error);
|
|
21767
|
-
const file = rel(
|
|
21879
|
+
const file = rel(path67.join(RULES, `${agent}.md`));
|
|
21768
21880
|
say(rule ? `wrote the \`${agent}\` rule (${file})` : `cleared the \`${agent}\` rule`);
|
|
21769
21881
|
return { agent, file, cleared: !rule };
|
|
21770
21882
|
}
|
|
@@ -21772,7 +21884,7 @@ function readRuleText(file, text5) {
|
|
|
21772
21884
|
if (file !== void 0 && text5 !== void 0) die("pass --file or --text, not both");
|
|
21773
21885
|
if (file !== void 0) {
|
|
21774
21886
|
try {
|
|
21775
|
-
return
|
|
21887
|
+
return fs64.readFileSync(file, "utf8").trim();
|
|
21776
21888
|
} catch {
|
|
21777
21889
|
die(`can't read ${file} \u2014 write the rule to a file, then pass its path`);
|
|
21778
21890
|
}
|
|
@@ -21899,22 +22011,22 @@ var init_storyboard = __esm({
|
|
|
21899
22011
|
});
|
|
21900
22012
|
|
|
21901
22013
|
// ../cli/src/lib/spec-contract.ts
|
|
21902
|
-
import
|
|
21903
|
-
import
|
|
22014
|
+
import fs65 from "node:fs";
|
|
22015
|
+
import path68 from "node:path";
|
|
21904
22016
|
function snapshotSpecs() {
|
|
21905
22017
|
const out = /* @__PURE__ */ new Map();
|
|
21906
|
-
if (!
|
|
22018
|
+
if (!fs65.existsSync(TODO)) return out;
|
|
21907
22019
|
for (const file of walkMd(TODO)) {
|
|
21908
|
-
if (isLegacyRecurring(
|
|
21909
|
-
const id = idPrefix(
|
|
21910
|
-
if (id !== null) out.set(file, { id, text:
|
|
22020
|
+
if (isLegacyRecurring(path68.relative(TODO, file))) continue;
|
|
22021
|
+
const id = idPrefix(path68.basename(file) === "root.md" ? path68.basename(path68.dirname(file)) : path68.basename(file));
|
|
22022
|
+
if (id !== null) out.set(file, { id, text: fs65.readFileSync(file, "utf8") });
|
|
21911
22023
|
}
|
|
21912
22024
|
return out;
|
|
21913
22025
|
}
|
|
21914
22026
|
function enclosingGroups(file) {
|
|
21915
22027
|
const ids = [];
|
|
21916
22028
|
for (let root = enclosingGroupRoot(file); root; root = enclosingGroupRoot(root)) {
|
|
21917
|
-
const id = idPrefix(
|
|
22029
|
+
const id = idPrefix(path68.basename(path68.dirname(root)));
|
|
21918
22030
|
if (id !== null) ids.push(id);
|
|
21919
22031
|
}
|
|
21920
22032
|
return ids;
|
|
@@ -22083,9 +22195,9 @@ function validateSpec(file, text5, id) {
|
|
|
22083
22195
|
}
|
|
22084
22196
|
function inside(dir, name) {
|
|
22085
22197
|
try {
|
|
22086
|
-
const root =
|
|
22087
|
-
const real2 =
|
|
22088
|
-
return real2.startsWith(root +
|
|
22198
|
+
const root = fs65.realpathSync(dir);
|
|
22199
|
+
const real2 = fs65.realpathSync(path68.join(dir, name));
|
|
22200
|
+
return real2.startsWith(root + path68.sep) && fs65.statSync(real2).isFile() ? real2 : null;
|
|
22089
22201
|
} catch {
|
|
22090
22202
|
return null;
|
|
22091
22203
|
}
|
|
@@ -22103,7 +22215,7 @@ function variantsProblem(raw, src, id) {
|
|
|
22103
22215
|
if (files2.length < 2 || !files2.every((f) => typeof f === "string" && f)) return `variants needs at least two labels, each naming a file. ${shape}`;
|
|
22104
22216
|
if (!files2.includes(src)) return `src="${src}" is not one of the variants. Set src to the version shown first.`;
|
|
22105
22217
|
if (id === void 0) return null;
|
|
22106
|
-
const dir =
|
|
22218
|
+
const dir = path68.join(ASSETS, String(id));
|
|
22107
22219
|
for (const f of files2) {
|
|
22108
22220
|
const parts = f.split("/");
|
|
22109
22221
|
if (parts.length !== 3 || parts[0] !== ".assets" || parts[1] !== String(id) || !inside(dir, parts[2])) {
|
|
@@ -22113,7 +22225,7 @@ function variantsProblem(raw, src, id) {
|
|
|
22113
22225
|
return null;
|
|
22114
22226
|
}
|
|
22115
22227
|
function compareProblem(attrs, id) {
|
|
22116
|
-
const dir =
|
|
22228
|
+
const dir = path68.join(ASSETS, String(id));
|
|
22117
22229
|
const src = (message) => ({ rule: "compare-src", message });
|
|
22118
22230
|
const shots = (message) => ({ rule: "compare-shots", message });
|
|
22119
22231
|
for (const side of ["a", "b"]) {
|
|
@@ -22133,7 +22245,7 @@ function compareProblem(attrs, id) {
|
|
|
22133
22245
|
const file = inside(dir, named2.name);
|
|
22134
22246
|
if (!file) return { message: `Compare shots ${at} names no file in this card's asset folder. Write the JSON there or fix the path.` };
|
|
22135
22247
|
try {
|
|
22136
|
-
return { data: JSON.parse(
|
|
22248
|
+
return { data: JSON.parse(fs65.readFileSync(file, "utf8")) };
|
|
22137
22249
|
} catch {
|
|
22138
22250
|
return { message: `${at} is not valid JSON.` };
|
|
22139
22251
|
}
|
|
@@ -22153,7 +22265,7 @@ function compareProblem(attrs, id) {
|
|
|
22153
22265
|
}
|
|
22154
22266
|
function validateStoryboards(file, id, text5) {
|
|
22155
22267
|
const errors = [];
|
|
22156
|
-
const dir =
|
|
22268
|
+
const dir = path68.join(ASSETS, String(id));
|
|
22157
22269
|
for (const marker of storyboardMarkers(text5)) {
|
|
22158
22270
|
const named2 = assetName(marker.src, id, ["json"]);
|
|
22159
22271
|
const add = (message) => errors.push({ file: rel(file), line: marker.line, rule: "storyboard-src", message });
|
|
@@ -22302,8 +22414,8 @@ var init_mockups = __esm({
|
|
|
22302
22414
|
});
|
|
22303
22415
|
|
|
22304
22416
|
// ../cli/src/commands/spec-write.ts
|
|
22305
|
-
import
|
|
22306
|
-
import
|
|
22417
|
+
import fs66 from "node:fs";
|
|
22418
|
+
import path69 from "node:path";
|
|
22307
22419
|
function cmdSpecWrite(id, askedName, flags) {
|
|
22308
22420
|
const agent = findSpecAgent(askedName);
|
|
22309
22421
|
if (!agent) die(notAnAgent(askedName), { kind: "no-such-spec-agent", specAgent: askedName });
|
|
@@ -22311,11 +22423,11 @@ function cmdSpecWrite(id, askedName, flags) {
|
|
|
22311
22423
|
const section = readSection(flags.file, flags.text);
|
|
22312
22424
|
const found2 = locate(id);
|
|
22313
22425
|
if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
|
|
22314
|
-
const file = found2.kind === "group" ?
|
|
22315
|
-
const { meta, body } = parseFrontmatter(
|
|
22426
|
+
const file = found2.kind === "group" ? path69.join(found2.target, "root.md") : found2.target;
|
|
22427
|
+
const { meta, body } = parseFrontmatter(fs66.readFileSync(file, "utf8"));
|
|
22316
22428
|
if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
|
|
22317
22429
|
const { body: next, replaced } = splice(body, name, section);
|
|
22318
|
-
|
|
22430
|
+
fs66.writeFileSync(file, serializeFrontmatter(meta) + "\n" + next);
|
|
22319
22431
|
say(`${replaced ? "rewrote" : "wrote"} the \`${name}\` section on #${id} (${rel(file)})`);
|
|
22320
22432
|
return { id, specAgent: name, replaced, file: rel(file) };
|
|
22321
22433
|
}
|
|
@@ -22324,7 +22436,7 @@ function readSection(file, text5) {
|
|
|
22324
22436
|
let raw;
|
|
22325
22437
|
if (file !== void 0) {
|
|
22326
22438
|
try {
|
|
22327
|
-
raw =
|
|
22439
|
+
raw = fs66.readFileSync(file, "utf8");
|
|
22328
22440
|
} catch {
|
|
22329
22441
|
die(`can't read ${file} \u2014 write your section to a file, then pass its path`);
|
|
22330
22442
|
}
|
|
@@ -22790,11 +22902,11 @@ var init_shared = __esm({
|
|
|
22790
22902
|
});
|
|
22791
22903
|
|
|
22792
22904
|
// ../cli/src/commands/goal.ts
|
|
22793
|
-
import
|
|
22905
|
+
import fs67 from "node:fs";
|
|
22794
22906
|
function load() {
|
|
22795
22907
|
const file = goalsFile();
|
|
22796
22908
|
if (!file) die("Goals exist only inside a WildCrane goal discussion.");
|
|
22797
|
-
const held4 = JSON.parse(
|
|
22909
|
+
const held4 = JSON.parse(fs67.readFileSync(file, "utf8"));
|
|
22798
22910
|
if (held4.goals !== void 0 && held4.ideas !== void 0 && JSON.stringify(held4.goals) !== JSON.stringify(held4.ideas)) die("goals and ideas do not match.");
|
|
22799
22911
|
const goals = held4.goals ?? held4.ideas;
|
|
22800
22912
|
if (!Array.isArray(goals)) die("The goals array is missing or invalid.");
|
|
@@ -22819,7 +22931,7 @@ function splitBody2(text5) {
|
|
|
22819
22931
|
}
|
|
22820
22932
|
function readBody(bodyFile) {
|
|
22821
22933
|
if (!bodyFile) die("--body-file is required; write the goal in a writable temporary file first.");
|
|
22822
|
-
const text5 =
|
|
22934
|
+
const text5 = fs67.readFileSync(bodyFile, "utf8");
|
|
22823
22935
|
if (!text5.trim()) die("The goal must not be empty.");
|
|
22824
22936
|
return splitBody2(text5);
|
|
22825
22937
|
}
|
|
@@ -22831,7 +22943,7 @@ function named(held4, id) {
|
|
|
22831
22943
|
function cmdGoal(args, opts) {
|
|
22832
22944
|
const sub = args[0] ?? "";
|
|
22833
22945
|
const { file, held: held4 } = load();
|
|
22834
|
-
const write2 = () =>
|
|
22946
|
+
const write2 = () => fs67.writeFileSync(file, `${JSON.stringify({ workflows: held4.workflows, ideas: held4.goals }, null, 2)}
|
|
22835
22947
|
`);
|
|
22836
22948
|
if (sub === "new") {
|
|
22837
22949
|
const title = opts.title?.trim();
|
|
@@ -22901,8 +23013,8 @@ __export(board_exports, {
|
|
|
22901
23013
|
buildBoardProgram: () => buildBoardProgram
|
|
22902
23014
|
});
|
|
22903
23015
|
import crypto4 from "node:crypto";
|
|
22904
|
-
import
|
|
22905
|
-
import
|
|
23016
|
+
import fs68 from "node:fs";
|
|
23017
|
+
import path70 from "node:path";
|
|
22906
23018
|
async function dispatch(move2, cmd, args, opts, cli) {
|
|
22907
23019
|
const ctx = ctxOf(cmd, cli.program);
|
|
22908
23020
|
const data = await runAction(ctx, {}, async () => {
|
|
@@ -22941,7 +23053,7 @@ async function cloudCard(task, opts) {
|
|
|
22941
23053
|
let request;
|
|
22942
23054
|
if (typeof opts.bodyFile === "string") {
|
|
22943
23055
|
try {
|
|
22944
|
-
request =
|
|
23056
|
+
request = fs68.readFileSync(opts.bodyFile, "utf8");
|
|
22945
23057
|
} catch {
|
|
22946
23058
|
}
|
|
22947
23059
|
}
|
|
@@ -22971,7 +23083,7 @@ async function cloudCard(task, opts) {
|
|
|
22971
23083
|
const file = creationReceipt(`${checked.value}:${requestId}`, slot);
|
|
22972
23084
|
const journal = readCreation(file, said3);
|
|
22973
23085
|
if (!journal.made) {
|
|
22974
|
-
const key = `akb-${
|
|
23086
|
+
const key = `akb-${path70.basename(file, ".json").slice(0, 40)}`;
|
|
22975
23087
|
const made = await createProjectTask(checked.value, journal.said, key);
|
|
22976
23088
|
if (!made.ok) throw new BoardError(creationError(`Cloud did not make the task: ${made.error}`, triage), { kind: "refused" });
|
|
22977
23089
|
journal.made = made.value;
|
|
@@ -23116,16 +23228,16 @@ var init_board2 = __esm({
|
|
|
23116
23228
|
});
|
|
23117
23229
|
|
|
23118
23230
|
// ../cli/src/lib/board-cli.ts
|
|
23119
|
-
import
|
|
23120
|
-
import
|
|
23231
|
+
import fs69 from "node:fs";
|
|
23232
|
+
import path71 from "node:path";
|
|
23121
23233
|
function useBoard(found2, dirNamed) {
|
|
23122
|
-
if (!found2.named && found2.board ===
|
|
23234
|
+
if (!found2.named && found2.board === path71.join(found2.root, "docs", "kanban")) setBoardRoot(found2.root, dirNamed);
|
|
23123
23235
|
else setBoardDir(found2.board, found2.root);
|
|
23124
23236
|
useProjectState();
|
|
23125
23237
|
}
|
|
23126
23238
|
function whole(found2, move2) {
|
|
23127
23239
|
if (move2 === MAKES_A_BOARD) return found2;
|
|
23128
|
-
if (!
|
|
23240
|
+
if (!fs69.existsSync(path71.join(found2.board, "todo"))) {
|
|
23129
23241
|
die(`the board in ${found2.board} has no todo/ \u2014 run \`init\` to add what is missing.`, {
|
|
23130
23242
|
kind: "board-incomplete",
|
|
23131
23243
|
dir: found2.root
|
|
@@ -23134,13 +23246,13 @@ function whole(found2, move2) {
|
|
|
23134
23246
|
return found2;
|
|
23135
23247
|
}
|
|
23136
23248
|
function findBoardUpward(from, move2) {
|
|
23137
|
-
let dir =
|
|
23249
|
+
let dir = path71.resolve(from);
|
|
23138
23250
|
for (; ; ) {
|
|
23139
|
-
if (hasBoard(dir)) return { root: dir, board:
|
|
23251
|
+
if (hasBoard(dir)) return { root: dir, board: path71.join(dir, "docs", "kanban") };
|
|
23140
23252
|
if (isBoardDir(dir)) return { root: projectRootOf(dir), board: dir };
|
|
23141
|
-
const kept2 =
|
|
23142
|
-
if (move2 !== MAKES_A_BOARD &&
|
|
23143
|
-
const up =
|
|
23253
|
+
const kept2 = path71.join(dir, ".akb", "kanban");
|
|
23254
|
+
if (move2 !== MAKES_A_BOARD && fs69.existsSync(path71.join(kept2, "todo"))) return { root: dir, board: kept2 };
|
|
23255
|
+
const up = path71.dirname(dir);
|
|
23144
23256
|
if (up === dir) return null;
|
|
23145
23257
|
dir = up;
|
|
23146
23258
|
}
|
|
@@ -23148,11 +23260,11 @@ function findBoardUpward(from, move2) {
|
|
|
23148
23260
|
function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
|
|
23149
23261
|
const named2 = board2 ?? boardFromEnv();
|
|
23150
23262
|
if (named2) {
|
|
23151
|
-
const folder =
|
|
23152
|
-
if (!
|
|
23263
|
+
const folder = path71.resolve(cwd, named2);
|
|
23264
|
+
if (!fs69.existsSync(folder) && move2 !== MAKES_A_BOARD) {
|
|
23153
23265
|
die(`no such folder: ${folder}`, { kind: "no-such-folder", dir: folder });
|
|
23154
23266
|
}
|
|
23155
|
-
if (
|
|
23267
|
+
if (fs69.existsSync(folder) && !isBoardDir(folder) && move2 !== MAKES_A_BOARD) {
|
|
23156
23268
|
die(`${folder} is not a board \u2014 it has no todo/ and config.md. ${installHint}\u3002`, {
|
|
23157
23269
|
kind: "no-board",
|
|
23158
23270
|
dir: folder
|
|
@@ -23161,24 +23273,24 @@ function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
|
|
|
23161
23273
|
return { root: projectRootOf(folder), board: folder, named: true };
|
|
23162
23274
|
}
|
|
23163
23275
|
if (dir !== null) {
|
|
23164
|
-
const root =
|
|
23165
|
-
if (!
|
|
23166
|
-
const kept2 =
|
|
23167
|
-
if (!hasBoard(root) && move2 !== MAKES_A_BOARD &&
|
|
23276
|
+
const root = path71.resolve(dir);
|
|
23277
|
+
if (!fs69.existsSync(root)) die(`no such folder: ${root}`, { kind: "no-such-folder", dir: root });
|
|
23278
|
+
const kept2 = path71.join(root, ".akb", "kanban");
|
|
23279
|
+
if (!hasBoard(root) && move2 !== MAKES_A_BOARD && fs69.existsSync(path71.join(kept2, "todo"))) return whole({ root, board: kept2 }, move2);
|
|
23168
23280
|
if (!hasBoard(root) && move2 !== MAKES_A_BOARD) {
|
|
23169
23281
|
die(`no board in ${root} \u2014 it has no docs/kanban/. ${installHint}\u3002`, {
|
|
23170
23282
|
kind: "no-board",
|
|
23171
23283
|
dir: root
|
|
23172
23284
|
});
|
|
23173
23285
|
}
|
|
23174
|
-
return whole({ root, board:
|
|
23286
|
+
return whole({ root, board: path71.join(root, "docs", "kanban") }, move2);
|
|
23175
23287
|
}
|
|
23176
23288
|
const found2 = findBoardUpward(cwd, move2);
|
|
23177
23289
|
if (found2) return whole(found2, move2);
|
|
23178
|
-
if (move2 === MAKES_A_BOARD) return { root:
|
|
23290
|
+
if (move2 === MAKES_A_BOARD) return { root: path71.resolve(cwd), board: path71.join(path71.resolve(cwd), "docs", "kanban") };
|
|
23179
23291
|
die(
|
|
23180
|
-
`no board here. Looked in ${
|
|
23181
|
-
{ kind: "no-board", dir:
|
|
23292
|
+
`no board here. Looked in ${path71.resolve(cwd)} and every folder above it for docs/kanban/. ${installHint}\uFF0C\u6216\u7528 --board \u6307\u5B9A\u5DF2\u6709\u770B\u677F\u3002`,
|
|
23293
|
+
{ kind: "no-board", dir: path71.resolve(cwd) }
|
|
23182
23294
|
);
|
|
23183
23295
|
}
|
|
23184
23296
|
function editDistance(a, b) {
|
|
@@ -23260,8 +23372,8 @@ var init_board_cli = __esm({
|
|
|
23260
23372
|
init_io();
|
|
23261
23373
|
init_pointer();
|
|
23262
23374
|
MAKES_A_BOARD = "init";
|
|
23263
|
-
hasBoard = (dir) =>
|
|
23264
|
-
isBoardDir = (dir) =>
|
|
23375
|
+
hasBoard = (dir) => fs69.existsSync(path71.join(dir, "docs", "kanban"));
|
|
23376
|
+
isBoardDir = (dir) => fs69.existsSync(path71.join(dir, "todo")) && fs69.existsSync(path71.join(dir, "config.md"));
|
|
23265
23377
|
boardFromEnv = () => {
|
|
23266
23378
|
const named2 = process.env.AI4KANBAN_BOARD?.trim();
|
|
23267
23379
|
return named2 ? named2 : null;
|
|
@@ -23528,8 +23640,8 @@ var init_guide = __esm({
|
|
|
23528
23640
|
});
|
|
23529
23641
|
|
|
23530
23642
|
// ../cli/src/lib/agent/approve.ts
|
|
23531
|
-
import
|
|
23532
|
-
import
|
|
23643
|
+
import fs75 from "node:fs";
|
|
23644
|
+
import path77 from "node:path";
|
|
23533
23645
|
function approvalRequest(req) {
|
|
23534
23646
|
const id = req.id;
|
|
23535
23647
|
const card = id === void 0 ? null : findCard(id);
|
|
@@ -23547,12 +23659,12 @@ function advancePhase(id) {
|
|
|
23547
23659
|
const card = findCard(id);
|
|
23548
23660
|
const found2 = locate(id);
|
|
23549
23661
|
if (!card || !found2 || !awaitingApproval(card)) return { error: `#${id} is no longer waiting on approval.` };
|
|
23550
|
-
const file = found2.kind === "group" ?
|
|
23551
|
-
const { meta, body } = parseFrontmatter(
|
|
23662
|
+
const file = found2.kind === "group" ? path77.join(found2.target, "root.md") : found2.target;
|
|
23663
|
+
const { meta, body } = parseFrontmatter(fs75.readFileSync(file, "utf8"));
|
|
23552
23664
|
if (!meta) return { error: `#${id} has no frontmatter` };
|
|
23553
23665
|
meta.phase = card.stage + 1;
|
|
23554
23666
|
meta.status = "todo";
|
|
23555
|
-
|
|
23667
|
+
fs75.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
|
|
23556
23668
|
return null;
|
|
23557
23669
|
}
|
|
23558
23670
|
var init_approve = __esm({
|
|
@@ -23676,16 +23788,16 @@ var init_start = __esm({
|
|
|
23676
23788
|
});
|
|
23677
23789
|
|
|
23678
23790
|
// ../cli/src/lib/agent/task-chats.ts
|
|
23679
|
-
import
|
|
23680
|
-
import
|
|
23791
|
+
import fs76 from "node:fs";
|
|
23792
|
+
import path78 from "node:path";
|
|
23681
23793
|
function taskChatFile(task, home3) {
|
|
23682
23794
|
if (!TASK.test(task)) throw new Error(`${task} is not a task id.`);
|
|
23683
|
-
return
|
|
23795
|
+
return path78.join(taskChatsDir(home3), `${task}.jsonl`);
|
|
23684
23796
|
}
|
|
23685
23797
|
function readTaskChat(task, home3) {
|
|
23686
23798
|
let text5;
|
|
23687
23799
|
try {
|
|
23688
|
-
text5 =
|
|
23800
|
+
text5 = fs76.readFileSync(taskChatFile(task, home3), "utf8");
|
|
23689
23801
|
} catch {
|
|
23690
23802
|
return [];
|
|
23691
23803
|
}
|
|
@@ -23706,13 +23818,13 @@ var init_task_chats = __esm({
|
|
|
23706
23818
|
"use strict";
|
|
23707
23819
|
init_home();
|
|
23708
23820
|
TASK = /^[\w-]{1,100}$/;
|
|
23709
|
-
taskChatsDir = (home3 = machineHome()) =>
|
|
23821
|
+
taskChatsDir = (home3 = machineHome()) => path78.join(home3, "chats");
|
|
23710
23822
|
}
|
|
23711
23823
|
});
|
|
23712
23824
|
|
|
23713
23825
|
// ../cli/src/lib/agent/memory-review.ts
|
|
23714
|
-
import
|
|
23715
|
-
import
|
|
23826
|
+
import fs77 from "node:fs";
|
|
23827
|
+
import path79 from "node:path";
|
|
23716
23828
|
function taskChatsToReview(tasks) {
|
|
23717
23829
|
const out = [];
|
|
23718
23830
|
for (const ref of [...new Set(tasks)]) {
|
|
@@ -23734,8 +23846,8 @@ function readArchived2(id) {
|
|
|
23734
23846
|
const found2 = Number.isInteger(id) && id > 0 ? locateArchived(id) : null;
|
|
23735
23847
|
if (!found2) return null;
|
|
23736
23848
|
try {
|
|
23737
|
-
const file = found2.kind === "group" ?
|
|
23738
|
-
const { meta, body } = parseFrontmatter(
|
|
23849
|
+
const file = found2.kind === "group" ? path79.join(found2.target, "root.md") : found2.target;
|
|
23850
|
+
const { meta, body } = parseFrontmatter(fs77.readFileSync(file, "utf8"));
|
|
23739
23851
|
if (!meta) return null;
|
|
23740
23852
|
return { id, title: meta.title, modules: meta.modules ?? [], rejected: meta.rejected, body };
|
|
23741
23853
|
} catch {
|
|
@@ -23804,17 +23916,17 @@ var init_memory_review = __esm({
|
|
|
23804
23916
|
});
|
|
23805
23917
|
|
|
23806
23918
|
// ../cli/src/lib/agent/dismissal-review.ts
|
|
23807
|
-
import
|
|
23808
|
-
import
|
|
23919
|
+
import fs78 from "node:fs";
|
|
23920
|
+
import path80 from "node:path";
|
|
23809
23921
|
function rejectionsToReview() {
|
|
23810
|
-
const dir =
|
|
23811
|
-
if (!
|
|
23922
|
+
const dir = path80.join(ARCHIVE, REJECTIONS_TO_REVIEW);
|
|
23923
|
+
if (!fs78.existsSync(dir)) return [];
|
|
23812
23924
|
const found2 = [];
|
|
23813
23925
|
for (const file of walkMd(dir)) {
|
|
23814
23926
|
const id = archivedId(file);
|
|
23815
23927
|
if (id === null) continue;
|
|
23816
23928
|
try {
|
|
23817
|
-
const { meta } = parseFrontmatter(
|
|
23929
|
+
const { meta } = parseFrontmatter(fs78.readFileSync(file, "utf8"));
|
|
23818
23930
|
if (!meta?.rejected || meta.rejected_by !== "user" || !meta.rejected_reason.trim()) continue;
|
|
23819
23931
|
found2.push({ id, title: meta.title, file: rel(file), reason: meta.rejected_reason.trim(), rejectedAt: meta.rejected_at });
|
|
23820
23932
|
} catch {
|
|
@@ -23844,7 +23956,7 @@ function citedSources(text5) {
|
|
|
23844
23956
|
function withdrawnSources() {
|
|
23845
23957
|
const cited = [...new Set(plannerCopies("rejected.md").flatMap((file) => {
|
|
23846
23958
|
try {
|
|
23847
|
-
return citedSources(
|
|
23959
|
+
return citedSources(fs78.readFileSync(file, "utf8"));
|
|
23848
23960
|
} catch {
|
|
23849
23961
|
return [];
|
|
23850
23962
|
}
|
|
@@ -23869,8 +23981,8 @@ var init_dismissal_review = __esm({
|
|
|
23869
23981
|
});
|
|
23870
23982
|
|
|
23871
23983
|
// ../cli/src/lib/agent/flow.ts
|
|
23872
|
-
import
|
|
23873
|
-
import
|
|
23984
|
+
import fs79 from "node:fs";
|
|
23985
|
+
import path81 from "node:path";
|
|
23874
23986
|
function readCard2(id, home3 = "board") {
|
|
23875
23987
|
const found2 = home3 === "archive" ? locateArchived(id) : locate(id);
|
|
23876
23988
|
if (!found2) {
|
|
@@ -23879,10 +23991,10 @@ function readCard2(id, home3 = "board") {
|
|
|
23879
23991
|
{ kind: "card-not-found", id }
|
|
23880
23992
|
);
|
|
23881
23993
|
}
|
|
23882
|
-
const file = found2.kind === "group" ?
|
|
23994
|
+
const file = found2.kind === "group" ? path81.join(found2.target, "root.md") : found2.target;
|
|
23883
23995
|
let text5;
|
|
23884
23996
|
try {
|
|
23885
|
-
text5 =
|
|
23997
|
+
text5 = fs79.readFileSync(file, "utf8");
|
|
23886
23998
|
} catch {
|
|
23887
23999
|
die(`#${id} is on the board but ${rel(file)} can't be read.`, { kind: "card-unreadable", id });
|
|
23888
24000
|
}
|
|
@@ -24006,7 +24118,7 @@ function workspaceField(delivery) {
|
|
|
24006
24118
|
if (!delivery?.worktree) return [];
|
|
24007
24119
|
return field("workspace", [
|
|
24008
24120
|
`write code in ${delivery.worktree} \u2014 this delivery's own worktree, on branch ${delivery.branch}.`,
|
|
24009
|
-
|
|
24121
|
+
path81.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.`,
|
|
24010
24122
|
`every board command names the project's own copy: \`${boardCommandFor()} <command>\`.`,
|
|
24011
24123
|
`${rel(REPO_ROOT)} is the project \u2014 the card, the memory files and the docs are changed there, not here.`
|
|
24012
24124
|
]);
|
|
@@ -24750,7 +24862,7 @@ __export(watch_exports, {
|
|
|
24750
24862
|
watchRun: () => watchRun
|
|
24751
24863
|
});
|
|
24752
24864
|
import { spawn as spawn4 } from "node:child_process";
|
|
24753
|
-
import
|
|
24865
|
+
import fs80 from "node:fs";
|
|
24754
24866
|
async function watchRun(sessionId, resume = startResume) {
|
|
24755
24867
|
const spec = readSpec(sessionId);
|
|
24756
24868
|
const run2 = peekRun(sessionId);
|
|
@@ -24786,11 +24898,11 @@ async function watchRun(sessionId, resume = startResume) {
|
|
|
24786
24898
|
const record = claimed ?? run2;
|
|
24787
24899
|
const sub = record.action === "sub";
|
|
24788
24900
|
if (claim) await setCardStatus(claim.cardId, claim.status);
|
|
24789
|
-
|
|
24790
|
-
const logFile =
|
|
24901
|
+
fs80.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
24902
|
+
const logFile = fs80.createWriteStream(record.logPath, { flags: "a" });
|
|
24791
24903
|
logFile.on("error", () => {
|
|
24792
24904
|
});
|
|
24793
|
-
const eventsFile =
|
|
24905
|
+
const eventsFile = fs80.createWriteStream(eventsPathOf(sessionId), { flags: "a" });
|
|
24794
24906
|
eventsFile.on("error", () => {
|
|
24795
24907
|
});
|
|
24796
24908
|
const event2 = (e) => eventsFile.write(`${JSON.stringify(e)}
|
|
@@ -25379,7 +25491,7 @@ var init_watch = __esm({
|
|
|
25379
25491
|
});
|
|
25380
25492
|
|
|
25381
25493
|
// ../cli/src/kanban.ts
|
|
25382
|
-
import
|
|
25494
|
+
import fs86 from "node:fs";
|
|
25383
25495
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
25384
25496
|
|
|
25385
25497
|
// ../cli/src/lib/cli/agent.ts
|
|
@@ -25868,8 +25980,8 @@ async function testAgent(args) {
|
|
|
25868
25980
|
}
|
|
25869
25981
|
|
|
25870
25982
|
// ../cli/src/commands/cloud.ts
|
|
25871
|
-
import
|
|
25872
|
-
import
|
|
25983
|
+
import fs71 from "node:fs";
|
|
25984
|
+
import path73 from "node:path";
|
|
25873
25985
|
|
|
25874
25986
|
// ../cli/src/lib/cloud/avatar.ts
|
|
25875
25987
|
init_session();
|
|
@@ -25917,13 +26029,13 @@ init_config();
|
|
|
25917
26029
|
init_home();
|
|
25918
26030
|
init_config();
|
|
25919
26031
|
init_session();
|
|
25920
|
-
import
|
|
25921
|
-
import
|
|
26032
|
+
import fs70 from "node:fs";
|
|
26033
|
+
import path72 from "node:path";
|
|
25922
26034
|
var FRESH_MS = 10 * 6e4;
|
|
25923
26035
|
var GRACE_MS = 7 * 24 * 60 * 6e4;
|
|
25924
|
-
var heldFile = () =>
|
|
26036
|
+
var heldFile = () => path72.join(machineHome(), "pro.json");
|
|
25925
26037
|
function forgetPro() {
|
|
25926
|
-
|
|
26038
|
+
fs70.rmSync(heldFile(), { force: true });
|
|
25927
26039
|
}
|
|
25928
26040
|
|
|
25929
26041
|
// ../cli/src/lib/cloud/account.ts
|
|
@@ -26100,10 +26212,10 @@ async function cloudImage(opts, program2) {
|
|
|
26100
26212
|
const prompt = opts.prompt.trim();
|
|
26101
26213
|
if (!prompt) usage("Missing the prompt.", line);
|
|
26102
26214
|
const references = opts.refs.map((file) => {
|
|
26103
|
-
const type = REF_TYPES[
|
|
26215
|
+
const type = REF_TYPES[path73.extname(file).toLowerCase()];
|
|
26104
26216
|
if (!type) usage(`--ref ${file}: use a PNG, JPEG or WebP file.`, line);
|
|
26105
26217
|
try {
|
|
26106
|
-
return `data:${type};base64,${
|
|
26218
|
+
return `data:${type};base64,${fs71.readFileSync(file).toString("base64")}`;
|
|
26107
26219
|
} catch (e) {
|
|
26108
26220
|
usage(`--ref ${file}: ${e instanceof Error ? e.message : String(e)}`, line);
|
|
26109
26221
|
}
|
|
@@ -26209,31 +26321,31 @@ async function generate(g) {
|
|
|
26209
26321
|
refuse(g.failed, g.fallback);
|
|
26210
26322
|
}
|
|
26211
26323
|
const out = g.outFor?.(res, g.out) ?? g.out;
|
|
26212
|
-
|
|
26213
|
-
|
|
26324
|
+
fs71.mkdirSync(path73.dirname(path73.resolve(out)), { recursive: true });
|
|
26325
|
+
fs71.writeFileSync(out, Buffer.from(await res.arrayBuffer()));
|
|
26214
26326
|
say(`${out} (${g.label(res)})`);
|
|
26215
26327
|
return { out };
|
|
26216
26328
|
}
|
|
26217
26329
|
|
|
26218
26330
|
// ../cli/src/commands/cloud-import.ts
|
|
26219
26331
|
import crypto5 from "node:crypto";
|
|
26220
|
-
import
|
|
26221
|
-
import
|
|
26332
|
+
import fs73 from "node:fs";
|
|
26333
|
+
import path75 from "node:path";
|
|
26222
26334
|
|
|
26223
26335
|
// ../cloud/src/board-files.ts
|
|
26224
26336
|
var ROOTS = ["todo/", ".archive/", "memory/", "workflows/", "triage/", "assets/", "rules/"];
|
|
26225
26337
|
var SETTINGS = ["modules.md", "config.md"];
|
|
26226
|
-
var isSettingsPath = (
|
|
26338
|
+
var isSettingsPath = (path87) => SETTINGS.includes(path87) || /^rules\/(?:workflows\/)?[^/]+\.md$/.test(path87);
|
|
26227
26339
|
var BOARD_FILE_LIMIT = 256 * 1024;
|
|
26228
|
-
function isBoardPath(
|
|
26229
|
-
if (!
|
|
26230
|
-
if (
|
|
26231
|
-
if (
|
|
26232
|
-
if (
|
|
26233
|
-
if (SETTINGS.includes(
|
|
26234
|
-
if (
|
|
26235
|
-
if (
|
|
26236
|
-
return ROOTS.some((root) =>
|
|
26340
|
+
function isBoardPath(path87) {
|
|
26341
|
+
if (!path87 || path87.length > 500 || path87.startsWith("/") || path87.includes("\\") || path87.includes("\0")) return false;
|
|
26342
|
+
if (path87.split("/").some((part) => !part || part === "." || part === "..")) return false;
|
|
26343
|
+
if (path87 === "todo/README.md" || path87.split("/").includes(".sync")) return false;
|
|
26344
|
+
if (path87.startsWith("workflows/")) return true;
|
|
26345
|
+
if (SETTINGS.includes(path87)) return true;
|
|
26346
|
+
if (path87.startsWith("rules/")) return isSettingsPath(path87);
|
|
26347
|
+
if (path87.startsWith("assets/")) return path87.endsWith(".json");
|
|
26348
|
+
return ROOTS.some((root) => path87.startsWith(root)) && path87.endsWith(".md");
|
|
26237
26349
|
}
|
|
26238
26350
|
var workflowFolder = (id) => id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
26239
26351
|
|
|
@@ -26337,8 +26449,8 @@ init_bundled();
|
|
|
26337
26449
|
// ../cli/src/lib/agents/shared-memory.ts
|
|
26338
26450
|
init_bundled();
|
|
26339
26451
|
init_parse();
|
|
26340
|
-
import
|
|
26341
|
-
import
|
|
26452
|
+
import fs72 from "node:fs";
|
|
26453
|
+
import path74 from "node:path";
|
|
26342
26454
|
function sharedMemory(board2) {
|
|
26343
26455
|
const declared = /* @__PURE__ */ new Map();
|
|
26344
26456
|
const take = (folder, text5, builtIn) => {
|
|
@@ -26350,16 +26462,16 @@ function sharedMemory(board2) {
|
|
|
26350
26462
|
const at = /^([^/]+)\/AGENT\.md$/.exec(key);
|
|
26351
26463
|
if (at) take(at[1], text5, true);
|
|
26352
26464
|
}
|
|
26353
|
-
const root =
|
|
26465
|
+
const root = path74.join(board2, "agents");
|
|
26354
26466
|
let folders2 = [];
|
|
26355
26467
|
try {
|
|
26356
|
-
folders2 =
|
|
26468
|
+
folders2 = fs72.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
|
|
26357
26469
|
} catch {
|
|
26358
26470
|
}
|
|
26359
26471
|
for (const folder of folders2.sort()) {
|
|
26360
26472
|
let text5 = null;
|
|
26361
26473
|
try {
|
|
26362
|
-
text5 =
|
|
26474
|
+
text5 = fs72.readFileSync(path74.join(root, folder, "AGENT.md"), "utf8");
|
|
26363
26475
|
} catch {
|
|
26364
26476
|
}
|
|
26365
26477
|
take(folder, text5, false);
|
|
@@ -26448,24 +26560,24 @@ async function findProject(asked) {
|
|
|
26448
26560
|
}
|
|
26449
26561
|
throw new Refused(`No project of yours has the id or prefix "${asked}".`, "not_found");
|
|
26450
26562
|
}
|
|
26451
|
-
var read = (file) =>
|
|
26563
|
+
var read = (file) => fs73.readFileSync(file, "utf8");
|
|
26452
26564
|
var bytes2 = (text5) => Buffer.byteLength(text5, "utf8");
|
|
26453
26565
|
function files(dir) {
|
|
26454
|
-
if (!
|
|
26566
|
+
if (!fs73.existsSync(dir)) return [];
|
|
26455
26567
|
const out = [];
|
|
26456
|
-
for (const entry of
|
|
26568
|
+
for (const entry of fs73.readdirSync(dir, { withFileTypes: true })) {
|
|
26457
26569
|
if (entry.name.startsWith(".")) continue;
|
|
26458
|
-
const full =
|
|
26570
|
+
const full = path75.join(dir, entry.name);
|
|
26459
26571
|
if (entry.isDirectory()) out.push(...files(full));
|
|
26460
26572
|
else if (entry.isFile()) out.push(full);
|
|
26461
26573
|
}
|
|
26462
26574
|
return out;
|
|
26463
26575
|
}
|
|
26464
|
-
var posix2 = (p) => p.split(
|
|
26576
|
+
var posix2 = (p) => p.split(path75.sep).join("/");
|
|
26465
26577
|
function openCards() {
|
|
26466
26578
|
const out = [];
|
|
26467
26579
|
for (const file of walkMd(TODO)) {
|
|
26468
|
-
const rel2 = posix2(
|
|
26580
|
+
const rel2 = posix2(path75.relative(TODO, file));
|
|
26469
26581
|
if (rel2 === "README.md") continue;
|
|
26470
26582
|
const parts = rel2.split("/");
|
|
26471
26583
|
const text5 = read(file);
|
|
@@ -26499,14 +26611,14 @@ function bodyProblems(text5) {
|
|
|
26499
26611
|
return out;
|
|
26500
26612
|
}
|
|
26501
26613
|
function packWorkflow(flow) {
|
|
26502
|
-
const own =
|
|
26614
|
+
const own = path75.join(WORKFLOWS, flow.id);
|
|
26503
26615
|
const out = {};
|
|
26504
|
-
for (const file of files(own)) out[posix2(
|
|
26616
|
+
for (const file of files(own)) out[posix2(path75.relative(own, file))] = read(file);
|
|
26505
26617
|
const named2 = [...flow.previews, ...flow.land, ...flow.scheduled].map((h) => h.agent).filter((a) => !LAND_ROLES.includes(a));
|
|
26506
26618
|
for (const agent of new Set(named2)) {
|
|
26507
|
-
const dir =
|
|
26508
|
-
if (
|
|
26509
|
-
for (const file of files(dir)) out[`agents/${agent}/${posix2(
|
|
26619
|
+
const dir = path75.join(AGENTS, agent);
|
|
26620
|
+
if (fs73.existsSync(path75.join(dir, "AGENT.md"))) {
|
|
26621
|
+
for (const file of files(dir)) out[`agents/${agent}/${posix2(path75.relative(dir, file))}`] = read(file);
|
|
26510
26622
|
continue;
|
|
26511
26623
|
}
|
|
26512
26624
|
for (const [key, text5] of Object.entries(BUNDLED_AGENT_FILES)) {
|
|
@@ -26558,8 +26670,8 @@ function draftsOf(text5) {
|
|
|
26558
26670
|
const at = /^\.(assets|mockups)\/(.+)$/.exec(name);
|
|
26559
26671
|
if (!at) continue;
|
|
26560
26672
|
const base = at[1] === "assets" ? ASSETS : MOCKUPS;
|
|
26561
|
-
const file =
|
|
26562
|
-
if (file.startsWith(base +
|
|
26673
|
+
const file = path75.resolve(base, at[2]);
|
|
26674
|
+
if (file.startsWith(base + path75.sep) && fs73.statSync(file, { throwIfNoEntry: false })?.isFile()) out.set(name, file);
|
|
26563
26675
|
}
|
|
26564
26676
|
return [...out].map(([name, file]) => ({ name, file }));
|
|
26565
26677
|
}
|
|
@@ -26593,9 +26705,9 @@ async function uploadDrafts(task, drafts) {
|
|
|
26593
26705
|
const assets = {};
|
|
26594
26706
|
for (const { name, file } of drafts) {
|
|
26595
26707
|
if (!card.body.includes(name)) continue;
|
|
26596
|
-
const ext =
|
|
26708
|
+
const ext = path75.extname(file).toLowerCase();
|
|
26597
26709
|
const type = TYPES[ext] ?? "application/octet-stream";
|
|
26598
|
-
const data =
|
|
26710
|
+
const data = fs73.readFileSync(file);
|
|
26599
26711
|
const artifact = await api("POST", `/v1/tasks/${task}/artifacts`, { type: kindOf(ext), name: name.slice(0, 200) });
|
|
26600
26712
|
const upload = await api("POST", `/v1/artifacts/${artifact.id}/uploads`, { content_type: type, size: Math.max(data.length, 1) });
|
|
26601
26713
|
await api("PUT", `/v1/uploads/${upload.id}`, void 0, { bytes: data, type });
|
|
@@ -26638,10 +26750,10 @@ usage: ${program2} cloud import --project <id or prefix> [--dry-run]`, { kind: "
|
|
|
26638
26750
|
async function run(asked, dryRun) {
|
|
26639
26751
|
const project = await findProject(asked);
|
|
26640
26752
|
const cloudFlows = await all(`/v1/workflows?workspace=${encodeURIComponent(project.workspace)}`);
|
|
26641
|
-
const board2 = crypto5.createHash("sha256").update(
|
|
26753
|
+
const board2 = crypto5.createHash("sha256").update(fs73.realpathSync(KANBAN)).digest("hex").slice(0, 16);
|
|
26642
26754
|
const cards = openCards();
|
|
26643
26755
|
const problems = [];
|
|
26644
|
-
const where = (file) => posix2(
|
|
26756
|
+
const where = (file) => posix2(path75.relative(KANBAN, file));
|
|
26645
26757
|
const own = workflows().filter((w) => !w.builtIn);
|
|
26646
26758
|
const packed = /* @__PURE__ */ new Map();
|
|
26647
26759
|
for (const flow of own) {
|
|
@@ -26668,7 +26780,7 @@ async function run(asked, dryRun) {
|
|
|
26668
26780
|
const root = `projects/${project.id}/`;
|
|
26669
26781
|
const boardFiles = [];
|
|
26670
26782
|
for (const file of [...files(MEMORY), ...files(RULES), CONFIG, MODULES_MD]) {
|
|
26671
|
-
if (!
|
|
26783
|
+
if (!fs73.existsSync(file)) continue;
|
|
26672
26784
|
const rel2 = where(file);
|
|
26673
26785
|
if (!isBoardPath(rel2)) continue;
|
|
26674
26786
|
const content = read(file);
|
|
@@ -26779,16 +26891,16 @@ async function run(asked, dryRun) {
|
|
|
26779
26891
|
}
|
|
26780
26892
|
function leftLocal(own) {
|
|
26781
26893
|
const out = [];
|
|
26782
|
-
const count4 = (dir) =>
|
|
26894
|
+
const count4 = (dir) => fs73.existsSync(dir) ? walkMd(dir).length : 0;
|
|
26783
26895
|
const archived = count4(ARCHIVE);
|
|
26784
26896
|
if (archived) out.push(`.archive/ (${archived})`);
|
|
26785
26897
|
const triage = count4(TRIAGE);
|
|
26786
26898
|
if (triage) out.push(`triage/ (${triage})`);
|
|
26787
|
-
if (
|
|
26788
|
-
if (
|
|
26899
|
+
if (fs73.existsSync(DELIVERIES)) out.push("deliveries/");
|
|
26900
|
+
if (fs73.existsSync(path75.join(KANBAN, "metrics.csv"))) out.push("metrics.csv");
|
|
26789
26901
|
const named2 = new Set(own.flatMap((w) => [...w.previews, ...w.land, ...w.scheduled].map((h) => h.agent)));
|
|
26790
|
-
if (
|
|
26791
|
-
for (const e of
|
|
26902
|
+
if (fs73.existsSync(AGENTS)) {
|
|
26903
|
+
for (const e of fs73.readdirSync(AGENTS, { withFileTypes: true })) {
|
|
26792
26904
|
if (e.isDirectory() && !e.name.startsWith(".") && !named2.has(e.name)) out.push(`agents/${e.name}/`);
|
|
26793
26905
|
}
|
|
26794
26906
|
}
|
|
@@ -26806,8 +26918,8 @@ init_session();
|
|
|
26806
26918
|
init_io();
|
|
26807
26919
|
init_paths();
|
|
26808
26920
|
import { createHash as createHash4 } from "node:crypto";
|
|
26809
|
-
import
|
|
26810
|
-
import
|
|
26921
|
+
import fs74 from "node:fs";
|
|
26922
|
+
import path76 from "node:path";
|
|
26811
26923
|
var SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
26812
26924
|
var CAPTURED = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
26813
26925
|
var MAX_BYTES3 = 50 * 1024 * 1024;
|
|
@@ -26906,10 +27018,10 @@ This template has no preview.`);
|
|
|
26906
27018
|
const written = [];
|
|
26907
27019
|
for (const p of previews) {
|
|
26908
27020
|
const bytes3 = Buffer.from(await (await ask(`/${at}/previews/${route(p.path)}`, {}, WITHOUT)).arrayBuffer());
|
|
26909
|
-
const file =
|
|
26910
|
-
const out =
|
|
26911
|
-
|
|
26912
|
-
|
|
27021
|
+
const file = path76.basename(p.path);
|
|
27022
|
+
const out = path76.join(opts.to, file.startsWith(`${name}-`) ? file : `${name}-${file}`);
|
|
27023
|
+
fs74.mkdirSync(opts.to, { recursive: true });
|
|
27024
|
+
fs74.writeFileSync(out, bytes3);
|
|
26913
27025
|
written.push(out);
|
|
26914
27026
|
say(out);
|
|
26915
27027
|
}
|
|
@@ -26922,15 +27034,15 @@ async function templateGet(name, opts, program2) {
|
|
|
26922
27034
|
const { template } = await asJson(await ask(`/${at}`, {}, WITHOUT));
|
|
26923
27035
|
const fetched = [];
|
|
26924
27036
|
for (const f of template.files) {
|
|
26925
|
-
const out =
|
|
26926
|
-
if (!out.startsWith(
|
|
27037
|
+
const out = path76.resolve(to, f.path);
|
|
27038
|
+
if (!out.startsWith(path76.resolve(to) + path76.sep)) refuse2(T.corrupt, WITHOUT);
|
|
26927
27039
|
const bytes3 = Buffer.from(await (await ask(`/${at}/files/${route(f.path)}`, {}, WITHOUT)).arrayBuffer());
|
|
26928
27040
|
if (bytes3.byteLength !== f.size || createHash4("sha256").update(bytes3).digest("hex") !== f.sha256) {
|
|
26929
27041
|
refuse2(T.corrupt, WITHOUT);
|
|
26930
27042
|
}
|
|
26931
27043
|
fetched.push({ out, bytes: bytes3 });
|
|
26932
27044
|
}
|
|
26933
|
-
const taken = fetched.filter((f) =>
|
|
27045
|
+
const taken = fetched.filter((f) => fs74.existsSync(f.out)).map((f) => path76.relative(to, f.out));
|
|
26934
27046
|
if (taken.length > 0) {
|
|
26935
27047
|
refuse2({
|
|
26936
27048
|
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`,
|
|
@@ -26938,8 +27050,8 @@ async function templateGet(name, opts, program2) {
|
|
|
26938
27050
|
});
|
|
26939
27051
|
}
|
|
26940
27052
|
for (const f of fetched) {
|
|
26941
|
-
|
|
26942
|
-
|
|
27053
|
+
fs74.mkdirSync(path76.dirname(f.out), { recursive: true });
|
|
27054
|
+
fs74.writeFileSync(f.out, f.bytes);
|
|
26943
27055
|
}
|
|
26944
27056
|
say(`\u5DF2\u53D6\u56DE ${name}\uFF1A${fetched.length} \u4E2A\u6587\u4EF6\uFF0C\u4F4D\u4E8E ${to}
|
|
26945
27057
|
Fetched ${name}: ${fetched.length} files into ${to}`);
|
|
@@ -26957,25 +27069,25 @@ async function templateSource(name, opts) {
|
|
|
26957
27069
|
say(`evidence: ${e.path}`);
|
|
26958
27070
|
continue;
|
|
26959
27071
|
}
|
|
26960
|
-
const out =
|
|
26961
|
-
|
|
26962
|
-
|
|
27072
|
+
const out = path76.join(opts.to, `${name}-${path76.basename(e.path)}`);
|
|
27073
|
+
fs74.mkdirSync(opts.to, { recursive: true });
|
|
27074
|
+
fs74.writeFileSync(out, Buffer.from(await (await ask(`/${at}/evidence/${route(e.path)}`)).arrayBuffer()));
|
|
26963
27075
|
written.push(out);
|
|
26964
27076
|
say(out);
|
|
26965
27077
|
}
|
|
26966
27078
|
return { name, source, evidence: written.length > 0 ? written : evidence.map((e) => e.path) };
|
|
26967
27079
|
}
|
|
26968
27080
|
async function templatePublish(dir, opts) {
|
|
26969
|
-
const root =
|
|
26970
|
-
const manifest =
|
|
26971
|
-
if (!
|
|
26972
|
-
const meta = readTemplateMeta(
|
|
27081
|
+
const root = path76.resolve(dir);
|
|
27082
|
+
const manifest = path76.join(root, "template.md");
|
|
27083
|
+
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.");
|
|
27084
|
+
const meta = readTemplateMeta(fs74.readFileSync(manifest, "utf8"));
|
|
26973
27085
|
const files2 = walk2(root);
|
|
26974
|
-
const total = files2.reduce((sum, f) => sum +
|
|
27086
|
+
const total = files2.reduce((sum, f) => sum + fs74.statSync(path76.join(root, f)).size, 0);
|
|
26975
27087
|
if (total > MAX_BYTES3) invalid("\u6A21\u677F\u6587\u4EF6\u5408\u8BA1\u8D85\u8FC7 50 MB\u3002", "the template\u2019s files exceed 50 MB.");
|
|
26976
27088
|
for (const extra of [...opts.preview, ...opts.evidence]) {
|
|
26977
|
-
if (!
|
|
26978
|
-
if (
|
|
27089
|
+
if (!fs74.existsSync(extra)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${extra}\u3002`, `${extra} does not exist.`);
|
|
27090
|
+
if (path76.resolve(extra).startsWith(root + path76.sep)) {
|
|
26979
27091
|
invalid(
|
|
26980
27092
|
`${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`,
|
|
26981
27093
|
`${extra} is inside the template folder; keep previews and evidence outside it.`
|
|
@@ -26987,10 +27099,10 @@ async function templatePublish(dir, opts) {
|
|
|
26987
27099
|
}
|
|
26988
27100
|
const form = new FormData();
|
|
26989
27101
|
form.set("meta", JSON.stringify({ ...meta, replace: !!opts.replace }));
|
|
26990
|
-
const add = (kind, name, file) => form.append(`${kind}:${name}`, new Blob([
|
|
26991
|
-
for (const f of files2) add("files", f,
|
|
26992
|
-
for (const f of opts.preview) add("previews",
|
|
26993
|
-
for (const f of opts.evidence) add("evidence",
|
|
27102
|
+
const add = (kind, name, file) => form.append(`${kind}:${name}`, new Blob([fs74.readFileSync(file)]), path76.basename(name));
|
|
27103
|
+
for (const f of files2) add("files", f, path76.join(root, f));
|
|
27104
|
+
for (const f of opts.preview) add("previews", path76.basename(f), f);
|
|
27105
|
+
for (const f of opts.evidence) add("evidence", path76.basename(f), f);
|
|
26994
27106
|
const done2 = await asJson(await ask("", { method: "POST", body: form }));
|
|
26995
27107
|
say(
|
|
26996
27108
|
done2.replaced ? `\u5DF2\u66FF\u6362 ${done2.name}\uFF1A${files2.length} \u4E2A\u6587\u4EF6\u3002
|
|
@@ -27006,11 +27118,11 @@ Deleted ${name} from the catalog.`);
|
|
|
27006
27118
|
return { name, deleted: true };
|
|
27007
27119
|
}
|
|
27008
27120
|
async function templateShowcase(name, dir) {
|
|
27009
|
-
const file =
|
|
27010
|
-
if (!
|
|
27121
|
+
const file = path76.join(dir, "showcase.json");
|
|
27122
|
+
if (!fs74.existsSync(file)) invalid(`${dir} \u4E2D\u7F3A\u5C11 showcase.json\u3002`, `showcase.json is missing from ${dir}.`);
|
|
27011
27123
|
let meta;
|
|
27012
27124
|
try {
|
|
27013
|
-
meta = JSON.parse(
|
|
27125
|
+
meta = JSON.parse(fs74.readFileSync(file, "utf8"));
|
|
27014
27126
|
} catch {
|
|
27015
27127
|
return invalid("showcase.json \u4E0D\u662F\u6709\u6548\u7684 JSON\u3002", "showcase.json is not valid JSON.");
|
|
27016
27128
|
}
|
|
@@ -27022,9 +27134,9 @@ async function templateShowcase(name, dir) {
|
|
|
27022
27134
|
const form = new FormData();
|
|
27023
27135
|
form.set("meta", JSON.stringify(meta));
|
|
27024
27136
|
for (const p of new Set(named2)) {
|
|
27025
|
-
const at =
|
|
27026
|
-
if (!
|
|
27027
|
-
form.append(`media:${p}`, new Blob([
|
|
27137
|
+
const at = path76.join(dir, p);
|
|
27138
|
+
if (!fs74.existsSync(at)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${at}\u3002`, `${at} does not exist.`);
|
|
27139
|
+
form.append(`media:${p}`, new Blob([fs74.readFileSync(at)]), p);
|
|
27028
27140
|
}
|
|
27029
27141
|
const done2 = await asJson(await ask(`/${encodeURIComponent(name)}/showcase`, { method: "PUT", body: form }));
|
|
27030
27142
|
say(`\u5DF2\u66F4\u65B0 ${name} \u7684\u5C55\u793A\u8D44\u6599\uFF1A${done2.files} \u4E2A\u6587\u4EF6\u3002
|
|
@@ -27033,7 +27145,7 @@ Updated ${name}'s showcase: ${done2.files} files.`);
|
|
|
27033
27145
|
}
|
|
27034
27146
|
function walk2(root, sub = "") {
|
|
27035
27147
|
const out = [];
|
|
27036
|
-
for (const entry of
|
|
27148
|
+
for (const entry of fs74.readdirSync(path76.join(root, sub), { withFileTypes: true })) {
|
|
27037
27149
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
27038
27150
|
const rel2 = sub ? `${sub}/${entry.name}` : entry.name;
|
|
27039
27151
|
if (entry.isDirectory()) out.push(...walk2(root, rel2));
|
|
@@ -27318,7 +27430,7 @@ init_opencode();
|
|
|
27318
27430
|
init_locate();
|
|
27319
27431
|
init_store();
|
|
27320
27432
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
27321
|
-
import
|
|
27433
|
+
import path82 from "node:path";
|
|
27322
27434
|
var AGENTS2 = {
|
|
27323
27435
|
claude: "claude-code",
|
|
27324
27436
|
codex: "codex",
|
|
@@ -27333,10 +27445,10 @@ var AGENTS2 = {
|
|
|
27333
27445
|
var RUNTIMES = /* @__PURE__ */ new Set(["node", "bun", "deno"]);
|
|
27334
27446
|
function agentOf2(args) {
|
|
27335
27447
|
const words = args.trim().split(/\s+/);
|
|
27336
|
-
const first =
|
|
27448
|
+
const first = path82.basename(words[0] ?? "");
|
|
27337
27449
|
const script = RUNTIMES.has(first) ? words[1] ?? "" : words[0] ?? "";
|
|
27338
27450
|
if (script.includes("/@anthropic-ai/claude-code/")) return "claude-code";
|
|
27339
|
-
return AGENTS2[
|
|
27451
|
+
return AGENTS2[path82.basename(script).replace(/\.(m?js|cjs|exe)$/, "")];
|
|
27340
27452
|
}
|
|
27341
27453
|
function processChain() {
|
|
27342
27454
|
if (process.platform === "win32") return [];
|
|
@@ -27400,8 +27512,8 @@ init_tasks();
|
|
|
27400
27512
|
init_read();
|
|
27401
27513
|
init_rules();
|
|
27402
27514
|
init_board();
|
|
27403
|
-
import
|
|
27404
|
-
import
|
|
27515
|
+
import fs81 from "node:fs";
|
|
27516
|
+
import path83 from "node:path";
|
|
27405
27517
|
var FOLLOW_MS = 400;
|
|
27406
27518
|
async function cmdStartRun(action, args, opts, program2 = "akb") {
|
|
27407
27519
|
const { req, follow: follow2, print } = readRequest(action, args, opts);
|
|
@@ -27691,7 +27803,7 @@ async function cmdSubStart(words, opts) {
|
|
|
27691
27803
|
let prompt = words.join(" ");
|
|
27692
27804
|
if (opts.file) {
|
|
27693
27805
|
try {
|
|
27694
|
-
prompt =
|
|
27806
|
+
prompt = fs81.readFileSync(path83.resolve(opts.file), "utf8");
|
|
27695
27807
|
} catch {
|
|
27696
27808
|
die(`--file: cannot read ${opts.file}`, { kind: "run-refused" });
|
|
27697
27809
|
}
|
|
@@ -27982,7 +28094,7 @@ async function cmdWatch(sessionId) {
|
|
|
27982
28094
|
// ../cli/src/commands/triage.ts
|
|
27983
28095
|
init_scheduled();
|
|
27984
28096
|
init_workflows();
|
|
27985
|
-
import
|
|
28097
|
+
import fs83 from "node:fs";
|
|
27986
28098
|
|
|
27987
28099
|
// ../cli/src/lib/signals/index.ts
|
|
27988
28100
|
init_cards();
|
|
@@ -27992,16 +28104,16 @@ init_yaml();
|
|
|
27992
28104
|
// ../cli/src/lib/signals/config.ts
|
|
27993
28105
|
init_settings2();
|
|
27994
28106
|
init_paths();
|
|
27995
|
-
import
|
|
27996
|
-
import
|
|
28107
|
+
import fs82 from "node:fs";
|
|
28108
|
+
import path84 from "node:path";
|
|
27997
28109
|
var TOKEN_KEY = "TRIAGE_ENDPOINT_TOKEN";
|
|
27998
28110
|
var ENDPOINT_SETTING = "Triage endpoint";
|
|
27999
28111
|
var LINE2 = /^- \*\*Triage endpoint\*\*\s*[—-]\s*(.+)$/m;
|
|
28000
|
-
var boardRel3 = (file) => rel(file).split(
|
|
28112
|
+
var boardRel3 = (file) => rel(file).split(path84.sep).join("/");
|
|
28001
28113
|
function signalEndpoint() {
|
|
28002
28114
|
let text5;
|
|
28003
28115
|
try {
|
|
28004
|
-
text5 =
|
|
28116
|
+
text5 = fs82.readFileSync(CONFIG, "utf8");
|
|
28005
28117
|
} catch {
|
|
28006
28118
|
return "";
|
|
28007
28119
|
}
|
|
@@ -28177,7 +28289,7 @@ async function cmdTriageAdd(opts) {
|
|
|
28177
28289
|
let body = opts.text;
|
|
28178
28290
|
if (opts.file !== void 0) {
|
|
28179
28291
|
try {
|
|
28180
|
-
body =
|
|
28292
|
+
body = fs83.readFileSync(opts.file, "utf8");
|
|
28181
28293
|
} catch {
|
|
28182
28294
|
die(`can't read ${opts.file} \u2014 write the item to a file, then pass its path`, { kind: "needs-input" });
|
|
28183
28295
|
}
|
|
@@ -28741,8 +28853,8 @@ function declareAgent(program2, cli) {
|
|
|
28741
28853
|
}
|
|
28742
28854
|
|
|
28743
28855
|
// ../cli/src/lib/cli/setup.ts
|
|
28744
|
-
import
|
|
28745
|
-
import
|
|
28856
|
+
import fs85 from "node:fs";
|
|
28857
|
+
import path86 from "node:path";
|
|
28746
28858
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
28747
28859
|
|
|
28748
28860
|
// ../cli/src/commands/update.ts
|
|
@@ -28756,8 +28868,8 @@ init_paths();
|
|
|
28756
28868
|
init_memory();
|
|
28757
28869
|
init_yaml2();
|
|
28758
28870
|
init_version();
|
|
28759
|
-
import
|
|
28760
|
-
import
|
|
28871
|
+
import fs84 from "node:fs";
|
|
28872
|
+
import path85 from "node:path";
|
|
28761
28873
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
28762
28874
|
var NAME = "wildcrane";
|
|
28763
28875
|
var REPO = "https://github.com/ai4kanban/ai4kanban";
|
|
@@ -28779,10 +28891,10 @@ var Report = class {
|
|
|
28779
28891
|
for (const line of this.notes) say(` ! ${line}`);
|
|
28780
28892
|
}
|
|
28781
28893
|
};
|
|
28782
|
-
var read3 = (file) =>
|
|
28894
|
+
var read3 = (file) => fs84.existsSync(file) ? fs84.readFileSync(file, "utf8") : null;
|
|
28783
28895
|
function statOf(p) {
|
|
28784
28896
|
try {
|
|
28785
|
-
return
|
|
28897
|
+
return fs84.lstatSync(p);
|
|
28786
28898
|
} catch {
|
|
28787
28899
|
return null;
|
|
28788
28900
|
}
|
|
@@ -28807,10 +28919,10 @@ async function cmdUpdate2(ctx) {
|
|
|
28807
28919
|
return { updated: ctx.dir, from, to: SKILL_VERSION };
|
|
28808
28920
|
}
|
|
28809
28921
|
async function repairBoard(root, report3, named2) {
|
|
28810
|
-
const legacy =
|
|
28811
|
-
const kept2 =
|
|
28812
|
-
const board2 = named2 ?? (
|
|
28813
|
-
if (!
|
|
28922
|
+
const legacy = path85.join(root, "docs", "kanban");
|
|
28923
|
+
const kept2 = path85.join(root, ".akb", "kanban");
|
|
28924
|
+
const board2 = named2 ?? (fs84.existsSync(legacy) ? legacy : kept2);
|
|
28925
|
+
if (!fs84.existsSync(board2)) {
|
|
28814
28926
|
report3.notes.push("\u672A\u627E\u5230\u770B\u677F\uFF0C\u8BF7\u901A\u8FC7\u63D2\u4EF6\u8FDE\u63A5\u4E91\u7AEF\u9879\u76EE\u3002");
|
|
28815
28927
|
return false;
|
|
28816
28928
|
}
|
|
@@ -28841,20 +28953,20 @@ function moveRuntimes(root, board2, report3) {
|
|
|
28841
28953
|
}
|
|
28842
28954
|
}
|
|
28843
28955
|
function moveLegacyMemory(board2, report3) {
|
|
28844
|
-
const memory =
|
|
28956
|
+
const memory = path85.join(board2, "memory");
|
|
28845
28957
|
const moved = [];
|
|
28846
28958
|
for (const name of MEMORY_FILES) {
|
|
28847
|
-
const from =
|
|
28959
|
+
const from = path85.join(board2, name);
|
|
28848
28960
|
const st = statOf(from);
|
|
28849
28961
|
if (!st || !st.isFile()) continue;
|
|
28850
|
-
|
|
28851
|
-
const to =
|
|
28962
|
+
fs84.mkdirSync(memory, { recursive: true });
|
|
28963
|
+
const to = path85.join(memory, name);
|
|
28852
28964
|
const there = read3(to);
|
|
28853
28965
|
if (there === null) {
|
|
28854
|
-
|
|
28966
|
+
fs84.renameSync(from, to);
|
|
28855
28967
|
moved.push(name);
|
|
28856
28968
|
} else if (there === read3(from)) {
|
|
28857
|
-
|
|
28969
|
+
fs84.rmSync(from);
|
|
28858
28970
|
moved.push(name);
|
|
28859
28971
|
} else {
|
|
28860
28972
|
report3.notes.push(`both docs/kanban/${name} and docs/kanban/memory/${name} exist and differ \u2014 merge them by hand`);
|
|
@@ -28863,23 +28975,23 @@ function moveLegacyMemory(board2, report3) {
|
|
|
28863
28975
|
if (moved.length) report3.did.push(`moved the memory set into docs/kanban/memory/: ${moved.join(", ")}`);
|
|
28864
28976
|
}
|
|
28865
28977
|
function dropRecordFile(board2) {
|
|
28866
|
-
const file =
|
|
28867
|
-
if (!
|
|
28868
|
-
|
|
28978
|
+
const file = path85.join(board2, "record.csv");
|
|
28979
|
+
if (!fs84.existsSync(file)) return null;
|
|
28980
|
+
fs84.rmSync(file);
|
|
28869
28981
|
return "removed docs/kanban/record.csv (the planning scores it fed are gone)";
|
|
28870
28982
|
}
|
|
28871
28983
|
function dropModuleGoals(board2, report3) {
|
|
28872
|
-
const memory =
|
|
28873
|
-
if (!
|
|
28874
|
-
const root = read3(
|
|
28984
|
+
const memory = path85.join(board2, "memory");
|
|
28985
|
+
if (!fs84.existsSync(memory)) return;
|
|
28986
|
+
const root = read3(path85.join(memory, "goal.md"));
|
|
28875
28987
|
let dropped = 0;
|
|
28876
|
-
for (const entry of
|
|
28988
|
+
for (const entry of fs84.readdirSync(memory, { withFileTypes: true })) {
|
|
28877
28989
|
if (!entry.isDirectory()) continue;
|
|
28878
|
-
const file =
|
|
28990
|
+
const file = path85.join(memory, entry.name, "goal.md");
|
|
28879
28991
|
const text5 = read3(file);
|
|
28880
28992
|
if (text5 === null) continue;
|
|
28881
28993
|
if (text5 === root || text5.includes(UNFILLED) || !goalWords(text5)) {
|
|
28882
|
-
|
|
28994
|
+
fs84.rmSync(file);
|
|
28883
28995
|
dropped++;
|
|
28884
28996
|
} else {
|
|
28885
28997
|
report3.notes.push(
|
|
@@ -28901,10 +29013,10 @@ var PLAN_LEADS = {
|
|
|
28901
29013
|
var GONE_LEADS = ["software-planner", "planner"];
|
|
28902
29014
|
function planLeads(board2) {
|
|
28903
29015
|
const leads = { ...PLAN_LEADS };
|
|
28904
|
-
const root =
|
|
28905
|
-
for (const entry of
|
|
29016
|
+
const root = path85.join(board2, "workflows");
|
|
29017
|
+
for (const entry of fs84.existsSync(root) ? fs84.readdirSync(root, { withFileTypes: true }) : []) {
|
|
28906
29018
|
if (!entry.isDirectory()) continue;
|
|
28907
|
-
const meta = splitFrontmatter(read3(
|
|
29019
|
+
const meta = splitFrontmatter(read3(path85.join(root, entry.name, "WORKFLOW.md")) ?? "").meta;
|
|
28908
29020
|
const stages = meta === null ? void 0 : parseYamlBlock(meta).stages;
|
|
28909
29021
|
const plan = stages && typeof stages === "object" && !Array.isArray(stages) ? stages.plan : void 0;
|
|
28910
29022
|
const lead = plan && typeof plan === "object" && !Array.isArray(plan) && typeof plan.lead === "string" ? plan.lead.trim() : "";
|
|
@@ -28913,30 +29025,30 @@ function planLeads(board2) {
|
|
|
28913
29025
|
return leads;
|
|
28914
29026
|
}
|
|
28915
29027
|
function movePlanRules(board2, report3) {
|
|
28916
|
-
const rules =
|
|
29028
|
+
const rules = path85.join(board2, "rules");
|
|
28917
29029
|
const moved = [];
|
|
28918
29030
|
for (const [id, leads] of Object.entries(planLeads(board2))) {
|
|
28919
29031
|
for (const lead of leads) {
|
|
28920
|
-
const from =
|
|
29032
|
+
const from = path85.join(rules, `${lead}.md`);
|
|
28921
29033
|
const rule = read3(from)?.trim();
|
|
28922
29034
|
if (!rule) continue;
|
|
28923
|
-
const to =
|
|
29035
|
+
const to = path85.join(rules, "workflows", `${id}.md`);
|
|
28924
29036
|
const there = read3(to)?.trim() ?? "";
|
|
28925
29037
|
if (!there.includes(rule)) {
|
|
28926
|
-
|
|
28927
|
-
|
|
29038
|
+
fs84.mkdirSync(path85.dirname(to), { recursive: true });
|
|
29039
|
+
fs84.writeFileSync(to, `${[there, rule].filter(Boolean).join("\n\n")}
|
|
28928
29040
|
`);
|
|
28929
29041
|
moved.push(`${lead} \u2192 ${id}`);
|
|
28930
29042
|
}
|
|
28931
29043
|
}
|
|
28932
29044
|
}
|
|
28933
|
-
for (const lead of GONE_LEADS)
|
|
29045
|
+
for (const lead of GONE_LEADS) fs84.rmSync(path85.join(rules, `${lead}.md`), { force: true });
|
|
28934
29046
|
if (moved.length) report3.did.push(`made each plan agent's rule its workflow's card rule (docs/kanban/rules/workflows/): ${moved.join(", ")}`);
|
|
28935
29047
|
}
|
|
28936
29048
|
function noteOldWorkflows(board2, report3) {
|
|
28937
29049
|
const old = (dir, file, keys) => {
|
|
28938
|
-
const root =
|
|
28939
|
-
return (
|
|
29050
|
+
const root = path85.join(board2, dir);
|
|
29051
|
+
return (fs84.existsSync(root) ? fs84.readdirSync(root, { withFileTypes: true }) : []).filter((e) => e.isDirectory() && keys.test(splitFrontmatter(read3(path85.join(root, e.name, file)) ?? "").meta ?? "")).map((e) => `docs/kanban/${dir}/${e.name}/${file}`);
|
|
28940
29052
|
};
|
|
28941
29053
|
const files2 = [...old("workflows", "WORKFLOW.md", /^(stages|delivers):/m), ...old("agents", "AGENT.md", /^\s+(hook|lead):/m)];
|
|
28942
29054
|
if (files2.length) {
|
|
@@ -28946,7 +29058,7 @@ function noteOldWorkflows(board2, report3) {
|
|
|
28946
29058
|
}
|
|
28947
29059
|
}
|
|
28948
29060
|
function checkConfig(board2, report3) {
|
|
28949
|
-
const current = read3(
|
|
29061
|
+
const current = read3(path85.join(board2, "config.md"));
|
|
28950
29062
|
if (!current) return;
|
|
28951
29063
|
const added = missingConfigKeys(current);
|
|
28952
29064
|
if (added.length) {
|
|
@@ -28956,7 +29068,7 @@ function checkConfig(board2, report3) {
|
|
|
28956
29068
|
}
|
|
28957
29069
|
}
|
|
28958
29070
|
function checkModules(board2, report3) {
|
|
28959
|
-
const map2 = read3(
|
|
29071
|
+
const map2 = read3(path85.join(board2, "modules.md"));
|
|
28960
29072
|
if (map2 && map2.includes(UNFILLED)) {
|
|
28961
29073
|
report3.notes.push(
|
|
28962
29074
|
"docs/kanban/modules.md is still blank \u2014 write it from the repo (`akb guide module-map`)"
|
|
@@ -29010,14 +29122,14 @@ function declareSetup(program2, cli) {
|
|
|
29010
29122
|
program2.version(SKILL_VERSION, "-v, --version", "print this version").addHelpText("beforeAll", spelled(cli.program));
|
|
29011
29123
|
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");
|
|
29012
29124
|
const where = (cmd) => {
|
|
29013
|
-
const dir =
|
|
29014
|
-
if (!
|
|
29125
|
+
const dir = path86.resolve(String(cmd.optsWithGlobals().dir ?? cli.cwd));
|
|
29126
|
+
if (!fs85.existsSync(dir)) throw new BoardError(`no such folder: ${dir}`, { kind: "no-such-folder", dir });
|
|
29015
29127
|
return dir;
|
|
29016
29128
|
};
|
|
29017
29129
|
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() {
|
|
29018
29130
|
const dir = where(this);
|
|
29019
29131
|
const named2 = this.optsWithGlobals().board ?? process.env.AI4KANBAN_BOARD?.trim();
|
|
29020
|
-
const board2 = named2 ?
|
|
29132
|
+
const board2 = named2 ? path86.resolve(cli.cwd, named2) : null;
|
|
29021
29133
|
await runAction(ctxOf(this, cli.program), {}, () => cmdUpdate2({ dir, program: cli.program, board: board2 }));
|
|
29022
29134
|
});
|
|
29023
29135
|
program2.command("__rules", { hidden: true }).action(function() {
|
|
@@ -29061,7 +29173,7 @@ function invokedDirectly() {
|
|
|
29061
29173
|
const entry = process.argv[1];
|
|
29062
29174
|
if (!entry) return false;
|
|
29063
29175
|
try {
|
|
29064
|
-
return
|
|
29176
|
+
return fs86.realpathSync(entry) === fs86.realpathSync(SELF2);
|
|
29065
29177
|
} catch {
|
|
29066
29178
|
return false;
|
|
29067
29179
|
}
|