wildcrane-runner 0.6.4 → 0.6.5
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 +371 -616
- package/dist/akb/package.json +1 -1
- package/dist/launcher.mjs +2 -2
- package/dist/runner.mjs +336 -346
- 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.4 — 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 path86 = __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 = path86.resolve(baseDir, baseName);
|
|
2171
|
+
if (fs87.existsSync(localBin)) return localBin;
|
|
2172
|
+
if (sourceExt.includes(path86.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 = path86.resolve(
|
|
2191
|
+
path86.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 = path86.basename(
|
|
2199
2199
|
this._scriptPath,
|
|
2200
|
-
|
|
2200
|
+
path86.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(path86.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 = path86.basename(filename, path86.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(path87) {
|
|
3073
|
+
if (path87 === void 0) return this._executableDir;
|
|
3074
|
+
this._executableDir = path87;
|
|
3075
3075
|
return this;
|
|
3076
3076
|
}
|
|
3077
3077
|
/**
|
|
@@ -3650,7 +3650,7 @@ function useProjectState() {
|
|
|
3650
3650
|
ensureAkbDir();
|
|
3651
3651
|
return ensureProjectState(KANBAN, REPO_ROOT);
|
|
3652
3652
|
}
|
|
3653
|
-
var REPO_ROOT, KANBAN, TODO, ARCHIVE, ARCHIVE_MD, README, MODULES_MD, CONFIG, KANBAN_GITIGNORE, RELEASES, RELEASE_SUMMARIES, AGENTS, WORKFLOWS, LEGACY_AGENTS, ASSETS, BOARD_STATE, MOCKUPS, MEMORY, AGENT_MEMORY, PROJECT_MD, LOCK, UI_CONFIG, LEGACY_UI_CONFIG, LOCAL_CONFIG, ENV_FILE, SESSIONS, SESSIONS_DIR, SESSIONS_LOCK2, INDEX_LOCK2, DELIVERIES, PLANS, PLANS_ARCHIVE, RULES, TRIAGE, SIGNALS_ARCHIVED, SIGNALS_DISMISSED, SIGNALS_FILES, SIGNALS_OLD_INBOX, SIGNALS_OLD_HANDLED, AKB_DIR, ROOT_GITIGNORE, AKB_IGNORE_LINE, BOARD_FLAG, rel, BOARD_PATH_IN_TEXT, ASSETS_PATH_IN_TEXT, MOCKUPS_PATH_IN_TEXT, BOARD_STATE_IN_TEXT;
|
|
3653
|
+
var REPO_ROOT, KANBAN, TODO, ARCHIVE, REJECTIONS_TO_REVIEW, ARCHIVE_MD, README, MODULES_MD, CONFIG, KANBAN_GITIGNORE, RELEASES, RELEASE_SUMMARIES, AGENTS, WORKFLOWS, LEGACY_AGENTS, ASSETS, BOARD_STATE, MOCKUPS, MEMORY, AGENT_MEMORY, PROJECT_MD, LOCK, UI_CONFIG, LEGACY_UI_CONFIG, LOCAL_CONFIG, ENV_FILE, SESSIONS, SESSIONS_DIR, SESSIONS_LOCK2, INDEX_LOCK2, DELIVERIES, PLANS, PLANS_ARCHIVE, RULES, TRIAGE, SIGNALS_ARCHIVED, SIGNALS_DISMISSED, SIGNALS_FILES, SIGNALS_OLD_INBOX, SIGNALS_OLD_HANDLED, AKB_DIR, ROOT_GITIGNORE, AKB_IGNORE_LINE, BOARD_FLAG, rel, BOARD_PATH_IN_TEXT, ASSETS_PATH_IN_TEXT, MOCKUPS_PATH_IN_TEXT, BOARD_STATE_IN_TEXT;
|
|
3654
3654
|
var init_paths = __esm({
|
|
3655
3655
|
"../cli/src/lib/paths.ts"() {
|
|
3656
3656
|
"use strict";
|
|
@@ -3660,6 +3660,7 @@ var init_paths = __esm({
|
|
|
3660
3660
|
KANBAN = "";
|
|
3661
3661
|
TODO = "";
|
|
3662
3662
|
ARCHIVE = "";
|
|
3663
|
+
REJECTIONS_TO_REVIEW = ".sync";
|
|
3663
3664
|
ARCHIVE_MD = "";
|
|
3664
3665
|
README = "";
|
|
3665
3666
|
MODULES_MD = "";
|
|
@@ -4018,11 +4019,12 @@ function insideRun() {
|
|
|
4018
4019
|
function runEnv(env2, value) {
|
|
4019
4020
|
return { ...env2, [RUN_ENV]: value };
|
|
4020
4021
|
}
|
|
4021
|
-
var RUN_ENV, STOP_ENV, SESSION_VARS;
|
|
4022
|
+
var RUN_ENV, SCHEDULED_AGENTS_ENV, STOP_ENV, SESSION_VARS;
|
|
4022
4023
|
var init_env = __esm({
|
|
4023
4024
|
"../cli/src/lib/agent/env.ts"() {
|
|
4024
4025
|
"use strict";
|
|
4025
4026
|
RUN_ENV = "KANBAN_RUN";
|
|
4027
|
+
SCHEDULED_AGENTS_ENV = "AI4KANBAN_SCHEDULED_AGENTS";
|
|
4026
4028
|
STOP_ENV = "KANBAN_STOP_ID";
|
|
4027
4029
|
SESSION_VARS = {
|
|
4028
4030
|
"claude-code": "CLAUDE_CODE_SESSION_ID",
|
|
@@ -4974,8 +4976,8 @@ var init_package = __esm({
|
|
|
4974
4976
|
"../cli/package.json"() {
|
|
4975
4977
|
package_default = {
|
|
4976
4978
|
name: "wildcrane",
|
|
4977
|
-
version: "0.11.
|
|
4978
|
-
akbContract:
|
|
4979
|
+
version: "0.11.4",
|
|
4980
|
+
akbContract: 2,
|
|
4979
4981
|
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.",
|
|
4980
4982
|
keywords: [
|
|
4981
4983
|
"kanban",
|
|
@@ -7867,10 +7869,10 @@ function findRollout(threadId) {
|
|
|
7867
7869
|
}
|
|
7868
7870
|
for (const name of names4) {
|
|
7869
7871
|
if (!name.startsWith("rollout-") || !name.endsWith(tail2)) continue;
|
|
7870
|
-
const
|
|
7872
|
+
const path86 = join(dir, name);
|
|
7871
7873
|
try {
|
|
7872
|
-
const at = statSync(
|
|
7873
|
-
if (!newest2 || at > newest2.at) newest2 = { path:
|
|
7874
|
+
const at = statSync(path86).mtimeMs;
|
|
7875
|
+
if (!newest2 || at > newest2.at) newest2 = { path: path86, at };
|
|
7874
7876
|
} catch {
|
|
7875
7877
|
}
|
|
7876
7878
|
}
|
|
@@ -7878,11 +7880,11 @@ function findRollout(threadId) {
|
|
|
7878
7880
|
return newest2?.path;
|
|
7879
7881
|
}
|
|
7880
7882
|
function readCodexRunFacts(threadId) {
|
|
7881
|
-
const
|
|
7882
|
-
if (!
|
|
7883
|
+
const path86 = findRollout(threadId);
|
|
7884
|
+
if (!path86) return void 0;
|
|
7883
7885
|
let raw;
|
|
7884
7886
|
try {
|
|
7885
|
-
raw = readFileSync(
|
|
7887
|
+
raw = readFileSync(path86, "utf8");
|
|
7886
7888
|
} catch {
|
|
7887
7889
|
return void 0;
|
|
7888
7890
|
}
|
|
@@ -8010,8 +8012,8 @@ function renderCompleted(item) {
|
|
|
8010
8012
|
const changes = Array.isArray(item.changes) ? item.changes : [];
|
|
8011
8013
|
return changes.map((raw) => {
|
|
8012
8014
|
const change2 = obj(raw);
|
|
8013
|
-
const
|
|
8014
|
-
return
|
|
8015
|
+
const path86 = str2(change2.path);
|
|
8016
|
+
return path86 ? `\u23FA ${str2(change2.kind) || "change"}(${path86})
|
|
8015
8017
|
` : "";
|
|
8016
8018
|
}).join("");
|
|
8017
8019
|
}
|
|
@@ -8270,10 +8272,10 @@ function readKimiRunFacts(sessionId) {
|
|
|
8270
8272
|
const total = { input: 0, cacheCreation: 0, cacheRead: 0, output: 0 };
|
|
8271
8273
|
let model;
|
|
8272
8274
|
let holding = 0;
|
|
8273
|
-
for (const { path:
|
|
8275
|
+
for (const { path: path86, main } of wireFiles(entry.sessionDir)) {
|
|
8274
8276
|
let raw;
|
|
8275
8277
|
try {
|
|
8276
|
-
raw = readFileSync2(
|
|
8278
|
+
raw = readFileSync2(path86, "utf8");
|
|
8277
8279
|
} catch {
|
|
8278
8280
|
continue;
|
|
8279
8281
|
}
|
|
@@ -8554,8 +8556,8 @@ var init_tail = __esm({
|
|
|
8554
8556
|
import fs22 from "node:fs";
|
|
8555
8557
|
function whereHint(update2, title) {
|
|
8556
8558
|
const locations = Array.isArray(update2.locations) ? update2.locations : [];
|
|
8557
|
-
const
|
|
8558
|
-
return
|
|
8559
|
+
const path86 = str2(obj(locations[0]).path);
|
|
8560
|
+
return path86 && !title.includes(path86) ? hint(path86) : "";
|
|
8559
8561
|
}
|
|
8560
8562
|
function saidBy(update2) {
|
|
8561
8563
|
const content = Array.isArray(update2.content) ? update2.content : [];
|
|
@@ -11781,7 +11783,7 @@ var init_flows = __esm({
|
|
|
11781
11783
|
argument: "<id> [note...]",
|
|
11782
11784
|
argumentNote: NOTE,
|
|
11783
11785
|
gloss: "build the card",
|
|
11784
|
-
options: [RUNTIME_OPTION]
|
|
11786
|
+
options: [RUNTIME_OPTION, ...CARRY_OPTIONS]
|
|
11785
11787
|
},
|
|
11786
11788
|
{
|
|
11787
11789
|
command: "conflict",
|
|
@@ -11860,9 +11862,8 @@ var init_flows = __esm({
|
|
|
11860
11862
|
action: "review-dismissals",
|
|
11861
11863
|
argument: "",
|
|
11862
11864
|
gloss: "learn what the user does not want from their rejection and dismissal reasons",
|
|
11863
|
-
options: [{ flags: "--all", description: "every reason on the board, not only those since the last review \u2014 a Runner's board" }],
|
|
11864
11865
|
more: [
|
|
11865
|
-
"Reads the reasons
|
|
11866
|
+
"Reads the reasons Cloud has not had learned, and withdraws what rested on an item since restored."
|
|
11866
11867
|
]
|
|
11867
11868
|
},
|
|
11868
11869
|
// The project writer's one flow (#1268). Typed bare: it acts on the project.
|
|
@@ -11957,21 +11958,10 @@ function parseStamp(raw) {
|
|
|
11957
11958
|
const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]), Number(m[4]), Number(m[5]), 0, 0);
|
|
11958
11959
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
11959
11960
|
}
|
|
11960
|
-
|
|
11961
|
-
const c = typeof cadence === "string" ? parseCadence(cadence) : cadence;
|
|
11962
|
-
if (!c) return null;
|
|
11963
|
-
const last = parseStamp(lastRun);
|
|
11964
|
-
if (!last) return /* @__PURE__ */ new Date(0);
|
|
11965
|
-
if (c.unit === "m") return new Date(last.getTime() + c.n * 6e4);
|
|
11966
|
-
if (c.unit === "h") return new Date(last.getTime() + c.n * 36e5);
|
|
11967
|
-
const [hour, minute] = c.at ? c.at.split(":").map(Number) : [last.getHours(), last.getMinutes()];
|
|
11968
|
-
return new Date(last.getFullYear(), last.getMonth(), last.getDate() + c.n, hour, minute, 0, 0);
|
|
11969
|
-
}
|
|
11970
|
-
var CADENCE_FORMS, AUTO_CADENCE, isAuto, CADENCE_RE, pad;
|
|
11961
|
+
var AUTO_CADENCE, isAuto, CADENCE_RE, pad;
|
|
11971
11962
|
var init_cadence = __esm({
|
|
11972
11963
|
"../cli/src/lib/cadence.ts"() {
|
|
11973
11964
|
"use strict";
|
|
11974
|
-
CADENCE_FORMS = "<N>m (minutes), <N>h (hours), <N>d (days), or <N>d at HH:MM (whole days, at that time of day) \u2014 e.g. 30m, 6h, 1d at 09:30";
|
|
11975
11965
|
AUTO_CADENCE = "auto";
|
|
11976
11966
|
isAuto = (cadence) => cadence.trim().toLowerCase() === AUTO_CADENCE;
|
|
11977
11967
|
CADENCE_RE = /^(\d+)\s*([mhd])(?:\s+at\s+(\d{1,2}):(\d{2}))?$/i;
|
|
@@ -11980,7 +11970,7 @@ var init_cadence = __esm({
|
|
|
11980
11970
|
});
|
|
11981
11971
|
|
|
11982
11972
|
// ../cli/src/lib/agent/types.ts
|
|
11983
|
-
var SPECIALIST_ACTIONS, CARD_FREE_ACTIONS, holdsCard, REFINE_ACTIONS, RETIRED, isRetired, refusal, RETIRED_WORKFLOWS, workflowDeleted
|
|
11973
|
+
var SPECIALIST_ACTIONS, CARD_FREE_ACTIONS, holdsCard, REFINE_ACTIONS, RETIRED, isRetired, refusal, RETIRED_WORKFLOWS, workflowDeleted;
|
|
11984
11974
|
var init_types3 = __esm({
|
|
11985
11975
|
"../cli/src/lib/agent/types.ts"() {
|
|
11986
11976
|
"use strict";
|
|
@@ -11996,7 +11986,6 @@ var init_types3 = __esm({
|
|
|
11996
11986
|
refusal = (reason, error, args) => args ? { error, reason, args } : { error, reason };
|
|
11997
11987
|
RETIRED_WORKFLOWS = ["content"];
|
|
11998
11988
|
workflowDeleted = (id, known2) => !!id && !known2.includes(id) && !RETIRED_WORKFLOWS.includes(id);
|
|
11999
|
-
SCHEDULED_CADENCE = "1d";
|
|
12000
11989
|
}
|
|
12001
11990
|
});
|
|
12002
11991
|
|
|
@@ -12217,16 +12206,6 @@ function setSecret(name, value) {
|
|
|
12217
12206
|
return { ok: false, ...refusal("fileWrite", `couldn't write ${ENV_FILE}: ${why}`, { path: ENV_FILE, details: why }) };
|
|
12218
12207
|
}
|
|
12219
12208
|
}
|
|
12220
|
-
function readSchedule(key) {
|
|
12221
|
-
const block = configBlock(safeConfig()[key]);
|
|
12222
|
-
const saved = stringIn(block, "cadence");
|
|
12223
|
-
return {
|
|
12224
|
-
enabled: block.off !== true,
|
|
12225
|
-
cadence: saved && parseCadence(saved) ? saved : AUTO_CADENCE,
|
|
12226
|
-
lastRun: stringIn(block, "lastRun"),
|
|
12227
|
-
...stringIn(block, "since") ? { since: stringIn(block, "since") } : {}
|
|
12228
|
-
};
|
|
12229
|
-
}
|
|
12230
12209
|
function stampSchedule(key, when) {
|
|
12231
12210
|
return writeConfig((cfg) => {
|
|
12232
12211
|
cfg[key] = { ...configBlock(cfg[key]), lastRun: formatStamp(when) };
|
|
@@ -12259,7 +12238,7 @@ function dropReflected(ids) {
|
|
|
12259
12238
|
cfg.reflectQueue = queueIn(cfg).filter((id) => !ids.includes(id));
|
|
12260
12239
|
});
|
|
12261
12240
|
}
|
|
12262
|
-
var SILENCE_MINUTES, BACKGROUND_WAIT, BOARD_SPEC_KEYS, IGNORE_BLOCK, stringIn, stampMemoryPrune, queueIn, reflectQueue,
|
|
12241
|
+
var SILENCE_MINUTES, BACKGROUND_WAIT, BOARD_SPEC_KEYS, IGNORE_BLOCK, stringIn, stampMemoryPrune, queueIn, reflectQueue, stampProjectDescription;
|
|
12263
12242
|
var init_settings2 = __esm({
|
|
12264
12243
|
"../cli/src/lib/agent/settings.ts"() {
|
|
12265
12244
|
"use strict";
|
|
@@ -12276,8 +12255,6 @@ var init_settings2 = __esm({
|
|
|
12276
12255
|
stampMemoryPrune = (when = /* @__PURE__ */ new Date()) => void stampSchedule("memoryPrune", when);
|
|
12277
12256
|
queueIn = (cfg) => Array.isArray(cfg.reflectQueue) ? cfg.reflectQueue.filter((id) => Number.isInteger(id) && id > 0) : [];
|
|
12278
12257
|
reflectQueue = () => queueIn(safeConfig());
|
|
12279
|
-
dismissalReview = () => readSchedule("dismissalReview");
|
|
12280
|
-
stampDismissalReview = (when) => void stampSchedule("dismissalReview", when);
|
|
12281
12258
|
stampProjectDescription = (when) => void stampSchedule("projectDescription", when);
|
|
12282
12259
|
}
|
|
12283
12260
|
});
|
|
@@ -13352,7 +13329,7 @@ ${rest}`);
|
|
|
13352
13329
|
fs31.rmSync(goal, { force: true });
|
|
13353
13330
|
return words ? `moved the goal to the top of ${rel(agentMemoryFile(PLANNER, "decisions.md"))}` : "removed the empty goal";
|
|
13354
13331
|
}
|
|
13355
|
-
var isGlobalMemory, PLANNER, PLANNER_MEMORY_FILES,
|
|
13332
|
+
var isGlobalMemory, PLANNER, PLANNER_MEMORY_FILES, BOARD_MEMORY_FILES, STARTERS, onlyStarter, setOf, RESERVED_MEMORY_DIR, KEPT_MEMORY_FOLDERS, agentMemoryDir, agentMemoryFile, writtenMemoryNames, memoryNamesOf, agentMemoryFiles, legacyAgentMemoryFile, HEADINGS, HEADING_RE, stripHeading, FOLDED_UNDER, moveMemory, RESERVED_MODULE_NAMES, isModule, moduleMemoryFile, plannerCopies, memoryFile, ENTRY_RE, entriesOf2, demote, promote, PRODUCT_STARTER, SEEDED_GOAL_PARAGRAPHS;
|
|
13356
13333
|
var init_memory = __esm({
|
|
13357
13334
|
"../cli/src/lib/memory.ts"() {
|
|
13358
13335
|
"use strict";
|
|
@@ -13363,9 +13340,6 @@ var init_memory = __esm({
|
|
|
13363
13340
|
isGlobalMemory = (name) => name in BUNDLED_MEMORY_FILES || fs31.existsSync(path32.join(MEMORY, name, "MEMORY.md"));
|
|
13364
13341
|
PLANNER = "planner";
|
|
13365
13342
|
PLANNER_MEMORY_FILES = ["decisions.md", "rejected.md", "redesign.md"];
|
|
13366
|
-
PROPOSER = "proposer";
|
|
13367
|
-
PROPOSER_MISSED = "missed.md";
|
|
13368
|
-
proposerMissedFile = () => rel(agentMemoryFile(PROPOSER, PROPOSER_MISSED));
|
|
13369
13343
|
BOARD_MEMORY_FILES = ["readme.md", "project.md"];
|
|
13370
13344
|
STARTERS = {
|
|
13371
13345
|
"readme.md": `# Shipped
|
|
@@ -14022,7 +13996,7 @@ function switchWorkflowAgent(id, agent, on) {
|
|
|
14022
13996
|
if (!listedHere(flow, wanted)) {
|
|
14023
13997
|
if (!onBoard().has(wanted)) return { ok: false, ...refusal("agentNotFound", `this board has no \`${wanted}\` agent`, { agent: wanted }) };
|
|
14024
13998
|
if (agentWorkflow(wanted) === id && flow.scheduled.some((h) => h.agent === wanted)) {
|
|
14025
|
-
return { ok: false, ...refusal("agentNotListed", `\`${wanted}\` is a scheduled agent of "${flow.name}" \u2014 switch it
|
|
13999
|
+
return { ok: false, ...refusal("agentNotListed", `\`${wanted}\` is a scheduled agent of "${flow.name}" \u2014 switch it on the project's Agents page`, { agent: wanted, name: flow.name, workflow: flow.id }) };
|
|
14026
14000
|
}
|
|
14027
14001
|
const refused = elsewhere(wanted, id);
|
|
14028
14002
|
if (refused) return refused;
|
|
@@ -14079,6 +14053,11 @@ function scheduledMembers(flow) {
|
|
|
14079
14053
|
const known2 = onBoard();
|
|
14080
14054
|
return flow.scheduled.filter((h) => known2.has(h.agent));
|
|
14081
14055
|
}
|
|
14056
|
+
function listedScheduled(flow) {
|
|
14057
|
+
const def = (flow.builtIn ? BUILTINS : boardWorkflowDefs().defs).find((d) => d.id === flow.id);
|
|
14058
|
+
const known2 = onBoard();
|
|
14059
|
+
return (def?.scheduled ?? []).map((s) => s.agent).filter((agent) => known2.has(agent));
|
|
14060
|
+
}
|
|
14082
14061
|
function setScheduled(id, change2, claim = "") {
|
|
14083
14062
|
const flow = workflowById(id);
|
|
14084
14063
|
if (!flow) return notFound(id);
|
|
@@ -14091,48 +14070,6 @@ function setScheduled(id, change2, claim = "") {
|
|
|
14091
14070
|
block.stages = stages;
|
|
14092
14071
|
});
|
|
14093
14072
|
}
|
|
14094
|
-
function switchWorkflowScheduled(id, agent, on, now = /* @__PURE__ */ new Date()) {
|
|
14095
|
-
const wanted = canonicalSpecAgent(agent.trim());
|
|
14096
|
-
return setScheduled(id, (rows) => {
|
|
14097
|
-
let one = agentWorkflow(wanted) === id ? rows.find((h) => h.agent === wanted) : void 0;
|
|
14098
|
-
if (!one) {
|
|
14099
|
-
if (!onBoard().has(wanted)) return { ok: false, ...refusal("agentNotFound", `this board has no \`${wanted}\` agent`, { agent: wanted }) };
|
|
14100
|
-
const flow = workflowById(id);
|
|
14101
|
-
if (listedHere(flow, wanted)) return notScheduled(wanted, flow);
|
|
14102
|
-
const refused = elsewhere(wanted, id);
|
|
14103
|
-
if (refused) return refused;
|
|
14104
|
-
one = { agent: wanted, extra: "", off: true, cadence: AUTO_CADENCE, lastRun: "" };
|
|
14105
|
-
rows.push(one);
|
|
14106
|
-
}
|
|
14107
|
-
if (!on) one.off = true;
|
|
14108
|
-
else if (one.off) {
|
|
14109
|
-
delete one.off;
|
|
14110
|
-
one.since = formatStamp(now);
|
|
14111
|
-
}
|
|
14112
|
-
});
|
|
14113
|
-
}
|
|
14114
|
-
function setWorkflowScheduledCadence(id, agent, cadence) {
|
|
14115
|
-
const wanted = canonicalSpecAgent(agent.trim());
|
|
14116
|
-
const said3 = cadence.trim();
|
|
14117
|
-
const next = !said3 || isAuto(said3) ? AUTO_CADENCE : said3;
|
|
14118
|
-
if (!isAuto(next) && !parseCadence(next)) {
|
|
14119
|
-
const formats = `${AUTO_CADENCE}, or ${CADENCE_FORMS}`;
|
|
14120
|
-
return { ok: false, ...refusal("cadence", `"${next}" isn't a cadence \u2014 use ${formats}`, { cadence: next, formats }) };
|
|
14121
|
-
}
|
|
14122
|
-
return setScheduled(id, (rows) => {
|
|
14123
|
-
const one = rows.find((h) => h.agent === wanted);
|
|
14124
|
-
if (!one) return notScheduled(wanted, workflowById(id));
|
|
14125
|
-
one.cadence = next;
|
|
14126
|
-
});
|
|
14127
|
-
}
|
|
14128
|
-
function setWorkflowScheduledExtra(id, agent, extra) {
|
|
14129
|
-
const wanted = canonicalSpecAgent(agent.trim());
|
|
14130
|
-
return setScheduled(id, (rows) => {
|
|
14131
|
-
const one = rows.find((h) => h.agent === wanted);
|
|
14132
|
-
if (!one) return notScheduled(wanted, workflowById(id));
|
|
14133
|
-
one.extra = extra;
|
|
14134
|
-
});
|
|
14135
|
-
}
|
|
14136
14073
|
function stampScheduledRun(id, agent, startedAt) {
|
|
14137
14074
|
return setScheduled(
|
|
14138
14075
|
id,
|
|
@@ -14181,7 +14118,7 @@ function frozenWorkflow(id) {
|
|
|
14181
14118
|
land: landAgents(flow).map(row)
|
|
14182
14119
|
};
|
|
14183
14120
|
}
|
|
14184
|
-
var LAND_ROLES, DEFAULT_WORKFLOW, BUILTINS, BUILTIN_WORKFLOW_IDS, isBuiltinWorkflow, builtinDescription, workflowsBlock, storedStages, PREVIEWS, LAND, SCHEDULE, toRow, scheduledRow, OLD_STAGE_KEYS, savedOldStages, workflowFileProblems, allListed, resolved, agentAfter, agentKeyProblems, agentWorkflow, workflows, workflowById, onBoard, previewMembers, previewAgents, landAgents, archivesOnApproval, workflowRuleFile, save2, ownDef, notFound, trimmedName, numbered, freeId2, nameTaken, listedHere, scheduledAgent,
|
|
14121
|
+
var LAND_ROLES, DEFAULT_WORKFLOW, BUILTINS, BUILTIN_WORKFLOW_IDS, isBuiltinWorkflow, builtinDescription, workflowsBlock, storedStages, PREVIEWS, LAND, SCHEDULE, toRow, scheduledRow, OLD_STAGE_KEYS, savedOldStages, workflowFileProblems, allListed, resolved, agentAfter, agentKeyProblems, agentWorkflow, workflows, workflowById, onBoard, previewMembers, previewAgents, landAgents, archivesOnApproval, workflowRuleFile, save2, ownDef, notFound, trimmedName, numbered, freeId2, nameTaken, listedHere, scheduledAgent, cardWorkflowId, cardWorkflow, cardStages, cardStage;
|
|
14185
14122
|
var init_workflows = __esm({
|
|
14186
14123
|
"../cli/src/lib/agent/workflows.ts"() {
|
|
14187
14124
|
"use strict";
|
|
@@ -14285,11 +14222,6 @@ var init_workflows = __esm({
|
|
|
14285
14222
|
const flow = workflowById(id);
|
|
14286
14223
|
return flow ? scheduledMembers(flow).find((h) => h.agent === canonicalSpecAgent(agent.trim())) : void 0;
|
|
14287
14224
|
};
|
|
14288
|
-
scheduledClock = (one) => [one.lastRun, one.since ?? ""].filter((stamp4) => parseStamp(stamp4)).sort().pop() ?? "";
|
|
14289
|
-
notScheduled = (agent, flow) => ({
|
|
14290
|
-
ok: false,
|
|
14291
|
-
...refusal("agentNotScheduled", `\`${agent}\` is not a scheduled agent of "${flow.name}"`, { agent, name: flow.name, workflow: flow.id })
|
|
14292
|
-
});
|
|
14293
14225
|
cardWorkflowId = (id) => cardMeta(id)?.workflow ?? "";
|
|
14294
14226
|
cardWorkflow = (id) => workflowFor(typeof id === "number" ? cardWorkflowId(id) : "");
|
|
14295
14227
|
cardStages = (meta, flow) => previewStages(flow, meta?.previews ?? []);
|
|
@@ -14614,7 +14546,7 @@ function agentRoster() {
|
|
|
14614
14546
|
...specialists.filter((a) => !a.builtIn)
|
|
14615
14547
|
];
|
|
14616
14548
|
}
|
|
14617
|
-
var PLANNER_DECISIONS, PLANNER_REJECTED, PLANNER_REDESIGN, DISCUSSION_HELPER, MEMORY_PRUNER, MEMORY_REVIEWER, DISMISSAL_REVIEWER, PROJECT_WRITER, SMALL_FIX,
|
|
14549
|
+
var PLANNER_DECISIONS, PLANNER_REJECTED, PLANNER_REDESIGN, DISCUSSION_HELPER, MEMORY_PRUNER, MEMORY_REVIEWER, DISMISSAL_REVIEWER, PROJECT_WRITER, SMALL_FIX, PROPOSER, PLANNER_ROLE, BOARD_ROLES, DISCUSSION_ROLE, ROLE_NAMES, PLANNING_ROLE, roles, roleForFlow, roleNamed, hiddenRole, stageContractProblems, memoryOf, ownMemoryOf, agentNames;
|
|
14618
14550
|
var init_roles = __esm({
|
|
14619
14551
|
"../cli/src/lib/agent/roles.ts"() {
|
|
14620
14552
|
"use strict";
|
|
@@ -14653,10 +14585,10 @@ var init_roles = __esm({
|
|
|
14653
14585
|
memory: ["memory/project.md"]
|
|
14654
14586
|
};
|
|
14655
14587
|
SMALL_FIX = "A small fix is a small change whose workflow needs none of its previews.";
|
|
14656
|
-
|
|
14588
|
+
PROPOSER = {
|
|
14657
14589
|
name: "proposer",
|
|
14658
14590
|
gloss: "proposes the work a finished card leaves behind",
|
|
14659
|
-
memory: [
|
|
14591
|
+
memory: [],
|
|
14660
14592
|
settings: [
|
|
14661
14593
|
{
|
|
14662
14594
|
key: "small-fixes",
|
|
@@ -14703,7 +14635,7 @@ var init_roles = __esm({
|
|
|
14703
14635
|
MEMORY_REVIEWER,
|
|
14704
14636
|
DISMISSAL_REVIEWER,
|
|
14705
14637
|
PROJECT_WRITER,
|
|
14706
|
-
|
|
14638
|
+
PROPOSER
|
|
14707
14639
|
];
|
|
14708
14640
|
DISCUSSION_ROLE = DISCUSSION_HELPER.name;
|
|
14709
14641
|
ROLE_NAMES = BOARD_ROLES.map((r) => r.name);
|
|
@@ -15597,11 +15529,6 @@ function parse(file, lenient = false) {
|
|
|
15597
15529
|
cardId: /^\d+$/.test(held4.card_id ?? "") ? Number(held4.card_id) : null,
|
|
15598
15530
|
archivedAt: held4.archived_at || "",
|
|
15599
15531
|
contentKept: held4.content_kept !== "false",
|
|
15600
|
-
verdict: VERDICTS.includes(held4.verdict) ? held4.verdict : "",
|
|
15601
|
-
verdictReason: REASONS.includes(held4.verdict_reason) ? held4.verdict_reason : "",
|
|
15602
|
-
verdictCard: /^\d+$/.test(held4.verdict_card ?? "") ? Number(held4.verdict_card) : null,
|
|
15603
|
-
dropConfidence: confidenceOf(held4.drop_confidence),
|
|
15604
|
-
doConfidence: confidenceOf(held4.do_confidence),
|
|
15605
15532
|
relPath: boardRel(file)
|
|
15606
15533
|
};
|
|
15607
15534
|
}
|
|
@@ -15637,11 +15564,6 @@ function writeSignal(incoming, importedAt, slug2 = "") {
|
|
|
15637
15564
|
cardId: null,
|
|
15638
15565
|
archivedAt: "",
|
|
15639
15566
|
contentKept: true,
|
|
15640
|
-
verdict: "",
|
|
15641
|
-
verdictReason: "",
|
|
15642
|
-
verdictCard: null,
|
|
15643
|
-
dropConfidence: null,
|
|
15644
|
-
doConfidence: null,
|
|
15645
15567
|
relPath: ""
|
|
15646
15568
|
};
|
|
15647
15569
|
const file = path41.join(TRIAGE, freeName2(TRIAGE, fileName(signal2.title, slug2)));
|
|
@@ -15726,7 +15648,7 @@ function restoreInboxItems(sourceIds) {
|
|
|
15726
15648
|
function restoreInboxItem(sourceId) {
|
|
15727
15649
|
return restoreInboxItems([sourceId])[0];
|
|
15728
15650
|
}
|
|
15729
|
-
var boardRel,
|
|
15651
|
+
var boardRel, FIELDS, META_LINE, slugOf, fileName, readArchived, readAllDismissed, bySource;
|
|
15730
15652
|
var init_inbox = __esm({
|
|
15731
15653
|
"../cli/src/lib/signals/inbox.ts"() {
|
|
15732
15654
|
"use strict";
|
|
@@ -15735,11 +15657,8 @@ var init_inbox = __esm({
|
|
|
15735
15657
|
init_paths();
|
|
15736
15658
|
init_yaml();
|
|
15737
15659
|
boardRel = (file) => rel(file).split(path41.sep).join("/");
|
|
15738
|
-
VERDICTS = ["plan", "plan-without-refine", "skip", "human-review"];
|
|
15739
|
-
REASONS = ["supported", "rejected", "duplicate", "low-value", "needs-user", "unsure", "small", "plan", "no-workflow"];
|
|
15740
15660
|
FIELDS = ["source_id", "title", "collected_at", "imported_at"];
|
|
15741
15661
|
META_LINE = /^\s+("(?:[^"\\]|\\.)*"|[^:]+):\s*(.*)$/;
|
|
15742
|
-
confidenceOf = (said3) => /^(0(\.\d+)?|1(\.0+)?)$/.test(said3 ?? "") ? Number(said3) : null;
|
|
15743
15662
|
slugOf = (said3) => said3.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 56).replace(/-+$/, "");
|
|
15744
15663
|
fileName = (title, slug2 = "") => `${slugOf(slug2) || slugOf(title) || "item"}.md`;
|
|
15745
15664
|
readArchived = () => readFolder(SIGNALS_ARCHIVED);
|
|
@@ -16404,11 +16323,11 @@ function resumeRefusal(delivery, store) {
|
|
|
16404
16323
|
);
|
|
16405
16324
|
}
|
|
16406
16325
|
if (!worktreeExists(delivery.worktree)) {
|
|
16407
|
-
const
|
|
16326
|
+
const path86 = delivery.worktree ?? "";
|
|
16408
16327
|
return refusal(
|
|
16409
16328
|
"deliveryWorktreeGone",
|
|
16410
|
-
`delivery ${id}'s worktree ${
|
|
16411
|
-
{ id, task, path:
|
|
16329
|
+
`delivery ${id}'s worktree ${path86} is gone, so there is nothing to carry on. ${rebuild}`,
|
|
16330
|
+
{ id, task, path: path86, command: discard }
|
|
16412
16331
|
);
|
|
16413
16332
|
}
|
|
16414
16333
|
if (!branchExists(delivery.branch)) {
|
|
@@ -17812,7 +17731,8 @@ function reflectBlocks(ids) {
|
|
|
17812
17731
|
]);
|
|
17813
17732
|
}
|
|
17814
17733
|
function scheduledBlock() {
|
|
17815
|
-
const
|
|
17734
|
+
const given = process.env[SCHEDULED_AGENTS_ENV];
|
|
17735
|
+
const on = new Set(given === void 0 ? workflows().flatMap((flow) => listedScheduled(flow)) : given.split(","));
|
|
17816
17736
|
const lines = specAgentCatalog().agents.filter((a) => on.has(a.name)).map((a) => `${a.name} \u2014 ${a.description}`);
|
|
17817
17737
|
return lines.length ? ["<scheduled-agents>", ...lines, "</scheduled-agents>"] : [];
|
|
17818
17738
|
}
|
|
@@ -17840,10 +17760,10 @@ function answeredNote(cardId2, command) {
|
|
|
17840
17760
|
}
|
|
17841
17761
|
function carriedNote(req) {
|
|
17842
17762
|
if (req.session !== "resume" && req.session !== "fork") {
|
|
17843
|
-
return req.carry?.transcript ? `This card came out of a discussion. Read it for what the card leaves out: ${req.carry.transcript}` : "";
|
|
17763
|
+
return req.carry?.transcript ? `This card came out of a discussion. The card is the final word: the discussion only fills in the background, and what it dropped stays dropped. Read it for what the card leaves out: ${req.carry.transcript}` : "";
|
|
17844
17764
|
}
|
|
17845
|
-
const held4 = req.session === "resume" ? "This session planned this card before." : "This session is a copy of the one this card was created in.";
|
|
17846
|
-
return `${held4} The card as it reads now outranks what you remember of it: read it again before you act.`;
|
|
17765
|
+
const held4 = req.session === "resume" ? "This session planned this card before." : req.carry?.resumeId ? "This session is a copy of the discussion this card came from, and that discussion's rules ended with it: follow this ask." : "This session is a copy of the one this card was created in.";
|
|
17766
|
+
return `${held4} The card as it reads now outranks what you remember of it, and what was dropped along the way stays dropped: read it again before you act.`;
|
|
17847
17767
|
}
|
|
17848
17768
|
function actionPrompt(req, command, notes) {
|
|
17849
17769
|
const kb = skillCall(agentForRun(req), req.runtime);
|
|
@@ -17866,6 +17786,7 @@ function actionPrompt(req, command, notes) {
|
|
|
17866
17786
|
}
|
|
17867
17787
|
return [
|
|
17868
17788
|
`${kb}. Implement task ${req.id} ${named2} following \`akb guide implement\`.`,
|
|
17789
|
+
carriedNote(req),
|
|
17869
17790
|
req.notes ? `Extra notes: ${req.notes}` : ""
|
|
17870
17791
|
].filter(Boolean).join(" ");
|
|
17871
17792
|
case "archive":
|
|
@@ -17933,10 +17854,7 @@ function actionPrompt(req, command, notes) {
|
|
|
17933
17854
|
// The dismissal review (#929). The flow lists the dismissals and restored ids; the guide
|
|
17934
17855
|
// holds every rule.
|
|
17935
17856
|
case "review-dismissals":
|
|
17936
|
-
return
|
|
17937
|
-
`${kb}. Learn what the user does not want from their rejections and dismissals, following \`akb guide review-rejections\`.`,
|
|
17938
|
-
req.allReasons ? `The reasons are the ones \`${command} review-dismissals --all --print\` prints.` : ""
|
|
17939
|
-
].filter(Boolean).join(" ");
|
|
17857
|
+
return `${kb}. Learn what the user does not want from their rejections and dismissals, following \`akb guide review-rejections\`.`;
|
|
17940
17858
|
case "describe-project":
|
|
17941
17859
|
return `${kb}. Describe this project following \`akb guide describe-project\`.`;
|
|
17942
17860
|
// Reflecting on the cards the board has just completed (#534, #1467). They are off the
|
|
@@ -17951,7 +17869,7 @@ function actionPrompt(req, command, notes) {
|
|
|
17951
17869
|
`${kb}. These tasks have just been completed: ${titles.join(", ")}. Propose the work that should follow them, following \`akb guide reflect\`.`,
|
|
17952
17870
|
`They have left the board \u2014 read each in its own <card> block below, with the discussion it came from when the block lists one.`,
|
|
17953
17871
|
`Skip anything already on the board, already in the inbox, or turned down before.`,
|
|
17954
|
-
`Write each survivor with \`${command} triage add\`, and revise an open card whose plan what shipped made untrue with \`${command} card revise\`: that is all you may do unless your settings below say otherwise \u2014 you edit and archive no card yourself, and finding nothing worth proposing is a complete result.`,
|
|
17872
|
+
`Write each survivor with \`${command} triage add\`, and revise an open card whose plan what shipped made untrue with \`${command} card revise\`: that is all you may do${settingsBlock(req) ? " unless your settings below say otherwise" : ""} \u2014 you edit and archive no card yourself, and finding nothing worth proposing is a complete result.`,
|
|
17955
17873
|
`Don't ask me questions with human-in-the-loop.`
|
|
17956
17874
|
].join(" "),
|
|
17957
17875
|
...reflectBlocks(ids),
|
|
@@ -18099,6 +18017,7 @@ var init_prompts = __esm({
|
|
|
18099
18017
|
init_agents();
|
|
18100
18018
|
init_command();
|
|
18101
18019
|
init_deliveries();
|
|
18020
|
+
init_env();
|
|
18102
18021
|
init_facts();
|
|
18103
18022
|
init_outputs();
|
|
18104
18023
|
init_archive();
|
|
@@ -18456,13 +18375,6 @@ function recordReflection(run2) {
|
|
|
18456
18375
|
} catch {
|
|
18457
18376
|
}
|
|
18458
18377
|
}
|
|
18459
|
-
function recordDismissalReview(run2) {
|
|
18460
|
-
if (run2.action !== "review-dismissals" || run2.status !== "done") return;
|
|
18461
|
-
try {
|
|
18462
|
-
stampDismissalReview(new Date(run2.startedAt));
|
|
18463
|
-
} catch {
|
|
18464
|
-
}
|
|
18465
|
-
}
|
|
18466
18378
|
function recordScheduledRun(run2) {
|
|
18467
18379
|
if (run2.action !== "scheduled" || run2.status !== "done" || !run2.workflow || !run2.specAgent) return;
|
|
18468
18380
|
try {
|
|
@@ -18822,7 +18734,8 @@ function planningSession(cardId2, cwd = REPO_ROOT) {
|
|
|
18822
18734
|
return created ?? { none: "unrecorded" };
|
|
18823
18735
|
}
|
|
18824
18736
|
function carriedSession(req) {
|
|
18825
|
-
|
|
18737
|
+
const carries = PLANNING.has(req.action) || req.action === "implement" && !!req.carry?.resumeId;
|
|
18738
|
+
if (!Number.isInteger(req.id) || !carries) return void 0;
|
|
18826
18739
|
const fresh = (why) => ({ note: `new session \u2014 ${why}` });
|
|
18827
18740
|
if (req.session === "new") return fresh("the earlier one is gone, so this step was started again");
|
|
18828
18741
|
const agent = agentForRun(req);
|
|
@@ -19153,7 +19066,6 @@ async function closeRun(sessionId, res) {
|
|
|
19153
19066
|
recordPrune(closed2);
|
|
19154
19067
|
recordMemoryReview(closed2);
|
|
19155
19068
|
recordReflection(closed2);
|
|
19156
|
-
recordDismissalReview(closed2);
|
|
19157
19069
|
recordProjectDescription(closed2);
|
|
19158
19070
|
recordScheduledRun(closed2);
|
|
19159
19071
|
dropSpec(sessionId);
|
|
@@ -19805,7 +19717,7 @@ import fs51 from "node:fs";
|
|
|
19805
19717
|
import path55 from "node:path";
|
|
19806
19718
|
function defaultBody() {
|
|
19807
19719
|
return [
|
|
19808
|
-
"
|
|
19720
|
+
"<\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>",
|
|
19809
19721
|
"",
|
|
19810
19722
|
"## Worth noting",
|
|
19811
19723
|
"",
|
|
@@ -20863,145 +20775,6 @@ _(not filled in yet \u2014 build the map from the repo before tagging a card.)_
|
|
|
20863
20775
|
}
|
|
20864
20776
|
});
|
|
20865
20777
|
|
|
20866
|
-
// ../cli/src/lib/agent/dismissal-review.ts
|
|
20867
|
-
import fs57 from "node:fs";
|
|
20868
|
-
function rejectionsToReview(since) {
|
|
20869
|
-
if (!fs57.existsSync(ARCHIVE)) return [];
|
|
20870
|
-
const found2 = [];
|
|
20871
|
-
for (const file of walkMd(ARCHIVE)) {
|
|
20872
|
-
const id = archivedId(file);
|
|
20873
|
-
if (id === null) continue;
|
|
20874
|
-
try {
|
|
20875
|
-
const { meta } = parseFrontmatter(fs57.readFileSync(file, "utf8"));
|
|
20876
|
-
if (!meta?.rejected || meta.rejected_by !== "user" || !meta.rejected_reason.trim()) continue;
|
|
20877
|
-
if ((parseStamp(meta.rejected_at)?.getTime() ?? 0) < since) continue;
|
|
20878
|
-
found2.push({ id, title: meta.title, file: rel(file), reason: meta.rejected_reason.trim(), rejectedAt: meta.rejected_at });
|
|
20879
|
-
} catch {
|
|
20880
|
-
continue;
|
|
20881
|
-
}
|
|
20882
|
-
}
|
|
20883
|
-
return found2.sort((a, b) => a.rejectedAt.localeCompare(b.rejectedAt) || a.id - b.id);
|
|
20884
|
-
}
|
|
20885
|
-
function dismissalsToReview(since) {
|
|
20886
|
-
return readAllDismissed().filter((s) => s.dismissedBy === "user" && s.dismissedReason.trim()).filter((s) => (parseStamp(s.dismissedAt)?.getTime() ?? 0) >= since).sort((a, b) => a.dismissedAt.localeCompare(b.dismissedAt)).map((s) => ({
|
|
20887
|
-
sourceId: s.sourceId,
|
|
20888
|
-
title: s.title,
|
|
20889
|
-
file: s.relPath,
|
|
20890
|
-
reason: s.dismissedReason.trim(),
|
|
20891
|
-
dismissedAt: s.dismissedAt
|
|
20892
|
-
}));
|
|
20893
|
-
}
|
|
20894
|
-
function citedSources(text5) {
|
|
20895
|
-
const ids = /* @__PURE__ */ new Set();
|
|
20896
|
-
for (const line of text5.split("\n")) {
|
|
20897
|
-
if (!/^\s*[-*]\s/.test(line)) continue;
|
|
20898
|
-
const m = line.match(SOURCES);
|
|
20899
|
-
for (const id of m?.[1].split(",") ?? []) if (id.trim()) ids.add(id.trim());
|
|
20900
|
-
}
|
|
20901
|
-
return [...ids];
|
|
20902
|
-
}
|
|
20903
|
-
function withdrawnSources() {
|
|
20904
|
-
const cited = [...new Set(plannerCopies("rejected.md").flatMap((file) => {
|
|
20905
|
-
try {
|
|
20906
|
-
return citedSources(fs57.readFileSync(file, "utf8"));
|
|
20907
|
-
} catch {
|
|
20908
|
-
return [];
|
|
20909
|
-
}
|
|
20910
|
-
}))];
|
|
20911
|
-
if (cited.length === 0) return [];
|
|
20912
|
-
const dismissed = new Set(readAllDismissed().map((s) => s.sourceId));
|
|
20913
|
-
const restored = new Set([...readInbox(), ...readArchived()].map((s) => s.sourceId));
|
|
20914
|
-
return cited.filter((id) => restored.has(id) && !dismissed.has(id));
|
|
20915
|
-
}
|
|
20916
|
-
function rejectionWorkWaiting(since) {
|
|
20917
|
-
return rejectionsToReview(since).length > 0 || dismissalsToReview(since).length > 0 || withdrawnSources().length > 0;
|
|
20918
|
-
}
|
|
20919
|
-
var SOURCES;
|
|
20920
|
-
var init_dismissal_review = __esm({
|
|
20921
|
-
"../cli/src/lib/agent/dismissal-review.ts"() {
|
|
20922
|
-
"use strict";
|
|
20923
|
-
init_cadence();
|
|
20924
|
-
init_cards();
|
|
20925
|
-
init_frontmatter();
|
|
20926
|
-
init_memory();
|
|
20927
|
-
init_paths();
|
|
20928
|
-
init_inbox();
|
|
20929
|
-
init_archive();
|
|
20930
|
-
SOURCES = /\(([^()]+)\)\s*$/;
|
|
20931
|
-
}
|
|
20932
|
-
});
|
|
20933
|
-
|
|
20934
|
-
// ../cli/src/lib/agent/due.ts
|
|
20935
|
-
import fs58 from "node:fs";
|
|
20936
|
-
function scheduleDue(ask2, now = Date.now()) {
|
|
20937
|
-
const gap = !isAuto(ask2.cadence) ? ask2.cadence : autoSchedule(ask2.reads, ask2.fallback).every;
|
|
20938
|
-
const from = Math.max(ask2.from, ...ask2.attempts.map((a) => a.startedAt));
|
|
20939
|
-
let next = ask2.goesOn ? /* @__PURE__ */ new Date(0) : from ? nextDue(formatStamp(new Date(from)), gap) ?? /* @__PURE__ */ new Date(0) : /* @__PURE__ */ new Date(0);
|
|
20940
|
-
const failed = failures(ask2.attempts);
|
|
20941
|
-
if (!ask2.goesOn && from && failed > 1) {
|
|
20942
|
-
const base = next.getTime() - from;
|
|
20943
|
-
next = new Date(from + Math.min(base * 2 ** (failed - 1), Math.max(base, DAY)));
|
|
20944
|
-
}
|
|
20945
|
-
const newest2 = ask2.attempts.reduce((a, r) => a && a.startedAt >= r.startedAt ? a : r, null);
|
|
20946
|
-
const retrying = isAuto(ask2.cadence) && newest2?.status === "error";
|
|
20947
|
-
const wait = next.getTime() > now ? retrying ? "retrying" : "tooSoon" : !ask2.newWork() ? "nothingNew" : ask2.building?.() ? "building" : null;
|
|
20948
|
-
const reason = !wait || wait === "tooSoon" ? void 0 : wait === "nothingNew" ? ask2.reads : wait;
|
|
20949
|
-
return { next, wait, ...reason ? { reason } : {} };
|
|
20950
|
-
}
|
|
20951
|
-
function readsNew(reads, since) {
|
|
20952
|
-
switch (reads) {
|
|
20953
|
-
case "archived-cards":
|
|
20954
|
-
return archivedSince(since);
|
|
20955
|
-
case "chats":
|
|
20956
|
-
return false;
|
|
20957
|
-
case "dismissals":
|
|
20958
|
-
return rejectionWorkWaiting(since);
|
|
20959
|
-
}
|
|
20960
|
-
}
|
|
20961
|
-
function archivedSince(since) {
|
|
20962
|
-
if (!fs58.existsSync(ARCHIVE)) return false;
|
|
20963
|
-
const day = formatDay(new Date(since));
|
|
20964
|
-
for (const file of walkMd(ARCHIVE)) {
|
|
20965
|
-
try {
|
|
20966
|
-
if (fs58.statSync(file).mtimeMs <= since) continue;
|
|
20967
|
-
const { meta } = parseFrontmatter(fs58.readFileSync(file, "utf8"));
|
|
20968
|
-
if (meta && !meta.rejected && (meta.archived ?? "") >= day) return true;
|
|
20969
|
-
} catch {
|
|
20970
|
-
continue;
|
|
20971
|
-
}
|
|
20972
|
-
}
|
|
20973
|
-
return false;
|
|
20974
|
-
}
|
|
20975
|
-
function building(store) {
|
|
20976
|
-
const builds = new Set(store.deliveries.filter((d) => d.status === "active" && !d.scheduled).map((d) => d.deliveryId));
|
|
20977
|
-
if (store.deliveries.some((d) => builds.has(d.deliveryId) && d.landing)) return true;
|
|
20978
|
-
return store.runs.some((r) => r.status === "running" && r.deliveryId !== void 0 && builds.has(r.deliveryId));
|
|
20979
|
-
}
|
|
20980
|
-
var AUTO_GAP, DAY, autoSchedule, failures, stampMs;
|
|
20981
|
-
var init_due = __esm({
|
|
20982
|
-
"../cli/src/lib/agent/due.ts"() {
|
|
20983
|
-
"use strict";
|
|
20984
|
-
init_cadence();
|
|
20985
|
-
init_cards();
|
|
20986
|
-
init_frontmatter();
|
|
20987
|
-
init_paths();
|
|
20988
|
-
init_dismissal_review();
|
|
20989
|
-
AUTO_GAP = "6h";
|
|
20990
|
-
DAY = 864e5;
|
|
20991
|
-
autoSchedule = (reads, fallback) => reads ? { every: AUTO_GAP, reads } : { every: fallback };
|
|
20992
|
-
failures = (attempts) => {
|
|
20993
|
-
let n = 0;
|
|
20994
|
-
for (const a of [...attempts].sort((x, y) => y.startedAt - x.startedAt)) {
|
|
20995
|
-
if (a.status === "running") continue;
|
|
20996
|
-
if (a.status === "done") break;
|
|
20997
|
-
n++;
|
|
20998
|
-
}
|
|
20999
|
-
return n;
|
|
21000
|
-
};
|
|
21001
|
-
stampMs = (stamp4) => parseStamp(stamp4)?.getTime() ?? 0;
|
|
21002
|
-
}
|
|
21003
|
-
});
|
|
21004
|
-
|
|
21005
20778
|
// ../cli/src/lib/agent/scheduled.ts
|
|
21006
20779
|
function scheduledBusy(pass, store = readStore()) {
|
|
21007
20780
|
const args = { agent: pass.agent, workflow: pass.workflow };
|
|
@@ -21013,25 +20786,6 @@ function scheduledBusy(pass, store = readStore()) {
|
|
|
21013
20786
|
}
|
|
21014
20787
|
return null;
|
|
21015
20788
|
}
|
|
21016
|
-
function scheduledWait(pass, one, store = readStore(), now = /* @__PURE__ */ new Date()) {
|
|
21017
|
-
const reads = specAgentCatalog().agents.find((a) => a.name === one.agent)?.reads;
|
|
21018
|
-
const lastRun = stampMs(one.lastRun);
|
|
21019
|
-
return scheduleDue(
|
|
21020
|
-
{
|
|
21021
|
-
cadence: one.cadence,
|
|
21022
|
-
fallback: SCHEDULED_CADENCE,
|
|
21023
|
-
reads,
|
|
21024
|
-
from: stampMs(scheduledClock(one)),
|
|
21025
|
-
attempts: store.runs.filter((r) => isPass(r, pass)),
|
|
21026
|
-
newWork: () => !reads || readsNew(reads, lastRun),
|
|
21027
|
-
building: () => building(store)
|
|
21028
|
-
},
|
|
21029
|
-
now.getTime()
|
|
21030
|
-
);
|
|
21031
|
-
}
|
|
21032
|
-
function scheduledNext(workflow, one) {
|
|
21033
|
-
return one.off || !scheduledClock(one) ? null : scheduledWait({ workflow, agent: one.agent }, one);
|
|
21034
|
-
}
|
|
21035
20789
|
function insideScheduledPass() {
|
|
21036
20790
|
const sessionId = insideRun();
|
|
21037
20791
|
const run2 = sessionId ? readStore().runs.find((r) => r.sessionId === sessionId) : void 0;
|
|
@@ -21054,13 +20808,10 @@ var scheduledRequest, isPass, openPasses, stalePasses;
|
|
|
21054
20808
|
var init_scheduled = __esm({
|
|
21055
20809
|
"../cli/src/lib/agent/scheduled.ts"() {
|
|
21056
20810
|
"use strict";
|
|
21057
|
-
init_catalog2();
|
|
21058
|
-
init_due();
|
|
21059
20811
|
init_env();
|
|
21060
20812
|
init_stages();
|
|
21061
20813
|
init_store();
|
|
21062
20814
|
init_types3();
|
|
21063
|
-
init_workflows();
|
|
21064
20815
|
scheduledRequest = (pass) => ({
|
|
21065
20816
|
action: "scheduled",
|
|
21066
20817
|
workflow: pass.workflow,
|
|
@@ -21075,15 +20826,15 @@ var init_scheduled = __esm({
|
|
|
21075
20826
|
});
|
|
21076
20827
|
|
|
21077
20828
|
// ../cli/src/lib/card-age.ts
|
|
21078
|
-
import
|
|
20829
|
+
import fs57 from "node:fs";
|
|
21079
20830
|
import path60 from "node:path";
|
|
21080
20831
|
function staleAfterIn(config) {
|
|
21081
20832
|
const days = Number(LINE.exec(config)?.[1]);
|
|
21082
20833
|
return Number.isInteger(days) && days > 0 ? days : STALE_AFTER_DEFAULT;
|
|
21083
20834
|
}
|
|
21084
20835
|
function staleAfter() {
|
|
21085
|
-
if (!
|
|
21086
|
-
return staleAfterIn(
|
|
20836
|
+
if (!fs57.existsSync(CONFIG)) return STALE_AFTER_DEFAULT;
|
|
20837
|
+
return staleAfterIn(fs57.readFileSync(CONFIG, "utf8"));
|
|
21087
20838
|
}
|
|
21088
20839
|
function todoPathspec(top) {
|
|
21089
20840
|
const from = path60.relative(real(top), real(TODO));
|
|
@@ -21125,11 +20876,11 @@ function cardAges(now = Date.now()) {
|
|
|
21125
20876
|
if (!file || ages.has(file) || dirty.has(file)) continue;
|
|
21126
20877
|
const at = Date.parse(when);
|
|
21127
20878
|
if (Number.isNaN(at)) continue;
|
|
21128
|
-
ages.set(file, { lastTouched: when.slice(0, 10), days: Math.max(0, Math.floor((now - at) /
|
|
20879
|
+
ages.set(file, { lastTouched: when.slice(0, 10), days: Math.max(0, Math.floor((now - at) / DAY)) });
|
|
21129
20880
|
}
|
|
21130
20881
|
return ages;
|
|
21131
20882
|
}
|
|
21132
|
-
var STALE_AFTER_DEFAULT, LINE, gitTop, real, unquotePath,
|
|
20883
|
+
var STALE_AFTER_DEFAULT, LINE, gitTop, real, unquotePath, DAY;
|
|
21133
20884
|
var init_card_age = __esm({
|
|
21134
20885
|
"../cli/src/lib/card-age.ts"() {
|
|
21135
20886
|
"use strict";
|
|
@@ -21140,7 +20891,7 @@ var init_card_age = __esm({
|
|
|
21140
20891
|
gitTop = () => git(["rev-parse", "--show-toplevel"])?.trim() || null;
|
|
21141
20892
|
real = (p) => {
|
|
21142
20893
|
try {
|
|
21143
|
-
return
|
|
20894
|
+
return fs57.realpathSync(p);
|
|
21144
20895
|
} catch {
|
|
21145
20896
|
return path60.resolve(p);
|
|
21146
20897
|
}
|
|
@@ -21153,7 +20904,7 @@ var init_card_age = __esm({
|
|
|
21153
20904
|
return line.slice(1, -1);
|
|
21154
20905
|
}
|
|
21155
20906
|
};
|
|
21156
|
-
|
|
20907
|
+
DAY = 24 * 60 * 60 * 1e3;
|
|
21157
20908
|
}
|
|
21158
20909
|
});
|
|
21159
20910
|
|
|
@@ -21177,7 +20928,7 @@ var init_card_holds = __esm({
|
|
|
21177
20928
|
});
|
|
21178
20929
|
|
|
21179
20930
|
// ../cli/src/commands/list.ts
|
|
21180
|
-
import
|
|
20931
|
+
import fs58 from "node:fs";
|
|
21181
20932
|
import path61 from "node:path";
|
|
21182
20933
|
function summaryLine(body) {
|
|
21183
20934
|
const lines = body.split("\n");
|
|
@@ -21200,7 +20951,7 @@ function openRows() {
|
|
|
21200
20951
|
const id = isRoot ? idPrefix(path61.basename(path61.dirname(file))) : idPrefix(base);
|
|
21201
20952
|
if (id == null) continue;
|
|
21202
20953
|
if (isLegacyRecurring(path61.relative(TODO, file))) continue;
|
|
21203
|
-
const { meta, body } = parseFrontmatter(
|
|
20954
|
+
const { meta, body } = parseFrontmatter(fs58.readFileSync(file, "utf8"));
|
|
21204
20955
|
rows.push({
|
|
21205
20956
|
id,
|
|
21206
20957
|
file,
|
|
@@ -21242,7 +20993,7 @@ function cmdStale(rows, all2, scope, mod) {
|
|
|
21242
20993
|
}
|
|
21243
20994
|
function archivedFiles() {
|
|
21244
20995
|
const files2 = /* @__PURE__ */ new Map();
|
|
21245
|
-
if (!
|
|
20996
|
+
if (!fs58.existsSync(ARCHIVE)) return files2;
|
|
21246
20997
|
for (const file of walkMd(ARCHIVE)) {
|
|
21247
20998
|
const id = idPrefix(path61.basename(file));
|
|
21248
20999
|
if (id !== null) files2.set(id, file);
|
|
@@ -21250,7 +21001,7 @@ function archivedFiles() {
|
|
|
21250
21001
|
for (const dir of walkDirs(ARCHIVE)) {
|
|
21251
21002
|
const id = idPrefix(path61.basename(dir));
|
|
21252
21003
|
const root = path61.join(dir, "root.md");
|
|
21253
|
-
if (id !== null &&
|
|
21004
|
+
if (id !== null && fs58.existsSync(root)) files2.set(id, root);
|
|
21254
21005
|
}
|
|
21255
21006
|
return files2;
|
|
21256
21007
|
}
|
|
@@ -21361,7 +21112,7 @@ var init_list = __esm({
|
|
|
21361
21112
|
});
|
|
21362
21113
|
|
|
21363
21114
|
// ../cli/src/commands/misc.ts
|
|
21364
|
-
import
|
|
21115
|
+
import fs59 from "node:fs";
|
|
21365
21116
|
import path62 from "node:path";
|
|
21366
21117
|
function extractOldMeta(text5, file) {
|
|
21367
21118
|
const grab = (re) => {
|
|
@@ -21399,7 +21150,7 @@ function cmdMigrate(opts) {
|
|
|
21399
21150
|
let changed = 0;
|
|
21400
21151
|
let skipped = 0;
|
|
21401
21152
|
for (const file of files2) {
|
|
21402
|
-
const text5 =
|
|
21153
|
+
const text5 = fs59.readFileSync(file, "utf8");
|
|
21403
21154
|
if (text5.trimStart().startsWith("---")) {
|
|
21404
21155
|
skipped++;
|
|
21405
21156
|
continue;
|
|
@@ -21408,7 +21159,7 @@ function cmdMigrate(opts) {
|
|
|
21408
21159
|
const out = serializeFrontmatter(meta) + "\n\n" + stripOldHeader(text5).replace(/^\n+/, "");
|
|
21409
21160
|
if (dry) say(`would migrate ${rel(file)} (priority=${meta.priority})`);
|
|
21410
21161
|
else {
|
|
21411
|
-
|
|
21162
|
+
fs59.writeFileSync(file, out.endsWith("\n") ? out : out + "\n");
|
|
21412
21163
|
say(`migrated ${rel(file)}`);
|
|
21413
21164
|
}
|
|
21414
21165
|
changed++;
|
|
@@ -21429,10 +21180,10 @@ var init_misc = __esm({
|
|
|
21429
21180
|
});
|
|
21430
21181
|
|
|
21431
21182
|
// ../cli/src/lib/group-close.ts
|
|
21432
|
-
import
|
|
21183
|
+
import fs60 from "node:fs";
|
|
21433
21184
|
function groupCloses(rootFile) {
|
|
21434
21185
|
try {
|
|
21435
|
-
return groupCloseOf(
|
|
21186
|
+
return groupCloseOf(fs60.readFileSync(rootFile, "utf8"));
|
|
21436
21187
|
} catch {
|
|
21437
21188
|
return false;
|
|
21438
21189
|
}
|
|
@@ -21445,7 +21196,7 @@ var init_group_close = __esm({
|
|
|
21445
21196
|
});
|
|
21446
21197
|
|
|
21447
21198
|
// ../cli/src/commands/remove.ts
|
|
21448
|
-
import
|
|
21199
|
+
import fs61 from "node:fs";
|
|
21449
21200
|
import path63 from "node:path";
|
|
21450
21201
|
function leavingIds(id, found2) {
|
|
21451
21202
|
const ids = /* @__PURE__ */ new Set([id]);
|
|
@@ -21462,10 +21213,10 @@ function findMentions(ids) {
|
|
|
21462
21213
|
const hits = [];
|
|
21463
21214
|
const re = new RegExp(ids.map((id) => `#${id}(?!\\d)`).join("|"));
|
|
21464
21215
|
for (const dir of [TODO, MEMORY]) {
|
|
21465
|
-
if (!
|
|
21216
|
+
if (!fs61.existsSync(dir)) continue;
|
|
21466
21217
|
for (const file of walkMd(dir)) {
|
|
21467
21218
|
if (path63.basename(file) === "README.md") continue;
|
|
21468
|
-
const lines =
|
|
21219
|
+
const lines = fs61.readFileSync(file, "utf8").split("\n");
|
|
21469
21220
|
const fmEnd = frontmatterEnd(lines);
|
|
21470
21221
|
lines.forEach((line, i) => {
|
|
21471
21222
|
if (!re.test(line)) return;
|
|
@@ -21495,10 +21246,10 @@ function stampLeaving(cards, rejection) {
|
|
|
21495
21246
|
const day = formatDay();
|
|
21496
21247
|
const stamp4 = rejection && !rejection.discard ? { reason: rejection.reason, at: (/* @__PURE__ */ new Date()).toISOString(), by: insideRun() ? "agent" : "user" } : rejection && { reason: rejection.reason };
|
|
21497
21248
|
for (const card of cards) {
|
|
21498
|
-
if (!
|
|
21499
|
-
const text5 =
|
|
21249
|
+
if (!fs61.existsSync(card.file)) continue;
|
|
21250
|
+
const text5 = fs61.readFileSync(card.file, "utf8");
|
|
21500
21251
|
const stamped = stampLeavingIn(text5, day, stamp4);
|
|
21501
|
-
if (stamped !== text5)
|
|
21252
|
+
if (stamped !== text5) fs61.writeFileSync(card.file, stamped);
|
|
21502
21253
|
}
|
|
21503
21254
|
}
|
|
21504
21255
|
function unfinishedDelivery(id) {
|
|
@@ -21514,7 +21265,7 @@ function unfinishedDelivery(id) {
|
|
|
21514
21265
|
}[gap];
|
|
21515
21266
|
return `#${id} is not finished: ${what}.`;
|
|
21516
21267
|
}
|
|
21517
|
-
const missing = planDeliverables(card.body).filter((src) => src.startsWith(".assets/")).find((src) => !
|
|
21268
|
+
const missing = planDeliverables(card.body).filter((src) => src.startsWith(".assets/")).find((src) => !fs61.existsSync(path63.join(ASSETS, src.slice(".assets/".length))));
|
|
21518
21269
|
return missing ? `#${id} is not finished: ${missing} is not on this machine.` : null;
|
|
21519
21270
|
}
|
|
21520
21271
|
function agentDecisions(text5) {
|
|
@@ -21542,7 +21293,7 @@ function removeCard2(id, metric, options) {
|
|
|
21542
21293
|
if (unfinished) die(unfinished, { kind: "plan-delivery-unfinished" });
|
|
21543
21294
|
const dest = options.cleanupDiscarded && locateArchived(id) ? null : archiveDest(found2);
|
|
21544
21295
|
const cardFile3 = found2.kind === "group" ? path63.join(found2.target, "root.md") : found2.target;
|
|
21545
|
-
const cardText =
|
|
21296
|
+
const cardText = fs61.existsSync(cardFile3) ? fs61.readFileSync(cardFile3, "utf8") : "";
|
|
21546
21297
|
const { meta: cardMeta3 } = parseFrontmatter(cardText);
|
|
21547
21298
|
const alsoRemoved = found2.kind === "group" ? walkMd(found2.target).filter((f) => f !== cardFile3).map((f) => rel(f)).sort() : [];
|
|
21548
21299
|
const leftIds = leavingIds(id, found2);
|
|
@@ -21573,10 +21324,10 @@ function removeCard2(id, metric, options) {
|
|
|
21573
21324
|
const groupRoot = found2.kind === "file" && !options.closing ? enclosingGroupRoot(found2.target) : null;
|
|
21574
21325
|
if (dest) {
|
|
21575
21326
|
stampLeaving(leaving, metric === "rejected" ? { reason: options.reason?.trim() ?? "", discard: options.discard === true } : null);
|
|
21576
|
-
|
|
21577
|
-
|
|
21327
|
+
fs61.mkdirSync(ARCHIVE, { recursive: true });
|
|
21328
|
+
fs61.renameSync(found2.target, dest);
|
|
21578
21329
|
} else {
|
|
21579
|
-
|
|
21330
|
+
fs61.rmSync(found2.target, { recursive: true, force: true });
|
|
21580
21331
|
}
|
|
21581
21332
|
const removedRefs = stripReadmeRefs(found2);
|
|
21582
21333
|
let marked2 = null;
|
|
@@ -21627,11 +21378,12 @@ function removeCard2(id, metric, options) {
|
|
|
21627
21378
|
function closeGroup(rootFile) {
|
|
21628
21379
|
const rootId = idPrefix(path63.basename(path63.dirname(rootFile)));
|
|
21629
21380
|
if (rootId === null) return null;
|
|
21381
|
+
if (runnerProject(KANBAN)) return null;
|
|
21630
21382
|
if (!groupCloses(rootFile)) return null;
|
|
21631
21383
|
say(`
|
|
21632
21384
|
every subtask line on #${rootId} is resolved \u2014 closing the group:`);
|
|
21633
21385
|
try {
|
|
21634
|
-
const { meta, body } = parseFrontmatter(
|
|
21386
|
+
const { meta, body } = parseFrontmatter(fs61.readFileSync(rootFile, "utf8"));
|
|
21635
21387
|
const res = cmdRemove(rootId, "completed", { closing: true });
|
|
21636
21388
|
return {
|
|
21637
21389
|
id: rootId,
|
|
@@ -21698,6 +21450,7 @@ var init_remove = __esm({
|
|
|
21698
21450
|
"use strict";
|
|
21699
21451
|
init_deliveries();
|
|
21700
21452
|
init_store();
|
|
21453
|
+
init_tasks();
|
|
21701
21454
|
init_env();
|
|
21702
21455
|
init_creation_lock();
|
|
21703
21456
|
init_lock();
|
|
@@ -21773,7 +21526,7 @@ var init_memory_file = __esm({
|
|
|
21773
21526
|
});
|
|
21774
21527
|
|
|
21775
21528
|
// ../cli/src/commands/rule.ts
|
|
21776
|
-
import
|
|
21529
|
+
import fs62 from "node:fs";
|
|
21777
21530
|
import path64 from "node:path";
|
|
21778
21531
|
function cmdRule(askedName, flags) {
|
|
21779
21532
|
const agent = askedName.trim();
|
|
@@ -21791,7 +21544,7 @@ function readRuleText(file, text5) {
|
|
|
21791
21544
|
if (file !== void 0 && text5 !== void 0) die("pass --file or --text, not both");
|
|
21792
21545
|
if (file !== void 0) {
|
|
21793
21546
|
try {
|
|
21794
|
-
return
|
|
21547
|
+
return fs62.readFileSync(file, "utf8").trim();
|
|
21795
21548
|
} catch {
|
|
21796
21549
|
die(`can't read ${file} \u2014 write the rule to a file, then pass its path`);
|
|
21797
21550
|
}
|
|
@@ -21918,15 +21671,15 @@ var init_storyboard = __esm({
|
|
|
21918
21671
|
});
|
|
21919
21672
|
|
|
21920
21673
|
// ../cli/src/lib/spec-contract.ts
|
|
21921
|
-
import
|
|
21674
|
+
import fs63 from "node:fs";
|
|
21922
21675
|
import path65 from "node:path";
|
|
21923
21676
|
function snapshotSpecs() {
|
|
21924
21677
|
const out = /* @__PURE__ */ new Map();
|
|
21925
|
-
if (!
|
|
21678
|
+
if (!fs63.existsSync(TODO)) return out;
|
|
21926
21679
|
for (const file of walkMd(TODO)) {
|
|
21927
21680
|
if (isLegacyRecurring(path65.relative(TODO, file))) continue;
|
|
21928
21681
|
const id = idPrefix(path65.basename(file) === "root.md" ? path65.basename(path65.dirname(file)) : path65.basename(file));
|
|
21929
|
-
if (id !== null) out.set(file, { id, text:
|
|
21682
|
+
if (id !== null) out.set(file, { id, text: fs63.readFileSync(file, "utf8") });
|
|
21930
21683
|
}
|
|
21931
21684
|
return out;
|
|
21932
21685
|
}
|
|
@@ -22102,9 +21855,9 @@ function validateSpec(file, text5, id) {
|
|
|
22102
21855
|
}
|
|
22103
21856
|
function inside(dir, name) {
|
|
22104
21857
|
try {
|
|
22105
|
-
const root =
|
|
22106
|
-
const real2 =
|
|
22107
|
-
return real2.startsWith(root + path65.sep) &&
|
|
21858
|
+
const root = fs63.realpathSync(dir);
|
|
21859
|
+
const real2 = fs63.realpathSync(path65.join(dir, name));
|
|
21860
|
+
return real2.startsWith(root + path65.sep) && fs63.statSync(real2).isFile() ? real2 : null;
|
|
22108
21861
|
} catch {
|
|
22109
21862
|
return null;
|
|
22110
21863
|
}
|
|
@@ -22152,7 +21905,7 @@ function compareProblem(attrs, id) {
|
|
|
22152
21905
|
const file = inside(dir, named2.name);
|
|
22153
21906
|
if (!file) return { message: `Compare shots ${at} names no file in this card's asset folder. Write the JSON there or fix the path.` };
|
|
22154
21907
|
try {
|
|
22155
|
-
return { data: JSON.parse(
|
|
21908
|
+
return { data: JSON.parse(fs63.readFileSync(file, "utf8")) };
|
|
22156
21909
|
} catch {
|
|
22157
21910
|
return { message: `${at} is not valid JSON.` };
|
|
22158
21911
|
}
|
|
@@ -22321,7 +22074,7 @@ var init_mockups = __esm({
|
|
|
22321
22074
|
});
|
|
22322
22075
|
|
|
22323
22076
|
// ../cli/src/commands/spec-write.ts
|
|
22324
|
-
import
|
|
22077
|
+
import fs64 from "node:fs";
|
|
22325
22078
|
import path66 from "node:path";
|
|
22326
22079
|
function cmdSpecWrite(id, askedName, flags) {
|
|
22327
22080
|
const agent = findSpecAgent(askedName);
|
|
@@ -22331,10 +22084,10 @@ function cmdSpecWrite(id, askedName, flags) {
|
|
|
22331
22084
|
const found2 = locate(id);
|
|
22332
22085
|
if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
|
|
22333
22086
|
const file = found2.kind === "group" ? path66.join(found2.target, "root.md") : found2.target;
|
|
22334
|
-
const { meta, body } = parseFrontmatter(
|
|
22087
|
+
const { meta, body } = parseFrontmatter(fs64.readFileSync(file, "utf8"));
|
|
22335
22088
|
if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
|
|
22336
22089
|
const { body: next, replaced } = splice(body, name, section);
|
|
22337
|
-
|
|
22090
|
+
fs64.writeFileSync(file, serializeFrontmatter(meta) + "\n" + next);
|
|
22338
22091
|
say(`${replaced ? "rewrote" : "wrote"} the \`${name}\` section on #${id} (${rel(file)})`);
|
|
22339
22092
|
return { id, specAgent: name, replaced, file: rel(file) };
|
|
22340
22093
|
}
|
|
@@ -22343,7 +22096,7 @@ function readSection(file, text5) {
|
|
|
22343
22096
|
let raw;
|
|
22344
22097
|
if (file !== void 0) {
|
|
22345
22098
|
try {
|
|
22346
|
-
raw =
|
|
22099
|
+
raw = fs64.readFileSync(file, "utf8");
|
|
22347
22100
|
} catch {
|
|
22348
22101
|
die(`can't read ${file} \u2014 write your section to a file, then pass its path`);
|
|
22349
22102
|
}
|
|
@@ -22809,11 +22562,11 @@ var init_shared = __esm({
|
|
|
22809
22562
|
});
|
|
22810
22563
|
|
|
22811
22564
|
// ../cli/src/commands/idea.ts
|
|
22812
|
-
import
|
|
22565
|
+
import fs65 from "node:fs";
|
|
22813
22566
|
function load() {
|
|
22814
22567
|
const file = ideasFile();
|
|
22815
22568
|
if (!file) die("Ideas exist only inside a WildCrane idea conversation.");
|
|
22816
|
-
const held4 = JSON.parse(
|
|
22569
|
+
const held4 = JSON.parse(fs65.readFileSync(file, "utf8"));
|
|
22817
22570
|
return { file, held: { workflows: held4.workflows ?? [], ideas: held4.ideas ?? [] } };
|
|
22818
22571
|
}
|
|
22819
22572
|
function workflowOf(held4, asked) {
|
|
@@ -22835,7 +22588,7 @@ function splitBody2(text5) {
|
|
|
22835
22588
|
}
|
|
22836
22589
|
function readBody(bodyFile) {
|
|
22837
22590
|
if (!bodyFile) die("--body-file is required; write the idea in a writable temporary file first.");
|
|
22838
|
-
const text5 =
|
|
22591
|
+
const text5 = fs65.readFileSync(bodyFile, "utf8");
|
|
22839
22592
|
if (!text5.trim()) die("The idea must not be empty.");
|
|
22840
22593
|
return splitBody2(text5);
|
|
22841
22594
|
}
|
|
@@ -22847,7 +22600,7 @@ function named(held4, id) {
|
|
|
22847
22600
|
function cmdIdea(args, opts) {
|
|
22848
22601
|
const sub = args[0] ?? "";
|
|
22849
22602
|
const { file, held: held4 } = load();
|
|
22850
|
-
const write2 = () =>
|
|
22603
|
+
const write2 = () => fs65.writeFileSync(file, `${JSON.stringify(held4, null, 2)}
|
|
22851
22604
|
`);
|
|
22852
22605
|
if (sub === "new") {
|
|
22853
22606
|
const title = opts.title?.trim();
|
|
@@ -22916,7 +22669,7 @@ var board_exports = {};
|
|
|
22916
22669
|
__export(board_exports, {
|
|
22917
22670
|
buildBoardProgram: () => buildBoardProgram
|
|
22918
22671
|
});
|
|
22919
|
-
import
|
|
22672
|
+
import fs66 from "node:fs";
|
|
22920
22673
|
import path67 from "node:path";
|
|
22921
22674
|
async function dispatch(move2, cmd, args, opts, cli) {
|
|
22922
22675
|
const ctx = ctxOf(cmd, cli.program);
|
|
@@ -22943,7 +22696,7 @@ async function cloudCard(task, opts) {
|
|
|
22943
22696
|
let request;
|
|
22944
22697
|
if (typeof opts.bodyFile === "string") {
|
|
22945
22698
|
try {
|
|
22946
|
-
request =
|
|
22699
|
+
request = fs66.readFileSync(opts.bodyFile, "utf8");
|
|
22947
22700
|
} catch {
|
|
22948
22701
|
}
|
|
22949
22702
|
}
|
|
@@ -23097,7 +22850,7 @@ var init_board2 = __esm({
|
|
|
23097
22850
|
});
|
|
23098
22851
|
|
|
23099
22852
|
// ../cli/src/lib/board-cli.ts
|
|
23100
|
-
import
|
|
22853
|
+
import fs67 from "node:fs";
|
|
23101
22854
|
import path68 from "node:path";
|
|
23102
22855
|
function useBoard(found2, dirNamed) {
|
|
23103
22856
|
if (!found2.named && found2.board === path68.join(found2.root, "docs", "kanban")) setBoardRoot(found2.root, dirNamed);
|
|
@@ -23106,7 +22859,7 @@ function useBoard(found2, dirNamed) {
|
|
|
23106
22859
|
}
|
|
23107
22860
|
function whole(found2, move2) {
|
|
23108
22861
|
if (move2 === MAKES_A_BOARD) return found2;
|
|
23109
|
-
if (!
|
|
22862
|
+
if (!fs67.existsSync(path68.join(found2.board, "todo"))) {
|
|
23110
22863
|
die(`the board in ${found2.board} has no todo/ \u2014 run \`init\` to add what is missing.`, {
|
|
23111
22864
|
kind: "board-incomplete",
|
|
23112
22865
|
dir: found2.root
|
|
@@ -23130,10 +22883,10 @@ function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
|
|
|
23130
22883
|
const named2 = board2 ?? boardFromEnv();
|
|
23131
22884
|
if (named2) {
|
|
23132
22885
|
const folder = path68.resolve(cwd, named2);
|
|
23133
|
-
if (!
|
|
22886
|
+
if (!fs67.existsSync(folder) && move2 !== MAKES_A_BOARD) {
|
|
23134
22887
|
die(`no such folder: ${folder}`, { kind: "no-such-folder", dir: folder });
|
|
23135
22888
|
}
|
|
23136
|
-
if (
|
|
22889
|
+
if (fs67.existsSync(folder) && !isBoardDir(folder) && move2 !== MAKES_A_BOARD) {
|
|
23137
22890
|
die(`${folder} is not a board \u2014 it has no todo/ and config.md. Run ${installHint} --board ${named2} to make one.`, {
|
|
23138
22891
|
kind: "no-board",
|
|
23139
22892
|
dir: folder
|
|
@@ -23143,7 +22896,7 @@ function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
|
|
|
23143
22896
|
}
|
|
23144
22897
|
if (dir !== null) {
|
|
23145
22898
|
const root = path68.resolve(dir);
|
|
23146
|
-
if (!
|
|
22899
|
+
if (!fs67.existsSync(root)) die(`no such folder: ${root}`, { kind: "no-such-folder", dir: root });
|
|
23147
22900
|
if (!hasBoard(root) && move2 !== MAKES_A_BOARD) {
|
|
23148
22901
|
die(`no board in ${root} \u2014 it has no docs/kanban/. Run ${installHint} there to make one.`, {
|
|
23149
22902
|
kind: "no-board",
|
|
@@ -23240,8 +22993,8 @@ var init_board_cli = __esm({
|
|
|
23240
22993
|
init_pointer();
|
|
23241
22994
|
init_tasks();
|
|
23242
22995
|
MAKES_A_BOARD = "init";
|
|
23243
|
-
hasBoard = (dir) =>
|
|
23244
|
-
isBoardDir = (dir) =>
|
|
22996
|
+
hasBoard = (dir) => fs67.existsSync(path68.join(dir, "docs", "kanban"));
|
|
22997
|
+
isBoardDir = (dir) => fs67.existsSync(path68.join(dir, "todo")) && fs67.existsSync(path68.join(dir, "config.md"));
|
|
23245
22998
|
boardFromEnv = () => {
|
|
23246
22999
|
const named2 = process.env.AI4KANBAN_BOARD?.trim();
|
|
23247
23000
|
return named2 ? named2 : null;
|
|
@@ -23269,7 +23022,7 @@ var init_archive2 = __esm({
|
|
|
23269
23022
|
var board_default;
|
|
23270
23023
|
var init_board3 = __esm({
|
|
23271
23024
|
"../cli/src/guide/board.md"() {
|
|
23272
|
-
board_default = "# How this board works\n\nThese rules apply to every card workflow. Read them once; each workflow's guide covers\nthe remaining details.\n\n## Layout\n\n```\ndocs/kanban/\n\u251C\u2500\u2500 todo/ open tasks\n\u2502 \u251C\u2500\u2500 README.md the index \u2014 read it first\n\u2502 \u2514\u2500\u2500 <id>-<slug>.md\n\u2502 one card per file; a group keeps its cards in one folder\n\u251C\u2500\u2500 memory/ all memory \u2014 see \"Who owns a memory file\"\n\u2502 \u251C\u2500\u2500 readme.md what shipped \u2014 the board's own record\n\u2502 \u251C\u2500\u2500 project.md what the project is today \u2014 written only by `akb describe-project`\n\u2502 \u251C\u2500\u2500 <name>/ a global memory: `MEMORY.md` (its rules, unless built in), `README.md`\n\u2502 \u2502 (its index) and its entries\n\u2502 \u2514\u2500\u2500 agents/ one folder per agent that keeps memory, named after it \u2014\n\u2502 `planner/` holds `decisions.md`, `rejected.md`, `redesign.md`\n\u251C\u2500\u2500 rules/ `<agent>.md` \u2014 the user's rule for one agent, appended to every run it\n\u2502 does; written from the board UI or `akb raw rule`\n\u251C\u2500\u2500 triage/ items waiting to be sorted, one file each \u2014 not cards. Only\n\u2502 \u2502 Cloud's sort or Refine idea on the web's Triage page turns one\n\u2502 \u2502 into a card; `akb triage check <source-id>` finds an existing one\n\u2502 \u251C\u2500\u2500 archived/ items a card was made of\n\u2502 \u251C\u2500\u2500 dismissed/ items ignored, with the reason \u2014 `akb triage restore` puts one back\n\u2502 \u2514\u2500\u2500 files/ dropped-in files\n\u2514\u2500\u2500 modules.md one line per module \u2014 `akb guide module-map` writes it\n```\n\n## Task ID\n\nEvery task's id is the number at the front of its filename (`04-plan-cap-enforcement.md` \u2192\nid 4): its Cloud task's number (`WC-4`). Only Cloud assigns one; `akb raw create` takes it.\n\n## Never hand-write a card's frontmatter\n\n`akb raw create`, `update`, and `update-questions` manage all of a card's frontmatter,\nincluding its `previews`. Edit only the card's **body** by hand. `akb raw help`\nlists all operations; `akb raw help <move>` explains one operation.\n\n## Inside a board run\n\nA board run carries `KANBAN_RUN` in its environment. Never unset or bypass it (for example\n`env -u KANBAN_RUN`): when a command only prints a flow, follow the printed flow.\n\n## Group task\n\nA group lives in `todo/<id>-<slug>/`: `root.md` tracks the whole job and each subtask is its\nown card beside it; `akb guide add-task` says how to build one. The root's `## Todo` lists the\nsubtasks, one line each ending in `#<subid>`: archiving a subtask ticks its line, rejecting\none strikes it through, and resolving the last line archives the root in the same run, with\nany open question or unticked todo of its own. A root listing no subtasks is
|
|
23025
|
+
board_default = "# How this board works\n\nThese rules apply to every card workflow. Read them once; each workflow's guide covers\nthe remaining details.\n\n## Layout\n\n```\ndocs/kanban/\n\u251C\u2500\u2500 todo/ open tasks\n\u2502 \u251C\u2500\u2500 README.md the index \u2014 read it first\n\u2502 \u2514\u2500\u2500 <id>-<slug>.md\n\u2502 one card per file; a group keeps its cards in one folder\n\u251C\u2500\u2500 memory/ all memory \u2014 see \"Who owns a memory file\"\n\u2502 \u251C\u2500\u2500 readme.md what shipped \u2014 the board's own record\n\u2502 \u251C\u2500\u2500 project.md what the project is today \u2014 written only by `akb describe-project`\n\u2502 \u251C\u2500\u2500 <name>/ a global memory: `MEMORY.md` (its rules, unless built in), `README.md`\n\u2502 \u2502 (its index) and its entries\n\u2502 \u2514\u2500\u2500 agents/ one folder per agent that keeps memory, named after it \u2014\n\u2502 `planner/` holds `decisions.md`, `rejected.md`, `redesign.md`\n\u251C\u2500\u2500 rules/ `<agent>.md` \u2014 the user's rule for one agent, appended to every run it\n\u2502 does; written from the board UI or `akb raw rule`\n\u251C\u2500\u2500 triage/ items waiting to be sorted, one file each \u2014 not cards. Only\n\u2502 \u2502 Cloud's sort or Refine idea on the web's Triage page turns one\n\u2502 \u2502 into a card; `akb triage check <source-id>` finds an existing one\n\u2502 \u251C\u2500\u2500 archived/ items a card was made of\n\u2502 \u251C\u2500\u2500 dismissed/ items ignored, with the reason \u2014 `akb triage restore` puts one back\n\u2502 \u2514\u2500\u2500 files/ dropped-in files\n\u2514\u2500\u2500 modules.md one line per module \u2014 `akb guide module-map` writes it\n```\n\n## Task ID\n\nEvery task's id is the number at the front of its filename (`04-plan-cap-enforcement.md` \u2192\nid 4): its Cloud task's number (`WC-4`). Only Cloud assigns one; `akb raw create` takes it.\n\n## Never hand-write a card's frontmatter\n\n`akb raw create`, `update`, and `update-questions` manage all of a card's frontmatter,\nincluding its `previews`. Edit only the card's **body** by hand. `akb raw help`\nlists all operations; `akb raw help <move>` explains one operation.\n\n## Inside a board run\n\nA board run carries `KANBAN_RUN` in its environment. Never unset or bypass it (for example\n`env -u KANBAN_RUN`): when a command only prints a flow, follow the printed flow.\n\n## Group task\n\nA group lives in `todo/<id>-<slug>/`: `root.md` tracks the whole job and each subtask is its\nown card beside it; `akb guide add-task` says how to build one. The root's `## Todo` lists the\nsubtasks, one line each ending in `#<subid>`: archiving a subtask ticks its line, rejecting\none strikes it through, and resolving the last line archives the root in the same run, with\nany open question or unticked todo of its own. A board a Runner keeps leaves that close to\nCloud, which waits for the root to be queued with no step on it. A root listing no subtasks is\nonly closed by hand.\n\n## Who owns a memory file\n\n**A memory file belongs to whoever reads and writes it.**\n\n- **`memory/readme.md`**: the board's record of shipped user-facing work.\n- **`memory/project.md`**: what the project is today, from its users' side, and the\n direction every judgement serves. Only `akb describe-project` writes it.\n- **`memory/agents/planner/`**: `decisions.md` holds user-facing answers that guide future\n planning and `redesign.md` design mistakes to avoid, both written by the memory review (`akb\n guide review-memory`); `rejected.md` holds what the user turned down and why, written only\n by the rejection and dismissal review (`akb guide review-rejections`). Other flows only read\n them, `akb prune-memory` aside.\n- **`memory/<name>/`**: a global memory, kept by every agent listing it under `akb.memory`\n by the rules each run is given. Only the user changes its `MEMORY.md`, from the board UI.\n- **`memory/agents/<agent>/`**: an agent keeps only the files and split paths its own AGENT.md\n declares. An agent whose instructions name none keeps none.\n\nFile a planning note about one module in that module's folder,\n`memory/agents/planner/<module>/`, under the same file name, creating it if missing. A note\nspanning modules, or from a card with none, stays in `memory/agents/planner/`.\n\n### What earns a note\n\nOnly what changes a future planning choice. Writing nothing is a complete outcome.\n\n- **Honor an opt-out**: told not to record, write no memory and finish the requested action.\n- **A conversation writes none**: a card chat, a discussion, a feedback conversation, and\n any flow started in it write no memory and say nothing about it.\n- **Require lasting value**: a durable preference, constraint, decision, or lesson.\n- **Skip housekeeping**: duplicates, routine status changes, and facts recorded elsewhere.\n A rejected duplicate is not a rejected feature.\n- **Merge, don't repeat**: rewrite an equivalent entry in place.\n\n`readme.md` is not planning memory.\n";
|
|
23273
23026
|
}
|
|
23274
23027
|
});
|
|
23275
23028
|
|
|
@@ -23301,7 +23054,7 @@ var init_describe_project = __esm({
|
|
|
23301
23054
|
var discuss_idea_default;
|
|
23302
23055
|
var init_discuss_idea = __esm({
|
|
23303
23056
|
"../cli/src/guide/discuss-idea.md"() {
|
|
23304
|
-
discuss_idea_default = "# Help decide what is worth building\n\nYou are this workspace's **Idea helper**. Help the user decide what to build and shape it into\nideas they start as tasks. Read the current project's code and board when it helps, but never\nchange a file. Each turn takes one of three branches.\n\n## Draft ideas (default)\n\nWork worth doing gets an idea
|
|
23057
|
+
discuss_idea_default = "# Help decide what is worth building\n\nYou are this workspace's **Idea helper**. Help the user decide what to build and shape it into\nideas they start as tasks. Read the current project's code and board when it helps, but never\nchange a file. Each turn takes one of three branches.\n\n## Draft ideas (default)\n\nWork worth doing gets an idea.\n\n- **Draft at once**: a clear ask gets a complete idea on the first turn, with\n `akb raw idea new --title \"<title>\" --body-file <draft>`; update it on later turns with\n `akb raw idea save --id <idea> --body-file <draft>`. Report it only after the command confirms.\n- **Idea shape**: in the user's language, the outcome in at most 150 words, then a\n `## Done when` list of checks a reviewer can verify. No technical design.\n- **Pick the workflow**: add `--workflow <id>` naming the workflow in `akb workflow list` that\n does the work, judged by the work and not the topic, and `coding` when none fits; use the\n one the user names or picks. Call it by its listed name in the user's language.\n- **One idea per deliverable**: give separate deliverables separate ideas, and drop a replaced\n one with `akb raw idea drop --id <idea>`.\n- **Settle what you can**: decide the outcome questions you can judge and say which calls you\n made; ask only what the user alone can answer and would change the outcome, with the option\n you recommend.\n- **Finish the turn**: save the ideas and stop; never end by asking them to confirm or press\n **Start**.\n\n## Start on a go\n\nWhen the user tells you to go ahead, start the ideas they mean with\n`akb raw idea start --id <idea>`, drafting them first when needed, and say which tasks started.\n\n## Just answer\n\nA question, an idea not yet clear enough to draft, or a doubt about whether it is worth doing\ngets an answer, with no idea.\n\n- **Answer first**: say whether it is worth doing and why; disagreeing, advising a pause, or\n asking for evidence is a complete answer. Read a reliable source before stating an external\n fact.\n\n## Always\n\n- **Hide machinery**: never mention plans, files, paths, commands, or the tools and connectors\n you run with, and refer to actions by their button labels.\n";
|
|
23305
23058
|
}
|
|
23306
23059
|
});
|
|
23307
23060
|
|
|
@@ -23317,7 +23070,7 @@ var init_evaluate_task = __esm({
|
|
|
23317
23070
|
var follow_up_default;
|
|
23318
23071
|
var init_follow_up = __esm({
|
|
23319
23072
|
"../cli/src/guide/follow-up.md"() {
|
|
23320
|
-
follow_up_default = '# Handle follow-up work\n\nUse when planning or implementation discovers additional work.\n\n- **
|
|
23073
|
+
follow_up_default = '# Handle follow-up work\n\nUse when planning or implementation discovers additional work.\n\n- **Current task**: finish required work on this card; queue independent follow-ups in triage, then resume the original task.\n- **Existing card**: when only another open card needs changing, run `akb card revise <id> "<the change and why>"`.\n- **Queue it**: use the command below; skip an item the command reports as already there.\n- **Workflow**: use the source card\'s `workflow:`, or the board default shown by `akb workflow` when it has none.\n\n```text\nakb triage add --title "<one line>" --slug <short-english-slug> --source "#<id>" --workflow <workflow> --text "<body>"\n```\n\n## Write the item\n\n- **Reader**: someone who has read neither the original card nor the code can decide in ten seconds whether the work is worth doing.\n- **Order**: first explain what happened and what the user runs into, then say what the work would do.\n- **Plain words**: keep file, function and field names, card ids, agent names and evidence trails out of the title and body.\n';
|
|
23321
23074
|
}
|
|
23322
23075
|
});
|
|
23323
23076
|
|
|
@@ -23349,7 +23102,7 @@ var init_prune_memory = __esm({
|
|
|
23349
23102
|
var review_memory_default;
|
|
23350
23103
|
var init_review_memory = __esm({
|
|
23351
23104
|
"../cli/src/guide/review-memory.md"() {
|
|
23352
|
-
review_memory_default = "# Review what the conversations settled\n\nRead each conversation given to you right through, then write what it settled into memory.\n\nChats and planning flows write no memory themselves. One turn cannot see where an exchange\nis going, so a \"what if\" thrown out and taken back three turns later used to land as a\ndecision. You read the whole exchange, so you can tell a decision from a passing thought \u2014\nand that judgement is the whole job.\n\n- **Read what you are given**: each card comes in its own `<conversation>` block: its summary,\n its `Worth noting` and `Overruled by the user` lines, then its chat and the comments the user\n left reviewing it, when it had any, the user's words as written and the agent's replies,\n oldest first. The card is archived, so it is complete and reviewed this once; open no card or\n transcript file.\n- **Keep them apart**: judge each block by itself; what one conversation settled never\n becomes a note for another's card or agents.\n- **Hold to the same bar**: \"What earns a note\" in `akb guide board` governs what you write,\n exactly as it governs a flow that writes one. A conversation that settled nothing earns no\n note, and most do not \u2014 writing nothing is a complete result.\n- **Only what the user settled**: a durable preference, constraint, decision or correction\n they stated. Your own proposal is not their decision, and neither is a question, a \"what\n if\", or a direction the conversation then moved off.\n- **Take the last word**: where they said one thing and then another, the later one stands.\n Where a note already in the file says what they have since overturned, rewrite that note in\n place \u2014 never leave both and never add a second.\n- **Honour \"don't record this\"**: told in the conversation not to record something, write no\n note for it. If one is already there from an earlier review, delete it now.\n- **Where it goes**: a durable choice goes in the planner's `decisions.md` as one\n `**<key>**: <call>` line under the topic that fits, without its rationale; a design lesson\n goes in its `redesign.md` under the relevant topic as\n `- \u274C **<mistake>** \u2192 \u2705 <what the design should be instead, one line>.`, written as guidance\n for future tasks, not a history of the fix. Both go in the folder of the module each\n conversation is listed with (`akb guide board`). Each conversation also lists the agents\n whose sections its card holds and the memory files each keeps: where what was settled lands\n on one agent's own work, it goes in that agent's memory instead. Read that agent's AGENT.md\n (`akb raw agent-file <agent> AGENT.md`) only then, and keep its memory the way it says.\n Distill settled feedback into reusable preferences (dos) and corrections (don'ts), retaining\n their scope; never copy raw reviews. An agent whose instructions name no memory gets no new file.\n- **What the card settled**: the summary says what the card was about and earns no note. A\n `Worth noting` line is a decision the user approved, by answering it or letting it stand, so\n it can be a choice but never a correction, and it earns a planner note only where the user's\n own words in the conversation back it; a line under\n `Overruled by the user` is a call the user reversed
|
|
23105
|
+
review_memory_default = "# Review what the conversations settled\n\nRead each conversation given to you right through, then write what it settled into memory.\n\nChats and planning flows write no memory themselves. One turn cannot see where an exchange\nis going, so a \"what if\" thrown out and taken back three turns later used to land as a\ndecision. You read the whole exchange, so you can tell a decision from a passing thought \u2014\nand that judgement is the whole job.\n\n- **Read what you are given**: each card comes in its own `<conversation>` block: its summary,\n its `Worth noting` and `Overruled by the user` lines, then its chat and the comments the user\n left reviewing it, when it had any, the user's words as written and the agent's replies,\n oldest first. The card is archived, so it is complete and reviewed this once; open no card or\n transcript file.\n- **Keep them apart**: judge each block by itself; what one conversation settled never\n becomes a note for another's card or agents.\n- **Hold to the same bar**: \"What earns a note\" in `akb guide board` governs what you write,\n exactly as it governs a flow that writes one. A conversation that settled nothing earns no\n note, and most do not \u2014 writing nothing is a complete result.\n- **Only what the user settled**: a durable preference, constraint, decision or correction\n they stated. Your own proposal is not their decision, and neither is a question, a \"what\n if\", or a direction the conversation then moved off.\n- **Take the last word**: where they said one thing and then another, the later one stands.\n Where a note already in the file says what they have since overturned, rewrite that note in\n place \u2014 never leave both and never add a second.\n- **Honour \"don't record this\"**: told in the conversation not to record something, write no\n note for it. If one is already there from an earlier review, delete it now.\n- **Where it goes**: a durable choice goes in the planner's `decisions.md` as one\n `**<key>**: <call>` line under the topic that fits, without its rationale; a design lesson\n goes in its `redesign.md` under the relevant topic as\n `- \u274C **<mistake>** \u2192 \u2705 <what the design should be instead, one line>.`, written as guidance\n for future tasks, not a history of the fix. Both go in the folder of the module each\n conversation is listed with (`akb guide board`). Each conversation also lists the agents\n whose sections its card holds and the memory files each keeps: where what was settled lands\n on one agent's own work, it goes in that agent's memory instead. Read that agent's AGENT.md\n (`akb raw agent-file <agent> AGENT.md`) only then, and keep its memory the way it says.\n Distill settled feedback into reusable preferences (dos) and corrections (don'ts), retaining\n their scope; never copy raw reviews. An agent whose instructions name no memory gets no new file.\n- **What the card settled**: the summary says what the card was about and earns no note. A\n `Worth noting` line is a decision the user approved, by answering it or letting it stand, so\n it can be a choice but never a correction, and it earns a planner note only where the user's\n own words in the conversation back it; a line under\n `Overruled by the user` is a call the user reversed, so it goes in `redesign.md`: that of\n the agent whose section it came from, or the planner's when none did.\n- **Change nothing else**: no card, no open question, no code. The memory\n files are the whole of what this run writes.\n- **Say what you wrote**: finish by reporting each conversation you read, and for each the\n notes you added, rewrote, deleted, or that you wrote none.\n";
|
|
23353
23106
|
}
|
|
23354
23107
|
});
|
|
23355
23108
|
|
|
@@ -23373,7 +23126,7 @@ var init_refine = __esm({
|
|
|
23373
23126
|
var reflect_default;
|
|
23374
23127
|
var init_reflect = __esm({
|
|
23375
23128
|
"../cli/src/guide/reflect.md"() {
|
|
23376
|
-
reflect_default =
|
|
23129
|
+
reflect_default = '# Reflect on completed cards\n\n## Read\n\nRead each given `<card>` in full and check its own `shipped` content; keep cards separate.\nFind needs the work left behind or revealed, and verify them in the project. Proposing nothing is fine.\n\n## Filter\n\n- **Duplicates**: check `akb raw list` and `akb triage check <source-id>`; propose shared work only once.\n- **Exclude**: skip unsupported guesses, work covered by `<scheduled-agents>`, work rejected in `docs/kanban/memory/agents/planner/rejected.md` or its module folders, and calls the user reversed, in the `redesign.md` beside each of those files or under a card\'s `Overruled by the user`.\n- **Existing cards**: when shipped work invalidates an open card\'s plan, run `akb card revise <id> "<the change and why>"` instead of proposing an item.\n\n## Write\n\n- **Reader**: someone who has read neither the original card nor the code can decide in ten seconds whether the work is worth doing.\n- **Order**: first explain what happened and what the user runs into, then say what the work would do.\n- **Plain words**: keep file, function and field names, card ids, agent names and evidence trails out of the title and body.\n\nWrite the body to a temporary Markdown file and end with the source card\'s archive path.\nWrite one triage item per proposal. Exploratory ideas are welcome; identify small chores as such.\nUse the source card\'s `workflow:`, or the board default shown by `akb workflow` when it has none.\n\n```text\nakb triage add --title "<one line>" --slug <short-english-slug> --source "#<id>" --workflow <workflow> --file <body.md>\n```\n\nBriefly report what you proposed and what you skipped, with reasons.\n';
|
|
23377
23130
|
}
|
|
23378
23131
|
});
|
|
23379
23132
|
|
|
@@ -23429,7 +23182,7 @@ var init_write_agent = __esm({
|
|
|
23429
23182
|
var writing_default;
|
|
23430
23183
|
var init_writing = __esm({
|
|
23431
23184
|
"../cli/src/guide/writing.md"() {
|
|
23432
|
-
writing_default = '# Write a card\n\nA card has a **human half** for review and a folded **agent half** for implementation.\nEvery flow writes a card in this order:\n\n```text\n
|
|
23185
|
+
writing_default = '# Write a card\n\nA card has a **human half** for review and a folded **agent half** for implementation.\nEvery flow writes a card in this order:\n\n```text\n<\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>\n\n## Worth noting <- what whoever builds it must not miss; keep empty when none\n- **<question the decision settles>**: <answer>\n\n## Worth noting after implementation\n- **<question the decision settles>**: <answer>\n <- answered material decisions building turned up; written\n by the build, omit when empty. Never approved delivery scope\n\n## By `<name>` agent <- every preview agent\'s section\n\n<!-- agent --> <- boundary\n\n## Today\n## Scope <- requirements, not rationale\n## Todo <- one build step per checkbox\n## Decided by the agent\n- **<question the decision settles>**: <answer>\n### Overruled by the user <- always last\n```\n\n## `Worth noting`\n\n- **What goes here**: what whoever builds the card must not miss \u2014 the user\'s answers first,\n then a decision that passes the material-decision test in `akb guide update-questions`; a lasting\n answer that fails it goes below the human half.\n- **Keep it glanceable**: one entry is one reviewer decision, written as one sentence that\n states the chosen behavior and its main cost. Merge entries governed by one policy. One\n to three independent entries is normal.\n- **Cut the story**: omit chronology, evidence trails, exhaustive consequences, and the\n rejected option unless it is necessary to understand the main cost.\n\n## `Worth noting after implementation`\n\n- **What goes here**: the same, for what the build surfaced and settled.\n- **Keep it non-blocking**: this section never changes or blocks a delivery already in\n flight.\n- **Use the same compact format**: one reviewer decision and its main cost in one sentence.\n- **Exclude work logs**: never put diagnostics, run history, check results, suggestions,\n or unresolved questions here.\n- **Never approve a deviation here**: work that contradicts the approved requirements must\n be fixed or sent to the user by the build.\n\n## Agent sections\n\n- **Owner\'s rules first**: before rewriting, removing, or overruling content a preview agent\n owns, read that agent\'s rules with `akb raw agent-file <agent> AGENT.md`, and the\n references they point to for the affected part with the same command and their path. When\n they cannot be read, surface the gap before changing that content.\n\n## `Today`\n\n- **Omit by default**: write it, heading included, only when the build depends on findings\n the code does not make obvious; a small change to a few simple files needs none.\n\n## `Todo`\n\n- **Build steps only**: finish planning on the card; never defer it to a todo.\n- **Preserve completed work**: never edit, delete, or untick a checked todo; append a todo to\n reverse it.\n\n## `Decided by the agent`\n\n- **Only what changes the build**: include answered decisions the builder would otherwise\n get wrong and neither `Worth noting` section holds; omit a finding that needs no action.\n- **Preserve superseded calls**: move agent or review decisions the user reverses to\n `Overruled by the user`; delete one the card leaves the builder no room to repeat.\n\n## General writing requirements\n\n- **Keep section titles fixed**: write every `##` and `###` section title in English, never\n rename or translate an existing one, and leave empty scaffold sections in place.\n- **Edit only the body**: use board commands for every frontmatter field (`akb guide board`).\n- **File name**: when the card\'s file name does not say in English what the card is, rename\n it with `akb raw update <id> --slug <short-english-slug>`; leave a group\'s root as it is.\n- **Name the source in frontmatter**: pass each plan path, card `#id`, or URL the card came\n from as `--source` to `akb raw create`, or `--add-source` to `akb raw update`; never write\n a `## Source` section.\n- **Write for a fresh reader**: use professional, comprehensible language with no assumed\n conversation context.\n- **Keep human half self-contained**: nothing above `<!-- agent -->` may rely on the folded\n agent half.\n- **Keep bullets atomic**: use `- **<short title>**: <one clear sentence>` for one rule per\n bullet.\n- **Specify behavior**: omit planning notes and unnecessary coding details.\n- **Supported embeds**: only valid `<Asset>`, `<Compare>` and `<Storyboard>` markers alone in a\n paragraph embed content; other HTML or JSX is not allowed. Inline or fenced examples remain text.\n- **Compare**: to show two images or two videos against each other, such as a source and its\n clone, write `<Compare a="<src>" b="<src>" label="..." a-label="..." b-label="..." />`;\n never render a side-by-side file for it. For videos, add `shots="<src>"`: a JSON list of\n `{ "id": "S1", "start": 0, "end": 2.25, "name": "..." }` covering video A in order, or a\n storyboard of A. B may be a storyboard instead of a video; it then shows its shot with the\n id A is on, beside that shot of a storyboard `shots`.\n- **Asset label and alt**: `label` is a tag of a few words; an image\'s caption goes in its\n optional `alt="..."`, its shipped alt text, which cannot contain `<`, `>` or its own quote mark.\n- **Asset variants**: versions of the same content, such as languages or devices, are one tag\n with `variants=\'{"<label>": "<src>", ...}\'`, `src` being the one shown first; alternatives the\n user picks between stay separate tags.\n- **Say each fact once**: a section holds only what the card does not already say; leave it\n empty rather than restate the opening paragraph or another section. Cut only the repeated\n part of a line: a name, value or case stated nowhere else stays.\n\n- **Validate after edits**: run `akb raw validate <id>` for every card you wrote. Fix each\n reported file and line before finishing; validation does not rewrite the card for you.\n';
|
|
23433
23186
|
}
|
|
23434
23187
|
});
|
|
23435
23188
|
|
|
@@ -23517,7 +23270,7 @@ var init_guide = __esm({
|
|
|
23517
23270
|
});
|
|
23518
23271
|
|
|
23519
23272
|
// ../cli/src/lib/agent/approve.ts
|
|
23520
|
-
import
|
|
23273
|
+
import fs73 from "node:fs";
|
|
23521
23274
|
import path74 from "node:path";
|
|
23522
23275
|
function approvalRequest(req) {
|
|
23523
23276
|
const id = req.id;
|
|
@@ -23537,11 +23290,11 @@ function advancePhase(id) {
|
|
|
23537
23290
|
const found2 = locate(id);
|
|
23538
23291
|
if (!card || !found2 || !awaitingApproval(card)) return { error: `#${id} is no longer waiting on approval.` };
|
|
23539
23292
|
const file = found2.kind === "group" ? path74.join(found2.target, "root.md") : found2.target;
|
|
23540
|
-
const { meta, body } = parseFrontmatter(
|
|
23293
|
+
const { meta, body } = parseFrontmatter(fs73.readFileSync(file, "utf8"));
|
|
23541
23294
|
if (!meta) return { error: `#${id} has no frontmatter` };
|
|
23542
23295
|
meta.phase = card.stage + 1;
|
|
23543
23296
|
meta.status = "todo";
|
|
23544
|
-
|
|
23297
|
+
fs73.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
|
|
23545
23298
|
return null;
|
|
23546
23299
|
}
|
|
23547
23300
|
var init_approve = __esm({
|
|
@@ -23661,7 +23414,7 @@ var init_start = __esm({
|
|
|
23661
23414
|
});
|
|
23662
23415
|
|
|
23663
23416
|
// ../cli/src/lib/agent/task-chats.ts
|
|
23664
|
-
import
|
|
23417
|
+
import fs74 from "node:fs";
|
|
23665
23418
|
import path75 from "node:path";
|
|
23666
23419
|
function taskChatFile(task, home3) {
|
|
23667
23420
|
if (!TASK.test(task)) throw new Error(`${task} is not a task id.`);
|
|
@@ -23670,7 +23423,7 @@ function taskChatFile(task, home3) {
|
|
|
23670
23423
|
function readTaskChat(task, home3) {
|
|
23671
23424
|
let text5;
|
|
23672
23425
|
try {
|
|
23673
|
-
text5 =
|
|
23426
|
+
text5 = fs74.readFileSync(taskChatFile(task, home3), "utf8");
|
|
23674
23427
|
} catch {
|
|
23675
23428
|
return [];
|
|
23676
23429
|
}
|
|
@@ -23696,7 +23449,7 @@ var init_task_chats = __esm({
|
|
|
23696
23449
|
});
|
|
23697
23450
|
|
|
23698
23451
|
// ../cli/src/lib/agent/memory-review.ts
|
|
23699
|
-
import
|
|
23452
|
+
import fs75 from "node:fs";
|
|
23700
23453
|
import path76 from "node:path";
|
|
23701
23454
|
function taskChatsToReview(tasks) {
|
|
23702
23455
|
const out = [];
|
|
@@ -23711,7 +23464,7 @@ function taskChatsToReview(tasks) {
|
|
|
23711
23464
|
}
|
|
23712
23465
|
const messages = lines.map((l) => ({ role: l.role === "user" ? "you" : "agent", text: l.text }));
|
|
23713
23466
|
const review = handed(task, messages, card);
|
|
23714
|
-
if (review.settled
|
|
23467
|
+
if (review.settled) out.push(review);
|
|
23715
23468
|
}
|
|
23716
23469
|
return out;
|
|
23717
23470
|
}
|
|
@@ -23720,7 +23473,7 @@ function readArchived2(id) {
|
|
|
23720
23473
|
if (!found2) return null;
|
|
23721
23474
|
try {
|
|
23722
23475
|
const file = found2.kind === "group" ? path76.join(found2.target, "root.md") : found2.target;
|
|
23723
|
-
const { meta, body } = parseFrontmatter(
|
|
23476
|
+
const { meta, body } = parseFrontmatter(fs75.readFileSync(file, "utf8"));
|
|
23724
23477
|
if (!meta) return null;
|
|
23725
23478
|
return { id, title: meta.title, modules: meta.modules ?? [], rejected: meta.rejected, body };
|
|
23726
23479
|
} catch {
|
|
@@ -23788,9 +23541,74 @@ var init_memory_review = __esm({
|
|
|
23788
23541
|
}
|
|
23789
23542
|
});
|
|
23790
23543
|
|
|
23791
|
-
// ../cli/src/lib/agent/
|
|
23792
|
-
import
|
|
23544
|
+
// ../cli/src/lib/agent/dismissal-review.ts
|
|
23545
|
+
import fs76 from "node:fs";
|
|
23793
23546
|
import path77 from "node:path";
|
|
23547
|
+
function rejectionsToReview() {
|
|
23548
|
+
const dir = path77.join(ARCHIVE, REJECTIONS_TO_REVIEW);
|
|
23549
|
+
if (!fs76.existsSync(dir)) return [];
|
|
23550
|
+
const found2 = [];
|
|
23551
|
+
for (const file of walkMd(dir)) {
|
|
23552
|
+
const id = archivedId(file);
|
|
23553
|
+
if (id === null) continue;
|
|
23554
|
+
try {
|
|
23555
|
+
const { meta } = parseFrontmatter(fs76.readFileSync(file, "utf8"));
|
|
23556
|
+
if (!meta?.rejected || meta.rejected_by !== "user" || !meta.rejected_reason.trim()) continue;
|
|
23557
|
+
found2.push({ id, title: meta.title, file: rel(file), reason: meta.rejected_reason.trim(), rejectedAt: meta.rejected_at });
|
|
23558
|
+
} catch {
|
|
23559
|
+
continue;
|
|
23560
|
+
}
|
|
23561
|
+
}
|
|
23562
|
+
return found2.sort((a, b) => a.rejectedAt.localeCompare(b.rejectedAt) || a.id - b.id);
|
|
23563
|
+
}
|
|
23564
|
+
function dismissalsToReview() {
|
|
23565
|
+
return readAllDismissed().filter((s) => s.dismissedBy === "user" && s.dismissedReason.trim()).sort((a, b) => a.dismissedAt.localeCompare(b.dismissedAt)).map((s) => ({
|
|
23566
|
+
sourceId: s.sourceId,
|
|
23567
|
+
title: s.title,
|
|
23568
|
+
file: s.relPath,
|
|
23569
|
+
reason: s.dismissedReason.trim(),
|
|
23570
|
+
dismissedAt: s.dismissedAt
|
|
23571
|
+
}));
|
|
23572
|
+
}
|
|
23573
|
+
function citedSources(text5) {
|
|
23574
|
+
const ids = /* @__PURE__ */ new Set();
|
|
23575
|
+
for (const line of text5.split("\n")) {
|
|
23576
|
+
if (!/^\s*[-*]\s/.test(line)) continue;
|
|
23577
|
+
const m = line.match(SOURCES);
|
|
23578
|
+
for (const id of m?.[1].split(",") ?? []) if (id.trim()) ids.add(id.trim());
|
|
23579
|
+
}
|
|
23580
|
+
return [...ids];
|
|
23581
|
+
}
|
|
23582
|
+
function withdrawnSources() {
|
|
23583
|
+
const cited = [...new Set(plannerCopies("rejected.md").flatMap((file) => {
|
|
23584
|
+
try {
|
|
23585
|
+
return citedSources(fs76.readFileSync(file, "utf8"));
|
|
23586
|
+
} catch {
|
|
23587
|
+
return [];
|
|
23588
|
+
}
|
|
23589
|
+
}))];
|
|
23590
|
+
if (cited.length === 0) return [];
|
|
23591
|
+
const dismissed = new Set(readAllDismissed().map((s) => s.sourceId));
|
|
23592
|
+
const restored = new Set([...readInbox(), ...readArchived()].map((s) => s.sourceId));
|
|
23593
|
+
return cited.filter((id) => restored.has(id) && !dismissed.has(id));
|
|
23594
|
+
}
|
|
23595
|
+
var SOURCES;
|
|
23596
|
+
var init_dismissal_review = __esm({
|
|
23597
|
+
"../cli/src/lib/agent/dismissal-review.ts"() {
|
|
23598
|
+
"use strict";
|
|
23599
|
+
init_cards();
|
|
23600
|
+
init_frontmatter();
|
|
23601
|
+
init_memory();
|
|
23602
|
+
init_paths();
|
|
23603
|
+
init_inbox();
|
|
23604
|
+
init_archive();
|
|
23605
|
+
SOURCES = /\(([^()]+)\)\s*$/;
|
|
23606
|
+
}
|
|
23607
|
+
});
|
|
23608
|
+
|
|
23609
|
+
// ../cli/src/lib/agent/flow.ts
|
|
23610
|
+
import fs77 from "node:fs";
|
|
23611
|
+
import path78 from "node:path";
|
|
23794
23612
|
function readCard2(id, home3 = "board") {
|
|
23795
23613
|
const found2 = home3 === "archive" ? locateArchived(id) : locate(id);
|
|
23796
23614
|
if (!found2) {
|
|
@@ -23799,10 +23617,10 @@ function readCard2(id, home3 = "board") {
|
|
|
23799
23617
|
{ kind: "card-not-found", id }
|
|
23800
23618
|
);
|
|
23801
23619
|
}
|
|
23802
|
-
const file = found2.kind === "group" ?
|
|
23620
|
+
const file = found2.kind === "group" ? path78.join(found2.target, "root.md") : found2.target;
|
|
23803
23621
|
let text5;
|
|
23804
23622
|
try {
|
|
23805
|
-
text5 =
|
|
23623
|
+
text5 = fs77.readFileSync(file, "utf8");
|
|
23806
23624
|
} catch {
|
|
23807
23625
|
die(`#${id} is on the board but ${rel(file)} can't be read.`, { kind: "card-unreadable", id });
|
|
23808
23626
|
}
|
|
@@ -23926,7 +23744,7 @@ function workspaceField(delivery) {
|
|
|
23926
23744
|
if (!delivery?.worktree) return [];
|
|
23927
23745
|
return field("workspace", [
|
|
23928
23746
|
`write code in ${delivery.worktree} \u2014 this delivery's own worktree, on branch ${delivery.branch}.`,
|
|
23929
|
-
|
|
23747
|
+
path78.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.`,
|
|
23930
23748
|
`every board command names the project's own copy: \`${boardCommandFor()} <command>\`.`,
|
|
23931
23749
|
`${rel(REPO_ROOT)} is the project \u2014 the card, the memory files and the docs are changed there, not here.`
|
|
23932
23750
|
]);
|
|
@@ -24107,12 +23925,12 @@ function buildFlow(req, program2) {
|
|
|
24107
23925
|
}
|
|
24108
23926
|
case "resolve": {
|
|
24109
23927
|
facts.push(...questionsField(card.meta), ...stageField(card.meta));
|
|
24110
|
-
const
|
|
24111
|
-
if (
|
|
24112
|
-
facts.push(...answeringField(
|
|
23928
|
+
const building = activeDelivery(req.id);
|
|
23929
|
+
if (building) {
|
|
23930
|
+
facts.push(...answeringField(building, self));
|
|
24113
23931
|
facts.push(...notesField(card));
|
|
24114
23932
|
}
|
|
24115
|
-
if (
|
|
23933
|
+
if (building) close.push(answeredClose(building, self));
|
|
24116
23934
|
close.push(...stageClose(card.meta));
|
|
24117
23935
|
break;
|
|
24118
23936
|
}
|
|
@@ -24189,7 +24007,6 @@ function buildFlow(req, program2) {
|
|
|
24189
24007
|
facts.push(
|
|
24190
24008
|
...field("memory", [
|
|
24191
24009
|
`${rel(agentMemoryDir(PLANNER))}/<module>/ \u2014 decisions.md, rejected.md, redesign.md: where a planning note goes, the planner's own files for one spanning modules`,
|
|
24192
|
-
`${proposerMissedFile()} \u2014 a follow-up the user says the proposer missed`,
|
|
24193
24010
|
`${rel(AGENT_MEMORY)}/<agent>/ \u2014 an agent's own files, which its AGENT.md names`
|
|
24194
24011
|
])
|
|
24195
24012
|
);
|
|
@@ -24204,12 +24021,9 @@ function buildFlow(req, program2) {
|
|
|
24204
24021
|
}
|
|
24205
24022
|
// The rejection review (#929, #1497): the lists are the job, decided before any run starts.
|
|
24206
24023
|
case "review-dismissals": {
|
|
24207
|
-
const
|
|
24208
|
-
const
|
|
24209
|
-
const rejections = rejectionsToReview(since);
|
|
24210
|
-
const dismissals = dismissalsToReview(since);
|
|
24024
|
+
const rejections = rejectionsToReview();
|
|
24025
|
+
const dismissals = dismissalsToReview();
|
|
24211
24026
|
const withdrawn = withdrawnSources();
|
|
24212
|
-
facts.push(...field("window", lastRun ? `since the last review that passed, ${lastRun}` : req.allReasons ? "every reason on the board" : "every reason \u2014 none has been reviewed yet"));
|
|
24213
24027
|
facts.push(
|
|
24214
24028
|
...field(
|
|
24215
24029
|
"rejections",
|
|
@@ -24327,7 +24141,6 @@ var init_flow = __esm({
|
|
|
24327
24141
|
init_frontmatter();
|
|
24328
24142
|
init_io();
|
|
24329
24143
|
init_guide();
|
|
24330
|
-
init_cadence();
|
|
24331
24144
|
init_memory();
|
|
24332
24145
|
init_paths();
|
|
24333
24146
|
init_start();
|
|
@@ -24338,7 +24151,6 @@ var init_flow = __esm({
|
|
|
24338
24151
|
init_deliveries();
|
|
24339
24152
|
init_facts();
|
|
24340
24153
|
init_memory_review();
|
|
24341
|
-
init_settings2();
|
|
24342
24154
|
init_dismissal_review();
|
|
24343
24155
|
init_language();
|
|
24344
24156
|
init_prompts();
|
|
@@ -24676,7 +24488,7 @@ __export(watch_exports, {
|
|
|
24676
24488
|
watchRun: () => watchRun
|
|
24677
24489
|
});
|
|
24678
24490
|
import { spawn as spawn4 } from "node:child_process";
|
|
24679
|
-
import
|
|
24491
|
+
import fs78 from "node:fs";
|
|
24680
24492
|
async function watchRun(sessionId, resume = startResume) {
|
|
24681
24493
|
const spec = readSpec(sessionId);
|
|
24682
24494
|
const run2 = peekRun(sessionId);
|
|
@@ -24712,11 +24524,11 @@ async function watchRun(sessionId, resume = startResume) {
|
|
|
24712
24524
|
const record = claimed ?? run2;
|
|
24713
24525
|
const sub = record.action === "sub";
|
|
24714
24526
|
if (claim) await setCardStatus(claim.cardId, claim.status);
|
|
24715
|
-
|
|
24716
|
-
const logFile =
|
|
24527
|
+
fs78.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
24528
|
+
const logFile = fs78.createWriteStream(record.logPath, { flags: "a" });
|
|
24717
24529
|
logFile.on("error", () => {
|
|
24718
24530
|
});
|
|
24719
|
-
const eventsFile =
|
|
24531
|
+
const eventsFile = fs78.createWriteStream(eventsPathOf(sessionId), { flags: "a" });
|
|
24720
24532
|
eventsFile.on("error", () => {
|
|
24721
24533
|
});
|
|
24722
24534
|
const event2 = (e) => eventsFile.write(`${JSON.stringify(e)}
|
|
@@ -24925,7 +24737,7 @@ async function watchRun(sessionId, resume = startResume) {
|
|
|
24925
24737
|
}
|
|
24926
24738
|
const retrying = !!again && !asked;
|
|
24927
24739
|
const spent = spoken ? spoken.usage : renderer?.usage?.();
|
|
24928
|
-
const lost = !!spec.again && !held4.deliveryId && !ours && !retrying && !spawnError && code !== 0 && lastUsed === 0 && !(spent && spent.input + spent.output + spent.cacheRead + spent.cacheCreation > 0);
|
|
24740
|
+
const lost = !!spec.again && (!held4.deliveryId || held4.action === "implement") && !ours && !retrying && !spawnError && code !== 0 && lastUsed === 0 && !(spent && spent.input + spent.output + spent.cacheRead + spent.cacheCreation > 0);
|
|
24929
24741
|
let status = asked ? "stopped" : silent || unfinished ? "error" : code === 0 ? "done" : "error";
|
|
24930
24742
|
const reported = spoken ? spoken.costUsd : renderer?.costUsd?.();
|
|
24931
24743
|
const totals = reportsSessionTotal(record.harness);
|
|
@@ -25297,7 +25109,7 @@ var init_watch = __esm({
|
|
|
25297
25109
|
});
|
|
25298
25110
|
|
|
25299
25111
|
// ../cli/src/kanban.ts
|
|
25300
|
-
import
|
|
25112
|
+
import fs86 from "node:fs";
|
|
25301
25113
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
25302
25114
|
|
|
25303
25115
|
// ../cli/src/lib/cli/agent.ts
|
|
@@ -25786,7 +25598,7 @@ async function testAgent(args) {
|
|
|
25786
25598
|
}
|
|
25787
25599
|
|
|
25788
25600
|
// ../cli/src/commands/cloud.ts
|
|
25789
|
-
import
|
|
25601
|
+
import fs69 from "node:fs";
|
|
25790
25602
|
import path70 from "node:path";
|
|
25791
25603
|
|
|
25792
25604
|
// ../cli/src/lib/cloud/avatar.ts
|
|
@@ -25835,13 +25647,13 @@ init_config();
|
|
|
25835
25647
|
init_home();
|
|
25836
25648
|
init_config();
|
|
25837
25649
|
init_session();
|
|
25838
|
-
import
|
|
25650
|
+
import fs68 from "node:fs";
|
|
25839
25651
|
import path69 from "node:path";
|
|
25840
25652
|
var FRESH_MS = 10 * 6e4;
|
|
25841
25653
|
var GRACE_MS = 7 * 24 * 60 * 6e4;
|
|
25842
25654
|
var heldFile = () => path69.join(machineHome(), "pro.json");
|
|
25843
25655
|
function forgetPro() {
|
|
25844
|
-
|
|
25656
|
+
fs68.rmSync(heldFile(), { force: true });
|
|
25845
25657
|
}
|
|
25846
25658
|
|
|
25847
25659
|
// ../cli/src/lib/cloud/account.ts
|
|
@@ -26021,7 +25833,7 @@ async function cloudImage(opts, program2) {
|
|
|
26021
25833
|
const type = REF_TYPES[path70.extname(file).toLowerCase()];
|
|
26022
25834
|
if (!type) usage(`--ref ${file}: use a PNG, JPEG or WebP file.`, line);
|
|
26023
25835
|
try {
|
|
26024
|
-
return `data:${type};base64,${
|
|
25836
|
+
return `data:${type};base64,${fs69.readFileSync(file).toString("base64")}`;
|
|
26025
25837
|
} catch (e) {
|
|
26026
25838
|
usage(`--ref ${file}: ${e instanceof Error ? e.message : String(e)}`, line);
|
|
26027
25839
|
}
|
|
@@ -26127,31 +25939,31 @@ async function generate(g) {
|
|
|
26127
25939
|
refuse(g.failed, g.fallback);
|
|
26128
25940
|
}
|
|
26129
25941
|
const out = g.outFor?.(res, g.out) ?? g.out;
|
|
26130
|
-
|
|
26131
|
-
|
|
25942
|
+
fs69.mkdirSync(path70.dirname(path70.resolve(out)), { recursive: true });
|
|
25943
|
+
fs69.writeFileSync(out, Buffer.from(await res.arrayBuffer()));
|
|
26132
25944
|
say(`${out} (${g.label(res)})`);
|
|
26133
25945
|
return { out };
|
|
26134
25946
|
}
|
|
26135
25947
|
|
|
26136
25948
|
// ../cli/src/commands/cloud-import.ts
|
|
26137
25949
|
import crypto3 from "node:crypto";
|
|
26138
|
-
import
|
|
25950
|
+
import fs71 from "node:fs";
|
|
26139
25951
|
import path72 from "node:path";
|
|
26140
25952
|
|
|
26141
25953
|
// ../cloud/src/board-files.ts
|
|
26142
25954
|
var ROOTS = ["todo/", ".archive/", "memory/", "workflows/", "triage/", "assets/", "rules/"];
|
|
26143
25955
|
var SETTINGS = ["modules.md", "config.md"];
|
|
26144
|
-
var isSettingsPath = (
|
|
25956
|
+
var isSettingsPath = (path86) => SETTINGS.includes(path86) || /^rules\/(?:workflows\/)?[^/]+\.md$/.test(path86);
|
|
26145
25957
|
var BOARD_FILE_LIMIT = 256 * 1024;
|
|
26146
|
-
function isBoardPath(
|
|
26147
|
-
if (!
|
|
26148
|
-
if (
|
|
26149
|
-
if (
|
|
26150
|
-
if (
|
|
26151
|
-
if (SETTINGS.includes(
|
|
26152
|
-
if (
|
|
26153
|
-
if (
|
|
26154
|
-
return ROOTS.some((root) =>
|
|
25958
|
+
function isBoardPath(path86) {
|
|
25959
|
+
if (!path86 || path86.length > 500 || path86.startsWith("/") || path86.includes("\\") || path86.includes("\0")) return false;
|
|
25960
|
+
if (path86.split("/").some((part) => !part || part === "." || part === "..")) return false;
|
|
25961
|
+
if (path86 === "todo/README.md" || path86.split("/").includes(".sync")) return false;
|
|
25962
|
+
if (path86.startsWith("workflows/")) return true;
|
|
25963
|
+
if (SETTINGS.includes(path86)) return true;
|
|
25964
|
+
if (path86.startsWith("rules/")) return isSettingsPath(path86);
|
|
25965
|
+
if (path86.startsWith("assets/")) return path86.endsWith(".json");
|
|
25966
|
+
return ROOTS.some((root) => path86.startsWith(root)) && path86.endsWith(".md");
|
|
26155
25967
|
}
|
|
26156
25968
|
var workflowFolder = (id) => id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
26157
25969
|
|
|
@@ -26255,7 +26067,7 @@ init_bundled();
|
|
|
26255
26067
|
// ../cli/src/lib/agents/shared-memory.ts
|
|
26256
26068
|
init_bundled();
|
|
26257
26069
|
init_parse();
|
|
26258
|
-
import
|
|
26070
|
+
import fs70 from "node:fs";
|
|
26259
26071
|
import path71 from "node:path";
|
|
26260
26072
|
function sharedMemory(board2) {
|
|
26261
26073
|
const declared = /* @__PURE__ */ new Map();
|
|
@@ -26271,13 +26083,13 @@ function sharedMemory(board2) {
|
|
|
26271
26083
|
const root = path71.join(board2, "agents");
|
|
26272
26084
|
let folders2 = [];
|
|
26273
26085
|
try {
|
|
26274
|
-
folders2 =
|
|
26086
|
+
folders2 = fs70.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
|
|
26275
26087
|
} catch {
|
|
26276
26088
|
}
|
|
26277
26089
|
for (const folder of folders2.sort()) {
|
|
26278
26090
|
let text5 = null;
|
|
26279
26091
|
try {
|
|
26280
|
-
text5 =
|
|
26092
|
+
text5 = fs70.readFileSync(path71.join(root, folder, "AGENT.md"), "utf8");
|
|
26281
26093
|
} catch {
|
|
26282
26094
|
}
|
|
26283
26095
|
take(folder, text5, false);
|
|
@@ -26366,12 +26178,12 @@ async function findProject(asked) {
|
|
|
26366
26178
|
}
|
|
26367
26179
|
throw new Refused(`No project of yours has the id or prefix "${asked}".`, "not_found");
|
|
26368
26180
|
}
|
|
26369
|
-
var read = (file) =>
|
|
26181
|
+
var read = (file) => fs71.readFileSync(file, "utf8");
|
|
26370
26182
|
var bytes2 = (text5) => Buffer.byteLength(text5, "utf8");
|
|
26371
26183
|
function files(dir) {
|
|
26372
|
-
if (!
|
|
26184
|
+
if (!fs71.existsSync(dir)) return [];
|
|
26373
26185
|
const out = [];
|
|
26374
|
-
for (const entry of
|
|
26186
|
+
for (const entry of fs71.readdirSync(dir, { withFileTypes: true })) {
|
|
26375
26187
|
if (entry.name.startsWith(".")) continue;
|
|
26376
26188
|
const full = path72.join(dir, entry.name);
|
|
26377
26189
|
if (entry.isDirectory()) out.push(...files(full));
|
|
@@ -26423,7 +26235,7 @@ function packWorkflow(flow) {
|
|
|
26423
26235
|
const named2 = [...flow.previews, ...flow.land, ...flow.scheduled].map((h) => h.agent).filter((a) => !LAND_ROLES.includes(a));
|
|
26424
26236
|
for (const agent of new Set(named2)) {
|
|
26425
26237
|
const dir = path72.join(AGENTS, agent);
|
|
26426
|
-
if (
|
|
26238
|
+
if (fs71.existsSync(path72.join(dir, "AGENT.md"))) {
|
|
26427
26239
|
for (const file of files(dir)) out[`agents/${agent}/${posix2(path72.relative(dir, file))}`] = read(file);
|
|
26428
26240
|
continue;
|
|
26429
26241
|
}
|
|
@@ -26477,7 +26289,7 @@ function draftsOf(text5) {
|
|
|
26477
26289
|
if (!at) continue;
|
|
26478
26290
|
const base = at[1] === "assets" ? ASSETS : MOCKUPS;
|
|
26479
26291
|
const file = path72.resolve(base, at[2]);
|
|
26480
|
-
if (file.startsWith(base + path72.sep) &&
|
|
26292
|
+
if (file.startsWith(base + path72.sep) && fs71.statSync(file, { throwIfNoEntry: false })?.isFile()) out.set(name, file);
|
|
26481
26293
|
}
|
|
26482
26294
|
return [...out].map(([name, file]) => ({ name, file }));
|
|
26483
26295
|
}
|
|
@@ -26513,7 +26325,7 @@ async function uploadDrafts(task, drafts) {
|
|
|
26513
26325
|
if (!card.body.includes(name)) continue;
|
|
26514
26326
|
const ext = path72.extname(file).toLowerCase();
|
|
26515
26327
|
const type = TYPES[ext] ?? "application/octet-stream";
|
|
26516
|
-
const data =
|
|
26328
|
+
const data = fs71.readFileSync(file);
|
|
26517
26329
|
const artifact = await api("POST", `/v1/tasks/${task}/artifacts`, { type: kindOf(ext), name: name.slice(0, 200) });
|
|
26518
26330
|
const upload = await api("POST", `/v1/artifacts/${artifact.id}/uploads`, { content_type: type, size: Math.max(data.length, 1) });
|
|
26519
26331
|
await api("PUT", `/v1/uploads/${upload.id}`, void 0, { bytes: data, type });
|
|
@@ -26556,7 +26368,7 @@ usage: ${program2} cloud import --project <id or prefix> [--dry-run]`, { kind: "
|
|
|
26556
26368
|
async function run(asked, dryRun) {
|
|
26557
26369
|
const project = await findProject(asked);
|
|
26558
26370
|
const cloudFlows = await all(`/v1/workflows?workspace=${encodeURIComponent(project.workspace)}`);
|
|
26559
|
-
const board2 = crypto3.createHash("sha256").update(
|
|
26371
|
+
const board2 = crypto3.createHash("sha256").update(fs71.realpathSync(KANBAN)).digest("hex").slice(0, 16);
|
|
26560
26372
|
const cards = openCards();
|
|
26561
26373
|
const problems = [];
|
|
26562
26374
|
const where = (file) => posix2(path72.relative(KANBAN, file));
|
|
@@ -26586,7 +26398,7 @@ async function run(asked, dryRun) {
|
|
|
26586
26398
|
const root = `projects/${project.id}/`;
|
|
26587
26399
|
const boardFiles = [];
|
|
26588
26400
|
for (const file of [...files(MEMORY), ...files(RULES), CONFIG, MODULES_MD]) {
|
|
26589
|
-
if (!
|
|
26401
|
+
if (!fs71.existsSync(file)) continue;
|
|
26590
26402
|
const rel2 = where(file);
|
|
26591
26403
|
if (!isBoardPath(rel2)) continue;
|
|
26592
26404
|
const content = read(file);
|
|
@@ -26697,16 +26509,16 @@ async function run(asked, dryRun) {
|
|
|
26697
26509
|
}
|
|
26698
26510
|
function leftLocal(own) {
|
|
26699
26511
|
const out = [];
|
|
26700
|
-
const count4 = (dir) =>
|
|
26512
|
+
const count4 = (dir) => fs71.existsSync(dir) ? walkMd(dir).length : 0;
|
|
26701
26513
|
const archived = count4(ARCHIVE);
|
|
26702
26514
|
if (archived) out.push(`.archive/ (${archived})`);
|
|
26703
26515
|
const triage = count4(TRIAGE);
|
|
26704
26516
|
if (triage) out.push(`triage/ (${triage})`);
|
|
26705
|
-
if (
|
|
26706
|
-
if (
|
|
26517
|
+
if (fs71.existsSync(DELIVERIES)) out.push("deliveries/");
|
|
26518
|
+
if (fs71.existsSync(path72.join(KANBAN, "metrics.csv"))) out.push("metrics.csv");
|
|
26707
26519
|
const named2 = new Set(own.flatMap((w) => [...w.previews, ...w.land, ...w.scheduled].map((h) => h.agent)));
|
|
26708
|
-
if (
|
|
26709
|
-
for (const e of
|
|
26520
|
+
if (fs71.existsSync(AGENTS)) {
|
|
26521
|
+
for (const e of fs71.readdirSync(AGENTS, { withFileTypes: true })) {
|
|
26710
26522
|
if (e.isDirectory() && !e.name.startsWith(".") && !named2.has(e.name)) out.push(`agents/${e.name}/`);
|
|
26711
26523
|
}
|
|
26712
26524
|
}
|
|
@@ -26724,7 +26536,7 @@ init_session();
|
|
|
26724
26536
|
init_io();
|
|
26725
26537
|
init_paths();
|
|
26726
26538
|
import { createHash as createHash4 } from "node:crypto";
|
|
26727
|
-
import
|
|
26539
|
+
import fs72 from "node:fs";
|
|
26728
26540
|
import path73 from "node:path";
|
|
26729
26541
|
var SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
26730
26542
|
var CAPTURED = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
@@ -26826,8 +26638,8 @@ This template has no preview.`);
|
|
|
26826
26638
|
const bytes3 = Buffer.from(await (await ask(`/${at}/previews/${route(p.path)}`, {}, WITHOUT)).arrayBuffer());
|
|
26827
26639
|
const file = path73.basename(p.path);
|
|
26828
26640
|
const out = path73.join(opts.to, file.startsWith(`${name}-`) ? file : `${name}-${file}`);
|
|
26829
|
-
|
|
26830
|
-
|
|
26641
|
+
fs72.mkdirSync(opts.to, { recursive: true });
|
|
26642
|
+
fs72.writeFileSync(out, bytes3);
|
|
26831
26643
|
written.push(out);
|
|
26832
26644
|
say(out);
|
|
26833
26645
|
}
|
|
@@ -26848,7 +26660,7 @@ async function templateGet(name, opts, program2) {
|
|
|
26848
26660
|
}
|
|
26849
26661
|
fetched.push({ out, bytes: bytes3 });
|
|
26850
26662
|
}
|
|
26851
|
-
const taken = fetched.filter((f) =>
|
|
26663
|
+
const taken = fetched.filter((f) => fs72.existsSync(f.out)).map((f) => path73.relative(to, f.out));
|
|
26852
26664
|
if (taken.length > 0) {
|
|
26853
26665
|
refuse2({
|
|
26854
26666
|
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`,
|
|
@@ -26856,8 +26668,8 @@ async function templateGet(name, opts, program2) {
|
|
|
26856
26668
|
});
|
|
26857
26669
|
}
|
|
26858
26670
|
for (const f of fetched) {
|
|
26859
|
-
|
|
26860
|
-
|
|
26671
|
+
fs72.mkdirSync(path73.dirname(f.out), { recursive: true });
|
|
26672
|
+
fs72.writeFileSync(f.out, f.bytes);
|
|
26861
26673
|
}
|
|
26862
26674
|
say(`\u5DF2\u53D6\u56DE ${name}\uFF1A${fetched.length} \u4E2A\u6587\u4EF6\uFF0C\u4F4D\u4E8E ${to}
|
|
26863
26675
|
Fetched ${name}: ${fetched.length} files into ${to}`);
|
|
@@ -26876,8 +26688,8 @@ async function templateSource(name, opts) {
|
|
|
26876
26688
|
continue;
|
|
26877
26689
|
}
|
|
26878
26690
|
const out = path73.join(opts.to, `${name}-${path73.basename(e.path)}`);
|
|
26879
|
-
|
|
26880
|
-
|
|
26691
|
+
fs72.mkdirSync(opts.to, { recursive: true });
|
|
26692
|
+
fs72.writeFileSync(out, Buffer.from(await (await ask(`/${at}/evidence/${route(e.path)}`)).arrayBuffer()));
|
|
26881
26693
|
written.push(out);
|
|
26882
26694
|
say(out);
|
|
26883
26695
|
}
|
|
@@ -26886,13 +26698,13 @@ async function templateSource(name, opts) {
|
|
|
26886
26698
|
async function templatePublish(dir, opts) {
|
|
26887
26699
|
const root = path73.resolve(dir);
|
|
26888
26700
|
const manifest = path73.join(root, "template.md");
|
|
26889
|
-
if (!
|
|
26890
|
-
const meta = readTemplateMeta(
|
|
26701
|
+
if (!fs72.existsSync(manifest)) invalid("\u6587\u4EF6\u5939\u9876\u5C42\u7F3A\u5C11 template.md\u3002", "template.md is missing from the folder\u2019s top level.");
|
|
26702
|
+
const meta = readTemplateMeta(fs72.readFileSync(manifest, "utf8"));
|
|
26891
26703
|
const files2 = walk2(root);
|
|
26892
|
-
const total = files2.reduce((sum, f) => sum +
|
|
26704
|
+
const total = files2.reduce((sum, f) => sum + fs72.statSync(path73.join(root, f)).size, 0);
|
|
26893
26705
|
if (total > MAX_BYTES3) invalid("\u6A21\u677F\u6587\u4EF6\u5408\u8BA1\u8D85\u8FC7 50 MB\u3002", "the template\u2019s files exceed 50 MB.");
|
|
26894
26706
|
for (const extra of [...opts.preview, ...opts.evidence]) {
|
|
26895
|
-
if (!
|
|
26707
|
+
if (!fs72.existsSync(extra)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${extra}\u3002`, `${extra} does not exist.`);
|
|
26896
26708
|
if (path73.resolve(extra).startsWith(root + path73.sep)) {
|
|
26897
26709
|
invalid(
|
|
26898
26710
|
`${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`,
|
|
@@ -26905,7 +26717,7 @@ async function templatePublish(dir, opts) {
|
|
|
26905
26717
|
}
|
|
26906
26718
|
const form = new FormData();
|
|
26907
26719
|
form.set("meta", JSON.stringify({ ...meta, replace: !!opts.replace }));
|
|
26908
|
-
const add = (kind, name, file) => form.append(`${kind}:${name}`, new Blob([
|
|
26720
|
+
const add = (kind, name, file) => form.append(`${kind}:${name}`, new Blob([fs72.readFileSync(file)]), path73.basename(name));
|
|
26909
26721
|
for (const f of files2) add("files", f, path73.join(root, f));
|
|
26910
26722
|
for (const f of opts.preview) add("previews", path73.basename(f), f);
|
|
26911
26723
|
for (const f of opts.evidence) add("evidence", path73.basename(f), f);
|
|
@@ -26925,10 +26737,10 @@ Deleted ${name} from the catalog.`);
|
|
|
26925
26737
|
}
|
|
26926
26738
|
async function templateShowcase(name, dir) {
|
|
26927
26739
|
const file = path73.join(dir, "showcase.json");
|
|
26928
|
-
if (!
|
|
26740
|
+
if (!fs72.existsSync(file)) invalid(`${dir} \u4E2D\u7F3A\u5C11 showcase.json\u3002`, `showcase.json is missing from ${dir}.`);
|
|
26929
26741
|
let meta;
|
|
26930
26742
|
try {
|
|
26931
|
-
meta = JSON.parse(
|
|
26743
|
+
meta = JSON.parse(fs72.readFileSync(file, "utf8"));
|
|
26932
26744
|
} catch {
|
|
26933
26745
|
return invalid("showcase.json \u4E0D\u662F\u6709\u6548\u7684 JSON\u3002", "showcase.json is not valid JSON.");
|
|
26934
26746
|
}
|
|
@@ -26941,8 +26753,8 @@ async function templateShowcase(name, dir) {
|
|
|
26941
26753
|
form.set("meta", JSON.stringify(meta));
|
|
26942
26754
|
for (const p of new Set(named2)) {
|
|
26943
26755
|
const at = path73.join(dir, p);
|
|
26944
|
-
if (!
|
|
26945
|
-
form.append(`media:${p}`, new Blob([
|
|
26756
|
+
if (!fs72.existsSync(at)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${at}\u3002`, `${at} does not exist.`);
|
|
26757
|
+
form.append(`media:${p}`, new Blob([fs72.readFileSync(at)]), p);
|
|
26946
26758
|
}
|
|
26947
26759
|
const done2 = await asJson(await ask(`/${encodeURIComponent(name)}/showcase`, { method: "PUT", body: form }));
|
|
26948
26760
|
say(`\u5DF2\u66F4\u65B0 ${name} \u7684\u5C55\u793A\u8D44\u6599\uFF1A${done2.files} \u4E2A\u6587\u4EF6\u3002
|
|
@@ -26951,7 +26763,7 @@ Updated ${name}'s showcase: ${done2.files} files.`);
|
|
|
26951
26763
|
}
|
|
26952
26764
|
function walk2(root, sub = "") {
|
|
26953
26765
|
const out = [];
|
|
26954
|
-
for (const entry of
|
|
26766
|
+
for (const entry of fs72.readdirSync(path73.join(root, sub), { withFileTypes: true })) {
|
|
26955
26767
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
26956
26768
|
const rel2 = sub ? `${sub}/${entry.name}` : entry.name;
|
|
26957
26769
|
if (entry.isDirectory()) out.push(...walk2(root, rel2));
|
|
@@ -27092,12 +26904,6 @@ init_io();
|
|
|
27092
26904
|
init_paths();
|
|
27093
26905
|
init_workflows();
|
|
27094
26906
|
init_workflow_cards();
|
|
27095
|
-
init_cadence();
|
|
27096
|
-
init_scheduled();
|
|
27097
|
-
init_start();
|
|
27098
|
-
var cadenceLine = (one) => one.off ? "off" : isAuto(one.cadence) ? "auto" : `every ${one.cadence}`;
|
|
27099
|
-
var WAITS = { tooSoon: "", retrying: "retrying after a failed run", nothingNew: "nothing new yet", building: "waits for the build to finish" };
|
|
27100
|
-
var nextLine = (due) => !due ? "" : ` \xB7 next after ${formatStamp(due.next)}${due.wait && WAITS[due.wait] ? ` (${WAITS[due.wait]})` : ""}`;
|
|
27101
26907
|
var found = (id) => {
|
|
27102
26908
|
const flow = workflowById(id);
|
|
27103
26909
|
if (!flow) {
|
|
@@ -27121,11 +26927,7 @@ function cmdWorkflowList() {
|
|
|
27121
26927
|
if (description !== flow.name) say(` ${description}`);
|
|
27122
26928
|
say(` previews ${previewNames(previewMembers(flow)) || "(none)"}`);
|
|
27123
26929
|
say(` land ${names3(flow.land) || "(none) \u2014 the last approval archives the card"}`);
|
|
27124
|
-
for (const
|
|
27125
|
-
const due = scheduledNext(flow.id, one);
|
|
27126
|
-
const ran = one.lastRun ? `last run ${one.lastRun}` : "never run";
|
|
27127
|
-
say(` scheduled ${one.agent} ${cadenceLine(one)} \xB7 ${ran}${nextLine(due)}`);
|
|
27128
|
-
}
|
|
26930
|
+
for (const agent of listedScheduled(flow)) say(` scheduled ${agent}`);
|
|
27129
26931
|
for (const problem of workflowProblems(flow.id)) say(` ! ${problem}`);
|
|
27130
26932
|
}
|
|
27131
26933
|
const fileProblems = workflowFileProblems();
|
|
@@ -27134,7 +26936,7 @@ function cmdWorkflowList() {
|
|
|
27134
26936
|
workflows: rows.map((flow) => ({
|
|
27135
26937
|
...flow,
|
|
27136
26938
|
previews: previewMembers(flow).map((h) => ({ ...h, after: agentAfter(h.agent) })),
|
|
27137
|
-
scheduled:
|
|
26939
|
+
scheduled: listedScheduled(flow),
|
|
27138
26940
|
description: workflowPurpose(flow),
|
|
27139
26941
|
problems: workflowProblems(flow.id)
|
|
27140
26942
|
})),
|
|
@@ -27208,48 +27010,6 @@ function cmdWorkflowAgent(id, flags) {
|
|
|
27208
27010
|
say(`${flow.name}: ${changes.join(", ")}`);
|
|
27209
27011
|
return { id: flow.id, changes };
|
|
27210
27012
|
}
|
|
27211
|
-
async function cmdWorkflowSchedule(id, flags) {
|
|
27212
|
-
const flow = found(id);
|
|
27213
|
-
const changes = [];
|
|
27214
|
-
if (flags.on !== void 0) {
|
|
27215
|
-
done(switchWorkflowScheduled(flow.id, flags.on, true));
|
|
27216
|
-
changes.push(`${flags.on.trim()} on`);
|
|
27217
|
-
}
|
|
27218
|
-
if (flags.off !== void 0) {
|
|
27219
|
-
done(switchWorkflowScheduled(flow.id, flags.off, false));
|
|
27220
|
-
changes.push(`${flags.off.trim()} off`);
|
|
27221
|
-
}
|
|
27222
|
-
const who = (flags.on ?? flags.off ?? flags.run ?? "").trim();
|
|
27223
|
-
if (flags.cadence !== void 0) {
|
|
27224
|
-
if (!who) die("--cadence says how often one agent runs, so name it: --on <agent> --cadence 1d");
|
|
27225
|
-
done(setWorkflowScheduledCadence(flow.id, who, flags.cadence));
|
|
27226
|
-
changes.push(`${who}: every ${flags.cadence.trim()}`);
|
|
27227
|
-
}
|
|
27228
|
-
if (flags.extra !== void 0) {
|
|
27229
|
-
if (!who) die('--extra says what one agent is asked for here, so name it: --on <agent> --extra "\u2026"');
|
|
27230
|
-
done(setWorkflowScheduledExtra(flow.id, who, flags.extra));
|
|
27231
|
-
changes.push(`${who}: extra requirements`);
|
|
27232
|
-
}
|
|
27233
|
-
if (flags.run !== void 0) {
|
|
27234
|
-
const started = await startRun(scheduledRequest({ workflow: flow.id, agent: flags.run.trim() }));
|
|
27235
|
-
if ("error" in started) die(started.error);
|
|
27236
|
-
say(`${flow.name}: started ${flags.run.trim()} \u2014 run ${started.run.sessionId.slice(0, 8)}`);
|
|
27237
|
-
return { id: flow.id, changes, sessionId: started.run.sessionId };
|
|
27238
|
-
}
|
|
27239
|
-
if (!changes.length) {
|
|
27240
|
-
const rows = scheduledMembers(workflowById(flow.id) ?? flow);
|
|
27241
|
-
say(`${flow.name} \xB7 scheduled`);
|
|
27242
|
-
if (!rows.length) say(" (none) \u2014 an agent no workflow lists can be switched on here");
|
|
27243
|
-
for (const one of rows) {
|
|
27244
|
-
const due = scheduledNext(flow.id, one);
|
|
27245
|
-
const ran = one.lastRun ? `last run ${one.lastRun}` : "never run";
|
|
27246
|
-
say(` ${one.agent} ${cadenceLine(one)} \xB7 ${ran}${nextLine(due)}`);
|
|
27247
|
-
}
|
|
27248
|
-
return { id: flow.id, scheduled: rows };
|
|
27249
|
-
}
|
|
27250
|
-
say(`${flow.name} \xB7 scheduled: ${changes.join(", ")}`);
|
|
27251
|
-
return { id: flow.id, changes };
|
|
27252
|
-
}
|
|
27253
27013
|
|
|
27254
27014
|
// ../cli/src/commands/run.ts
|
|
27255
27015
|
init_answers();
|
|
@@ -27288,7 +27048,7 @@ init_opencode();
|
|
|
27288
27048
|
init_locate();
|
|
27289
27049
|
init_store();
|
|
27290
27050
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
27291
|
-
import
|
|
27051
|
+
import path79 from "node:path";
|
|
27292
27052
|
var AGENTS2 = {
|
|
27293
27053
|
claude: "claude-code",
|
|
27294
27054
|
codex: "codex",
|
|
@@ -27303,10 +27063,10 @@ var AGENTS2 = {
|
|
|
27303
27063
|
var RUNTIMES = /* @__PURE__ */ new Set(["node", "bun", "deno"]);
|
|
27304
27064
|
function agentOf2(args) {
|
|
27305
27065
|
const words = args.trim().split(/\s+/);
|
|
27306
|
-
const first =
|
|
27066
|
+
const first = path79.basename(words[0] ?? "");
|
|
27307
27067
|
const script = RUNTIMES.has(first) ? words[1] ?? "" : words[0] ?? "";
|
|
27308
27068
|
if (script.includes("/@anthropic-ai/claude-code/")) return "claude-code";
|
|
27309
|
-
return AGENTS2[
|
|
27069
|
+
return AGENTS2[path79.basename(script).replace(/\.(m?js|cjs|exe)$/, "")];
|
|
27310
27070
|
}
|
|
27311
27071
|
function processChain() {
|
|
27312
27072
|
if (process.platform === "win32") return [];
|
|
@@ -27369,8 +27129,8 @@ init_tasks();
|
|
|
27369
27129
|
init_read();
|
|
27370
27130
|
init_rules();
|
|
27371
27131
|
init_board();
|
|
27372
|
-
import
|
|
27373
|
-
import
|
|
27132
|
+
import fs79 from "node:fs";
|
|
27133
|
+
import path80 from "node:path";
|
|
27374
27134
|
var FOLLOW_MS = 400;
|
|
27375
27135
|
async function cmdStartRun(action, args, opts, program2 = "akb") {
|
|
27376
27136
|
const { req, follow: follow2, print } = readRequest(action, args, opts);
|
|
@@ -27532,7 +27292,7 @@ function readRequest(action, args, opts) {
|
|
|
27532
27292
|
if (!opts.task?.length) die("name the archived Cloud tasks to review: --task <task:card...>", { kind: "run-refused", action });
|
|
27533
27293
|
return { req: { action, tasks: opts.task }, follow: follow2, print };
|
|
27534
27294
|
}
|
|
27535
|
-
if (action === "review-dismissals") return { req: { action
|
|
27295
|
+
if (action === "review-dismissals") return { req: { action }, follow: follow2, print };
|
|
27536
27296
|
if (action === "reflect") {
|
|
27537
27297
|
const cards = (opts.cards ?? []).map(Number);
|
|
27538
27298
|
if (!cards.length || !cards.every((n) => Number.isInteger(n) && n > 0)) {
|
|
@@ -27655,7 +27415,7 @@ async function cmdSubStart(words, opts) {
|
|
|
27655
27415
|
let prompt = words.join(" ");
|
|
27656
27416
|
if (opts.file) {
|
|
27657
27417
|
try {
|
|
27658
|
-
prompt =
|
|
27418
|
+
prompt = fs79.readFileSync(path80.resolve(opts.file), "utf8");
|
|
27659
27419
|
} catch {
|
|
27660
27420
|
die(`--file: cannot read ${opts.file}`, { kind: "run-refused" });
|
|
27661
27421
|
}
|
|
@@ -27937,7 +27697,7 @@ async function cmdWatch(sessionId) {
|
|
|
27937
27697
|
// ../cli/src/commands/triage.ts
|
|
27938
27698
|
init_scheduled();
|
|
27939
27699
|
init_workflows();
|
|
27940
|
-
import
|
|
27700
|
+
import fs81 from "node:fs";
|
|
27941
27701
|
|
|
27942
27702
|
// ../cli/src/lib/signals/index.ts
|
|
27943
27703
|
init_cards();
|
|
@@ -27947,16 +27707,16 @@ init_yaml();
|
|
|
27947
27707
|
// ../cli/src/lib/signals/config.ts
|
|
27948
27708
|
init_settings2();
|
|
27949
27709
|
init_paths();
|
|
27950
|
-
import
|
|
27951
|
-
import
|
|
27710
|
+
import fs80 from "node:fs";
|
|
27711
|
+
import path81 from "node:path";
|
|
27952
27712
|
var TOKEN_KEY = "TRIAGE_ENDPOINT_TOKEN";
|
|
27953
27713
|
var ENDPOINT_SETTING = "Triage endpoint";
|
|
27954
27714
|
var LINE2 = /^- \*\*Triage endpoint\*\*\s*[—-]\s*(.+)$/m;
|
|
27955
|
-
var boardRel3 = (file) => rel(file).split(
|
|
27715
|
+
var boardRel3 = (file) => rel(file).split(path81.sep).join("/");
|
|
27956
27716
|
function signalEndpoint() {
|
|
27957
27717
|
let text5;
|
|
27958
27718
|
try {
|
|
27959
|
-
text5 =
|
|
27719
|
+
text5 = fs80.readFileSync(CONFIG, "utf8");
|
|
27960
27720
|
} catch {
|
|
27961
27721
|
return "";
|
|
27962
27722
|
}
|
|
@@ -28132,7 +27892,7 @@ async function cmdTriageAdd(opts) {
|
|
|
28132
27892
|
let body = opts.text;
|
|
28133
27893
|
if (opts.file !== void 0) {
|
|
28134
27894
|
try {
|
|
28135
|
-
body =
|
|
27895
|
+
body = fs81.readFileSync(opts.file, "utf8");
|
|
28136
27896
|
} catch {
|
|
28137
27897
|
die(`can't read ${opts.file} \u2014 write the item to a file, then pass its path`, { kind: "needs-input" });
|
|
28138
27898
|
}
|
|
@@ -28485,7 +28245,8 @@ function declareRuns(program2, cli) {
|
|
|
28485
28245
|
}
|
|
28486
28246
|
const workflow = withShared(program2.command("workflow")).summary("the workflows this board runs, and the agents each one lists").description(
|
|
28487
28247
|
"Every card runs through one workflow: previews approved by you, each waiting on the approvals its `after` names, then the agents that land the work, after which the card is archived; with none, the last approval archives it. A card chooses its previews when first planned. The built-ins can have their agents switched, and can be copied but not renamed or deleted. A card names its workflow in its own frontmatter (`akb raw create --workflow`), and a delivery freezes the one it started with."
|
|
28488
|
-
).action(async function() {
|
|
28248
|
+
).allowExcessArguments().allowUnknownOption().action(async function() {
|
|
28249
|
+
if (this.args.length) this.error(`unknown command '${this.args[0]}'`);
|
|
28489
28250
|
await workflowList(this, cli);
|
|
28490
28251
|
});
|
|
28491
28252
|
const flowWord = (name) => withShared(workflow.command(name));
|
|
@@ -28517,12 +28278,6 @@ function declareRuns(program2, cli) {
|
|
|
28517
28278
|
const flags = this.opts();
|
|
28518
28279
|
await onBoard2(this, cli, () => cmdWorkflowAgent(id, flags));
|
|
28519
28280
|
});
|
|
28520
|
-
flowWord("schedule").argument("<id>", "the workflow, from `workflow list`").option("--on <agent>", "switch one of this workflow's scheduled agents on").option("--off <agent>", "switch one off; it stays in the workflow").option("--cadence <cadence>", "how often the --on agent runs, e.g. 6h, 1d, 1d at 09:30, or auto (the default)").option("--extra <text>", "what the --on or --off agent is asked for here, on top of its own instructions").option("--run <agent>", "start one run of it now, due or not").summary("switch a workflow's scheduled agents, set how often each runs, or run one now").description(
|
|
28521
|
-
"With no change asked for, it lists them with their cadence and last run. A scheduled agent is listed under its workflow's `scheduled` and runs by itself on no card \u2014 in `auto`, when what its `akb.reads` names has something new; what it changes is committed and landed like a build."
|
|
28522
|
-
).action(async function(id) {
|
|
28523
|
-
const flags = this.opts();
|
|
28524
|
-
await onBoard2(this, cli, () => cmdWorkflowSchedule(id, flags));
|
|
28525
|
-
});
|
|
28526
28281
|
withShared(program2.command("guide")).argument("[topic]", "one flow in full; left off, every flow is listed one line each").summary("the flows the board works by, shipped with this command").description(
|
|
28527
28282
|
"A printed flow already carries the flows its action is done by, so this is for the rest: how the board works at all, the module map, updating, the local UI. `guide board` is the card format, the layout and the memory set."
|
|
28528
28283
|
).action(async function(...vals) {
|
|
@@ -28701,8 +28456,8 @@ function declareAgent(program2, cli) {
|
|
|
28701
28456
|
}
|
|
28702
28457
|
|
|
28703
28458
|
// ../cli/src/lib/cli/setup.ts
|
|
28704
|
-
import
|
|
28705
|
-
import
|
|
28459
|
+
import fs85 from "node:fs";
|
|
28460
|
+
import path85 from "node:path";
|
|
28706
28461
|
|
|
28707
28462
|
// ../cli/src/commands/install.ts
|
|
28708
28463
|
init_local();
|
|
@@ -28714,14 +28469,14 @@ init_io();
|
|
|
28714
28469
|
init_paths();
|
|
28715
28470
|
init_memory();
|
|
28716
28471
|
init_yaml2();
|
|
28717
|
-
import
|
|
28718
|
-
import
|
|
28472
|
+
import fs84 from "node:fs";
|
|
28473
|
+
import path84 from "node:path";
|
|
28719
28474
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
28720
28475
|
|
|
28721
28476
|
// ../cli/src/lib/skill/install.ts
|
|
28722
28477
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
28723
|
-
import
|
|
28724
|
-
import
|
|
28478
|
+
import fs83 from "node:fs";
|
|
28479
|
+
import path83 from "node:path";
|
|
28725
28480
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
28726
28481
|
|
|
28727
28482
|
// ../skill/SKILL.md
|
|
@@ -28735,8 +28490,8 @@ init_version();
|
|
|
28735
28490
|
init_version();
|
|
28736
28491
|
init_project();
|
|
28737
28492
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
28738
|
-
import
|
|
28739
|
-
import
|
|
28493
|
+
import fs82 from "node:fs";
|
|
28494
|
+
import path82 from "node:path";
|
|
28740
28495
|
var MARKER3 = "# ai4kanban commit guard";
|
|
28741
28496
|
var MARKER_LINE = new RegExp(`^${MARKER3}\\b`, "m");
|
|
28742
28497
|
var CHECK = [
|
|
@@ -28770,7 +28525,7 @@ function hookScript(sessions) {
|
|
|
28770
28525
|
}
|
|
28771
28526
|
var read3 = (file) => {
|
|
28772
28527
|
try {
|
|
28773
|
-
return
|
|
28528
|
+
return fs82.readFileSync(file, "utf8");
|
|
28774
28529
|
} catch {
|
|
28775
28530
|
return null;
|
|
28776
28531
|
}
|
|
@@ -28782,7 +28537,7 @@ function hooksDir(root) {
|
|
|
28782
28537
|
windowsHide: true
|
|
28783
28538
|
});
|
|
28784
28539
|
if (out.error || out.status !== 0 || !out.stdout.trim()) return null;
|
|
28785
|
-
return
|
|
28540
|
+
return path82.resolve(root, out.stdout.trim());
|
|
28786
28541
|
}
|
|
28787
28542
|
function hasOwnHooksPath(root) {
|
|
28788
28543
|
const out = spawnSync6("git", ["config", "--get", "core.hooksPath"], {
|
|
@@ -28792,18 +28547,18 @@ function hasOwnHooksPath(root) {
|
|
|
28792
28547
|
});
|
|
28793
28548
|
return !out.error && out.status === 0 && !!out.stdout.trim();
|
|
28794
28549
|
}
|
|
28795
|
-
var sessionsPath = (root) =>
|
|
28550
|
+
var sessionsPath = (root) => path82.join(projectStateDir(path82.join(root, "docs", "kanban"), root), SESSIONS_FILE);
|
|
28796
28551
|
function readCommitHook(root) {
|
|
28797
28552
|
const dir = hooksDir(root);
|
|
28798
28553
|
if (!dir) return { state: "no-git" };
|
|
28799
|
-
const where = shown(root,
|
|
28554
|
+
const where = shown(root, path82.join(dir, "pre-commit"));
|
|
28800
28555
|
const line = guardLine(sessionsPath(root));
|
|
28801
28556
|
if (hasOwnHooksPath(root)) return { state: "hooks-path", path: where, line };
|
|
28802
|
-
const text5 = read3(
|
|
28557
|
+
const text5 = read3(path82.join(dir, "pre-commit"));
|
|
28803
28558
|
if (text5 === null) return { state: "absent", path: where };
|
|
28804
28559
|
return MARKER_LINE.test(text5) ? { state: "ours", path: where } : { state: "foreign", path: where, line };
|
|
28805
28560
|
}
|
|
28806
|
-
var shown = (root, p) => (
|
|
28561
|
+
var shown = (root, p) => (path82.relative(root, p) || p).split(path82.sep).join("/");
|
|
28807
28562
|
function installCommitHook(root) {
|
|
28808
28563
|
const hook = readCommitHook(root);
|
|
28809
28564
|
if (hook.state === "no-git") return {};
|
|
@@ -28814,11 +28569,11 @@ function installCommitHook(root) {
|
|
|
28814
28569
|
${hook.line}`
|
|
28815
28570
|
};
|
|
28816
28571
|
}
|
|
28817
|
-
const file =
|
|
28572
|
+
const file = path82.join(root, hook.path);
|
|
28818
28573
|
try {
|
|
28819
|
-
|
|
28820
|
-
|
|
28821
|
-
|
|
28574
|
+
fs82.mkdirSync(path82.dirname(file), { recursive: true });
|
|
28575
|
+
fs82.writeFileSync(file, hookScript(sessionsPath(root)), { mode: 493 });
|
|
28576
|
+
fs82.chmodSync(file, 493);
|
|
28822
28577
|
} catch (e) {
|
|
28823
28578
|
return { note: `${hook.path} could not be written \u2014 ${e instanceof Error ? e.message : String(e)}` };
|
|
28824
28579
|
}
|
|
@@ -28834,9 +28589,9 @@ function sayCommitHook(state) {
|
|
|
28834
28589
|
|
|
28835
28590
|
// ../cli/src/lib/skill/install.ts
|
|
28836
28591
|
var TARGETS = [
|
|
28837
|
-
{ rel:
|
|
28592
|
+
{ rel: path83.join(".claude", "skills", "kanban"), agent: "Claude Code" },
|
|
28838
28593
|
{
|
|
28839
|
-
rel:
|
|
28594
|
+
rel: path83.join(".agents", "skills", "kanban"),
|
|
28840
28595
|
agent: "Codex, Cursor, OpenCode, Kimi Code, DeepSeek Harness, ZCode, Grok Build"
|
|
28841
28596
|
}
|
|
28842
28597
|
];
|
|
@@ -28850,23 +28605,23 @@ function rulesPath() {
|
|
|
28850
28605
|
}
|
|
28851
28606
|
function read4(file) {
|
|
28852
28607
|
try {
|
|
28853
|
-
return
|
|
28608
|
+
return fs83.readFileSync(file, "utf8");
|
|
28854
28609
|
} catch {
|
|
28855
28610
|
return null;
|
|
28856
28611
|
}
|
|
28857
28612
|
}
|
|
28858
28613
|
function statOf(p) {
|
|
28859
28614
|
try {
|
|
28860
|
-
return
|
|
28615
|
+
return fs83.lstatSync(p);
|
|
28861
28616
|
} catch {
|
|
28862
28617
|
return null;
|
|
28863
28618
|
}
|
|
28864
28619
|
}
|
|
28865
28620
|
function versionIn2(dir) {
|
|
28866
|
-
const note = read4(
|
|
28621
|
+
const note = read4(path83.join(dir, "SKILL.md"));
|
|
28867
28622
|
const stampedNote = note?.match(NOTE_STAMP);
|
|
28868
28623
|
if (stampedNote) return stampedNote[1];
|
|
28869
|
-
const src = read4(
|
|
28624
|
+
const src = read4(path83.join(dir, "kanban.mjs"));
|
|
28870
28625
|
if (!src) return null;
|
|
28871
28626
|
const stamped = src.slice(0, 400).match(BUILT_STAMP);
|
|
28872
28627
|
if (stamped) return stamped[1];
|
|
@@ -28890,7 +28645,7 @@ function folderState(dir) {
|
|
|
28890
28645
|
const st = statOf(dir);
|
|
28891
28646
|
if (!st) return { state: "absent", version: null };
|
|
28892
28647
|
if (st.isSymbolicLink()) return { state: "linked", version: versionIn2(dir) };
|
|
28893
|
-
if (!
|
|
28648
|
+
if (!fs83.existsSync(path83.join(dir, "SKILL.md"))) return { state: "absent", version: null };
|
|
28894
28649
|
const version = versionIn2(dir);
|
|
28895
28650
|
if (!version) return { state: "unknown", version: null };
|
|
28896
28651
|
return { state: isOlder(version, SKILL_VERSION) ? "stale" : "current", version };
|
|
@@ -28898,9 +28653,9 @@ function folderState(dir) {
|
|
|
28898
28653
|
function readSkillState(root) {
|
|
28899
28654
|
const dir = rootOr(root);
|
|
28900
28655
|
const folders2 = TARGETS.map((target) => ({
|
|
28901
|
-
path: target.rel.split(
|
|
28656
|
+
path: target.rel.split(path83.sep).join("/"),
|
|
28902
28657
|
agent: target.agent,
|
|
28903
|
-
...folderState(
|
|
28658
|
+
...folderState(path83.join(dir, target.rel))
|
|
28904
28659
|
}));
|
|
28905
28660
|
return {
|
|
28906
28661
|
version: SKILL_VERSION,
|
|
@@ -28910,9 +28665,9 @@ function readSkillState(root) {
|
|
|
28910
28665
|
};
|
|
28911
28666
|
}
|
|
28912
28667
|
function writeFolder(dir) {
|
|
28913
|
-
|
|
28914
|
-
|
|
28915
|
-
|
|
28668
|
+
fs83.rmSync(dir, { recursive: true, force: true });
|
|
28669
|
+
fs83.mkdirSync(dir, { recursive: true });
|
|
28670
|
+
fs83.writeFileSync(path83.join(dir, "SKILL.md"), `${SKILL_default.trimEnd()}
|
|
28916
28671
|
|
|
28917
28672
|
<!-- ai4kanban ${SKILL_VERSION} -->
|
|
28918
28673
|
`);
|
|
@@ -28927,8 +28682,8 @@ function installSkill(root, only) {
|
|
|
28927
28682
|
const wrote = [];
|
|
28928
28683
|
const skipped = [];
|
|
28929
28684
|
for (const target of TARGETS) {
|
|
28930
|
-
const dest =
|
|
28931
|
-
const shown2 = target.rel.split(
|
|
28685
|
+
const dest = path83.join(dir, target.rel);
|
|
28686
|
+
const shown2 = target.rel.split(path83.sep).join("/");
|
|
28932
28687
|
const { state: state2 } = folderState(dest);
|
|
28933
28688
|
if (state2 === "linked") {
|
|
28934
28689
|
skipped.push({ path: shown2, agent: target.agent, why: "a symlink into a source checkout \u2014 left untouched" });
|
|
@@ -28997,10 +28752,10 @@ var Report = class {
|
|
|
28997
28752
|
for (const line of this.notes) say(` ! ${line}`);
|
|
28998
28753
|
}
|
|
28999
28754
|
};
|
|
29000
|
-
var read5 = (file) =>
|
|
28755
|
+
var read5 = (file) => fs84.existsSync(file) ? fs84.readFileSync(file, "utf8") : null;
|
|
29001
28756
|
function statOf2(p) {
|
|
29002
28757
|
try {
|
|
29003
|
-
return
|
|
28758
|
+
return fs84.lstatSync(p);
|
|
29004
28759
|
} catch {
|
|
29005
28760
|
return null;
|
|
29006
28761
|
}
|
|
@@ -29011,10 +28766,10 @@ async function boardMove(root, args, board2) {
|
|
|
29011
28766
|
if (code !== 0) throw new BoardError(`\`board ${args.join(" ")}\` failed \u2014 nothing else was changed`, { kind: "board-move-failed" });
|
|
29012
28767
|
}
|
|
29013
28768
|
function boardAbove(root) {
|
|
29014
|
-
let at =
|
|
28769
|
+
let at = path84.dirname(path84.resolve(root));
|
|
29015
28770
|
for (; ; ) {
|
|
29016
|
-
if (
|
|
29017
|
-
const up =
|
|
28771
|
+
if (fs84.existsSync(path84.join(at, "docs", "kanban"))) return at;
|
|
28772
|
+
const up = path84.dirname(at);
|
|
29018
28773
|
if (up === at) return null;
|
|
29019
28774
|
at = up;
|
|
29020
28775
|
}
|
|
@@ -29022,10 +28777,10 @@ function boardAbove(root) {
|
|
|
29022
28777
|
async function cmdInstall(ctx) {
|
|
29023
28778
|
const report3 = new Report();
|
|
29024
28779
|
const board2 = ctx.board ?? null;
|
|
29025
|
-
const where = board2 ?
|
|
28780
|
+
const where = board2 ? path84.relative(ctx.dir, board2) || board2 : "docs/kanban";
|
|
29026
28781
|
say(`wildcrane ${SKILL_VERSION} \u2014 installing into ${ctx.dir}`);
|
|
29027
28782
|
say("");
|
|
29028
|
-
const above = board2 ||
|
|
28783
|
+
const above = board2 || fs84.existsSync(path84.join(ctx.dir, "docs", "kanban")) ? null : boardAbove(ctx.dir);
|
|
29029
28784
|
if (above) {
|
|
29030
28785
|
report3.notes.push(`there is already a board at ${above} \u2014 this makes a second one, and commands run here will find this one`);
|
|
29031
28786
|
}
|
|
@@ -29047,7 +28802,7 @@ async function cmdInstall(ctx) {
|
|
|
29047
28802
|
say("board UI (Configuration \u2192 Agent setup), or here:");
|
|
29048
28803
|
say("");
|
|
29049
28804
|
say(` ${ctx.program} skill`);
|
|
29050
|
-
return { installed: ctx.dir, board: board2 ??
|
|
28805
|
+
return { installed: ctx.dir, board: board2 ?? path84.join(ctx.dir, "docs", "kanban") };
|
|
29051
28806
|
}
|
|
29052
28807
|
async function cmdSkill(ctx, mode) {
|
|
29053
28808
|
const report3 = new Report();
|
|
@@ -29088,7 +28843,7 @@ function placeSkill(ctx, mode, report3) {
|
|
|
29088
28843
|
const before = readSkillState(ctx.dir).folders;
|
|
29089
28844
|
for (const folder of before) {
|
|
29090
28845
|
if (folder.state !== "absent" && folder.state !== "linked") {
|
|
29091
|
-
rescueSkillConfig(ctx.dir,
|
|
28846
|
+
rescueSkillConfig(ctx.dir, path84.join(ctx.dir, folder.path), report3);
|
|
29092
28847
|
}
|
|
29093
28848
|
}
|
|
29094
28849
|
const result = installSkill(ctx.dir, mode === "update" ? "present" : void 0);
|
|
@@ -29111,20 +28866,20 @@ function placeSkill(ctx, mode, report3) {
|
|
|
29111
28866
|
return { result, from: before.map((f) => f.version).find(Boolean) || null };
|
|
29112
28867
|
}
|
|
29113
28868
|
function rescueSkillConfig(root, skillDir, report3) {
|
|
29114
|
-
const old = read5(
|
|
28869
|
+
const old = read5(path84.join(skillDir, "config.md"));
|
|
29115
28870
|
if (!old || old.includes("{{")) return;
|
|
29116
|
-
const boardConfig =
|
|
28871
|
+
const boardConfig = path84.join(root, "docs", "kanban", "config.md");
|
|
29117
28872
|
const current = read5(boardConfig);
|
|
29118
28873
|
if (current === null) {
|
|
29119
|
-
|
|
29120
|
-
|
|
28874
|
+
fs84.mkdirSync(path84.dirname(boardConfig), { recursive: true });
|
|
28875
|
+
fs84.writeFileSync(boardConfig, old);
|
|
29121
28876
|
report3.did.push("moved your filled-in config.md out of the skill folder to docs/kanban/config.md");
|
|
29122
28877
|
return;
|
|
29123
28878
|
}
|
|
29124
28879
|
if (current === old) return;
|
|
29125
|
-
const aside =
|
|
29126
|
-
if (
|
|
29127
|
-
|
|
28880
|
+
const aside = path84.join(root, "docs", "kanban", "config.from-skill.md");
|
|
28881
|
+
if (fs84.existsSync(aside)) return;
|
|
28882
|
+
fs84.writeFileSync(aside, old);
|
|
29128
28883
|
report3.notes.push(
|
|
29129
28884
|
"the old skill folder held a different filled-in config.md \u2014 saved as docs/kanban/config.from-skill.md; fold anything worth keeping into docs/kanban/config.md and delete it"
|
|
29130
28885
|
);
|
|
@@ -29177,8 +28932,8 @@ async function cmdUpdate2(ctx) {
|
|
|
29177
28932
|
return { updated: ctx.dir, from, to: SKILL_VERSION };
|
|
29178
28933
|
}
|
|
29179
28934
|
async function repairBoard(root, report3) {
|
|
29180
|
-
const board2 =
|
|
29181
|
-
if (!
|
|
28935
|
+
const board2 = path84.join(root, "docs", "kanban");
|
|
28936
|
+
if (!fs84.existsSync(board2)) {
|
|
29182
28937
|
report3.notes.push("no docs/kanban/ here \u2014 run `akb install` to scaffold the board");
|
|
29183
28938
|
return;
|
|
29184
28939
|
}
|
|
@@ -29208,20 +28963,20 @@ function moveRuntimes(root, board2, report3) {
|
|
|
29208
28963
|
}
|
|
29209
28964
|
}
|
|
29210
28965
|
function moveLegacyMemory(board2, report3) {
|
|
29211
|
-
const memory =
|
|
28966
|
+
const memory = path84.join(board2, "memory");
|
|
29212
28967
|
const moved = [];
|
|
29213
28968
|
for (const name of MEMORY_FILES) {
|
|
29214
|
-
const from =
|
|
28969
|
+
const from = path84.join(board2, name);
|
|
29215
28970
|
const st = statOf2(from);
|
|
29216
28971
|
if (!st || !st.isFile()) continue;
|
|
29217
|
-
|
|
29218
|
-
const to =
|
|
28972
|
+
fs84.mkdirSync(memory, { recursive: true });
|
|
28973
|
+
const to = path84.join(memory, name);
|
|
29219
28974
|
const there = read5(to);
|
|
29220
28975
|
if (there === null) {
|
|
29221
|
-
|
|
28976
|
+
fs84.renameSync(from, to);
|
|
29222
28977
|
moved.push(name);
|
|
29223
28978
|
} else if (there === read5(from)) {
|
|
29224
|
-
|
|
28979
|
+
fs84.rmSync(from);
|
|
29225
28980
|
moved.push(name);
|
|
29226
28981
|
} else {
|
|
29227
28982
|
report3.notes.push(`both docs/kanban/${name} and docs/kanban/memory/${name} exist and differ \u2014 merge them by hand`);
|
|
@@ -29230,23 +28985,23 @@ function moveLegacyMemory(board2, report3) {
|
|
|
29230
28985
|
if (moved.length) report3.did.push(`moved the memory set into docs/kanban/memory/: ${moved.join(", ")}`);
|
|
29231
28986
|
}
|
|
29232
28987
|
function dropRecordFile(board2) {
|
|
29233
|
-
const file =
|
|
29234
|
-
if (!
|
|
29235
|
-
|
|
28988
|
+
const file = path84.join(board2, "record.csv");
|
|
28989
|
+
if (!fs84.existsSync(file)) return null;
|
|
28990
|
+
fs84.rmSync(file);
|
|
29236
28991
|
return "removed docs/kanban/record.csv (the planning scores it fed are gone)";
|
|
29237
28992
|
}
|
|
29238
28993
|
function dropModuleGoals(board2, report3) {
|
|
29239
|
-
const memory =
|
|
29240
|
-
if (!
|
|
29241
|
-
const root = read5(
|
|
28994
|
+
const memory = path84.join(board2, "memory");
|
|
28995
|
+
if (!fs84.existsSync(memory)) return;
|
|
28996
|
+
const root = read5(path84.join(memory, "goal.md"));
|
|
29242
28997
|
let dropped = 0;
|
|
29243
|
-
for (const entry of
|
|
28998
|
+
for (const entry of fs84.readdirSync(memory, { withFileTypes: true })) {
|
|
29244
28999
|
if (!entry.isDirectory()) continue;
|
|
29245
|
-
const file =
|
|
29000
|
+
const file = path84.join(memory, entry.name, "goal.md");
|
|
29246
29001
|
const text5 = read5(file);
|
|
29247
29002
|
if (text5 === null) continue;
|
|
29248
29003
|
if (text5 === root || text5.includes(UNFILLED) || !goalWords(text5)) {
|
|
29249
|
-
|
|
29004
|
+
fs84.rmSync(file);
|
|
29250
29005
|
dropped++;
|
|
29251
29006
|
} else {
|
|
29252
29007
|
report3.notes.push(
|
|
@@ -29268,10 +29023,10 @@ var PLAN_LEADS = {
|
|
|
29268
29023
|
var GONE_LEADS = ["software-planner", "planner"];
|
|
29269
29024
|
function planLeads(board2) {
|
|
29270
29025
|
const leads = { ...PLAN_LEADS };
|
|
29271
|
-
const root =
|
|
29272
|
-
for (const entry of
|
|
29026
|
+
const root = path84.join(board2, "workflows");
|
|
29027
|
+
for (const entry of fs84.existsSync(root) ? fs84.readdirSync(root, { withFileTypes: true }) : []) {
|
|
29273
29028
|
if (!entry.isDirectory()) continue;
|
|
29274
|
-
const meta = splitFrontmatter(read5(
|
|
29029
|
+
const meta = splitFrontmatter(read5(path84.join(root, entry.name, "WORKFLOW.md")) ?? "").meta;
|
|
29275
29030
|
const stages = meta === null ? void 0 : parseYamlBlock(meta).stages;
|
|
29276
29031
|
const plan = stages && typeof stages === "object" && !Array.isArray(stages) ? stages.plan : void 0;
|
|
29277
29032
|
const lead = plan && typeof plan === "object" && !Array.isArray(plan) && typeof plan.lead === "string" ? plan.lead.trim() : "";
|
|
@@ -29280,30 +29035,30 @@ function planLeads(board2) {
|
|
|
29280
29035
|
return leads;
|
|
29281
29036
|
}
|
|
29282
29037
|
function movePlanRules(board2, report3) {
|
|
29283
|
-
const rules =
|
|
29038
|
+
const rules = path84.join(board2, "rules");
|
|
29284
29039
|
const moved = [];
|
|
29285
29040
|
for (const [id, leads] of Object.entries(planLeads(board2))) {
|
|
29286
29041
|
for (const lead of leads) {
|
|
29287
|
-
const from =
|
|
29042
|
+
const from = path84.join(rules, `${lead}.md`);
|
|
29288
29043
|
const rule = read5(from)?.trim();
|
|
29289
29044
|
if (!rule) continue;
|
|
29290
|
-
const to =
|
|
29045
|
+
const to = path84.join(rules, "workflows", `${id}.md`);
|
|
29291
29046
|
const there = read5(to)?.trim() ?? "";
|
|
29292
29047
|
if (!there.includes(rule)) {
|
|
29293
|
-
|
|
29294
|
-
|
|
29048
|
+
fs84.mkdirSync(path84.dirname(to), { recursive: true });
|
|
29049
|
+
fs84.writeFileSync(to, `${[there, rule].filter(Boolean).join("\n\n")}
|
|
29295
29050
|
`);
|
|
29296
29051
|
moved.push(`${lead} \u2192 ${id}`);
|
|
29297
29052
|
}
|
|
29298
29053
|
}
|
|
29299
29054
|
}
|
|
29300
|
-
for (const lead of GONE_LEADS)
|
|
29055
|
+
for (const lead of GONE_LEADS) fs84.rmSync(path84.join(rules, `${lead}.md`), { force: true });
|
|
29301
29056
|
if (moved.length) report3.did.push(`made each plan agent's rule its workflow's card rule (docs/kanban/rules/workflows/): ${moved.join(", ")}`);
|
|
29302
29057
|
}
|
|
29303
29058
|
function noteOldWorkflows(board2, report3) {
|
|
29304
29059
|
const old = (dir, file, keys) => {
|
|
29305
|
-
const root =
|
|
29306
|
-
return (
|
|
29060
|
+
const root = path84.join(board2, dir);
|
|
29061
|
+
return (fs84.existsSync(root) ? fs84.readdirSync(root, { withFileTypes: true }) : []).filter((e) => e.isDirectory() && keys.test(splitFrontmatter(read5(path84.join(root, e.name, file)) ?? "").meta ?? "")).map((e) => `docs/kanban/${dir}/${e.name}/${file}`);
|
|
29307
29062
|
};
|
|
29308
29063
|
const files2 = [...old("workflows", "WORKFLOW.md", /^(stages|delivers):/m), ...old("agents", "AGENT.md", /^\s+(hook|lead):/m)];
|
|
29309
29064
|
if (files2.length) {
|
|
@@ -29313,7 +29068,7 @@ function noteOldWorkflows(board2, report3) {
|
|
|
29313
29068
|
}
|
|
29314
29069
|
}
|
|
29315
29070
|
function checkConfig(board2, report3) {
|
|
29316
|
-
const current = read5(
|
|
29071
|
+
const current = read5(path84.join(board2, "config.md"));
|
|
29317
29072
|
if (!current) return;
|
|
29318
29073
|
const added = missingConfigKeys(current);
|
|
29319
29074
|
if (added.length) {
|
|
@@ -29323,7 +29078,7 @@ function checkConfig(board2, report3) {
|
|
|
29323
29078
|
}
|
|
29324
29079
|
}
|
|
29325
29080
|
function checkModules(board2, report3) {
|
|
29326
|
-
const map2 = read5(
|
|
29081
|
+
const map2 = read5(path84.join(board2, "modules.md"));
|
|
29327
29082
|
if (map2 && map2.includes(UNFILLED)) {
|
|
29328
29083
|
report3.notes.push(
|
|
29329
29084
|
"docs/kanban/modules.md is still blank \u2014 write it from the repo (`akb guide module-map`)"
|
|
@@ -29377,13 +29132,13 @@ function declareSetup(program2, cli) {
|
|
|
29377
29132
|
program2.version(SKILL_VERSION, "-v, --version", "print this version").addHelpText("beforeAll", spelled(cli.program));
|
|
29378
29133
|
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");
|
|
29379
29134
|
const where = (cmd) => {
|
|
29380
|
-
const dir =
|
|
29381
|
-
if (!
|
|
29135
|
+
const dir = path85.resolve(String(cmd.optsWithGlobals().dir ?? cli.cwd));
|
|
29136
|
+
if (!fs85.existsSync(dir)) throw new BoardError(`no such folder: ${dir}`, { kind: "no-such-folder", dir });
|
|
29382
29137
|
return dir;
|
|
29383
29138
|
};
|
|
29384
29139
|
const boardOf = (cmd) => {
|
|
29385
29140
|
const named2 = cmd.optsWithGlobals().board;
|
|
29386
|
-
return named2 ?
|
|
29141
|
+
return named2 ? path85.resolve(cli.cwd, named2) : null;
|
|
29387
29142
|
};
|
|
29388
29143
|
dirOption(program2.command("install")).option("--board <dir>", "put the board here instead of docs/kanban/ (relative to this folder)").summary("create the board in this project, and nothing else").description(
|
|
29389
29144
|
"Installing writes the board and nothing outside docs/kanban/. Driving that board from a coding agent is a later extra \u2014 `akb skill install`, or the button in the board UI under Configuration \u2192 Agent setup. Safe to run twice."
|
|
@@ -29456,7 +29211,7 @@ function invokedDirectly() {
|
|
|
29456
29211
|
const entry = process.argv[1];
|
|
29457
29212
|
if (!entry) return false;
|
|
29458
29213
|
try {
|
|
29459
|
-
return
|
|
29214
|
+
return fs86.realpathSync(entry) === fs86.realpathSync(SELF3);
|
|
29460
29215
|
} catch {
|
|
29461
29216
|
return false;
|
|
29462
29217
|
}
|