wildcrane-runner 0.6.12 → 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 +1008 -890
- package/dist/akb/package.json +1 -1
- package/dist/launcher.mjs +2 -2
- package/dist/runner.mjs +33 -10
- 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,8 +5477,8 @@ var init_package = __esm({
|
|
|
5477
5477
|
"../cli/package.json"() {
|
|
5478
5478
|
package_default = {
|
|
5479
5479
|
name: "wildcrane",
|
|
5480
|
-
version: "0.11.
|
|
5481
|
-
akbContract:
|
|
5480
|
+
version: "0.11.11",
|
|
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: [
|
|
5484
5484
|
"kanban",
|
|
@@ -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) : "";
|
|
@@ -18213,288 +18529,81 @@ ${shared}` : ""
|
|
|
18213
18529
|
req.notes ? `Extra notes: ${req.notes}` : "",
|
|
18214
18530
|
`Don't ask me questions with human-in-the-loop. Leave any questions as open questions.`
|
|
18215
18531
|
].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
|
-
}
|
|
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" };
|
|
@@ -18993,7 +19102,7 @@ function requestOf(record) {
|
|
|
18993
19102
|
pictures: record.pictures
|
|
18994
19103
|
};
|
|
18995
19104
|
}
|
|
18996
|
-
async function openResume(id) {
|
|
19105
|
+
async function openResume(id, requireSession = false) {
|
|
18997
19106
|
const restore = [];
|
|
18998
19107
|
const runs = withRuns((all2) => {
|
|
18999
19108
|
reap(all2, [], restore);
|
|
@@ -19013,6 +19122,9 @@ async function openResume(id) {
|
|
|
19013
19122
|
if (refused) return refused;
|
|
19014
19123
|
}
|
|
19015
19124
|
const retrying = lacksSession(prev);
|
|
19125
|
+
if (requireSession && !resumeSessionId(prev)) {
|
|
19126
|
+
return refusal("runNoSession", "the original conversation cannot be resumed; no new conversation was started");
|
|
19127
|
+
}
|
|
19016
19128
|
const ask2 = retrying && prev.status !== "stopped" ? retryAsk(prev) : void 0;
|
|
19017
19129
|
if (retrying && !ask2) return refusal("runNoSession", "that run never reported a session id to continue by");
|
|
19018
19130
|
const ended = endedDeliveryRefusal(prev);
|
|
@@ -19143,8 +19255,8 @@ async function resumeHeld(prev, plan, sessionId, resuming, fresh) {
|
|
|
19143
19255
|
clearAsks(prev.sessionId);
|
|
19144
19256
|
try {
|
|
19145
19257
|
if (prev.action !== "create") {
|
|
19146
|
-
|
|
19147
|
-
|
|
19258
|
+
fs49.unlinkSync(prev.logPath);
|
|
19259
|
+
fs49.rmSync(eventsPathOf(prev.sessionId), { force: true });
|
|
19148
19260
|
}
|
|
19149
19261
|
} catch {
|
|
19150
19262
|
}
|
|
@@ -19168,8 +19280,8 @@ function forgetRun(sessionId) {
|
|
|
19168
19280
|
if (!gone) return;
|
|
19169
19281
|
clearAsks(sessionId);
|
|
19170
19282
|
try {
|
|
19171
|
-
|
|
19172
|
-
|
|
19283
|
+
fs49.unlinkSync(gone.logPath);
|
|
19284
|
+
fs49.rmSync(eventsPathOf(gone.sessionId), { force: true });
|
|
19173
19285
|
} catch {
|
|
19174
19286
|
}
|
|
19175
19287
|
}
|
|
@@ -19198,14 +19310,14 @@ function noteSpecRefusals(sessionId, refused) {
|
|
|
19198
19310
|
}
|
|
19199
19311
|
function clearAsks(sessionId) {
|
|
19200
19312
|
try {
|
|
19201
|
-
|
|
19313
|
+
fs49.unlinkSync(asksPathOf(sessionId));
|
|
19202
19314
|
} catch {
|
|
19203
19315
|
}
|
|
19204
19316
|
}
|
|
19205
19317
|
function readAsks(sessionId) {
|
|
19206
19318
|
let data;
|
|
19207
19319
|
try {
|
|
19208
|
-
data = JSON.parse(
|
|
19320
|
+
data = JSON.parse(fs49.readFileSync(asksPathOf(sessionId), "utf8"));
|
|
19209
19321
|
} catch {
|
|
19210
19322
|
return { asks: [], refines: [], refused: [] };
|
|
19211
19323
|
}
|
|
@@ -19232,10 +19344,10 @@ function readAsks(sessionId) {
|
|
|
19232
19344
|
return { asks: asks.filter((a) => !discarded.has(a.cardId)), refines: refines.filter((a) => !discarded.has(a.cardId)), refused };
|
|
19233
19345
|
}
|
|
19234
19346
|
function writeAsks(sessionId, file) {
|
|
19235
|
-
|
|
19347
|
+
fs49.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
19236
19348
|
const tmp = `${asksPathOf(sessionId)}.tmp`;
|
|
19237
|
-
|
|
19238
|
-
|
|
19349
|
+
fs49.writeFileSync(tmp, JSON.stringify(file, null, 2) + "\n");
|
|
19350
|
+
fs49.renameSync(tmp, asksPathOf(sessionId));
|
|
19239
19351
|
}
|
|
19240
19352
|
function markSpawned(sessionId, pid) {
|
|
19241
19353
|
patch(sessionId, (r) => {
|
|
@@ -19323,7 +19435,10 @@ async function cancelDelivery(id) {
|
|
|
19323
19435
|
if (!id.trim()) return { ok: false, ...refusal("deliveryUnnamed", "name the delivery to cancel", { action: "cancel" }) };
|
|
19324
19436
|
const delivery = namedDelivery(id);
|
|
19325
19437
|
if (!delivery) return { ok: false, ...unknownDelivery(id) };
|
|
19326
|
-
if (delivery.status !== "active")
|
|
19438
|
+
if (delivery.status !== "active") {
|
|
19439
|
+
tidyCheckout(delivery.deliveryId);
|
|
19440
|
+
return { ok: true, deliveryId: delivery.deliveryId };
|
|
19441
|
+
}
|
|
19327
19442
|
endDelivery(delivery.deliveryId, "cancelled");
|
|
19328
19443
|
const live = readRuns().find((r) => r.status === "running" && r.deliveryId === delivery.deliveryId && r.action !== "sub");
|
|
19329
19444
|
if (live) await stopRun(live.sessionId);
|
|
@@ -19389,12 +19504,12 @@ async function discardDelivery(id) {
|
|
|
19389
19504
|
return { ok: true, deliveryId: delivery.deliveryId };
|
|
19390
19505
|
}
|
|
19391
19506
|
function writeSpec(spec) {
|
|
19392
|
-
|
|
19393
|
-
|
|
19507
|
+
fs49.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
19508
|
+
fs49.writeFileSync(specPathOf(spec.sessionId), JSON.stringify(spec, null, 2) + "\n");
|
|
19394
19509
|
}
|
|
19395
19510
|
function readSpec(sessionId) {
|
|
19396
19511
|
try {
|
|
19397
|
-
const spec = JSON.parse(
|
|
19512
|
+
const spec = JSON.parse(fs49.readFileSync(specPathOf(sessionId), "utf8"));
|
|
19398
19513
|
return spec && Array.isArray(spec.plan?.argv) ? spec : null;
|
|
19399
19514
|
} catch {
|
|
19400
19515
|
return null;
|
|
@@ -19402,28 +19517,28 @@ function readSpec(sessionId) {
|
|
|
19402
19517
|
}
|
|
19403
19518
|
function dropSpec(sessionId) {
|
|
19404
19519
|
try {
|
|
19405
|
-
|
|
19520
|
+
fs49.unlinkSync(specPathOf(sessionId));
|
|
19406
19521
|
} catch {
|
|
19407
19522
|
}
|
|
19408
19523
|
}
|
|
19409
19524
|
async function acquireIndexLock(giveUp) {
|
|
19410
19525
|
const until = Date.now() + INDEX_WAIT_MS;
|
|
19411
|
-
const holder =
|
|
19526
|
+
const holder = path54.join(INDEX_LOCK2, "holder");
|
|
19412
19527
|
for (; ; ) {
|
|
19413
19528
|
if (giveUp()) return null;
|
|
19414
19529
|
try {
|
|
19415
|
-
|
|
19416
|
-
|
|
19417
|
-
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 });
|
|
19418
19533
|
} catch (err) {
|
|
19419
19534
|
const code = err.code;
|
|
19420
19535
|
if (code === "ENOENT") {
|
|
19421
|
-
|
|
19536
|
+
fs49.mkdirSync(path54.dirname(INDEX_LOCK2), { recursive: true });
|
|
19422
19537
|
continue;
|
|
19423
19538
|
}
|
|
19424
19539
|
if (code !== "EEXIST") throw err;
|
|
19425
19540
|
if (!pidAlive(Number(readHolder(holder)))) {
|
|
19426
|
-
|
|
19541
|
+
fs49.rmSync(INDEX_LOCK2, { recursive: true, force: true });
|
|
19427
19542
|
continue;
|
|
19428
19543
|
}
|
|
19429
19544
|
if (Date.now() > until) return null;
|
|
@@ -19433,7 +19548,7 @@ async function acquireIndexLock(giveUp) {
|
|
|
19433
19548
|
}
|
|
19434
19549
|
function readHolder(file) {
|
|
19435
19550
|
try {
|
|
19436
|
-
return
|
|
19551
|
+
return fs49.readFileSync(file, "utf8").trim();
|
|
19437
19552
|
} catch {
|
|
19438
19553
|
return String(process.pid);
|
|
19439
19554
|
}
|
|
@@ -19524,8 +19639,8 @@ var init_sessions = __esm({
|
|
|
19524
19639
|
conflict: "implementing",
|
|
19525
19640
|
hook: "implementing"
|
|
19526
19641
|
};
|
|
19527
|
-
specPathOf = (sessionId) =>
|
|
19528
|
-
asksPathOf = (sessionId) =>
|
|
19642
|
+
specPathOf = (sessionId) => path54.join(SESSIONS_DIR, `${sessionId}.plan.json`);
|
|
19643
|
+
asksPathOf = (sessionId) => path54.join(SESSIONS_DIR, `${sessionId}.asks.json`);
|
|
19529
19644
|
carriesSession = (r) => !!(r.resumedFrom || r.continues && !r.continues.fork);
|
|
19530
19645
|
canPickUp = (r) => (r.status === "error" || r.status === "interrupted" || r.status === "stopped") && !(r.action === "create" && readRuns().some((next) => next.resumedFrom === r.sessionId));
|
|
19531
19646
|
neverStarted = (r) => r.unspawned === true || !!r.errorWhy?.some((why) => why.kind === "notInstalled");
|
|
@@ -19659,8 +19774,8 @@ var init_questions = __esm({
|
|
|
19659
19774
|
});
|
|
19660
19775
|
|
|
19661
19776
|
// ../cli/src/lib/agent/workflow-cards.ts
|
|
19662
|
-
import
|
|
19663
|
-
import
|
|
19777
|
+
import fs50 from "node:fs";
|
|
19778
|
+
import path55 from "node:path";
|
|
19664
19779
|
function cardsOnWorkflow(id) {
|
|
19665
19780
|
const wanted = id || DEFAULT_WORKFLOW;
|
|
19666
19781
|
const ids = [];
|
|
@@ -19671,12 +19786,12 @@ function cardsOnWorkflow(id) {
|
|
|
19671
19786
|
return [];
|
|
19672
19787
|
}
|
|
19673
19788
|
for (const file of files2) {
|
|
19674
|
-
const base =
|
|
19675
|
-
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);
|
|
19676
19791
|
if (card === null) continue;
|
|
19677
19792
|
let meta;
|
|
19678
19793
|
try {
|
|
19679
|
-
meta = parseFrontmatter(
|
|
19794
|
+
meta = parseFrontmatter(fs50.readFileSync(file, "utf8")).meta;
|
|
19680
19795
|
} catch {
|
|
19681
19796
|
continue;
|
|
19682
19797
|
}
|
|
@@ -19734,12 +19849,12 @@ var init_workflow_cards = __esm({
|
|
|
19734
19849
|
});
|
|
19735
19850
|
|
|
19736
19851
|
// ../cli/src/lib/readme.ts
|
|
19737
|
-
import
|
|
19738
|
-
import
|
|
19852
|
+
import fs51 from "node:fs";
|
|
19853
|
+
import path56 from "node:path";
|
|
19739
19854
|
function stripReadmeRefs(target) {
|
|
19740
|
-
if (!
|
|
19855
|
+
if (!fs51.existsSync(README)) return [];
|
|
19741
19856
|
const needle = target.kind === "group" ? `](${target.rel}/` : `](${target.rel})`;
|
|
19742
|
-
const lines =
|
|
19857
|
+
const lines = fs51.readFileSync(README, "utf8").split("\n");
|
|
19743
19858
|
const out = [];
|
|
19744
19859
|
const removed = [];
|
|
19745
19860
|
let i = 0;
|
|
@@ -19761,20 +19876,20 @@ function stripReadmeRefs(target) {
|
|
|
19761
19876
|
out.push(line);
|
|
19762
19877
|
i++;
|
|
19763
19878
|
}
|
|
19764
|
-
if (removed.length)
|
|
19879
|
+
if (removed.length) fs51.writeFileSync(README, out.join("\n"));
|
|
19765
19880
|
return removed;
|
|
19766
19881
|
}
|
|
19767
19882
|
function addReadmeRef(id, title, relPath) {
|
|
19768
|
-
if (!
|
|
19769
|
-
const link2 = relPath.split(
|
|
19883
|
+
if (!fs51.existsSync(README)) return false;
|
|
19884
|
+
const link2 = relPath.split(path56.sep).join("/");
|
|
19770
19885
|
const bullet = `- [#${id} ${title}](${link2})`;
|
|
19771
19886
|
const heading = `## ${TASKS_HEADING}`;
|
|
19772
|
-
let lines =
|
|
19887
|
+
let lines = fs51.readFileSync(README, "utf8").split("\n");
|
|
19773
19888
|
const hi = lines.findIndex((l) => l.trim().toLowerCase() === heading.toLowerCase());
|
|
19774
19889
|
if (hi === -1) {
|
|
19775
19890
|
while (lines.length && lines[lines.length - 1].trim() === "") lines.pop();
|
|
19776
19891
|
lines.push("", heading, "", bullet);
|
|
19777
|
-
|
|
19892
|
+
fs51.writeFileSync(README, lines.join("\n") + "\n");
|
|
19778
19893
|
return true;
|
|
19779
19894
|
}
|
|
19780
19895
|
let end2 = hi + 1;
|
|
@@ -19788,15 +19903,15 @@ function addReadmeRef(id, title, relPath) {
|
|
|
19788
19903
|
const at = lastBullet !== -1 ? lastBullet + 1 : lines[hi + 1] === "" ? hi + 2 : hi + 1;
|
|
19789
19904
|
lines.splice(at, 0, bullet);
|
|
19790
19905
|
}
|
|
19791
|
-
|
|
19906
|
+
fs51.writeFileSync(README, lines.join("\n"));
|
|
19792
19907
|
return true;
|
|
19793
19908
|
}
|
|
19794
19909
|
function repointReadmeLink(id, oldRel, newRel, title) {
|
|
19795
|
-
if (!
|
|
19796
|
-
const oldLink = oldRel.split(
|
|
19797
|
-
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("/");
|
|
19798
19913
|
const linkRe = new RegExp(`\\[#${id}\\b[^\\]]*\\]\\(${escapeRegex(oldLink)}\\)`);
|
|
19799
|
-
const lines =
|
|
19914
|
+
const lines = fs51.readFileSync(README, "utf8").split("\n");
|
|
19800
19915
|
let changed = false;
|
|
19801
19916
|
for (let i = 0; i < lines.length; i++) {
|
|
19802
19917
|
if (!lines[i].includes(`](${oldLink})`)) continue;
|
|
@@ -19806,7 +19921,7 @@ function repointReadmeLink(id, oldRel, newRel, title) {
|
|
|
19806
19921
|
changed = true;
|
|
19807
19922
|
}
|
|
19808
19923
|
}
|
|
19809
|
-
if (changed)
|
|
19924
|
+
if (changed) fs51.writeFileSync(README, lines.join("\n"));
|
|
19810
19925
|
return changed;
|
|
19811
19926
|
}
|
|
19812
19927
|
var isTableRow, isBulletStart, bulletIndent, TASKS_HEADING, escapeRegex;
|
|
@@ -19823,14 +19938,14 @@ var init_readme = __esm({
|
|
|
19823
19938
|
});
|
|
19824
19939
|
|
|
19825
19940
|
// ../cli/src/lib/reconcile.ts
|
|
19826
|
-
import
|
|
19827
|
-
import
|
|
19941
|
+
import fs52 from "node:fs";
|
|
19942
|
+
import path57 from "node:path";
|
|
19828
19943
|
function liveIds() {
|
|
19829
19944
|
const ids = /* @__PURE__ */ new Set();
|
|
19830
19945
|
for (const file of walkMd(TODO)) {
|
|
19831
|
-
const base =
|
|
19946
|
+
const base = path57.basename(file);
|
|
19832
19947
|
if (base === "README.md") continue;
|
|
19833
|
-
const id = base === "root.md" ? idPrefix(
|
|
19948
|
+
const id = base === "root.md" ? idPrefix(path57.basename(path57.dirname(file))) : idPrefix(base);
|
|
19834
19949
|
if (id != null) ids.add(id);
|
|
19835
19950
|
}
|
|
19836
19951
|
return ids;
|
|
@@ -19838,16 +19953,16 @@ function liveIds() {
|
|
|
19838
19953
|
function indexableCards() {
|
|
19839
19954
|
const out = [];
|
|
19840
19955
|
for (const dir of walkDirs(TODO)) {
|
|
19841
|
-
const id = idPrefix(
|
|
19842
|
-
if (id != null &&
|
|
19843
|
-
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("/") });
|
|
19844
19959
|
}
|
|
19845
19960
|
}
|
|
19846
19961
|
for (const file of walkMd(TODO)) {
|
|
19847
|
-
const base =
|
|
19962
|
+
const base = path57.basename(file);
|
|
19848
19963
|
if (base === "README.md" || base === "root.md") continue;
|
|
19849
|
-
const relTODO =
|
|
19850
|
-
const segs = relTODO.split(
|
|
19964
|
+
const relTODO = path57.relative(TODO, file);
|
|
19965
|
+
const segs = relTODO.split(path57.sep);
|
|
19851
19966
|
if (segs.length !== 1) continue;
|
|
19852
19967
|
const id = idPrefix(base);
|
|
19853
19968
|
if (id != null) out.push({ id, rel: segs.join("/") });
|
|
@@ -19856,7 +19971,7 @@ function indexableCards() {
|
|
|
19856
19971
|
}
|
|
19857
19972
|
function readmeComplaints(fix) {
|
|
19858
19973
|
const said3 = [];
|
|
19859
|
-
const lines =
|
|
19974
|
+
const lines = fs52.readFileSync(README, "utf8").split("\n");
|
|
19860
19975
|
const indexed = /* @__PURE__ */ new Set();
|
|
19861
19976
|
let fixed = 0;
|
|
19862
19977
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -19865,13 +19980,13 @@ function readmeComplaints(fix) {
|
|
|
19865
19980
|
const id = Number(m[1]);
|
|
19866
19981
|
const linkPath = m[2];
|
|
19867
19982
|
indexed.add(id);
|
|
19868
|
-
if (
|
|
19983
|
+
if (fs52.existsSync(path57.join(TODO, linkPath))) continue;
|
|
19869
19984
|
const found2 = locate(id);
|
|
19870
19985
|
if (!found2) {
|
|
19871
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.`);
|
|
19872
19987
|
continue;
|
|
19873
19988
|
}
|
|
19874
|
-
const want = (found2.kind === "group" ?
|
|
19989
|
+
const want = (found2.kind === "group" ? path57.join(found2.rel, "root.md") : found2.rel).split(path57.sep).join("/");
|
|
19875
19990
|
if (want === linkPath) continue;
|
|
19876
19991
|
if (!fix) {
|
|
19877
19992
|
said3.push(`README links #${id} \u2192 ${linkPath}, which isn't there any more; the card is at ${want}.`);
|
|
@@ -19881,7 +19996,7 @@ function readmeComplaints(fix) {
|
|
|
19881
19996
|
said3.push(`README link #${id} pointed at missing ${linkPath} \u2192 repointed to ${want}.`);
|
|
19882
19997
|
fixed++;
|
|
19883
19998
|
}
|
|
19884
|
-
if (fixed)
|
|
19999
|
+
if (fixed) fs52.writeFileSync(README, lines.join("\n"));
|
|
19885
20000
|
for (const c of indexableCards()) {
|
|
19886
20001
|
if (!indexed.has(c.id)) {
|
|
19887
20002
|
said3.push(`card ${c.rel} (#${c.id}) is not in the README index. Add it under "## ${TASKS_HEADING}".`);
|
|
@@ -19893,11 +20008,11 @@ function crossRefComplaints() {
|
|
|
19893
20008
|
const said3 = [];
|
|
19894
20009
|
const live = liveIds();
|
|
19895
20010
|
for (const file of walkMd(TODO)) {
|
|
19896
|
-
const base =
|
|
20011
|
+
const base = path57.basename(file);
|
|
19897
20012
|
if (base === "README.md") continue;
|
|
19898
|
-
const ownerId = base === "root.md" ? idPrefix(
|
|
20013
|
+
const ownerId = base === "root.md" ? idPrefix(path57.basename(path57.dirname(file))) : idPrefix(base);
|
|
19899
20014
|
if (ownerId == null) continue;
|
|
19900
|
-
const { meta } = parseFrontmatter(
|
|
20015
|
+
const { meta } = parseFrontmatter(fs52.readFileSync(file, "utf8"));
|
|
19901
20016
|
if (!meta) continue;
|
|
19902
20017
|
for (const field2 of ["blocked_by", "related"]) {
|
|
19903
20018
|
for (const ref of meta[field2] || []) {
|
|
@@ -19910,11 +20025,11 @@ function crossRefComplaints() {
|
|
|
19910
20025
|
return said3;
|
|
19911
20026
|
}
|
|
19912
20027
|
function reconcileBoard() {
|
|
19913
|
-
if (!
|
|
20028
|
+
if (!fs52.existsSync(README)) return;
|
|
19914
20029
|
for (const line of [...readmeComplaints(true), ...crossRefComplaints()]) warn2(line);
|
|
19915
20030
|
}
|
|
19916
20031
|
function boardComplaints() {
|
|
19917
|
-
if (!
|
|
20032
|
+
if (!fs52.existsSync(README)) return [];
|
|
19918
20033
|
try {
|
|
19919
20034
|
return [...readmeComplaints(false), ...crossRefComplaints()];
|
|
19920
20035
|
} catch {
|
|
@@ -19934,8 +20049,8 @@ var init_reconcile = __esm({
|
|
|
19934
20049
|
});
|
|
19935
20050
|
|
|
19936
20051
|
// ../cli/src/commands/card.ts
|
|
19937
|
-
import
|
|
19938
|
-
import
|
|
20052
|
+
import fs53 from "node:fs";
|
|
20053
|
+
import path58 from "node:path";
|
|
19939
20054
|
function defaultBody() {
|
|
19940
20055
|
return [
|
|
19941
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>",
|
|
@@ -19961,7 +20076,7 @@ function bodyFromFile(opts) {
|
|
|
19961
20076
|
if (opts.body === false) die("pass --body-file or --no-body, not both");
|
|
19962
20077
|
let text5;
|
|
19963
20078
|
try {
|
|
19964
|
-
text5 =
|
|
20079
|
+
text5 = fs53.readFileSync(opts.bodyFile, "utf8");
|
|
19965
20080
|
} catch {
|
|
19966
20081
|
die(`can't read ${opts.bodyFile} \u2014 write the body to a file, then pass its path`, { kind: "needs-input" });
|
|
19967
20082
|
}
|
|
@@ -19993,20 +20108,20 @@ function cmdCreate(opts) {
|
|
|
19993
20108
|
const start = opts.id;
|
|
19994
20109
|
const slug2 = slugify(opts.slug !== void 0 ? opts.slug : title);
|
|
19995
20110
|
const fileRel = `${start}-${slug2}.md`;
|
|
19996
|
-
const file =
|
|
20111
|
+
const file = path58.join(TODO, fileRel);
|
|
19997
20112
|
const held4 = locate(start) ?? locateArchived(start);
|
|
19998
|
-
if (held4 &&
|
|
20113
|
+
if (held4 && path58.basename(held4.target) === `${start}-${slug2}.md`) {
|
|
19999
20114
|
say(start);
|
|
20000
20115
|
say(` ${rel(held4.target)} was already written`);
|
|
20001
20116
|
return { id: start, ids: [start], title, file: rel(held4.target), indexed: false };
|
|
20002
20117
|
}
|
|
20003
20118
|
if (held4) die(`#${start} is already a card here (${rel(held4.target)})`, { kind: "id-taken", id: start });
|
|
20004
|
-
if (
|
|
20119
|
+
if (fs53.existsSync(file)) die(`${rel(file)} already exists \u2014 pick a different --slug`);
|
|
20005
20120
|
countBoardEvent(triage ? "cards_created_proposed" : "cards_created_asked");
|
|
20006
20121
|
const meta = { title, priority, status: "todo", blocked_by, related, modules, workflow, triage, source, questions };
|
|
20007
20122
|
const scaffolded = !written && opts.body !== false;
|
|
20008
20123
|
const body = written ?? (scaffolded ? defaultBody() : "");
|
|
20009
|
-
|
|
20124
|
+
fs53.writeFileSync(file, serializeFrontmatter(meta) + "\n\n" + body);
|
|
20010
20125
|
const indexed = addReadmeRef(start, title, fileRel);
|
|
20011
20126
|
say(start);
|
|
20012
20127
|
say(
|
|
@@ -20044,8 +20159,8 @@ function cmdUpdate(id, flags) {
|
|
|
20044
20159
|
if (creating) die(creating, { kind: "card-being-created", id });
|
|
20045
20160
|
const found2 = locate(id);
|
|
20046
20161
|
if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
|
|
20047
|
-
const file = found2.kind === "group" ?
|
|
20048
|
-
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"));
|
|
20049
20164
|
if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
|
|
20050
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");
|
|
20051
20166
|
if (flags.related !== void 0 && flags.addRelated !== void 0) die("--related replaces the list and --add-related appends to it \u2014 pass one");
|
|
@@ -20125,26 +20240,26 @@ function cmdUpdate(id, flags) {
|
|
|
20125
20240
|
meta.status = "todo";
|
|
20126
20241
|
changes.push("status\u2192todo (open questions)");
|
|
20127
20242
|
}
|
|
20128
|
-
const curRel =
|
|
20243
|
+
const curRel = path58.relative(TODO, file);
|
|
20129
20244
|
const isSubtask = found2.kind === "file" && enclosingGroupRoot(file) !== null;
|
|
20130
|
-
let base =
|
|
20245
|
+
let base = path58.basename(file);
|
|
20131
20246
|
if (flags.slug !== void 0) {
|
|
20132
20247
|
if (found2.kind === "group") die("renaming a group root by script is not supported");
|
|
20133
20248
|
base = `${id}-${slugify(flags.slug)}.md`;
|
|
20134
20249
|
}
|
|
20135
|
-
const destRel =
|
|
20136
|
-
const dest =
|
|
20250
|
+
const destRel = path58.join(path58.dirname(curRel), base);
|
|
20251
|
+
const dest = path58.join(TODO, destRel);
|
|
20137
20252
|
const moving = dest !== file;
|
|
20138
|
-
if (moving &&
|
|
20139
|
-
|
|
20140
|
-
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);
|
|
20141
20256
|
if (isSubtask) {
|
|
20142
20257
|
if (moving || changes.includes("title")) repointReadmeLink(id, curRel, destRel, meta.title);
|
|
20143
|
-
if (moving) changes.push(`renamed \u2192 ${destRel.split(
|
|
20258
|
+
if (moving) changes.push(`renamed \u2192 ${destRel.split(path58.sep).join("/")}`);
|
|
20144
20259
|
} else if (moving) {
|
|
20145
20260
|
stripReadmeRefs({ kind: "file", rel: curRel });
|
|
20146
20261
|
addReadmeRef(id, meta.title, destRel);
|
|
20147
|
-
changes.push(`renamed \u2192 ${destRel.split(
|
|
20262
|
+
changes.push(`renamed \u2192 ${destRel.split(path58.sep).join("/")}`);
|
|
20148
20263
|
} else if (changes.includes("title")) {
|
|
20149
20264
|
stripReadmeRefs({ kind: "file", rel: curRel });
|
|
20150
20265
|
addReadmeRef(id, meta.title, curRel);
|
|
@@ -20156,8 +20271,8 @@ function cmdUpdateQuestions(id, input) {
|
|
|
20156
20271
|
const ops = readQuestionOps(input.ops ?? []);
|
|
20157
20272
|
const found2 = locate(id);
|
|
20158
20273
|
if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
|
|
20159
|
-
const file = found2.kind === "group" ?
|
|
20160
|
-
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"));
|
|
20161
20276
|
if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
|
|
20162
20277
|
if (insideRun() && ops.some((op) => op.kind === "skip" || op.kind === "unskip")) {
|
|
20163
20278
|
die("only the user skips or reopens a question \u2014 a run leaves questions for them to answer");
|
|
@@ -20230,7 +20345,7 @@ function cmdUpdateQuestions(id, input) {
|
|
|
20230
20345
|
meta.status = "todo";
|
|
20231
20346
|
changes.push("status\u2192todo (open questions)");
|
|
20232
20347
|
}
|
|
20233
|
-
|
|
20348
|
+
fs53.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
|
|
20234
20349
|
for (const kind of closed2) countBoardEvent(kind);
|
|
20235
20350
|
settleDelivery2(id, skipped, unskipped);
|
|
20236
20351
|
say(
|
|
@@ -20304,13 +20419,13 @@ var init_config_template = __esm({
|
|
|
20304
20419
|
});
|
|
20305
20420
|
|
|
20306
20421
|
// ../cli/src/lib/agent/local.ts
|
|
20307
|
-
import
|
|
20308
|
-
import
|
|
20422
|
+
import fs54 from "node:fs";
|
|
20423
|
+
import path59 from "node:path";
|
|
20309
20424
|
function takeLocalModels(board2) {
|
|
20310
|
-
const file =
|
|
20425
|
+
const file = path59.join(board2, ".local.json");
|
|
20311
20426
|
let held4;
|
|
20312
20427
|
try {
|
|
20313
|
-
held4 = configBlock(JSON.parse(
|
|
20428
|
+
held4 = configBlock(JSON.parse(fs54.readFileSync(file, "utf8")));
|
|
20314
20429
|
} catch {
|
|
20315
20430
|
return {};
|
|
20316
20431
|
}
|
|
@@ -20328,7 +20443,7 @@ function takeLocalModels(board2) {
|
|
|
20328
20443
|
}
|
|
20329
20444
|
if (!Object.keys(agents).length) return out;
|
|
20330
20445
|
delete held4.agents;
|
|
20331
|
-
|
|
20446
|
+
fs54.writeFileSync(file, JSON.stringify(held4, null, 2) + "\n");
|
|
20332
20447
|
return out;
|
|
20333
20448
|
}
|
|
20334
20449
|
var LOCAL_IGNORE_LINE;
|
|
@@ -20389,12 +20504,12 @@ var init_identity2 = __esm({
|
|
|
20389
20504
|
});
|
|
20390
20505
|
|
|
20391
20506
|
// ../cli/src/lib/signals/migrate.ts
|
|
20392
|
-
import
|
|
20393
|
-
import
|
|
20507
|
+
import fs55 from "node:fs";
|
|
20508
|
+
import path60 from "node:path";
|
|
20394
20509
|
function readHandled() {
|
|
20395
20510
|
let text5;
|
|
20396
20511
|
try {
|
|
20397
|
-
text5 =
|
|
20512
|
+
text5 = fs55.readFileSync(SIGNALS_OLD_HANDLED, "utf8");
|
|
20398
20513
|
} catch {
|
|
20399
20514
|
return [];
|
|
20400
20515
|
}
|
|
@@ -20411,57 +20526,57 @@ function readHandled() {
|
|
|
20411
20526
|
return held4;
|
|
20412
20527
|
}
|
|
20413
20528
|
function lift(from, into) {
|
|
20414
|
-
|
|
20415
|
-
|
|
20529
|
+
fs55.mkdirSync(into, { recursive: true });
|
|
20530
|
+
fs55.renameSync(from, path60.join(into, freeName(into, path60.basename(from))));
|
|
20416
20531
|
}
|
|
20417
20532
|
function repoint(dir, from, into) {
|
|
20418
20533
|
let entries;
|
|
20419
20534
|
try {
|
|
20420
|
-
entries =
|
|
20535
|
+
entries = fs55.readdirSync(dir, { withFileTypes: true });
|
|
20421
20536
|
} catch {
|
|
20422
20537
|
return;
|
|
20423
20538
|
}
|
|
20424
20539
|
for (const entry of entries) {
|
|
20425
20540
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
20426
|
-
const file =
|
|
20427
|
-
const text5 =
|
|
20428
|
-
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));
|
|
20429
20544
|
}
|
|
20430
20545
|
}
|
|
20431
20546
|
function dropIfEmpty2(dir) {
|
|
20432
20547
|
try {
|
|
20433
|
-
|
|
20548
|
+
fs55.rmdirSync(dir);
|
|
20434
20549
|
} catch {
|
|
20435
20550
|
}
|
|
20436
20551
|
}
|
|
20437
20552
|
function liftInbox() {
|
|
20438
|
-
if (!
|
|
20439
|
-
const oldFiles =
|
|
20440
|
-
if (
|
|
20441
|
-
for (const entry of
|
|
20442
|
-
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);
|
|
20443
20558
|
}
|
|
20444
20559
|
dropIfEmpty2(oldFiles);
|
|
20445
20560
|
}
|
|
20446
20561
|
const held4 = new Set(readFolder(TRIAGE).map((signal2) => signal2.sourceId));
|
|
20447
|
-
for (const entry of
|
|
20562
|
+
for (const entry of fs55.readdirSync(SIGNALS_OLD_INBOX, { withFileTypes: true })) {
|
|
20448
20563
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
20449
|
-
const from =
|
|
20564
|
+
const from = path60.join(SIGNALS_OLD_INBOX, entry.name);
|
|
20450
20565
|
const item = parse(from);
|
|
20451
20566
|
if (item && held4.has(item.sourceId)) {
|
|
20452
|
-
|
|
20567
|
+
fs55.rmSync(from, { force: true });
|
|
20453
20568
|
continue;
|
|
20454
20569
|
}
|
|
20455
20570
|
if (item) held4.add(item.sourceId);
|
|
20456
20571
|
lift(from, TRIAGE);
|
|
20457
20572
|
}
|
|
20458
|
-
const wasAt = asWritten(
|
|
20573
|
+
const wasAt = asWritten(path60.join(SIGNALS_OLD_INBOX, "files"));
|
|
20459
20574
|
const nowAt = asWritten(SIGNALS_FILES);
|
|
20460
20575
|
for (const dir of [TRIAGE, SIGNALS_ARCHIVED, SIGNALS_DISMISSED]) repoint(dir, wasAt, nowAt);
|
|
20461
20576
|
dropIfEmpty2(SIGNALS_OLD_INBOX);
|
|
20462
20577
|
}
|
|
20463
20578
|
function liftHandled() {
|
|
20464
|
-
if (!
|
|
20579
|
+
if (!fs55.existsSync(SIGNALS_OLD_HANDLED)) return;
|
|
20465
20580
|
const lines = readHandled();
|
|
20466
20581
|
const held4 = /* @__PURE__ */ new Set([
|
|
20467
20582
|
...readFolder(TRIAGE).map((signal2) => signal2.sourceId),
|
|
@@ -20471,7 +20586,7 @@ function liftHandled() {
|
|
|
20471
20586
|
for (const { sourceId, when } of lines) {
|
|
20472
20587
|
if (held4.has(sourceId)) continue;
|
|
20473
20588
|
held4.add(sourceId);
|
|
20474
|
-
|
|
20589
|
+
fs55.mkdirSync(SIGNALS_DISMISSED, { recursive: true });
|
|
20475
20590
|
const name = freeName(SIGNALS_DISMISSED, fileName("", sourceId));
|
|
20476
20591
|
const front = [
|
|
20477
20592
|
"---",
|
|
@@ -20481,13 +20596,13 @@ function liftHandled() {
|
|
|
20481
20596
|
"---",
|
|
20482
20597
|
""
|
|
20483
20598
|
];
|
|
20484
|
-
|
|
20599
|
+
fs55.writeFileSync(path60.join(SIGNALS_DISMISSED, name), `${front.join("\n")}
|
|
20485
20600
|
`);
|
|
20486
20601
|
}
|
|
20487
|
-
|
|
20602
|
+
fs55.rmSync(SIGNALS_OLD_HANDLED, { force: true });
|
|
20488
20603
|
}
|
|
20489
20604
|
function migrateTriage() {
|
|
20490
|
-
if (!
|
|
20605
|
+
if (!fs55.existsSync(SIGNALS_OLD_INBOX) && !fs55.existsSync(SIGNALS_OLD_HANDLED)) return;
|
|
20491
20606
|
try {
|
|
20492
20607
|
liftInbox();
|
|
20493
20608
|
liftHandled();
|
|
@@ -20502,13 +20617,13 @@ var init_migrate = __esm({
|
|
|
20502
20617
|
init_yaml();
|
|
20503
20618
|
init_inbox();
|
|
20504
20619
|
HANDLED_LINE = /^-\s+(.+?)\s+—\s+(.+)$/;
|
|
20505
|
-
asWritten = (dir) => `${rel(dir).split(
|
|
20620
|
+
asWritten = (dir) => `${rel(dir).split(path60.sep).join("/")}/`;
|
|
20506
20621
|
}
|
|
20507
20622
|
});
|
|
20508
20623
|
|
|
20509
20624
|
// ../cli/src/lib/signals/add.ts
|
|
20510
|
-
import
|
|
20511
|
-
import
|
|
20625
|
+
import fs56 from "node:fs";
|
|
20626
|
+
import path61 from "node:path";
|
|
20512
20627
|
import { createHash as createHash3 } from "node:crypto";
|
|
20513
20628
|
function linkTitle(url) {
|
|
20514
20629
|
let at;
|
|
@@ -20521,7 +20636,7 @@ function linkTitle(url) {
|
|
|
20521
20636
|
return oneLine(where) || url.slice(0, TITLE_MAX);
|
|
20522
20637
|
}
|
|
20523
20638
|
function asText(file) {
|
|
20524
|
-
const ext =
|
|
20639
|
+
const ext = path61.extname(file.name).toLowerCase();
|
|
20525
20640
|
if (!file.type.startsWith("text/") && file.type !== "application/json" && !TEXT_EXTENSIONS.has(ext)) return null;
|
|
20526
20641
|
try {
|
|
20527
20642
|
const text5 = new TextDecoder("utf-8", { fatal: true }).decode(file.data);
|
|
@@ -20531,9 +20646,9 @@ function asText(file) {
|
|
|
20531
20646
|
}
|
|
20532
20647
|
}
|
|
20533
20648
|
function fileName2(name, data) {
|
|
20534
|
-
const base =
|
|
20535
|
-
const ext =
|
|
20536
|
-
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";
|
|
20537
20652
|
const hash2 = createHash3("sha256").update(data).digest("hex").slice(0, 8);
|
|
20538
20653
|
return `${word}-${hash2}${ext}`;
|
|
20539
20654
|
}
|
|
@@ -20542,9 +20657,9 @@ function addToInbox(drop) {
|
|
|
20542
20657
|
const typed = (drop.text ?? "").trim();
|
|
20543
20658
|
const file = drop.file;
|
|
20544
20659
|
if (!typed && !file) return { ok: false, error: "nothing to add \u2014 paste a link or some text, or drop a file." };
|
|
20545
|
-
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.` };
|
|
20546
20661
|
if (file && file.data.length > MAX_BYTES) {
|
|
20547
|
-
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.` };
|
|
20548
20663
|
}
|
|
20549
20664
|
const read4 = describe(typed, file);
|
|
20550
20665
|
const keep2 = read4.keep;
|
|
@@ -20566,8 +20681,8 @@ ${incoming.summary}`);
|
|
|
20566
20681
|
if (seen.status === "pending" || seen.status === "archived") return { ok: false, error: `${sayHit(seen)}.` };
|
|
20567
20682
|
try {
|
|
20568
20683
|
if (keep2) {
|
|
20569
|
-
|
|
20570
|
-
|
|
20684
|
+
fs56.mkdirSync(SIGNALS_FILES, { recursive: true });
|
|
20685
|
+
fs56.writeFileSync(keep2.at, keep2.data);
|
|
20571
20686
|
}
|
|
20572
20687
|
return { ok: true, signal: writeSignal({ ...incoming, sourceId }, formatStamp(/* @__PURE__ */ new Date()), drop.slug) };
|
|
20573
20688
|
} catch (e) {
|
|
@@ -20581,7 +20696,7 @@ function describe(typed, file) {
|
|
|
20581
20696
|
return only ? [only] : [];
|
|
20582
20697
|
};
|
|
20583
20698
|
if (file) {
|
|
20584
|
-
const name =
|
|
20699
|
+
const name = path61.basename(file.name);
|
|
20585
20700
|
const text5 = asText(file);
|
|
20586
20701
|
const note = typed ? `
|
|
20587
20702
|
|
|
@@ -20593,7 +20708,7 @@ ${typed}` : "";
|
|
|
20593
20708
|
const at = keepAt(file);
|
|
20594
20709
|
return {
|
|
20595
20710
|
incoming: {
|
|
20596
|
-
title:
|
|
20711
|
+
title: path61.basename(name, path61.extname(name)) || name,
|
|
20597
20712
|
summary: `${name} \u2014 ${boardRel2(at)}${note}`,
|
|
20598
20713
|
...named2
|
|
20599
20714
|
},
|
|
@@ -20631,73 +20746,73 @@ var init_add = __esm({
|
|
|
20631
20746
|
MAX_BYTES = 25 * 1024 * 1024;
|
|
20632
20747
|
TITLE_MAX = 120;
|
|
20633
20748
|
TEXT_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".markdown", ".txt", ".text", ".csv", ".json", ".yml", ".yaml", ".log"]);
|
|
20634
|
-
boardRel2 = (file) => rel(file).split(
|
|
20749
|
+
boardRel2 = (file) => rel(file).split(path61.sep).join("/");
|
|
20635
20750
|
oneLine = (text5) => {
|
|
20636
20751
|
const first = text5.split("\n").find((line) => line.trim()) ?? "";
|
|
20637
20752
|
const clean = first.replace(/^#+\s*/, "").trim();
|
|
20638
20753
|
return clean.length > TITLE_MAX ? `${clean.slice(0, TITLE_MAX - 1).trimEnd()}\u2026` : clean;
|
|
20639
20754
|
};
|
|
20640
|
-
keepAt = (file) =>
|
|
20755
|
+
keepAt = (file) => path61.join(SIGNALS_FILES, fileName2(file.name, file.data));
|
|
20641
20756
|
}
|
|
20642
20757
|
});
|
|
20643
20758
|
|
|
20644
20759
|
// ../cli/src/lib/recurring.ts
|
|
20645
|
-
import
|
|
20646
|
-
import
|
|
20760
|
+
import fs57 from "node:fs";
|
|
20761
|
+
import path62 from "node:path";
|
|
20647
20762
|
function migratePruneMemoryCard() {
|
|
20648
20763
|
const dir = recurringDir();
|
|
20649
20764
|
let names4;
|
|
20650
20765
|
try {
|
|
20651
|
-
names4 =
|
|
20766
|
+
names4 = fs57.readdirSync(dir);
|
|
20652
20767
|
} catch {
|
|
20653
20768
|
return null;
|
|
20654
20769
|
}
|
|
20655
20770
|
const name = names4.find((entry) => entry.endsWith(`-${PRUNE_SLUG}.md`));
|
|
20656
20771
|
if (!name) return null;
|
|
20657
|
-
const file =
|
|
20772
|
+
const file = path62.join(dir, name);
|
|
20658
20773
|
try {
|
|
20659
|
-
const { meta } = parseFrontmatter(
|
|
20774
|
+
const { meta } = parseFrontmatter(fs57.readFileSync(file, "utf8"));
|
|
20660
20775
|
const cadence = meta?.cadence;
|
|
20661
20776
|
if (typeof cadence === "string") adoptMemoryPruneCadence(cadence);
|
|
20662
20777
|
} catch {
|
|
20663
20778
|
}
|
|
20664
|
-
|
|
20779
|
+
fs57.rmSync(file, { force: true });
|
|
20665
20780
|
return rel(file);
|
|
20666
20781
|
}
|
|
20667
20782
|
function legacyCards() {
|
|
20668
20783
|
const dir = recurringDir();
|
|
20669
|
-
if (!
|
|
20784
|
+
if (!fs57.existsSync(dir)) return [];
|
|
20670
20785
|
const cards = [];
|
|
20671
|
-
for (const entry of
|
|
20786
|
+
for (const entry of fs57.readdirSync(dir, { withFileTypes: true })) {
|
|
20672
20787
|
const id = idPrefix(entry.name);
|
|
20673
20788
|
if (id === null) continue;
|
|
20674
|
-
const full =
|
|
20789
|
+
const full = path62.join(dir, entry.name);
|
|
20675
20790
|
const slug2 = entry.name.replace(/\.md$/, "").replace(/^\d+-?/, "");
|
|
20676
20791
|
if (entry.isFile() && entry.name.endsWith(".md")) cards.push({ id, slug: slug2, file: full });
|
|
20677
|
-
else if (entry.isDirectory() &&
|
|
20678
|
-
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 });
|
|
20679
20794
|
}
|
|
20680
20795
|
}
|
|
20681
20796
|
return cards.sort((a, b) => a.id - b.id);
|
|
20682
20797
|
}
|
|
20683
20798
|
function mergeInto2(from, to, skip = []) {
|
|
20684
|
-
|
|
20685
|
-
for (const entry of
|
|
20799
|
+
fs57.mkdirSync(to, { recursive: true });
|
|
20800
|
+
for (const entry of fs57.readdirSync(from, { withFileTypes: true })) {
|
|
20686
20801
|
if (skip.includes(entry.name)) continue;
|
|
20687
|
-
const source =
|
|
20688
|
-
const target =
|
|
20689
|
-
if (!
|
|
20690
|
-
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);
|
|
20691
20806
|
}
|
|
20692
|
-
if (isEmpty(from))
|
|
20807
|
+
if (isEmpty(from)) fs57.rmSync(from, { recursive: true, force: true });
|
|
20693
20808
|
}
|
|
20694
20809
|
function moveSideFolders() {
|
|
20695
20810
|
const dir = recurringDir();
|
|
20696
|
-
for (const entry of
|
|
20811
|
+
for (const entry of fs57.readdirSync(dir, { withFileTypes: true })) {
|
|
20697
20812
|
if (!entry.isDirectory() || idPrefix(entry.name) !== null) continue;
|
|
20698
|
-
const from =
|
|
20699
|
-
if (isEmpty(from))
|
|
20700
|
-
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));
|
|
20701
20816
|
}
|
|
20702
20817
|
}
|
|
20703
20818
|
function sections(body) {
|
|
@@ -20747,8 +20862,8 @@ function agentName(card, text5) {
|
|
|
20747
20862
|
const known2 = /* @__PURE__ */ new Set([...specAgentCatalog().agents.map((a) => a.name), ...ROLE_NAMES]);
|
|
20748
20863
|
for (const name of [slug2, `${slug2}-${card.id}`]) {
|
|
20749
20864
|
if (!AGENT_NAME.test(name)) continue;
|
|
20750
|
-
const file =
|
|
20751
|
-
const written =
|
|
20865
|
+
const file = path62.join(AGENTS, name, AGENT_FILE3);
|
|
20866
|
+
const written = fs57.existsSync(file) ? fs57.readFileSync(file, "utf8") : null;
|
|
20752
20867
|
if (written === text5(name)) return name;
|
|
20753
20868
|
if (written === null && !known2.has(name)) return name;
|
|
20754
20869
|
}
|
|
@@ -20765,11 +20880,11 @@ ${q.options.map((o) => `- ${o}`).join("\n")}` : "";
|
|
|
20765
20880
|
function removeCard(card) {
|
|
20766
20881
|
dropCrossRefs(card.id);
|
|
20767
20882
|
const target = card.folder ?? card.file;
|
|
20768
|
-
stripReadmeRefs({ kind: card.folder ? "group" : "file", rel:
|
|
20769
|
-
|
|
20883
|
+
stripReadmeRefs({ kind: card.folder ? "group" : "file", rel: path62.relative(TODO, target).split(path62.sep).join("/") });
|
|
20884
|
+
fs57.rmSync(target, { recursive: true, force: true });
|
|
20770
20885
|
}
|
|
20771
20886
|
function migrateCard(card) {
|
|
20772
|
-
const { meta, body } = parseFrontmatter(
|
|
20887
|
+
const { meta, body } = parseFrontmatter(fs57.readFileSync(card.file, "utf8"));
|
|
20773
20888
|
if (!meta) throw new Error("its frontmatter cannot be read");
|
|
20774
20889
|
const raw = meta;
|
|
20775
20890
|
const said3 = (key) => typeof raw[key] === "string" ? raw[key].trim() : "";
|
|
@@ -20779,19 +20894,19 @@ function migrateCard(card) {
|
|
|
20779
20894
|
if (!flow) throw new Error("this board has no workflow to put it in");
|
|
20780
20895
|
const text5 = (name2) => agentText(name2, meta.title || card.slug, body);
|
|
20781
20896
|
const name = agentName(card, text5);
|
|
20782
|
-
const dir =
|
|
20783
|
-
const file =
|
|
20784
|
-
const madeDir = !
|
|
20785
|
-
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);
|
|
20786
20901
|
try {
|
|
20787
|
-
|
|
20788
|
-
if (wrote)
|
|
20902
|
+
fs57.mkdirSync(dir, { recursive: true });
|
|
20903
|
+
if (wrote) fs57.writeFileSync(file, text5(name));
|
|
20789
20904
|
const adopted = adoptWorkflowScheduled(flow.id, name, { cadence, lastRun });
|
|
20790
20905
|
if (!adopted.ok) throw new Error(adopted.error);
|
|
20791
20906
|
questionsToTriage(meta, name);
|
|
20792
20907
|
} catch (e) {
|
|
20793
|
-
if (madeDir)
|
|
20794
|
-
else if (wrote)
|
|
20908
|
+
if (madeDir) fs57.rmSync(dir, { recursive: true, force: true });
|
|
20909
|
+
else if (wrote) fs57.rmSync(file, { force: true });
|
|
20795
20910
|
throw e;
|
|
20796
20911
|
}
|
|
20797
20912
|
if (card.folder) mergeInto2(card.folder, dir, ["root.md"]);
|
|
@@ -20801,7 +20916,7 @@ function migrateCard(card) {
|
|
|
20801
20916
|
function migrateRecurringCards() {
|
|
20802
20917
|
const done2 = { agents: [], removed: [], failed: [] };
|
|
20803
20918
|
const dir = recurringDir();
|
|
20804
|
-
if (!
|
|
20919
|
+
if (!fs57.existsSync(dir)) return done2;
|
|
20805
20920
|
migratePruneMemoryCard();
|
|
20806
20921
|
moveSideFolders();
|
|
20807
20922
|
for (const card of legacyCards()) {
|
|
@@ -20814,7 +20929,7 @@ function migrateRecurringCards() {
|
|
|
20814
20929
|
done2.failed.push({ card: rel(card.file), why: e instanceof Error ? e.message : String(e) });
|
|
20815
20930
|
}
|
|
20816
20931
|
}
|
|
20817
|
-
if (isEmpty(dir))
|
|
20932
|
+
if (isEmpty(dir)) fs57.rmSync(dir, { recursive: true, force: true });
|
|
20818
20933
|
return done2;
|
|
20819
20934
|
}
|
|
20820
20935
|
function recurringMigrationLines(done2) {
|
|
@@ -20847,14 +20962,14 @@ var init_recurring = __esm({
|
|
|
20847
20962
|
PRUNE_SLUG = "prune-the-memory";
|
|
20848
20963
|
FETCH_SLUG = "fetch-triage-items";
|
|
20849
20964
|
AGENT_FILE3 = "AGENT.md";
|
|
20850
|
-
recurringDir = () =>
|
|
20851
|
-
isEmpty = (dir) =>
|
|
20965
|
+
recurringDir = () => path62.join(TODO, LEGACY_RECURRING);
|
|
20966
|
+
isEmpty = (dir) => fs57.readdirSync(dir).every((name) => name === ".DS_Store");
|
|
20852
20967
|
SECTION = /^##\s+(.+?)\s*$/;
|
|
20853
20968
|
}
|
|
20854
20969
|
});
|
|
20855
20970
|
|
|
20856
20971
|
// ../cli/src/commands/init.ts
|
|
20857
|
-
import
|
|
20972
|
+
import fs58 from "node:fs";
|
|
20858
20973
|
function boardReadme() {
|
|
20859
20974
|
return `# Board
|
|
20860
20975
|
|
|
@@ -20867,20 +20982,20 @@ _(none)_
|
|
|
20867
20982
|
`;
|
|
20868
20983
|
}
|
|
20869
20984
|
function writeConfigIfMissing() {
|
|
20870
|
-
if (
|
|
20871
|
-
|
|
20872
|
-
|
|
20985
|
+
if (fs58.existsSync(CONFIG)) return false;
|
|
20986
|
+
fs58.mkdirSync(KANBAN, { recursive: true });
|
|
20987
|
+
fs58.writeFileSync(CONFIG, CONFIG_TEMPLATE);
|
|
20873
20988
|
return true;
|
|
20874
20989
|
}
|
|
20875
20990
|
function writeModulesIfMissing() {
|
|
20876
|
-
if (
|
|
20877
|
-
|
|
20878
|
-
|
|
20991
|
+
if (fs58.existsSync(MODULES_MD)) return false;
|
|
20992
|
+
fs58.mkdirSync(KANBAN, { recursive: true });
|
|
20993
|
+
fs58.writeFileSync(MODULES_MD, MODULES_TEMPLATE);
|
|
20879
20994
|
return true;
|
|
20880
20995
|
}
|
|
20881
20996
|
function writeGitignoreIfMissing() {
|
|
20882
|
-
|
|
20883
|
-
const text5 =
|
|
20997
|
+
fs58.mkdirSync(KANBAN, { recursive: true });
|
|
20998
|
+
const text5 = fs58.existsSync(KANBAN_GITIGNORE) ? fs58.readFileSync(KANBAN_GITIGNORE, "utf8") : "";
|
|
20884
20999
|
const has = (rule) => text5.split("\n").some((line) => line.trim() === rule.line);
|
|
20885
21000
|
const missing = IGNORE_RULES.filter((rule) => !has(rule));
|
|
20886
21001
|
if (!missing.length) return false;
|
|
@@ -20888,37 +21003,37 @@ function writeGitignoreIfMissing() {
|
|
|
20888
21003
|
${rule.line}
|
|
20889
21004
|
`).join("");
|
|
20890
21005
|
const separator = !text5 || text5.endsWith("\n") ? "" : "\n";
|
|
20891
|
-
|
|
21006
|
+
fs58.writeFileSync(KANBAN_GITIGNORE, `${text5}${separator}${block}`);
|
|
20892
21007
|
return true;
|
|
20893
21008
|
}
|
|
20894
21009
|
function writeSkeletonIfMissing() {
|
|
20895
21010
|
const added = [];
|
|
20896
|
-
if (!
|
|
20897
|
-
|
|
21011
|
+
if (!fs58.existsSync(TODO)) {
|
|
21012
|
+
fs58.mkdirSync(TODO, { recursive: true });
|
|
20898
21013
|
added.push(`${rel(TODO)}/`);
|
|
20899
21014
|
}
|
|
20900
|
-
if (!
|
|
20901
|
-
|
|
21015
|
+
if (!fs58.existsSync(README)) {
|
|
21016
|
+
fs58.writeFileSync(README, boardReadme());
|
|
20902
21017
|
added.push(rel(README));
|
|
20903
21018
|
}
|
|
20904
21019
|
return added;
|
|
20905
21020
|
}
|
|
20906
21021
|
function retireReleases() {
|
|
20907
21022
|
const done2 = [];
|
|
20908
|
-
if (
|
|
20909
|
-
|
|
21023
|
+
if (fs58.existsSync(RELEASES)) {
|
|
21024
|
+
fs58.rmSync(RELEASES);
|
|
20910
21025
|
done2.push(`deleted ${rel(RELEASES)}`);
|
|
20911
21026
|
}
|
|
20912
21027
|
let cards = 0;
|
|
20913
21028
|
for (const dir of [TODO, ARCHIVE]) {
|
|
20914
|
-
if (!
|
|
21029
|
+
if (!fs58.existsSync(dir)) continue;
|
|
20915
21030
|
for (const file of walkMd(dir)) {
|
|
20916
|
-
const text5 =
|
|
21031
|
+
const text5 = fs58.readFileSync(file, "utf8");
|
|
20917
21032
|
const end2 = text5.startsWith("---\n") ? text5.indexOf("\n---", 3) : -1;
|
|
20918
21033
|
if (end2 < 0) continue;
|
|
20919
21034
|
const head = text5.slice(0, end2).replace(/\nrelease:[^\n]*/, "");
|
|
20920
21035
|
if (head.length === end2) continue;
|
|
20921
|
-
|
|
21036
|
+
fs58.writeFileSync(file, head + text5.slice(end2));
|
|
20922
21037
|
cards++;
|
|
20923
21038
|
}
|
|
20924
21039
|
}
|
|
@@ -20926,7 +21041,7 @@ function retireReleases() {
|
|
|
20926
21041
|
return done2.join(", ");
|
|
20927
21042
|
}
|
|
20928
21043
|
function cmdInit() {
|
|
20929
|
-
if (
|
|
21044
|
+
if (fs58.existsSync(KANBAN)) {
|
|
20930
21045
|
const added = [
|
|
20931
21046
|
...writeSkeletonIfMissing(),
|
|
20932
21047
|
writeConfigIfMissing() && rel(CONFIG),
|
|
@@ -20961,8 +21076,8 @@ function cmdInit() {
|
|
|
20961
21076
|
}
|
|
20962
21077
|
return { board: rel(KANBAN), created: false, added, memory_paths: scaffolded.map((s) => rel(s.dir)) };
|
|
20963
21078
|
}
|
|
20964
|
-
|
|
20965
|
-
|
|
21079
|
+
fs58.mkdirSync(TODO, { recursive: true });
|
|
21080
|
+
fs58.writeFileSync(README, boardReadme());
|
|
20966
21081
|
writeConfigIfMissing();
|
|
20967
21082
|
scaffoldProjectMemory();
|
|
20968
21083
|
writeModulesIfMissing();
|
|
@@ -21051,25 +21166,25 @@ var init_scheduled = __esm({
|
|
|
21051
21166
|
});
|
|
21052
21167
|
|
|
21053
21168
|
// ../cli/src/lib/card-age.ts
|
|
21054
|
-
import
|
|
21055
|
-
import
|
|
21169
|
+
import fs59 from "node:fs";
|
|
21170
|
+
import path63 from "node:path";
|
|
21056
21171
|
function staleAfterIn(config) {
|
|
21057
21172
|
const days = Number(LINE.exec(config)?.[1]);
|
|
21058
21173
|
return Number.isInteger(days) && days > 0 ? days : STALE_AFTER_DEFAULT;
|
|
21059
21174
|
}
|
|
21060
21175
|
function staleAfter() {
|
|
21061
|
-
if (!
|
|
21062
|
-
return staleAfterIn(
|
|
21176
|
+
if (!fs59.existsSync(CONFIG)) return STALE_AFTER_DEFAULT;
|
|
21177
|
+
return staleAfterIn(fs59.readFileSync(CONFIG, "utf8"));
|
|
21063
21178
|
}
|
|
21064
21179
|
function todoPathspec(top) {
|
|
21065
|
-
const from =
|
|
21066
|
-
if (!from || from.startsWith("..") ||
|
|
21067
|
-
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("/");
|
|
21068
21183
|
}
|
|
21069
21184
|
function cardFile2(spec, printed) {
|
|
21070
21185
|
const p = unquotePath(printed);
|
|
21071
21186
|
if (!p.startsWith(`${spec}/`)) return null;
|
|
21072
|
-
return
|
|
21187
|
+
return path63.join(TODO, ...p.slice(spec.length + 1).split("/"));
|
|
21073
21188
|
}
|
|
21074
21189
|
function dirtyCards(top, spec) {
|
|
21075
21190
|
const out = git(["status", "--porcelain", "--untracked-files=all", "--", spec], top);
|
|
@@ -21116,9 +21231,9 @@ var init_card_age = __esm({
|
|
|
21116
21231
|
gitTop = () => git(["rev-parse", "--show-toplevel"])?.trim() || null;
|
|
21117
21232
|
real = (p) => {
|
|
21118
21233
|
try {
|
|
21119
|
-
return
|
|
21234
|
+
return fs59.realpathSync(p);
|
|
21120
21235
|
} catch {
|
|
21121
|
-
return
|
|
21236
|
+
return path63.resolve(p);
|
|
21122
21237
|
}
|
|
21123
21238
|
};
|
|
21124
21239
|
unquotePath = (line) => {
|
|
@@ -21153,8 +21268,8 @@ var init_card_holds = __esm({
|
|
|
21153
21268
|
});
|
|
21154
21269
|
|
|
21155
21270
|
// ../cli/src/commands/list.ts
|
|
21156
|
-
import
|
|
21157
|
-
import
|
|
21271
|
+
import fs60 from "node:fs";
|
|
21272
|
+
import path64 from "node:path";
|
|
21158
21273
|
function summaryLine(body) {
|
|
21159
21274
|
const lines = body.split("\n");
|
|
21160
21275
|
let i = 0;
|
|
@@ -21170,13 +21285,13 @@ function summaryLine(body) {
|
|
|
21170
21285
|
function openRows() {
|
|
21171
21286
|
const rows = [];
|
|
21172
21287
|
for (const file of walkMd(TODO)) {
|
|
21173
|
-
const base =
|
|
21288
|
+
const base = path64.basename(file);
|
|
21174
21289
|
if (base === "README.md") continue;
|
|
21175
21290
|
const isRoot = base === "root.md";
|
|
21176
|
-
const id = isRoot ? idPrefix(
|
|
21291
|
+
const id = isRoot ? idPrefix(path64.basename(path64.dirname(file))) : idPrefix(base);
|
|
21177
21292
|
if (id == null) continue;
|
|
21178
|
-
if (isLegacyRecurring(
|
|
21179
|
-
const { meta, body } = parseFrontmatter(
|
|
21293
|
+
if (isLegacyRecurring(path64.relative(TODO, file))) continue;
|
|
21294
|
+
const { meta, body } = parseFrontmatter(fs60.readFileSync(file, "utf8"));
|
|
21180
21295
|
rows.push({
|
|
21181
21296
|
id,
|
|
21182
21297
|
file,
|
|
@@ -21218,15 +21333,15 @@ function cmdStale(rows, all2, scope, mod) {
|
|
|
21218
21333
|
}
|
|
21219
21334
|
function archivedFiles() {
|
|
21220
21335
|
const files2 = /* @__PURE__ */ new Map();
|
|
21221
|
-
if (!
|
|
21336
|
+
if (!fs60.existsSync(ARCHIVE)) return files2;
|
|
21222
21337
|
for (const file of walkMd(ARCHIVE)) {
|
|
21223
|
-
const id = idPrefix(
|
|
21338
|
+
const id = idPrefix(path64.basename(file));
|
|
21224
21339
|
if (id !== null) files2.set(id, file);
|
|
21225
21340
|
}
|
|
21226
21341
|
for (const dir of walkDirs(ARCHIVE)) {
|
|
21227
|
-
const id = idPrefix(
|
|
21228
|
-
const root =
|
|
21229
|
-
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);
|
|
21230
21345
|
}
|
|
21231
21346
|
return files2;
|
|
21232
21347
|
}
|
|
@@ -21337,14 +21452,14 @@ var init_list = __esm({
|
|
|
21337
21452
|
});
|
|
21338
21453
|
|
|
21339
21454
|
// ../cli/src/commands/misc.ts
|
|
21340
|
-
import
|
|
21341
|
-
import
|
|
21455
|
+
import fs61 from "node:fs";
|
|
21456
|
+
import path65 from "node:path";
|
|
21342
21457
|
function extractOldMeta(text5, file) {
|
|
21343
21458
|
const grab = (re) => {
|
|
21344
21459
|
const m = text5.match(re);
|
|
21345
21460
|
return m ? m[1].trim() : null;
|
|
21346
21461
|
};
|
|
21347
|
-
const title = grab(/^#\s+(.+)$/m) || slugify(
|
|
21462
|
+
const title = grab(/^#\s+(.+)$/m) || slugify(path65.basename(file, ".md").replace(/^\d+-/, "")).replace(/-/g, " ");
|
|
21348
21463
|
const norm = (raw) => {
|
|
21349
21464
|
const v = (raw || "").toLowerCase();
|
|
21350
21465
|
return LEVELS.includes(v) ? v : "med";
|
|
@@ -21371,11 +21486,11 @@ function stripOldHeader(text5) {
|
|
|
21371
21486
|
}
|
|
21372
21487
|
function cmdMigrate(opts) {
|
|
21373
21488
|
const dry = opts.dryRun === true;
|
|
21374
|
-
const files2 = walkMd(TODO).filter((f) =>
|
|
21489
|
+
const files2 = walkMd(TODO).filter((f) => path65.basename(f) !== "README.md");
|
|
21375
21490
|
let changed = 0;
|
|
21376
21491
|
let skipped = 0;
|
|
21377
21492
|
for (const file of files2) {
|
|
21378
|
-
const text5 =
|
|
21493
|
+
const text5 = fs61.readFileSync(file, "utf8");
|
|
21379
21494
|
if (text5.trimStart().startsWith("---")) {
|
|
21380
21495
|
skipped++;
|
|
21381
21496
|
continue;
|
|
@@ -21384,7 +21499,7 @@ function cmdMigrate(opts) {
|
|
|
21384
21499
|
const out = serializeFrontmatter(meta) + "\n\n" + stripOldHeader(text5).replace(/^\n+/, "");
|
|
21385
21500
|
if (dry) say(`would migrate ${rel(file)} (priority=${meta.priority})`);
|
|
21386
21501
|
else {
|
|
21387
|
-
|
|
21502
|
+
fs61.writeFileSync(file, out.endsWith("\n") ? out : out + "\n");
|
|
21388
21503
|
say(`migrated ${rel(file)}`);
|
|
21389
21504
|
}
|
|
21390
21505
|
changed++;
|
|
@@ -21405,10 +21520,10 @@ var init_misc = __esm({
|
|
|
21405
21520
|
});
|
|
21406
21521
|
|
|
21407
21522
|
// ../cli/src/lib/group-close.ts
|
|
21408
|
-
import
|
|
21523
|
+
import fs62 from "node:fs";
|
|
21409
21524
|
function groupCloses(rootFile) {
|
|
21410
21525
|
try {
|
|
21411
|
-
return groupCloseOf(
|
|
21526
|
+
return groupCloseOf(fs62.readFileSync(rootFile, "utf8"));
|
|
21412
21527
|
} catch {
|
|
21413
21528
|
return false;
|
|
21414
21529
|
}
|
|
@@ -21421,12 +21536,12 @@ var init_group_close = __esm({
|
|
|
21421
21536
|
});
|
|
21422
21537
|
|
|
21423
21538
|
// ../cli/src/commands/remove.ts
|
|
21424
|
-
import
|
|
21425
|
-
import
|
|
21539
|
+
import fs63 from "node:fs";
|
|
21540
|
+
import path66 from "node:path";
|
|
21426
21541
|
function leavingIds(id, found2) {
|
|
21427
21542
|
const ids = /* @__PURE__ */ new Set([id]);
|
|
21428
21543
|
if (found2.kind === "group") {
|
|
21429
|
-
const names4 = [...walkMd(found2.target), ...walkDirs(found2.target)].map((f) =>
|
|
21544
|
+
const names4 = [...walkMd(found2.target), ...walkDirs(found2.target)].map((f) => path66.basename(f));
|
|
21430
21545
|
for (const name of names4) {
|
|
21431
21546
|
const n = idPrefix(name);
|
|
21432
21547
|
if (n !== null) ids.add(n);
|
|
@@ -21438,10 +21553,10 @@ function findMentions(ids) {
|
|
|
21438
21553
|
const hits = [];
|
|
21439
21554
|
const re = new RegExp(ids.map((id) => `#${id}(?!\\d)`).join("|"));
|
|
21440
21555
|
for (const dir of [TODO, MEMORY]) {
|
|
21441
|
-
if (!
|
|
21556
|
+
if (!fs63.existsSync(dir)) continue;
|
|
21442
21557
|
for (const file of walkMd(dir)) {
|
|
21443
|
-
if (
|
|
21444
|
-
const lines =
|
|
21558
|
+
if (path66.basename(file) === "README.md") continue;
|
|
21559
|
+
const lines = fs63.readFileSync(file, "utf8").split("\n");
|
|
21445
21560
|
const fmEnd = frontmatterEnd(lines);
|
|
21446
21561
|
lines.forEach((line, i) => {
|
|
21447
21562
|
if (!re.test(line)) return;
|
|
@@ -21457,12 +21572,12 @@ function findMentions(ids) {
|
|
|
21457
21572
|
return hits;
|
|
21458
21573
|
}
|
|
21459
21574
|
function leavingCards(id, found2) {
|
|
21460
|
-
const own = found2.kind === "group" ?
|
|
21575
|
+
const own = found2.kind === "group" ? path66.join(found2.target, "root.md") : found2.target;
|
|
21461
21576
|
const cards = [{ id, file: own }];
|
|
21462
21577
|
if (found2.kind !== "group") return cards;
|
|
21463
21578
|
for (const file of walkMd(found2.target)) {
|
|
21464
21579
|
if (file === own) continue;
|
|
21465
|
-
const n = idPrefix(
|
|
21580
|
+
const n = idPrefix(path66.basename(file));
|
|
21466
21581
|
if (n !== null) cards.push({ id: n, file });
|
|
21467
21582
|
}
|
|
21468
21583
|
return cards;
|
|
@@ -21471,10 +21586,10 @@ function stampLeaving(cards, rejection) {
|
|
|
21471
21586
|
const day = formatDay();
|
|
21472
21587
|
const stamp4 = rejection && !rejection.discard ? { reason: rejection.reason, at: (/* @__PURE__ */ new Date()).toISOString(), by: insideRun() ? "agent" : "user" } : rejection && { reason: rejection.reason };
|
|
21473
21588
|
for (const card of cards) {
|
|
21474
|
-
if (!
|
|
21475
|
-
const text5 =
|
|
21589
|
+
if (!fs63.existsSync(card.file)) continue;
|
|
21590
|
+
const text5 = fs63.readFileSync(card.file, "utf8");
|
|
21476
21591
|
const stamped = stampLeavingIn(text5, day, stamp4);
|
|
21477
|
-
if (stamped !== text5)
|
|
21592
|
+
if (stamped !== text5) fs63.writeFileSync(card.file, stamped);
|
|
21478
21593
|
}
|
|
21479
21594
|
}
|
|
21480
21595
|
function unfinishedDelivery(id) {
|
|
@@ -21490,7 +21605,7 @@ function unfinishedDelivery(id) {
|
|
|
21490
21605
|
}[gap];
|
|
21491
21606
|
return `#${id} is not finished: ${what}.`;
|
|
21492
21607
|
}
|
|
21493
|
-
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))));
|
|
21494
21609
|
return missing ? `#${id} is not finished: ${missing} is not on this machine.` : null;
|
|
21495
21610
|
}
|
|
21496
21611
|
function agentDecisions(text5) {
|
|
@@ -21517,8 +21632,8 @@ function removeCard2(id, metric, options) {
|
|
|
21517
21632
|
const unfinished = metric === "completed" ? unfinishedDelivery(id) : null;
|
|
21518
21633
|
if (unfinished) die(unfinished, { kind: "plan-delivery-unfinished" });
|
|
21519
21634
|
const dest = options.cleanupDiscarded && locateArchived(id) ? null : archiveDest(found2);
|
|
21520
|
-
const cardFile3 = found2.kind === "group" ?
|
|
21521
|
-
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") : "";
|
|
21522
21637
|
const { meta: cardMeta3 } = parseFrontmatter(cardText);
|
|
21523
21638
|
const alsoRemoved = found2.kind === "group" ? walkMd(found2.target).filter((f) => f !== cardFile3).map((f) => rel(f)).sort() : [];
|
|
21524
21639
|
const leftIds = leavingIds(id, found2);
|
|
@@ -21549,10 +21664,10 @@ function removeCard2(id, metric, options) {
|
|
|
21549
21664
|
const groupRoot = found2.kind === "file" && !options.closing ? enclosingGroupRoot(found2.target) : null;
|
|
21550
21665
|
if (dest) {
|
|
21551
21666
|
stampLeaving(leaving, metric === "rejected" ? { reason: options.reason?.trim() ?? "", discard: options.discard === true } : null);
|
|
21552
|
-
|
|
21553
|
-
|
|
21667
|
+
fs63.mkdirSync(ARCHIVE, { recursive: true });
|
|
21668
|
+
fs63.renameSync(found2.target, dest);
|
|
21554
21669
|
} else {
|
|
21555
|
-
|
|
21670
|
+
fs63.rmSync(found2.target, { recursive: true, force: true });
|
|
21556
21671
|
}
|
|
21557
21672
|
const removedRefs = stripReadmeRefs(found2);
|
|
21558
21673
|
let marked2 = null;
|
|
@@ -21601,14 +21716,14 @@ function removeCard2(id, metric, options) {
|
|
|
21601
21716
|
};
|
|
21602
21717
|
}
|
|
21603
21718
|
function closeGroup(rootFile) {
|
|
21604
|
-
const rootId = idPrefix(
|
|
21719
|
+
const rootId = idPrefix(path66.basename(path66.dirname(rootFile)));
|
|
21605
21720
|
if (rootId === null) return null;
|
|
21606
21721
|
if (runnerProject(KANBAN)) return null;
|
|
21607
21722
|
if (!groupCloses(rootFile)) return null;
|
|
21608
21723
|
say(`
|
|
21609
21724
|
every subtask line on #${rootId} is resolved \u2014 closing the group:`);
|
|
21610
21725
|
try {
|
|
21611
|
-
const { meta, body } = parseFrontmatter(
|
|
21726
|
+
const { meta, body } = parseFrontmatter(fs63.readFileSync(rootFile, "utf8"));
|
|
21612
21727
|
const res = cmdRemove(rootId, "completed", { closing: true });
|
|
21613
21728
|
return {
|
|
21614
21729
|
id: rootId,
|
|
@@ -21665,7 +21780,7 @@ function cleanupDiscardedCards(sessionId) {
|
|
|
21665
21780
|
if (card.pending) throw new Error(`Discarding #${card.id} did not finish. Retry the discard.`);
|
|
21666
21781
|
while (locate(card.id)) cmdRemove(card.id, "rejected", { discard: true, cleanupDiscarded: true });
|
|
21667
21782
|
dropCrossRefs(card.id);
|
|
21668
|
-
stripReadmeRefs({ kind: "file", rel:
|
|
21783
|
+
stripReadmeRefs({ kind: "file", rel: path66.relative(TODO, path66.resolve(REPO_ROOT, card.path)).split(path66.sep).join("/") });
|
|
21669
21784
|
}
|
|
21670
21785
|
return discarded.map((c) => c.id);
|
|
21671
21786
|
});
|
|
@@ -21751,8 +21866,8 @@ var init_memory_file = __esm({
|
|
|
21751
21866
|
});
|
|
21752
21867
|
|
|
21753
21868
|
// ../cli/src/commands/rule.ts
|
|
21754
|
-
import
|
|
21755
|
-
import
|
|
21869
|
+
import fs64 from "node:fs";
|
|
21870
|
+
import path67 from "node:path";
|
|
21756
21871
|
function cmdRule(askedName, flags) {
|
|
21757
21872
|
const agent = askedName.trim();
|
|
21758
21873
|
if (!agentRoster().some((a) => a.name === agent)) {
|
|
@@ -21761,7 +21876,7 @@ function cmdRule(askedName, flags) {
|
|
|
21761
21876
|
const rule = readRuleText(flags.file, flags.text);
|
|
21762
21877
|
const res = setAgentRule(agent, rule);
|
|
21763
21878
|
if (!res.ok) die(res.error);
|
|
21764
|
-
const file = rel(
|
|
21879
|
+
const file = rel(path67.join(RULES, `${agent}.md`));
|
|
21765
21880
|
say(rule ? `wrote the \`${agent}\` rule (${file})` : `cleared the \`${agent}\` rule`);
|
|
21766
21881
|
return { agent, file, cleared: !rule };
|
|
21767
21882
|
}
|
|
@@ -21769,7 +21884,7 @@ function readRuleText(file, text5) {
|
|
|
21769
21884
|
if (file !== void 0 && text5 !== void 0) die("pass --file or --text, not both");
|
|
21770
21885
|
if (file !== void 0) {
|
|
21771
21886
|
try {
|
|
21772
|
-
return
|
|
21887
|
+
return fs64.readFileSync(file, "utf8").trim();
|
|
21773
21888
|
} catch {
|
|
21774
21889
|
die(`can't read ${file} \u2014 write the rule to a file, then pass its path`);
|
|
21775
21890
|
}
|
|
@@ -21896,22 +22011,22 @@ var init_storyboard = __esm({
|
|
|
21896
22011
|
});
|
|
21897
22012
|
|
|
21898
22013
|
// ../cli/src/lib/spec-contract.ts
|
|
21899
|
-
import
|
|
21900
|
-
import
|
|
22014
|
+
import fs65 from "node:fs";
|
|
22015
|
+
import path68 from "node:path";
|
|
21901
22016
|
function snapshotSpecs() {
|
|
21902
22017
|
const out = /* @__PURE__ */ new Map();
|
|
21903
|
-
if (!
|
|
22018
|
+
if (!fs65.existsSync(TODO)) return out;
|
|
21904
22019
|
for (const file of walkMd(TODO)) {
|
|
21905
|
-
if (isLegacyRecurring(
|
|
21906
|
-
const id = idPrefix(
|
|
21907
|
-
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") });
|
|
21908
22023
|
}
|
|
21909
22024
|
return out;
|
|
21910
22025
|
}
|
|
21911
22026
|
function enclosingGroups(file) {
|
|
21912
22027
|
const ids = [];
|
|
21913
22028
|
for (let root = enclosingGroupRoot(file); root; root = enclosingGroupRoot(root)) {
|
|
21914
|
-
const id = idPrefix(
|
|
22029
|
+
const id = idPrefix(path68.basename(path68.dirname(root)));
|
|
21915
22030
|
if (id !== null) ids.push(id);
|
|
21916
22031
|
}
|
|
21917
22032
|
return ids;
|
|
@@ -22080,9 +22195,9 @@ function validateSpec(file, text5, id) {
|
|
|
22080
22195
|
}
|
|
22081
22196
|
function inside(dir, name) {
|
|
22082
22197
|
try {
|
|
22083
|
-
const root =
|
|
22084
|
-
const real2 =
|
|
22085
|
-
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;
|
|
22086
22201
|
} catch {
|
|
22087
22202
|
return null;
|
|
22088
22203
|
}
|
|
@@ -22100,7 +22215,7 @@ function variantsProblem(raw, src, id) {
|
|
|
22100
22215
|
if (files2.length < 2 || !files2.every((f) => typeof f === "string" && f)) return `variants needs at least two labels, each naming a file. ${shape}`;
|
|
22101
22216
|
if (!files2.includes(src)) return `src="${src}" is not one of the variants. Set src to the version shown first.`;
|
|
22102
22217
|
if (id === void 0) return null;
|
|
22103
|
-
const dir =
|
|
22218
|
+
const dir = path68.join(ASSETS, String(id));
|
|
22104
22219
|
for (const f of files2) {
|
|
22105
22220
|
const parts = f.split("/");
|
|
22106
22221
|
if (parts.length !== 3 || parts[0] !== ".assets" || parts[1] !== String(id) || !inside(dir, parts[2])) {
|
|
@@ -22110,7 +22225,7 @@ function variantsProblem(raw, src, id) {
|
|
|
22110
22225
|
return null;
|
|
22111
22226
|
}
|
|
22112
22227
|
function compareProblem(attrs, id) {
|
|
22113
|
-
const dir =
|
|
22228
|
+
const dir = path68.join(ASSETS, String(id));
|
|
22114
22229
|
const src = (message) => ({ rule: "compare-src", message });
|
|
22115
22230
|
const shots = (message) => ({ rule: "compare-shots", message });
|
|
22116
22231
|
for (const side of ["a", "b"]) {
|
|
@@ -22130,7 +22245,7 @@ function compareProblem(attrs, id) {
|
|
|
22130
22245
|
const file = inside(dir, named2.name);
|
|
22131
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.` };
|
|
22132
22247
|
try {
|
|
22133
|
-
return { data: JSON.parse(
|
|
22248
|
+
return { data: JSON.parse(fs65.readFileSync(file, "utf8")) };
|
|
22134
22249
|
} catch {
|
|
22135
22250
|
return { message: `${at} is not valid JSON.` };
|
|
22136
22251
|
}
|
|
@@ -22150,7 +22265,7 @@ function compareProblem(attrs, id) {
|
|
|
22150
22265
|
}
|
|
22151
22266
|
function validateStoryboards(file, id, text5) {
|
|
22152
22267
|
const errors = [];
|
|
22153
|
-
const dir =
|
|
22268
|
+
const dir = path68.join(ASSETS, String(id));
|
|
22154
22269
|
for (const marker of storyboardMarkers(text5)) {
|
|
22155
22270
|
const named2 = assetName(marker.src, id, ["json"]);
|
|
22156
22271
|
const add = (message) => errors.push({ file: rel(file), line: marker.line, rule: "storyboard-src", message });
|
|
@@ -22299,8 +22414,8 @@ var init_mockups = __esm({
|
|
|
22299
22414
|
});
|
|
22300
22415
|
|
|
22301
22416
|
// ../cli/src/commands/spec-write.ts
|
|
22302
|
-
import
|
|
22303
|
-
import
|
|
22417
|
+
import fs66 from "node:fs";
|
|
22418
|
+
import path69 from "node:path";
|
|
22304
22419
|
function cmdSpecWrite(id, askedName, flags) {
|
|
22305
22420
|
const agent = findSpecAgent(askedName);
|
|
22306
22421
|
if (!agent) die(notAnAgent(askedName), { kind: "no-such-spec-agent", specAgent: askedName });
|
|
@@ -22308,11 +22423,11 @@ function cmdSpecWrite(id, askedName, flags) {
|
|
|
22308
22423
|
const section = readSection(flags.file, flags.text);
|
|
22309
22424
|
const found2 = locate(id);
|
|
22310
22425
|
if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
|
|
22311
|
-
const file = found2.kind === "group" ?
|
|
22312
|
-
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"));
|
|
22313
22428
|
if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
|
|
22314
22429
|
const { body: next, replaced } = splice(body, name, section);
|
|
22315
|
-
|
|
22430
|
+
fs66.writeFileSync(file, serializeFrontmatter(meta) + "\n" + next);
|
|
22316
22431
|
say(`${replaced ? "rewrote" : "wrote"} the \`${name}\` section on #${id} (${rel(file)})`);
|
|
22317
22432
|
return { id, specAgent: name, replaced, file: rel(file) };
|
|
22318
22433
|
}
|
|
@@ -22321,7 +22436,7 @@ function readSection(file, text5) {
|
|
|
22321
22436
|
let raw;
|
|
22322
22437
|
if (file !== void 0) {
|
|
22323
22438
|
try {
|
|
22324
|
-
raw =
|
|
22439
|
+
raw = fs66.readFileSync(file, "utf8");
|
|
22325
22440
|
} catch {
|
|
22326
22441
|
die(`can't read ${file} \u2014 write your section to a file, then pass its path`);
|
|
22327
22442
|
}
|
|
@@ -22515,7 +22630,7 @@ function localBoard() {
|
|
|
22515
22630
|
const no = checkWrite({ board: true }, env2, boardRevision());
|
|
22516
22631
|
if (no) return no;
|
|
22517
22632
|
const res = await resumeDelivery(deliveryId);
|
|
22518
|
-
if (!res.ok) return opRefused(new Error(res.error || "the delivery could not be carried on"));
|
|
22633
|
+
if (!res.ok) return { ...opRefused(new Error(res.error || "the delivery could not be carried on")), reason: res.reason, args: res.args };
|
|
22519
22634
|
return opOk(boardRevision(), { deliveryId: res.deliveryId, landed: res.landed, carryOn: res.carryOn });
|
|
22520
22635
|
},
|
|
22521
22636
|
async discardDelivery(deliveryId, env2) {
|
|
@@ -22787,11 +22902,11 @@ var init_shared = __esm({
|
|
|
22787
22902
|
});
|
|
22788
22903
|
|
|
22789
22904
|
// ../cli/src/commands/goal.ts
|
|
22790
|
-
import
|
|
22905
|
+
import fs67 from "node:fs";
|
|
22791
22906
|
function load() {
|
|
22792
22907
|
const file = goalsFile();
|
|
22793
22908
|
if (!file) die("Goals exist only inside a WildCrane goal discussion.");
|
|
22794
|
-
const held4 = JSON.parse(
|
|
22909
|
+
const held4 = JSON.parse(fs67.readFileSync(file, "utf8"));
|
|
22795
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.");
|
|
22796
22911
|
const goals = held4.goals ?? held4.ideas;
|
|
22797
22912
|
if (!Array.isArray(goals)) die("The goals array is missing or invalid.");
|
|
@@ -22816,7 +22931,7 @@ function splitBody2(text5) {
|
|
|
22816
22931
|
}
|
|
22817
22932
|
function readBody(bodyFile) {
|
|
22818
22933
|
if (!bodyFile) die("--body-file is required; write the goal in a writable temporary file first.");
|
|
22819
|
-
const text5 =
|
|
22934
|
+
const text5 = fs67.readFileSync(bodyFile, "utf8");
|
|
22820
22935
|
if (!text5.trim()) die("The goal must not be empty.");
|
|
22821
22936
|
return splitBody2(text5);
|
|
22822
22937
|
}
|
|
@@ -22828,7 +22943,7 @@ function named(held4, id) {
|
|
|
22828
22943
|
function cmdGoal(args, opts) {
|
|
22829
22944
|
const sub = args[0] ?? "";
|
|
22830
22945
|
const { file, held: held4 } = load();
|
|
22831
|
-
const write2 = () =>
|
|
22946
|
+
const write2 = () => fs67.writeFileSync(file, `${JSON.stringify({ workflows: held4.workflows, ideas: held4.goals }, null, 2)}
|
|
22832
22947
|
`);
|
|
22833
22948
|
if (sub === "new") {
|
|
22834
22949
|
const title = opts.title?.trim();
|
|
@@ -22898,8 +23013,8 @@ __export(board_exports, {
|
|
|
22898
23013
|
buildBoardProgram: () => buildBoardProgram
|
|
22899
23014
|
});
|
|
22900
23015
|
import crypto4 from "node:crypto";
|
|
22901
|
-
import
|
|
22902
|
-
import
|
|
23016
|
+
import fs68 from "node:fs";
|
|
23017
|
+
import path70 from "node:path";
|
|
22903
23018
|
async function dispatch(move2, cmd, args, opts, cli) {
|
|
22904
23019
|
const ctx = ctxOf(cmd, cli.program);
|
|
22905
23020
|
const data = await runAction(ctx, {}, async () => {
|
|
@@ -22938,7 +23053,7 @@ async function cloudCard(task, opts) {
|
|
|
22938
23053
|
let request;
|
|
22939
23054
|
if (typeof opts.bodyFile === "string") {
|
|
22940
23055
|
try {
|
|
22941
|
-
request =
|
|
23056
|
+
request = fs68.readFileSync(opts.bodyFile, "utf8");
|
|
22942
23057
|
} catch {
|
|
22943
23058
|
}
|
|
22944
23059
|
}
|
|
@@ -22968,7 +23083,7 @@ async function cloudCard(task, opts) {
|
|
|
22968
23083
|
const file = creationReceipt(`${checked.value}:${requestId}`, slot);
|
|
22969
23084
|
const journal = readCreation(file, said3);
|
|
22970
23085
|
if (!journal.made) {
|
|
22971
|
-
const key = `akb-${
|
|
23086
|
+
const key = `akb-${path70.basename(file, ".json").slice(0, 40)}`;
|
|
22972
23087
|
const made = await createProjectTask(checked.value, journal.said, key);
|
|
22973
23088
|
if (!made.ok) throw new BoardError(creationError(`Cloud did not make the task: ${made.error}`, triage), { kind: "refused" });
|
|
22974
23089
|
journal.made = made.value;
|
|
@@ -23113,16 +23228,16 @@ var init_board2 = __esm({
|
|
|
23113
23228
|
});
|
|
23114
23229
|
|
|
23115
23230
|
// ../cli/src/lib/board-cli.ts
|
|
23116
|
-
import
|
|
23117
|
-
import
|
|
23231
|
+
import fs69 from "node:fs";
|
|
23232
|
+
import path71 from "node:path";
|
|
23118
23233
|
function useBoard(found2, dirNamed) {
|
|
23119
|
-
if (!found2.named && found2.board ===
|
|
23234
|
+
if (!found2.named && found2.board === path71.join(found2.root, "docs", "kanban")) setBoardRoot(found2.root, dirNamed);
|
|
23120
23235
|
else setBoardDir(found2.board, found2.root);
|
|
23121
23236
|
useProjectState();
|
|
23122
23237
|
}
|
|
23123
23238
|
function whole(found2, move2) {
|
|
23124
23239
|
if (move2 === MAKES_A_BOARD) return found2;
|
|
23125
|
-
if (!
|
|
23240
|
+
if (!fs69.existsSync(path71.join(found2.board, "todo"))) {
|
|
23126
23241
|
die(`the board in ${found2.board} has no todo/ \u2014 run \`init\` to add what is missing.`, {
|
|
23127
23242
|
kind: "board-incomplete",
|
|
23128
23243
|
dir: found2.root
|
|
@@ -23131,13 +23246,13 @@ function whole(found2, move2) {
|
|
|
23131
23246
|
return found2;
|
|
23132
23247
|
}
|
|
23133
23248
|
function findBoardUpward(from, move2) {
|
|
23134
|
-
let dir =
|
|
23249
|
+
let dir = path71.resolve(from);
|
|
23135
23250
|
for (; ; ) {
|
|
23136
|
-
if (hasBoard(dir)) return { root: dir, board:
|
|
23251
|
+
if (hasBoard(dir)) return { root: dir, board: path71.join(dir, "docs", "kanban") };
|
|
23137
23252
|
if (isBoardDir(dir)) return { root: projectRootOf(dir), board: dir };
|
|
23138
|
-
const kept2 =
|
|
23139
|
-
if (move2 !== MAKES_A_BOARD &&
|
|
23140
|
-
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);
|
|
23141
23256
|
if (up === dir) return null;
|
|
23142
23257
|
dir = up;
|
|
23143
23258
|
}
|
|
@@ -23145,11 +23260,11 @@ function findBoardUpward(from, move2) {
|
|
|
23145
23260
|
function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
|
|
23146
23261
|
const named2 = board2 ?? boardFromEnv();
|
|
23147
23262
|
if (named2) {
|
|
23148
|
-
const folder =
|
|
23149
|
-
if (!
|
|
23263
|
+
const folder = path71.resolve(cwd, named2);
|
|
23264
|
+
if (!fs69.existsSync(folder) && move2 !== MAKES_A_BOARD) {
|
|
23150
23265
|
die(`no such folder: ${folder}`, { kind: "no-such-folder", dir: folder });
|
|
23151
23266
|
}
|
|
23152
|
-
if (
|
|
23267
|
+
if (fs69.existsSync(folder) && !isBoardDir(folder) && move2 !== MAKES_A_BOARD) {
|
|
23153
23268
|
die(`${folder} is not a board \u2014 it has no todo/ and config.md. ${installHint}\u3002`, {
|
|
23154
23269
|
kind: "no-board",
|
|
23155
23270
|
dir: folder
|
|
@@ -23158,24 +23273,24 @@ function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
|
|
|
23158
23273
|
return { root: projectRootOf(folder), board: folder, named: true };
|
|
23159
23274
|
}
|
|
23160
23275
|
if (dir !== null) {
|
|
23161
|
-
const root =
|
|
23162
|
-
if (!
|
|
23163
|
-
const kept2 =
|
|
23164
|
-
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);
|
|
23165
23280
|
if (!hasBoard(root) && move2 !== MAKES_A_BOARD) {
|
|
23166
23281
|
die(`no board in ${root} \u2014 it has no docs/kanban/. ${installHint}\u3002`, {
|
|
23167
23282
|
kind: "no-board",
|
|
23168
23283
|
dir: root
|
|
23169
23284
|
});
|
|
23170
23285
|
}
|
|
23171
|
-
return whole({ root, board:
|
|
23286
|
+
return whole({ root, board: path71.join(root, "docs", "kanban") }, move2);
|
|
23172
23287
|
}
|
|
23173
23288
|
const found2 = findBoardUpward(cwd, move2);
|
|
23174
23289
|
if (found2) return whole(found2, move2);
|
|
23175
|
-
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") };
|
|
23176
23291
|
die(
|
|
23177
|
-
`no board here. Looked in ${
|
|
23178
|
-
{ 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) }
|
|
23179
23294
|
);
|
|
23180
23295
|
}
|
|
23181
23296
|
function editDistance(a, b) {
|
|
@@ -23257,8 +23372,8 @@ var init_board_cli = __esm({
|
|
|
23257
23372
|
init_io();
|
|
23258
23373
|
init_pointer();
|
|
23259
23374
|
MAKES_A_BOARD = "init";
|
|
23260
|
-
hasBoard = (dir) =>
|
|
23261
|
-
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"));
|
|
23262
23377
|
boardFromEnv = () => {
|
|
23263
23378
|
const named2 = process.env.AI4KANBAN_BOARD?.trim();
|
|
23264
23379
|
return named2 ? named2 : null;
|
|
@@ -23525,8 +23640,8 @@ var init_guide = __esm({
|
|
|
23525
23640
|
});
|
|
23526
23641
|
|
|
23527
23642
|
// ../cli/src/lib/agent/approve.ts
|
|
23528
|
-
import
|
|
23529
|
-
import
|
|
23643
|
+
import fs75 from "node:fs";
|
|
23644
|
+
import path77 from "node:path";
|
|
23530
23645
|
function approvalRequest(req) {
|
|
23531
23646
|
const id = req.id;
|
|
23532
23647
|
const card = id === void 0 ? null : findCard(id);
|
|
@@ -23544,12 +23659,12 @@ function advancePhase(id) {
|
|
|
23544
23659
|
const card = findCard(id);
|
|
23545
23660
|
const found2 = locate(id);
|
|
23546
23661
|
if (!card || !found2 || !awaitingApproval(card)) return { error: `#${id} is no longer waiting on approval.` };
|
|
23547
|
-
const file = found2.kind === "group" ?
|
|
23548
|
-
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"));
|
|
23549
23664
|
if (!meta) return { error: `#${id} has no frontmatter` };
|
|
23550
23665
|
meta.phase = card.stage + 1;
|
|
23551
23666
|
meta.status = "todo";
|
|
23552
|
-
|
|
23667
|
+
fs75.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
|
|
23553
23668
|
return null;
|
|
23554
23669
|
}
|
|
23555
23670
|
var init_approve = __esm({
|
|
@@ -23647,8 +23762,8 @@ function open2(req, sessionId) {
|
|
|
23647
23762
|
markSpawned(run2.sessionId, pid);
|
|
23648
23763
|
return { run: run2, spawned: pid !== void 0 };
|
|
23649
23764
|
}
|
|
23650
|
-
async function startResume(id) {
|
|
23651
|
-
const opened = await openResume(id);
|
|
23765
|
+
async function startResume(id, requireSession = false) {
|
|
23766
|
+
const opened = await openResume(id, requireSession);
|
|
23652
23767
|
if ("error" in opened) return opened;
|
|
23653
23768
|
const pid = spawnWatcher(opened.run.sessionId);
|
|
23654
23769
|
markSpawned(opened.run.sessionId, pid);
|
|
@@ -23673,16 +23788,16 @@ var init_start = __esm({
|
|
|
23673
23788
|
});
|
|
23674
23789
|
|
|
23675
23790
|
// ../cli/src/lib/agent/task-chats.ts
|
|
23676
|
-
import
|
|
23677
|
-
import
|
|
23791
|
+
import fs76 from "node:fs";
|
|
23792
|
+
import path78 from "node:path";
|
|
23678
23793
|
function taskChatFile(task, home3) {
|
|
23679
23794
|
if (!TASK.test(task)) throw new Error(`${task} is not a task id.`);
|
|
23680
|
-
return
|
|
23795
|
+
return path78.join(taskChatsDir(home3), `${task}.jsonl`);
|
|
23681
23796
|
}
|
|
23682
23797
|
function readTaskChat(task, home3) {
|
|
23683
23798
|
let text5;
|
|
23684
23799
|
try {
|
|
23685
|
-
text5 =
|
|
23800
|
+
text5 = fs76.readFileSync(taskChatFile(task, home3), "utf8");
|
|
23686
23801
|
} catch {
|
|
23687
23802
|
return [];
|
|
23688
23803
|
}
|
|
@@ -23703,13 +23818,13 @@ var init_task_chats = __esm({
|
|
|
23703
23818
|
"use strict";
|
|
23704
23819
|
init_home();
|
|
23705
23820
|
TASK = /^[\w-]{1,100}$/;
|
|
23706
|
-
taskChatsDir = (home3 = machineHome()) =>
|
|
23821
|
+
taskChatsDir = (home3 = machineHome()) => path78.join(home3, "chats");
|
|
23707
23822
|
}
|
|
23708
23823
|
});
|
|
23709
23824
|
|
|
23710
23825
|
// ../cli/src/lib/agent/memory-review.ts
|
|
23711
|
-
import
|
|
23712
|
-
import
|
|
23826
|
+
import fs77 from "node:fs";
|
|
23827
|
+
import path79 from "node:path";
|
|
23713
23828
|
function taskChatsToReview(tasks) {
|
|
23714
23829
|
const out = [];
|
|
23715
23830
|
for (const ref of [...new Set(tasks)]) {
|
|
@@ -23731,8 +23846,8 @@ function readArchived2(id) {
|
|
|
23731
23846
|
const found2 = Number.isInteger(id) && id > 0 ? locateArchived(id) : null;
|
|
23732
23847
|
if (!found2) return null;
|
|
23733
23848
|
try {
|
|
23734
|
-
const file = found2.kind === "group" ?
|
|
23735
|
-
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"));
|
|
23736
23851
|
if (!meta) return null;
|
|
23737
23852
|
return { id, title: meta.title, modules: meta.modules ?? [], rejected: meta.rejected, body };
|
|
23738
23853
|
} catch {
|
|
@@ -23801,17 +23916,17 @@ var init_memory_review = __esm({
|
|
|
23801
23916
|
});
|
|
23802
23917
|
|
|
23803
23918
|
// ../cli/src/lib/agent/dismissal-review.ts
|
|
23804
|
-
import
|
|
23805
|
-
import
|
|
23919
|
+
import fs78 from "node:fs";
|
|
23920
|
+
import path80 from "node:path";
|
|
23806
23921
|
function rejectionsToReview() {
|
|
23807
|
-
const dir =
|
|
23808
|
-
if (!
|
|
23922
|
+
const dir = path80.join(ARCHIVE, REJECTIONS_TO_REVIEW);
|
|
23923
|
+
if (!fs78.existsSync(dir)) return [];
|
|
23809
23924
|
const found2 = [];
|
|
23810
23925
|
for (const file of walkMd(dir)) {
|
|
23811
23926
|
const id = archivedId(file);
|
|
23812
23927
|
if (id === null) continue;
|
|
23813
23928
|
try {
|
|
23814
|
-
const { meta } = parseFrontmatter(
|
|
23929
|
+
const { meta } = parseFrontmatter(fs78.readFileSync(file, "utf8"));
|
|
23815
23930
|
if (!meta?.rejected || meta.rejected_by !== "user" || !meta.rejected_reason.trim()) continue;
|
|
23816
23931
|
found2.push({ id, title: meta.title, file: rel(file), reason: meta.rejected_reason.trim(), rejectedAt: meta.rejected_at });
|
|
23817
23932
|
} catch {
|
|
@@ -23841,7 +23956,7 @@ function citedSources(text5) {
|
|
|
23841
23956
|
function withdrawnSources() {
|
|
23842
23957
|
const cited = [...new Set(plannerCopies("rejected.md").flatMap((file) => {
|
|
23843
23958
|
try {
|
|
23844
|
-
return citedSources(
|
|
23959
|
+
return citedSources(fs78.readFileSync(file, "utf8"));
|
|
23845
23960
|
} catch {
|
|
23846
23961
|
return [];
|
|
23847
23962
|
}
|
|
@@ -23866,8 +23981,8 @@ var init_dismissal_review = __esm({
|
|
|
23866
23981
|
});
|
|
23867
23982
|
|
|
23868
23983
|
// ../cli/src/lib/agent/flow.ts
|
|
23869
|
-
import
|
|
23870
|
-
import
|
|
23984
|
+
import fs79 from "node:fs";
|
|
23985
|
+
import path81 from "node:path";
|
|
23871
23986
|
function readCard2(id, home3 = "board") {
|
|
23872
23987
|
const found2 = home3 === "archive" ? locateArchived(id) : locate(id);
|
|
23873
23988
|
if (!found2) {
|
|
@@ -23876,10 +23991,10 @@ function readCard2(id, home3 = "board") {
|
|
|
23876
23991
|
{ kind: "card-not-found", id }
|
|
23877
23992
|
);
|
|
23878
23993
|
}
|
|
23879
|
-
const file = found2.kind === "group" ?
|
|
23994
|
+
const file = found2.kind === "group" ? path81.join(found2.target, "root.md") : found2.target;
|
|
23880
23995
|
let text5;
|
|
23881
23996
|
try {
|
|
23882
|
-
text5 =
|
|
23997
|
+
text5 = fs79.readFileSync(file, "utf8");
|
|
23883
23998
|
} catch {
|
|
23884
23999
|
die(`#${id} is on the board but ${rel(file)} can't be read.`, { kind: "card-unreadable", id });
|
|
23885
24000
|
}
|
|
@@ -24003,7 +24118,7 @@ function workspaceField(delivery) {
|
|
|
24003
24118
|
if (!delivery?.worktree) return [];
|
|
24004
24119
|
return field("workspace", [
|
|
24005
24120
|
`write code in ${delivery.worktree} \u2014 this delivery's own worktree, on branch ${delivery.branch}.`,
|
|
24006
|
-
|
|
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.`,
|
|
24007
24122
|
`every board command names the project's own copy: \`${boardCommandFor()} <command>\`.`,
|
|
24008
24123
|
`${rel(REPO_ROOT)} is the project \u2014 the card, the memory files and the docs are changed there, not here.`
|
|
24009
24124
|
]);
|
|
@@ -24747,7 +24862,7 @@ __export(watch_exports, {
|
|
|
24747
24862
|
watchRun: () => watchRun
|
|
24748
24863
|
});
|
|
24749
24864
|
import { spawn as spawn4 } from "node:child_process";
|
|
24750
|
-
import
|
|
24865
|
+
import fs80 from "node:fs";
|
|
24751
24866
|
async function watchRun(sessionId, resume = startResume) {
|
|
24752
24867
|
const spec = readSpec(sessionId);
|
|
24753
24868
|
const run2 = peekRun(sessionId);
|
|
@@ -24783,11 +24898,11 @@ async function watchRun(sessionId, resume = startResume) {
|
|
|
24783
24898
|
const record = claimed ?? run2;
|
|
24784
24899
|
const sub = record.action === "sub";
|
|
24785
24900
|
if (claim) await setCardStatus(claim.cardId, claim.status);
|
|
24786
|
-
|
|
24787
|
-
const logFile =
|
|
24901
|
+
fs80.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
24902
|
+
const logFile = fs80.createWriteStream(record.logPath, { flags: "a" });
|
|
24788
24903
|
logFile.on("error", () => {
|
|
24789
24904
|
});
|
|
24790
|
-
const eventsFile =
|
|
24905
|
+
const eventsFile = fs80.createWriteStream(eventsPathOf(sessionId), { flags: "a" });
|
|
24791
24906
|
eventsFile.on("error", () => {
|
|
24792
24907
|
});
|
|
24793
24908
|
const event2 = (e) => eventsFile.write(`${JSON.stringify(e)}
|
|
@@ -25376,7 +25491,7 @@ var init_watch = __esm({
|
|
|
25376
25491
|
});
|
|
25377
25492
|
|
|
25378
25493
|
// ../cli/src/kanban.ts
|
|
25379
|
-
import
|
|
25494
|
+
import fs86 from "node:fs";
|
|
25380
25495
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
25381
25496
|
|
|
25382
25497
|
// ../cli/src/lib/cli/agent.ts
|
|
@@ -25865,8 +25980,8 @@ async function testAgent(args) {
|
|
|
25865
25980
|
}
|
|
25866
25981
|
|
|
25867
25982
|
// ../cli/src/commands/cloud.ts
|
|
25868
|
-
import
|
|
25869
|
-
import
|
|
25983
|
+
import fs71 from "node:fs";
|
|
25984
|
+
import path73 from "node:path";
|
|
25870
25985
|
|
|
25871
25986
|
// ../cli/src/lib/cloud/avatar.ts
|
|
25872
25987
|
init_session();
|
|
@@ -25914,13 +26029,13 @@ init_config();
|
|
|
25914
26029
|
init_home();
|
|
25915
26030
|
init_config();
|
|
25916
26031
|
init_session();
|
|
25917
|
-
import
|
|
25918
|
-
import
|
|
26032
|
+
import fs70 from "node:fs";
|
|
26033
|
+
import path72 from "node:path";
|
|
25919
26034
|
var FRESH_MS = 10 * 6e4;
|
|
25920
26035
|
var GRACE_MS = 7 * 24 * 60 * 6e4;
|
|
25921
|
-
var heldFile = () =>
|
|
26036
|
+
var heldFile = () => path72.join(machineHome(), "pro.json");
|
|
25922
26037
|
function forgetPro() {
|
|
25923
|
-
|
|
26038
|
+
fs70.rmSync(heldFile(), { force: true });
|
|
25924
26039
|
}
|
|
25925
26040
|
|
|
25926
26041
|
// ../cli/src/lib/cloud/account.ts
|
|
@@ -26097,10 +26212,10 @@ async function cloudImage(opts, program2) {
|
|
|
26097
26212
|
const prompt = opts.prompt.trim();
|
|
26098
26213
|
if (!prompt) usage("Missing the prompt.", line);
|
|
26099
26214
|
const references = opts.refs.map((file) => {
|
|
26100
|
-
const type = REF_TYPES[
|
|
26215
|
+
const type = REF_TYPES[path73.extname(file).toLowerCase()];
|
|
26101
26216
|
if (!type) usage(`--ref ${file}: use a PNG, JPEG or WebP file.`, line);
|
|
26102
26217
|
try {
|
|
26103
|
-
return `data:${type};base64,${
|
|
26218
|
+
return `data:${type};base64,${fs71.readFileSync(file).toString("base64")}`;
|
|
26104
26219
|
} catch (e) {
|
|
26105
26220
|
usage(`--ref ${file}: ${e instanceof Error ? e.message : String(e)}`, line);
|
|
26106
26221
|
}
|
|
@@ -26206,31 +26321,31 @@ async function generate(g) {
|
|
|
26206
26321
|
refuse(g.failed, g.fallback);
|
|
26207
26322
|
}
|
|
26208
26323
|
const out = g.outFor?.(res, g.out) ?? g.out;
|
|
26209
|
-
|
|
26210
|
-
|
|
26324
|
+
fs71.mkdirSync(path73.dirname(path73.resolve(out)), { recursive: true });
|
|
26325
|
+
fs71.writeFileSync(out, Buffer.from(await res.arrayBuffer()));
|
|
26211
26326
|
say(`${out} (${g.label(res)})`);
|
|
26212
26327
|
return { out };
|
|
26213
26328
|
}
|
|
26214
26329
|
|
|
26215
26330
|
// ../cli/src/commands/cloud-import.ts
|
|
26216
26331
|
import crypto5 from "node:crypto";
|
|
26217
|
-
import
|
|
26218
|
-
import
|
|
26332
|
+
import fs73 from "node:fs";
|
|
26333
|
+
import path75 from "node:path";
|
|
26219
26334
|
|
|
26220
26335
|
// ../cloud/src/board-files.ts
|
|
26221
26336
|
var ROOTS = ["todo/", ".archive/", "memory/", "workflows/", "triage/", "assets/", "rules/"];
|
|
26222
26337
|
var SETTINGS = ["modules.md", "config.md"];
|
|
26223
|
-
var isSettingsPath = (
|
|
26338
|
+
var isSettingsPath = (path87) => SETTINGS.includes(path87) || /^rules\/(?:workflows\/)?[^/]+\.md$/.test(path87);
|
|
26224
26339
|
var BOARD_FILE_LIMIT = 256 * 1024;
|
|
26225
|
-
function isBoardPath(
|
|
26226
|
-
if (!
|
|
26227
|
-
if (
|
|
26228
|
-
if (
|
|
26229
|
-
if (
|
|
26230
|
-
if (SETTINGS.includes(
|
|
26231
|
-
if (
|
|
26232
|
-
if (
|
|
26233
|
-
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");
|
|
26234
26349
|
}
|
|
26235
26350
|
var workflowFolder = (id) => id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
26236
26351
|
|
|
@@ -26334,8 +26449,8 @@ init_bundled();
|
|
|
26334
26449
|
// ../cli/src/lib/agents/shared-memory.ts
|
|
26335
26450
|
init_bundled();
|
|
26336
26451
|
init_parse();
|
|
26337
|
-
import
|
|
26338
|
-
import
|
|
26452
|
+
import fs72 from "node:fs";
|
|
26453
|
+
import path74 from "node:path";
|
|
26339
26454
|
function sharedMemory(board2) {
|
|
26340
26455
|
const declared = /* @__PURE__ */ new Map();
|
|
26341
26456
|
const take = (folder, text5, builtIn) => {
|
|
@@ -26347,16 +26462,16 @@ function sharedMemory(board2) {
|
|
|
26347
26462
|
const at = /^([^/]+)\/AGENT\.md$/.exec(key);
|
|
26348
26463
|
if (at) take(at[1], text5, true);
|
|
26349
26464
|
}
|
|
26350
|
-
const root =
|
|
26465
|
+
const root = path74.join(board2, "agents");
|
|
26351
26466
|
let folders2 = [];
|
|
26352
26467
|
try {
|
|
26353
|
-
folders2 =
|
|
26468
|
+
folders2 = fs72.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
|
|
26354
26469
|
} catch {
|
|
26355
26470
|
}
|
|
26356
26471
|
for (const folder of folders2.sort()) {
|
|
26357
26472
|
let text5 = null;
|
|
26358
26473
|
try {
|
|
26359
|
-
text5 =
|
|
26474
|
+
text5 = fs72.readFileSync(path74.join(root, folder, "AGENT.md"), "utf8");
|
|
26360
26475
|
} catch {
|
|
26361
26476
|
}
|
|
26362
26477
|
take(folder, text5, false);
|
|
@@ -26445,24 +26560,24 @@ async function findProject(asked) {
|
|
|
26445
26560
|
}
|
|
26446
26561
|
throw new Refused(`No project of yours has the id or prefix "${asked}".`, "not_found");
|
|
26447
26562
|
}
|
|
26448
|
-
var read = (file) =>
|
|
26563
|
+
var read = (file) => fs73.readFileSync(file, "utf8");
|
|
26449
26564
|
var bytes2 = (text5) => Buffer.byteLength(text5, "utf8");
|
|
26450
26565
|
function files(dir) {
|
|
26451
|
-
if (!
|
|
26566
|
+
if (!fs73.existsSync(dir)) return [];
|
|
26452
26567
|
const out = [];
|
|
26453
|
-
for (const entry of
|
|
26568
|
+
for (const entry of fs73.readdirSync(dir, { withFileTypes: true })) {
|
|
26454
26569
|
if (entry.name.startsWith(".")) continue;
|
|
26455
|
-
const full =
|
|
26570
|
+
const full = path75.join(dir, entry.name);
|
|
26456
26571
|
if (entry.isDirectory()) out.push(...files(full));
|
|
26457
26572
|
else if (entry.isFile()) out.push(full);
|
|
26458
26573
|
}
|
|
26459
26574
|
return out;
|
|
26460
26575
|
}
|
|
26461
|
-
var posix2 = (p) => p.split(
|
|
26576
|
+
var posix2 = (p) => p.split(path75.sep).join("/");
|
|
26462
26577
|
function openCards() {
|
|
26463
26578
|
const out = [];
|
|
26464
26579
|
for (const file of walkMd(TODO)) {
|
|
26465
|
-
const rel2 = posix2(
|
|
26580
|
+
const rel2 = posix2(path75.relative(TODO, file));
|
|
26466
26581
|
if (rel2 === "README.md") continue;
|
|
26467
26582
|
const parts = rel2.split("/");
|
|
26468
26583
|
const text5 = read(file);
|
|
@@ -26496,14 +26611,14 @@ function bodyProblems(text5) {
|
|
|
26496
26611
|
return out;
|
|
26497
26612
|
}
|
|
26498
26613
|
function packWorkflow(flow) {
|
|
26499
|
-
const own =
|
|
26614
|
+
const own = path75.join(WORKFLOWS, flow.id);
|
|
26500
26615
|
const out = {};
|
|
26501
|
-
for (const file of files(own)) out[posix2(
|
|
26616
|
+
for (const file of files(own)) out[posix2(path75.relative(own, file))] = read(file);
|
|
26502
26617
|
const named2 = [...flow.previews, ...flow.land, ...flow.scheduled].map((h) => h.agent).filter((a) => !LAND_ROLES.includes(a));
|
|
26503
26618
|
for (const agent of new Set(named2)) {
|
|
26504
|
-
const dir =
|
|
26505
|
-
if (
|
|
26506
|
-
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);
|
|
26507
26622
|
continue;
|
|
26508
26623
|
}
|
|
26509
26624
|
for (const [key, text5] of Object.entries(BUNDLED_AGENT_FILES)) {
|
|
@@ -26555,8 +26670,8 @@ function draftsOf(text5) {
|
|
|
26555
26670
|
const at = /^\.(assets|mockups)\/(.+)$/.exec(name);
|
|
26556
26671
|
if (!at) continue;
|
|
26557
26672
|
const base = at[1] === "assets" ? ASSETS : MOCKUPS;
|
|
26558
|
-
const file =
|
|
26559
|
-
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);
|
|
26560
26675
|
}
|
|
26561
26676
|
return [...out].map(([name, file]) => ({ name, file }));
|
|
26562
26677
|
}
|
|
@@ -26590,9 +26705,9 @@ async function uploadDrafts(task, drafts) {
|
|
|
26590
26705
|
const assets = {};
|
|
26591
26706
|
for (const { name, file } of drafts) {
|
|
26592
26707
|
if (!card.body.includes(name)) continue;
|
|
26593
|
-
const ext =
|
|
26708
|
+
const ext = path75.extname(file).toLowerCase();
|
|
26594
26709
|
const type = TYPES[ext] ?? "application/octet-stream";
|
|
26595
|
-
const data =
|
|
26710
|
+
const data = fs73.readFileSync(file);
|
|
26596
26711
|
const artifact = await api("POST", `/v1/tasks/${task}/artifacts`, { type: kindOf(ext), name: name.slice(0, 200) });
|
|
26597
26712
|
const upload = await api("POST", `/v1/artifacts/${artifact.id}/uploads`, { content_type: type, size: Math.max(data.length, 1) });
|
|
26598
26713
|
await api("PUT", `/v1/uploads/${upload.id}`, void 0, { bytes: data, type });
|
|
@@ -26635,10 +26750,10 @@ usage: ${program2} cloud import --project <id or prefix> [--dry-run]`, { kind: "
|
|
|
26635
26750
|
async function run(asked, dryRun) {
|
|
26636
26751
|
const project = await findProject(asked);
|
|
26637
26752
|
const cloudFlows = await all(`/v1/workflows?workspace=${encodeURIComponent(project.workspace)}`);
|
|
26638
|
-
const board2 = crypto5.createHash("sha256").update(
|
|
26753
|
+
const board2 = crypto5.createHash("sha256").update(fs73.realpathSync(KANBAN)).digest("hex").slice(0, 16);
|
|
26639
26754
|
const cards = openCards();
|
|
26640
26755
|
const problems = [];
|
|
26641
|
-
const where = (file) => posix2(
|
|
26756
|
+
const where = (file) => posix2(path75.relative(KANBAN, file));
|
|
26642
26757
|
const own = workflows().filter((w) => !w.builtIn);
|
|
26643
26758
|
const packed = /* @__PURE__ */ new Map();
|
|
26644
26759
|
for (const flow of own) {
|
|
@@ -26665,7 +26780,7 @@ async function run(asked, dryRun) {
|
|
|
26665
26780
|
const root = `projects/${project.id}/`;
|
|
26666
26781
|
const boardFiles = [];
|
|
26667
26782
|
for (const file of [...files(MEMORY), ...files(RULES), CONFIG, MODULES_MD]) {
|
|
26668
|
-
if (!
|
|
26783
|
+
if (!fs73.existsSync(file)) continue;
|
|
26669
26784
|
const rel2 = where(file);
|
|
26670
26785
|
if (!isBoardPath(rel2)) continue;
|
|
26671
26786
|
const content = read(file);
|
|
@@ -26776,16 +26891,16 @@ async function run(asked, dryRun) {
|
|
|
26776
26891
|
}
|
|
26777
26892
|
function leftLocal(own) {
|
|
26778
26893
|
const out = [];
|
|
26779
|
-
const count4 = (dir) =>
|
|
26894
|
+
const count4 = (dir) => fs73.existsSync(dir) ? walkMd(dir).length : 0;
|
|
26780
26895
|
const archived = count4(ARCHIVE);
|
|
26781
26896
|
if (archived) out.push(`.archive/ (${archived})`);
|
|
26782
26897
|
const triage = count4(TRIAGE);
|
|
26783
26898
|
if (triage) out.push(`triage/ (${triage})`);
|
|
26784
|
-
if (
|
|
26785
|
-
if (
|
|
26899
|
+
if (fs73.existsSync(DELIVERIES)) out.push("deliveries/");
|
|
26900
|
+
if (fs73.existsSync(path75.join(KANBAN, "metrics.csv"))) out.push("metrics.csv");
|
|
26786
26901
|
const named2 = new Set(own.flatMap((w) => [...w.previews, ...w.land, ...w.scheduled].map((h) => h.agent)));
|
|
26787
|
-
if (
|
|
26788
|
-
for (const e of
|
|
26902
|
+
if (fs73.existsSync(AGENTS)) {
|
|
26903
|
+
for (const e of fs73.readdirSync(AGENTS, { withFileTypes: true })) {
|
|
26789
26904
|
if (e.isDirectory() && !e.name.startsWith(".") && !named2.has(e.name)) out.push(`agents/${e.name}/`);
|
|
26790
26905
|
}
|
|
26791
26906
|
}
|
|
@@ -26803,8 +26918,8 @@ init_session();
|
|
|
26803
26918
|
init_io();
|
|
26804
26919
|
init_paths();
|
|
26805
26920
|
import { createHash as createHash4 } from "node:crypto";
|
|
26806
|
-
import
|
|
26807
|
-
import
|
|
26921
|
+
import fs74 from "node:fs";
|
|
26922
|
+
import path76 from "node:path";
|
|
26808
26923
|
var SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
26809
26924
|
var CAPTURED = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
26810
26925
|
var MAX_BYTES3 = 50 * 1024 * 1024;
|
|
@@ -26903,10 +27018,10 @@ This template has no preview.`);
|
|
|
26903
27018
|
const written = [];
|
|
26904
27019
|
for (const p of previews) {
|
|
26905
27020
|
const bytes3 = Buffer.from(await (await ask(`/${at}/previews/${route(p.path)}`, {}, WITHOUT)).arrayBuffer());
|
|
26906
|
-
const file =
|
|
26907
|
-
const out =
|
|
26908
|
-
|
|
26909
|
-
|
|
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);
|
|
26910
27025
|
written.push(out);
|
|
26911
27026
|
say(out);
|
|
26912
27027
|
}
|
|
@@ -26919,15 +27034,15 @@ async function templateGet(name, opts, program2) {
|
|
|
26919
27034
|
const { template } = await asJson(await ask(`/${at}`, {}, WITHOUT));
|
|
26920
27035
|
const fetched = [];
|
|
26921
27036
|
for (const f of template.files) {
|
|
26922
|
-
const out =
|
|
26923
|
-
if (!out.startsWith(
|
|
27037
|
+
const out = path76.resolve(to, f.path);
|
|
27038
|
+
if (!out.startsWith(path76.resolve(to) + path76.sep)) refuse2(T.corrupt, WITHOUT);
|
|
26924
27039
|
const bytes3 = Buffer.from(await (await ask(`/${at}/files/${route(f.path)}`, {}, WITHOUT)).arrayBuffer());
|
|
26925
27040
|
if (bytes3.byteLength !== f.size || createHash4("sha256").update(bytes3).digest("hex") !== f.sha256) {
|
|
26926
27041
|
refuse2(T.corrupt, WITHOUT);
|
|
26927
27042
|
}
|
|
26928
27043
|
fetched.push({ out, bytes: bytes3 });
|
|
26929
27044
|
}
|
|
26930
|
-
const taken = fetched.filter((f) =>
|
|
27045
|
+
const taken = fetched.filter((f) => fs74.existsSync(f.out)).map((f) => path76.relative(to, f.out));
|
|
26931
27046
|
if (taken.length > 0) {
|
|
26932
27047
|
refuse2({
|
|
26933
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`,
|
|
@@ -26935,8 +27050,8 @@ async function templateGet(name, opts, program2) {
|
|
|
26935
27050
|
});
|
|
26936
27051
|
}
|
|
26937
27052
|
for (const f of fetched) {
|
|
26938
|
-
|
|
26939
|
-
|
|
27053
|
+
fs74.mkdirSync(path76.dirname(f.out), { recursive: true });
|
|
27054
|
+
fs74.writeFileSync(f.out, f.bytes);
|
|
26940
27055
|
}
|
|
26941
27056
|
say(`\u5DF2\u53D6\u56DE ${name}\uFF1A${fetched.length} \u4E2A\u6587\u4EF6\uFF0C\u4F4D\u4E8E ${to}
|
|
26942
27057
|
Fetched ${name}: ${fetched.length} files into ${to}`);
|
|
@@ -26954,25 +27069,25 @@ async function templateSource(name, opts) {
|
|
|
26954
27069
|
say(`evidence: ${e.path}`);
|
|
26955
27070
|
continue;
|
|
26956
27071
|
}
|
|
26957
|
-
const out =
|
|
26958
|
-
|
|
26959
|
-
|
|
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()));
|
|
26960
27075
|
written.push(out);
|
|
26961
27076
|
say(out);
|
|
26962
27077
|
}
|
|
26963
27078
|
return { name, source, evidence: written.length > 0 ? written : evidence.map((e) => e.path) };
|
|
26964
27079
|
}
|
|
26965
27080
|
async function templatePublish(dir, opts) {
|
|
26966
|
-
const root =
|
|
26967
|
-
const manifest =
|
|
26968
|
-
if (!
|
|
26969
|
-
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"));
|
|
26970
27085
|
const files2 = walk2(root);
|
|
26971
|
-
const total = files2.reduce((sum, f) => sum +
|
|
27086
|
+
const total = files2.reduce((sum, f) => sum + fs74.statSync(path76.join(root, f)).size, 0);
|
|
26972
27087
|
if (total > MAX_BYTES3) invalid("\u6A21\u677F\u6587\u4EF6\u5408\u8BA1\u8D85\u8FC7 50 MB\u3002", "the template\u2019s files exceed 50 MB.");
|
|
26973
27088
|
for (const extra of [...opts.preview, ...opts.evidence]) {
|
|
26974
|
-
if (!
|
|
26975
|
-
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)) {
|
|
26976
27091
|
invalid(
|
|
26977
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`,
|
|
26978
27093
|
`${extra} is inside the template folder; keep previews and evidence outside it.`
|
|
@@ -26984,10 +27099,10 @@ async function templatePublish(dir, opts) {
|
|
|
26984
27099
|
}
|
|
26985
27100
|
const form = new FormData();
|
|
26986
27101
|
form.set("meta", JSON.stringify({ ...meta, replace: !!opts.replace }));
|
|
26987
|
-
const add = (kind, name, file) => form.append(`${kind}:${name}`, new Blob([
|
|
26988
|
-
for (const f of files2) add("files", f,
|
|
26989
|
-
for (const f of opts.preview) add("previews",
|
|
26990
|
-
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);
|
|
26991
27106
|
const done2 = await asJson(await ask("", { method: "POST", body: form }));
|
|
26992
27107
|
say(
|
|
26993
27108
|
done2.replaced ? `\u5DF2\u66FF\u6362 ${done2.name}\uFF1A${files2.length} \u4E2A\u6587\u4EF6\u3002
|
|
@@ -27003,11 +27118,11 @@ Deleted ${name} from the catalog.`);
|
|
|
27003
27118
|
return { name, deleted: true };
|
|
27004
27119
|
}
|
|
27005
27120
|
async function templateShowcase(name, dir) {
|
|
27006
|
-
const file =
|
|
27007
|
-
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}.`);
|
|
27008
27123
|
let meta;
|
|
27009
27124
|
try {
|
|
27010
|
-
meta = JSON.parse(
|
|
27125
|
+
meta = JSON.parse(fs74.readFileSync(file, "utf8"));
|
|
27011
27126
|
} catch {
|
|
27012
27127
|
return invalid("showcase.json \u4E0D\u662F\u6709\u6548\u7684 JSON\u3002", "showcase.json is not valid JSON.");
|
|
27013
27128
|
}
|
|
@@ -27019,9 +27134,9 @@ async function templateShowcase(name, dir) {
|
|
|
27019
27134
|
const form = new FormData();
|
|
27020
27135
|
form.set("meta", JSON.stringify(meta));
|
|
27021
27136
|
for (const p of new Set(named2)) {
|
|
27022
|
-
const at =
|
|
27023
|
-
if (!
|
|
27024
|
-
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);
|
|
27025
27140
|
}
|
|
27026
27141
|
const done2 = await asJson(await ask(`/${encodeURIComponent(name)}/showcase`, { method: "PUT", body: form }));
|
|
27027
27142
|
say(`\u5DF2\u66F4\u65B0 ${name} \u7684\u5C55\u793A\u8D44\u6599\uFF1A${done2.files} \u4E2A\u6587\u4EF6\u3002
|
|
@@ -27030,7 +27145,7 @@ Updated ${name}'s showcase: ${done2.files} files.`);
|
|
|
27030
27145
|
}
|
|
27031
27146
|
function walk2(root, sub = "") {
|
|
27032
27147
|
const out = [];
|
|
27033
|
-
for (const entry of
|
|
27148
|
+
for (const entry of fs74.readdirSync(path76.join(root, sub), { withFileTypes: true })) {
|
|
27034
27149
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
27035
27150
|
const rel2 = sub ? `${sub}/${entry.name}` : entry.name;
|
|
27036
27151
|
if (entry.isDirectory()) out.push(...walk2(root, rel2));
|
|
@@ -27315,7 +27430,7 @@ init_opencode();
|
|
|
27315
27430
|
init_locate();
|
|
27316
27431
|
init_store();
|
|
27317
27432
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
27318
|
-
import
|
|
27433
|
+
import path82 from "node:path";
|
|
27319
27434
|
var AGENTS2 = {
|
|
27320
27435
|
claude: "claude-code",
|
|
27321
27436
|
codex: "codex",
|
|
@@ -27330,10 +27445,10 @@ var AGENTS2 = {
|
|
|
27330
27445
|
var RUNTIMES = /* @__PURE__ */ new Set(["node", "bun", "deno"]);
|
|
27331
27446
|
function agentOf2(args) {
|
|
27332
27447
|
const words = args.trim().split(/\s+/);
|
|
27333
|
-
const first =
|
|
27448
|
+
const first = path82.basename(words[0] ?? "");
|
|
27334
27449
|
const script = RUNTIMES.has(first) ? words[1] ?? "" : words[0] ?? "";
|
|
27335
27450
|
if (script.includes("/@anthropic-ai/claude-code/")) return "claude-code";
|
|
27336
|
-
return AGENTS2[
|
|
27451
|
+
return AGENTS2[path82.basename(script).replace(/\.(m?js|cjs|exe)$/, "")];
|
|
27337
27452
|
}
|
|
27338
27453
|
function processChain() {
|
|
27339
27454
|
if (process.platform === "win32") return [];
|
|
@@ -27397,8 +27512,8 @@ init_tasks();
|
|
|
27397
27512
|
init_read();
|
|
27398
27513
|
init_rules();
|
|
27399
27514
|
init_board();
|
|
27400
|
-
import
|
|
27401
|
-
import
|
|
27515
|
+
import fs81 from "node:fs";
|
|
27516
|
+
import path83 from "node:path";
|
|
27402
27517
|
var FOLLOW_MS = 400;
|
|
27403
27518
|
async function cmdStartRun(action, args, opts, program2 = "akb") {
|
|
27404
27519
|
const { req, follow: follow2, print } = readRequest(action, args, opts);
|
|
@@ -27588,13 +27703,13 @@ function readRequest(action, args, opts) {
|
|
|
27588
27703
|
return { req, follow: follow2, print };
|
|
27589
27704
|
}
|
|
27590
27705
|
async function cmdResume(id, opts) {
|
|
27591
|
-
const opened = await startResume(id ?? "last");
|
|
27706
|
+
const opened = await startResume(id ?? "last", opts.requireSession);
|
|
27592
27707
|
if ("error" in opened) die(opened.error, { kind: "run-refused" });
|
|
27593
27708
|
const { run: run2 } = opened;
|
|
27594
27709
|
if (!opened.spawned) die(`couldn't start a process for run ${run2.sessionId}`, { kind: "spawn-failed" });
|
|
27595
27710
|
const within = run2.deliveryId ? ` in delivery ${run2.deliveryId}` : "";
|
|
27596
27711
|
say(run2.resumedFrom ? `continuing ${short(run2.resumedFrom)} \u2014 run ${run2.sessionId}${within}` : `trying again \u2014 run ${run2.sessionId}${within}`);
|
|
27597
|
-
if (opts.follow === true) return await followStarted(run2.sessionId, "akb");
|
|
27712
|
+
if (opts.follow === true) return await followStarted(run2.sessionId, "akb", opts.events === true);
|
|
27598
27713
|
return { sessionId: run2.sessionId, resumedFrom: run2.resumedFrom };
|
|
27599
27714
|
}
|
|
27600
27715
|
async function cmdCancel(named2) {
|
|
@@ -27608,7 +27723,10 @@ async function cmdCancel(named2) {
|
|
|
27608
27723
|
}
|
|
27609
27724
|
async function cmdResumeDelivery(named2) {
|
|
27610
27725
|
const res = await resumeBoardDelivery(named2);
|
|
27611
|
-
if (!res.ok) die(res.error ?? "that delivery could not be carried on", {
|
|
27726
|
+
if (!res.ok) die(res.error ?? "that delivery could not be carried on", {
|
|
27727
|
+
kind: "run-refused",
|
|
27728
|
+
...res.kind === "refused" ? { reason: res.reason, args: res.args } : {}
|
|
27729
|
+
});
|
|
27612
27730
|
if (res.landed) {
|
|
27613
27731
|
say(`delivery ${res.deliveryId} is done \u2014 its changes were already on the target branch.`);
|
|
27614
27732
|
return { deliveryId: res.deliveryId, landed: true };
|
|
@@ -27685,7 +27803,7 @@ async function cmdSubStart(words, opts) {
|
|
|
27685
27803
|
let prompt = words.join(" ");
|
|
27686
27804
|
if (opts.file) {
|
|
27687
27805
|
try {
|
|
27688
|
-
prompt =
|
|
27806
|
+
prompt = fs81.readFileSync(path83.resolve(opts.file), "utf8");
|
|
27689
27807
|
} catch {
|
|
27690
27808
|
die(`--file: cannot read ${opts.file}`, { kind: "run-refused" });
|
|
27691
27809
|
}
|
|
@@ -27976,7 +28094,7 @@ async function cmdWatch(sessionId) {
|
|
|
27976
28094
|
// ../cli/src/commands/triage.ts
|
|
27977
28095
|
init_scheduled();
|
|
27978
28096
|
init_workflows();
|
|
27979
|
-
import
|
|
28097
|
+
import fs83 from "node:fs";
|
|
27980
28098
|
|
|
27981
28099
|
// ../cli/src/lib/signals/index.ts
|
|
27982
28100
|
init_cards();
|
|
@@ -27986,16 +28104,16 @@ init_yaml();
|
|
|
27986
28104
|
// ../cli/src/lib/signals/config.ts
|
|
27987
28105
|
init_settings2();
|
|
27988
28106
|
init_paths();
|
|
27989
|
-
import
|
|
27990
|
-
import
|
|
28107
|
+
import fs82 from "node:fs";
|
|
28108
|
+
import path84 from "node:path";
|
|
27991
28109
|
var TOKEN_KEY = "TRIAGE_ENDPOINT_TOKEN";
|
|
27992
28110
|
var ENDPOINT_SETTING = "Triage endpoint";
|
|
27993
28111
|
var LINE2 = /^- \*\*Triage endpoint\*\*\s*[—-]\s*(.+)$/m;
|
|
27994
|
-
var boardRel3 = (file) => rel(file).split(
|
|
28112
|
+
var boardRel3 = (file) => rel(file).split(path84.sep).join("/");
|
|
27995
28113
|
function signalEndpoint() {
|
|
27996
28114
|
let text5;
|
|
27997
28115
|
try {
|
|
27998
|
-
text5 =
|
|
28116
|
+
text5 = fs82.readFileSync(CONFIG, "utf8");
|
|
27999
28117
|
} catch {
|
|
28000
28118
|
return "";
|
|
28001
28119
|
}
|
|
@@ -28171,7 +28289,7 @@ async function cmdTriageAdd(opts) {
|
|
|
28171
28289
|
let body = opts.text;
|
|
28172
28290
|
if (opts.file !== void 0) {
|
|
28173
28291
|
try {
|
|
28174
|
-
body =
|
|
28292
|
+
body = fs83.readFileSync(opts.file, "utf8");
|
|
28175
28293
|
} catch {
|
|
28176
28294
|
die(`can't read ${opts.file} \u2014 write the item to a file, then pass its path`, { kind: "needs-input" });
|
|
28177
28295
|
}
|
|
@@ -28603,7 +28721,7 @@ function declareRunning(run2, cli) {
|
|
|
28603
28721
|
});
|
|
28604
28722
|
verb("resume").argument("[id]", RUN_ID, String).summary("continue a run that failed, was cut off or was stopped").description(
|
|
28605
28723
|
"The one command that reaches past the board: it picks the coding agent's own session back up \u2014 the conversation it was having, with everything it had already read \u2014 while the board counts the work as a fresh run. Before resuming a creation with no card, check sign-in, project access and connection. A refusal starts no run and leaves triage unchanged. Keep the triage source and request identity; reuse any task already created in Cloud. Repairing an existing card creates no other task. Example: akb run resume abc12345"
|
|
28606
|
-
).option("-f, --follow", "watch its log instead of returning").action(async function(...vals) {
|
|
28724
|
+
).option("-f, --follow", "watch its log instead of returning").option("--events", "with --follow: print its structured events, one JSON object per line").option("--require-session", "refuse if the original conversation cannot be resumed").action(async function(...vals) {
|
|
28607
28725
|
const [id] = positional(vals);
|
|
28608
28726
|
await onBoard2(this, cli, () => cmdResume(id, this.opts()));
|
|
28609
28727
|
});
|
|
@@ -28735,8 +28853,8 @@ function declareAgent(program2, cli) {
|
|
|
28735
28853
|
}
|
|
28736
28854
|
|
|
28737
28855
|
// ../cli/src/lib/cli/setup.ts
|
|
28738
|
-
import
|
|
28739
|
-
import
|
|
28856
|
+
import fs85 from "node:fs";
|
|
28857
|
+
import path86 from "node:path";
|
|
28740
28858
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
28741
28859
|
|
|
28742
28860
|
// ../cli/src/commands/update.ts
|
|
@@ -28750,8 +28868,8 @@ init_paths();
|
|
|
28750
28868
|
init_memory();
|
|
28751
28869
|
init_yaml2();
|
|
28752
28870
|
init_version();
|
|
28753
|
-
import
|
|
28754
|
-
import
|
|
28871
|
+
import fs84 from "node:fs";
|
|
28872
|
+
import path85 from "node:path";
|
|
28755
28873
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
28756
28874
|
var NAME = "wildcrane";
|
|
28757
28875
|
var REPO = "https://github.com/ai4kanban/ai4kanban";
|
|
@@ -28773,10 +28891,10 @@ var Report = class {
|
|
|
28773
28891
|
for (const line of this.notes) say(` ! ${line}`);
|
|
28774
28892
|
}
|
|
28775
28893
|
};
|
|
28776
|
-
var read3 = (file) =>
|
|
28894
|
+
var read3 = (file) => fs84.existsSync(file) ? fs84.readFileSync(file, "utf8") : null;
|
|
28777
28895
|
function statOf(p) {
|
|
28778
28896
|
try {
|
|
28779
|
-
return
|
|
28897
|
+
return fs84.lstatSync(p);
|
|
28780
28898
|
} catch {
|
|
28781
28899
|
return null;
|
|
28782
28900
|
}
|
|
@@ -28801,10 +28919,10 @@ async function cmdUpdate2(ctx) {
|
|
|
28801
28919
|
return { updated: ctx.dir, from, to: SKILL_VERSION };
|
|
28802
28920
|
}
|
|
28803
28921
|
async function repairBoard(root, report3, named2) {
|
|
28804
|
-
const legacy =
|
|
28805
|
-
const kept2 =
|
|
28806
|
-
const board2 = named2 ?? (
|
|
28807
|
-
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)) {
|
|
28808
28926
|
report3.notes.push("\u672A\u627E\u5230\u770B\u677F\uFF0C\u8BF7\u901A\u8FC7\u63D2\u4EF6\u8FDE\u63A5\u4E91\u7AEF\u9879\u76EE\u3002");
|
|
28809
28927
|
return false;
|
|
28810
28928
|
}
|
|
@@ -28835,20 +28953,20 @@ function moveRuntimes(root, board2, report3) {
|
|
|
28835
28953
|
}
|
|
28836
28954
|
}
|
|
28837
28955
|
function moveLegacyMemory(board2, report3) {
|
|
28838
|
-
const memory =
|
|
28956
|
+
const memory = path85.join(board2, "memory");
|
|
28839
28957
|
const moved = [];
|
|
28840
28958
|
for (const name of MEMORY_FILES) {
|
|
28841
|
-
const from =
|
|
28959
|
+
const from = path85.join(board2, name);
|
|
28842
28960
|
const st = statOf(from);
|
|
28843
28961
|
if (!st || !st.isFile()) continue;
|
|
28844
|
-
|
|
28845
|
-
const to =
|
|
28962
|
+
fs84.mkdirSync(memory, { recursive: true });
|
|
28963
|
+
const to = path85.join(memory, name);
|
|
28846
28964
|
const there = read3(to);
|
|
28847
28965
|
if (there === null) {
|
|
28848
|
-
|
|
28966
|
+
fs84.renameSync(from, to);
|
|
28849
28967
|
moved.push(name);
|
|
28850
28968
|
} else if (there === read3(from)) {
|
|
28851
|
-
|
|
28969
|
+
fs84.rmSync(from);
|
|
28852
28970
|
moved.push(name);
|
|
28853
28971
|
} else {
|
|
28854
28972
|
report3.notes.push(`both docs/kanban/${name} and docs/kanban/memory/${name} exist and differ \u2014 merge them by hand`);
|
|
@@ -28857,23 +28975,23 @@ function moveLegacyMemory(board2, report3) {
|
|
|
28857
28975
|
if (moved.length) report3.did.push(`moved the memory set into docs/kanban/memory/: ${moved.join(", ")}`);
|
|
28858
28976
|
}
|
|
28859
28977
|
function dropRecordFile(board2) {
|
|
28860
|
-
const file =
|
|
28861
|
-
if (!
|
|
28862
|
-
|
|
28978
|
+
const file = path85.join(board2, "record.csv");
|
|
28979
|
+
if (!fs84.existsSync(file)) return null;
|
|
28980
|
+
fs84.rmSync(file);
|
|
28863
28981
|
return "removed docs/kanban/record.csv (the planning scores it fed are gone)";
|
|
28864
28982
|
}
|
|
28865
28983
|
function dropModuleGoals(board2, report3) {
|
|
28866
|
-
const memory =
|
|
28867
|
-
if (!
|
|
28868
|
-
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"));
|
|
28869
28987
|
let dropped = 0;
|
|
28870
|
-
for (const entry of
|
|
28988
|
+
for (const entry of fs84.readdirSync(memory, { withFileTypes: true })) {
|
|
28871
28989
|
if (!entry.isDirectory()) continue;
|
|
28872
|
-
const file =
|
|
28990
|
+
const file = path85.join(memory, entry.name, "goal.md");
|
|
28873
28991
|
const text5 = read3(file);
|
|
28874
28992
|
if (text5 === null) continue;
|
|
28875
28993
|
if (text5 === root || text5.includes(UNFILLED) || !goalWords(text5)) {
|
|
28876
|
-
|
|
28994
|
+
fs84.rmSync(file);
|
|
28877
28995
|
dropped++;
|
|
28878
28996
|
} else {
|
|
28879
28997
|
report3.notes.push(
|
|
@@ -28895,10 +29013,10 @@ var PLAN_LEADS = {
|
|
|
28895
29013
|
var GONE_LEADS = ["software-planner", "planner"];
|
|
28896
29014
|
function planLeads(board2) {
|
|
28897
29015
|
const leads = { ...PLAN_LEADS };
|
|
28898
|
-
const root =
|
|
28899
|
-
for (const entry of
|
|
29016
|
+
const root = path85.join(board2, "workflows");
|
|
29017
|
+
for (const entry of fs84.existsSync(root) ? fs84.readdirSync(root, { withFileTypes: true }) : []) {
|
|
28900
29018
|
if (!entry.isDirectory()) continue;
|
|
28901
|
-
const meta = splitFrontmatter(read3(
|
|
29019
|
+
const meta = splitFrontmatter(read3(path85.join(root, entry.name, "WORKFLOW.md")) ?? "").meta;
|
|
28902
29020
|
const stages = meta === null ? void 0 : parseYamlBlock(meta).stages;
|
|
28903
29021
|
const plan = stages && typeof stages === "object" && !Array.isArray(stages) ? stages.plan : void 0;
|
|
28904
29022
|
const lead = plan && typeof plan === "object" && !Array.isArray(plan) && typeof plan.lead === "string" ? plan.lead.trim() : "";
|
|
@@ -28907,30 +29025,30 @@ function planLeads(board2) {
|
|
|
28907
29025
|
return leads;
|
|
28908
29026
|
}
|
|
28909
29027
|
function movePlanRules(board2, report3) {
|
|
28910
|
-
const rules =
|
|
29028
|
+
const rules = path85.join(board2, "rules");
|
|
28911
29029
|
const moved = [];
|
|
28912
29030
|
for (const [id, leads] of Object.entries(planLeads(board2))) {
|
|
28913
29031
|
for (const lead of leads) {
|
|
28914
|
-
const from =
|
|
29032
|
+
const from = path85.join(rules, `${lead}.md`);
|
|
28915
29033
|
const rule = read3(from)?.trim();
|
|
28916
29034
|
if (!rule) continue;
|
|
28917
|
-
const to =
|
|
29035
|
+
const to = path85.join(rules, "workflows", `${id}.md`);
|
|
28918
29036
|
const there = read3(to)?.trim() ?? "";
|
|
28919
29037
|
if (!there.includes(rule)) {
|
|
28920
|
-
|
|
28921
|
-
|
|
29038
|
+
fs84.mkdirSync(path85.dirname(to), { recursive: true });
|
|
29039
|
+
fs84.writeFileSync(to, `${[there, rule].filter(Boolean).join("\n\n")}
|
|
28922
29040
|
`);
|
|
28923
29041
|
moved.push(`${lead} \u2192 ${id}`);
|
|
28924
29042
|
}
|
|
28925
29043
|
}
|
|
28926
29044
|
}
|
|
28927
|
-
for (const lead of GONE_LEADS)
|
|
29045
|
+
for (const lead of GONE_LEADS) fs84.rmSync(path85.join(rules, `${lead}.md`), { force: true });
|
|
28928
29046
|
if (moved.length) report3.did.push(`made each plan agent's rule its workflow's card rule (docs/kanban/rules/workflows/): ${moved.join(", ")}`);
|
|
28929
29047
|
}
|
|
28930
29048
|
function noteOldWorkflows(board2, report3) {
|
|
28931
29049
|
const old = (dir, file, keys) => {
|
|
28932
|
-
const root =
|
|
28933
|
-
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}`);
|
|
28934
29052
|
};
|
|
28935
29053
|
const files2 = [...old("workflows", "WORKFLOW.md", /^(stages|delivers):/m), ...old("agents", "AGENT.md", /^\s+(hook|lead):/m)];
|
|
28936
29054
|
if (files2.length) {
|
|
@@ -28940,7 +29058,7 @@ function noteOldWorkflows(board2, report3) {
|
|
|
28940
29058
|
}
|
|
28941
29059
|
}
|
|
28942
29060
|
function checkConfig(board2, report3) {
|
|
28943
|
-
const current = read3(
|
|
29061
|
+
const current = read3(path85.join(board2, "config.md"));
|
|
28944
29062
|
if (!current) return;
|
|
28945
29063
|
const added = missingConfigKeys(current);
|
|
28946
29064
|
if (added.length) {
|
|
@@ -28950,7 +29068,7 @@ function checkConfig(board2, report3) {
|
|
|
28950
29068
|
}
|
|
28951
29069
|
}
|
|
28952
29070
|
function checkModules(board2, report3) {
|
|
28953
|
-
const map2 = read3(
|
|
29071
|
+
const map2 = read3(path85.join(board2, "modules.md"));
|
|
28954
29072
|
if (map2 && map2.includes(UNFILLED)) {
|
|
28955
29073
|
report3.notes.push(
|
|
28956
29074
|
"docs/kanban/modules.md is still blank \u2014 write it from the repo (`akb guide module-map`)"
|
|
@@ -29004,14 +29122,14 @@ function declareSetup(program2, cli) {
|
|
|
29004
29122
|
program2.version(SKILL_VERSION, "-v, --version", "print this version").addHelpText("beforeAll", spelled(cli.program));
|
|
29005
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");
|
|
29006
29124
|
const where = (cmd) => {
|
|
29007
|
-
const dir =
|
|
29008
|
-
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 });
|
|
29009
29127
|
return dir;
|
|
29010
29128
|
};
|
|
29011
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() {
|
|
29012
29130
|
const dir = where(this);
|
|
29013
29131
|
const named2 = this.optsWithGlobals().board ?? process.env.AI4KANBAN_BOARD?.trim();
|
|
29014
|
-
const board2 = named2 ?
|
|
29132
|
+
const board2 = named2 ? path86.resolve(cli.cwd, named2) : null;
|
|
29015
29133
|
await runAction(ctxOf(this, cli.program), {}, () => cmdUpdate2({ dir, program: cli.program, board: board2 }));
|
|
29016
29134
|
});
|
|
29017
29135
|
program2.command("__rules", { hidden: true }).action(function() {
|
|
@@ -29055,7 +29173,7 @@ function invokedDirectly() {
|
|
|
29055
29173
|
const entry = process.argv[1];
|
|
29056
29174
|
if (!entry) return false;
|
|
29057
29175
|
try {
|
|
29058
|
-
return
|
|
29176
|
+
return fs86.realpathSync(entry) === fs86.realpathSync(SELF2);
|
|
29059
29177
|
} catch {
|
|
29060
29178
|
return false;
|
|
29061
29179
|
}
|