wildcrane-runner 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -6
- package/dist/akb/dist/kanban.mjs +1324 -723
- package/dist/launcher.mjs +2 -2
- package/dist/runner.mjs +753 -698
- package/package.json +1 -1
package/dist/akb/dist/kanban.mjs
CHANGED
|
@@ -29,9 +29,9 @@ var __commonJS = (cb, mod) => function __require2() {
|
|
|
29
29
|
throw mod = 0, e;
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
|
-
var __export = (target,
|
|
33
|
-
for (var name in
|
|
34
|
-
__defProp(target, name, { get:
|
|
32
|
+
var __export = (target, all2) => {
|
|
33
|
+
for (var name in all2)
|
|
34
|
+
__defProp(target, name, { get: all2[name], enumerable: true });
|
|
35
35
|
};
|
|
36
36
|
var __copyProps = (to, from, except, desc) => {
|
|
37
37
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
@@ -50,9 +50,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
50
50
|
mod
|
|
51
51
|
));
|
|
52
52
|
|
|
53
|
-
//
|
|
53
|
+
// ../cli/node_modules/commander/lib/error.js
|
|
54
54
|
var require_error = __commonJS({
|
|
55
|
-
"
|
|
55
|
+
"../cli/node_modules/commander/lib/error.js"(exports) {
|
|
56
56
|
var CommanderError2 = class extends Error {
|
|
57
57
|
/**
|
|
58
58
|
* Constructs the CommanderError class
|
|
@@ -85,9 +85,9 @@ var require_error = __commonJS({
|
|
|
85
85
|
}
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
//
|
|
88
|
+
// ../cli/node_modules/commander/lib/argument.js
|
|
89
89
|
var require_argument = __commonJS({
|
|
90
|
-
"
|
|
90
|
+
"../cli/node_modules/commander/lib/argument.js"(exports) {
|
|
91
91
|
var { InvalidArgumentError: InvalidArgumentError2 } = require_error();
|
|
92
92
|
var Argument2 = class {
|
|
93
93
|
/**
|
|
@@ -212,9 +212,9 @@ var require_argument = __commonJS({
|
|
|
212
212
|
}
|
|
213
213
|
});
|
|
214
214
|
|
|
215
|
-
//
|
|
215
|
+
// ../cli/node_modules/commander/lib/help.js
|
|
216
216
|
var require_help = __commonJS({
|
|
217
|
-
"
|
|
217
|
+
"../cli/node_modules/commander/lib/help.js"(exports) {
|
|
218
218
|
var { humanReadableArgName } = require_argument();
|
|
219
219
|
var Help2 = class {
|
|
220
220
|
constructor() {
|
|
@@ -781,9 +781,9 @@ ${itemIndentStr}`);
|
|
|
781
781
|
}
|
|
782
782
|
});
|
|
783
783
|
|
|
784
|
-
//
|
|
784
|
+
// ../cli/node_modules/commander/lib/option.js
|
|
785
785
|
var require_option = __commonJS({
|
|
786
|
-
"
|
|
786
|
+
"../cli/node_modules/commander/lib/option.js"(exports) {
|
|
787
787
|
var { InvalidArgumentError: InvalidArgumentError2 } = require_error();
|
|
788
788
|
var Option2 = class {
|
|
789
789
|
/**
|
|
@@ -1082,9 +1082,9 @@ var require_option = __commonJS({
|
|
|
1082
1082
|
}
|
|
1083
1083
|
});
|
|
1084
1084
|
|
|
1085
|
-
//
|
|
1085
|
+
// ../cli/node_modules/commander/lib/suggestSimilar.js
|
|
1086
1086
|
var require_suggestSimilar = __commonJS({
|
|
1087
|
-
"
|
|
1087
|
+
"../cli/node_modules/commander/lib/suggestSimilar.js"(exports) {
|
|
1088
1088
|
var maxDistance = 3;
|
|
1089
1089
|
function editDistance2(a, b) {
|
|
1090
1090
|
if (Math.abs(a.length - b.length) > maxDistance)
|
|
@@ -1162,13 +1162,13 @@ var require_suggestSimilar = __commonJS({
|
|
|
1162
1162
|
}
|
|
1163
1163
|
});
|
|
1164
1164
|
|
|
1165
|
-
//
|
|
1165
|
+
// ../cli/node_modules/commander/lib/command.js
|
|
1166
1166
|
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 path83 = __require("node:path");
|
|
1171
|
+
var fs88 = __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 (fs88.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 = path83.resolve(baseDir, baseName);
|
|
2171
|
+
if (fs88.existsSync(localBin)) return localBin;
|
|
2172
|
+
if (sourceExt.includes(path83.extname(baseName))) return void 0;
|
|
2173
2173
|
const foundExt = sourceExt.find(
|
|
2174
|
-
(ext) =>
|
|
2174
|
+
(ext) => fs88.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 = fs88.realpathSync(this._scriptPath);
|
|
2187
2187
|
} catch {
|
|
2188
2188
|
resolvedScriptPath = this._scriptPath;
|
|
2189
2189
|
}
|
|
2190
|
-
executableDir =
|
|
2191
|
-
|
|
2190
|
+
executableDir = path83.resolve(
|
|
2191
|
+
path83.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 = path83.basename(
|
|
2199
2199
|
this._scriptPath,
|
|
2200
|
-
|
|
2200
|
+
path83.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(path83.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 = path83.basename(filename, path83.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(path84) {
|
|
3073
|
+
if (path84 === void 0) return this._executableDir;
|
|
3074
|
+
this._executableDir = path84;
|
|
3075
3075
|
return this;
|
|
3076
3076
|
}
|
|
3077
3077
|
/**
|
|
@@ -3321,9 +3321,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3321
3321
|
}
|
|
3322
3322
|
});
|
|
3323
3323
|
|
|
3324
|
-
//
|
|
3324
|
+
// ../cli/node_modules/commander/index.js
|
|
3325
3325
|
var require_commander = __commonJS({
|
|
3326
|
-
"
|
|
3326
|
+
"../cli/node_modules/commander/index.js"(exports) {
|
|
3327
3327
|
var { Argument: Argument2 } = require_argument();
|
|
3328
3328
|
var { Command: Command2 } = require_command();
|
|
3329
3329
|
var { CommanderError: CommanderError2, InvalidArgumentError: InvalidArgumentError2 } = require_error();
|
|
@@ -3343,10 +3343,10 @@ var require_commander = __commonJS({
|
|
|
3343
3343
|
}
|
|
3344
3344
|
});
|
|
3345
3345
|
|
|
3346
|
-
//
|
|
3346
|
+
// ../cli/node_modules/commander/esm.mjs
|
|
3347
3347
|
var import_index, program, createCommand, createArgument, createOption, CommanderError, InvalidArgumentError, InvalidOptionArgumentError, Command, Argument, Option, Help;
|
|
3348
3348
|
var init_esm = __esm({
|
|
3349
|
-
"
|
|
3349
|
+
"../cli/node_modules/commander/esm.mjs"() {
|
|
3350
3350
|
import_index = __toESM(require_commander(), 1);
|
|
3351
3351
|
({
|
|
3352
3352
|
program,
|
|
@@ -3637,11 +3637,11 @@ function boardPath() {
|
|
|
3637
3637
|
return rel(KANBAN).split(path3.sep).join("/");
|
|
3638
3638
|
}
|
|
3639
3639
|
function boardText(text5) {
|
|
3640
|
-
const
|
|
3640
|
+
const posix3 = (p) => rel(p).split(path3.sep).join("/");
|
|
3641
3641
|
const swaps = [
|
|
3642
|
-
[ASSETS_PATH_IN_TEXT,
|
|
3643
|
-
[MOCKUPS_PATH_IN_TEXT,
|
|
3644
|
-
[BOARD_STATE_IN_TEXT,
|
|
3642
|
+
[ASSETS_PATH_IN_TEXT, posix3(ASSETS)],
|
|
3643
|
+
[MOCKUPS_PATH_IN_TEXT, posix3(MOCKUPS)],
|
|
3644
|
+
[BOARD_STATE_IN_TEXT, posix3(BOARD_STATE)],
|
|
3645
3645
|
[BOARD_PATH_IN_TEXT, boardPath()]
|
|
3646
3646
|
];
|
|
3647
3647
|
const swap = (part, i) => i === swaps.length ? part : part.split(swaps[i][0]).map((p) => swap(p, i + 1)).join(swaps[i][1]);
|
|
@@ -3850,8 +3850,8 @@ var init_question_block = __esm({
|
|
|
3850
3850
|
// ../cli/src/lib/view/rules.ts
|
|
3851
3851
|
function creationRefusal(id, creation, what, discard = false) {
|
|
3852
3852
|
if (!creation || creation.state === "unfinished" && discard) return null;
|
|
3853
|
-
const
|
|
3854
|
-
return creation.state === "creating" ? `#${id} is still being created by run ${
|
|
3853
|
+
const run2 = creation.runId.slice(0, 8);
|
|
3854
|
+
return creation.state === "creating" ? `#${id} is still being created by run ${run2} \u2014 \`${what}\` is refused until that run finishes.` : `#${id} was never finished being created: run ${run2} stopped short. \`${what}\` is refused until that run is picked back up from the board and finishes. Use reject --discard to discard it instead.`;
|
|
3855
3855
|
}
|
|
3856
3856
|
function hasOptions(q) {
|
|
3857
3857
|
return Array.isArray(q.options) && q.options.length > 0;
|
|
@@ -4481,10 +4481,10 @@ function claimBreak(dir) {
|
|
|
4481
4481
|
return false;
|
|
4482
4482
|
}
|
|
4483
4483
|
}
|
|
4484
|
-
function removeClaim(dir,
|
|
4485
|
-
if (
|
|
4484
|
+
function removeClaim(dir, token3) {
|
|
4485
|
+
if (token3) {
|
|
4486
4486
|
try {
|
|
4487
|
-
fs7.unlinkSync(path8.join(dir,
|
|
4487
|
+
fs7.unlinkSync(path8.join(dir, token3));
|
|
4488
4488
|
} catch (err) {
|
|
4489
4489
|
if (err.code !== "ENOENT") throw err;
|
|
4490
4490
|
}
|
|
@@ -4501,15 +4501,15 @@ function removeClaim(dir, token2) {
|
|
|
4501
4501
|
}
|
|
4502
4502
|
function tryLock(dir) {
|
|
4503
4503
|
fs7.mkdirSync(path8.dirname(dir), { recursive: true });
|
|
4504
|
-
const
|
|
4504
|
+
const token3 = `${process.pid}-${randomUUID()}`;
|
|
4505
4505
|
const candidate = fs7.mkdtempSync(`${dir}.`);
|
|
4506
4506
|
try {
|
|
4507
|
-
fs7.writeFileSync(path8.join(candidate,
|
|
4507
|
+
fs7.writeFileSync(path8.join(candidate, token3), "");
|
|
4508
4508
|
for (; ; ) {
|
|
4509
4509
|
try {
|
|
4510
4510
|
fs7.renameSync(candidate, dir);
|
|
4511
4511
|
return () => {
|
|
4512
|
-
removeClaim(dir,
|
|
4512
|
+
removeClaim(dir, token3);
|
|
4513
4513
|
};
|
|
4514
4514
|
} catch (err) {
|
|
4515
4515
|
const code = err.code;
|
|
@@ -5133,16 +5133,12 @@ var init_catalog = __esm({
|
|
|
5133
5133
|
function isLanguage(value) {
|
|
5134
5134
|
return typeof value === "string" && LANGUAGES.includes(value);
|
|
5135
5135
|
}
|
|
5136
|
-
var LANGUAGES, DEFAULT_LANGUAGE
|
|
5136
|
+
var LANGUAGES, DEFAULT_LANGUAGE;
|
|
5137
5137
|
var init_types = __esm({
|
|
5138
5138
|
"../cli/src/lib/machine/types.ts"() {
|
|
5139
5139
|
"use strict";
|
|
5140
5140
|
LANGUAGES = ["en", "zh"];
|
|
5141
5141
|
DEFAULT_LANGUAGE = "en";
|
|
5142
|
-
LANGUAGE_NAMES = {
|
|
5143
|
-
en: "English",
|
|
5144
|
-
zh: "\u4E2D\u6587"
|
|
5145
|
-
};
|
|
5146
5142
|
}
|
|
5147
5143
|
});
|
|
5148
5144
|
|
|
@@ -5483,13 +5479,13 @@ async function attempt(now) {
|
|
|
5483
5479
|
}
|
|
5484
5480
|
function fill(queued, install) {
|
|
5485
5481
|
const events = [];
|
|
5486
|
-
let
|
|
5482
|
+
let bytes3 = Buffer.byteLength(JSON.stringify({ v: VERSION, install, events: [] }));
|
|
5487
5483
|
for (const event2 of queued) {
|
|
5488
5484
|
if (events.length >= LIMITS.batchEvents) break;
|
|
5489
5485
|
const piece = Buffer.byteLength(JSON.stringify(event2)) + (events.length ? 1 : 0);
|
|
5490
|
-
if (
|
|
5486
|
+
if (bytes3 + piece > LIMITS.batchBytes) break;
|
|
5491
5487
|
events.push(event2);
|
|
5492
|
-
|
|
5488
|
+
bytes3 += piece;
|
|
5493
5489
|
}
|
|
5494
5490
|
return { events, body: JSON.stringify({ v: VERSION, install, events }) };
|
|
5495
5491
|
}
|
|
@@ -5585,14 +5581,14 @@ function currentTurn() {
|
|
|
5585
5581
|
function forgetFileMemos() {
|
|
5586
5582
|
turn++;
|
|
5587
5583
|
}
|
|
5588
|
-
function memoByFiles(paths,
|
|
5584
|
+
function memoByFiles(paths, read6) {
|
|
5589
5585
|
let last = null;
|
|
5590
5586
|
return () => {
|
|
5591
5587
|
const now = currentTurn();
|
|
5592
5588
|
if (last?.turn !== now) {
|
|
5593
5589
|
const seen = stamp2(paths());
|
|
5594
5590
|
if (last?.stamp === seen) last.turn = now;
|
|
5595
|
-
else last = { stamp: seen, turn: now, value:
|
|
5591
|
+
else last = { stamp: seen, turn: now, value: read6() };
|
|
5596
5592
|
}
|
|
5597
5593
|
return copy(last.value);
|
|
5598
5594
|
};
|
|
@@ -6653,16 +6649,16 @@ function readCatalog() {
|
|
|
6653
6649
|
const agents = [];
|
|
6654
6650
|
const problems = [];
|
|
6655
6651
|
const refused = [];
|
|
6656
|
-
const take = (
|
|
6657
|
-
if ("problem" in
|
|
6658
|
-
if (!
|
|
6659
|
-
if (!
|
|
6660
|
-
const { file, name: name2, old } =
|
|
6661
|
-
const cause =
|
|
6652
|
+
const take = (read6, folder) => {
|
|
6653
|
+
if ("problem" in read6) {
|
|
6654
|
+
if (!read6.missing) problems.push(read6.problem);
|
|
6655
|
+
if (!read6.file) return;
|
|
6656
|
+
const { file, name: name2, old } = read6;
|
|
6657
|
+
const cause = read6.missing ? "noFile" : old ? "oldKeys" : "file";
|
|
6662
6658
|
refused.push({ folder, file, cause, ...name2 ? { name: name2 } : {}, ...old ? { keys: old.keys } : {} });
|
|
6663
6659
|
return;
|
|
6664
6660
|
}
|
|
6665
|
-
const { name, from, builtIn } =
|
|
6661
|
+
const { name, from, builtIn } = read6.agent;
|
|
6666
6662
|
const refuse3 = (cause, problem) => {
|
|
6667
6663
|
problems.push(problem);
|
|
6668
6664
|
if (!builtIn) refused.push({ folder, name, file: from, cause });
|
|
@@ -6686,14 +6682,14 @@ function readCatalog() {
|
|
|
6686
6682
|
`${from}: its folder is \`${folder}\` but it calls itself \`${name}\`. An agent is asked for by its folder name \u2014 make the two match.`
|
|
6687
6683
|
);
|
|
6688
6684
|
}
|
|
6689
|
-
agents.push(
|
|
6685
|
+
agents.push(read6.agent);
|
|
6690
6686
|
};
|
|
6691
6687
|
for (const folder of bundledFolders()) take(readBundled(folder), folder);
|
|
6692
6688
|
for (const folder of projectFolders(AGENTS)) {
|
|
6693
|
-
const
|
|
6694
|
-
take(
|
|
6695
|
-
if ("agent" in
|
|
6696
|
-
problems.push(`${
|
|
6689
|
+
const read6 = readProject(AGENTS, folder);
|
|
6690
|
+
take(read6, folder);
|
|
6691
|
+
if ("agent" in read6 && read6.agent.from.endsWith(LEGACY_AGENT_FILE)) {
|
|
6692
|
+
problems.push(`${read6.agent.from}: rename it to ${AGENT_FILE}`);
|
|
6697
6693
|
}
|
|
6698
6694
|
}
|
|
6699
6695
|
for (const folder of projectFolders(LEGACY_AGENTS)) {
|
|
@@ -6795,10 +6791,10 @@ function readProject(root, folder) {
|
|
|
6795
6791
|
const missing = rel(path17.join(dir, AGENT_FILE));
|
|
6796
6792
|
if (!found2) return { problem: `${missing} is missing`, file: missing, missing: true };
|
|
6797
6793
|
const from = rel(path17.join(dir, found2.name));
|
|
6798
|
-
const
|
|
6799
|
-
if ("problem" in
|
|
6800
|
-
if ("agent" in
|
|
6801
|
-
return
|
|
6794
|
+
const read6 = parseSpecAgent(found2.text, from, file, false, () => ownFiles(walkFolder(dir, "")));
|
|
6795
|
+
if ("problem" in read6) return { ...read6, file: from };
|
|
6796
|
+
if ("agent" in read6) Object.assign(read6.agent, { dir, text: found2.text });
|
|
6797
|
+
return read6;
|
|
6802
6798
|
}
|
|
6803
6799
|
var specAgentCatalog, bundledFolders, AGENT_FILE, LEGACY_AGENT_FILE, AGENT_FILES, ownFiles;
|
|
6804
6800
|
var init_catalog2 = __esm({
|
|
@@ -7602,10 +7598,10 @@ function findRollout(threadId) {
|
|
|
7602
7598
|
}
|
|
7603
7599
|
for (const name of names5) {
|
|
7604
7600
|
if (!name.startsWith("rollout-") || !name.endsWith(tail2)) continue;
|
|
7605
|
-
const
|
|
7601
|
+
const path83 = join(dir, name);
|
|
7606
7602
|
try {
|
|
7607
|
-
const at = statSync(
|
|
7608
|
-
if (!newest || at > newest.at) newest = { path:
|
|
7603
|
+
const at = statSync(path83).mtimeMs;
|
|
7604
|
+
if (!newest || at > newest.at) newest = { path: path83, at };
|
|
7609
7605
|
} catch {
|
|
7610
7606
|
}
|
|
7611
7607
|
}
|
|
@@ -7613,11 +7609,11 @@ function findRollout(threadId) {
|
|
|
7613
7609
|
return newest?.path;
|
|
7614
7610
|
}
|
|
7615
7611
|
function readCodexRunFacts(threadId) {
|
|
7616
|
-
const
|
|
7617
|
-
if (!
|
|
7612
|
+
const path83 = findRollout(threadId);
|
|
7613
|
+
if (!path83) return void 0;
|
|
7618
7614
|
let raw;
|
|
7619
7615
|
try {
|
|
7620
|
-
raw = readFileSync(
|
|
7616
|
+
raw = readFileSync(path83, "utf8");
|
|
7621
7617
|
} catch {
|
|
7622
7618
|
return void 0;
|
|
7623
7619
|
}
|
|
@@ -7745,8 +7741,8 @@ function renderCompleted(item) {
|
|
|
7745
7741
|
const changes = Array.isArray(item.changes) ? item.changes : [];
|
|
7746
7742
|
return changes.map((raw) => {
|
|
7747
7743
|
const change2 = obj(raw);
|
|
7748
|
-
const
|
|
7749
|
-
return
|
|
7744
|
+
const path83 = str2(change2.path);
|
|
7745
|
+
return path83 ? `\u23FA ${str2(change2.kind) || "change"}(${path83})
|
|
7750
7746
|
` : "";
|
|
7751
7747
|
}).join("");
|
|
7752
7748
|
}
|
|
@@ -8005,10 +8001,10 @@ function readKimiRunFacts(sessionId) {
|
|
|
8005
8001
|
const total = { input: 0, cacheCreation: 0, cacheRead: 0, output: 0 };
|
|
8006
8002
|
let model;
|
|
8007
8003
|
let holding = 0;
|
|
8008
|
-
for (const { path:
|
|
8004
|
+
for (const { path: path83, main } of wireFiles(entry.sessionDir)) {
|
|
8009
8005
|
let raw;
|
|
8010
8006
|
try {
|
|
8011
|
-
raw = readFileSync2(
|
|
8007
|
+
raw = readFileSync2(path83, "utf8");
|
|
8012
8008
|
} catch {
|
|
8013
8009
|
continue;
|
|
8014
8010
|
}
|
|
@@ -8289,8 +8285,8 @@ var init_tail = __esm({
|
|
|
8289
8285
|
import fs20 from "node:fs";
|
|
8290
8286
|
function whereHint(update2, title) {
|
|
8291
8287
|
const locations = Array.isArray(update2.locations) ? update2.locations : [];
|
|
8292
|
-
const
|
|
8293
|
-
return
|
|
8288
|
+
const path83 = str2(obj(locations[0]).path);
|
|
8289
|
+
return path83 && !title.includes(path83) ? hint(path83) : "";
|
|
8294
8290
|
}
|
|
8295
8291
|
function saidBy(update2) {
|
|
8296
8292
|
const content = Array.isArray(update2.content) ? update2.content : [];
|
|
@@ -8761,8 +8757,8 @@ async function tokensOf(rpc, sessionId) {
|
|
|
8761
8757
|
}
|
|
8762
8758
|
async function contextOf(rpc, sessionId) {
|
|
8763
8759
|
try {
|
|
8764
|
-
const
|
|
8765
|
-
const state = obj(
|
|
8760
|
+
const read6 = await rpc.call("session/read", { sessionId, messageLimit: 1 });
|
|
8761
|
+
const state = obj(read6.projection);
|
|
8766
8762
|
const used = num(state.contextUsed);
|
|
8767
8763
|
const limit = num(state.contextWindow);
|
|
8768
8764
|
return used > 0 ? { used, limit: limit > 0 ? limit : void 0 } : void 0;
|
|
@@ -9259,9 +9255,9 @@ function outsideBoardForAdd(cwd = REPO_ROOT) {
|
|
|
9259
9255
|
);
|
|
9260
9256
|
return ["--", ".", ...kept.map((p) => `:(exclude)${p}`)];
|
|
9261
9257
|
}
|
|
9262
|
-
function boardPathsAmong(
|
|
9258
|
+
function boardPathsAmong(files2) {
|
|
9263
9259
|
const owned = boardManagedPaths();
|
|
9264
|
-
return
|
|
9260
|
+
return files2.filter((file) => {
|
|
9265
9261
|
const p = posix(file);
|
|
9266
9262
|
return owned.some((dir) => p === dir || p.startsWith(`${dir}/`));
|
|
9267
9263
|
});
|
|
@@ -9375,9 +9371,9 @@ function treeMark(base, cwd = REPO_ROOT) {
|
|
|
9375
9371
|
const index = path22.join(ensureAkbDir(), `index-${process.pid}-${Date.now()}`);
|
|
9376
9372
|
try {
|
|
9377
9373
|
const env = { ...process.env, GIT_INDEX_FILE: index };
|
|
9378
|
-
const
|
|
9379
|
-
if (!
|
|
9380
|
-
if (!
|
|
9374
|
+
const run2 = (args) => spawnSync2("git", args, { cwd, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true, env }).status === 0;
|
|
9375
|
+
if (!run2(["read-tree", base])) return null;
|
|
9376
|
+
if (!run2(["add", "-A", ...outsideBoardForAdd(cwd)])) return null;
|
|
9381
9377
|
const tree = spawnSync2("git", ["write-tree"], {
|
|
9382
9378
|
cwd,
|
|
9383
9379
|
encoding: "utf8",
|
|
@@ -9467,7 +9463,7 @@ function ignoredInTheWay(commit, cwd) {
|
|
|
9467
9463
|
}
|
|
9468
9464
|
function blockedBy(why) {
|
|
9469
9465
|
let kind;
|
|
9470
|
-
const
|
|
9466
|
+
const files2 = [];
|
|
9471
9467
|
for (const line2 of why.split("\n")) {
|
|
9472
9468
|
if (!kind) {
|
|
9473
9469
|
kind = headingKind(line2);
|
|
@@ -9475,9 +9471,9 @@ function blockedBy(why) {
|
|
|
9475
9471
|
}
|
|
9476
9472
|
if (!/^\s/.test(line2)) break;
|
|
9477
9473
|
const file = line2.trim();
|
|
9478
|
-
if (file)
|
|
9474
|
+
if (file) files2.push(file);
|
|
9479
9475
|
}
|
|
9480
|
-
return kind &&
|
|
9476
|
+
return kind && files2.length ? { kind, files: files2 } : void 0;
|
|
9481
9477
|
}
|
|
9482
9478
|
function branchWorktree(branch) {
|
|
9483
9479
|
const out = git(["worktree", "list", "--porcelain"]);
|
|
@@ -9523,7 +9519,7 @@ var init_worktree = __esm({
|
|
|
9523
9519
|
return out === null ? null : out.split("\n").filter(Boolean);
|
|
9524
9520
|
};
|
|
9525
9521
|
branchPatch = (base, branch, cwd = REPO_ROOT) => gitDiff(["diff", "--binary", base, branch, ...outsideBoard()], cwd);
|
|
9526
|
-
lastCommitTouching = (ref,
|
|
9522
|
+
lastCommitTouching = (ref, files2, cwd = REPO_ROOT) => git(["log", "-1", "--format=%H", ref, "--", ...files2], cwd)?.trim() || null;
|
|
9527
9523
|
continueRebase = (dir) => scripted(["rebase", "--continue"], dir);
|
|
9528
9524
|
abortRebase = (dir) => void scripted(["rebase", "--abort"], dir);
|
|
9529
9525
|
conflictedPaths = (dir) => (git(["diff", "--name-only", "--diff-filter=U"], dir) ?? "").split("\n").filter(Boolean);
|
|
@@ -9591,8 +9587,8 @@ function modified(file) {
|
|
|
9591
9587
|
function byPath(a, b) {
|
|
9592
9588
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
9593
9589
|
}
|
|
9594
|
-
function byNewest(
|
|
9595
|
-
return
|
|
9590
|
+
function byNewest(files2) {
|
|
9591
|
+
return files2.map((file) => ({ file, at: modified(file) })).sort((a, b) => b.at - a.at || byPath(a.file, b.file)).map((one) => one.file);
|
|
9596
9592
|
}
|
|
9597
9593
|
function versionOf(name) {
|
|
9598
9594
|
const found2 = /(\d+(?:\.\d+)+)/.exec(name);
|
|
@@ -11058,17 +11054,17 @@ function noteRefineTried(store, cardId2) {
|
|
|
11058
11054
|
}
|
|
11059
11055
|
function cardsBeingCreated(runs = readRuns()) {
|
|
11060
11056
|
const newest = /* @__PURE__ */ new Map();
|
|
11061
|
-
for (const
|
|
11062
|
-
for (const id of
|
|
11063
|
-
if (
|
|
11057
|
+
for (const run2 of runs) {
|
|
11058
|
+
for (const id of run2.createdCardIds ?? []) {
|
|
11059
|
+
if (run2.cardId === id) continue;
|
|
11064
11060
|
const best = newest.get(id);
|
|
11065
|
-
if (!best ||
|
|
11061
|
+
if (!best || run2.startedAt >= best.startedAt) newest.set(id, run2);
|
|
11066
11062
|
}
|
|
11067
11063
|
}
|
|
11068
11064
|
const out = /* @__PURE__ */ new Map();
|
|
11069
|
-
for (const [id,
|
|
11070
|
-
if (runIsLive(
|
|
11071
|
-
else if (
|
|
11065
|
+
for (const [id, run2] of newest) {
|
|
11066
|
+
if (runIsLive(run2)) out.set(id, { state: "creating", runId: run2.sessionId });
|
|
11067
|
+
else if (run2.status !== "done") out.set(id, { state: "unfinished", runId: run2.sessionId });
|
|
11072
11068
|
}
|
|
11073
11069
|
return out;
|
|
11074
11070
|
}
|
|
@@ -11156,8 +11152,8 @@ function readTouched(raw) {
|
|
|
11156
11152
|
}
|
|
11157
11153
|
function readPictures(raw) {
|
|
11158
11154
|
if (!Array.isArray(raw)) return void 0;
|
|
11159
|
-
const
|
|
11160
|
-
return
|
|
11155
|
+
const files2 = raw.filter((p) => typeof p === "string" && p.length > 0);
|
|
11156
|
+
return files2.length ? files2 : void 0;
|
|
11161
11157
|
}
|
|
11162
11158
|
function readBlocker(raw) {
|
|
11163
11159
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
@@ -11214,18 +11210,18 @@ function readLandingWait(raw) {
|
|
|
11214
11210
|
if (!raw || typeof raw !== "object") return void 0;
|
|
11215
11211
|
const box2 = raw;
|
|
11216
11212
|
if (box2.kind !== "overwrite" && box2.kind !== "untracked") return void 0;
|
|
11217
|
-
const
|
|
11218
|
-
return
|
|
11213
|
+
const files2 = Array.isArray(box2.files) ? box2.files.filter((f) => typeof f === "string") : [];
|
|
11214
|
+
return files2.length ? { kind: box2.kind, files: files2 } : void 0;
|
|
11219
11215
|
}
|
|
11220
11216
|
function readLandingReason(raw) {
|
|
11221
11217
|
if (!raw || typeof raw !== "object") return void 0;
|
|
11222
11218
|
const box2 = raw;
|
|
11223
11219
|
if (!LANDING_REASONS.has(box2.kind)) return void 0;
|
|
11224
|
-
const
|
|
11220
|
+
const files2 = Array.isArray(box2.files) ? box2.files.filter((f) => typeof f === "string") : [];
|
|
11225
11221
|
return {
|
|
11226
11222
|
kind: box2.kind,
|
|
11227
11223
|
...text2(box2.behind) ? { behind: text2(box2.behind) } : {},
|
|
11228
|
-
...
|
|
11224
|
+
...files2.length ? { files: files2 } : {}
|
|
11229
11225
|
};
|
|
11230
11226
|
}
|
|
11231
11227
|
function readAnswers(raw) {
|
|
@@ -11249,7 +11245,7 @@ function readReview(raw) {
|
|
|
11249
11245
|
if (!raw || typeof raw !== "object") return void 0;
|
|
11250
11246
|
const box2 = raw;
|
|
11251
11247
|
const rounds = Array.isArray(box2.rounds) ? box2.rounds : [];
|
|
11252
|
-
const
|
|
11248
|
+
const stop2 = box2.stopped;
|
|
11253
11249
|
return {
|
|
11254
11250
|
rounds: rounds.flatMap(
|
|
11255
11251
|
(r) => r && typeof r.sessionId === "string" && isVerdict(r.verdict) ? [
|
|
@@ -11263,11 +11259,11 @@ function readReview(raw) {
|
|
|
11263
11259
|
}
|
|
11264
11260
|
] : []
|
|
11265
11261
|
),
|
|
11266
|
-
stopped:
|
|
11267
|
-
reason: asStopReason(
|
|
11268
|
-
why:
|
|
11269
|
-
at: typeof
|
|
11270
|
-
...Array.isArray(
|
|
11262
|
+
stopped: stop2 && typeof stop2 === "object" && typeof stop2.why === "string" ? {
|
|
11263
|
+
reason: asStopReason(stop2.reason),
|
|
11264
|
+
why: stop2.why,
|
|
11265
|
+
at: typeof stop2.at === "number" ? stop2.at : 0,
|
|
11266
|
+
...Array.isArray(stop2.paths) ? { paths: stop2.paths.filter((p) => typeof p === "string") } : {}
|
|
11271
11267
|
} : void 0
|
|
11272
11268
|
};
|
|
11273
11269
|
}
|
|
@@ -11330,9 +11326,9 @@ function recordCreatedCards(sessionId, ids) {
|
|
|
11330
11326
|
const valid = ids.filter((id) => Number.isInteger(id) && id > 0);
|
|
11331
11327
|
if (!valid.length) return false;
|
|
11332
11328
|
return withRuns((runs) => {
|
|
11333
|
-
const
|
|
11334
|
-
if (!
|
|
11335
|
-
|
|
11329
|
+
const run2 = runs.find((entry) => entry.sessionId === sessionId && entry.status === "running");
|
|
11330
|
+
if (!run2) return false;
|
|
11331
|
+
run2.createdCardIds = [.../* @__PURE__ */ new Set([...run2.createdCardIds ?? [], ...valid])];
|
|
11336
11332
|
return true;
|
|
11337
11333
|
});
|
|
11338
11334
|
}
|
|
@@ -11382,7 +11378,7 @@ var init_store = __esm({
|
|
|
11382
11378
|
stringRecord = (raw) => raw && typeof raw === "object" && !Array.isArray(raw) && Object.values(raw).every((v) => typeof v === "string") ? raw : void 0;
|
|
11383
11379
|
readRuns = () => readStore().runs;
|
|
11384
11380
|
PENDING_MS = 3e4;
|
|
11385
|
-
runIsLive = (
|
|
11381
|
+
runIsLive = (run2) => run2.status === "running" && (run2.pid ? pidAlive(run2.pid) : Date.now() - run2.startedAt < PENDING_MS);
|
|
11386
11382
|
creationOf = (runs, cardId2) => cardsBeingCreated(runs).get(cardId2);
|
|
11387
11383
|
cardCreation = (cardId2, strict = false) => {
|
|
11388
11384
|
const runs = readRuns();
|
|
@@ -11553,24 +11549,28 @@ var init_flows = __esm({
|
|
|
11553
11549
|
});
|
|
11554
11550
|
|
|
11555
11551
|
// ../cli/src/lib/agent/language.ts
|
|
11556
|
-
function
|
|
11552
|
+
function runLanguage() {
|
|
11553
|
+
const asked = process.env[RUN_LANGUAGE_ENV];
|
|
11554
|
+
return isLanguage(asked) ? asked : readLanguage();
|
|
11555
|
+
}
|
|
11556
|
+
function translating(language = runLanguage()) {
|
|
11557
11557
|
return language !== DEFAULT_LANGUAGE;
|
|
11558
11558
|
}
|
|
11559
|
-
function languageNote(language =
|
|
11560
|
-
|
|
11561
|
-
const name = LANGUAGE_NAMES[language];
|
|
11562
|
-
return [
|
|
11563
|
-
`Write this board's prose in ${name}: card titles and bodies, open questions and their options, memory notes, and what you say back to me.`,
|
|
11564
|
-
`Keep English whatever the language: frontmatter keys and their fixed values, \`##\` and \`###\` section headings, the \`<!-- agent -->\` boundary, todo checkboxes, the \`[user]\` tag, track and module names, and card filenames. Never rename or translate a section title \u2014 the board matches those by literal English text, so a changed one is a card it can no longer read, and a title that is not English needs \`akb raw create --slug\` a short English slug.`,
|
|
11565
|
-
`Rewriting a card or a memory file that already exists keeps the language that file is already in; only what you write for me to read personally \u2014 an open question and its options \u2014 follows ${name} on every card, and so does the first note in a memory file still holding nothing but its seeded header.`,
|
|
11566
|
-
`This governs the board's prose alone: code, comments, commit messages and the repository's own documents follow the repository, not me. \`akb guide board\` carries the rule in full.`
|
|
11567
|
-
].join(" ");
|
|
11559
|
+
function languageNote(language = runLanguage()) {
|
|
11560
|
+
return LINES[language];
|
|
11568
11561
|
}
|
|
11562
|
+
var RUN_LANGUAGE_ENV, LINES, inLanguage;
|
|
11569
11563
|
var init_language = __esm({
|
|
11570
11564
|
"../cli/src/lib/agent/language.ts"() {
|
|
11571
11565
|
"use strict";
|
|
11572
11566
|
init_settings();
|
|
11573
11567
|
init_types();
|
|
11568
|
+
RUN_LANGUAGE_ENV = "AI4KANBAN_RUN_LANGUAGE";
|
|
11569
|
+
LINES = {
|
|
11570
|
+
en: "",
|
|
11571
|
+
zh: "\u8BF7\u7528\u4E2D\u6587\u56DE\u590D\u548C\u5199\u4F5C\u3002"
|
|
11572
|
+
};
|
|
11573
|
+
inLanguage = (prompt, language = runLanguage()) => [prompt, languageNote(language)].filter(Boolean).join("\n\n");
|
|
11574
11574
|
}
|
|
11575
11575
|
});
|
|
11576
11576
|
|
|
@@ -12497,11 +12497,11 @@ function modelsRun() {
|
|
|
12497
12497
|
const ran = /* @__PURE__ */ new Map();
|
|
12498
12498
|
const { runs } = readStore();
|
|
12499
12499
|
for (let i = runs.length - 1; i >= 0; i--) {
|
|
12500
|
-
const
|
|
12501
|
-
if (!
|
|
12502
|
-
const seen = ran.get(
|
|
12503
|
-
if (!seen.includes(
|
|
12504
|
-
ran.set(
|
|
12500
|
+
const run2 = runs[i];
|
|
12501
|
+
if (!run2.harness || !run2.model) continue;
|
|
12502
|
+
const seen = ran.get(run2.harness) ?? [];
|
|
12503
|
+
if (!seen.includes(run2.model)) seen.push(run2.model);
|
|
12504
|
+
ran.set(run2.harness, seen);
|
|
12505
12505
|
}
|
|
12506
12506
|
return ran;
|
|
12507
12507
|
}
|
|
@@ -13346,9 +13346,9 @@ function boardWorkflowDefs() {
|
|
|
13346
13346
|
problems.push(`${from} is missing`);
|
|
13347
13347
|
continue;
|
|
13348
13348
|
}
|
|
13349
|
-
const
|
|
13350
|
-
if ("problem" in
|
|
13351
|
-
else defs.push(
|
|
13349
|
+
const read6 = parseWorkflowFile(id, source, from);
|
|
13350
|
+
if ("problem" in read6) problems.push(read6.problem);
|
|
13351
|
+
else defs.push(read6.def);
|
|
13352
13352
|
}
|
|
13353
13353
|
return { defs, problems };
|
|
13354
13354
|
}
|
|
@@ -13392,9 +13392,9 @@ var init_workflow_file = __esm({
|
|
|
13392
13392
|
plain = (agents) => agents.every((h) => !h.extra && !h.off);
|
|
13393
13393
|
flowList = (agents) => `[${agents.map((h) => scalar(h.agent)).join(", ")}]`;
|
|
13394
13394
|
BUILTIN_DEFS = BUNDLED.map(([id, source]) => {
|
|
13395
|
-
const
|
|
13396
|
-
if ("problem" in
|
|
13397
|
-
return
|
|
13395
|
+
const read6 = parseWorkflowFile(id, source, `the built-in \`${id}\` workflow`);
|
|
13396
|
+
if ("problem" in read6) throw new Error(read6.problem);
|
|
13397
|
+
return read6.def;
|
|
13398
13398
|
});
|
|
13399
13399
|
fileOf = (id) => path30.join(WORKFLOWS, id, WORKFLOW_FILE);
|
|
13400
13400
|
}
|
|
@@ -13465,8 +13465,8 @@ function foldOldStages(cfg) {
|
|
|
13465
13465
|
if (!savedOldStages(cfg)) return false;
|
|
13466
13466
|
const { ok } = writeConfig((raw) => {
|
|
13467
13467
|
const block = configBlock(raw.workflows);
|
|
13468
|
-
const
|
|
13469
|
-
for (const [id, flow] of Object.entries(
|
|
13468
|
+
const all2 = configBlock(block.stages);
|
|
13469
|
+
for (const [id, flow] of Object.entries(all2)) {
|
|
13470
13470
|
const mine = { ...configBlock(flow) };
|
|
13471
13471
|
const plan = readRows(mine.plan);
|
|
13472
13472
|
const lead = configBlock(mine.execute).lead;
|
|
@@ -13476,10 +13476,10 @@ function foldOldStages(cfg) {
|
|
|
13476
13476
|
if (typeof lead === "string" && lead.trim() && isBuiltinWorkflow(id) && !BUILTINS.find((w) => w.id === id).land.length) {
|
|
13477
13477
|
mine[LAND] = { helpers: [{ agent: canonicalSpecAgent(lead), extra: "" }] };
|
|
13478
13478
|
}
|
|
13479
|
-
if (Object.keys(mine).length)
|
|
13480
|
-
else delete
|
|
13479
|
+
if (Object.keys(mine).length) all2[id] = mine;
|
|
13480
|
+
else delete all2[id];
|
|
13481
13481
|
}
|
|
13482
|
-
if (Object.keys(
|
|
13482
|
+
if (Object.keys(all2).length) block.stages = all2;
|
|
13483
13483
|
else delete block.stages;
|
|
13484
13484
|
for (const key of ["added", "agentsOwned", "shipped", "qaScheduled"]) delete block[key];
|
|
13485
13485
|
if (Object.keys(block).length) raw.workflows = block;
|
|
@@ -13753,17 +13753,17 @@ function forgetWorkflowAgent(agent) {
|
|
|
13753
13753
|
}
|
|
13754
13754
|
}
|
|
13755
13755
|
return save2((block) => {
|
|
13756
|
-
const
|
|
13757
|
-
for (const [id, flow] of Object.entries(
|
|
13756
|
+
const all2 = configBlock(block.stages);
|
|
13757
|
+
for (const [id, flow] of Object.entries(all2)) {
|
|
13758
13758
|
const mine = configBlock(flow);
|
|
13759
13759
|
for (const [key, value] of Object.entries(mine)) {
|
|
13760
13760
|
const one = configBlock(value);
|
|
13761
13761
|
if (Array.isArray(one.helpers)) one.helpers = one.helpers.filter((h) => configBlock(h).agent !== agent);
|
|
13762
13762
|
mine[key] = one;
|
|
13763
13763
|
}
|
|
13764
|
-
|
|
13764
|
+
all2[id] = mine;
|
|
13765
13765
|
}
|
|
13766
|
-
if (Object.keys(
|
|
13766
|
+
if (Object.keys(all2).length) block.stages = all2;
|
|
13767
13767
|
});
|
|
13768
13768
|
}
|
|
13769
13769
|
function scheduledMembers(flow) {
|
|
@@ -14042,11 +14042,11 @@ function agentFilesBlock(agent) {
|
|
|
14042
14042
|
}
|
|
14043
14043
|
function agentMemoryBlock(agent) {
|
|
14044
14044
|
const dir = rel(agentMemoryDir(agent.name));
|
|
14045
|
-
const
|
|
14046
|
-
if (!
|
|
14045
|
+
const files2 = readAgentMemory(agent.name);
|
|
14046
|
+
if (!files2.length) return `Your memory folder is \`${dir}/\`. Nothing is in it yet.`;
|
|
14047
14047
|
return [
|
|
14048
14048
|
`Your memory, from \`${dir}/\` \u2014 what you kept on this board in earlier runs. Follow it here:`,
|
|
14049
|
-
...
|
|
14049
|
+
...files2.map((file) => `### \`${file.name}\`
|
|
14050
14050
|
|
|
14051
14051
|
${file.text || "_(empty)_"}`)
|
|
14052
14052
|
].join("\n\n");
|
|
@@ -14145,8 +14145,8 @@ var init_agents = __esm({
|
|
|
14145
14145
|
return RETIRED_SETTINGS.filter((r) => r.agent === agent.name && r.key in entry.extra).map((r) => r.note);
|
|
14146
14146
|
};
|
|
14147
14147
|
agentsOf = (names5) => {
|
|
14148
|
-
const
|
|
14149
|
-
return names5.map((name) =>
|
|
14148
|
+
const all2 = specAgents();
|
|
14149
|
+
return names5.map((name) => all2.find((a) => a.name === name)).filter((a) => !!a);
|
|
14150
14150
|
};
|
|
14151
14151
|
specAgentAssigned = (name, workflow) => {
|
|
14152
14152
|
const flow = workflowFor(workflow);
|
|
@@ -14558,12 +14558,12 @@ function candidateFileStats(candidate) {
|
|
|
14558
14558
|
const change2 = added === "-" || removed === "-" ? "binary" : `+${added} -${removed}`;
|
|
14559
14559
|
return `${file} (${change2}${fresh ? ", new" : ""})`;
|
|
14560
14560
|
};
|
|
14561
|
-
const
|
|
14561
|
+
const files2 = tracked.split("\n").filter(Boolean).map((line2) => format(line2));
|
|
14562
14562
|
for (const file of untrackedFiles(candidate)) {
|
|
14563
14563
|
const stat = gitDiff(["diff", "--no-index", "--numstat", "--", "/dev/null", file], candidate.cwd)?.trim();
|
|
14564
|
-
|
|
14564
|
+
files2.push(stat ? format(stat, true) : `${file} (new)`);
|
|
14565
14565
|
}
|
|
14566
|
-
return
|
|
14566
|
+
return files2;
|
|
14567
14567
|
}
|
|
14568
14568
|
function candidateStat(candidate) {
|
|
14569
14569
|
if (!candidate.base) return null;
|
|
@@ -14644,7 +14644,7 @@ var init_outputs = __esm({
|
|
|
14644
14644
|
return m ? [{ done: m[1] !== " ", text: m[2].trim() }] : [];
|
|
14645
14645
|
});
|
|
14646
14646
|
plannedTodos = (cardId2) => todos(cardId2).map((t) => t.text);
|
|
14647
|
-
looksLikePath = (
|
|
14647
|
+
looksLikePath = (token3) => !/\s/.test(token3) && !token3.includes("://") && !token3.startsWith("-") && token3.includes("/");
|
|
14648
14648
|
resolveOutput = (file) => {
|
|
14649
14649
|
if (file.startsWith("~/")) return path35.join(os4.homedir(), file.slice(2));
|
|
14650
14650
|
if (path35.isAbsolute(file)) return file;
|
|
@@ -14681,11 +14681,11 @@ function noWorktreeWhy() {
|
|
|
14681
14681
|
if (!currentBranch()) return "this checkout is on a detached HEAD, so a build has nowhere to land";
|
|
14682
14682
|
return void 0;
|
|
14683
14683
|
}
|
|
14684
|
-
function prepareDelivery(cardId2, wants,
|
|
14684
|
+
function prepareDelivery(cardId2, wants, files2 = false) {
|
|
14685
14685
|
const deliveryId = newDeliveryId();
|
|
14686
14686
|
const gated = cardId2 !== null;
|
|
14687
14687
|
const base = inGitRepo() ? headCommit() : null;
|
|
14688
|
-
if (
|
|
14688
|
+
if (files2) return { start: { deliveryId, commitMode: "files", base: base ?? void 0 } };
|
|
14689
14689
|
if (gated && cardWorkflow(cardId2)?.needsArtifact) {
|
|
14690
14690
|
return {
|
|
14691
14691
|
start: {
|
|
@@ -14805,15 +14805,15 @@ var init_commit_mode = __esm({
|
|
|
14805
14805
|
ID_LENGTH2 = 8;
|
|
14806
14806
|
MAX_NAMED = 5;
|
|
14807
14807
|
deliveryCwd = (delivery) => delivery.worktree ? worktreeDir(delivery.worktree) : REPO_ROOT;
|
|
14808
|
-
dirtyRefusal = (
|
|
14808
|
+
dirtyRefusal = (files2) => ({
|
|
14809
14809
|
error: [
|
|
14810
14810
|
"Commit or stash your changes before starting.",
|
|
14811
14811
|
"",
|
|
14812
|
-
...
|
|
14813
|
-
...
|
|
14812
|
+
...files2.slice(0, MAX_NAMED),
|
|
14813
|
+
...files2.length > MAX_NAMED ? [`and ${files2.length - MAX_NAMED} more`] : []
|
|
14814
14814
|
].join("\n"),
|
|
14815
14815
|
reason: "dirty",
|
|
14816
|
-
paths:
|
|
14816
|
+
paths: files2.slice(0, MAX_NAMED)
|
|
14817
14817
|
});
|
|
14818
14818
|
MAX_SUBJECT = 72;
|
|
14819
14819
|
deliveryName = (delivery) => delivery.cardId === null ? `delivery ${delivery.deliveryId}` : `#${delivery.cardId}`;
|
|
@@ -14899,19 +14899,19 @@ function deliveryState(delivery, questions) {
|
|
|
14899
14899
|
};
|
|
14900
14900
|
}
|
|
14901
14901
|
if (landing?.conflictFiles?.length || landing?.fitAt) {
|
|
14902
|
-
const
|
|
14902
|
+
const files2 = landing.conflictFiles ?? [];
|
|
14903
14903
|
const attempt2 = (landing.conflictFails ?? 0) + 1;
|
|
14904
14904
|
const branch2 = `\`${delivery.targetBranch ?? "the target branch"}\``;
|
|
14905
|
-
const where2 =
|
|
14905
|
+
const where2 = files2.length ? `${some(files2)} against ${branch2}` : `the combined result with ${branch2}`;
|
|
14906
14906
|
if (landing.status === "waiting" && landing.conflictAt) {
|
|
14907
14907
|
const left = Math.max(0, Math.round((landing.conflictAt - Date.now()) / 1e3));
|
|
14908
14908
|
return {
|
|
14909
14909
|
stage: "retry",
|
|
14910
14910
|
label: "Waiting to retry",
|
|
14911
|
-
line: `Attempt ${attempt2 - 1} left ${where2} ${
|
|
14911
|
+
line: `Attempt ${attempt2 - 1} left ${where2} ${files2.length ? "conflicted" : "unchecked"}. It gave the landing slot up and opens attempt ${attempt2} ${left ? `in ${left}s` : "now"} \u2014 another delivery can land while it waits.`,
|
|
14912
14912
|
paused: false,
|
|
14913
14913
|
kind: "conflict-wait",
|
|
14914
|
-
files,
|
|
14914
|
+
files: files2,
|
|
14915
14915
|
branch: delivery.targetBranch,
|
|
14916
14916
|
attempt: attempt2,
|
|
14917
14917
|
retryIn: left
|
|
@@ -14920,11 +14920,11 @@ function deliveryState(delivery, questions) {
|
|
|
14920
14920
|
if (landing.status === "landing") {
|
|
14921
14921
|
return {
|
|
14922
14922
|
stage: "conflict",
|
|
14923
|
-
label:
|
|
14924
|
-
line: `Attempt ${attempt2}: ${
|
|
14923
|
+
label: files2.length ? "Resolving a conflict" : "Checking the combined result",
|
|
14924
|
+
line: `Attempt ${attempt2}: ${files2.length ? "resolving" : "checking"} ${where2}. It lands by itself once ${files2.length ? "the conflict is out" : "that is done"} \u2014 nothing is asked of you.`,
|
|
14925
14925
|
paused: false,
|
|
14926
14926
|
kind: "conflict",
|
|
14927
|
-
files,
|
|
14927
|
+
files: files2,
|
|
14928
14928
|
branch: delivery.targetBranch,
|
|
14929
14929
|
attempt: attempt2
|
|
14930
14930
|
};
|
|
@@ -14986,7 +14986,7 @@ var init_pause = __esm({
|
|
|
14986
14986
|
HELD_ON_QUESTIONS = "held on an open question";
|
|
14987
14987
|
IN_LINE = "in line behind";
|
|
14988
14988
|
count2 = (n) => `${n} open question${n === 1 ? "" : "s"}`;
|
|
14989
|
-
some = (
|
|
14989
|
+
some = (files2) => files2.length === 1 ? `\`${files2[0]}\`` : `${files2.length} files`;
|
|
14990
14990
|
upper = (text5) => text5 ? text5[0].toUpperCase() + text5.slice(1) : text5;
|
|
14991
14991
|
end = (text5) => /[.!?)]$/.test(text5) ? text5 : `${text5}.`;
|
|
14992
14992
|
isHold = (why) => why.startsWith(HELD_ON_QUESTIONS);
|
|
@@ -15863,7 +15863,7 @@ var init_stage_end = __esm({
|
|
|
15863
15863
|
const flow = flowByAction(action);
|
|
15864
15864
|
return flow ? stageOfFlow(flow.command) : void 0;
|
|
15865
15865
|
};
|
|
15866
|
-
askedFor = (cardId2, agent) => readStore().runs.some((
|
|
15866
|
+
askedFor = (cardId2, agent) => readStore().runs.some((run2) => run2.action === "spec" && run2.cardId === cardId2 && run2.specAgent === agent);
|
|
15867
15867
|
}
|
|
15868
15868
|
});
|
|
15869
15869
|
|
|
@@ -15872,10 +15872,10 @@ function reviewOf(delivery) {
|
|
|
15872
15872
|
if (!delivery.review) delivery.review = { rounds: [] };
|
|
15873
15873
|
return delivery.review;
|
|
15874
15874
|
}
|
|
15875
|
-
function nextAfterSession(delivery,
|
|
15875
|
+
function nextAfterSession(delivery, run2) {
|
|
15876
15876
|
if (delivery.status !== "active") return HOLD;
|
|
15877
|
-
if (
|
|
15878
|
-
if (
|
|
15877
|
+
if (run2.action === "scheduled") return run2.status === "done" ? { finish: true } : HOLD;
|
|
15878
|
+
if (run2.action !== "implement" || run2.status !== "done") return HOLD;
|
|
15879
15879
|
return stageShortfall("build", delivery) ?? { finish: true };
|
|
15880
15880
|
}
|
|
15881
15881
|
function stageShortfall(stage, delivery) {
|
|
@@ -15913,23 +15913,23 @@ import path41 from "node:path";
|
|
|
15913
15913
|
function writeAudit(delivery, runs) {
|
|
15914
15914
|
const known = new Map(readAudit(delivery.deliveryId)?.sessions.map((s) => [s.sessionId, s]) ?? []);
|
|
15915
15915
|
const sessions = delivery.sessions.flatMap((id) => {
|
|
15916
|
-
const
|
|
15917
|
-
if (!
|
|
15916
|
+
const run2 = runs.find((r) => r.sessionId === id);
|
|
15917
|
+
if (!run2) {
|
|
15918
15918
|
const before = known.get(id);
|
|
15919
15919
|
return before ? [before] : [];
|
|
15920
15920
|
}
|
|
15921
15921
|
return [
|
|
15922
15922
|
{
|
|
15923
|
-
sessionId:
|
|
15924
|
-
action:
|
|
15925
|
-
status:
|
|
15926
|
-
startedAt:
|
|
15927
|
-
endedAt:
|
|
15928
|
-
harness:
|
|
15929
|
-
model:
|
|
15930
|
-
costUsd:
|
|
15931
|
-
resumedFrom:
|
|
15932
|
-
log: rel(
|
|
15923
|
+
sessionId: run2.sessionId,
|
|
15924
|
+
action: run2.action,
|
|
15925
|
+
status: run2.status,
|
|
15926
|
+
startedAt: run2.startedAt,
|
|
15927
|
+
endedAt: run2.endedAt,
|
|
15928
|
+
harness: run2.harness || void 0,
|
|
15929
|
+
model: run2.model,
|
|
15930
|
+
costUsd: run2.costUsd,
|
|
15931
|
+
resumedFrom: run2.resumedFrom,
|
|
15932
|
+
log: rel(run2.logPath)
|
|
15933
15933
|
}
|
|
15934
15934
|
];
|
|
15935
15935
|
});
|
|
@@ -16007,7 +16007,7 @@ function settleOrphanedDeliveries() {
|
|
|
16007
16007
|
const recovered = [];
|
|
16008
16008
|
withStore((store) => {
|
|
16009
16009
|
const known = new Set(store.deliveries.map((d) => d.deliveryId));
|
|
16010
|
-
for (const
|
|
16010
|
+
for (const run2 of store.runs) if (run2.deliveryId) known.add(run2.deliveryId);
|
|
16011
16011
|
for (const audit of audits) {
|
|
16012
16012
|
if (known.has(audit.deliveryId) || !worktreeExists(audit.worktree)) continue;
|
|
16013
16013
|
if (lastSessionRunning(audit)) {
|
|
@@ -16056,13 +16056,13 @@ function endedDelivery(id) {
|
|
|
16056
16056
|
const row = auditRow(audit);
|
|
16057
16057
|
if (row) rows.set(row.deliveryId, row);
|
|
16058
16058
|
}
|
|
16059
|
-
const
|
|
16059
|
+
const all2 = [...rows.values()].sort((a, b) => a.startedAt - b.startedAt);
|
|
16060
16060
|
if (/^#?\d+$/.test(key)) {
|
|
16061
16061
|
const cardId2 = Number(key.replace("#", ""));
|
|
16062
|
-
const mine =
|
|
16062
|
+
const mine = all2.filter((d) => d.cardId === cardId2);
|
|
16063
16063
|
return mine.filter((d) => d.status !== "active").pop() ?? mine.pop();
|
|
16064
16064
|
}
|
|
16065
|
-
return
|
|
16065
|
+
return all2.find((d) => d.deliveryId === key) ?? all2.find((d) => d.deliveryId.startsWith(key));
|
|
16066
16066
|
}
|
|
16067
16067
|
function resumeRefusal(delivery, store) {
|
|
16068
16068
|
const id = delivery.deliveryId;
|
|
@@ -16094,11 +16094,11 @@ function resumeRefusal(delivery, store) {
|
|
|
16094
16094
|
);
|
|
16095
16095
|
}
|
|
16096
16096
|
if (!worktreeExists(delivery.worktree)) {
|
|
16097
|
-
const
|
|
16097
|
+
const path83 = delivery.worktree ?? "";
|
|
16098
16098
|
return refusal(
|
|
16099
16099
|
"deliveryWorktreeGone",
|
|
16100
|
-
`delivery ${id}'s worktree ${
|
|
16101
|
-
{ id, task, path:
|
|
16100
|
+
`delivery ${id}'s worktree ${path83} is gone, so there is nothing to carry on. ${rebuild}`,
|
|
16101
|
+
{ id, task, path: path83, command: discard }
|
|
16102
16102
|
);
|
|
16103
16103
|
}
|
|
16104
16104
|
if (!branchExists(delivery.branch)) {
|
|
@@ -16275,8 +16275,8 @@ function activeDelivery(cardId2) {
|
|
|
16275
16275
|
function findDelivery(id) {
|
|
16276
16276
|
const key = id.trim();
|
|
16277
16277
|
if (!key) return void 0;
|
|
16278
|
-
const
|
|
16279
|
-
return
|
|
16278
|
+
const all2 = readStore().deliveries;
|
|
16279
|
+
return all2.find((d) => d.deliveryId === key) ?? all2.find((d) => d.deliveryId.startsWith(key));
|
|
16280
16280
|
}
|
|
16281
16281
|
function namedDelivery(id) {
|
|
16282
16282
|
const key = id.trim();
|
|
@@ -16292,8 +16292,8 @@ function deliveryFor(req) {
|
|
|
16292
16292
|
}
|
|
16293
16293
|
return req.id === void 0 ? void 0 : activeDelivery(req.id);
|
|
16294
16294
|
}
|
|
16295
|
-
function joinDelivery(store,
|
|
16296
|
-
const cardId2 =
|
|
16295
|
+
function joinDelivery(store, run2, title, step, start, direct) {
|
|
16296
|
+
const cardId2 = run2.cardId;
|
|
16297
16297
|
let delivery = cardId2 === null ? void 0 : activeIn(store, cardId2);
|
|
16298
16298
|
if (!delivery) {
|
|
16299
16299
|
delivery = {
|
|
@@ -16301,7 +16301,7 @@ function joinDelivery(store, run, title, step, start, direct) {
|
|
|
16301
16301
|
cardId: cardId2,
|
|
16302
16302
|
title,
|
|
16303
16303
|
status: "active",
|
|
16304
|
-
startedAt:
|
|
16304
|
+
startedAt: run2.startedAt,
|
|
16305
16305
|
sessions: [],
|
|
16306
16306
|
// The one read of the card this delivery will ever make for its requirements — or,
|
|
16307
16307
|
// with no card, the sentence or the plan's words it was handed, which can never move
|
|
@@ -16346,21 +16346,21 @@ function joinDelivery(store, run, title, step, start, direct) {
|
|
|
16346
16346
|
} else if (step === "implement" && delivery.review?.stopped) {
|
|
16347
16347
|
delivery.review.stopped = void 0;
|
|
16348
16348
|
}
|
|
16349
|
-
delivery.sessions.push(
|
|
16350
|
-
delivery.steps.push({ step, at:
|
|
16351
|
-
joinFlow(
|
|
16349
|
+
delivery.sessions.push(run2.sessionId);
|
|
16350
|
+
delivery.steps.push({ step, at: run2.startedAt });
|
|
16351
|
+
joinFlow(run2, delivery);
|
|
16352
16352
|
writeAudit(delivery, store.runs);
|
|
16353
16353
|
return delivery;
|
|
16354
16354
|
}
|
|
16355
16355
|
function adoptDirectCard(sessionId, cardId2) {
|
|
16356
16356
|
return withStore((store) => {
|
|
16357
|
-
const
|
|
16358
|
-
if (!
|
|
16359
|
-
const delivery =
|
|
16360
|
-
if (
|
|
16357
|
+
const run2 = store.runs.find((r) => r.sessionId === sessionId && r.status === "running");
|
|
16358
|
+
if (!run2 || run2.action !== "implement" || run2.cardId !== null) return false;
|
|
16359
|
+
const delivery = run2.deliveryId ? store.deliveries.find((d) => d.deliveryId === run2.deliveryId && d.status === "active") : void 0;
|
|
16360
|
+
if (run2.deliveryId && (!delivery || delivery.cardId !== null)) return false;
|
|
16361
16361
|
const idle = "ready";
|
|
16362
|
-
|
|
16363
|
-
|
|
16362
|
+
run2.cardId = cardId2;
|
|
16363
|
+
run2.priorStatus = idle;
|
|
16364
16364
|
if (!delivery) return true;
|
|
16365
16365
|
delivery.cardId = cardId2;
|
|
16366
16366
|
delivery.priorStatus = idle;
|
|
@@ -16368,21 +16368,21 @@ function adoptDirectCard(sessionId, cardId2) {
|
|
|
16368
16368
|
return true;
|
|
16369
16369
|
});
|
|
16370
16370
|
}
|
|
16371
|
-
function joinActive(store,
|
|
16372
|
-
const delivery = deliveryId ? store.deliveries.find((d) => d.status === "active" && d.deliveryId === deliveryId) :
|
|
16371
|
+
function joinActive(store, run2, step, deliveryId) {
|
|
16372
|
+
const delivery = deliveryId ? store.deliveries.find((d) => d.status === "active" && d.deliveryId === deliveryId) : run2.cardId === null ? void 0 : activeIn(store, run2.cardId);
|
|
16373
16373
|
if (!delivery) return void 0;
|
|
16374
|
-
delivery.sessions.push(
|
|
16375
|
-
delivery.steps.push({ step, at:
|
|
16374
|
+
delivery.sessions.push(run2.sessionId);
|
|
16375
|
+
delivery.steps.push({ step, at: run2.startedAt });
|
|
16376
16376
|
if (delivery.review?.stopped) delivery.review.stopped = void 0;
|
|
16377
16377
|
const now = Date.now();
|
|
16378
16378
|
for (const answer2 of delivery.answers ?? []) if (!answer2.actedAt) answer2.actedAt = now;
|
|
16379
|
-
joinFlow(
|
|
16379
|
+
joinFlow(run2, delivery);
|
|
16380
16380
|
writeAudit(delivery, store.runs);
|
|
16381
16381
|
return delivery;
|
|
16382
16382
|
}
|
|
16383
|
-
function joinFlow(
|
|
16384
|
-
|
|
16385
|
-
|
|
16383
|
+
function joinFlow(run2, delivery) {
|
|
16384
|
+
run2.deliveryId = delivery.deliveryId;
|
|
16385
|
+
run2.flowId = delivery.deliveryId;
|
|
16386
16386
|
}
|
|
16387
16387
|
function endDelivery(deliveryId, status) {
|
|
16388
16388
|
const ended = withStore((store) => {
|
|
@@ -16398,18 +16398,18 @@ function endDelivery(deliveryId, status) {
|
|
|
16398
16398
|
}
|
|
16399
16399
|
return ended;
|
|
16400
16400
|
}
|
|
16401
|
-
async function settleDelivery(
|
|
16402
|
-
if (!
|
|
16403
|
-
const before = readStore().deliveries.find((d) => d.deliveryId ===
|
|
16401
|
+
async function settleDelivery(run2) {
|
|
16402
|
+
if (!run2.deliveryId) return;
|
|
16403
|
+
const before = readStore().deliveries.find((d) => d.deliveryId === run2.deliveryId);
|
|
16404
16404
|
if (!before) return;
|
|
16405
|
-
const built =
|
|
16406
|
-
const passed =
|
|
16405
|
+
const built = run2.status === "done" && run2.action === "implement";
|
|
16406
|
+
const passed = run2.status === "done" && run2.action === "scheduled";
|
|
16407
16407
|
const commit = (built || passed) && before.status === "active" ? commitDeliveryWork(before) : { ok: true };
|
|
16408
16408
|
const uncommitted = commit.ok ? void 0 : commit.why;
|
|
16409
16409
|
const reviewed = !uncommitted && before.commitMode === "manual" && before.cardId !== null && (built || passed) ? snapshotReviewed(before) : void 0;
|
|
16410
|
-
const
|
|
16410
|
+
const files2 = (built || passed) && before.commitMode === "files" && !before.scheduled ? filesStop(before) : void 0;
|
|
16411
16411
|
const settled = withStore((store) => {
|
|
16412
|
-
const delivery = store.deliveries.find((d) => d.deliveryId ===
|
|
16412
|
+
const delivery = store.deliveries.find((d) => d.deliveryId === run2.deliveryId);
|
|
16413
16413
|
if (!delivery) return null;
|
|
16414
16414
|
if (uncommitted && delivery.status === "active") {
|
|
16415
16415
|
const review = reviewOf(delivery);
|
|
@@ -16417,7 +16417,7 @@ async function settleDelivery(run) {
|
|
|
16417
16417
|
releaseLanding(delivery);
|
|
16418
16418
|
return null;
|
|
16419
16419
|
}
|
|
16420
|
-
const next = nextAfterSession(delivery,
|
|
16420
|
+
const next = nextAfterSession(delivery, run2);
|
|
16421
16421
|
if ("hold" in next) return null;
|
|
16422
16422
|
if ("finish" in next) {
|
|
16423
16423
|
if (reviewed) {
|
|
@@ -16426,12 +16426,12 @@ async function settleDelivery(run) {
|
|
|
16426
16426
|
}
|
|
16427
16427
|
if (delivery.commitMode === "manual" && !delivery.base) return { end: "finished", complete: delivery.cardId !== null };
|
|
16428
16428
|
if (delivery.commitMode === "files") {
|
|
16429
|
-
if (!
|
|
16430
|
-
reviewOf(delivery).stopped = { reason:
|
|
16429
|
+
if (!files2) return { end: "finished", complete: delivery.cardId !== null };
|
|
16430
|
+
reviewOf(delivery).stopped = { reason: files2.reason, why: files2.why, at: Date.now(), paths: files2.paths };
|
|
16431
16431
|
return null;
|
|
16432
16432
|
}
|
|
16433
16433
|
if (wantsLanding(delivery)) {
|
|
16434
|
-
queueLanding(delivery,
|
|
16434
|
+
queueLanding(delivery, run2);
|
|
16435
16435
|
return null;
|
|
16436
16436
|
}
|
|
16437
16437
|
return { end: "finished" };
|
|
@@ -16441,15 +16441,15 @@ async function settleDelivery(run) {
|
|
|
16441
16441
|
return null;
|
|
16442
16442
|
});
|
|
16443
16443
|
if (settled && "end" in settled) {
|
|
16444
|
-
endDelivery(
|
|
16445
|
-
if (settled.complete) await completeCard(before.cardId,
|
|
16444
|
+
endDelivery(run2.deliveryId, settled.end);
|
|
16445
|
+
if (settled.complete) await completeCard(before.cardId, run2.deliveryId);
|
|
16446
16446
|
}
|
|
16447
|
-
syncAudit(
|
|
16447
|
+
syncAudit(run2.deliveryId, run2);
|
|
16448
16448
|
}
|
|
16449
16449
|
function deliveryAcceptsAnswers(cardId2) {
|
|
16450
16450
|
return !!deliveryStateOf(cardId2)?.paused;
|
|
16451
16451
|
}
|
|
16452
|
-
function queueLanding(delivery,
|
|
16452
|
+
function queueLanding(delivery, run2) {
|
|
16453
16453
|
const landing = delivery.landing ?? { status: "waiting", attempts: 0, at: Date.now() };
|
|
16454
16454
|
delivery.landing = {
|
|
16455
16455
|
...landing,
|
|
@@ -16458,7 +16458,7 @@ function queueLanding(delivery, run) {
|
|
|
16458
16458
|
reason: void 0,
|
|
16459
16459
|
checks: [
|
|
16460
16460
|
...landing.checks ?? [],
|
|
16461
|
-
{ name: `${
|
|
16461
|
+
{ name: `${run2.action} ${run2.sessionId.slice(0, 8)}`, ok: true, at: Date.now() }
|
|
16462
16462
|
],
|
|
16463
16463
|
at: Date.now()
|
|
16464
16464
|
};
|
|
@@ -16917,20 +16917,20 @@ function waitForTarget(delivery) {
|
|
|
16917
16917
|
});
|
|
16918
16918
|
return { done: true };
|
|
16919
16919
|
}
|
|
16920
|
-
function startConflict(delivery, target,
|
|
16920
|
+
function startConflict(delivery, target, files2) {
|
|
16921
16921
|
patchLanding(delivery.deliveryId, (landing) => {
|
|
16922
16922
|
landing.onto = target;
|
|
16923
|
-
landing.conflictFiles =
|
|
16924
|
-
landing.why = `resolving a conflict with ${delivery.targetBranch} in ${names2(
|
|
16923
|
+
landing.conflictFiles = files2;
|
|
16924
|
+
landing.why = `resolving a conflict with ${delivery.targetBranch} in ${names2(files2)}`;
|
|
16925
16925
|
landing.reason = void 0;
|
|
16926
16926
|
});
|
|
16927
16927
|
return { start: conflictRun(delivery) };
|
|
16928
16928
|
}
|
|
16929
16929
|
function reopenConflict(delivery, dir) {
|
|
16930
|
-
const
|
|
16930
|
+
const files2 = conflictedPaths(dir);
|
|
16931
16931
|
patchLanding(delivery.deliveryId, (landing) => {
|
|
16932
16932
|
landing.conflictAt = void 0;
|
|
16933
|
-
if (
|
|
16933
|
+
if (files2.length) landing.conflictFiles = files2;
|
|
16934
16934
|
if (landing.fitAt) landing.fitAt = Date.now();
|
|
16935
16935
|
});
|
|
16936
16936
|
return { start: conflictRun(delivery) };
|
|
@@ -17034,9 +17034,9 @@ async function move(delivery, tip, target) {
|
|
|
17034
17034
|
return { done: true };
|
|
17035
17035
|
}
|
|
17036
17036
|
function landWait(wait, where) {
|
|
17037
|
-
const
|
|
17037
|
+
const files2 = wait.files;
|
|
17038
17038
|
const at = sameDir(where, REPO_ROOT) ? "your checkout" : `\`${rel(where)}\``;
|
|
17039
|
-
return wait.kind === "untracked" ? `${some2(
|
|
17039
|
+
return wait.kind === "untracked" ? `${some2(files2)} in ${at} ${are(files2.length)} not in git, and landing would write over ${them(files2.length)} \u2014 move or delete ${them(files2.length)}` : `your changes to ${some2(files2)} in ${at} would be overwritten by landing \u2014 commit or stash ${them(files2.length)}`;
|
|
17040
17040
|
}
|
|
17041
17041
|
async function finish(delivery, landed) {
|
|
17042
17042
|
patchLanding(delivery.deliveryId, (landing) => {
|
|
@@ -17059,11 +17059,11 @@ function alreadyOnTarget(delivery) {
|
|
|
17059
17059
|
const onto = branchTip(delivery.targetBranch);
|
|
17060
17060
|
if (!onto) return null;
|
|
17061
17061
|
const dir = worktreeDir(delivery.worktree);
|
|
17062
|
-
const
|
|
17063
|
-
if (!
|
|
17062
|
+
const files2 = changedPaths(delivery.base, delivery.branch, dir);
|
|
17063
|
+
if (!files2?.length) return null;
|
|
17064
17064
|
const patch2 = branchPatch(delivery.base, delivery.branch, dir);
|
|
17065
17065
|
if (!patch2 || !reverseApplies(patch2, onto, dir)) return null;
|
|
17066
|
-
return { commit: lastCommitTouching(onto,
|
|
17066
|
+
return { commit: lastCommitTouching(onto, files2, dir) ?? void 0, onto };
|
|
17067
17067
|
}
|
|
17068
17068
|
async function settleAlreadyLanded(delivery) {
|
|
17069
17069
|
const found2 = alreadyOnTarget(delivery);
|
|
@@ -17120,8 +17120,8 @@ var init_landing = __esm({
|
|
|
17120
17120
|
init_store();
|
|
17121
17121
|
init_worktree();
|
|
17122
17122
|
MAX_NAMED2 = 5;
|
|
17123
|
-
names2 = (
|
|
17124
|
-
some2 = (
|
|
17123
|
+
names2 = (files2) => `${files2.slice(0, MAX_NAMED2).join(", ")}${files2.length > MAX_NAMED2 ? `, and ${files2.length - MAX_NAMED2} more` : ""}`;
|
|
17124
|
+
some2 = (files2) => files2.length === 1 ? `\`${files2[0]}\`` : `${files2.length} files`;
|
|
17125
17125
|
are = (n) => n === 1 ? "is" : "are";
|
|
17126
17126
|
them = (n) => n === 1 ? "it" : "them";
|
|
17127
17127
|
sayWhy = (deliveryId, why, wait, reason) => patchLanding(deliveryId, (landing) => {
|
|
@@ -17181,14 +17181,14 @@ function returnRunPictures(sessionId, box2) {
|
|
|
17181
17181
|
} catch {
|
|
17182
17182
|
}
|
|
17183
17183
|
}
|
|
17184
|
-
function moveRunPictures(from, to,
|
|
17185
|
-
if (
|
|
17184
|
+
function moveRunPictures(from, to, files2 = []) {
|
|
17185
|
+
if (files2.length === 0) return [];
|
|
17186
17186
|
try {
|
|
17187
17187
|
fs42.renameSync(pictureBox(from), pictureBox(to));
|
|
17188
17188
|
} catch {
|
|
17189
|
-
return
|
|
17189
|
+
return files2;
|
|
17190
17190
|
}
|
|
17191
|
-
return
|
|
17191
|
+
return files2.map((file) => path43.join(pictureBox(to), path43.basename(file)));
|
|
17192
17192
|
}
|
|
17193
17193
|
var pictureName, boxId, freeBox, pictureBox;
|
|
17194
17194
|
var init_pictures = __esm({
|
|
@@ -17248,8 +17248,8 @@ function globalMemories() {
|
|
|
17248
17248
|
return out;
|
|
17249
17249
|
}
|
|
17250
17250
|
function globalMemoryBlock(agent) {
|
|
17251
|
-
const
|
|
17252
|
-
const kept = agent.memory.map((name) =>
|
|
17251
|
+
const all2 = globalMemories();
|
|
17252
|
+
const kept = agent.memory.map((name) => all2.find((m) => m.name === name)).filter((m) => !!m);
|
|
17253
17253
|
if (!kept.length) return "";
|
|
17254
17254
|
return [
|
|
17255
17255
|
"Global memories you share with other agents \u2014 open one only when the work needs it, and",
|
|
@@ -17455,7 +17455,7 @@ var init_write_agent = __esm({
|
|
|
17455
17455
|
var writing_default;
|
|
17456
17456
|
var init_writing = __esm({
|
|
17457
17457
|
"../cli/src/guide/writing.md"() {
|
|
17458
|
-
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<one short paragraph: the observable result and the current behavior or constraint it changes.>\n\n## Worth noting <- answered material decisions; 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- **Classify first**: follow `akb guide update-questions`; this section contains only answers\n classified as `Worth noting`.\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- **Classify first**: follow `akb guide update-questions`; this section contains only answers\n classified as `Worth noting after implementation`.\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**:
|
|
17458
|
+
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<one short paragraph: the observable result and the current behavior or constraint it changes.>\n\n## Worth noting <- answered material decisions; 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- **Classify first**: follow `akb guide update-questions`; this section contains only answers\n classified as `Worth noting`.\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- **Classify first**: follow `akb guide update-questions`; this section contains only answers\n classified as `Worth noting after implementation`.\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- **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';
|
|
17459
17459
|
}
|
|
17460
17460
|
});
|
|
17461
17461
|
|
|
@@ -17633,7 +17633,6 @@ function contractRepairPrompt(req, errors) {
|
|
|
17633
17633
|
"The task ended with invalid card formatting. Fix only the errors below in the listed files. Preserve planned behavior and completed work; do not repeat the task or request other agents.",
|
|
17634
17634
|
errors,
|
|
17635
17635
|
`Run \`${command} raw validate <id>\` for every affected card and fix every error before finishing.`,
|
|
17636
|
-
languageNote(),
|
|
17637
17636
|
ruleBlock(req, frozenRules(req))
|
|
17638
17637
|
].filter(Boolean).join("\n\n"));
|
|
17639
17638
|
}
|
|
@@ -17647,7 +17646,7 @@ function buildAsk(rawReq, notes = []) {
|
|
|
17647
17646
|
const req = withWorkflow(rawReq);
|
|
17648
17647
|
const command = DELIVERY_FLOWS.has(req.action) ? boardCommandFor(req.id) : boardCommand();
|
|
17649
17648
|
const ask2 = [actionPrompt(req, command, notes), pictureNote(req), commandNote(command)].filter(Boolean).join(" ");
|
|
17650
|
-
return realPaths(boardText([ask2,
|
|
17649
|
+
return realPaths(boardText([ask2, roster(req)].filter(Boolean).join("\n\n")), req);
|
|
17651
17650
|
}
|
|
17652
17651
|
function cardFileOf(id) {
|
|
17653
17652
|
const found2 = id === void 0 ? null : locate(id);
|
|
@@ -17733,10 +17732,10 @@ function settingsBlock(req) {
|
|
|
17733
17732
|
return role ? roleSettingsBlock(role) : "";
|
|
17734
17733
|
}
|
|
17735
17734
|
function pictureNote(req) {
|
|
17736
|
-
const
|
|
17737
|
-
if (!
|
|
17738
|
-
const one =
|
|
17739
|
-
return `${one ? "A picture came" : `${
|
|
17735
|
+
const files2 = req.pictures ?? [];
|
|
17736
|
+
if (!files2.length || agentImages(agentForRun(req), req.runtime)?.as !== "message") return "";
|
|
17737
|
+
const one = files2.length === 1;
|
|
17738
|
+
return `${one ? "A picture came" : `${files2.length} pictures came`} with this. Read ${one ? "it" : "them"} first: ${files2.join(", ")}.`;
|
|
17740
17739
|
}
|
|
17741
17740
|
function frozenRules(req) {
|
|
17742
17741
|
return deliveryFor(req)?.rules;
|
|
@@ -17751,8 +17750,8 @@ function shippedLines(cardId2) {
|
|
|
17751
17750
|
const commit = delivery?.landing?.commit;
|
|
17752
17751
|
if (commit) return [`\`git show ${commit.slice(0, 12)}\` \u2014 the change that landed`];
|
|
17753
17752
|
if (delivery?.commitMode === "files") {
|
|
17754
|
-
const
|
|
17755
|
-
if (
|
|
17753
|
+
const files2 = recordedOutputs(delivery);
|
|
17754
|
+
if (files2.length) return ["the files it delivered:", ...files2.map((file) => ` ${file}`)];
|
|
17756
17755
|
}
|
|
17757
17756
|
return [`nothing on record \u2014 \`git log --grep "(#${cardId2})"\`, or check the card against the project as it stands`];
|
|
17758
17757
|
}
|
|
@@ -18077,7 +18076,6 @@ var init_prompts = __esm({
|
|
|
18077
18076
|
init_outputs();
|
|
18078
18077
|
init_archive();
|
|
18079
18078
|
init_flows();
|
|
18080
|
-
init_language();
|
|
18081
18079
|
init_resolve();
|
|
18082
18080
|
init_runner();
|
|
18083
18081
|
init_workflows();
|
|
@@ -18109,7 +18107,7 @@ var init_prompts = __esm({
|
|
|
18109
18107
|
`The session you were continuing is gone, and this one holds nothing of it.`,
|
|
18110
18108
|
`Do the task below from the top, checking each step's precondition before you do it \u2014 work an earlier run already finished is done, so don't repeat it.`
|
|
18111
18109
|
].join(" ");
|
|
18112
|
-
restartable = (
|
|
18110
|
+
restartable = (run2) => (run2.cardId !== null || !!run2.deliveryId) && RESTARTABLE.has(run2.action);
|
|
18113
18111
|
NO_IMPLEMENT = `(Unless the request explicitly asks for implementation, don't implement it; revising the card's drafts is not implementing.)`;
|
|
18114
18112
|
BOARD_FOLDER = "<board folder>";
|
|
18115
18113
|
CARD_FILE = "<card file>";
|
|
@@ -18367,9 +18365,9 @@ function reap(runs, reaped = [], restore = []) {
|
|
|
18367
18365
|
}
|
|
18368
18366
|
return changed;
|
|
18369
18367
|
}
|
|
18370
|
-
function stampDuration(
|
|
18368
|
+
function stampDuration(run2, endedAt) {
|
|
18371
18369
|
try {
|
|
18372
|
-
fs46.appendFileSync(
|
|
18370
|
+
fs46.appendFileSync(run2.logPath, durationLine(endedAt - run2.startedAt));
|
|
18373
18371
|
} catch {
|
|
18374
18372
|
}
|
|
18375
18373
|
}
|
|
@@ -18390,19 +18388,19 @@ function cardNow(cardId2) {
|
|
|
18390
18388
|
return null;
|
|
18391
18389
|
}
|
|
18392
18390
|
}
|
|
18393
|
-
function claimCard(
|
|
18394
|
-
const wanted =
|
|
18395
|
-
if (
|
|
18396
|
-
const delivery =
|
|
18397
|
-
|
|
18398
|
-
return { cardId:
|
|
18391
|
+
function claimCard(run2) {
|
|
18392
|
+
const wanted = run2.cardId !== null ? RUN_STATUS[run2.action] : void 0;
|
|
18393
|
+
if (run2.cardId === null || !wanted) return void 0;
|
|
18394
|
+
const delivery = run2.deliveryId ? activeDelivery(run2.cardId) : void 0;
|
|
18395
|
+
run2.priorStatus = (delivery && delivery.deliveryId === run2.deliveryId ? delivery.priorStatus : void 0) ?? cardNow(run2.cardId)?.status ?? "todo";
|
|
18396
|
+
return { cardId: run2.cardId, status: wanted };
|
|
18399
18397
|
}
|
|
18400
|
-
async function restoreCardStatus(
|
|
18401
|
-
if (
|
|
18402
|
-
if (
|
|
18403
|
-
const card = cardNow(
|
|
18398
|
+
async function restoreCardStatus(run2) {
|
|
18399
|
+
if (run2.cardId === null || !RUN_STATUS[run2.action]) return;
|
|
18400
|
+
if (run2.deliveryId && activeDelivery(run2.cardId)?.deliveryId === run2.deliveryId) return;
|
|
18401
|
+
const card = cardNow(run2.cardId);
|
|
18404
18402
|
if (!card) return;
|
|
18405
|
-
await setCardStatus(
|
|
18403
|
+
await setCardStatus(run2.cardId, card.questions > 0 ? "todo" : run2.priorStatus ?? "todo");
|
|
18406
18404
|
}
|
|
18407
18405
|
async function releaseCard(delivery) {
|
|
18408
18406
|
if (delivery.cardId === null) return;
|
|
@@ -18410,46 +18408,46 @@ async function releaseCard(delivery) {
|
|
|
18410
18408
|
if (card?.status !== "implementing") return;
|
|
18411
18409
|
await setCardStatus(delivery.cardId, card.questions > 0 ? "todo" : delivery.priorStatus ?? "ready");
|
|
18412
18410
|
}
|
|
18413
|
-
function recordPrune(
|
|
18414
|
-
if (
|
|
18411
|
+
function recordPrune(run2) {
|
|
18412
|
+
if (run2.action !== "prune-memory" || run2.status !== "done") return;
|
|
18415
18413
|
try {
|
|
18416
18414
|
stampMemoryPrune();
|
|
18417
18415
|
} catch {
|
|
18418
18416
|
}
|
|
18419
18417
|
}
|
|
18420
|
-
function recordMemoryReview(
|
|
18421
|
-
if (
|
|
18418
|
+
function recordMemoryReview(run2) {
|
|
18419
|
+
if (run2.action !== "review-memory" || run2.status !== "done") return;
|
|
18422
18420
|
try {
|
|
18423
|
-
stampMemoryReview(new Date(
|
|
18421
|
+
stampMemoryReview(new Date(run2.startedAt));
|
|
18424
18422
|
} catch {
|
|
18425
18423
|
}
|
|
18426
18424
|
}
|
|
18427
|
-
function recordReflection(
|
|
18428
|
-
if (
|
|
18425
|
+
function recordReflection(run2) {
|
|
18426
|
+
if (run2.action !== "reflect" || run2.status !== "done" || !run2.cards?.length) return;
|
|
18429
18427
|
try {
|
|
18430
|
-
dropReflected(
|
|
18428
|
+
dropReflected(run2.cards);
|
|
18431
18429
|
} catch {
|
|
18432
18430
|
}
|
|
18433
18431
|
}
|
|
18434
|
-
function recordDismissalReview(
|
|
18435
|
-
if (
|
|
18432
|
+
function recordDismissalReview(run2) {
|
|
18433
|
+
if (run2.action !== "review-dismissals" || run2.status !== "done") return;
|
|
18436
18434
|
try {
|
|
18437
|
-
stampDismissalReview(new Date(
|
|
18435
|
+
stampDismissalReview(new Date(run2.startedAt));
|
|
18438
18436
|
} catch {
|
|
18439
18437
|
}
|
|
18440
18438
|
}
|
|
18441
|
-
function recordScheduledRun(
|
|
18442
|
-
if (
|
|
18439
|
+
function recordScheduledRun(run2) {
|
|
18440
|
+
if (run2.action !== "scheduled" || run2.status !== "done" || !run2.workflow || !run2.specAgent) return;
|
|
18443
18441
|
try {
|
|
18444
|
-
const began = (
|
|
18445
|
-
stampScheduledRun(
|
|
18442
|
+
const began = (run2.deliveryId ? findDelivery(run2.deliveryId)?.startedAt : void 0) ?? run2.startedAt;
|
|
18443
|
+
stampScheduledRun(run2.workflow, run2.specAgent, new Date(began));
|
|
18446
18444
|
} catch {
|
|
18447
18445
|
}
|
|
18448
18446
|
}
|
|
18449
|
-
function recordProjectDescription(
|
|
18450
|
-
if (
|
|
18447
|
+
function recordProjectDescription(run2) {
|
|
18448
|
+
if (run2.action !== "describe-project" || run2.status !== "done") return;
|
|
18451
18449
|
try {
|
|
18452
|
-
stampProjectDescription(new Date(
|
|
18450
|
+
stampProjectDescription(new Date(run2.startedAt));
|
|
18453
18451
|
} catch {
|
|
18454
18452
|
}
|
|
18455
18453
|
}
|
|
@@ -18457,8 +18455,8 @@ function resumeIdOf(r) {
|
|
|
18457
18455
|
if (adoptsSessionId(r.harness) && !carriesSession(r)) return r.sessionId;
|
|
18458
18456
|
return r.resumeId;
|
|
18459
18457
|
}
|
|
18460
|
-
function resumeSessionId(
|
|
18461
|
-
return resumesUnder(
|
|
18458
|
+
function resumeSessionId(run2) {
|
|
18459
|
+
return resumesUnder(run2.harness) && !neverStarted(run2) ? resumeIdOf(run2) : void 0;
|
|
18462
18460
|
}
|
|
18463
18461
|
function endedDeliveryRefusal(r, refusals) {
|
|
18464
18462
|
if (!r.deliveryId || !canPickUp(r)) return void 0;
|
|
@@ -18532,7 +18530,7 @@ function leftBoardOnLanding(id) {
|
|
|
18532
18530
|
}
|
|
18533
18531
|
function goneCards(runs) {
|
|
18534
18532
|
const asking = /* @__PURE__ */ new Set();
|
|
18535
|
-
for (const
|
|
18533
|
+
for (const run2 of runs) if (run2.cardId !== null && canPickUp(run2)) asking.add(run2.cardId);
|
|
18536
18534
|
if (asking.size === 0) return asking;
|
|
18537
18535
|
const onBoard3 = boardCardIds();
|
|
18538
18536
|
const gone = /* @__PURE__ */ new Set();
|
|
@@ -18542,12 +18540,12 @@ function goneCards(runs) {
|
|
|
18542
18540
|
async function listRuns() {
|
|
18543
18541
|
const reaped = [];
|
|
18544
18542
|
const restore = [];
|
|
18545
|
-
const runs = withRuns((
|
|
18546
|
-
reap(
|
|
18547
|
-
return
|
|
18543
|
+
const runs = withRuns((all2) => {
|
|
18544
|
+
reap(all2, reaped, restore);
|
|
18545
|
+
return all2.map((r) => ({ ...r }));
|
|
18548
18546
|
});
|
|
18549
|
-
for (const
|
|
18550
|
-
for (const
|
|
18547
|
+
for (const run2 of restore) await restoreCardStatus(run2);
|
|
18548
|
+
for (const run2 of reaped) await settleDelivery(run2);
|
|
18551
18549
|
for (const sub of runs) {
|
|
18552
18550
|
if (sub.action !== "sub" || sub.status !== "running" || !sub.parentId) continue;
|
|
18553
18551
|
if (!runs.some((r) => r.sessionId === sub.parentId && r.status === "running")) await stopRun(sub.sessionId);
|
|
@@ -18584,17 +18582,17 @@ function findRun(runs, id) {
|
|
|
18584
18582
|
if (hits.length === 1) return hits[0];
|
|
18585
18583
|
return hits.length > 1 ? null : void 0;
|
|
18586
18584
|
}
|
|
18587
|
-
async function getRun(id,
|
|
18585
|
+
async function getRun(id, bytes3) {
|
|
18588
18586
|
const restore = [];
|
|
18589
|
-
const runs = withRuns((
|
|
18590
|
-
reap(
|
|
18591
|
-
return
|
|
18587
|
+
const runs = withRuns((all2) => {
|
|
18588
|
+
reap(all2, [], restore);
|
|
18589
|
+
return all2.map((r) => ({ ...r }));
|
|
18592
18590
|
});
|
|
18593
|
-
for (const
|
|
18591
|
+
for (const run2 of restore) await restoreCardStatus(run2);
|
|
18594
18592
|
const found2 = findRun(runs, id);
|
|
18595
18593
|
if (!found2) return null;
|
|
18596
18594
|
const view = toView(found2, goneCards([found2]));
|
|
18597
|
-
const raw = readLogTail(found2.logPath,
|
|
18595
|
+
const raw = readLogTail(found2.logPath, bytes3) ?? "";
|
|
18598
18596
|
const { tail: tail2, result, durationMs, costUsd, model, usage: usage3 } = splitLog(raw);
|
|
18599
18597
|
view.tail = tail2;
|
|
18600
18598
|
if (result && found2.status !== "running" && !view.result) view.result = result;
|
|
@@ -18689,8 +18687,8 @@ function openRun(req, prompt, notes = [], sessionId = randomUUID6(), said3) {
|
|
|
18689
18687
|
const cardless = !!direct;
|
|
18690
18688
|
let start;
|
|
18691
18689
|
if (pass || req.action === "implement" && (cardless || cardId2 !== null && !activeDelivery(cardId2))) {
|
|
18692
|
-
const
|
|
18693
|
-
const prepared = prepareDelivery(cardId2, req.commitMode,
|
|
18690
|
+
const files2 = direct?.scheduled ? !!workflowById(direct.scheduled.workflow)?.needsArtifact : false;
|
|
18691
|
+
const prepared = prepareDelivery(cardId2, req.commitMode, files2);
|
|
18694
18692
|
if ("error" in prepared) return prepared;
|
|
18695
18693
|
start = prepared.start;
|
|
18696
18694
|
}
|
|
@@ -18783,15 +18781,15 @@ function openRun(req, prompt, notes = [], sessionId = randomUUID6(), said3) {
|
|
|
18783
18781
|
function planningSession(cardId2, cwd = REPO_ROOT) {
|
|
18784
18782
|
const here = (dir) => !dir || path48.resolve(dir) === cwd;
|
|
18785
18783
|
const ended = readRuns().filter((r) => r.status === "done").sort((a, b) => b.startedAt - a.startedAt);
|
|
18786
|
-
const of = (
|
|
18787
|
-
if (!here(
|
|
18788
|
-
const resumeId = resumeSessionId(
|
|
18789
|
-
return resumeId ? { session: { harness:
|
|
18784
|
+
const of = (run3, fork) => {
|
|
18785
|
+
if (!here(run3.cwd)) return { none: "elsewhere" };
|
|
18786
|
+
const resumeId = resumeSessionId(run3);
|
|
18787
|
+
return resumeId ? { session: { harness: run3.harness, resumeId }, fork } : { none: "unrecorded" };
|
|
18790
18788
|
};
|
|
18791
18789
|
const planned = ended.find((r) => r.cardId === cardId2 && PLANNING.has(r.action));
|
|
18792
18790
|
if (planned) return of(planned, false);
|
|
18793
|
-
const
|
|
18794
|
-
const created =
|
|
18791
|
+
const run2 = ended.find((r) => (r.action === "create" || r.action === "setup") && r.createdCardIds?.includes(cardId2));
|
|
18792
|
+
const created = run2 ? of(run2, true) : void 0;
|
|
18795
18793
|
return created ?? { none: "unrecorded" };
|
|
18796
18794
|
}
|
|
18797
18795
|
function carriedSession(req) {
|
|
@@ -18822,8 +18820,8 @@ function carriedSession(req) {
|
|
|
18822
18820
|
};
|
|
18823
18821
|
}
|
|
18824
18822
|
function planningRun(sessionId) {
|
|
18825
|
-
const
|
|
18826
|
-
return
|
|
18823
|
+
const run2 = peekRun(sessionId);
|
|
18824
|
+
return run2?.status === "running" && PLANNING.has(run2.action) ? run2 : void 0;
|
|
18827
18825
|
}
|
|
18828
18826
|
function requestOf(record) {
|
|
18829
18827
|
const id = record.cardId ?? void 0;
|
|
@@ -18849,11 +18847,11 @@ function requestOf(record) {
|
|
|
18849
18847
|
}
|
|
18850
18848
|
async function openResume(id) {
|
|
18851
18849
|
const restore = [];
|
|
18852
|
-
const runs = withRuns((
|
|
18853
|
-
reap(
|
|
18854
|
-
return
|
|
18850
|
+
const runs = withRuns((all2) => {
|
|
18851
|
+
reap(all2, [], restore);
|
|
18852
|
+
return all2.map((r) => ({ ...r }));
|
|
18855
18853
|
});
|
|
18856
|
-
for (const
|
|
18854
|
+
for (const run2 of restore) await restoreCardStatus(run2);
|
|
18857
18855
|
const prev = findRun(runs, id);
|
|
18858
18856
|
if (prev === null) return ambiguousRun(id);
|
|
18859
18857
|
if (!prev) return unknownRun(id);
|
|
@@ -18952,8 +18950,8 @@ async function resumeHeld(prev, plan, sessionId, resuming, fresh) {
|
|
|
18952
18950
|
flowId: prev.flowId
|
|
18953
18951
|
};
|
|
18954
18952
|
const out = withCreationLock(() => withStore((store) => {
|
|
18955
|
-
const
|
|
18956
|
-
const latest =
|
|
18953
|
+
const all2 = store.runs;
|
|
18954
|
+
const latest = all2.find((r) => r.sessionId === prev.sessionId);
|
|
18957
18955
|
if (!latest) return refusal("runContinued", "that run has already been continued");
|
|
18958
18956
|
if (latest.discardedCards?.some((c) => c.pending || locate(c.id))) {
|
|
18959
18957
|
return refusal("discardUnfinished", "A card discard did not finish. Retry discarding it before resuming creation.");
|
|
@@ -18968,11 +18966,11 @@ async function resumeHeld(prev, plan, sessionId, resuming, fresh) {
|
|
|
18968
18966
|
repair.errors = repair.errors.split("\n").filter((line2) => !record.discardedCards.some((c) => line2.includes(c.path) || new RegExp(`Task #${c.id}(?![0-9])`).test(line2))).join("\n");
|
|
18969
18967
|
if (!repair.cardIds.length) record.formatRepair = void 0;
|
|
18970
18968
|
}
|
|
18971
|
-
const heldCreation =
|
|
18969
|
+
const heldCreation = all2.find((r) => runIsLive(r) && (r.cardId !== null && (latest.createdCardIds ?? []).includes(r.cardId) || (r.createdCardIds ?? []).some((id) => (latest.createdCardIds ?? []).includes(id))));
|
|
18972
18970
|
if (heldCreation) return refusal("creationHeld", "A card from this creation is already being worked on. Wait for that run to finish.");
|
|
18973
|
-
const locked = lockedBy(
|
|
18971
|
+
const locked = lockedBy(all2, prev.action, prev.cardId, prev.discard);
|
|
18974
18972
|
if (locked) return locked;
|
|
18975
|
-
|
|
18973
|
+
all2.push(record);
|
|
18976
18974
|
const delivery = store.deliveries.find((d) => d.deliveryId === prev.deliveryId && d.status === "active");
|
|
18977
18975
|
if (delivery) {
|
|
18978
18976
|
delivery.sessions.push(record.sessionId);
|
|
@@ -18980,8 +18978,8 @@ async function resumeHeld(prev, plan, sessionId, resuming, fresh) {
|
|
|
18980
18978
|
record.deliveryId = delivery.deliveryId;
|
|
18981
18979
|
record.flowId = delivery.deliveryId;
|
|
18982
18980
|
}
|
|
18983
|
-
const at =
|
|
18984
|
-
if (at >= 0)
|
|
18981
|
+
const at = all2.findIndex((r) => r.sessionId === prev.sessionId);
|
|
18982
|
+
if (at >= 0) all2.splice(at, 1);
|
|
18985
18983
|
return { run: record };
|
|
18986
18984
|
}));
|
|
18987
18985
|
if ("error" in out) return out;
|
|
@@ -19020,8 +19018,8 @@ function forgetRun(sessionId) {
|
|
|
19020
19018
|
}
|
|
19021
19019
|
}
|
|
19022
19020
|
function askForSpec(sessionId, ask2) {
|
|
19023
|
-
const
|
|
19024
|
-
if (!
|
|
19021
|
+
const run2 = peekRun(sessionId);
|
|
19022
|
+
if (!run2) return "no-run";
|
|
19025
19023
|
const file = readAsks(sessionId);
|
|
19026
19024
|
if (file.asks.some((a) => a.specAgent === ask2.specAgent && a.cardId === ask2.cardId)) return "already";
|
|
19027
19025
|
file.asks.push(ask2);
|
|
@@ -19090,9 +19088,9 @@ function markSpawned(sessionId, pid) {
|
|
|
19090
19088
|
}
|
|
19091
19089
|
function patch(sessionId, change2) {
|
|
19092
19090
|
return withRuns((runs) => {
|
|
19093
|
-
const
|
|
19094
|
-
if (
|
|
19095
|
-
return
|
|
19091
|
+
const run2 = runs.find((r) => r.sessionId === sessionId);
|
|
19092
|
+
if (run2) change2(run2);
|
|
19093
|
+
return run2 ? { ...run2 } : void 0;
|
|
19096
19094
|
});
|
|
19097
19095
|
}
|
|
19098
19096
|
function peekRun(sessionId) {
|
|
@@ -19100,19 +19098,19 @@ function peekRun(sessionId) {
|
|
|
19100
19098
|
}
|
|
19101
19099
|
async function closeRun(sessionId, res) {
|
|
19102
19100
|
const closed = withRuns((runs) => {
|
|
19103
|
-
const
|
|
19104
|
-
if (!
|
|
19105
|
-
|
|
19106
|
-
if (res.ok !== void 0)
|
|
19107
|
-
|
|
19108
|
-
if (res.error)
|
|
19109
|
-
if (res.note)
|
|
19110
|
-
if (res.errorWhy?.length)
|
|
19111
|
-
if (res.noteWhy?.length)
|
|
19112
|
-
if (res.tickedNothing)
|
|
19113
|
-
|
|
19114
|
-
|
|
19115
|
-
return { ...
|
|
19101
|
+
const run2 = runs.find((r) => r.sessionId === sessionId);
|
|
19102
|
+
if (!run2 || run2.status !== "running") return void 0;
|
|
19103
|
+
run2.status = res.status;
|
|
19104
|
+
if (res.ok !== void 0) run2.ok = res.ok;
|
|
19105
|
+
run2.code = res.code ?? null;
|
|
19106
|
+
if (res.error) run2.error = res.error;
|
|
19107
|
+
if (res.note) run2.note = res.note;
|
|
19108
|
+
if (res.errorWhy?.length) run2.errorWhy = res.errorWhy;
|
|
19109
|
+
if (res.noteWhy?.length) run2.noteWhy = res.noteWhy;
|
|
19110
|
+
if (res.tickedNothing) run2.tickedNothing = true;
|
|
19111
|
+
run2.endedAt = res.endedAt ?? Date.now();
|
|
19112
|
+
run2.pid = void 0;
|
|
19113
|
+
return { ...run2 };
|
|
19116
19114
|
});
|
|
19117
19115
|
if (!closed) return;
|
|
19118
19116
|
if (closed.status === "done" || closed.status === "error") {
|
|
@@ -19138,14 +19136,14 @@ async function stopRun(id) {
|
|
|
19138
19136
|
const restore = [];
|
|
19139
19137
|
const out = withRuns((runs) => {
|
|
19140
19138
|
reap(runs, [], restore);
|
|
19141
|
-
const
|
|
19142
|
-
if (
|
|
19143
|
-
if (!
|
|
19144
|
-
if (
|
|
19145
|
-
|
|
19146
|
-
return { ok: true, sessionId:
|
|
19139
|
+
const run2 = findRun(runs, id);
|
|
19140
|
+
if (run2 === null) return { ok: false, ...ambiguousRun(id) };
|
|
19141
|
+
if (!run2) return { ok: false, ...unknownRun(id) };
|
|
19142
|
+
if (run2.status !== "running") return { ok: true, sessionId: run2.sessionId };
|
|
19143
|
+
run2.stopping = true;
|
|
19144
|
+
return { ok: true, sessionId: run2.sessionId, pid: run2.pid, live: true };
|
|
19147
19145
|
});
|
|
19148
|
-
for (const
|
|
19146
|
+
for (const run2 of restore) await restoreCardStatus(run2);
|
|
19149
19147
|
const live = out;
|
|
19150
19148
|
if (!live.ok || !live.live) {
|
|
19151
19149
|
return { ok: live.ok, sessionId: live.sessionId, error: live.error, reason: live.reason, args: live.args };
|
|
@@ -19179,13 +19177,13 @@ async function cancelDelivery(id) {
|
|
|
19179
19177
|
}
|
|
19180
19178
|
async function stopSubRuns(parentId) {
|
|
19181
19179
|
const live = liveSubRuns(parentId);
|
|
19182
|
-
for (const
|
|
19183
|
-
await Promise.all(live.map((
|
|
19180
|
+
for (const run2 of live) await stopRun(run2.sessionId);
|
|
19181
|
+
await Promise.all(live.map((run2) => runIsDown(run2.sessionId)));
|
|
19184
19182
|
}
|
|
19185
19183
|
async function runIsDown(sessionId) {
|
|
19186
19184
|
for (const until = Date.now() + STOP_WAIT_MS; Date.now() < until; ) {
|
|
19187
|
-
const
|
|
19188
|
-
if (!
|
|
19185
|
+
const run2 = readRuns().find((r) => r.sessionId === sessionId);
|
|
19186
|
+
if (!run2 || !runIsLive(run2)) return;
|
|
19189
19187
|
await sleep2(STOP_POLL_MS);
|
|
19190
19188
|
}
|
|
19191
19189
|
}
|
|
@@ -19529,13 +19527,13 @@ import path49 from "node:path";
|
|
|
19529
19527
|
function cardsOnWorkflow(id) {
|
|
19530
19528
|
const wanted = id || DEFAULT_WORKFLOW;
|
|
19531
19529
|
const ids = [];
|
|
19532
|
-
let
|
|
19530
|
+
let files2;
|
|
19533
19531
|
try {
|
|
19534
|
-
|
|
19532
|
+
files2 = walkMd(TODO);
|
|
19535
19533
|
} catch {
|
|
19536
19534
|
return [];
|
|
19537
19535
|
}
|
|
19538
|
-
for (const file of
|
|
19536
|
+
for (const file of files2) {
|
|
19539
19537
|
const base = path49.basename(file);
|
|
19540
19538
|
const card = base === "root.md" ? idPrefix(path49.basename(path49.dirname(file))) : idPrefix(base);
|
|
19541
19539
|
if (card === null) continue;
|
|
@@ -20116,8 +20114,8 @@ function settleDelivery2(id, skipped, unskipped) {
|
|
|
20116
20114
|
function specRunAgent() {
|
|
20117
20115
|
const id = insideRun();
|
|
20118
20116
|
if (!id) return void 0;
|
|
20119
|
-
const
|
|
20120
|
-
return
|
|
20117
|
+
const run2 = readStore().runs.find((r) => r.sessionId === id);
|
|
20118
|
+
return run2?.action === "spec" ? run2.specAgent : void 0;
|
|
20121
20119
|
}
|
|
20122
20120
|
function cmdTag(id, positions, tagRaw) {
|
|
20123
20121
|
const ns = positions.split(",").map((s) => s.trim()).filter((s) => s.length > 0).map(Number);
|
|
@@ -20442,16 +20440,16 @@ function addToInbox(drop) {
|
|
|
20442
20440
|
if (file && file.data.length > MAX_BYTES) {
|
|
20443
20441
|
return { ok: false, error: `${path55.basename(file.name)} is over ${MAX_BYTES / 1024 / 1024} MB \u2014 too big for the board.` };
|
|
20444
20442
|
}
|
|
20445
|
-
const
|
|
20446
|
-
const keep =
|
|
20443
|
+
const read6 = describe(typed, file);
|
|
20444
|
+
const keep = read6.keep;
|
|
20447
20445
|
const said3 = drop.source?.trim() ?? "";
|
|
20448
20446
|
const hit = said3 ? matchSourceType(said3) : "";
|
|
20449
20447
|
const kept = said3 && !hit ? metaPair("source", said3) : null;
|
|
20450
20448
|
const incoming = {
|
|
20451
|
-
...
|
|
20449
|
+
...read6.incoming,
|
|
20452
20450
|
...drop.title?.trim() ? { title: oneLine(drop.title) } : {},
|
|
20453
20451
|
...hit ? { sourceType: hit } : {},
|
|
20454
|
-
...kept ? { meta: [kept, ...
|
|
20452
|
+
...kept ? { meta: [kept, ...read6.incoming.meta] } : {},
|
|
20455
20453
|
...drop.agent ? { agent: drop.agent } : {},
|
|
20456
20454
|
...drop.workflow ? { workflow: drop.workflow } : {}
|
|
20457
20455
|
};
|
|
@@ -21080,21 +21078,21 @@ function scheduledNext(workflow, one) {
|
|
|
21080
21078
|
}
|
|
21081
21079
|
function insideScheduledPass() {
|
|
21082
21080
|
const sessionId = insideRun();
|
|
21083
|
-
const
|
|
21084
|
-
return
|
|
21081
|
+
const run2 = sessionId ? readStore().runs.find((r) => r.sessionId === sessionId) : void 0;
|
|
21082
|
+
return run2?.action === "scheduled" && run2.workflow && run2.specAgent ? { workflow: run2.workflow, agent: run2.specAgent } : null;
|
|
21085
21083
|
}
|
|
21086
21084
|
function runAgent() {
|
|
21087
21085
|
const sessionId = insideRun();
|
|
21088
21086
|
const runs = sessionId ? readStore().runs : [];
|
|
21089
|
-
const
|
|
21090
|
-
if (!
|
|
21091
|
-
if (
|
|
21092
|
-
const parent = runs.find((r) => r.sessionId ===
|
|
21093
|
-
return parent ? agentOf(parent) :
|
|
21087
|
+
const run2 = runs.find((r) => r.sessionId === sessionId);
|
|
21088
|
+
if (!run2) return null;
|
|
21089
|
+
if (run2.action !== "sub") return agentOf(run2);
|
|
21090
|
+
const parent = runs.find((r) => r.sessionId === run2.parentId);
|
|
21091
|
+
return parent ? agentOf(parent) : run2.specAgent ?? null;
|
|
21094
21092
|
}
|
|
21095
|
-
function agentOf(
|
|
21096
|
-
if (
|
|
21097
|
-
return flowNodes().find((n) => n.flow ===
|
|
21093
|
+
function agentOf(run2) {
|
|
21094
|
+
if (run2.action === "scheduled") return run2.specAgent ?? null;
|
|
21095
|
+
return flowNodes().find((n) => n.flow === run2.action)?.agent ?? null;
|
|
21098
21096
|
}
|
|
21099
21097
|
var scheduledRequest, isPass, openPasses, stalePasses;
|
|
21100
21098
|
var init_scheduled = __esm({
|
|
@@ -21112,7 +21110,7 @@ var init_scheduled = __esm({
|
|
|
21112
21110
|
workflow: pass.workflow,
|
|
21113
21111
|
specAgent: pass.agent
|
|
21114
21112
|
});
|
|
21115
|
-
isPass = (
|
|
21113
|
+
isPass = (run2, pass) => run2.action === "scheduled" && run2.workflow === pass.workflow && run2.specAgent === pass.agent;
|
|
21116
21114
|
openPasses = (deliveries, pass) => deliveries.filter(
|
|
21117
21115
|
(d) => d.status === "active" && d.scheduled?.workflow === pass.workflow && d.scheduled.agent === pass.agent
|
|
21118
21116
|
);
|
|
@@ -21263,14 +21261,14 @@ function openRows() {
|
|
|
21263
21261
|
}
|
|
21264
21262
|
return rows.sort((a, b) => a.id - b.id);
|
|
21265
21263
|
}
|
|
21266
|
-
function cmdStale(rows,
|
|
21264
|
+
function cmdStale(rows, all2, scope, mod) {
|
|
21267
21265
|
const days = staleAfter();
|
|
21268
21266
|
const ages = cardAges();
|
|
21269
21267
|
if (ages === null) {
|
|
21270
21268
|
say(`not a git repository \u2014 a card's age is the date git last saw its file, so there is nothing to list.`);
|
|
21271
21269
|
return { cards: [], module: mod, staleAfter: days };
|
|
21272
21270
|
}
|
|
21273
|
-
const open3 = new Set(
|
|
21271
|
+
const open3 = new Set(all2.map((r) => r.id));
|
|
21274
21272
|
const stale3 = rows.filter(canGoStale).map((r) => ({ row: r, age: ages.get(r.file) })).filter((r) => r.age !== void 0 && r.age.days >= days).sort((a, b) => b.age.days - a.age.days || a.row.id - b.row.id);
|
|
21275
21273
|
const cards = stale3.map(({ row, age }) => ({ ...row, file: rel(row.file), ...age }));
|
|
21276
21274
|
if (!stale3.length) {
|
|
@@ -21288,18 +21286,18 @@ function cmdStale(rows, all, scope, mod) {
|
|
|
21288
21286
|
return { cards, module: mod, staleAfter: days };
|
|
21289
21287
|
}
|
|
21290
21288
|
function archivedFiles() {
|
|
21291
|
-
const
|
|
21292
|
-
if (!fs59.existsSync(ARCHIVE)) return
|
|
21289
|
+
const files2 = /* @__PURE__ */ new Map();
|
|
21290
|
+
if (!fs59.existsSync(ARCHIVE)) return files2;
|
|
21293
21291
|
for (const file of walkMd(ARCHIVE)) {
|
|
21294
21292
|
const id = idPrefix(path58.basename(file));
|
|
21295
|
-
if (id !== null)
|
|
21293
|
+
if (id !== null) files2.set(id, file);
|
|
21296
21294
|
}
|
|
21297
21295
|
for (const dir of walkDirs(ARCHIVE)) {
|
|
21298
21296
|
const id = idPrefix(path58.basename(dir));
|
|
21299
21297
|
const root = path58.join(dir, "root.md");
|
|
21300
|
-
if (id !== null && fs59.existsSync(root))
|
|
21298
|
+
if (id !== null && fs59.existsSync(root)) files2.set(id, root);
|
|
21301
21299
|
}
|
|
21302
|
-
return
|
|
21300
|
+
return files2;
|
|
21303
21301
|
}
|
|
21304
21302
|
function cmdArchived(opts) {
|
|
21305
21303
|
let since = null;
|
|
@@ -21319,14 +21317,14 @@ function cmdArchived(opts) {
|
|
|
21319
21317
|
}
|
|
21320
21318
|
const workflow = opts.workflow === void 0 ? null : knownWorkflow(opts.workflow);
|
|
21321
21319
|
const open3 = boardCardIds();
|
|
21322
|
-
const
|
|
21320
|
+
const files2 = archivedFiles();
|
|
21323
21321
|
const cards = landedDeliveries({ since: since?.getTime(), workflow: workflow ?? void 0 }).filter((d) => !open3.has(d.cardId)).map((d) => ({
|
|
21324
21322
|
id: d.cardId,
|
|
21325
21323
|
title: d.title,
|
|
21326
21324
|
workflow: d.workflow,
|
|
21327
21325
|
landedAt: formatStamp(new Date(d.at)),
|
|
21328
21326
|
commit: d.commit,
|
|
21329
|
-
file:
|
|
21327
|
+
file: files2.has(d.cardId) ? rel(files2.get(d.cardId)) : ""
|
|
21330
21328
|
}));
|
|
21331
21329
|
const result = { cards, since: since ? formatStamp(since) : null, workflow };
|
|
21332
21330
|
const scope = [workflow ? `on workflow \`${workflow}\`` : "", since ? `since ${formatStamp(since)}` : ""].filter(Boolean).join(" ");
|
|
@@ -21350,8 +21348,8 @@ function cmdList(opts) {
|
|
|
21350
21348
|
}
|
|
21351
21349
|
if (opts.since !== void 0) die("--since only applies to --archived.");
|
|
21352
21350
|
if (opts.workflow !== void 0) die("--workflow only applies to --archived.");
|
|
21353
|
-
const
|
|
21354
|
-
let rows =
|
|
21351
|
+
const all2 = openRows();
|
|
21352
|
+
let rows = all2;
|
|
21355
21353
|
let scope = "on the board";
|
|
21356
21354
|
const mod = opts.module;
|
|
21357
21355
|
if (mod !== void 0) {
|
|
@@ -21363,7 +21361,7 @@ function cmdList(opts) {
|
|
|
21363
21361
|
rows = rows.filter((r) => r.modules.includes(mod));
|
|
21364
21362
|
scope = `tagged \`${mod}\``;
|
|
21365
21363
|
}
|
|
21366
|
-
if (opts.stale) return cmdStale(rows,
|
|
21364
|
+
if (opts.stale) return cmdStale(rows, all2, scope, mod === void 0 ? null : mod);
|
|
21367
21365
|
const cards = rows.map((r) => ({ ...r, file: rel(r.file) }));
|
|
21368
21366
|
if (!rows.length) {
|
|
21369
21367
|
say(`no open cards ${scope}.`);
|
|
@@ -21443,10 +21441,10 @@ function stripOldHeader(text5) {
|
|
|
21443
21441
|
}
|
|
21444
21442
|
function cmdMigrate(opts) {
|
|
21445
21443
|
const dry = opts.dryRun === true;
|
|
21446
|
-
const
|
|
21444
|
+
const files2 = walkMd(TODO).filter((f) => path59.basename(f) !== "README.md");
|
|
21447
21445
|
let changed = 0;
|
|
21448
21446
|
let skipped = 0;
|
|
21449
|
-
for (const file of
|
|
21447
|
+
for (const file of files2) {
|
|
21450
21448
|
const text5 = fs60.readFileSync(file, "utf8");
|
|
21451
21449
|
if (text5.trimStart().startsWith("---")) {
|
|
21452
21450
|
skipped++;
|
|
@@ -21646,16 +21644,16 @@ function removeCard2(id, metric, options) {
|
|
|
21646
21644
|
}
|
|
21647
21645
|
}
|
|
21648
21646
|
withStore((store) => {
|
|
21649
|
-
for (const
|
|
21650
|
-
if (!leaving.some((c) =>
|
|
21651
|
-
const marks = new Map((
|
|
21647
|
+
for (const run2 of store.runs) {
|
|
21648
|
+
if (!leaving.some((c) => run2.createdCardIds?.includes(c.id))) continue;
|
|
21649
|
+
const marks = new Map((run2.discardedCards ?? []).map((c) => [c.id, c]));
|
|
21652
21650
|
for (const c of leaving) marks.set(c.id, { id: c.id, path: rel(c.file), pending: true });
|
|
21653
|
-
|
|
21651
|
+
run2.discardedCards = [...marks.values()];
|
|
21654
21652
|
}
|
|
21655
21653
|
});
|
|
21656
21654
|
}
|
|
21657
21655
|
if (options.discard) withStore((store) => {
|
|
21658
|
-
for (const
|
|
21656
|
+
for (const run2 of store.runs) for (const card of run2.discardedCards ?? []) {
|
|
21659
21657
|
if (leftIds.includes(card.id)) delete card.pending;
|
|
21660
21658
|
}
|
|
21661
21659
|
});
|
|
@@ -21856,10 +21854,10 @@ var init_agent_file = __esm({
|
|
|
21856
21854
|
// ../cli/src/commands/memory-file.ts
|
|
21857
21855
|
function cmdMemoryFile(askedName) {
|
|
21858
21856
|
const name = askedName.trim();
|
|
21859
|
-
const
|
|
21860
|
-
const memory =
|
|
21857
|
+
const all2 = globalMemories();
|
|
21858
|
+
const memory = all2.find((m) => m.name === name);
|
|
21861
21859
|
if (!memory) {
|
|
21862
|
-
const has =
|
|
21860
|
+
const has = all2.length ? `It has: ${all2.map((m) => m.name).join(", ")}.` : "It has none.";
|
|
21863
21861
|
die(`"${name}" is not a global memory on this board. ${has}`, { kind: "no-such-memory", memory: name });
|
|
21864
21862
|
}
|
|
21865
21863
|
say(memory.text);
|
|
@@ -22295,12 +22293,12 @@ function variantsProblem(raw, src, id) {
|
|
|
22295
22293
|
return `variants is not valid JSON. ${shape}`;
|
|
22296
22294
|
}
|
|
22297
22295
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return `variants is not a JSON object. ${shape}`;
|
|
22298
|
-
const
|
|
22299
|
-
if (
|
|
22300
|
-
if (!
|
|
22296
|
+
const files2 = Object.values(parsed);
|
|
22297
|
+
if (files2.length < 2 || !files2.every((f) => typeof f === "string" && f)) return `variants needs at least two labels, each naming a file. ${shape}`;
|
|
22298
|
+
if (!files2.includes(src)) return `src="${src}" is not one of the variants. Set src to the version shown first.`;
|
|
22301
22299
|
if (id === void 0) return null;
|
|
22302
22300
|
const dir = path62.join(ASSETS, String(id));
|
|
22303
|
-
for (const f of
|
|
22301
|
+
for (const f of files2) {
|
|
22304
22302
|
const parts = f.split("/");
|
|
22305
22303
|
if (parts.length !== 3 || parts[0] !== ".assets" || parts[1] !== String(id) || !inside(dir, parts[2])) {
|
|
22306
22304
|
return `Variant ${f} names no file in this card's asset folder .assets/${id}/. Write the file there or fix the path.`;
|
|
@@ -22323,7 +22321,7 @@ function compareProblem(attrs, id) {
|
|
|
22323
22321
|
if (!script && kind !== compareKind(attrs.b)) return src("Compare a and b must both be images or both be videos.");
|
|
22324
22322
|
if (attrs.shots === void 0) return script ? shots('shots is required when b is a storyboard. Add shots="..." naming a storyboard or shots list of video a.') : null;
|
|
22325
22323
|
if (kind !== "video") return shots("shots is for videos only. Remove it from this Compare tag.");
|
|
22326
|
-
const
|
|
22324
|
+
const read6 = (at) => {
|
|
22327
22325
|
const named2 = assetName(at, id, ["json"]);
|
|
22328
22326
|
if (!("name" in named2)) return { message: `Compare shots is ${named2.actual}; expected ${named2.expected}.` };
|
|
22329
22327
|
const file = inside(dir, named2.name);
|
|
@@ -22334,12 +22332,12 @@ function compareProblem(attrs, id) {
|
|
|
22334
22332
|
return { message: `${at} is not valid JSON.` };
|
|
22335
22333
|
}
|
|
22336
22334
|
};
|
|
22337
|
-
const list =
|
|
22335
|
+
const list = read6(attrs.shots);
|
|
22338
22336
|
if ("message" in list) return shots(list.message);
|
|
22339
22337
|
const why = Array.isArray(list.data) ? shotsProblem(list.data) : storyboardProblem(list.data);
|
|
22340
22338
|
if (why) return shots(`${attrs.shots}: ${why}`);
|
|
22341
22339
|
if (!script) return null;
|
|
22342
|
-
const b =
|
|
22340
|
+
const b = read6(attrs.b);
|
|
22343
22341
|
if ("message" in b) return src(b.message);
|
|
22344
22342
|
const bad = storyboardProblem(b.data);
|
|
22345
22343
|
if (bad) return src(`${attrs.b}: ${bad}`);
|
|
@@ -22586,11 +22584,11 @@ function newOpId() {
|
|
|
22586
22584
|
opCounter += 1;
|
|
22587
22585
|
return `${process.pid.toString(36)}-${Date.now().toString(36)}-${opCounter.toString(36)}`;
|
|
22588
22586
|
}
|
|
22589
|
-
async function leaseAnd(provider, target,
|
|
22587
|
+
async function leaseAnd(provider, target, run2) {
|
|
22590
22588
|
const got = await provider.lease(target);
|
|
22591
22589
|
if (!got.ok) return { ok: false, kind: "refused", error: got.error };
|
|
22592
22590
|
try {
|
|
22593
|
-
return await
|
|
22591
|
+
return await run2(envelope(got.lease.revision, got.lease.id));
|
|
22594
22592
|
} finally {
|
|
22595
22593
|
await provider.releaseLease(got.lease.id);
|
|
22596
22594
|
}
|
|
@@ -22732,14 +22730,14 @@ function localBoard() {
|
|
|
22732
22730
|
},
|
|
22733
22731
|
// ---- the named `akb raw` moves ----------------------------------------
|
|
22734
22732
|
async runMove(move2, input, env) {
|
|
22735
|
-
const
|
|
22736
|
-
if (!
|
|
22733
|
+
const run2 = MOVES[move2];
|
|
22734
|
+
if (!run2) return Promise.resolve(opRefused(new Error(`unknown command "${move2}".`)));
|
|
22737
22735
|
const target = moveTarget(move2, input.args);
|
|
22738
22736
|
try {
|
|
22739
22737
|
return await withBoardLock(() => {
|
|
22740
22738
|
const no = checkWrite(target, env, revisionAt(target));
|
|
22741
22739
|
if (no) return no;
|
|
22742
|
-
const data =
|
|
22740
|
+
const data = run2(input);
|
|
22743
22741
|
if (data instanceof Promise) return data.then((data2) => opOk(revisionAt(target), { data: data2 }));
|
|
22744
22742
|
return opOk(revisionAt(target), { data: data || {} });
|
|
22745
22743
|
});
|
|
@@ -22748,10 +22746,10 @@ function localBoard() {
|
|
|
22748
22746
|
}
|
|
22749
22747
|
},
|
|
22750
22748
|
readMove(move2, input) {
|
|
22751
|
-
const
|
|
22752
|
-
if (!
|
|
22749
|
+
const run2 = MOVES[move2];
|
|
22750
|
+
if (!run2) return Promise.reject(new Error(`unknown command "${move2}".`));
|
|
22753
22751
|
try {
|
|
22754
|
-
return Promise.resolve(
|
|
22752
|
+
return Promise.resolve(run2(input) || {});
|
|
22755
22753
|
} catch (e) {
|
|
22756
22754
|
return Promise.reject(e);
|
|
22757
22755
|
}
|
|
@@ -22813,8 +22811,8 @@ function board() {
|
|
|
22813
22811
|
if (!active) active = localBoard();
|
|
22814
22812
|
return active;
|
|
22815
22813
|
}
|
|
22816
|
-
function withLease(target,
|
|
22817
|
-
return leaseAnd(board(), target,
|
|
22814
|
+
function withLease(target, run2) {
|
|
22815
|
+
return leaseAnd(board(), target, run2);
|
|
22818
22816
|
}
|
|
22819
22817
|
var active, setCardStatusOn, archiveCard, finishSetupStep, cancelBoardDelivery, resumeBoardDelivery, discardBoardDelivery;
|
|
22820
22818
|
var init_board2 = __esm({
|
|
@@ -23093,18 +23091,18 @@ async function refresh() {
|
|
|
23093
23091
|
}
|
|
23094
23092
|
}
|
|
23095
23093
|
function sessionFrom(body, previous) {
|
|
23096
|
-
const
|
|
23097
|
-
const subject2 =
|
|
23098
|
-
if (!
|
|
23099
|
-
const expiresAt =
|
|
23094
|
+
const token3 = body;
|
|
23095
|
+
const subject2 = token3.user?.id ?? previous?.subject;
|
|
23096
|
+
if (!token3.access_token || !token3.refresh_token || !subject2) return null;
|
|
23097
|
+
const expiresAt = token3.expires_at ? token3.expires_at * 1e3 : Date.now() + (token3.expires_in ?? 3600) * 1e3;
|
|
23100
23098
|
return {
|
|
23101
23099
|
version: 1,
|
|
23102
23100
|
supabaseUrl: cloudEndpoints().supabaseUrl,
|
|
23103
|
-
accessToken:
|
|
23104
|
-
refreshToken:
|
|
23101
|
+
accessToken: token3.access_token,
|
|
23102
|
+
refreshToken: token3.refresh_token,
|
|
23105
23103
|
expiresAt,
|
|
23106
23104
|
subject: subject2,
|
|
23107
|
-
email:
|
|
23105
|
+
email: token3.user?.email ?? previous?.email,
|
|
23108
23106
|
handle: previous?.handle,
|
|
23109
23107
|
name: previous?.name,
|
|
23110
23108
|
avatarUrl: previous?.avatarUrl,
|
|
@@ -23193,16 +23191,16 @@ function cloudWorkflow(name) {
|
|
|
23193
23191
|
}
|
|
23194
23192
|
async function call(method, route2, body, key) {
|
|
23195
23193
|
if (!cloudConfigured()) return { ok: false, error: NOT_CONFIGURED, code: "bad_request" };
|
|
23196
|
-
const
|
|
23197
|
-
if (!
|
|
23198
|
-
return
|
|
23194
|
+
const token3 = await accessToken();
|
|
23195
|
+
if (!token3.ok) {
|
|
23196
|
+
return token3.reason === "unreachable" ? { ok: false, error: `Cloud could not be reached: ${token3.error}` } : { ok: false, error: "This machine is not signed in to Cloud.", code: "unauthenticated" };
|
|
23199
23197
|
}
|
|
23200
23198
|
let response;
|
|
23201
23199
|
try {
|
|
23202
23200
|
response = await fetch(`${cloudEndpoints().api}${route2}`, {
|
|
23203
23201
|
method,
|
|
23204
23202
|
headers: {
|
|
23205
|
-
authorization: `Bearer ${
|
|
23203
|
+
authorization: `Bearer ${token3.token}`,
|
|
23206
23204
|
...body ? { "content-type": "application/json" } : {},
|
|
23207
23205
|
...key ? { "idempotency-key": key } : {}
|
|
23208
23206
|
},
|
|
@@ -23228,7 +23226,8 @@ async function createTask(from, said3) {
|
|
|
23228
23226
|
source: "api",
|
|
23229
23227
|
source_context: { task: from },
|
|
23230
23228
|
...said3.run ? { run: true } : {},
|
|
23231
|
-
...said3.metadata && Object.keys(said3.metadata).length ? { metadata: said3.metadata } : {}
|
|
23229
|
+
...said3.metadata && Object.keys(said3.metadata).length ? { metadata: said3.metadata } : {},
|
|
23230
|
+
...said3.triage ? { triage: `tri_${source.value.project.replace(/^prj_/, "")}_${said3.triage}` } : {}
|
|
23232
23231
|
};
|
|
23233
23232
|
const key = `akb-${crypto2.createHash("sha256").update(`${from}
|
|
23234
23233
|
${said3.slug?.trim() || said3.title}`).digest("hex").slice(0, 40)}`;
|
|
@@ -23358,6 +23357,7 @@ __export(board_exports, {
|
|
|
23358
23357
|
buildBoardProgram: () => buildBoardProgram
|
|
23359
23358
|
});
|
|
23360
23359
|
import fs68 from "node:fs";
|
|
23360
|
+
import path65 from "node:path";
|
|
23361
23361
|
async function dispatch(move2, cmd, args, opts, cli) {
|
|
23362
23362
|
const ctx = ctxOf(cmd, cli.program);
|
|
23363
23363
|
const data = await runAction(ctx, {}, async () => {
|
|
@@ -23391,24 +23391,30 @@ async function cloudCard(task, opts) {
|
|
|
23391
23391
|
} catch {
|
|
23392
23392
|
}
|
|
23393
23393
|
}
|
|
23394
|
-
const
|
|
23394
|
+
const run2 = opts.schedule !== void 0;
|
|
23395
23395
|
const made = await createTask(task, {
|
|
23396
23396
|
title: String(opts.title ?? "").trim(),
|
|
23397
23397
|
request,
|
|
23398
23398
|
workflow: typeof opts.workflow === "string" && opts.workflow.trim() ? opts.workflow.trim() : void 0,
|
|
23399
|
-
run,
|
|
23399
|
+
run: run2,
|
|
23400
23400
|
slug: typeof opts.slug === "string" ? opts.slug : void 0,
|
|
23401
|
-
metadata:
|
|
23401
|
+
metadata: run2 ? cardFields(opts) : void 0,
|
|
23402
|
+
triage: typeof opts.triage === "string" ? triageFile(opts.triage) : void 0
|
|
23402
23403
|
});
|
|
23403
23404
|
if (!made.ok) throw new BoardError(`Cloud did not make the task: ${made.error}`, { kind: "refused" });
|
|
23404
23405
|
const { task: created, number, from } = made.value;
|
|
23405
|
-
if (
|
|
23406
|
+
if (run2) {
|
|
23406
23407
|
say(number);
|
|
23407
23408
|
say(` made ${created.key} in Cloud${made.value.replayed ? " (already made)" : ""} \u2014 it is planned there, from ${from.key}`);
|
|
23408
23409
|
return { done: { board: KANBAN, id: number, ids: [], key: created.key, task: created.id, replayed: made.value.replayed } };
|
|
23409
23410
|
}
|
|
23410
23411
|
return { opts: { ...opts, id: number, fromTask: from.key, schedule: void 0 } };
|
|
23411
23412
|
}
|
|
23413
|
+
function triageFile(sourceId) {
|
|
23414
|
+
const found2 = [...readInbox(), ...readAllDismissed(), ...readArchived()].find((s) => s.sourceId === sourceId.trim());
|
|
23415
|
+
if (!found2) throw new BoardError(`nothing in triage is ${sourceId}`, { kind: "refused" });
|
|
23416
|
+
return path65.basename(found2.relPath, ".md");
|
|
23417
|
+
}
|
|
23412
23418
|
function cardFields(opts) {
|
|
23413
23419
|
const list = (v) => Array.isArray(v) && v.length ? v.map((x) => String(x).replace(/^#/, "")) : void 0;
|
|
23414
23420
|
return Object.fromEntries(Object.entries({
|
|
@@ -23568,6 +23574,7 @@ var init_board3 = __esm({
|
|
|
23568
23574
|
init_rules();
|
|
23569
23575
|
init_shared();
|
|
23570
23576
|
init_esm();
|
|
23577
|
+
init_inbox();
|
|
23571
23578
|
init_tasks();
|
|
23572
23579
|
init_idea();
|
|
23573
23580
|
CREATE_SCHEDULES = SCHEDULED_ACTIONS.filter((a) => a !== "revise");
|
|
@@ -23577,15 +23584,15 @@ var init_board3 = __esm({
|
|
|
23577
23584
|
|
|
23578
23585
|
// ../cli/src/lib/board-cli.ts
|
|
23579
23586
|
import fs69 from "node:fs";
|
|
23580
|
-
import
|
|
23587
|
+
import path66 from "node:path";
|
|
23581
23588
|
function useBoard(found2, dirNamed) {
|
|
23582
|
-
if (!found2.named && found2.board ===
|
|
23589
|
+
if (!found2.named && found2.board === path66.join(found2.root, "docs", "kanban")) setBoardRoot(found2.root, dirNamed);
|
|
23583
23590
|
else setBoardDir(found2.board, found2.root);
|
|
23584
23591
|
useProjectState();
|
|
23585
23592
|
}
|
|
23586
23593
|
function whole(found2, move2) {
|
|
23587
23594
|
if (move2 === MAKES_A_BOARD) return found2;
|
|
23588
|
-
if (!fs69.existsSync(
|
|
23595
|
+
if (!fs69.existsSync(path66.join(found2.board, "todo"))) {
|
|
23589
23596
|
die(`the board in ${found2.board} has no todo/ \u2014 run \`init\` to add what is missing.`, {
|
|
23590
23597
|
kind: "board-incomplete",
|
|
23591
23598
|
dir: found2.root
|
|
@@ -23594,11 +23601,11 @@ function whole(found2, move2) {
|
|
|
23594
23601
|
return found2;
|
|
23595
23602
|
}
|
|
23596
23603
|
function findBoardUpward(from) {
|
|
23597
|
-
let dir =
|
|
23604
|
+
let dir = path66.resolve(from);
|
|
23598
23605
|
for (; ; ) {
|
|
23599
|
-
if (hasBoard(dir)) return { root: dir, board:
|
|
23606
|
+
if (hasBoard(dir)) return { root: dir, board: path66.join(dir, "docs", "kanban") };
|
|
23600
23607
|
if (isBoardDir(dir)) return { root: projectRootOf(dir), board: dir };
|
|
23601
|
-
const up =
|
|
23608
|
+
const up = path66.dirname(dir);
|
|
23602
23609
|
if (up === dir) return null;
|
|
23603
23610
|
dir = up;
|
|
23604
23611
|
}
|
|
@@ -23606,7 +23613,7 @@ function findBoardUpward(from) {
|
|
|
23606
23613
|
function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
|
|
23607
23614
|
const named2 = board2 ?? boardFromEnv();
|
|
23608
23615
|
if (named2) {
|
|
23609
|
-
const folder =
|
|
23616
|
+
const folder = path66.resolve(cwd, named2);
|
|
23610
23617
|
if (!fs69.existsSync(folder) && move2 !== MAKES_A_BOARD) {
|
|
23611
23618
|
die(`no such folder: ${folder}`, { kind: "no-such-folder", dir: folder });
|
|
23612
23619
|
}
|
|
@@ -23619,7 +23626,7 @@ function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
|
|
|
23619
23626
|
return { root: projectRootOf(folder), board: folder, named: true };
|
|
23620
23627
|
}
|
|
23621
23628
|
if (dir !== null) {
|
|
23622
|
-
const root =
|
|
23629
|
+
const root = path66.resolve(dir);
|
|
23623
23630
|
if (!fs69.existsSync(root)) die(`no such folder: ${root}`, { kind: "no-such-folder", dir: root });
|
|
23624
23631
|
if (!hasBoard(root) && move2 !== MAKES_A_BOARD) {
|
|
23625
23632
|
die(`no board in ${root} \u2014 it has no docs/kanban/. Run ${installHint} there to make one.`, {
|
|
@@ -23627,14 +23634,14 @@ function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
|
|
|
23627
23634
|
dir: root
|
|
23628
23635
|
});
|
|
23629
23636
|
}
|
|
23630
|
-
return whole({ root, board:
|
|
23637
|
+
return whole({ root, board: path66.join(root, "docs", "kanban") }, move2);
|
|
23631
23638
|
}
|
|
23632
23639
|
const found2 = findBoardUpward(cwd);
|
|
23633
23640
|
if (found2) return whole(found2, move2);
|
|
23634
|
-
if (move2 === MAKES_A_BOARD) return { root:
|
|
23641
|
+
if (move2 === MAKES_A_BOARD) return { root: path66.resolve(cwd), board: path66.join(path66.resolve(cwd), "docs", "kanban") };
|
|
23635
23642
|
die(
|
|
23636
|
-
`no board here. Looked in ${
|
|
23637
|
-
{ kind: "no-board", dir:
|
|
23643
|
+
`no board here. Looked in ${path66.resolve(cwd)} and every folder above it for docs/kanban/. Run ${installHint} to make one, or name the board with --board.`,
|
|
23644
|
+
{ kind: "no-board", dir: path66.resolve(cwd) }
|
|
23638
23645
|
);
|
|
23639
23646
|
}
|
|
23640
23647
|
function editDistance(a, b) {
|
|
@@ -23716,8 +23723,8 @@ var init_board_cli = __esm({
|
|
|
23716
23723
|
init_io();
|
|
23717
23724
|
init_pointer();
|
|
23718
23725
|
MAKES_A_BOARD = "init";
|
|
23719
|
-
hasBoard = (dir) => fs69.existsSync(
|
|
23720
|
-
isBoardDir = (dir) => fs69.existsSync(
|
|
23726
|
+
hasBoard = (dir) => fs69.existsSync(path66.join(dir, "docs", "kanban"));
|
|
23727
|
+
isBoardDir = (dir) => fs69.existsSync(path66.join(dir, "todo")) && fs69.existsSync(path66.join(dir, "config.md"));
|
|
23721
23728
|
boardFromEnv = () => {
|
|
23722
23729
|
const named2 = process.env.AI4KANBAN_BOARD?.trim();
|
|
23723
23730
|
return named2 ? named2 : null;
|
|
@@ -23726,8 +23733,8 @@ var init_board_cli = __esm({
|
|
|
23726
23733
|
});
|
|
23727
23734
|
|
|
23728
23735
|
// ../cli/src/lib/agent/approve.ts
|
|
23729
|
-
import
|
|
23730
|
-
import
|
|
23736
|
+
import fs75 from "node:fs";
|
|
23737
|
+
import path72 from "node:path";
|
|
23731
23738
|
function approvalRequest(req) {
|
|
23732
23739
|
const id = req.id;
|
|
23733
23740
|
const card = id === void 0 ? null : findCard(id);
|
|
@@ -23745,12 +23752,12 @@ function advancePhase(id) {
|
|
|
23745
23752
|
const card = findCard(id);
|
|
23746
23753
|
const found2 = locate(id);
|
|
23747
23754
|
if (!card || !found2 || !awaitingApproval(card)) return { error: `#${id} is no longer waiting on approval.` };
|
|
23748
|
-
const file = found2.kind === "group" ?
|
|
23749
|
-
const { meta, body } = parseFrontmatter(
|
|
23755
|
+
const file = found2.kind === "group" ? path72.join(found2.target, "root.md") : found2.target;
|
|
23756
|
+
const { meta, body } = parseFrontmatter(fs75.readFileSync(file, "utf8"));
|
|
23750
23757
|
if (!meta) return { error: `#${id} has no frontmatter` };
|
|
23751
23758
|
meta.phase = card.stage + 1;
|
|
23752
23759
|
meta.status = "todo";
|
|
23753
|
-
|
|
23760
|
+
fs75.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
|
|
23754
23761
|
return null;
|
|
23755
23762
|
}
|
|
23756
23763
|
var init_approve = __esm({
|
|
@@ -23840,10 +23847,10 @@ function open2(req, sessionId) {
|
|
|
23840
23847
|
returnRunPictures(sessionId, req.box);
|
|
23841
23848
|
return opened;
|
|
23842
23849
|
}
|
|
23843
|
-
const { run } = opened;
|
|
23844
|
-
const pid = spawnWatcher(
|
|
23845
|
-
markSpawned(
|
|
23846
|
-
return { run, spawned: pid !== void 0 };
|
|
23850
|
+
const { run: run2 } = opened;
|
|
23851
|
+
const pid = spawnWatcher(run2.sessionId);
|
|
23852
|
+
markSpawned(run2.sessionId, pid);
|
|
23853
|
+
return { run: run2, spawned: pid !== void 0 };
|
|
23847
23854
|
}
|
|
23848
23855
|
async function startResume(id) {
|
|
23849
23856
|
const opened = await openResume(id);
|
|
@@ -23870,16 +23877,16 @@ var init_start = __esm({
|
|
|
23870
23877
|
});
|
|
23871
23878
|
|
|
23872
23879
|
// ../cli/src/lib/agent/task-chats.ts
|
|
23873
|
-
import
|
|
23874
|
-
import
|
|
23880
|
+
import fs76 from "node:fs";
|
|
23881
|
+
import path73 from "node:path";
|
|
23875
23882
|
function taskChatFile(task, home3) {
|
|
23876
23883
|
if (!TASK.test(task)) throw new Error(`${task} is not a task id.`);
|
|
23877
|
-
return
|
|
23884
|
+
return path73.join(taskChatsDir(home3), `${task}.jsonl`);
|
|
23878
23885
|
}
|
|
23879
23886
|
function readTaskChat(task, home3) {
|
|
23880
23887
|
let text5;
|
|
23881
23888
|
try {
|
|
23882
|
-
text5 =
|
|
23889
|
+
text5 = fs76.readFileSync(taskChatFile(task, home3), "utf8");
|
|
23883
23890
|
} catch {
|
|
23884
23891
|
return [];
|
|
23885
23892
|
}
|
|
@@ -23900,20 +23907,20 @@ var init_task_chats = __esm({
|
|
|
23900
23907
|
"use strict";
|
|
23901
23908
|
init_home();
|
|
23902
23909
|
TASK = /^[\w-]{1,100}$/;
|
|
23903
|
-
taskChatsDir = (home3 = machineHome()) =>
|
|
23910
|
+
taskChatsDir = (home3 = machineHome()) => path73.join(home3, "chats");
|
|
23904
23911
|
}
|
|
23905
23912
|
});
|
|
23906
23913
|
|
|
23907
23914
|
// ../cli/src/lib/agent/memory-review.ts
|
|
23908
|
-
import
|
|
23909
|
-
import
|
|
23915
|
+
import fs77 from "node:fs";
|
|
23916
|
+
import path74 from "node:path";
|
|
23910
23917
|
function taskChatsToReview(tasks) {
|
|
23911
|
-
const cards =
|
|
23918
|
+
const cards = fs77.existsSync(ARCHIVE) ? walkMd(ARCHIVE) : [];
|
|
23912
23919
|
const out = [];
|
|
23913
23920
|
for (const task of [...new Set(tasks)]) {
|
|
23914
23921
|
const slug2 = task.toLowerCase().replace(/_/g, "-");
|
|
23915
|
-
const file = cards.find((f) => /^\d+-/.test(
|
|
23916
|
-
const id = file ? idPrefix(
|
|
23922
|
+
const file = cards.find((f) => /^\d+-/.test(path74.basename(f)) && path74.basename(f).endsWith(`-${slug2}.md`));
|
|
23923
|
+
const id = file ? idPrefix(path74.basename(file)) : null;
|
|
23917
23924
|
const card = file && id !== null ? readArchived2(id, file) : null;
|
|
23918
23925
|
let lines = [];
|
|
23919
23926
|
try {
|
|
@@ -23929,7 +23936,7 @@ function taskChatsToReview(tasks) {
|
|
|
23929
23936
|
}
|
|
23930
23937
|
function readArchived2(id, file) {
|
|
23931
23938
|
try {
|
|
23932
|
-
const { meta, body } = parseFrontmatter(
|
|
23939
|
+
const { meta, body } = parseFrontmatter(fs77.readFileSync(file, "utf8"));
|
|
23933
23940
|
if (!meta) return null;
|
|
23934
23941
|
return { id, title: meta.title, modules: meta.modules ?? [], rejected: meta.rejected, body };
|
|
23935
23942
|
} catch {
|
|
@@ -23975,8 +23982,8 @@ var init_memory_review = __esm({
|
|
|
23975
23982
|
});
|
|
23976
23983
|
|
|
23977
23984
|
// ../cli/src/lib/agent/flow.ts
|
|
23978
|
-
import
|
|
23979
|
-
import
|
|
23985
|
+
import fs78 from "node:fs";
|
|
23986
|
+
import path75 from "node:path";
|
|
23980
23987
|
function readCard2(id, home3 = "board") {
|
|
23981
23988
|
const found2 = home3 === "archive" ? locateArchived(id) : locate(id);
|
|
23982
23989
|
if (!found2) {
|
|
@@ -23985,10 +23992,10 @@ function readCard2(id, home3 = "board") {
|
|
|
23985
23992
|
{ kind: "card-not-found", id }
|
|
23986
23993
|
);
|
|
23987
23994
|
}
|
|
23988
|
-
const file = found2.kind === "group" ?
|
|
23995
|
+
const file = found2.kind === "group" ? path75.join(found2.target, "root.md") : found2.target;
|
|
23989
23996
|
let text5;
|
|
23990
23997
|
try {
|
|
23991
|
-
text5 =
|
|
23998
|
+
text5 = fs78.readFileSync(file, "utf8");
|
|
23992
23999
|
} catch {
|
|
23993
24000
|
die(`#${id} is on the board but ${rel(file)} can't be read.`, { kind: "card-unreadable", id });
|
|
23994
24001
|
}
|
|
@@ -24046,7 +24053,7 @@ function memoryLines(modules, name) {
|
|
|
24046
24053
|
}
|
|
24047
24054
|
function configText() {
|
|
24048
24055
|
try {
|
|
24049
|
-
return
|
|
24056
|
+
return fs78.readFileSync(CONFIG, "utf8").trim() || null;
|
|
24050
24057
|
} catch {
|
|
24051
24058
|
return null;
|
|
24052
24059
|
}
|
|
@@ -24092,10 +24099,10 @@ function candidateField(delivery) {
|
|
|
24092
24099
|
}
|
|
24093
24100
|
const candidate = candidateOf(delivery);
|
|
24094
24101
|
const stat = candidateStat(candidate);
|
|
24095
|
-
const
|
|
24102
|
+
const files2 = candidateFileStats(candidate);
|
|
24096
24103
|
const patch2 = candidatePatch(candidate);
|
|
24097
24104
|
const command = delivery.worktree && delivery.branch ? `git diff ${delivery.base.slice(0, 12)} ${delivery.branch}` : `git diff ${delivery.base.slice(0, 12)}`;
|
|
24098
|
-
const shown2 =
|
|
24105
|
+
const shown2 = files2?.length ? ["changed files:", ...files2.map((file) => ` ${file}`)] : ["no file changed"];
|
|
24099
24106
|
let diff = [];
|
|
24100
24107
|
if (patch2 === null) diff = ["", "the diff could not be read"];
|
|
24101
24108
|
else if (patch2.length > MAX_DIFF) {
|
|
@@ -24119,7 +24126,7 @@ function workspaceField(delivery) {
|
|
|
24119
24126
|
if (!delivery?.worktree) return [];
|
|
24120
24127
|
return field("workspace", [
|
|
24121
24128
|
`write code in ${delivery.worktree} \u2014 this delivery's own worktree, on branch ${delivery.branch}.`,
|
|
24122
|
-
|
|
24129
|
+
path75.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.`,
|
|
24123
24130
|
`every board command names the project's own copy: \`${boardCommandFor()} <command>\`.`,
|
|
24124
24131
|
`${rel(REPO_ROOT)} is the project \u2014 the card, the memory files and the docs are changed there, not here.`
|
|
24125
24132
|
]);
|
|
@@ -24127,7 +24134,7 @@ function workspaceField(delivery) {
|
|
|
24127
24134
|
function conflictField(delivery) {
|
|
24128
24135
|
if (!delivery?.worktree) return field("conflict", "no delivery with a worktree is landing right now");
|
|
24129
24136
|
const dir = worktreeDir(delivery.worktree);
|
|
24130
|
-
const
|
|
24137
|
+
const files2 = conflictedPaths(dir);
|
|
24131
24138
|
const target = delivery.targetBranch ?? "the target branch";
|
|
24132
24139
|
const since = delivery.landing?.originalBase ?? delivery.base;
|
|
24133
24140
|
const onto = delivery.landing?.onto ?? delivery.targetBranch;
|
|
@@ -24138,7 +24145,7 @@ function conflictField(delivery) {
|
|
|
24138
24145
|
return field("conflict", [
|
|
24139
24146
|
...delivery.landing?.why ? [`landing: ${delivery.landing.why}`] : [],
|
|
24140
24147
|
`worktree: ${delivery.worktree}`,
|
|
24141
|
-
...
|
|
24148
|
+
...files2.length ? [`conflicted files (${files2.length}):`, ...listed(files2)] : ["conflicted files: none"],
|
|
24142
24149
|
...since && onto ? [
|
|
24143
24150
|
`original base: ${short2(since)} \u2014 \`git diff ${short2(since)} ${short2(onto)}\` is what ${target} brought in since this delivery started`,
|
|
24144
24151
|
`${target} changed since then (${theirs.length}):`,
|
|
@@ -24500,7 +24507,7 @@ function printFlow(rawReq, program2 = "akb") {
|
|
|
24500
24507
|
say("");
|
|
24501
24508
|
say(`there is no \`akb\` on this machine \u2014 every \`akb\` below, in the flows too, is \`${program2}\` here.`);
|
|
24502
24509
|
}
|
|
24503
|
-
if (req.action !== "setup" &&
|
|
24510
|
+
if (req.action !== "setup" && fs78.existsSync(SETUP_CHECKLIST)) {
|
|
24504
24511
|
say("");
|
|
24505
24512
|
say(`this board is not set up yet \u2014 ${rel(SETUP_CHECKLIST)} is still there.`);
|
|
24506
24513
|
say(`finish it first: ${setupInstruction()}`);
|
|
@@ -24540,6 +24547,11 @@ function printFlow(rawReq, program2 = "akb") {
|
|
|
24540
24547
|
say("");
|
|
24541
24548
|
say(rule);
|
|
24542
24549
|
}
|
|
24550
|
+
const language = languageNote();
|
|
24551
|
+
if (language) {
|
|
24552
|
+
say("");
|
|
24553
|
+
say(language);
|
|
24554
|
+
}
|
|
24543
24555
|
return {
|
|
24544
24556
|
mode: "print",
|
|
24545
24557
|
action: req.action,
|
|
@@ -24589,9 +24601,9 @@ var init_flow = __esm({
|
|
|
24589
24601
|
EDITABLE_FIELDS = "--title|--priority|--roi|--modules|--blocked-by|--related";
|
|
24590
24602
|
bodyScaffoldClose = (lead = "fill the existing") => [`${lead} body scaffold; do not rename or translate its section titles, and leave empty scaffold sections in place`];
|
|
24591
24603
|
MAX_LISTED = 50;
|
|
24592
|
-
listed = (
|
|
24593
|
-
...
|
|
24594
|
-
...
|
|
24604
|
+
listed = (files2) => [
|
|
24605
|
+
...files2.slice(0, MAX_LISTED).map((f) => ` ${f}`),
|
|
24606
|
+
...files2.length > MAX_LISTED ? [` \u2026and ${files2.length - MAX_LISTED} more`] : []
|
|
24595
24607
|
];
|
|
24596
24608
|
answeredClose = (delivery, self) => `${self} delivery answered ${delivery.deliveryId} --changed|--unchanged "<why>" \u2014 before you drop the questions; dropping the last one with nothing said carries the build on as --unchanged`;
|
|
24597
24609
|
stageClose = (meta) => {
|
|
@@ -24715,30 +24727,30 @@ function stalledLine(cardId2, next) {
|
|
|
24715
24727
|
if (!card || card.schedule || refinementStep(card) === "done") return null;
|
|
24716
24728
|
return next === "incomplete" ? `QA left untagged questions on #${card.id}, so it did not finish. #${card.id} is still at todo and nothing else will pick it up \u2014 refine it again, or mark it ready yourself.` : `#${card.id} is still in To do after planning, and the board won't plan it again on its own \u2014 refine it again, or mark it ready yourself.`;
|
|
24717
24729
|
}
|
|
24718
|
-
function planStageEnd(
|
|
24719
|
-
if (
|
|
24720
|
-
const card = currentCard(
|
|
24730
|
+
function planStageEnd(run2) {
|
|
24731
|
+
if (run2.cardId === null || stageOfAction(run2.action) !== "plan") return null;
|
|
24732
|
+
const card = currentCard(run2.cardId);
|
|
24721
24733
|
if (!card || card.openBlockers.length > 0 || openOf(card.questions).length > 0 || card.schedule) return null;
|
|
24722
24734
|
if (refinementStep(card) !== "done") return null;
|
|
24723
24735
|
return endOfStage(stageContract("plan"), card);
|
|
24724
24736
|
}
|
|
24725
|
-
function refinementRunsAfter(
|
|
24726
|
-
for (const id of /* @__PURE__ */ new Set([...changed, ...
|
|
24737
|
+
function refinementRunsAfter(run2, changed, before, waitingForSpec = false, refused = []) {
|
|
24738
|
+
for (const id of /* @__PURE__ */ new Set([...changed, ...run2.cardId === null ? [] : [run2.cardId]])) {
|
|
24727
24739
|
scheduleRefineOnBlock(id);
|
|
24728
24740
|
}
|
|
24729
|
-
const next = waitingForSpec ||
|
|
24730
|
-
const starts2 = refinesAfter(
|
|
24731
|
-
(req) => req.id !==
|
|
24741
|
+
const next = waitingForSpec || run2.cardId === null || !["clarify", "resolve", "edit", "approve"].includes(run2.action) ? null : refinementAfter(run2.action, run2.cardId, run2.flowId);
|
|
24742
|
+
const starts2 = refinesAfter(run2.action, changed, before).filter(
|
|
24743
|
+
(req) => req.id !== run2.cardId || run2.refineRound === void 0 && run2.action !== "spec"
|
|
24732
24744
|
);
|
|
24733
24745
|
const refusedNote = waitingForSpec || !refused.length ? "" : `Spec agents not started: ${refused.map((r) => r.error).join(" ")}`;
|
|
24734
24746
|
const plain2 = typeof next === "object" && next ? next : null;
|
|
24735
24747
|
const carryOn = plain2 && refusedNote ? { ...plain2, notes: [plain2.notes, refusedNote].filter(Boolean).join("\n\n") } : plain2;
|
|
24736
|
-
const end2 = waitingForSpec || carryOn || starts2.length ? null : planStageEnd(
|
|
24748
|
+
const end2 = waitingForSpec || carryOn || starts2.length ? null : planStageEnd(run2);
|
|
24737
24749
|
const short2 = end2 && "missing" in end2 ? end2 : null;
|
|
24738
|
-
const stalled = (short2 ? shortLine(short2,
|
|
24750
|
+
const stalled = (short2 ? shortLine(short2, run2.cardId) : void 0) ?? (waitingForSpec ? void 0 : (run2.refineRound !== void 0 || next === "incomplete") && stalledLine(run2.cardId, next) || void 0);
|
|
24739
24751
|
const stalledWhy = [
|
|
24740
24752
|
...refusedNote ? [{ kind: "specRefused", refusals: refused }] : [],
|
|
24741
|
-
...!stalled ? [] : short2 ? [{ kind: "stageShort", args: { stage: short2.stage, card: String(
|
|
24753
|
+
...!stalled ? [] : short2 ? [{ kind: "stageShort", args: { stage: short2.stage, card: String(run2.cardId), agents: short2.missing.join(", ") } }] : next === "incomplete" ? [{ kind: "qaUnfinished", args: { card: String(run2.cardId) } }] : [{ kind: "refineStalled", args: { card: String(run2.cardId) } }]
|
|
24742
24754
|
];
|
|
24743
24755
|
return {
|
|
24744
24756
|
runs: [...starts2, ...carryOn ? [carryOn] : [], ...end2 && "ask" in end2 ? [end2.ask] : []],
|
|
@@ -24921,21 +24933,21 @@ __export(watch_exports, {
|
|
|
24921
24933
|
watchRun: () => watchRun
|
|
24922
24934
|
});
|
|
24923
24935
|
import { spawn as spawn3 } from "node:child_process";
|
|
24924
|
-
import
|
|
24936
|
+
import fs79 from "node:fs";
|
|
24925
24937
|
async function watchRun(sessionId, resume = startResume) {
|
|
24926
24938
|
const spec = readSpec(sessionId);
|
|
24927
|
-
const
|
|
24928
|
-
if (!
|
|
24929
|
-
if (
|
|
24939
|
+
const run2 = peekRun(sessionId);
|
|
24940
|
+
if (!run2 || !spec) {
|
|
24941
|
+
if (run2?.status === "running") await closeRun(sessionId, { status: "interrupted", code: null });
|
|
24930
24942
|
return 1;
|
|
24931
24943
|
}
|
|
24932
|
-
if (
|
|
24944
|
+
if (run2.stopping) {
|
|
24933
24945
|
await closeRun(sessionId, { status: "stopped", code: null });
|
|
24934
24946
|
return 1;
|
|
24935
24947
|
}
|
|
24936
24948
|
const stopping = () => peekRun(sessionId)?.status !== "running";
|
|
24937
24949
|
let releaseIndex = null;
|
|
24938
|
-
if (needsIndexLock(
|
|
24950
|
+
if (needsIndexLock(run2.action)) {
|
|
24939
24951
|
releaseIndex = await acquireIndexLock(stopping);
|
|
24940
24952
|
if (!releaseIndex) {
|
|
24941
24953
|
if (!stopping()) await closeRun(sessionId, { status: "interrupted", code: null });
|
|
@@ -24954,14 +24966,14 @@ async function watchRun(sessionId, resume = startResume) {
|
|
|
24954
24966
|
const claimed = patch(sessionId, (r) => {
|
|
24955
24967
|
claim = claimCard(r);
|
|
24956
24968
|
});
|
|
24957
|
-
const record = claimed ??
|
|
24969
|
+
const record = claimed ?? run2;
|
|
24958
24970
|
const sub = record.action === "sub";
|
|
24959
24971
|
if (claim) await setCardStatus(claim.cardId, claim.status);
|
|
24960
|
-
|
|
24961
|
-
const logFile =
|
|
24972
|
+
fs79.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
24973
|
+
const logFile = fs79.createWriteStream(record.logPath, { flags: "a" });
|
|
24962
24974
|
logFile.on("error", () => {
|
|
24963
24975
|
});
|
|
24964
|
-
const eventsFile =
|
|
24976
|
+
const eventsFile = fs79.createWriteStream(eventsPathOf(sessionId), { flags: "a" });
|
|
24965
24977
|
eventsFile.on("error", () => {
|
|
24966
24978
|
});
|
|
24967
24979
|
const event2 = (e) => eventsFile.write(`${JSON.stringify(e)}
|
|
@@ -24982,7 +24994,7 @@ async function watchRun(sessionId, resume = startResume) {
|
|
|
24982
24994
|
if (active2.startNote) log.write(`[board] ${active2.startNote}
|
|
24983
24995
|
`);
|
|
24984
24996
|
const basePrompt = record.formatRepair ? contractRepairPrompt(requestOf(record), record.formatRepair.errors) : record.resumedFrom ? spec.prompt || resumePrompt(record.deliveryId, record.cardId, record.action) : spec.prompt;
|
|
24985
|
-
const prompt = [basePrompt, discardedCardsPrompt(record.discardedCards)].filter(Boolean).join("\n\n");
|
|
24997
|
+
const prompt = inLanguage([basePrompt, discardedCardsPrompt(record.discardedCards)].filter(Boolean).join("\n\n"));
|
|
24986
24998
|
const before = markBoard();
|
|
24987
24999
|
const sources = snapshotSpecs();
|
|
24988
25000
|
const wasBroken = new Set(boardComplaints());
|
|
@@ -24995,7 +25007,7 @@ async function watchRun(sessionId, resume = startResume) {
|
|
|
24995
25007
|
void refreshCatalog();
|
|
24996
25008
|
const client = active2.client;
|
|
24997
25009
|
const restartBase = !client || record.formatRepair ? void 0 : record.resumedFrom ? restartPrompt(requestOf(record), record.deliveryId) : record.continues && !record.continues.fork ? spec.prompt : void 0;
|
|
24998
|
-
const restart = restartBase ? [restartBase, discardedCardsPrompt(record.discardedCards)].filter(Boolean).join("\n\n") : void 0;
|
|
25010
|
+
const restart = restartBase ? inLanguage([restartBase, discardedCardsPrompt(record.discardedCards)].filter(Boolean).join("\n\n")) : void 0;
|
|
24999
25011
|
const stdinPrompt = client ? void 0 : active2.stdinPrompt;
|
|
25000
25012
|
const stdio = [client || stdinPrompt ? "pipe" : "ignore", "pipe", "pipe"];
|
|
25001
25013
|
let child;
|
|
@@ -25444,11 +25456,11 @@ function tickedNoSetupStep(record) {
|
|
|
25444
25456
|
const now = tickedSetupSteps();
|
|
25445
25457
|
return now !== void 0 && now <= record.setupTicked;
|
|
25446
25458
|
}
|
|
25447
|
-
function settleBoard(
|
|
25459
|
+
function settleBoard(run2, changed, before) {
|
|
25448
25460
|
try {
|
|
25449
|
-
const helpers = specRunsAfter(readSpecAsks(
|
|
25450
|
-
const waitingForSpec = helpers.some((req) => req.id ===
|
|
25451
|
-
return refinementRunsAfter(
|
|
25461
|
+
const helpers = specRunsAfter(readSpecAsks(run2.sessionId));
|
|
25462
|
+
const waitingForSpec = helpers.some((req) => req.id === run2.cardId);
|
|
25463
|
+
return refinementRunsAfter(run2, changed, before, waitingForSpec, readSpecRefusals(run2.sessionId));
|
|
25452
25464
|
} catch {
|
|
25453
25465
|
return null;
|
|
25454
25466
|
}
|
|
@@ -25530,6 +25542,7 @@ var init_watch = __esm({
|
|
|
25530
25542
|
init_log();
|
|
25531
25543
|
init_wire();
|
|
25532
25544
|
init_prompts();
|
|
25545
|
+
init_language();
|
|
25533
25546
|
init_resolve();
|
|
25534
25547
|
init_runner();
|
|
25535
25548
|
init_retry();
|
|
@@ -25549,7 +25562,7 @@ var init_watch = __esm({
|
|
|
25549
25562
|
});
|
|
25550
25563
|
|
|
25551
25564
|
// ../cli/src/kanban.ts
|
|
25552
|
-
import
|
|
25565
|
+
import fs87 from "node:fs";
|
|
25553
25566
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
25554
25567
|
|
|
25555
25568
|
// ../cli/src/lib/cli/agent.ts
|
|
@@ -25646,11 +25659,11 @@ ${final}` : lines;
|
|
|
25646
25659
|
}
|
|
25647
25660
|
function testConnection(pin) {
|
|
25648
25661
|
const ask2 = !pin ? {} : runtimeById(pin) ? { pin } : { pin, harness: pin };
|
|
25649
|
-
const
|
|
25662
|
+
const run2 = openPlan(planRun(randomUUID7(), REPO_ROOT, void 0, ask2));
|
|
25650
25663
|
const startedAt = Date.now();
|
|
25651
|
-
const [cmd, ...args] =
|
|
25652
|
-
const client =
|
|
25653
|
-
const stdinPrompt = client ? void 0 :
|
|
25664
|
+
const [cmd, ...args] = run2.argv;
|
|
25665
|
+
const client = run2.client;
|
|
25666
|
+
const stdinPrompt = client ? void 0 : run2.stdinPrompt;
|
|
25654
25667
|
const stdio = [client || stdinPrompt ? "pipe" : "ignore", "pipe", "pipe"];
|
|
25655
25668
|
return new Promise((resolve2) => {
|
|
25656
25669
|
let events = "";
|
|
@@ -25662,16 +25675,16 @@ function testConnection(pin) {
|
|
|
25662
25675
|
if (settled) return;
|
|
25663
25676
|
settled = true;
|
|
25664
25677
|
stopTimer();
|
|
25665
|
-
const output =
|
|
25678
|
+
const output = run2.startNote && res.output ? `${run2.startNote}
|
|
25666
25679
|
${res.output}` : res.output;
|
|
25667
|
-
resolve2({ ...res, output, harness:
|
|
25680
|
+
resolve2({ ...res, output, harness: run2.harness, ms: Date.now() - startedAt });
|
|
25668
25681
|
};
|
|
25669
25682
|
const spawnFailed = (e) => {
|
|
25670
25683
|
if (e?.code === "ENOENT") {
|
|
25671
|
-
done2({ ok: false, missing: cmd, install:
|
|
25684
|
+
done2({ ok: false, missing: cmd, install: run2.install });
|
|
25672
25685
|
return;
|
|
25673
25686
|
}
|
|
25674
|
-
done2({ ok: false, output: said2(events,
|
|
25687
|
+
done2({ ok: false, output: said2(events, run2.renderer?.result(), `${stderr}
|
|
25675
25688
|
${String(e)}`) });
|
|
25676
25689
|
};
|
|
25677
25690
|
const mark = stopMark();
|
|
@@ -25683,7 +25696,7 @@ ${String(e)}`) });
|
|
|
25683
25696
|
// folder — an agent started there is outside the repo, and codex refuses to run at
|
|
25684
25697
|
// all ("Not inside a trusted directory").
|
|
25685
25698
|
cwd: REPO_ROOT,
|
|
25686
|
-
env: markEnv(
|
|
25699
|
+
env: markEnv(run2.env, mark),
|
|
25687
25700
|
shell: false,
|
|
25688
25701
|
// Same as a run: a piped stdin makes `claude -p` wait and then warn, which would
|
|
25689
25702
|
// land in the output as if it were the failure — and it is a pipe, and stays open,
|
|
@@ -25705,18 +25718,18 @@ ${String(e)}`) });
|
|
|
25705
25718
|
done2({
|
|
25706
25719
|
ok: false,
|
|
25707
25720
|
timedOut: true,
|
|
25708
|
-
output: said2(events + (
|
|
25721
|
+
output: said2(events + (run2.renderer?.flush() ?? ""), run2.renderer?.result(), stderr + errs.flush())
|
|
25709
25722
|
});
|
|
25710
25723
|
}, TIMEOUT_MS);
|
|
25711
25724
|
if (typeof timer.unref === "function") timer.unref();
|
|
25712
25725
|
stopTimer = () => clearTimeout(timer);
|
|
25713
|
-
const renderer =
|
|
25726
|
+
const renderer = run2.renderer;
|
|
25714
25727
|
if (renderer) {
|
|
25715
25728
|
child.stdout.on("data", (d) => {
|
|
25716
25729
|
events += renderer.push(d.toString());
|
|
25717
25730
|
});
|
|
25718
25731
|
}
|
|
25719
|
-
const errs = createStderrFilter(
|
|
25732
|
+
const errs = createStderrFilter(run2.quietStderr);
|
|
25720
25733
|
child.stderr.on("data", (d) => {
|
|
25721
25734
|
stderr += errs.push(d.toString());
|
|
25722
25735
|
});
|
|
@@ -26053,7 +26066,7 @@ async function testAgent(args) {
|
|
|
26053
26066
|
|
|
26054
26067
|
// ../cli/src/commands/cloud.ts
|
|
26055
26068
|
import fs71 from "node:fs";
|
|
26056
|
-
import
|
|
26069
|
+
import path68 from "node:path";
|
|
26057
26070
|
|
|
26058
26071
|
// ../cli/src/lib/cloud/avatar.ts
|
|
26059
26072
|
init_session();
|
|
@@ -26077,9 +26090,9 @@ async function download(url) {
|
|
|
26077
26090
|
if (!response.ok) return null;
|
|
26078
26091
|
const type = response.headers.get("content-type")?.split(";")[0]?.trim() ?? "";
|
|
26079
26092
|
if (!type.startsWith("image/")) return null;
|
|
26080
|
-
const
|
|
26081
|
-
if (
|
|
26082
|
-
return `data:${type};base64,${
|
|
26093
|
+
const bytes3 = Buffer.from(await response.arrayBuffer());
|
|
26094
|
+
if (bytes3.byteLength === 0 || bytes3.byteLength > MAX_BYTES2) return null;
|
|
26095
|
+
return `data:${type};base64,${bytes3.toString("base64")}`;
|
|
26083
26096
|
} catch {
|
|
26084
26097
|
return null;
|
|
26085
26098
|
}
|
|
@@ -26102,10 +26115,10 @@ init_home();
|
|
|
26102
26115
|
init_config2();
|
|
26103
26116
|
init_session();
|
|
26104
26117
|
import fs70 from "node:fs";
|
|
26105
|
-
import
|
|
26118
|
+
import path67 from "node:path";
|
|
26106
26119
|
var FRESH_MS = 10 * 6e4;
|
|
26107
26120
|
var GRACE_MS = 7 * 24 * 60 * 6e4;
|
|
26108
|
-
var heldFile = () =>
|
|
26121
|
+
var heldFile = () => path67.join(machineHome(), "pro.json");
|
|
26109
26122
|
function forgetPro() {
|
|
26110
26123
|
fs70.rmSync(heldFile(), { force: true });
|
|
26111
26124
|
}
|
|
@@ -26126,18 +26139,18 @@ async function readCloudAccount() {
|
|
|
26126
26139
|
configured: cloudConfigured()
|
|
26127
26140
|
};
|
|
26128
26141
|
if (!blank.configured) return { ...blank, message: NOT_CONFIGURED };
|
|
26129
|
-
const
|
|
26130
|
-
if (!
|
|
26131
|
-
if (
|
|
26132
|
-
if (
|
|
26133
|
-
return { ...blank, ...held3(), state: "signed-in", error:
|
|
26142
|
+
const token3 = await accessToken();
|
|
26143
|
+
if (!token3.ok) {
|
|
26144
|
+
if (token3.reason === "signed-out") return blank;
|
|
26145
|
+
if (token3.reason === "unreachable") {
|
|
26146
|
+
return { ...blank, ...held3(), state: "signed-in", error: token3.error };
|
|
26134
26147
|
}
|
|
26135
26148
|
return { ...blank, ...held3(), state: "expired" };
|
|
26136
26149
|
}
|
|
26137
26150
|
let response;
|
|
26138
26151
|
try {
|
|
26139
26152
|
response = await fetch(`${cloudEndpoints().api}/v1/session`, {
|
|
26140
|
-
headers: { authorization: `Bearer ${
|
|
26153
|
+
headers: { authorization: `Bearer ${token3.token}` }
|
|
26141
26154
|
});
|
|
26142
26155
|
} catch (e) {
|
|
26143
26156
|
return {
|
|
@@ -26280,7 +26293,7 @@ async function cloudImage(opts, program2) {
|
|
|
26280
26293
|
const prompt = opts.prompt.trim();
|
|
26281
26294
|
if (!prompt) usage("Missing the prompt.", line2);
|
|
26282
26295
|
const references = opts.refs.map((file) => {
|
|
26283
|
-
const type = REF_TYPES[
|
|
26296
|
+
const type = REF_TYPES[path68.extname(file).toLowerCase()];
|
|
26284
26297
|
if (!type) usage(`--ref ${file}: use a PNG, JPEG or WebP file.`, line2);
|
|
26285
26298
|
try {
|
|
26286
26299
|
return `data:${type};base64,${fs71.readFileSync(file).toString("base64")}`;
|
|
@@ -26361,13 +26374,13 @@ function refuse(m, fallback) {
|
|
|
26361
26374
|
die([m.zh, m.en, fallback.zh, fallback.en].join("\n"), { kind: "cloud-refused", bare: true });
|
|
26362
26375
|
}
|
|
26363
26376
|
async function generate(g) {
|
|
26364
|
-
const
|
|
26365
|
-
if (!
|
|
26377
|
+
const token3 = await accessToken();
|
|
26378
|
+
if (!token3.ok) refuse(SAID[token3.reason], g.fallback);
|
|
26366
26379
|
let res;
|
|
26367
26380
|
try {
|
|
26368
26381
|
res = await fetch(`${cloudEndpoints().api}${g.endpoint}`, {
|
|
26369
26382
|
method: "POST",
|
|
26370
|
-
headers: { authorization: `Bearer ${
|
|
26383
|
+
headers: { authorization: `Bearer ${token3.token}`, "content-type": "application/json" },
|
|
26371
26384
|
body: JSON.stringify(g.body)
|
|
26372
26385
|
});
|
|
26373
26386
|
} catch {
|
|
@@ -26383,20 +26396,602 @@ async function generate(g) {
|
|
|
26383
26396
|
if (res.status === 400) usage(error?.message ?? "The request was refused.", g.usage);
|
|
26384
26397
|
refuse(g.failed, g.fallback);
|
|
26385
26398
|
}
|
|
26386
|
-
fs71.mkdirSync(
|
|
26399
|
+
fs71.mkdirSync(path68.dirname(path68.resolve(g.out)), { recursive: true });
|
|
26387
26400
|
fs71.writeFileSync(g.out, Buffer.from(await res.arrayBuffer()));
|
|
26388
26401
|
say(`${g.out} (${g.label(res)})`);
|
|
26389
26402
|
return { out: g.out };
|
|
26390
26403
|
}
|
|
26391
26404
|
|
|
26405
|
+
// ../cli/src/commands/cloud-import.ts
|
|
26406
|
+
import crypto3 from "node:crypto";
|
|
26407
|
+
import fs73 from "node:fs";
|
|
26408
|
+
import path70 from "node:path";
|
|
26409
|
+
|
|
26410
|
+
// ../cloud/src/board-files.ts
|
|
26411
|
+
var ROOTS = ["todo/", ".archive/", "memory/", "workflows/", "triage/", "assets/", "rules/"];
|
|
26412
|
+
var SETTINGS = ["modules.md", "config.md"];
|
|
26413
|
+
var isSettingsPath = (path83) => SETTINGS.includes(path83) || /^rules\/(?:workflows\/)?[^/]+\.md$/.test(path83);
|
|
26414
|
+
var BOARD_FILE_LIMIT = 256 * 1024;
|
|
26415
|
+
function isBoardPath(path83) {
|
|
26416
|
+
if (!path83 || path83.length > 500 || path83.startsWith("/") || path83.includes("\\") || path83.includes("\0")) return false;
|
|
26417
|
+
if (path83.split("/").some((part) => !part || part === "." || part === "..")) return false;
|
|
26418
|
+
if (path83 === "todo/README.md" || path83.split("/").includes(".sync")) return false;
|
|
26419
|
+
if (path83.startsWith("workflows/")) return true;
|
|
26420
|
+
if (SETTINGS.includes(path83)) return true;
|
|
26421
|
+
if (path83.startsWith("rules/")) return isSettingsPath(path83);
|
|
26422
|
+
if (path83.startsWith("assets/")) return path83.endsWith(".json");
|
|
26423
|
+
return ROOTS.some((root) => path83.startsWith(root)) && path83.endsWith(".md");
|
|
26424
|
+
}
|
|
26425
|
+
var workflowFolder = (id) => id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
26426
|
+
|
|
26427
|
+
// ../cloud/src/v1/workflow-files.ts
|
|
26428
|
+
init_workflow_def();
|
|
26429
|
+
init_parse();
|
|
26430
|
+
var WORKFLOW_FILE2 = "WORKFLOW.md";
|
|
26431
|
+
var MCP_FILE = "mcp.json";
|
|
26432
|
+
var KB = 1024;
|
|
26433
|
+
var FILE_LIMITS = { file: 256 * KB, total: 2 * KB * KB, count: 500, path: 512 };
|
|
26434
|
+
var LAND_ROLES2 = ["builder"];
|
|
26435
|
+
var SEGMENT2 = /^[\w.-]+$/;
|
|
26436
|
+
var bytes = (text5) => new TextEncoder().encode(text5).length;
|
|
26437
|
+
function folderPath(file, top) {
|
|
26438
|
+
const parts = file.split("/");
|
|
26439
|
+
if (parts.length < 3 || parts[0] !== top || !AGENT_NAME.test(parts[1])) return null;
|
|
26440
|
+
if (parts.slice(2).some((p) => !SEGMENT2.test(p) || p === "." || p === "..")) return null;
|
|
26441
|
+
return [parts[1], parts.slice(2).join("/")];
|
|
26442
|
+
}
|
|
26443
|
+
function mcpProblem(text5) {
|
|
26444
|
+
let read6;
|
|
26445
|
+
try {
|
|
26446
|
+
read6 = JSON.parse(text5);
|
|
26447
|
+
} catch {
|
|
26448
|
+
return "is not JSON";
|
|
26449
|
+
}
|
|
26450
|
+
const servers = read6?.mcpServers;
|
|
26451
|
+
if (!servers || typeof servers !== "object" || Array.isArray(servers)) return "must hold `mcpServers`, an object of name \u2192 server";
|
|
26452
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
26453
|
+
const { command, args, ...rest } = server ?? {};
|
|
26454
|
+
if (!AGENT_NAME.test(name)) return `names a server \`${name}\`; use lowercase letters, digits and dashes`;
|
|
26455
|
+
if (typeof command !== "string" || !command) return `server \`${name}\` needs a \`command\``;
|
|
26456
|
+
if (args !== void 0 && !(Array.isArray(args) && args.every((a) => typeof a === "string"))) return `server \`${name}\`: \`args\` must be a list of strings`;
|
|
26457
|
+
if (Object.keys(rest).length) return `server \`${name}\` takes only \`command\` and \`args\``;
|
|
26458
|
+
}
|
|
26459
|
+
return null;
|
|
26460
|
+
}
|
|
26461
|
+
function checkWorkflowFiles(value, official = false) {
|
|
26462
|
+
const fail = (file, reason, tooLarge = false) => ({ ok: false, file, reason, ...tooLarge ? { tooLarge } : {} });
|
|
26463
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return fail("files", "must be an object of path \u2192 text");
|
|
26464
|
+
const entries = Object.entries(value);
|
|
26465
|
+
if (entries.length > FILE_LIMITS.count) return fail("files", `holds at most ${FILE_LIMITS.count} files`, true);
|
|
26466
|
+
let total = 0;
|
|
26467
|
+
const folders2 = /* @__PURE__ */ new Map();
|
|
26468
|
+
const skills = /* @__PURE__ */ new Set();
|
|
26469
|
+
for (const [file, text5] of entries) {
|
|
26470
|
+
if (typeof text5 !== "string" || text5.includes("\0")) return fail(file, "is not a text file");
|
|
26471
|
+
if (file.length > FILE_LIMITS.path) return fail(file, `the path is longer than ${FILE_LIMITS.path} characters`);
|
|
26472
|
+
const size = bytes(text5);
|
|
26473
|
+
if (size > FILE_LIMITS.file) return fail(file, `is larger than ${FILE_LIMITS.file / KB} KB`, true);
|
|
26474
|
+
total += size;
|
|
26475
|
+
if (total > FILE_LIMITS.total) return fail("files", `are larger than ${FILE_LIMITS.total / KB / KB} MB together`, true);
|
|
26476
|
+
if (file === WORKFLOW_FILE2) continue;
|
|
26477
|
+
if (official && file === MCP_FILE) {
|
|
26478
|
+
const problem = mcpProblem(text5);
|
|
26479
|
+
if (problem) return fail(file, problem);
|
|
26480
|
+
continue;
|
|
26481
|
+
}
|
|
26482
|
+
if (official && file.startsWith("skills/")) {
|
|
26483
|
+
if (!folderPath(file, "skills")) return fail(file, "is not inside skills/<name>/");
|
|
26484
|
+
skills.add(file.split("/")[1]);
|
|
26485
|
+
continue;
|
|
26486
|
+
}
|
|
26487
|
+
const at = folderPath(file, "agents");
|
|
26488
|
+
if (!at) return fail(file, `is neither ${WORKFLOW_FILE2} nor inside agents/<name>/`);
|
|
26489
|
+
if (!folders2.has(at[0])) folders2.set(at[0], /* @__PURE__ */ new Map());
|
|
26490
|
+
folders2.get(at[0]).set(at[1], text5);
|
|
26491
|
+
}
|
|
26492
|
+
for (const name of skills) {
|
|
26493
|
+
if (value[`skills/${name}/SKILL.md`] === void 0) return fail(`skills/${name}/SKILL.md`, "is missing");
|
|
26494
|
+
}
|
|
26495
|
+
const source = value[WORKFLOW_FILE2];
|
|
26496
|
+
if (source === void 0) return fail(WORKFLOW_FILE2, "is missing");
|
|
26497
|
+
const read6 = parseWorkflowFile("workflow", source, WORKFLOW_FILE2);
|
|
26498
|
+
if ("problem" in read6) return fail(WORKFLOW_FILE2, read6.problem.replace(`${WORKFLOW_FILE2}: `, ""));
|
|
26499
|
+
const def = read6.def;
|
|
26500
|
+
const agents = /* @__PURE__ */ new Map();
|
|
26501
|
+
for (const [name, own] of folders2) {
|
|
26502
|
+
const from = `agents/${name}/AGENT.md`;
|
|
26503
|
+
const text5 = own.get("AGENT.md");
|
|
26504
|
+
if (text5 === void 0) return fail(from, "is missing");
|
|
26505
|
+
const parsed = parseSpecAgent(text5, from, (p) => own.get(p) ?? null, false, () => [...own.keys()].filter((p) => p !== "AGENT.md"));
|
|
26506
|
+
if ("problem" in parsed) return fail(from, parsed.problem.replace(`${from}: `, ""));
|
|
26507
|
+
if (parsed.agent.name !== name) return fail(from, `calls itself \`${parsed.agent.name}\`; it must match its folder`);
|
|
26508
|
+
agents.set(name, parsed.agent);
|
|
26509
|
+
}
|
|
26510
|
+
const named2 = [
|
|
26511
|
+
...def.previews.map((h) => [h.agent, "it previews with"]),
|
|
26512
|
+
...def.land.filter((h) => !LAND_ROLES2.includes(h.agent)).map((h) => [h.agent, "it lands with"]),
|
|
26513
|
+
...def.scheduled.map((s) => [s.agent, "it schedules"])
|
|
26514
|
+
];
|
|
26515
|
+
for (const [name, why] of named2) {
|
|
26516
|
+
if (!agents.has(name)) return fail(WORKFLOW_FILE2, `${why} \`${name}\`, which has no agents/${name}/AGENT.md`);
|
|
26517
|
+
}
|
|
26518
|
+
return { ok: true, def, files: value };
|
|
26519
|
+
}
|
|
26520
|
+
|
|
26521
|
+
// ../cli/src/commands/cloud-import.ts
|
|
26522
|
+
init_bundled();
|
|
26523
|
+
|
|
26524
|
+
// ../cli/src/lib/agents/shared-memory.ts
|
|
26525
|
+
init_bundled();
|
|
26526
|
+
init_parse();
|
|
26527
|
+
import fs72 from "node:fs";
|
|
26528
|
+
import path69 from "node:path";
|
|
26529
|
+
function sharedMemory(board2) {
|
|
26530
|
+
const declared = /* @__PURE__ */ new Map();
|
|
26531
|
+
const take = (folder, text5, builtIn) => {
|
|
26532
|
+
if (text5 === null || declared.has(folder)) return;
|
|
26533
|
+
const read6 = parseSpecAgent(text5, folder, () => null, builtIn);
|
|
26534
|
+
if ("agent" in read6 && read6.agent.name === folder) declared.set(folder, read6.agent.memoryFiles.workspace);
|
|
26535
|
+
};
|
|
26536
|
+
for (const [key, text5] of Object.entries(BUNDLED_AGENT_FILES)) {
|
|
26537
|
+
const at = /^([^/]+)\/AGENT\.md$/.exec(key);
|
|
26538
|
+
if (at) take(at[1], text5, true);
|
|
26539
|
+
}
|
|
26540
|
+
const root = path69.join(board2, "agents");
|
|
26541
|
+
let folders2 = [];
|
|
26542
|
+
try {
|
|
26543
|
+
folders2 = fs72.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
|
|
26544
|
+
} catch {
|
|
26545
|
+
}
|
|
26546
|
+
for (const folder of folders2.sort()) {
|
|
26547
|
+
let text5 = null;
|
|
26548
|
+
try {
|
|
26549
|
+
text5 = fs72.readFileSync(path69.join(root, folder, "AGENT.md"), "utf8");
|
|
26550
|
+
} catch {
|
|
26551
|
+
}
|
|
26552
|
+
take(folder, text5, false);
|
|
26553
|
+
}
|
|
26554
|
+
const matches = [...declared].flatMap(([agent, paths]) => paths.map((p) => matcher(`agents/${agent}/${p}`)));
|
|
26555
|
+
return (name) => matches.some((m) => m(name));
|
|
26556
|
+
}
|
|
26557
|
+
function matcher(entry) {
|
|
26558
|
+
if (entry.endsWith("/")) return (name) => name.startsWith(entry);
|
|
26559
|
+
const re = new RegExp(`^${entry.split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join("[^/]*")}$`);
|
|
26560
|
+
return (name) => re.test(name);
|
|
26561
|
+
}
|
|
26562
|
+
|
|
26563
|
+
// ../cli/src/commands/cloud-import.ts
|
|
26564
|
+
init_workflows();
|
|
26565
|
+
init_cards();
|
|
26566
|
+
init_config2();
|
|
26567
|
+
init_tasks();
|
|
26568
|
+
init_session();
|
|
26569
|
+
init_frontmatter();
|
|
26570
|
+
init_io();
|
|
26571
|
+
init_paths();
|
|
26572
|
+
init_questions();
|
|
26573
|
+
var BATCH = 50;
|
|
26574
|
+
var TITLE_LIMIT = 200;
|
|
26575
|
+
var APP_URL = "https://app.wildcrane.ai";
|
|
26576
|
+
var BOUNDARY = "<!-- agent -->";
|
|
26577
|
+
var Refused = class extends Error {
|
|
26578
|
+
constructor(message, code) {
|
|
26579
|
+
super(message);
|
|
26580
|
+
this.code = code;
|
|
26581
|
+
}
|
|
26582
|
+
code;
|
|
26583
|
+
};
|
|
26584
|
+
var token2 = "";
|
|
26585
|
+
async function api(method, route2, body, raw) {
|
|
26586
|
+
let res;
|
|
26587
|
+
try {
|
|
26588
|
+
res = await fetch(`${cloudEndpoints().api}${route2}`, {
|
|
26589
|
+
method,
|
|
26590
|
+
headers: {
|
|
26591
|
+
authorization: `Bearer ${token2}`,
|
|
26592
|
+
...raw ? { "content-type": raw.type } : body !== void 0 ? { "content-type": "application/json" } : {}
|
|
26593
|
+
},
|
|
26594
|
+
...raw ? { body: raw.bytes } : body !== void 0 ? { body: JSON.stringify(body) } : {},
|
|
26595
|
+
signal: AbortSignal.timeout(12e4)
|
|
26596
|
+
});
|
|
26597
|
+
} catch (e) {
|
|
26598
|
+
throw new Refused(`Cloud could not be reached: ${e instanceof Error ? e.message : String(e)}`);
|
|
26599
|
+
}
|
|
26600
|
+
const parsed = await res.json().catch(() => ({}));
|
|
26601
|
+
if (!res.ok) throw new Refused(parsed.error?.message ?? `Cloud answered ${res.status}.`, parsed.error?.code);
|
|
26602
|
+
return parsed;
|
|
26603
|
+
}
|
|
26604
|
+
async function all(route2) {
|
|
26605
|
+
const out = [];
|
|
26606
|
+
let after2 = null;
|
|
26607
|
+
do {
|
|
26608
|
+
const sep = route2.includes("?") ? "&" : "?";
|
|
26609
|
+
const page = await api("GET", `${route2}${sep}limit=100${after2 ? `&after=${encodeURIComponent(after2)}` : ""}`);
|
|
26610
|
+
out.push(...page.data);
|
|
26611
|
+
after2 = page.next;
|
|
26612
|
+
} while (after2);
|
|
26613
|
+
return out;
|
|
26614
|
+
}
|
|
26615
|
+
async function findProject(asked) {
|
|
26616
|
+
if (/^prj_/.test(asked)) return api("GET", `/v1/projects/${encodeURIComponent(asked)}`);
|
|
26617
|
+
const workspaces = await all("/v1/workspaces");
|
|
26618
|
+
for (const ws of workspaces) {
|
|
26619
|
+
const found2 = (await all(`/v1/projects?workspace=${encodeURIComponent(ws.id)}`)).find((p) => p.prefix.toLowerCase() === asked.toLowerCase());
|
|
26620
|
+
if (found2) return found2;
|
|
26621
|
+
}
|
|
26622
|
+
throw new Refused(`No project of yours has the id or prefix "${asked}".`, "not_found");
|
|
26623
|
+
}
|
|
26624
|
+
var read = (file) => fs73.readFileSync(file, "utf8");
|
|
26625
|
+
var bytes2 = (text5) => Buffer.byteLength(text5, "utf8");
|
|
26626
|
+
function files(dir) {
|
|
26627
|
+
if (!fs73.existsSync(dir)) return [];
|
|
26628
|
+
const out = [];
|
|
26629
|
+
for (const entry of fs73.readdirSync(dir, { withFileTypes: true })) {
|
|
26630
|
+
if (entry.name.startsWith(".")) continue;
|
|
26631
|
+
const full = path70.join(dir, entry.name);
|
|
26632
|
+
if (entry.isDirectory()) out.push(...files(full));
|
|
26633
|
+
else if (entry.isFile()) out.push(full);
|
|
26634
|
+
}
|
|
26635
|
+
return out;
|
|
26636
|
+
}
|
|
26637
|
+
var posix2 = (p) => p.split(path70.sep).join("/");
|
|
26638
|
+
function openCards() {
|
|
26639
|
+
const out = [];
|
|
26640
|
+
for (const file of walkMd(TODO)) {
|
|
26641
|
+
const rel2 = posix2(path70.relative(TODO, file));
|
|
26642
|
+
if (rel2 === "README.md") continue;
|
|
26643
|
+
const parts = rel2.split("/");
|
|
26644
|
+
const text5 = read(file);
|
|
26645
|
+
const meta = parseFrontmatter(text5).meta;
|
|
26646
|
+
if (!meta) continue;
|
|
26647
|
+
const name = parts[parts.length - 1];
|
|
26648
|
+
const root = name === "root.md" && parts.length === 2;
|
|
26649
|
+
const id = root ? idPrefix(parts[0]) : idPrefix(name);
|
|
26650
|
+
if (id === null) continue;
|
|
26651
|
+
const group = !root && parts.length === 2 ? idPrefix(parts[0]) : null;
|
|
26652
|
+
const slug2 = (root ? parts[0] : name.replace(/\.md$/, "")).replace(/^\d+-?/, "");
|
|
26653
|
+
out.push({ id, rel: rel2, text: text5, meta, group, root, slug: slug2 });
|
|
26654
|
+
}
|
|
26655
|
+
return out.sort((a, b) => a.id - b.id);
|
|
26656
|
+
}
|
|
26657
|
+
function bodyProblems(text5) {
|
|
26658
|
+
const out = [];
|
|
26659
|
+
if (bytes2(text5) > BOARD_FILE_LIMIT) out.push({ zh: "\u8D85\u8FC7 256 KB", en: "over 256 KB" });
|
|
26660
|
+
let fence = null;
|
|
26661
|
+
let boundaries = 0;
|
|
26662
|
+
for (const line2 of text5.split("\n")) {
|
|
26663
|
+
const mark = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line2);
|
|
26664
|
+
const run2 = mark?.[1] ?? "";
|
|
26665
|
+
if (fence) {
|
|
26666
|
+
if (run2[0] === fence.char && run2.length >= fence.size && !mark?.[2]?.trim()) fence = null;
|
|
26667
|
+
} else if (run2) fence = { char: run2[0], size: run2.length };
|
|
26668
|
+
else if (line2.trim() === BOUNDARY) boundaries++;
|
|
26669
|
+
}
|
|
26670
|
+
if (fence) out.push({ zh: "\u4EE3\u7801\u5757\u672A\u95ED\u5408", en: "a code fence is never closed" });
|
|
26671
|
+
if (boundaries > 1) out.push({ zh: `\`${BOUNDARY}\` \u51FA\u73B0\u591A\u4E8E\u4E00\u6B21`, en: `holds \`${BOUNDARY}\` more than once` });
|
|
26672
|
+
return out;
|
|
26673
|
+
}
|
|
26674
|
+
function packWorkflow(flow) {
|
|
26675
|
+
const own = path70.join(WORKFLOWS, flow.id);
|
|
26676
|
+
const out = {};
|
|
26677
|
+
for (const file of files(own)) out[posix2(path70.relative(own, file))] = read(file);
|
|
26678
|
+
const named2 = [...flow.previews, ...flow.land, ...flow.scheduled].map((h) => h.agent).filter((a) => !LAND_ROLES.includes(a));
|
|
26679
|
+
for (const agent of new Set(named2)) {
|
|
26680
|
+
const dir = path70.join(AGENTS, agent);
|
|
26681
|
+
if (fs73.existsSync(path70.join(dir, "AGENT.md"))) {
|
|
26682
|
+
for (const file of files(dir)) out[`agents/${agent}/${posix2(path70.relative(dir, file))}`] = read(file);
|
|
26683
|
+
continue;
|
|
26684
|
+
}
|
|
26685
|
+
for (const [key, text5] of Object.entries(BUNDLED_AGENT_FILES)) {
|
|
26686
|
+
if (key.startsWith(`${agent}/`)) out[`agents/${key}`] = text5;
|
|
26687
|
+
}
|
|
26688
|
+
}
|
|
26689
|
+
return out;
|
|
26690
|
+
}
|
|
26691
|
+
var builtinId = (id) => `wf_builtin_${id.replace(/-/g, "_")}`;
|
|
26692
|
+
function rewriteRefs(text5, numbers) {
|
|
26693
|
+
const swap = (s) => s.replace(/(^|[^\w#&/])#(\d+)\b/g, (_, lead, n) => {
|
|
26694
|
+
const to = numbers.get(Number(n));
|
|
26695
|
+
return to === void 0 ? `${lead}old#${n}` : `${lead}#${to}`;
|
|
26696
|
+
});
|
|
26697
|
+
let fence = null;
|
|
26698
|
+
return text5.split("\n").map((line2) => {
|
|
26699
|
+
const mark = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line2);
|
|
26700
|
+
const run2 = mark?.[1] ?? "";
|
|
26701
|
+
if (fence) {
|
|
26702
|
+
if (run2[0] === fence.char && run2.length >= fence.size && !mark?.[2]?.trim()) fence = null;
|
|
26703
|
+
return line2;
|
|
26704
|
+
}
|
|
26705
|
+
if (run2) {
|
|
26706
|
+
fence = { char: run2[0], size: run2.length };
|
|
26707
|
+
return line2;
|
|
26708
|
+
}
|
|
26709
|
+
return line2.split(/(`+[^`]*`+)/).map((part, i) => i % 2 ? part : swap(part)).join("");
|
|
26710
|
+
}).join("\n");
|
|
26711
|
+
}
|
|
26712
|
+
function cardForCloud(text5, numbers, workflow) {
|
|
26713
|
+
const end2 = /^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/.exec(text5)?.[0].length ?? 0;
|
|
26714
|
+
const front = end2 ? text5.slice(0, end2) : "";
|
|
26715
|
+
const lines = rewriteRefs(front, numbers).split("\n").map((line2) => {
|
|
26716
|
+
const list = /^(blocked_by|related):\s*\[(.*)\]\s*$/.exec(line2);
|
|
26717
|
+
if (list) {
|
|
26718
|
+
const kept = list[2].split(",").map((s) => numbers.get(Number(s.trim()))).filter((n) => n !== void 0);
|
|
26719
|
+
return `${list[1]}: [${kept.join(", ")}]`;
|
|
26720
|
+
}
|
|
26721
|
+
if (/^status:\s*implementing\s*$/.test(line2)) return "status: ready";
|
|
26722
|
+
if (workflow && /^workflow:/.test(line2)) return `workflow: ${workflow}`;
|
|
26723
|
+
return line2;
|
|
26724
|
+
});
|
|
26725
|
+
return lines.join("\n") + rewriteRefs(text5.slice(end2), numbers);
|
|
26726
|
+
}
|
|
26727
|
+
function reviewOf2(meta) {
|
|
26728
|
+
const questions = meta.questions ?? [];
|
|
26729
|
+
let open3 = false;
|
|
26730
|
+
for (const [i, q] of questions.entries()) {
|
|
26731
|
+
const { tag, text: text5 } = parseQuestion(q.text);
|
|
26732
|
+
if (tag !== "user" || !openOf([q]).length) continue;
|
|
26733
|
+
open3 = true;
|
|
26734
|
+
if (q.options?.length) return { question: text5, options: q.options.slice(0, 8), position: i + 1 };
|
|
26735
|
+
}
|
|
26736
|
+
return open3 || meta.status === "ready" || meta.status === "implementing" ? true : null;
|
|
26737
|
+
}
|
|
26738
|
+
function draftsOf(text5) {
|
|
26739
|
+
const out = /* @__PURE__ */ new Map();
|
|
26740
|
+
for (const m of text5.matchAll(/\]\(\s*<?([^)\s>]+)>?(?:\s+"[^"]*")?\s*\)|\bsrc=["']([^"']+)["']/g)) {
|
|
26741
|
+
const name = m[1] ?? m[2];
|
|
26742
|
+
const at = /^\.(assets|mockups)\/(.+)$/.exec(name);
|
|
26743
|
+
if (!at) continue;
|
|
26744
|
+
const base = at[1] === "assets" ? ASSETS : MOCKUPS;
|
|
26745
|
+
const file = path70.resolve(base, at[2]);
|
|
26746
|
+
if (file.startsWith(base + path70.sep) && fs73.statSync(file, { throwIfNoEntry: false })?.isFile()) out.set(name, file);
|
|
26747
|
+
}
|
|
26748
|
+
return [...out].map(([name, file]) => ({ name, file }));
|
|
26749
|
+
}
|
|
26750
|
+
var TYPES = {
|
|
26751
|
+
".png": "image/png",
|
|
26752
|
+
".jpg": "image/jpeg",
|
|
26753
|
+
".jpeg": "image/jpeg",
|
|
26754
|
+
".gif": "image/gif",
|
|
26755
|
+
".webp": "image/webp",
|
|
26756
|
+
".svg": "image/svg+xml",
|
|
26757
|
+
".mp4": "video/mp4",
|
|
26758
|
+
".webm": "video/webm",
|
|
26759
|
+
".mp3": "audio/mpeg",
|
|
26760
|
+
".pdf": "application/pdf",
|
|
26761
|
+
".html": "text/html",
|
|
26762
|
+
".md": "text/markdown",
|
|
26763
|
+
".mov": "video/quicktime",
|
|
26764
|
+
".wav": "audio/wav",
|
|
26765
|
+
".json": "application/json"
|
|
26766
|
+
};
|
|
26767
|
+
function kindOf(ext) {
|
|
26768
|
+
if (ext === ".html") return "html";
|
|
26769
|
+
if (ext === ".md" || ext === ".mdx") return "markdown";
|
|
26770
|
+
const type = TYPES[ext] ?? "";
|
|
26771
|
+
if (type.startsWith("image/")) return "image";
|
|
26772
|
+
return type.startsWith("video/") || type.startsWith("audio/") ? "video" : "asset";
|
|
26773
|
+
}
|
|
26774
|
+
async function uploadDrafts(task, drafts) {
|
|
26775
|
+
const card = await api("GET", `/v1/tasks/${task}/card`);
|
|
26776
|
+
if (Object.keys(card.assets ?? {}).length) return 0;
|
|
26777
|
+
const assets = {};
|
|
26778
|
+
for (const { name, file } of drafts) {
|
|
26779
|
+
if (!card.body.includes(name)) continue;
|
|
26780
|
+
const ext = path70.extname(file).toLowerCase();
|
|
26781
|
+
const type = TYPES[ext] ?? "application/octet-stream";
|
|
26782
|
+
const data = fs73.readFileSync(file);
|
|
26783
|
+
const artifact = await api("POST", `/v1/tasks/${task}/artifacts`, { type: kindOf(ext), name: name.slice(0, 200) });
|
|
26784
|
+
const upload = await api("POST", `/v1/artifacts/${artifact.id}/uploads`, { content_type: type, size: Math.max(data.length, 1) });
|
|
26785
|
+
await api("PUT", `/v1/uploads/${upload.id}`, void 0, { bytes: data, type });
|
|
26786
|
+
const version = await api("POST", `/v1/artifacts/${artifact.id}/versions`, { upload: upload.id });
|
|
26787
|
+
assets[name] = { artifact: artifact.id, version: version.version };
|
|
26788
|
+
}
|
|
26789
|
+
if (!Object.keys(assets).length) return 0;
|
|
26790
|
+
await api("PUT", `/v1/tasks/${task}/card`, { body: card.body, base_version: card.version, assets });
|
|
26791
|
+
return Object.keys(assets).length;
|
|
26792
|
+
}
|
|
26793
|
+
function stop(lines) {
|
|
26794
|
+
die(lines.join("\n"), { kind: "cloud-refused", bare: true });
|
|
26795
|
+
}
|
|
26796
|
+
async function cloudImport(opts, program2) {
|
|
26797
|
+
if (!opts.project) {
|
|
26798
|
+
die(`Missing --project.
|
|
26799
|
+
usage: ${program2} cloud import --project <id or prefix> [--dry-run]`, { kind: "bad-args", bare: true, exitCode: 2 });
|
|
26800
|
+
}
|
|
26801
|
+
const held4 = await accessToken();
|
|
26802
|
+
if (!held4.ok) {
|
|
26803
|
+
stop(held4.reason === "unreachable" ? ["\u65E0\u6CD5\u8FDE\u63A5 WildCrane Cloud\u3002\u8BF7\u68C0\u67E5\u7F51\u7EDC\u540E\u91CD\u8BD5\u3002", "Could not reach WildCrane Cloud. Check your connection and try again."] : [
|
|
26804
|
+
"\u672A\u767B\u5F55 WildCrane Cloud\u3002\u8BF7\u5728 WildCrane \u5E94\u7528\u7684\u201C\u914D\u7F6E \u2192 Cloud\u201D\u4E2D\u767B\u5F55\u540E\u91CD\u8BD5\u3002",
|
|
26805
|
+
"Not signed in to WildCrane Cloud. Sign in from Configuration \u2192 Cloud in the WildCrane app, then try again."
|
|
26806
|
+
]);
|
|
26807
|
+
}
|
|
26808
|
+
token2 = held4.token;
|
|
26809
|
+
try {
|
|
26810
|
+
return await run(opts.project, !!opts.dryRun);
|
|
26811
|
+
} catch (e) {
|
|
26812
|
+
if (!(e instanceof Refused)) throw e;
|
|
26813
|
+
stop([
|
|
26814
|
+
"\u5BFC\u5165\u5DF2\u505C\u6B62\uFF0CCloud \u62D2\u7EDD\u4E86\u8BF7\u6C42\uFF1A",
|
|
26815
|
+
"The import stopped; Cloud refused it:",
|
|
26816
|
+
` ${e.message}`,
|
|
26817
|
+
"\u4FEE\u6B63\u540E\u91CD\u65B0\u8FD0\u884C\uFF0C\u5DF2\u5BFC\u5165\u7684\u5185\u5BB9\u4E0D\u4F1A\u91CD\u590D\u3002",
|
|
26818
|
+
"Fix it and run again; what was imported is not imported twice."
|
|
26819
|
+
]);
|
|
26820
|
+
}
|
|
26821
|
+
}
|
|
26822
|
+
async function run(asked, dryRun) {
|
|
26823
|
+
const project = await findProject(asked);
|
|
26824
|
+
const cloudFlows = await all(`/v1/workflows?workspace=${encodeURIComponent(project.workspace)}`);
|
|
26825
|
+
const board2 = crypto3.createHash("sha256").update(fs73.realpathSync(KANBAN)).digest("hex").slice(0, 16);
|
|
26826
|
+
const cards = openCards();
|
|
26827
|
+
const problems = [];
|
|
26828
|
+
const where = (file) => posix2(path70.relative(KANBAN, file));
|
|
26829
|
+
const own = workflows().filter((w) => !w.builtIn);
|
|
26830
|
+
const packed = /* @__PURE__ */ new Map();
|
|
26831
|
+
for (const flow of own) {
|
|
26832
|
+
const files2 = packWorkflow(flow);
|
|
26833
|
+
const checked = checkWorkflowFiles(files2);
|
|
26834
|
+
if (!checked.ok) problems.push({ file: `workflows/${flow.id}`, reason: { zh: `${checked.file} ${checked.reason}`, en: `${checked.file} ${checked.reason}` } });
|
|
26835
|
+
else packed.set(flow.id, files2);
|
|
26836
|
+
}
|
|
26837
|
+
for (const card of cards) {
|
|
26838
|
+
const file = `todo/${card.rel}`;
|
|
26839
|
+
for (const reason of bodyProblems(card.text)) problems.push({ file, reason });
|
|
26840
|
+
if (!card.meta.title.trim()) problems.push({ file, reason: { zh: "\u6CA1\u6709\u6807\u9898", en: "has no title" } });
|
|
26841
|
+
if (bytes2(card.meta.title.trim()) > TITLE_LIMIT) problems.push({ file, reason: { zh: `\u6807\u9898\u8D85\u8FC7 ${TITLE_LIMIT} \u5B57\u8282`, en: `the title is over ${TITLE_LIMIT} bytes` } });
|
|
26842
|
+
const wf = card.meta.workflow || "coding";
|
|
26843
|
+
if (isBuiltinWorkflow(wf)) {
|
|
26844
|
+
if (!cloudFlows.some((w) => w.id === builtinId(wf))) {
|
|
26845
|
+
problems.push({ file, reason: { zh: `workflow \`${wf}\` \u672A\u5728\u8BE5 workspace \u5B89\u88C5\uFF0C\u8BF7\u5148\u5728 Cloud \u7684 Workflows \u9875\u5B89\u88C5`, en: `workflow \`${wf}\` is not installed in this workspace; install it from Workflows in Cloud first` } });
|
|
26846
|
+
}
|
|
26847
|
+
} else if (!workflowById(wf)) {
|
|
26848
|
+
problems.push({ file, reason: { zh: `\u672A\u77E5\u7684 workflow \`${wf}\``, en: `unknown workflow \`${wf}\`` } });
|
|
26849
|
+
}
|
|
26850
|
+
}
|
|
26851
|
+
const shared = sharedMemory(KANBAN);
|
|
26852
|
+
const root = `projects/${project.id}/`;
|
|
26853
|
+
const boardFiles = [];
|
|
26854
|
+
for (const file of [...files(MEMORY), ...files(RULES), CONFIG, MODULES_MD]) {
|
|
26855
|
+
if (!fs73.existsSync(file)) continue;
|
|
26856
|
+
const rel2 = where(file);
|
|
26857
|
+
if (!isBoardPath(rel2)) continue;
|
|
26858
|
+
const content = read(file);
|
|
26859
|
+
if (bytes2(content) > BOARD_FILE_LIMIT) {
|
|
26860
|
+
problems.push({ file: rel2, reason: { zh: "\u8D85\u8FC7 256 KB", en: "over 256 KB" } });
|
|
26861
|
+
continue;
|
|
26862
|
+
}
|
|
26863
|
+
const isShared = rel2.startsWith("memory/") && shared(rel2.slice("memory/".length));
|
|
26864
|
+
boardFiles.push({ local: rel2, path: isShared ? rel2 : root + rel2, content });
|
|
26865
|
+
}
|
|
26866
|
+
const listed2 = await all(
|
|
26867
|
+
`/v1/tasks?project=${project.id}&workspace=${encodeURIComponent(project.workspace)}`
|
|
26868
|
+
);
|
|
26869
|
+
if (listed2.some((t) => t.source_context?.import?.board !== board2)) {
|
|
26870
|
+
problems.push({ file: `${project.name} (${project.prefix})`, reason: { zh: "\u8BE5\u9879\u76EE\u5DF2\u6709\u81EA\u5DF1\u7684\u4EFB\u52A1\uFF0C\u8BF7\u5BFC\u5165\u4E00\u4E2A\u7A7A\u9879\u76EE", en: "this project already has tasks of its own; import into an empty project" } });
|
|
26871
|
+
}
|
|
26872
|
+
const left = leftLocal(own);
|
|
26873
|
+
if (problems.length) {
|
|
26874
|
+
stop([
|
|
26875
|
+
"\u5BFC\u5165\u524D\u68C0\u67E5\u53D1\u73B0\u95EE\u9898\uFF0C\u4EC0\u4E48\u90FD\u6CA1\u6709\u5199\u5165\uFF1A",
|
|
26876
|
+
"The check before importing found problems; nothing was written:",
|
|
26877
|
+
...problems.map((p) => ` ${p.file}: ${p.reason.zh} / ${p.reason.en}`)
|
|
26878
|
+
]);
|
|
26879
|
+
}
|
|
26880
|
+
const reviews = cards.filter((c) => reviewOf2(c.meta));
|
|
26881
|
+
if (dryRun) {
|
|
26882
|
+
const wouldShare = boardFiles.filter((f) => !f.path.startsWith(root)).length;
|
|
26883
|
+
say(`\u9884\u68C0\u901A\u8FC7 \xB7 Check passed \u2014 ${project.name} (${project.prefix})`);
|
|
26884
|
+
say(` \u5361\u7247 Cards: ${cards.length}\uFF08\u5F85\u5BA1 on review: ${reviews.length}\uFF09`);
|
|
26885
|
+
say(` \u81EA\u5EFA workflow Own workflows: ${own.length}`);
|
|
26886
|
+
say(` \u9879\u76EE\u6587\u4EF6 Project files: ${boardFiles.length - wouldShare} \xB7 workspace \u8BB0\u5FC6 Workspace memory: ${wouldShare}`);
|
|
26887
|
+
cards.forEach((c, i) => say(` #${c.id} \u2192 ${project.prefix}-${i + 1} ${c.meta.title}${reviewOf2(c.meta) ? " [\u5F85\u5BA1 review]" : ""}`));
|
|
26888
|
+
sayLeft(left);
|
|
26889
|
+
return { dryRun: true, cards: cards.length, reviews: reviews.length, workflows: own.length, files: boardFiles.length, left };
|
|
26890
|
+
}
|
|
26891
|
+
const cloudOf = /* @__PURE__ */ new Map();
|
|
26892
|
+
for (const flow of own) {
|
|
26893
|
+
const found2 = cloudFlows.find((w) => !w.builtin && w.name === flow.name);
|
|
26894
|
+
const id = found2?.id ?? (await api("POST", "/v1/workflows", { files: packed.get(flow.id), workspace: project.workspace })).id;
|
|
26895
|
+
cloudOf.set(flow.id, id);
|
|
26896
|
+
}
|
|
26897
|
+
const { next } = await api("POST", `/v1/projects/${project.id}/import`, { board: board2, cards: [] });
|
|
26898
|
+
const numbers = /* @__PURE__ */ new Map();
|
|
26899
|
+
for (const t of listed2) {
|
|
26900
|
+
const at = t.source_context?.import;
|
|
26901
|
+
if (at?.board === board2 && typeof at.number === "number") numbers.set(at.number, numberOf(t.key));
|
|
26902
|
+
}
|
|
26903
|
+
let n = next;
|
|
26904
|
+
for (const c of cards) if (!numbers.has(c.id)) numbers.set(c.id, n++);
|
|
26905
|
+
const ruleFile2 = (rel2) => {
|
|
26906
|
+
const at = /^rules\/workflows\/([^/]+)\.md$/.exec(rel2);
|
|
26907
|
+
const id = at && cloudOf.get(at[1]);
|
|
26908
|
+
return id ? `rules/workflows/${workflowFolder(id)}.md` : rel2;
|
|
26909
|
+
};
|
|
26910
|
+
const sent = boardFiles.map((f) => ({ path: f.path.startsWith(root) ? root + ruleFile2(f.local) : f.path, content: f.content }));
|
|
26911
|
+
const pathOf = (c) => {
|
|
26912
|
+
const at = numbers.get(c.id);
|
|
26913
|
+
if (c.root) return `todo/${at}-${c.slug || "group"}/root.md`;
|
|
26914
|
+
const group = c.group === null ? null : cards.find((x) => x.root && x.id === c.group);
|
|
26915
|
+
const name = `${at}-${c.slug || "task"}.md`;
|
|
26916
|
+
return group ? `todo/${numbers.get(group.id)}-${group.slug || "group"}/${name}` : `todo/${name}`;
|
|
26917
|
+
};
|
|
26918
|
+
const made = [];
|
|
26919
|
+
const kept = [];
|
|
26920
|
+
for (let i = 0; i < Math.max(cards.length, 1); i += BATCH) {
|
|
26921
|
+
const batch = cards.slice(i, i + BATCH).map((c) => {
|
|
26922
|
+
const wf = c.meta.workflow || "coding";
|
|
26923
|
+
const cloud = cloudOf.get(wf);
|
|
26924
|
+
const review = reviewOf2(c.meta);
|
|
26925
|
+
return {
|
|
26926
|
+
ref: c.id,
|
|
26927
|
+
number: numbers.get(c.id),
|
|
26928
|
+
title: c.meta.title.trim(),
|
|
26929
|
+
workflow: cloud ?? builtinId(wf),
|
|
26930
|
+
path: pathOf(c),
|
|
26931
|
+
body: cardForCloud(c.text, numbers, cloud ? workflowFolder(cloud) : null),
|
|
26932
|
+
...review ? { review } : {}
|
|
26933
|
+
};
|
|
26934
|
+
});
|
|
26935
|
+
const done2 = await api(
|
|
26936
|
+
"POST",
|
|
26937
|
+
`/v1/projects/${project.id}/import`,
|
|
26938
|
+
{ board: board2, cards: batch, ...i === 0 ? { files: sent } : {} }
|
|
26939
|
+
);
|
|
26940
|
+
made.push(...done2.cards);
|
|
26941
|
+
kept.push(...done2.kept);
|
|
26942
|
+
}
|
|
26943
|
+
let drafts = 0;
|
|
26944
|
+
for (const c of reviews) {
|
|
26945
|
+
const task = made.find((m) => m.ref === c.id)?.task;
|
|
26946
|
+
const named2 = draftsOf(c.text);
|
|
26947
|
+
if (task && named2.length) drafts += await uploadDrafts(task, named2);
|
|
26948
|
+
}
|
|
26949
|
+
const memory = sent.filter((f) => /(^|\/)memory\//.test(f.path)).length - kept.length;
|
|
26950
|
+
say(`\u5DF2\u5BFC\u5165\u5230 ${project.name} \xB7 Imported into ${project.name} (${project.prefix})`);
|
|
26951
|
+
say(` \u5361\u7247 Cards: ${made.length}\uFF08\u5F85\u5BA1 on review: ${reviews.length}\uFF09`);
|
|
26952
|
+
say(` \u81EA\u5EFA workflow Own workflows: ${cloudOf.size}`);
|
|
26953
|
+
say(` \u8BB0\u5FC6 Memory files: ${memory}${drafts ? ` \xB7 \u8349\u7A3F Drafts: ${drafts}` : ""}`);
|
|
26954
|
+
if (kept.length) {
|
|
26955
|
+
say(" \u8FD9\u4E9B workspace \u8BB0\u5FC6 Cloud \u5DF2\u6709\uFF0C\u4FDD\u7559\u4E86 Cloud \u7684\u7248\u672C \xB7 Kept Cloud\u2019s copy of these workspace memory files:");
|
|
26956
|
+
for (const k of kept) say(` ${k}`);
|
|
26957
|
+
}
|
|
26958
|
+
say(` ${APP_URL}/projects/${project.id}`);
|
|
26959
|
+
sayLeft(left);
|
|
26960
|
+
say("\u672C\u5730\u770B\u677F\u5DF2\u4E0D\u518D\u751F\u6548\uFF0C\u6838\u5BF9\u65E0\u8BEF\u540E\u8BF7\u81EA\u884C\u5220\u9664 `docs/kanban/`\u3002");
|
|
26961
|
+
say("This local board is no longer used. Once you have checked Cloud, delete `docs/kanban/` yourself.");
|
|
26962
|
+
return { project: project.id, cards: made, workflows: [...cloudOf.values()], memory, drafts, kept, left };
|
|
26963
|
+
}
|
|
26964
|
+
function leftLocal(own) {
|
|
26965
|
+
const out = [];
|
|
26966
|
+
const count4 = (dir) => fs73.existsSync(dir) ? walkMd(dir).length : 0;
|
|
26967
|
+
const archived = count4(ARCHIVE);
|
|
26968
|
+
if (archived) out.push(`.archive/ (${archived})`);
|
|
26969
|
+
const triage = count4(TRIAGE);
|
|
26970
|
+
if (triage) out.push(`triage/ (${triage})`);
|
|
26971
|
+
if (fs73.existsSync(DELIVERIES)) out.push("deliveries/");
|
|
26972
|
+
if (fs73.existsSync(path70.join(KANBAN, "metrics.csv"))) out.push("metrics.csv");
|
|
26973
|
+
const named2 = new Set(own.flatMap((w) => [...w.previews, ...w.land, ...w.scheduled].map((h) => h.agent)));
|
|
26974
|
+
if (fs73.existsSync(AGENTS)) {
|
|
26975
|
+
for (const e of fs73.readdirSync(AGENTS, { withFileTypes: true })) {
|
|
26976
|
+
if (e.isDirectory() && !e.name.startsWith(".") && !named2.has(e.name)) out.push(`agents/${e.name}/`);
|
|
26977
|
+
}
|
|
26978
|
+
}
|
|
26979
|
+
return out;
|
|
26980
|
+
}
|
|
26981
|
+
function sayLeft(left) {
|
|
26982
|
+
if (!left.length) return;
|
|
26983
|
+
say(" \u7559\u5728\u672C\u5730\u3001\u672A\u5BFC\u5165 \xB7 Left on this computer, not imported:");
|
|
26984
|
+
for (const l of left) say(` ${l}`);
|
|
26985
|
+
}
|
|
26986
|
+
|
|
26392
26987
|
// ../cli/src/commands/cloud-template.ts
|
|
26393
26988
|
init_config2();
|
|
26394
26989
|
init_session();
|
|
26395
26990
|
init_io();
|
|
26396
26991
|
init_paths();
|
|
26397
26992
|
import { createHash as createHash4 } from "node:crypto";
|
|
26398
|
-
import
|
|
26399
|
-
import
|
|
26993
|
+
import fs74 from "node:fs";
|
|
26994
|
+
import path71 from "node:path";
|
|
26400
26995
|
var SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
26401
26996
|
var CAPTURED = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
26402
26997
|
var MAX_BYTES3 = 50 * 1024 * 1024;
|
|
@@ -26429,13 +27024,13 @@ function invalid(zh, en) {
|
|
|
26429
27024
|
refuse2({ zh: `\u6A21\u677F\u672A\u901A\u8FC7\u6821\u9A8C\uFF1A${zh}`, en: `The template is not valid: ${en}` });
|
|
26430
27025
|
}
|
|
26431
27026
|
async function ask(route2, init = {}, fallback) {
|
|
26432
|
-
const
|
|
26433
|
-
if (!
|
|
27027
|
+
const token3 = await accessToken();
|
|
27028
|
+
if (!token3.ok) refuse2(SAID[token3.reason], fallback);
|
|
26434
27029
|
let res;
|
|
26435
27030
|
try {
|
|
26436
27031
|
res = await fetch(`${cloudEndpoints().api}/v1/templates${route2}`, {
|
|
26437
27032
|
...init,
|
|
26438
|
-
headers: { authorization: `Bearer ${
|
|
27033
|
+
headers: { authorization: `Bearer ${token3.token}` }
|
|
26439
27034
|
});
|
|
26440
27035
|
} catch {
|
|
26441
27036
|
refuse2(SAID.unreachable, fallback);
|
|
@@ -26494,11 +27089,11 @@ async function templatePreview(name, opts, program2) {
|
|
|
26494
27089
|
This template has no preview.`);
|
|
26495
27090
|
const written = [];
|
|
26496
27091
|
for (const p of previews) {
|
|
26497
|
-
const
|
|
26498
|
-
const file =
|
|
26499
|
-
const out =
|
|
26500
|
-
|
|
26501
|
-
|
|
27092
|
+
const bytes3 = Buffer.from(await (await ask(`/${at}/previews/${route(p.path)}`, {}, WITHOUT)).arrayBuffer());
|
|
27093
|
+
const file = path71.basename(p.path);
|
|
27094
|
+
const out = path71.join(opts.to, file.startsWith(`${name}-`) ? file : `${name}-${file}`);
|
|
27095
|
+
fs74.mkdirSync(opts.to, { recursive: true });
|
|
27096
|
+
fs74.writeFileSync(out, bytes3);
|
|
26502
27097
|
written.push(out);
|
|
26503
27098
|
say(out);
|
|
26504
27099
|
}
|
|
@@ -26511,15 +27106,15 @@ async function templateGet(name, opts, program2) {
|
|
|
26511
27106
|
const { template } = await asJson(await ask(`/${at}`, {}, WITHOUT));
|
|
26512
27107
|
const fetched = [];
|
|
26513
27108
|
for (const f of template.files) {
|
|
26514
|
-
const out =
|
|
26515
|
-
if (!out.startsWith(
|
|
26516
|
-
const
|
|
26517
|
-
if (
|
|
27109
|
+
const out = path71.resolve(to, f.path);
|
|
27110
|
+
if (!out.startsWith(path71.resolve(to) + path71.sep)) refuse2(T.corrupt, WITHOUT);
|
|
27111
|
+
const bytes3 = Buffer.from(await (await ask(`/${at}/files/${route(f.path)}`, {}, WITHOUT)).arrayBuffer());
|
|
27112
|
+
if (bytes3.byteLength !== f.size || createHash4("sha256").update(bytes3).digest("hex") !== f.sha256) {
|
|
26518
27113
|
refuse2(T.corrupt, WITHOUT);
|
|
26519
27114
|
}
|
|
26520
|
-
fetched.push({ out, bytes });
|
|
27115
|
+
fetched.push({ out, bytes: bytes3 });
|
|
26521
27116
|
}
|
|
26522
|
-
const taken = fetched.filter((f) =>
|
|
27117
|
+
const taken = fetched.filter((f) => fs74.existsSync(f.out)).map((f) => path71.relative(to, f.out));
|
|
26523
27118
|
if (taken.length > 0) {
|
|
26524
27119
|
refuse2({
|
|
26525
27120
|
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`,
|
|
@@ -26527,8 +27122,8 @@ async function templateGet(name, opts, program2) {
|
|
|
26527
27122
|
});
|
|
26528
27123
|
}
|
|
26529
27124
|
for (const f of fetched) {
|
|
26530
|
-
|
|
26531
|
-
|
|
27125
|
+
fs74.mkdirSync(path71.dirname(f.out), { recursive: true });
|
|
27126
|
+
fs74.writeFileSync(f.out, f.bytes);
|
|
26532
27127
|
}
|
|
26533
27128
|
say(`\u5DF2\u53D6\u56DE ${name}\uFF1A${fetched.length} \u4E2A\u6587\u4EF6\uFF0C\u4F4D\u4E8E ${to}
|
|
26534
27129
|
Fetched ${name}: ${fetched.length} files into ${to}`);
|
|
@@ -26546,25 +27141,25 @@ async function templateSource(name, opts) {
|
|
|
26546
27141
|
say(`evidence: ${e.path}`);
|
|
26547
27142
|
continue;
|
|
26548
27143
|
}
|
|
26549
|
-
const out =
|
|
26550
|
-
|
|
26551
|
-
|
|
27144
|
+
const out = path71.join(opts.to, `${name}-${path71.basename(e.path)}`);
|
|
27145
|
+
fs74.mkdirSync(opts.to, { recursive: true });
|
|
27146
|
+
fs74.writeFileSync(out, Buffer.from(await (await ask(`/${at}/evidence/${route(e.path)}`)).arrayBuffer()));
|
|
26552
27147
|
written.push(out);
|
|
26553
27148
|
say(out);
|
|
26554
27149
|
}
|
|
26555
27150
|
return { name, source, evidence: written.length > 0 ? written : evidence.map((e) => e.path) };
|
|
26556
27151
|
}
|
|
26557
27152
|
async function templatePublish(dir, opts) {
|
|
26558
|
-
const root =
|
|
26559
|
-
const manifest =
|
|
26560
|
-
if (!
|
|
26561
|
-
const meta = readTemplateMeta(
|
|
26562
|
-
const
|
|
26563
|
-
const total =
|
|
27153
|
+
const root = path71.resolve(dir);
|
|
27154
|
+
const manifest = path71.join(root, "template.md");
|
|
27155
|
+
if (!fs74.existsSync(manifest)) invalid("\u6587\u4EF6\u5939\u9876\u5C42\u7F3A\u5C11 template.md\u3002", "template.md is missing from the folder\u2019s top level.");
|
|
27156
|
+
const meta = readTemplateMeta(fs74.readFileSync(manifest, "utf8"));
|
|
27157
|
+
const files2 = walk2(root);
|
|
27158
|
+
const total = files2.reduce((sum, f) => sum + fs74.statSync(path71.join(root, f)).size, 0);
|
|
26564
27159
|
if (total > MAX_BYTES3) invalid("\u6A21\u677F\u6587\u4EF6\u5408\u8BA1\u8D85\u8FC7 50 MB\u3002", "the template\u2019s files exceed 50 MB.");
|
|
26565
27160
|
for (const extra of [...opts.preview, ...opts.evidence]) {
|
|
26566
|
-
if (!
|
|
26567
|
-
if (
|
|
27161
|
+
if (!fs74.existsSync(extra)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${extra}\u3002`, `${extra} does not exist.`);
|
|
27162
|
+
if (path71.resolve(extra).startsWith(root + path71.sep)) {
|
|
26568
27163
|
invalid(
|
|
26569
27164
|
`${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`,
|
|
26570
27165
|
`${extra} is inside the template folder; keep previews and evidence outside it.`
|
|
@@ -26576,17 +27171,17 @@ async function templatePublish(dir, opts) {
|
|
|
26576
27171
|
}
|
|
26577
27172
|
const form = new FormData();
|
|
26578
27173
|
form.set("meta", JSON.stringify({ ...meta, replace: !!opts.replace }));
|
|
26579
|
-
const add = (kind, name, file) => form.append(`${kind}:${name}`, new Blob([
|
|
26580
|
-
for (const f of
|
|
26581
|
-
for (const f of opts.preview) add("previews",
|
|
26582
|
-
for (const f of opts.evidence) add("evidence",
|
|
27174
|
+
const add = (kind, name, file) => form.append(`${kind}:${name}`, new Blob([fs74.readFileSync(file)]), path71.basename(name));
|
|
27175
|
+
for (const f of files2) add("files", f, path71.join(root, f));
|
|
27176
|
+
for (const f of opts.preview) add("previews", path71.basename(f), f);
|
|
27177
|
+
for (const f of opts.evidence) add("evidence", path71.basename(f), f);
|
|
26583
27178
|
const done2 = await asJson(await ask("", { method: "POST", body: form }));
|
|
26584
27179
|
say(
|
|
26585
|
-
done2.replaced ? `\u5DF2\u66FF\u6362 ${done2.name}\uFF1A${
|
|
26586
|
-
Replaced ${done2.name}: ${
|
|
26587
|
-
Published ${done2.name}: ${
|
|
27180
|
+
done2.replaced ? `\u5DF2\u66FF\u6362 ${done2.name}\uFF1A${files2.length} \u4E2A\u6587\u4EF6\u3002
|
|
27181
|
+
Replaced ${done2.name}: ${files2.length} files.` : `\u5DF2\u53D1\u5E03 ${done2.name}\uFF1A${files2.length} \u4E2A\u6587\u4EF6\u3002
|
|
27182
|
+
Published ${done2.name}: ${files2.length} files.`
|
|
26588
27183
|
);
|
|
26589
|
-
return { name: done2.name, replaced: done2.replaced, files };
|
|
27184
|
+
return { name: done2.name, replaced: done2.replaced, files: files2 };
|
|
26590
27185
|
}
|
|
26591
27186
|
async function templateDelete(name) {
|
|
26592
27187
|
await ask(`/${encodeURIComponent(name)}`, { method: "DELETE" });
|
|
@@ -26595,11 +27190,11 @@ Deleted ${name} from the catalog.`);
|
|
|
26595
27190
|
return { name, deleted: true };
|
|
26596
27191
|
}
|
|
26597
27192
|
async function templateShowcase(name, dir) {
|
|
26598
|
-
const file =
|
|
26599
|
-
if (!
|
|
27193
|
+
const file = path71.join(dir, "showcase.json");
|
|
27194
|
+
if (!fs74.existsSync(file)) invalid(`${dir} \u4E2D\u7F3A\u5C11 showcase.json\u3002`, `showcase.json is missing from ${dir}.`);
|
|
26600
27195
|
let meta;
|
|
26601
27196
|
try {
|
|
26602
|
-
meta = JSON.parse(
|
|
27197
|
+
meta = JSON.parse(fs74.readFileSync(file, "utf8"));
|
|
26603
27198
|
} catch {
|
|
26604
27199
|
return invalid("showcase.json \u4E0D\u662F\u6709\u6548\u7684 JSON\u3002", "showcase.json is not valid JSON.");
|
|
26605
27200
|
}
|
|
@@ -26611,9 +27206,9 @@ async function templateShowcase(name, dir) {
|
|
|
26611
27206
|
const form = new FormData();
|
|
26612
27207
|
form.set("meta", JSON.stringify(meta));
|
|
26613
27208
|
for (const p of new Set(named2)) {
|
|
26614
|
-
const at =
|
|
26615
|
-
if (!
|
|
26616
|
-
form.append(`media:${p}`, new Blob([
|
|
27209
|
+
const at = path71.join(dir, p);
|
|
27210
|
+
if (!fs74.existsSync(at)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${at}\u3002`, `${at} does not exist.`);
|
|
27211
|
+
form.append(`media:${p}`, new Blob([fs74.readFileSync(at)]), p);
|
|
26617
27212
|
}
|
|
26618
27213
|
const done2 = await asJson(await ask(`/${encodeURIComponent(name)}/showcase`, { method: "PUT", body: form }));
|
|
26619
27214
|
say(`\u5DF2\u66F4\u65B0 ${name} \u7684\u5C55\u793A\u8D44\u6599\uFF1A${done2.files} \u4E2A\u6587\u4EF6\u3002
|
|
@@ -26622,7 +27217,7 @@ Updated ${name}'s showcase: ${done2.files} files.`);
|
|
|
26622
27217
|
}
|
|
26623
27218
|
function walk2(root, sub = "") {
|
|
26624
27219
|
const out = [];
|
|
26625
|
-
for (const entry of
|
|
27220
|
+
for (const entry of fs74.readdirSync(path71.join(root, sub), { withFileTypes: true })) {
|
|
26626
27221
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
26627
27222
|
const rel2 = sub ? `${sub}/${entry.name}` : entry.name;
|
|
26628
27223
|
if (entry.isDirectory()) out.push(...walk2(root, rel2));
|
|
@@ -26956,7 +27551,7 @@ init_sessions();
|
|
|
26956
27551
|
init_env();
|
|
26957
27552
|
init_store();
|
|
26958
27553
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
26959
|
-
import
|
|
27554
|
+
import path76 from "node:path";
|
|
26960
27555
|
var AGENTS2 = {
|
|
26961
27556
|
claude: "claude-code",
|
|
26962
27557
|
codex: "codex",
|
|
@@ -26971,10 +27566,10 @@ var AGENTS2 = {
|
|
|
26971
27566
|
var RUNTIMES = /* @__PURE__ */ new Set(["node", "bun", "deno"]);
|
|
26972
27567
|
function agentOf2(args) {
|
|
26973
27568
|
const words = args.trim().split(/\s+/);
|
|
26974
|
-
const first =
|
|
27569
|
+
const first = path76.basename(words[0] ?? "");
|
|
26975
27570
|
const script = RUNTIMES.has(first) ? words[1] ?? "" : words[0] ?? "";
|
|
26976
27571
|
if (script.includes("/@anthropic-ai/claude-code/")) return "claude-code";
|
|
26977
|
-
return AGENTS2[
|
|
27572
|
+
return AGENTS2[path76.basename(script).replace(/\.(m?js|cjs|exe)$/, "")];
|
|
26978
27573
|
}
|
|
26979
27574
|
function processChain() {
|
|
26980
27575
|
if (process.platform === "win32") return [];
|
|
@@ -27018,8 +27613,8 @@ function terminalSession(chain, env, opencode, cwd = process.cwd()) {
|
|
|
27018
27613
|
return resumeId ? { harness, resumeId, cwd } : void 0;
|
|
27019
27614
|
}
|
|
27020
27615
|
function currentSession() {
|
|
27021
|
-
const
|
|
27022
|
-
if (
|
|
27616
|
+
const run2 = insideRun();
|
|
27617
|
+
if (run2) return readStore().runs.find((r) => r.sessionId === run2)?.origin;
|
|
27023
27618
|
return terminalSession(probes.chain(), process.env, probes.opencode);
|
|
27024
27619
|
}
|
|
27025
27620
|
|
|
@@ -27035,8 +27630,8 @@ init_tasks();
|
|
|
27035
27630
|
init_read();
|
|
27036
27631
|
init_rules();
|
|
27037
27632
|
init_board2();
|
|
27038
|
-
import
|
|
27039
|
-
import
|
|
27633
|
+
import fs80 from "node:fs";
|
|
27634
|
+
import path77 from "node:path";
|
|
27040
27635
|
var FOLLOW_MS = 400;
|
|
27041
27636
|
async function cmdStartRun(action, args, opts, program2 = "akb") {
|
|
27042
27637
|
const { req, follow, print } = readRequest(action, args, opts);
|
|
@@ -27076,13 +27671,13 @@ async function cmdStartRun(action, args, opts, program2 = "akb") {
|
|
|
27076
27671
|
sayIfHeld(req, program2);
|
|
27077
27672
|
const started = action === "refine" ? await startRefinement(req) : await startRun(runnable);
|
|
27078
27673
|
if ("error" in started) die(started.error, { kind: "run-refused", action });
|
|
27079
|
-
const { run, spawned } = started;
|
|
27080
|
-
if (!spawned) die(`couldn't start a process for run ${
|
|
27081
|
-
say(`${action} \u2014 run ${
|
|
27082
|
-
say(` follow it: ${program2} run log ${short(
|
|
27083
|
-
say(` stop it: ${program2} run stop ${short(
|
|
27084
|
-
if (follow) return await followStarted(
|
|
27085
|
-
return { sessionId:
|
|
27674
|
+
const { run: run2, spawned } = started;
|
|
27675
|
+
if (!spawned) die(`couldn't start a process for run ${run2.sessionId}`, { kind: "spawn-failed" });
|
|
27676
|
+
say(`${action} \u2014 run ${run2.sessionId}${run2.deliveryId ? ` in delivery ${run2.deliveryId}` : ""}`);
|
|
27677
|
+
say(` follow it: ${program2} run log ${short(run2.sessionId)} --follow${BOARD_FLAG}`);
|
|
27678
|
+
say(` stop it: ${program2} run stop ${short(run2.sessionId)}${BOARD_FLAG}`);
|
|
27679
|
+
if (follow) return await followStarted(run2.sessionId, program2, opts.events === true);
|
|
27680
|
+
return { sessionId: run2.sessionId, action, cardId: run2.cardId };
|
|
27086
27681
|
}
|
|
27087
27682
|
async function cmdRunScheduled(workflow, agent, opts, program2 = "akb") {
|
|
27088
27683
|
const id = knownWorkflow(workflow);
|
|
@@ -27092,12 +27687,12 @@ async function cmdRunScheduled(workflow, agent, opts, program2 = "akb") {
|
|
|
27092
27687
|
if (insideRun() || opts.print) return printFlow(req, program2);
|
|
27093
27688
|
const started = await startRun(req);
|
|
27094
27689
|
if ("error" in started) die(started.error, { kind: "run-refused", action: "scheduled" });
|
|
27095
|
-
const { run, spawned } = started;
|
|
27096
|
-
if (!spawned) die(`couldn't start a process for run ${
|
|
27097
|
-
say(`${one.agent} \u2014 run ${
|
|
27098
|
-
say(` follow it: ${program2} run log ${short(
|
|
27099
|
-
if (opts.follow) return await followStarted(
|
|
27100
|
-
return { sessionId:
|
|
27690
|
+
const { run: run2, spawned } = started;
|
|
27691
|
+
if (!spawned) die(`couldn't start a process for run ${run2.sessionId}`, { kind: "spawn-failed" });
|
|
27692
|
+
say(`${one.agent} \u2014 run ${run2.sessionId}${run2.deliveryId ? ` in delivery ${run2.deliveryId}` : ""}`);
|
|
27693
|
+
say(` follow it: ${program2} run log ${short(run2.sessionId)} --follow${BOARD_FLAG}`);
|
|
27694
|
+
if (opts.follow) return await followStarted(run2.sessionId, program2, opts.events === true);
|
|
27695
|
+
return { sessionId: run2.sessionId, action: "scheduled" };
|
|
27101
27696
|
}
|
|
27102
27697
|
async function createInCloud(task, description, workflow) {
|
|
27103
27698
|
const title = description.split("\n")[0].trim().slice(0, 120);
|
|
@@ -27215,12 +27810,12 @@ function readRequest(action, args, opts) {
|
|
|
27215
27810
|
async function cmdResume(id, opts) {
|
|
27216
27811
|
const opened = await startResume(id ?? "last");
|
|
27217
27812
|
if ("error" in opened) die(opened.error, { kind: "run-refused" });
|
|
27218
|
-
const { run } = opened;
|
|
27219
|
-
if (!opened.spawned) die(`couldn't start a process for run ${
|
|
27220
|
-
const within =
|
|
27221
|
-
say(
|
|
27222
|
-
if (opts.follow === true) return await followStarted(
|
|
27223
|
-
return { sessionId:
|
|
27813
|
+
const { run: run2 } = opened;
|
|
27814
|
+
if (!opened.spawned) die(`couldn't start a process for run ${run2.sessionId}`, { kind: "spawn-failed" });
|
|
27815
|
+
const within = run2.deliveryId ? ` in delivery ${run2.deliveryId}` : "";
|
|
27816
|
+
say(run2.resumedFrom ? `continuing ${short(run2.resumedFrom)} \u2014 run ${run2.sessionId}${within}` : `trying again \u2014 run ${run2.sessionId}${within}`);
|
|
27817
|
+
if (opts.follow === true) return await followStarted(run2.sessionId, "akb");
|
|
27818
|
+
return { sessionId: run2.sessionId, resumedFrom: run2.resumedFrom };
|
|
27224
27819
|
}
|
|
27225
27820
|
async function cmdCancel(named2) {
|
|
27226
27821
|
const res = await cancelBoardDelivery(named2);
|
|
@@ -27310,7 +27905,7 @@ async function cmdSubStart(words, opts) {
|
|
|
27310
27905
|
let prompt = words.join(" ");
|
|
27311
27906
|
if (opts.file) {
|
|
27312
27907
|
try {
|
|
27313
|
-
prompt =
|
|
27908
|
+
prompt = fs80.readFileSync(path77.resolve(opts.file), "utf8");
|
|
27314
27909
|
} catch {
|
|
27315
27910
|
die(`--file: cannot read ${opts.file}`, { kind: "run-refused" });
|
|
27316
27911
|
}
|
|
@@ -27576,7 +28171,7 @@ async function cmdWatch(sessionId) {
|
|
|
27576
28171
|
// ../cli/src/commands/triage.ts
|
|
27577
28172
|
init_scheduled();
|
|
27578
28173
|
init_workflows();
|
|
27579
|
-
import
|
|
28174
|
+
import fs82 from "node:fs";
|
|
27580
28175
|
|
|
27581
28176
|
// ../cli/src/lib/signals/index.ts
|
|
27582
28177
|
init_cards();
|
|
@@ -27586,16 +28181,16 @@ init_yaml();
|
|
|
27586
28181
|
// ../cli/src/lib/signals/config.ts
|
|
27587
28182
|
init_settings2();
|
|
27588
28183
|
init_paths();
|
|
27589
|
-
import
|
|
27590
|
-
import
|
|
28184
|
+
import fs81 from "node:fs";
|
|
28185
|
+
import path78 from "node:path";
|
|
27591
28186
|
var TOKEN_KEY = "TRIAGE_ENDPOINT_TOKEN";
|
|
27592
28187
|
var ENDPOINT_SETTING = "Triage endpoint";
|
|
27593
28188
|
var LINE2 = /^- \*\*Triage endpoint\*\*\s*[—-]\s*(.+)$/m;
|
|
27594
|
-
var boardRel3 = (file) => rel(file).split(
|
|
28189
|
+
var boardRel3 = (file) => rel(file).split(path78.sep).join("/");
|
|
27595
28190
|
function signalEndpoint() {
|
|
27596
28191
|
let text5;
|
|
27597
28192
|
try {
|
|
27598
|
-
text5 =
|
|
28193
|
+
text5 = fs81.readFileSync(CONFIG, "utf8");
|
|
27599
28194
|
} catch {
|
|
27600
28195
|
return "";
|
|
27601
28196
|
}
|
|
@@ -27639,7 +28234,7 @@ function readMeta(sent) {
|
|
|
27639
28234
|
}
|
|
27640
28235
|
return pairs;
|
|
27641
28236
|
}
|
|
27642
|
-
function
|
|
28237
|
+
function read2(raw) {
|
|
27643
28238
|
const missing = REQUIRED.filter((field2) => !text4(raw[field2]));
|
|
27644
28239
|
if (missing.length > 0) return { ok: false, why: `missing ${missing.join(", ")}` };
|
|
27645
28240
|
const sent = text4(raw.collected_at);
|
|
@@ -27715,7 +28310,7 @@ async function fetchSignals() {
|
|
|
27715
28310
|
report3.failed.push({ which: `item ${at + 1}`, why: "not an object" });
|
|
27716
28311
|
return;
|
|
27717
28312
|
}
|
|
27718
|
-
const found2 =
|
|
28313
|
+
const found2 = read2(raw);
|
|
27719
28314
|
if (!found2.ok) {
|
|
27720
28315
|
report3.failed.push({ which: text4(raw.source_id) || `item ${at + 1}`, why: found2.why });
|
|
27721
28316
|
return;
|
|
@@ -27771,7 +28366,7 @@ async function cmdTriageAdd(opts) {
|
|
|
27771
28366
|
let body = opts.text;
|
|
27772
28367
|
if (opts.file !== void 0) {
|
|
27773
28368
|
try {
|
|
27774
|
-
body =
|
|
28369
|
+
body = fs82.readFileSync(opts.file, "utf8");
|
|
27775
28370
|
} catch {
|
|
27776
28371
|
die(`can't read ${opts.file} \u2014 write the item to a file, then pass its path`, { kind: "needs-input" });
|
|
27777
28372
|
}
|
|
@@ -27895,16 +28490,16 @@ async function cmdSpec(opts, program2 = "akb") {
|
|
|
27895
28490
|
}
|
|
27896
28491
|
const started = await startRun(req);
|
|
27897
28492
|
if ("error" in started) die(started.error, { kind: "run-refused", action: "spec" });
|
|
27898
|
-
const { run, spawned } = started;
|
|
27899
|
-
if (!spawned) die(`couldn't start a process to run ${
|
|
28493
|
+
const { run: run2, spawned } = started;
|
|
28494
|
+
if (!spawned) die(`couldn't start a process to run ${run2.sessionId}`, { kind: "spawn-failed" });
|
|
27900
28495
|
if (ownRuntime || ownSession) {
|
|
27901
28496
|
say(`${name} runs in ${ownRuntime ? "its own runtime" : "a session of its own"}, so it started as a separate run \u2014 don't write its section yourself.`);
|
|
27902
28497
|
}
|
|
27903
|
-
say(`spec ${name} #${id} \u2014 run ${
|
|
27904
|
-
say(` follow it: ${program2} run log ${short(
|
|
27905
|
-
say(` stop it: ${program2} run stop ${short(
|
|
27906
|
-
if (opts.follow === true) return await followStarted(
|
|
27907
|
-
return { sessionId:
|
|
28498
|
+
say(`spec ${name} #${id} \u2014 run ${run2.sessionId}`);
|
|
28499
|
+
say(` follow it: ${program2} run log ${short(run2.sessionId)} --follow${BOARD_FLAG}`);
|
|
28500
|
+
say(` stop it: ${program2} run stop ${short(run2.sessionId)}${BOARD_FLAG}`);
|
|
28501
|
+
if (opts.follow === true) return await followStarted(run2.sessionId, program2);
|
|
28502
|
+
return { sessionId: run2.sessionId, action: "spec", specAgent: name, cardId: id };
|
|
27908
28503
|
}
|
|
27909
28504
|
function noteOf(words, flag) {
|
|
27910
28505
|
const typed = words.join(" ").trim();
|
|
@@ -28055,7 +28650,13 @@ function declareRuns(program2, cli) {
|
|
|
28055
28650
|
withShared(cloud.command("sign-out")).summary("forget it: this machine stops reaching Cloud, and nothing on any board changes").action(async function() {
|
|
28056
28651
|
await boardless(this, cli, (p) => cmdCloud(["sign-out"], p));
|
|
28057
28652
|
});
|
|
28058
|
-
withShared(cloud.command("
|
|
28653
|
+
withShared(cloud.command("import")).summary("import this board into an empty Cloud project").description(
|
|
28654
|
+
"Imports the open cards, memory, rules and own workflows of this board into an empty Cloud project, numbered from 1 in the order of their ids. Checks everything first and writes nothing on a problem; changes nothing on this computer. Run again to finish an import that stopped."
|
|
28655
|
+
).option("--project <id-or-prefix>", "the Cloud project, by id or by its prefix").option("--dry-run", "only check, and list what would be imported and what stays here").action(async function() {
|
|
28656
|
+
const o = this.opts();
|
|
28657
|
+
await onBoard2(this, cli, (p) => cloudImport({ project: o.project, dryRun: o.dryRun }, p));
|
|
28658
|
+
});
|
|
28659
|
+
withShared(cloud.command("image")).argument("[prompt...]", "what to draw").summary("generate an image through Cloud, spending Pro credits").option("--aspect <ratio>", "the aspect ratio, e.g. 16:9").option("--ref <file>", "a PNG, JPEG or WebP to draw from; repeat for more", (v, all2) => [...all2, v], []).option("--out <file>", "where to write the image").action(async function(prompt) {
|
|
28059
28660
|
const o = this.opts();
|
|
28060
28661
|
await boardless(
|
|
28061
28662
|
this,
|
|
@@ -28094,7 +28695,7 @@ function declareRuns(program2, cli) {
|
|
|
28094
28695
|
withShared(template.command("source")).argument("<name>", "the template").summary("where a template came from, and the evidence captured at the time").option("--to <dir>", "download the evidence into this folder").action(async function(name) {
|
|
28095
28696
|
await boardless(this, cli, () => templateSource(name, this.opts()));
|
|
28096
28697
|
});
|
|
28097
|
-
withShared(template.command("publish")).argument("<dir>", "the template's folder, holding template.md").summary("publish a template (maintainers only)").option("--preview <file>", "a catalog preview image, kept outside the folder; repeat for more", (v,
|
|
28698
|
+
withShared(template.command("publish")).argument("<dir>", "the template's folder, holding template.md").summary("publish a template (maintainers only)").option("--preview <file>", "a catalog preview image, kept outside the folder; repeat for more", (v, all2) => [...all2, v], []).option("--evidence <file>", "a capture of the reference, kept outside the folder; repeat for more", (v, all2) => [...all2, v], []).option("--replace", "replace the template already published under this name").action(async function(dir) {
|
|
28098
28699
|
const o = this.opts();
|
|
28099
28700
|
await boardless(this, cli, () => templatePublish(dir, o));
|
|
28100
28701
|
});
|
|
@@ -28177,8 +28778,8 @@ function declareRuns(program2, cli) {
|
|
|
28177
28778
|
}
|
|
28178
28779
|
var RUN_ID = "a run's id, any prefix of one that names only one run, or `last`. Left out, it means the newest run";
|
|
28179
28780
|
var DELIVERY = "the delivery, or the card it is on";
|
|
28180
|
-
function declareRunning(
|
|
28181
|
-
const verb = (name) => withShared(
|
|
28781
|
+
function declareRunning(run2, cli) {
|
|
28782
|
+
const verb = (name) => withShared(run2.command(name));
|
|
28182
28783
|
verb("list").summary("what is running, and what ran lately").option("-c, --card <id>", "only the runs on that card", cardId).option("-a, --all", "every run this board has kept, not just the last ten").action(async function() {
|
|
28183
28784
|
await onBoard2(this, cli, (p) => cmdRuns(this.opts(), p));
|
|
28184
28785
|
});
|
|
@@ -28334,8 +28935,8 @@ function declareAgent(program2, cli) {
|
|
|
28334
28935
|
}
|
|
28335
28936
|
|
|
28336
28937
|
// ../cli/src/lib/cli/setup.ts
|
|
28337
|
-
import
|
|
28338
|
-
import
|
|
28938
|
+
import fs86 from "node:fs";
|
|
28939
|
+
import path82 from "node:path";
|
|
28339
28940
|
|
|
28340
28941
|
// ../cli/src/commands/install.ts
|
|
28341
28942
|
init_local();
|
|
@@ -28347,14 +28948,14 @@ init_io();
|
|
|
28347
28948
|
init_paths();
|
|
28348
28949
|
init_memory();
|
|
28349
28950
|
init_yaml2();
|
|
28350
|
-
import
|
|
28351
|
-
import
|
|
28951
|
+
import fs85 from "node:fs";
|
|
28952
|
+
import path81 from "node:path";
|
|
28352
28953
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
28353
28954
|
|
|
28354
28955
|
// ../cli/src/lib/skill/install.ts
|
|
28355
28956
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
28356
|
-
import
|
|
28357
|
-
import
|
|
28957
|
+
import fs84 from "node:fs";
|
|
28958
|
+
import path80 from "node:path";
|
|
28358
28959
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
28359
28960
|
|
|
28360
28961
|
// ../skill/SKILL.md
|
|
@@ -28369,8 +28970,8 @@ init_version();
|
|
|
28369
28970
|
init_version();
|
|
28370
28971
|
init_project();
|
|
28371
28972
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
28372
|
-
import
|
|
28373
|
-
import
|
|
28973
|
+
import fs83 from "node:fs";
|
|
28974
|
+
import path79 from "node:path";
|
|
28374
28975
|
var MARKER3 = "# ai4kanban commit guard";
|
|
28375
28976
|
var MARKER_LINE = new RegExp(`^${MARKER3}\\b`, "m");
|
|
28376
28977
|
var CHECK = [
|
|
@@ -28402,9 +29003,9 @@ function hookScript(sessions) {
|
|
|
28402
29003
|
""
|
|
28403
29004
|
].join("\n");
|
|
28404
29005
|
}
|
|
28405
|
-
var
|
|
29006
|
+
var read3 = (file) => {
|
|
28406
29007
|
try {
|
|
28407
|
-
return
|
|
29008
|
+
return fs83.readFileSync(file, "utf8");
|
|
28408
29009
|
} catch {
|
|
28409
29010
|
return null;
|
|
28410
29011
|
}
|
|
@@ -28416,7 +29017,7 @@ function hooksDir(root) {
|
|
|
28416
29017
|
windowsHide: true
|
|
28417
29018
|
});
|
|
28418
29019
|
if (out.error || out.status !== 0 || !out.stdout.trim()) return null;
|
|
28419
|
-
return
|
|
29020
|
+
return path79.resolve(root, out.stdout.trim());
|
|
28420
29021
|
}
|
|
28421
29022
|
function hasOwnHooksPath(root) {
|
|
28422
29023
|
const out = spawnSync6("git", ["config", "--get", "core.hooksPath"], {
|
|
@@ -28426,18 +29027,18 @@ function hasOwnHooksPath(root) {
|
|
|
28426
29027
|
});
|
|
28427
29028
|
return !out.error && out.status === 0 && !!out.stdout.trim();
|
|
28428
29029
|
}
|
|
28429
|
-
var sessionsPath = (root) =>
|
|
29030
|
+
var sessionsPath = (root) => path79.join(projectStateDir(path79.join(root, "docs", "kanban"), root), SESSIONS_FILE);
|
|
28430
29031
|
function readCommitHook(root) {
|
|
28431
29032
|
const dir = hooksDir(root);
|
|
28432
29033
|
if (!dir) return { state: "no-git" };
|
|
28433
|
-
const where = shown(root,
|
|
29034
|
+
const where = shown(root, path79.join(dir, "pre-commit"));
|
|
28434
29035
|
const line2 = guardLine(sessionsPath(root));
|
|
28435
29036
|
if (hasOwnHooksPath(root)) return { state: "hooks-path", path: where, line: line2 };
|
|
28436
|
-
const text5 =
|
|
29037
|
+
const text5 = read3(path79.join(dir, "pre-commit"));
|
|
28437
29038
|
if (text5 === null) return { state: "absent", path: where };
|
|
28438
29039
|
return MARKER_LINE.test(text5) ? { state: "ours", path: where } : { state: "foreign", path: where, line: line2 };
|
|
28439
29040
|
}
|
|
28440
|
-
var shown = (root, p) => (
|
|
29041
|
+
var shown = (root, p) => (path79.relative(root, p) || p).split(path79.sep).join("/");
|
|
28441
29042
|
function installCommitHook(root) {
|
|
28442
29043
|
const hook = readCommitHook(root);
|
|
28443
29044
|
if (hook.state === "no-git") return {};
|
|
@@ -28448,11 +29049,11 @@ function installCommitHook(root) {
|
|
|
28448
29049
|
${hook.line}`
|
|
28449
29050
|
};
|
|
28450
29051
|
}
|
|
28451
|
-
const file =
|
|
29052
|
+
const file = path79.join(root, hook.path);
|
|
28452
29053
|
try {
|
|
28453
|
-
|
|
28454
|
-
|
|
28455
|
-
|
|
29054
|
+
fs83.mkdirSync(path79.dirname(file), { recursive: true });
|
|
29055
|
+
fs83.writeFileSync(file, hookScript(sessionsPath(root)), { mode: 493 });
|
|
29056
|
+
fs83.chmodSync(file, 493);
|
|
28456
29057
|
} catch (e) {
|
|
28457
29058
|
return { note: `${hook.path} could not be written \u2014 ${e instanceof Error ? e.message : String(e)}` };
|
|
28458
29059
|
}
|
|
@@ -28468,9 +29069,9 @@ function sayCommitHook(state) {
|
|
|
28468
29069
|
|
|
28469
29070
|
// ../cli/src/lib/skill/install.ts
|
|
28470
29071
|
var TARGETS = [
|
|
28471
|
-
{ rel:
|
|
29072
|
+
{ rel: path80.join(".claude", "skills", "kanban"), agent: "Claude Code" },
|
|
28472
29073
|
{
|
|
28473
|
-
rel:
|
|
29074
|
+
rel: path80.join(".agents", "skills", "kanban"),
|
|
28474
29075
|
agent: "Codex, Cursor, OpenCode, Kimi Code, DeepSeek Harness, ZCode, Grok Build"
|
|
28475
29076
|
}
|
|
28476
29077
|
];
|
|
@@ -28482,25 +29083,25 @@ var SELF2 = fileURLToPath2(import.meta.url);
|
|
|
28482
29083
|
function rulesPath() {
|
|
28483
29084
|
return SELF2;
|
|
28484
29085
|
}
|
|
28485
|
-
function
|
|
29086
|
+
function read4(file) {
|
|
28486
29087
|
try {
|
|
28487
|
-
return
|
|
29088
|
+
return fs84.readFileSync(file, "utf8");
|
|
28488
29089
|
} catch {
|
|
28489
29090
|
return null;
|
|
28490
29091
|
}
|
|
28491
29092
|
}
|
|
28492
29093
|
function statOf(p) {
|
|
28493
29094
|
try {
|
|
28494
|
-
return
|
|
29095
|
+
return fs84.lstatSync(p);
|
|
28495
29096
|
} catch {
|
|
28496
29097
|
return null;
|
|
28497
29098
|
}
|
|
28498
29099
|
}
|
|
28499
29100
|
function versionIn(dir) {
|
|
28500
|
-
const note =
|
|
29101
|
+
const note = read4(path80.join(dir, "SKILL.md"));
|
|
28501
29102
|
const stampedNote = note?.match(NOTE_STAMP);
|
|
28502
29103
|
if (stampedNote) return stampedNote[1];
|
|
28503
|
-
const src =
|
|
29104
|
+
const src = read4(path80.join(dir, "kanban.mjs"));
|
|
28504
29105
|
if (!src) return null;
|
|
28505
29106
|
const stamped = src.slice(0, 400).match(BUILT_STAMP);
|
|
28506
29107
|
if (stamped) return stamped[1];
|
|
@@ -28524,7 +29125,7 @@ function folderState(dir) {
|
|
|
28524
29125
|
const st = statOf(dir);
|
|
28525
29126
|
if (!st) return { state: "absent", version: null };
|
|
28526
29127
|
if (st.isSymbolicLink()) return { state: "linked", version: versionIn(dir) };
|
|
28527
|
-
if (!
|
|
29128
|
+
if (!fs84.existsSync(path80.join(dir, "SKILL.md"))) return { state: "absent", version: null };
|
|
28528
29129
|
const version = versionIn(dir);
|
|
28529
29130
|
if (!version) return { state: "unknown", version: null };
|
|
28530
29131
|
return { state: isOlder(version, SKILL_VERSION) ? "stale" : "current", version };
|
|
@@ -28532,9 +29133,9 @@ function folderState(dir) {
|
|
|
28532
29133
|
function readSkillState(root) {
|
|
28533
29134
|
const dir = rootOr(root);
|
|
28534
29135
|
const folders2 = TARGETS.map((target) => ({
|
|
28535
|
-
path: target.rel.split(
|
|
29136
|
+
path: target.rel.split(path80.sep).join("/"),
|
|
28536
29137
|
agent: target.agent,
|
|
28537
|
-
...folderState(
|
|
29138
|
+
...folderState(path80.join(dir, target.rel))
|
|
28538
29139
|
}));
|
|
28539
29140
|
return {
|
|
28540
29141
|
version: SKILL_VERSION,
|
|
@@ -28571,10 +29172,10 @@ function commandParagraph(command) {
|
|
|
28571
29172
|
].join("\n");
|
|
28572
29173
|
}
|
|
28573
29174
|
function writeFolder(dir, command) {
|
|
28574
|
-
|
|
28575
|
-
|
|
29175
|
+
fs84.rmSync(dir, { recursive: true, force: true });
|
|
29176
|
+
fs84.mkdirSync(dir, { recursive: true });
|
|
28576
29177
|
const filled = SKILL_default.replace(COMMAND_BLOCK, commandParagraph(command));
|
|
28577
|
-
|
|
29178
|
+
fs84.writeFileSync(path80.join(dir, "SKILL.md"), `${filled.trimEnd()}
|
|
28578
29179
|
|
|
28579
29180
|
<!-- ai4kanban ${SKILL_VERSION} -->
|
|
28580
29181
|
`);
|
|
@@ -28590,8 +29191,8 @@ function installSkill(root, only, invoked) {
|
|
|
28590
29191
|
const wrote = [];
|
|
28591
29192
|
const skipped = [];
|
|
28592
29193
|
for (const target of TARGETS) {
|
|
28593
|
-
const dest =
|
|
28594
|
-
const shown2 = target.rel.split(
|
|
29194
|
+
const dest = path80.join(dir, target.rel);
|
|
29195
|
+
const shown2 = target.rel.split(path80.sep).join("/");
|
|
28595
29196
|
const { state: state2 } = folderState(dest);
|
|
28596
29197
|
if (state2 === "linked") {
|
|
28597
29198
|
skipped.push({ path: shown2, agent: target.agent, why: "a symlink into a source checkout \u2014 left untouched" });
|
|
@@ -28661,10 +29262,10 @@ var Report = class {
|
|
|
28661
29262
|
for (const line2 of this.notes) say(` ! ${line2}`);
|
|
28662
29263
|
}
|
|
28663
29264
|
};
|
|
28664
|
-
var
|
|
29265
|
+
var read5 = (file) => fs85.existsSync(file) ? fs85.readFileSync(file, "utf8") : null;
|
|
28665
29266
|
function statOf2(p) {
|
|
28666
29267
|
try {
|
|
28667
|
-
return
|
|
29268
|
+
return fs85.lstatSync(p);
|
|
28668
29269
|
} catch {
|
|
28669
29270
|
return null;
|
|
28670
29271
|
}
|
|
@@ -28675,10 +29276,10 @@ async function boardMove(root, args, board2) {
|
|
|
28675
29276
|
if (code !== 0) throw new BoardError(`\`board ${args.join(" ")}\` failed \u2014 nothing else was changed`, { kind: "board-move-failed" });
|
|
28676
29277
|
}
|
|
28677
29278
|
function boardAbove(root) {
|
|
28678
|
-
let at =
|
|
29279
|
+
let at = path81.dirname(path81.resolve(root));
|
|
28679
29280
|
for (; ; ) {
|
|
28680
|
-
if (
|
|
28681
|
-
const up =
|
|
29281
|
+
if (fs85.existsSync(path81.join(at, "docs", "kanban"))) return at;
|
|
29282
|
+
const up = path81.dirname(at);
|
|
28682
29283
|
if (up === at) return null;
|
|
28683
29284
|
at = up;
|
|
28684
29285
|
}
|
|
@@ -28686,10 +29287,10 @@ function boardAbove(root) {
|
|
|
28686
29287
|
async function cmdInstall(ctx) {
|
|
28687
29288
|
const report3 = new Report();
|
|
28688
29289
|
const board2 = ctx.board ?? null;
|
|
28689
|
-
const where = board2 ?
|
|
29290
|
+
const where = board2 ? path81.relative(ctx.dir, board2) || board2 : "docs/kanban";
|
|
28690
29291
|
say(`wildcrane ${SKILL_VERSION} \u2014 installing into ${ctx.dir}`);
|
|
28691
29292
|
say("");
|
|
28692
|
-
const above = board2 ||
|
|
29293
|
+
const above = board2 || fs85.existsSync(path81.join(ctx.dir, "docs", "kanban")) ? null : boardAbove(ctx.dir);
|
|
28693
29294
|
if (above) {
|
|
28694
29295
|
report3.notes.push(`there is already a board at ${above} \u2014 this makes a second one, and commands run here will find this one`);
|
|
28695
29296
|
}
|
|
@@ -28711,7 +29312,7 @@ async function cmdInstall(ctx) {
|
|
|
28711
29312
|
say("board UI (Configuration \u2192 Agent setup), or here:");
|
|
28712
29313
|
say("");
|
|
28713
29314
|
say(` ${ctx.program} skill`);
|
|
28714
|
-
return { installed: ctx.dir, board: board2 ??
|
|
29315
|
+
return { installed: ctx.dir, board: board2 ?? path81.join(ctx.dir, "docs", "kanban") };
|
|
28715
29316
|
}
|
|
28716
29317
|
async function cmdSkill(ctx, mode) {
|
|
28717
29318
|
const report3 = new Report();
|
|
@@ -28756,7 +29357,7 @@ function placeSkill(ctx, mode, report3) {
|
|
|
28756
29357
|
const before = readSkillState(ctx.dir).folders;
|
|
28757
29358
|
for (const folder of before) {
|
|
28758
29359
|
if (folder.state !== "absent" && folder.state !== "linked") {
|
|
28759
|
-
rescueSkillConfig(ctx.dir,
|
|
29360
|
+
rescueSkillConfig(ctx.dir, path81.join(ctx.dir, folder.path), report3);
|
|
28760
29361
|
}
|
|
28761
29362
|
}
|
|
28762
29363
|
const result = installSkill(ctx.dir, mode === "update" ? "present" : void 0, ctx.program);
|
|
@@ -28779,20 +29380,20 @@ function placeSkill(ctx, mode, report3) {
|
|
|
28779
29380
|
return { result, from: before.map((f) => f.version).find(Boolean) || null };
|
|
28780
29381
|
}
|
|
28781
29382
|
function rescueSkillConfig(root, skillDir, report3) {
|
|
28782
|
-
const old =
|
|
29383
|
+
const old = read5(path81.join(skillDir, "config.md"));
|
|
28783
29384
|
if (!old || old.includes("{{")) return;
|
|
28784
|
-
const boardConfig =
|
|
28785
|
-
const current =
|
|
29385
|
+
const boardConfig = path81.join(root, "docs", "kanban", "config.md");
|
|
29386
|
+
const current = read5(boardConfig);
|
|
28786
29387
|
if (current === null) {
|
|
28787
|
-
|
|
28788
|
-
|
|
29388
|
+
fs85.mkdirSync(path81.dirname(boardConfig), { recursive: true });
|
|
29389
|
+
fs85.writeFileSync(boardConfig, old);
|
|
28789
29390
|
report3.did.push("moved your filled-in config.md out of the skill folder to docs/kanban/config.md");
|
|
28790
29391
|
return;
|
|
28791
29392
|
}
|
|
28792
29393
|
if (current === old) return;
|
|
28793
|
-
const aside =
|
|
28794
|
-
if (
|
|
28795
|
-
|
|
29394
|
+
const aside = path81.join(root, "docs", "kanban", "config.from-skill.md");
|
|
29395
|
+
if (fs85.existsSync(aside)) return;
|
|
29396
|
+
fs85.writeFileSync(aside, old);
|
|
28796
29397
|
report3.notes.push(
|
|
28797
29398
|
"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"
|
|
28798
29399
|
);
|
|
@@ -28846,8 +29447,8 @@ async function cmdUpdate2(ctx) {
|
|
|
28846
29447
|
return { updated: ctx.dir, from, to: SKILL_VERSION };
|
|
28847
29448
|
}
|
|
28848
29449
|
async function repairBoard(root, report3) {
|
|
28849
|
-
const board2 =
|
|
28850
|
-
if (!
|
|
29450
|
+
const board2 = path81.join(root, "docs", "kanban");
|
|
29451
|
+
if (!fs85.existsSync(board2)) {
|
|
28851
29452
|
report3.notes.push("no docs/kanban/ here \u2014 run `akb install` to scaffold the board");
|
|
28852
29453
|
return;
|
|
28853
29454
|
}
|
|
@@ -28877,20 +29478,20 @@ function moveRuntimes(root, board2, report3) {
|
|
|
28877
29478
|
}
|
|
28878
29479
|
}
|
|
28879
29480
|
function moveLegacyMemory(board2, report3) {
|
|
28880
|
-
const memory =
|
|
29481
|
+
const memory = path81.join(board2, "memory");
|
|
28881
29482
|
const moved = [];
|
|
28882
29483
|
for (const name of MEMORY_FILES) {
|
|
28883
|
-
const from =
|
|
29484
|
+
const from = path81.join(board2, name);
|
|
28884
29485
|
const st = statOf2(from);
|
|
28885
29486
|
if (!st || !st.isFile()) continue;
|
|
28886
|
-
|
|
28887
|
-
const to =
|
|
28888
|
-
const there =
|
|
29487
|
+
fs85.mkdirSync(memory, { recursive: true });
|
|
29488
|
+
const to = path81.join(memory, name);
|
|
29489
|
+
const there = read5(to);
|
|
28889
29490
|
if (there === null) {
|
|
28890
|
-
|
|
29491
|
+
fs85.renameSync(from, to);
|
|
28891
29492
|
moved.push(name);
|
|
28892
|
-
} else if (there ===
|
|
28893
|
-
|
|
29493
|
+
} else if (there === read5(from)) {
|
|
29494
|
+
fs85.rmSync(from);
|
|
28894
29495
|
moved.push(name);
|
|
28895
29496
|
} else {
|
|
28896
29497
|
report3.notes.push(`both docs/kanban/${name} and docs/kanban/memory/${name} exist and differ \u2014 merge them by hand`);
|
|
@@ -28899,23 +29500,23 @@ function moveLegacyMemory(board2, report3) {
|
|
|
28899
29500
|
if (moved.length) report3.did.push(`moved the memory set into docs/kanban/memory/: ${moved.join(", ")}`);
|
|
28900
29501
|
}
|
|
28901
29502
|
function dropRecordFile(board2) {
|
|
28902
|
-
const file =
|
|
28903
|
-
if (!
|
|
28904
|
-
|
|
29503
|
+
const file = path81.join(board2, "record.csv");
|
|
29504
|
+
if (!fs85.existsSync(file)) return null;
|
|
29505
|
+
fs85.rmSync(file);
|
|
28905
29506
|
return "removed docs/kanban/record.csv (the planning scores it fed are gone)";
|
|
28906
29507
|
}
|
|
28907
29508
|
function dropModuleGoals(board2, report3) {
|
|
28908
|
-
const memory =
|
|
28909
|
-
if (!
|
|
28910
|
-
const root =
|
|
29509
|
+
const memory = path81.join(board2, "memory");
|
|
29510
|
+
if (!fs85.existsSync(memory)) return;
|
|
29511
|
+
const root = read5(path81.join(memory, "goal.md"));
|
|
28911
29512
|
let dropped = 0;
|
|
28912
|
-
for (const entry of
|
|
29513
|
+
for (const entry of fs85.readdirSync(memory, { withFileTypes: true })) {
|
|
28913
29514
|
if (!entry.isDirectory()) continue;
|
|
28914
|
-
const file =
|
|
28915
|
-
const text5 =
|
|
29515
|
+
const file = path81.join(memory, entry.name, "goal.md");
|
|
29516
|
+
const text5 = read5(file);
|
|
28916
29517
|
if (text5 === null) continue;
|
|
28917
29518
|
if (text5 === root || text5.includes(UNFILLED) || !goalWords(text5)) {
|
|
28918
|
-
|
|
29519
|
+
fs85.rmSync(file);
|
|
28919
29520
|
dropped++;
|
|
28920
29521
|
} else {
|
|
28921
29522
|
report3.notes.push(
|
|
@@ -28937,10 +29538,10 @@ var PLAN_LEADS = {
|
|
|
28937
29538
|
var GONE_LEADS = ["software-planner", "planner"];
|
|
28938
29539
|
function planLeads(board2) {
|
|
28939
29540
|
const leads = { ...PLAN_LEADS };
|
|
28940
|
-
const root =
|
|
28941
|
-
for (const entry of
|
|
29541
|
+
const root = path81.join(board2, "workflows");
|
|
29542
|
+
for (const entry of fs85.existsSync(root) ? fs85.readdirSync(root, { withFileTypes: true }) : []) {
|
|
28942
29543
|
if (!entry.isDirectory()) continue;
|
|
28943
|
-
const meta = splitFrontmatter(
|
|
29544
|
+
const meta = splitFrontmatter(read5(path81.join(root, entry.name, "WORKFLOW.md")) ?? "").meta;
|
|
28944
29545
|
const stages = meta === null ? void 0 : parseYamlBlock(meta).stages;
|
|
28945
29546
|
const plan = stages && typeof stages === "object" && !Array.isArray(stages) ? stages.plan : void 0;
|
|
28946
29547
|
const lead = plan && typeof plan === "object" && !Array.isArray(plan) && typeof plan.lead === "string" ? plan.lead.trim() : "";
|
|
@@ -28949,40 +29550,40 @@ function planLeads(board2) {
|
|
|
28949
29550
|
return leads;
|
|
28950
29551
|
}
|
|
28951
29552
|
function movePlanRules(board2, report3) {
|
|
28952
|
-
const rules =
|
|
29553
|
+
const rules = path81.join(board2, "rules");
|
|
28953
29554
|
const moved = [];
|
|
28954
29555
|
for (const [id, leads] of Object.entries(planLeads(board2))) {
|
|
28955
29556
|
for (const lead of leads) {
|
|
28956
|
-
const from =
|
|
28957
|
-
const rule =
|
|
29557
|
+
const from = path81.join(rules, `${lead}.md`);
|
|
29558
|
+
const rule = read5(from)?.trim();
|
|
28958
29559
|
if (!rule) continue;
|
|
28959
|
-
const to =
|
|
28960
|
-
const there =
|
|
29560
|
+
const to = path81.join(rules, "workflows", `${id}.md`);
|
|
29561
|
+
const there = read5(to)?.trim() ?? "";
|
|
28961
29562
|
if (!there.includes(rule)) {
|
|
28962
|
-
|
|
28963
|
-
|
|
29563
|
+
fs85.mkdirSync(path81.dirname(to), { recursive: true });
|
|
29564
|
+
fs85.writeFileSync(to, `${[there, rule].filter(Boolean).join("\n\n")}
|
|
28964
29565
|
`);
|
|
28965
29566
|
moved.push(`${lead} \u2192 ${id}`);
|
|
28966
29567
|
}
|
|
28967
29568
|
}
|
|
28968
29569
|
}
|
|
28969
|
-
for (const lead of GONE_LEADS)
|
|
29570
|
+
for (const lead of GONE_LEADS) fs85.rmSync(path81.join(rules, `${lead}.md`), { force: true });
|
|
28970
29571
|
if (moved.length) report3.did.push(`made each plan agent's rule its workflow's card rule (docs/kanban/rules/workflows/): ${moved.join(", ")}`);
|
|
28971
29572
|
}
|
|
28972
29573
|
function noteOldWorkflows(board2, report3) {
|
|
28973
29574
|
const old = (dir, file, keys) => {
|
|
28974
|
-
const root =
|
|
28975
|
-
return (
|
|
29575
|
+
const root = path81.join(board2, dir);
|
|
29576
|
+
return (fs85.existsSync(root) ? fs85.readdirSync(root, { withFileTypes: true }) : []).filter((e) => e.isDirectory() && keys.test(splitFrontmatter(read5(path81.join(root, e.name, file)) ?? "").meta ?? "")).map((e) => `docs/kanban/${dir}/${e.name}/${file}`);
|
|
28976
29577
|
};
|
|
28977
|
-
const
|
|
28978
|
-
if (
|
|
29578
|
+
const files2 = [...old("workflows", "WORKFLOW.md", /^(stages|delivers):/m), ...old("agents", "AGENT.md", /^\s+(hook|lead):/m)];
|
|
29579
|
+
if (files2.length) {
|
|
28979
29580
|
report3.notes.push(
|
|
28980
|
-
`written in the old form, so not read until rewritten \u2014 list a workflow's agents under \`previews\`, \`land\` and \`scheduled\`, and drop \`hook\`/\`lead\` from each agent (or ask the Workflow builder): ${
|
|
29581
|
+
`written in the old form, so not read until rewritten \u2014 list a workflow's agents under \`previews\`, \`land\` and \`scheduled\`, and drop \`hook\`/\`lead\` from each agent (or ask the Workflow builder): ${files2.join(", ")}`
|
|
28981
29582
|
);
|
|
28982
29583
|
}
|
|
28983
29584
|
}
|
|
28984
29585
|
function checkConfig(board2, report3) {
|
|
28985
|
-
const current =
|
|
29586
|
+
const current = read5(path81.join(board2, "config.md"));
|
|
28986
29587
|
if (!current) return;
|
|
28987
29588
|
const added = missingConfigKeys(current);
|
|
28988
29589
|
if (added.length) {
|
|
@@ -28992,7 +29593,7 @@ function checkConfig(board2, report3) {
|
|
|
28992
29593
|
}
|
|
28993
29594
|
}
|
|
28994
29595
|
function checkModules(board2, report3) {
|
|
28995
|
-
const map2 =
|
|
29596
|
+
const map2 = read5(path81.join(board2, "modules.md"));
|
|
28996
29597
|
if (map2 && map2.includes(UNFILLED)) {
|
|
28997
29598
|
report3.notes.push(
|
|
28998
29599
|
"docs/kanban/modules.md is still blank \u2014 write it from the repo (`akb guide module-map`)"
|
|
@@ -29046,13 +29647,13 @@ function declareSetup(program2, cli) {
|
|
|
29046
29647
|
program2.version(SKILL_VERSION, "-v, --version", "print this version").addHelpText("beforeAll", spelled(cli.program));
|
|
29047
29648
|
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");
|
|
29048
29649
|
const where = (cmd) => {
|
|
29049
|
-
const dir =
|
|
29050
|
-
if (!
|
|
29650
|
+
const dir = path82.resolve(String(cmd.optsWithGlobals().dir ?? cli.cwd));
|
|
29651
|
+
if (!fs86.existsSync(dir)) throw new BoardError(`no such folder: ${dir}`, { kind: "no-such-folder", dir });
|
|
29051
29652
|
return dir;
|
|
29052
29653
|
};
|
|
29053
29654
|
const boardOf = (cmd) => {
|
|
29054
29655
|
const named2 = cmd.optsWithGlobals().board;
|
|
29055
|
-
return named2 ?
|
|
29656
|
+
return named2 ? path82.resolve(cli.cwd, named2) : null;
|
|
29056
29657
|
};
|
|
29057
29658
|
dirOption(program2.command("install")).option("--board <dir>", "put the board here instead of docs/kanban/ (relative to this folder)").summary("scaffold docs/kanban/ \u2014 the board, and nothing else").description(
|
|
29058
29659
|
"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."
|
|
@@ -29125,7 +29726,7 @@ function invokedDirectly() {
|
|
|
29125
29726
|
const entry = process.argv[1];
|
|
29126
29727
|
if (!entry) return false;
|
|
29127
29728
|
try {
|
|
29128
|
-
return
|
|
29729
|
+
return fs87.realpathSync(entry) === fs87.realpathSync(SELF3);
|
|
29129
29730
|
} catch {
|
|
29130
29731
|
return false;
|
|
29131
29732
|
}
|