wildcrane-runner 0.6.18 → 0.6.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/akb/dist/kanban.mjs +1274 -1148
- package/dist/akb/package.json +1 -1
- package/dist/launcher.mjs +20 -5
- package/dist/runner.mjs +48 -11
- package/package.json +1 -1
package/dist/akb/dist/kanban.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// ai4kanban 0.11.
|
|
2
|
+
// ai4kanban 0.11.17 — built by runner/scripts/build.mjs. Do not edit.
|
|
3
3
|
import { createRequire as __ai4kanbanRequire } from 'node:module'
|
|
4
4
|
const require = __ai4kanbanRequire(import.meta.url)
|
|
5
5
|
var __create = Object.create;
|
|
@@ -1167,8 +1167,8 @@ var require_command = __commonJS({
|
|
|
1167
1167
|
"../cli/node_modules/commander/lib/command.js"(exports) {
|
|
1168
1168
|
var EventEmitter = __require("node:events").EventEmitter;
|
|
1169
1169
|
var childProcess = __require("node:child_process");
|
|
1170
|
-
var
|
|
1171
|
-
var
|
|
1170
|
+
var path88 = __require("node:path");
|
|
1171
|
+
var fs87 = __require("node:fs");
|
|
1172
1172
|
var process2 = __require("node:process");
|
|
1173
1173
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
1174
1174
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -2149,7 +2149,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2149
2149
|
* @param {string} subcommandName
|
|
2150
2150
|
*/
|
|
2151
2151
|
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
|
|
2152
|
-
if (
|
|
2152
|
+
if (fs87.existsSync(executableFile)) return;
|
|
2153
2153
|
const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
|
|
2154
2154
|
const executableMissing = `'${executableFile}' does not exist
|
|
2155
2155
|
- if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
@@ -2167,11 +2167,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2167
2167
|
let launchWithNode = false;
|
|
2168
2168
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
2169
2169
|
function findFile(baseDir, baseName) {
|
|
2170
|
-
const localBin =
|
|
2171
|
-
if (
|
|
2172
|
-
if (sourceExt.includes(
|
|
2170
|
+
const localBin = path88.resolve(baseDir, baseName);
|
|
2171
|
+
if (fs87.existsSync(localBin)) return localBin;
|
|
2172
|
+
if (sourceExt.includes(path88.extname(baseName))) return void 0;
|
|
2173
2173
|
const foundExt = sourceExt.find(
|
|
2174
|
-
(ext) =>
|
|
2174
|
+
(ext) => fs87.existsSync(`${localBin}${ext}`)
|
|
2175
2175
|
);
|
|
2176
2176
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
2177
2177
|
return void 0;
|
|
@@ -2183,21 +2183,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2183
2183
|
if (this._scriptPath) {
|
|
2184
2184
|
let resolvedScriptPath;
|
|
2185
2185
|
try {
|
|
2186
|
-
resolvedScriptPath =
|
|
2186
|
+
resolvedScriptPath = fs87.realpathSync(this._scriptPath);
|
|
2187
2187
|
} catch {
|
|
2188
2188
|
resolvedScriptPath = this._scriptPath;
|
|
2189
2189
|
}
|
|
2190
|
-
executableDir =
|
|
2191
|
-
|
|
2190
|
+
executableDir = path88.resolve(
|
|
2191
|
+
path88.dirname(resolvedScriptPath),
|
|
2192
2192
|
executableDir
|
|
2193
2193
|
);
|
|
2194
2194
|
}
|
|
2195
2195
|
if (executableDir) {
|
|
2196
2196
|
let localFile = findFile(executableDir, executableFile);
|
|
2197
2197
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
2198
|
-
const legacyName =
|
|
2198
|
+
const legacyName = path88.basename(
|
|
2199
2199
|
this._scriptPath,
|
|
2200
|
-
|
|
2200
|
+
path88.extname(this._scriptPath)
|
|
2201
2201
|
);
|
|
2202
2202
|
if (legacyName !== this._name) {
|
|
2203
2203
|
localFile = findFile(
|
|
@@ -2208,7 +2208,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2208
2208
|
}
|
|
2209
2209
|
executableFile = localFile || executableFile;
|
|
2210
2210
|
}
|
|
2211
|
-
launchWithNode = sourceExt.includes(
|
|
2211
|
+
launchWithNode = sourceExt.includes(path88.extname(executableFile));
|
|
2212
2212
|
let proc;
|
|
2213
2213
|
if (process2.platform !== "win32") {
|
|
2214
2214
|
if (launchWithNode) {
|
|
@@ -3055,7 +3055,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3055
3055
|
* @return {Command}
|
|
3056
3056
|
*/
|
|
3057
3057
|
nameFromFilename(filename) {
|
|
3058
|
-
this._name =
|
|
3058
|
+
this._name = path88.basename(filename, path88.extname(filename));
|
|
3059
3059
|
return this;
|
|
3060
3060
|
}
|
|
3061
3061
|
/**
|
|
@@ -3069,9 +3069,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3069
3069
|
* @param {string} [path]
|
|
3070
3070
|
* @return {(string|null|Command)}
|
|
3071
3071
|
*/
|
|
3072
|
-
executableDir(
|
|
3073
|
-
if (
|
|
3074
|
-
this._executableDir =
|
|
3072
|
+
executableDir(path89) {
|
|
3073
|
+
if (path89 === void 0) return this._executableDir;
|
|
3074
|
+
this._executableDir = path89;
|
|
3075
3075
|
return this;
|
|
3076
3076
|
}
|
|
3077
3077
|
/**
|
|
@@ -3754,192 +3754,6 @@ var init_env = __esm({
|
|
|
3754
3754
|
}
|
|
3755
3755
|
});
|
|
3756
3756
|
|
|
3757
|
-
// ../cli/src/lib/lock.ts
|
|
3758
|
-
import { randomUUID } from "node:crypto";
|
|
3759
|
-
import fs4 from "node:fs";
|
|
3760
|
-
import path5 from "node:path";
|
|
3761
|
-
function sleep(ms) {
|
|
3762
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
3763
|
-
}
|
|
3764
|
-
function ageOf(dir) {
|
|
3765
|
-
try {
|
|
3766
|
-
return Date.now() - fs4.statSync(dir).mtimeMs;
|
|
3767
|
-
} catch {
|
|
3768
|
-
return 0;
|
|
3769
|
-
}
|
|
3770
|
-
}
|
|
3771
|
-
function pidAlive(pid) {
|
|
3772
|
-
if (!pid) return false;
|
|
3773
|
-
try {
|
|
3774
|
-
process.kill(pid, 0);
|
|
3775
|
-
return true;
|
|
3776
|
-
} catch (e) {
|
|
3777
|
-
return e.code === "EPERM";
|
|
3778
|
-
}
|
|
3779
|
-
}
|
|
3780
|
-
function ownerOf(dir) {
|
|
3781
|
-
try {
|
|
3782
|
-
const pid = Number(fs4.readFileSync(path5.join(dir, OWNER), "utf8").trim());
|
|
3783
|
-
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
3784
|
-
} catch {
|
|
3785
|
-
return void 0;
|
|
3786
|
-
}
|
|
3787
|
-
}
|
|
3788
|
-
function claimBreak(dir) {
|
|
3789
|
-
try {
|
|
3790
|
-
fs4.mkdirSync(path5.join(dir, BREAKING));
|
|
3791
|
-
return true;
|
|
3792
|
-
} catch {
|
|
3793
|
-
return false;
|
|
3794
|
-
}
|
|
3795
|
-
}
|
|
3796
|
-
function removeClaim(dir, token3) {
|
|
3797
|
-
if (token3) {
|
|
3798
|
-
try {
|
|
3799
|
-
fs4.unlinkSync(path5.join(dir, token3));
|
|
3800
|
-
} catch (err) {
|
|
3801
|
-
if (err.code !== "ENOENT") throw err;
|
|
3802
|
-
}
|
|
3803
|
-
}
|
|
3804
|
-
try {
|
|
3805
|
-
fs4.rmdirSync(dir);
|
|
3806
|
-
return true;
|
|
3807
|
-
} catch (err) {
|
|
3808
|
-
const code = err.code;
|
|
3809
|
-
if (code === "ENOENT") return true;
|
|
3810
|
-
if (code === "ENOTEMPTY" || code === "EEXIST") return false;
|
|
3811
|
-
throw err;
|
|
3812
|
-
}
|
|
3813
|
-
}
|
|
3814
|
-
function tryLock(dir) {
|
|
3815
|
-
fs4.mkdirSync(path5.dirname(dir), { recursive: true });
|
|
3816
|
-
const token3 = `${process.pid}-${randomUUID()}`;
|
|
3817
|
-
const candidate = fs4.mkdtempSync(`${dir}.`);
|
|
3818
|
-
try {
|
|
3819
|
-
fs4.writeFileSync(path5.join(candidate, token3), "");
|
|
3820
|
-
for (; ; ) {
|
|
3821
|
-
try {
|
|
3822
|
-
fs4.renameSync(candidate, dir);
|
|
3823
|
-
return () => {
|
|
3824
|
-
removeClaim(dir, token3);
|
|
3825
|
-
};
|
|
3826
|
-
} catch (err) {
|
|
3827
|
-
const code = err.code;
|
|
3828
|
-
if (code !== "ENOTEMPTY" && code !== "EEXIST") throw err;
|
|
3829
|
-
}
|
|
3830
|
-
let entries;
|
|
3831
|
-
try {
|
|
3832
|
-
entries = fs4.readdirSync(dir);
|
|
3833
|
-
} catch (err) {
|
|
3834
|
-
if (err.code === "ENOENT") continue;
|
|
3835
|
-
throw err;
|
|
3836
|
-
}
|
|
3837
|
-
if (entries.length) {
|
|
3838
|
-
if (entries.length !== 1 || !/^\d+-[a-f0-9-]{36}$/.test(entries[0])) return void 0;
|
|
3839
|
-
const owner = Number(entries[0].split("-")[0]);
|
|
3840
|
-
if (pidAlive(owner)) return void 0;
|
|
3841
|
-
}
|
|
3842
|
-
if (!removeClaim(dir, entries[0])) return void 0;
|
|
3843
|
-
}
|
|
3844
|
-
} finally {
|
|
3845
|
-
fs4.rmSync(candidate, { recursive: true, force: true });
|
|
3846
|
-
}
|
|
3847
|
-
}
|
|
3848
|
-
function withLock(dir, what, fn) {
|
|
3849
|
-
const until = Date.now() + WAIT_MS;
|
|
3850
|
-
let held4;
|
|
3851
|
-
for (; ; ) {
|
|
3852
|
-
try {
|
|
3853
|
-
fs4.mkdirSync(dir, { recursive: false });
|
|
3854
|
-
try {
|
|
3855
|
-
fs4.writeFileSync(path5.join(dir, OWNER), `${process.pid}
|
|
3856
|
-
`);
|
|
3857
|
-
} catch {
|
|
3858
|
-
}
|
|
3859
|
-
break;
|
|
3860
|
-
} catch (err) {
|
|
3861
|
-
const code = err.code;
|
|
3862
|
-
if (code === "ENOENT") {
|
|
3863
|
-
fs4.mkdirSync(path5.dirname(dir), { recursive: true });
|
|
3864
|
-
continue;
|
|
3865
|
-
}
|
|
3866
|
-
if (code !== "EEXIST") throw err;
|
|
3867
|
-
if (ageOf(dir) > STALE_MS) {
|
|
3868
|
-
fs4.rmSync(dir, { recursive: true, force: true });
|
|
3869
|
-
continue;
|
|
3870
|
-
}
|
|
3871
|
-
held4 = ownerOf(dir);
|
|
3872
|
-
if (held4 !== void 0 && !pidAlive(held4) && claimBreak(dir)) {
|
|
3873
|
-
fs4.rmSync(dir, { recursive: true, force: true });
|
|
3874
|
-
continue;
|
|
3875
|
-
}
|
|
3876
|
-
if (Date.now() > until) {
|
|
3877
|
-
die(
|
|
3878
|
-
`another command is ${what}${held4 ? ` (process ${held4})` : ""} and did not finish in ${WAIT_MS / 1e3}s. If nothing else is running, remove ${rel(dir)}/ and try again.`,
|
|
3879
|
-
"board-busy"
|
|
3880
|
-
);
|
|
3881
|
-
}
|
|
3882
|
-
sleep(RETRY_MS);
|
|
3883
|
-
}
|
|
3884
|
-
}
|
|
3885
|
-
try {
|
|
3886
|
-
return fn();
|
|
3887
|
-
} finally {
|
|
3888
|
-
const owner = ownerOf(dir);
|
|
3889
|
-
if (owner === void 0 || owner === process.pid) fs4.rmSync(dir, { recursive: true, force: true });
|
|
3890
|
-
}
|
|
3891
|
-
}
|
|
3892
|
-
function withBoardLock(fn) {
|
|
3893
|
-
if (!fs4.existsSync(KANBAN)) return fn();
|
|
3894
|
-
if (depth > 0) return fn();
|
|
3895
|
-
try {
|
|
3896
|
-
ensureAkbDir();
|
|
3897
|
-
} catch {
|
|
3898
|
-
}
|
|
3899
|
-
depth++;
|
|
3900
|
-
try {
|
|
3901
|
-
return withLock(LOCK, "writing this board", fn);
|
|
3902
|
-
} finally {
|
|
3903
|
-
depth--;
|
|
3904
|
-
}
|
|
3905
|
-
}
|
|
3906
|
-
var WAIT_MS, STALE_MS, RETRY_MS, OWNER, BREAKING, depth;
|
|
3907
|
-
var init_lock = __esm({
|
|
3908
|
-
"../cli/src/lib/lock.ts"() {
|
|
3909
|
-
"use strict";
|
|
3910
|
-
init_paths();
|
|
3911
|
-
WAIT_MS = 1e4;
|
|
3912
|
-
STALE_MS = 6e4;
|
|
3913
|
-
RETRY_MS = 25;
|
|
3914
|
-
OWNER = "owner";
|
|
3915
|
-
BREAKING = "breaking";
|
|
3916
|
-
depth = 0;
|
|
3917
|
-
}
|
|
3918
|
-
});
|
|
3919
|
-
|
|
3920
|
-
// ../cli/src/lib/agent/creation-lock.ts
|
|
3921
|
-
import path6 from "node:path";
|
|
3922
|
-
function withCreationLock(fn) {
|
|
3923
|
-
if (depth2) return fn();
|
|
3924
|
-
return withLock(path6.join(SESSIONS_DIR, "creation.lock"), "changing card creation", () => {
|
|
3925
|
-
depth2++;
|
|
3926
|
-
try {
|
|
3927
|
-
return fn();
|
|
3928
|
-
} finally {
|
|
3929
|
-
depth2--;
|
|
3930
|
-
}
|
|
3931
|
-
});
|
|
3932
|
-
}
|
|
3933
|
-
var depth2;
|
|
3934
|
-
var init_creation_lock = __esm({
|
|
3935
|
-
"../cli/src/lib/agent/creation-lock.ts"() {
|
|
3936
|
-
"use strict";
|
|
3937
|
-
init_lock();
|
|
3938
|
-
init_paths();
|
|
3939
|
-
depth2 = 0;
|
|
3940
|
-
}
|
|
3941
|
-
});
|
|
3942
|
-
|
|
3943
3757
|
// ../cli/src/lib/cloud/config.ts
|
|
3944
3758
|
function cloudEndpoints() {
|
|
3945
3759
|
return {
|
|
@@ -3965,12 +3779,12 @@ var init_config = __esm({
|
|
|
3965
3779
|
});
|
|
3966
3780
|
|
|
3967
3781
|
// ../cli/src/lib/cloud/session.ts
|
|
3968
|
-
import
|
|
3969
|
-
import
|
|
3782
|
+
import fs4 from "node:fs";
|
|
3783
|
+
import path5 from "node:path";
|
|
3970
3784
|
function readSession() {
|
|
3971
3785
|
let raw;
|
|
3972
3786
|
try {
|
|
3973
|
-
raw =
|
|
3787
|
+
raw = fs4.readFileSync(sessionFile(), "utf8");
|
|
3974
3788
|
} catch {
|
|
3975
3789
|
return null;
|
|
3976
3790
|
}
|
|
@@ -3984,15 +3798,15 @@ function readSession() {
|
|
|
3984
3798
|
}
|
|
3985
3799
|
}
|
|
3986
3800
|
function writeSession(session) {
|
|
3987
|
-
|
|
3801
|
+
fs4.mkdirSync(machineHome(), { recursive: true, mode: 448 });
|
|
3988
3802
|
const tmp = `${sessionFile()}.${process.pid}.tmp`;
|
|
3989
|
-
|
|
3803
|
+
fs4.writeFileSync(tmp, `${JSON.stringify(session, null, 2)}
|
|
3990
3804
|
`, { mode: 384 });
|
|
3991
|
-
|
|
3805
|
+
fs4.renameSync(tmp, sessionFile());
|
|
3992
3806
|
}
|
|
3993
3807
|
function clearSession() {
|
|
3994
3808
|
try {
|
|
3995
|
-
|
|
3809
|
+
fs4.rmSync(sessionFile());
|
|
3996
3810
|
} catch {
|
|
3997
3811
|
}
|
|
3998
3812
|
}
|
|
@@ -4060,42 +3874,42 @@ async function takeLock() {
|
|
|
4060
3874
|
const until = Date.now() + LOCK_WAIT_MS;
|
|
4061
3875
|
for (; ; ) {
|
|
4062
3876
|
try {
|
|
4063
|
-
|
|
4064
|
-
|
|
3877
|
+
fs4.mkdirSync(machineHome(), { recursive: true, mode: 448 });
|
|
3878
|
+
fs4.mkdirSync(lockDir());
|
|
4065
3879
|
try {
|
|
4066
|
-
|
|
3880
|
+
fs4.writeFileSync(lockOwner(), `${process.pid}
|
|
4067
3881
|
`);
|
|
4068
3882
|
} catch {
|
|
4069
3883
|
}
|
|
4070
3884
|
return () => {
|
|
4071
3885
|
try {
|
|
4072
|
-
|
|
3886
|
+
fs4.rmSync(lockDir(), { recursive: true, force: true });
|
|
4073
3887
|
} catch {
|
|
4074
3888
|
}
|
|
4075
3889
|
};
|
|
4076
3890
|
} catch (err) {
|
|
4077
3891
|
if (err.code !== "EEXIST") throw err;
|
|
4078
3892
|
if (stale()) {
|
|
4079
|
-
|
|
3893
|
+
fs4.rmSync(lockDir(), { recursive: true, force: true });
|
|
4080
3894
|
continue;
|
|
4081
3895
|
}
|
|
4082
3896
|
if (Date.now() > until) return () => {
|
|
4083
3897
|
};
|
|
4084
|
-
await
|
|
3898
|
+
await sleep(LOCK_RETRY_MS);
|
|
4085
3899
|
}
|
|
4086
3900
|
}
|
|
4087
3901
|
}
|
|
4088
3902
|
function stale() {
|
|
4089
3903
|
let held4;
|
|
4090
3904
|
try {
|
|
4091
|
-
const pid = Number(
|
|
3905
|
+
const pid = Number(fs4.readFileSync(lockOwner(), "utf8").trim());
|
|
4092
3906
|
held4 = Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
4093
3907
|
} catch {
|
|
4094
3908
|
held4 = void 0;
|
|
4095
3909
|
}
|
|
4096
3910
|
if (held4 !== void 0 && !alive(held4)) return true;
|
|
4097
3911
|
try {
|
|
4098
|
-
return Date.now() -
|
|
3912
|
+
return Date.now() - fs4.statSync(lockDir()).mtimeMs > LOCK_STALE_MS;
|
|
4099
3913
|
} catch {
|
|
4100
3914
|
return false;
|
|
4101
3915
|
}
|
|
@@ -4108,7 +3922,7 @@ function alive(pid) {
|
|
|
4108
3922
|
return e.code === "EPERM";
|
|
4109
3923
|
}
|
|
4110
3924
|
}
|
|
4111
|
-
var REFRESH_MARGIN_MS, LOCK_STALE_MS, LOCK_WAIT_MS, LOCK_RETRY_MS, sessionFile, lockDir, lockOwner, expiringSoon,
|
|
3925
|
+
var REFRESH_MARGIN_MS, LOCK_STALE_MS, LOCK_WAIT_MS, LOCK_RETRY_MS, sessionFile, lockDir, lockOwner, expiringSoon, sleep;
|
|
4112
3926
|
var init_session = __esm({
|
|
4113
3927
|
"../cli/src/lib/cloud/session.ts"() {
|
|
4114
3928
|
"use strict";
|
|
@@ -4118,11 +3932,234 @@ var init_session = __esm({
|
|
|
4118
3932
|
LOCK_STALE_MS = 3e4;
|
|
4119
3933
|
LOCK_WAIT_MS = 2e4;
|
|
4120
3934
|
LOCK_RETRY_MS = 50;
|
|
4121
|
-
sessionFile = () =>
|
|
4122
|
-
lockDir = () =>
|
|
4123
|
-
lockOwner = () =>
|
|
3935
|
+
sessionFile = () => path5.join(machineHome(), "session.json");
|
|
3936
|
+
lockDir = () => path5.join(machineHome(), "session.lock");
|
|
3937
|
+
lockOwner = () => path5.join(lockDir(), "owner");
|
|
4124
3938
|
expiringSoon = (session) => session.expiresAt - REFRESH_MARGIN_MS <= Date.now();
|
|
4125
|
-
|
|
3939
|
+
sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
3940
|
+
}
|
|
3941
|
+
});
|
|
3942
|
+
|
|
3943
|
+
// ../cli/src/lib/cloud/execution.ts
|
|
3944
|
+
function executionOf(value = process.env.AI4KANBAN_EXECUTION) {
|
|
3945
|
+
try {
|
|
3946
|
+
const held4 = typeof value === "string" ? JSON.parse(value) : value;
|
|
3947
|
+
if (held4 && UUID.test(held4.job) && UUID.test(held4.machine)) return { job: held4.job, machine: held4.machine };
|
|
3948
|
+
} catch {
|
|
3949
|
+
}
|
|
3950
|
+
return void 0;
|
|
3951
|
+
}
|
|
3952
|
+
async function executionHeld(execution) {
|
|
3953
|
+
if (!execution) return true;
|
|
3954
|
+
try {
|
|
3955
|
+
const auth = await accessToken();
|
|
3956
|
+
if (!auth.ok) return false;
|
|
3957
|
+
const response = await fetch(`${cloudEndpoints().api}/v1/runner/jobs/${execution.job}/renew`, {
|
|
3958
|
+
method: "POST",
|
|
3959
|
+
headers: { authorization: `Bearer ${auth.token}`, "content-type": "application/json" },
|
|
3960
|
+
body: JSON.stringify({ machineId: execution.machine }),
|
|
3961
|
+
signal: AbortSignal.timeout(2e4)
|
|
3962
|
+
});
|
|
3963
|
+
if (!response.ok) return false;
|
|
3964
|
+
const held4 = await response.json();
|
|
3965
|
+
return held4.renewed === true && held4.stop !== true;
|
|
3966
|
+
} catch {
|
|
3967
|
+
return false;
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
var UUID;
|
|
3971
|
+
var init_execution = __esm({
|
|
3972
|
+
"../cli/src/lib/cloud/execution.ts"() {
|
|
3973
|
+
"use strict";
|
|
3974
|
+
init_config();
|
|
3975
|
+
init_session();
|
|
3976
|
+
UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3977
|
+
}
|
|
3978
|
+
});
|
|
3979
|
+
|
|
3980
|
+
// ../cli/src/lib/lock.ts
|
|
3981
|
+
import { randomUUID } from "node:crypto";
|
|
3982
|
+
import fs5 from "node:fs";
|
|
3983
|
+
import path6 from "node:path";
|
|
3984
|
+
function sleep2(ms) {
|
|
3985
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
3986
|
+
}
|
|
3987
|
+
function ageOf(dir) {
|
|
3988
|
+
try {
|
|
3989
|
+
return Date.now() - fs5.statSync(dir).mtimeMs;
|
|
3990
|
+
} catch {
|
|
3991
|
+
return 0;
|
|
3992
|
+
}
|
|
3993
|
+
}
|
|
3994
|
+
function pidAlive(pid) {
|
|
3995
|
+
if (!pid) return false;
|
|
3996
|
+
try {
|
|
3997
|
+
process.kill(pid, 0);
|
|
3998
|
+
return true;
|
|
3999
|
+
} catch (e) {
|
|
4000
|
+
return e.code === "EPERM";
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
function ownerOf(dir) {
|
|
4004
|
+
try {
|
|
4005
|
+
const pid = Number(fs5.readFileSync(path6.join(dir, OWNER), "utf8").trim());
|
|
4006
|
+
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
4007
|
+
} catch {
|
|
4008
|
+
return void 0;
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
function claimBreak(dir) {
|
|
4012
|
+
try {
|
|
4013
|
+
fs5.mkdirSync(path6.join(dir, BREAKING));
|
|
4014
|
+
return true;
|
|
4015
|
+
} catch {
|
|
4016
|
+
return false;
|
|
4017
|
+
}
|
|
4018
|
+
}
|
|
4019
|
+
function removeClaim(dir, token3) {
|
|
4020
|
+
if (token3) {
|
|
4021
|
+
try {
|
|
4022
|
+
fs5.unlinkSync(path6.join(dir, token3));
|
|
4023
|
+
} catch (err) {
|
|
4024
|
+
if (err.code !== "ENOENT") throw err;
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
try {
|
|
4028
|
+
fs5.rmdirSync(dir);
|
|
4029
|
+
return true;
|
|
4030
|
+
} catch (err) {
|
|
4031
|
+
const code = err.code;
|
|
4032
|
+
if (code === "ENOENT") return true;
|
|
4033
|
+
if (code === "ENOTEMPTY" || code === "EEXIST") return false;
|
|
4034
|
+
throw err;
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
function tryLock(dir) {
|
|
4038
|
+
fs5.mkdirSync(path6.dirname(dir), { recursive: true });
|
|
4039
|
+
const token3 = `${process.pid}-${randomUUID()}`;
|
|
4040
|
+
const candidate = fs5.mkdtempSync(`${dir}.`);
|
|
4041
|
+
try {
|
|
4042
|
+
fs5.writeFileSync(path6.join(candidate, token3), "");
|
|
4043
|
+
for (; ; ) {
|
|
4044
|
+
try {
|
|
4045
|
+
fs5.renameSync(candidate, dir);
|
|
4046
|
+
return () => {
|
|
4047
|
+
removeClaim(dir, token3);
|
|
4048
|
+
};
|
|
4049
|
+
} catch (err) {
|
|
4050
|
+
const code = err.code;
|
|
4051
|
+
if (code !== "ENOTEMPTY" && code !== "EEXIST") throw err;
|
|
4052
|
+
}
|
|
4053
|
+
let entries;
|
|
4054
|
+
try {
|
|
4055
|
+
entries = fs5.readdirSync(dir);
|
|
4056
|
+
} catch (err) {
|
|
4057
|
+
if (err.code === "ENOENT") continue;
|
|
4058
|
+
throw err;
|
|
4059
|
+
}
|
|
4060
|
+
if (entries.length) {
|
|
4061
|
+
if (entries.length !== 1 || !/^\d+-[a-f0-9-]{36}$/.test(entries[0])) return void 0;
|
|
4062
|
+
const owner = Number(entries[0].split("-")[0]);
|
|
4063
|
+
if (pidAlive(owner)) return void 0;
|
|
4064
|
+
}
|
|
4065
|
+
if (!removeClaim(dir, entries[0])) return void 0;
|
|
4066
|
+
}
|
|
4067
|
+
} finally {
|
|
4068
|
+
fs5.rmSync(candidate, { recursive: true, force: true });
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
function withLock(dir, what, fn) {
|
|
4072
|
+
const until = Date.now() + WAIT_MS;
|
|
4073
|
+
let held4;
|
|
4074
|
+
for (; ; ) {
|
|
4075
|
+
try {
|
|
4076
|
+
fs5.mkdirSync(dir, { recursive: false });
|
|
4077
|
+
try {
|
|
4078
|
+
fs5.writeFileSync(path6.join(dir, OWNER), `${process.pid}
|
|
4079
|
+
`);
|
|
4080
|
+
} catch {
|
|
4081
|
+
}
|
|
4082
|
+
break;
|
|
4083
|
+
} catch (err) {
|
|
4084
|
+
const code = err.code;
|
|
4085
|
+
if (code === "ENOENT") {
|
|
4086
|
+
fs5.mkdirSync(path6.dirname(dir), { recursive: true });
|
|
4087
|
+
continue;
|
|
4088
|
+
}
|
|
4089
|
+
if (code !== "EEXIST") throw err;
|
|
4090
|
+
if (ageOf(dir) > STALE_MS) {
|
|
4091
|
+
fs5.rmSync(dir, { recursive: true, force: true });
|
|
4092
|
+
continue;
|
|
4093
|
+
}
|
|
4094
|
+
held4 = ownerOf(dir);
|
|
4095
|
+
if (held4 !== void 0 && !pidAlive(held4) && claimBreak(dir)) {
|
|
4096
|
+
fs5.rmSync(dir, { recursive: true, force: true });
|
|
4097
|
+
continue;
|
|
4098
|
+
}
|
|
4099
|
+
if (Date.now() > until) {
|
|
4100
|
+
die(
|
|
4101
|
+
`another command is ${what}${held4 ? ` (process ${held4})` : ""} and did not finish in ${WAIT_MS / 1e3}s. If nothing else is running, remove ${rel(dir)}/ and try again.`,
|
|
4102
|
+
"board-busy"
|
|
4103
|
+
);
|
|
4104
|
+
}
|
|
4105
|
+
sleep2(RETRY_MS);
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
try {
|
|
4109
|
+
return fn();
|
|
4110
|
+
} finally {
|
|
4111
|
+
const owner = ownerOf(dir);
|
|
4112
|
+
if (owner === void 0 || owner === process.pid) fs5.rmSync(dir, { recursive: true, force: true });
|
|
4113
|
+
}
|
|
4114
|
+
}
|
|
4115
|
+
function withBoardLock(fn) {
|
|
4116
|
+
if (!fs5.existsSync(KANBAN)) return fn();
|
|
4117
|
+
if (depth > 0) return fn();
|
|
4118
|
+
try {
|
|
4119
|
+
ensureAkbDir();
|
|
4120
|
+
} catch {
|
|
4121
|
+
}
|
|
4122
|
+
depth++;
|
|
4123
|
+
try {
|
|
4124
|
+
return withLock(LOCK, "writing this board", fn);
|
|
4125
|
+
} finally {
|
|
4126
|
+
depth--;
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
var WAIT_MS, STALE_MS, RETRY_MS, OWNER, BREAKING, depth;
|
|
4130
|
+
var init_lock = __esm({
|
|
4131
|
+
"../cli/src/lib/lock.ts"() {
|
|
4132
|
+
"use strict";
|
|
4133
|
+
init_paths();
|
|
4134
|
+
WAIT_MS = 1e4;
|
|
4135
|
+
STALE_MS = 6e4;
|
|
4136
|
+
RETRY_MS = 25;
|
|
4137
|
+
OWNER = "owner";
|
|
4138
|
+
BREAKING = "breaking";
|
|
4139
|
+
depth = 0;
|
|
4140
|
+
}
|
|
4141
|
+
});
|
|
4142
|
+
|
|
4143
|
+
// ../cli/src/lib/agent/creation-lock.ts
|
|
4144
|
+
import path7 from "node:path";
|
|
4145
|
+
function withCreationLock(fn) {
|
|
4146
|
+
if (depth2) return fn();
|
|
4147
|
+
return withLock(path7.join(SESSIONS_DIR, "creation.lock"), "changing card creation", () => {
|
|
4148
|
+
depth2++;
|
|
4149
|
+
try {
|
|
4150
|
+
return fn();
|
|
4151
|
+
} finally {
|
|
4152
|
+
depth2--;
|
|
4153
|
+
}
|
|
4154
|
+
});
|
|
4155
|
+
}
|
|
4156
|
+
var depth2;
|
|
4157
|
+
var init_creation_lock = __esm({
|
|
4158
|
+
"../cli/src/lib/agent/creation-lock.ts"() {
|
|
4159
|
+
"use strict";
|
|
4160
|
+
init_lock();
|
|
4161
|
+
init_paths();
|
|
4162
|
+
depth2 = 0;
|
|
4126
4163
|
}
|
|
4127
4164
|
});
|
|
4128
4165
|
|
|
@@ -5480,7 +5517,7 @@ var init_package = __esm({
|
|
|
5480
5517
|
"../cli/package.json"() {
|
|
5481
5518
|
package_default = {
|
|
5482
5519
|
name: "wildcrane",
|
|
5483
|
-
version: "0.11.
|
|
5520
|
+
version: "0.11.17",
|
|
5484
5521
|
akbContract: 5,
|
|
5485
5522
|
description: "WildCrane's `akb` command \u2014 scaffold the Markdown board under docs/kanban/, put an agent on a card, and carry the flows the agent works by.",
|
|
5486
5523
|
keywords: [
|
|
@@ -8321,10 +8358,10 @@ function findRollout(threadId) {
|
|
|
8321
8358
|
}
|
|
8322
8359
|
for (const name of names4) {
|
|
8323
8360
|
if (!name.startsWith("rollout-") || !name.endsWith(tail2)) continue;
|
|
8324
|
-
const
|
|
8361
|
+
const path88 = join(dir, name);
|
|
8325
8362
|
try {
|
|
8326
|
-
const at = statSync(
|
|
8327
|
-
if (!newest2 || at > newest2.at) newest2 = { path:
|
|
8363
|
+
const at = statSync(path88).mtimeMs;
|
|
8364
|
+
if (!newest2 || at > newest2.at) newest2 = { path: path88, at };
|
|
8328
8365
|
} catch {
|
|
8329
8366
|
}
|
|
8330
8367
|
}
|
|
@@ -8332,11 +8369,11 @@ function findRollout(threadId) {
|
|
|
8332
8369
|
return newest2?.path;
|
|
8333
8370
|
}
|
|
8334
8371
|
function readCodexRunFacts(threadId) {
|
|
8335
|
-
const
|
|
8336
|
-
if (!
|
|
8372
|
+
const path88 = findRollout(threadId);
|
|
8373
|
+
if (!path88) return void 0;
|
|
8337
8374
|
let raw;
|
|
8338
8375
|
try {
|
|
8339
|
-
raw = readFileSync(
|
|
8376
|
+
raw = readFileSync(path88, "utf8");
|
|
8340
8377
|
} catch {
|
|
8341
8378
|
return void 0;
|
|
8342
8379
|
}
|
|
@@ -8464,8 +8501,8 @@ function renderCompleted(item) {
|
|
|
8464
8501
|
const changes = Array.isArray(item.changes) ? item.changes : [];
|
|
8465
8502
|
return changes.map((raw) => {
|
|
8466
8503
|
const change2 = obj(raw);
|
|
8467
|
-
const
|
|
8468
|
-
return
|
|
8504
|
+
const path88 = str2(change2.path);
|
|
8505
|
+
return path88 ? `\u23FA ${str2(change2.kind) || "change"}(${path88})
|
|
8469
8506
|
` : "";
|
|
8470
8507
|
}).join("");
|
|
8471
8508
|
}
|
|
@@ -8727,10 +8764,10 @@ function readKimiRunFacts(sessionId) {
|
|
|
8727
8764
|
const total = { input: 0, cacheCreation: 0, cacheRead: 0, output: 0 };
|
|
8728
8765
|
let model;
|
|
8729
8766
|
let holding = 0;
|
|
8730
|
-
for (const { path:
|
|
8767
|
+
for (const { path: path88, main } of wireFiles(entry.sessionDir)) {
|
|
8731
8768
|
let raw;
|
|
8732
8769
|
try {
|
|
8733
|
-
raw = readFileSync2(
|
|
8770
|
+
raw = readFileSync2(path88, "utf8");
|
|
8734
8771
|
} catch {
|
|
8735
8772
|
continue;
|
|
8736
8773
|
}
|
|
@@ -9011,8 +9048,8 @@ var init_tail = __esm({
|
|
|
9011
9048
|
import fs24 from "node:fs";
|
|
9012
9049
|
function whereHint(update2, title) {
|
|
9013
9050
|
const locations = Array.isArray(update2.locations) ? update2.locations : [];
|
|
9014
|
-
const
|
|
9015
|
-
return
|
|
9051
|
+
const path88 = str2(obj(locations[0]).path);
|
|
9052
|
+
return path88 && !title.includes(path88) ? hint(path88) : "";
|
|
9016
9053
|
}
|
|
9017
9054
|
function saidBy(update2) {
|
|
9018
9055
|
const content = Array.isArray(update2.content) ? update2.content : [];
|
|
@@ -9998,27 +10035,40 @@ import crypto3 from "node:crypto";
|
|
|
9998
10035
|
import fs25 from "node:fs";
|
|
9999
10036
|
import os4 from "node:os";
|
|
10000
10037
|
import path28 from "node:path";
|
|
10038
|
+
function spawnGit(args, options) {
|
|
10039
|
+
const out = spawnSync3("git", args, {
|
|
10040
|
+
...options,
|
|
10041
|
+
encoding: "utf8",
|
|
10042
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
10043
|
+
timeout: gitLimits.timeoutMs,
|
|
10044
|
+
killSignal: "SIGKILL"
|
|
10045
|
+
});
|
|
10046
|
+
if (out.error?.code === "ETIMEDOUT") throw new GitTimeoutError(`Git ${args[0]} timed out after ${gitLimits.timeoutMs / 1e3}s; the command was killed.`);
|
|
10047
|
+
return out;
|
|
10048
|
+
}
|
|
10001
10049
|
function git(args, cwd = REPO_ROOT) {
|
|
10002
10050
|
try {
|
|
10003
|
-
const out =
|
|
10051
|
+
const out = spawnGit(args, { cwd, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true });
|
|
10004
10052
|
if (out.error || out.status !== 0) return null;
|
|
10005
10053
|
return out.stdout;
|
|
10006
|
-
} catch {
|
|
10054
|
+
} catch (error) {
|
|
10055
|
+
if (error instanceof GitTimeoutError) throw error;
|
|
10007
10056
|
return null;
|
|
10008
10057
|
}
|
|
10009
10058
|
}
|
|
10010
10059
|
function gitDiff(args, cwd = REPO_ROOT) {
|
|
10011
10060
|
try {
|
|
10012
|
-
const out =
|
|
10061
|
+
const out = spawnGit(args, { cwd, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true });
|
|
10013
10062
|
if (out.error || out.status !== 0 && out.status !== 1) return null;
|
|
10014
10063
|
return out.stdout;
|
|
10015
|
-
} catch {
|
|
10064
|
+
} catch (error) {
|
|
10065
|
+
if (error instanceof GitTimeoutError) throw error;
|
|
10016
10066
|
return null;
|
|
10017
10067
|
}
|
|
10018
10068
|
}
|
|
10019
10069
|
function tryGit(args, cwd = REPO_ROOT, env2) {
|
|
10020
10070
|
try {
|
|
10021
|
-
const out =
|
|
10071
|
+
const out = spawnGit(args, {
|
|
10022
10072
|
cwd,
|
|
10023
10073
|
encoding: "utf8",
|
|
10024
10074
|
maxBuffer: MAX_OUTPUT,
|
|
@@ -10029,6 +10079,7 @@ function tryGit(args, cwd = REPO_ROOT, env2) {
|
|
|
10029
10079
|
if (out.status !== 0) return { ok: false, why: (out.stderr || out.stdout || "").trim() };
|
|
10030
10080
|
return { ok: true };
|
|
10031
10081
|
} catch (e) {
|
|
10082
|
+
if (e instanceof GitTimeoutError) throw e;
|
|
10032
10083
|
return { ok: false, why: e instanceof Error ? e.message : String(e) };
|
|
10033
10084
|
}
|
|
10034
10085
|
}
|
|
@@ -10037,7 +10088,7 @@ function boardManagedPaths() {
|
|
|
10037
10088
|
}
|
|
10038
10089
|
function outsideBoardForAdd(cwd = REPO_ROOT) {
|
|
10039
10090
|
const kept2 = boardManagedPaths().filter(
|
|
10040
|
-
(p) =>
|
|
10091
|
+
(p) => spawnGit(["check-ignore", "-q", p], { cwd, windowsHide: true }).status !== 0
|
|
10041
10092
|
);
|
|
10042
10093
|
return ["--", ".", ...kept2.map((p) => `:(exclude)${p}`)];
|
|
10043
10094
|
}
|
|
@@ -10157,10 +10208,10 @@ function treeMark(base, cwd = REPO_ROOT) {
|
|
|
10157
10208
|
const index = path28.join(ensureAkbDir(), `index-${process.pid}-${Date.now()}`);
|
|
10158
10209
|
try {
|
|
10159
10210
|
const env2 = { ...process.env, GIT_INDEX_FILE: index };
|
|
10160
|
-
const run2 = (args) =>
|
|
10211
|
+
const run2 = (args) => spawnGit(args, { cwd, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true, env: env2 }).status === 0;
|
|
10161
10212
|
if (!run2(["read-tree", base])) return null;
|
|
10162
10213
|
if (!run2(["add", "-A", ...outsideBoardForAdd(cwd)])) return null;
|
|
10163
|
-
const tree =
|
|
10214
|
+
const tree = spawnGit(["write-tree"], {
|
|
10164
10215
|
cwd,
|
|
10165
10216
|
encoding: "utf8",
|
|
10166
10217
|
maxBuffer: MAX_OUTPUT,
|
|
@@ -10168,7 +10219,8 @@ function treeMark(base, cwd = REPO_ROOT) {
|
|
|
10168
10219
|
env: env2
|
|
10169
10220
|
});
|
|
10170
10221
|
return tree.status === 0 ? tree.stdout.trim() || null : null;
|
|
10171
|
-
} catch {
|
|
10222
|
+
} catch (error) {
|
|
10223
|
+
if (error instanceof GitTimeoutError) throw error;
|
|
10172
10224
|
return null;
|
|
10173
10225
|
} finally {
|
|
10174
10226
|
try {
|
|
@@ -10179,23 +10231,26 @@ function treeMark(base, cwd = REPO_ROOT) {
|
|
|
10179
10231
|
}
|
|
10180
10232
|
function reverseApplies(patch2, ref, cwd = REPO_ROOT) {
|
|
10181
10233
|
if (!patch2.trim()) return false;
|
|
10182
|
-
const
|
|
10234
|
+
const folder = fs25.mkdtempSync(path28.join(os4.tmpdir(), `akb-apply-${process.pid}-`));
|
|
10235
|
+
const index = path28.join(folder, "index");
|
|
10236
|
+
const patchFile = path28.join(folder, "patch");
|
|
10183
10237
|
const env2 = { GIT_INDEX_FILE: index };
|
|
10184
10238
|
try {
|
|
10239
|
+
fs25.writeFileSync(patchFile, patch2);
|
|
10185
10240
|
if (!tryGit(["read-tree", ref], cwd, env2).ok) return false;
|
|
10186
|
-
const out =
|
|
10241
|
+
const out = spawnGit(["apply", "--cached", "--reverse", "--check", patchFile], {
|
|
10187
10242
|
cwd,
|
|
10188
|
-
input: patch2,
|
|
10189
10243
|
encoding: "utf8",
|
|
10190
10244
|
maxBuffer: MAX_OUTPUT,
|
|
10191
10245
|
windowsHide: true,
|
|
10192
10246
|
env: { ...process.env, ...env2 }
|
|
10193
10247
|
});
|
|
10194
10248
|
return !out.error && out.status === 0;
|
|
10195
|
-
} catch {
|
|
10249
|
+
} catch (error) {
|
|
10250
|
+
if (error instanceof GitTimeoutError) throw error;
|
|
10196
10251
|
return false;
|
|
10197
10252
|
} finally {
|
|
10198
|
-
fs25.rmSync(
|
|
10253
|
+
fs25.rmSync(folder, { recursive: true, force: true });
|
|
10199
10254
|
}
|
|
10200
10255
|
}
|
|
10201
10256
|
function squashOnto(dir, base, message) {
|
|
@@ -10277,12 +10332,15 @@ function moveBranchRef(branch, commit, expect) {
|
|
|
10277
10332
|
if (branchTip(branch) !== expect) return { moved: true };
|
|
10278
10333
|
return { ok: false, error: `couldn't move ${branch} to ${commit.slice(0, 12)}: ${done2.why ?? "git refused"}` };
|
|
10279
10334
|
}
|
|
10280
|
-
var MAX_OUTPUT, scripted, posix, outsideBoard, deliveryBranch, deliveryWorktree, worktreeDir, inGitRepo, headCommit, worktreeExists, branchExists, pruneWorktreeMetadata, branchTip, isAncestor, changedPaths, branchPatch, lastCommitTouching, continueRebase, abortRebase, conflictedPaths, headingKind;
|
|
10335
|
+
var MAX_OUTPUT, gitLimits, GitTimeoutError, scripted, posix, outsideBoard, deliveryBranch, deliveryWorktree, worktreeDir, inGitRepo, headCommit, worktreeExists, branchExists, pruneWorktreeMetadata, branchTip, isAncestor, changedPaths, branchPatch, lastCommitTouching, continueRebase, abortRebase, conflictedPaths, headingKind;
|
|
10281
10336
|
var init_worktree = __esm({
|
|
10282
10337
|
"../cli/src/lib/agent/worktree.ts"() {
|
|
10283
10338
|
"use strict";
|
|
10284
10339
|
init_paths();
|
|
10285
10340
|
MAX_OUTPUT = 64 * 1024 * 1024;
|
|
10341
|
+
gitLimits = { timeoutMs: 3e4 };
|
|
10342
|
+
GitTimeoutError = class extends Error {
|
|
10343
|
+
};
|
|
10286
10344
|
scripted = (args, cwd) => tryGit(["-c", "core.hooksPath=/dev/null", ...args], cwd, { GIT_EDITOR: "true", GIT_SEQUENCE_EDITOR: "true" });
|
|
10287
10345
|
posix = (p) => p.split(path28.sep).join("/");
|
|
10288
10346
|
outsideBoard = () => [
|
|
@@ -10299,7 +10357,7 @@ var init_worktree = __esm({
|
|
|
10299
10357
|
branchExists = (branch) => !!branch && git(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`]) !== null;
|
|
10300
10358
|
pruneWorktreeMetadata = () => void git(["worktree", "prune"]);
|
|
10301
10359
|
branchTip = (branch) => git(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`])?.trim() || null;
|
|
10302
|
-
isAncestor = (older, newer, cwd = REPO_ROOT) =>
|
|
10360
|
+
isAncestor = (older, newer, cwd = REPO_ROOT) => spawnGit(["merge-base", "--is-ancestor", older, newer], { cwd, windowsHide: true }).status === 0;
|
|
10303
10361
|
changedPaths = (base, branch, cwd = REPO_ROOT) => {
|
|
10304
10362
|
const out = git(["diff", "--name-only", base, branch, ...outsideBoard()], cwd);
|
|
10305
10363
|
return out === null ? null : out.split("\n").filter(Boolean);
|
|
@@ -11647,50 +11705,13 @@ function withSystemLanguage(harness, argv, env2, language) {
|
|
|
11647
11705
|
}
|
|
11648
11706
|
var init_run_settings = __esm({
|
|
11649
11707
|
"../cli/src/lib/agent/run-settings.ts"() {
|
|
11650
|
-
"use strict";
|
|
11651
|
-
init_home();
|
|
11652
|
-
init_argv();
|
|
11653
|
-
init_env();
|
|
11654
|
-
init_language();
|
|
11655
|
-
init_types2();
|
|
11656
|
-
init_providers();
|
|
11657
|
-
}
|
|
11658
|
-
});
|
|
11659
|
-
|
|
11660
|
-
// ../cli/src/lib/cloud/execution.ts
|
|
11661
|
-
function executionOf(value = process.env.AI4KANBAN_EXECUTION) {
|
|
11662
|
-
try {
|
|
11663
|
-
const held4 = typeof value === "string" ? JSON.parse(value) : value;
|
|
11664
|
-
if (held4 && UUID.test(held4.job) && UUID.test(held4.machine)) return { job: held4.job, machine: held4.machine };
|
|
11665
|
-
} catch {
|
|
11666
|
-
}
|
|
11667
|
-
return void 0;
|
|
11668
|
-
}
|
|
11669
|
-
async function executionHeld(execution) {
|
|
11670
|
-
if (!execution) return true;
|
|
11671
|
-
try {
|
|
11672
|
-
const auth = await accessToken();
|
|
11673
|
-
if (!auth.ok) return false;
|
|
11674
|
-
const response = await fetch(`${cloudEndpoints().api}/v1/runner/jobs/${execution.job}/renew`, {
|
|
11675
|
-
method: "POST",
|
|
11676
|
-
headers: { authorization: `Bearer ${auth.token}`, "content-type": "application/json" },
|
|
11677
|
-
body: JSON.stringify({ machineId: execution.machine }),
|
|
11678
|
-
signal: AbortSignal.timeout(2e4)
|
|
11679
|
-
});
|
|
11680
|
-
if (!response.ok) return false;
|
|
11681
|
-
const held4 = await response.json();
|
|
11682
|
-
return held4.renewed === true && held4.stop !== true;
|
|
11683
|
-
} catch {
|
|
11684
|
-
return false;
|
|
11685
|
-
}
|
|
11686
|
-
}
|
|
11687
|
-
var UUID;
|
|
11688
|
-
var init_execution = __esm({
|
|
11689
|
-
"../cli/src/lib/cloud/execution.ts"() {
|
|
11690
|
-
"use strict";
|
|
11691
|
-
init_config();
|
|
11692
|
-
init_session();
|
|
11693
|
-
UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
11708
|
+
"use strict";
|
|
11709
|
+
init_home();
|
|
11710
|
+
init_argv();
|
|
11711
|
+
init_env();
|
|
11712
|
+
init_language();
|
|
11713
|
+
init_types2();
|
|
11714
|
+
init_providers();
|
|
11694
11715
|
}
|
|
11695
11716
|
});
|
|
11696
11717
|
|
|
@@ -15638,6 +15659,252 @@ var init_complete = __esm({
|
|
|
15638
15659
|
}
|
|
15639
15660
|
});
|
|
15640
15661
|
|
|
15662
|
+
// ../cli/src/lib/agent/stop.ts
|
|
15663
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
15664
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
15665
|
+
import fs39 from "node:fs";
|
|
15666
|
+
import os6 from "node:os";
|
|
15667
|
+
import path44 from "node:path";
|
|
15668
|
+
function markedInProc(entry, strict = false) {
|
|
15669
|
+
const pids = [];
|
|
15670
|
+
for (const name of fs39.readdirSync("/proc")) {
|
|
15671
|
+
if (!/^\d+$/.test(name)) continue;
|
|
15672
|
+
try {
|
|
15673
|
+
if (fs39.readFileSync(`/proc/${name}/environ`, "utf8").split("\0").includes(entry)) pids.push(Number(name));
|
|
15674
|
+
} catch (error) {
|
|
15675
|
+
if (strict && !["ENOENT", "ESRCH"].includes(error.code ?? "")) {
|
|
15676
|
+
const stat = fs39.statSync(`/proc/${name}`, { throwIfNoEntry: false });
|
|
15677
|
+
if (stat?.uid === process.getuid?.()) throw error;
|
|
15678
|
+
}
|
|
15679
|
+
}
|
|
15680
|
+
}
|
|
15681
|
+
return pids;
|
|
15682
|
+
}
|
|
15683
|
+
function markedInPs(entry, roots) {
|
|
15684
|
+
const ps = spawnSync4("ps", ["-A", "-E", "-ww", "-o", "pid=,ppid=,command="], {
|
|
15685
|
+
encoding: "utf8",
|
|
15686
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
15687
|
+
timeout: 5e3
|
|
15688
|
+
});
|
|
15689
|
+
if (ps.status !== 0 || !ps.stdout) throw new Error("\u65E0\u6CD5\u67E5\u8BE2\u8FDB\u7A0B\u6811\u3002");
|
|
15690
|
+
const children = /* @__PURE__ */ new Map();
|
|
15691
|
+
const listed2 = /* @__PURE__ */ new Set();
|
|
15692
|
+
const found2 = /* @__PURE__ */ new Set();
|
|
15693
|
+
for (const line of ps.stdout.split("\n")) {
|
|
15694
|
+
const m = /^\s*(\d+)\s+(\d+)\s(.*)$/.exec(line);
|
|
15695
|
+
if (!m) continue;
|
|
15696
|
+
const pid = Number(m[1]);
|
|
15697
|
+
const ppid = Number(m[2]);
|
|
15698
|
+
listed2.add(pid);
|
|
15699
|
+
children.set(ppid, [...children.get(ppid) ?? [], pid]);
|
|
15700
|
+
if (`${m[3]} `.includes(` ${entry} `)) found2.add(pid);
|
|
15701
|
+
}
|
|
15702
|
+
const queue = [...found2, ...roots.filter((pid) => listed2.has(pid))];
|
|
15703
|
+
for (const pid of queue) {
|
|
15704
|
+
found2.add(pid);
|
|
15705
|
+
for (const child of children.get(pid) ?? []) if (!found2.has(child)) queue.push(child);
|
|
15706
|
+
}
|
|
15707
|
+
return [...found2];
|
|
15708
|
+
}
|
|
15709
|
+
function marked(mark, roots = []) {
|
|
15710
|
+
if (WINDOWS3 || !mark.value) return [];
|
|
15711
|
+
const entry = `${mark.name}=${mark.value}`;
|
|
15712
|
+
try {
|
|
15713
|
+
const pids = process.platform === "linux" ? markedInProc(entry) : markedInPs(entry, roots);
|
|
15714
|
+
return pids.filter((pid) => pid !== process.pid);
|
|
15715
|
+
} catch {
|
|
15716
|
+
return [];
|
|
15717
|
+
}
|
|
15718
|
+
}
|
|
15719
|
+
function signal(pids, sig) {
|
|
15720
|
+
for (const pid of pids) {
|
|
15721
|
+
try {
|
|
15722
|
+
process.kill(pid, sig);
|
|
15723
|
+
} catch {
|
|
15724
|
+
}
|
|
15725
|
+
}
|
|
15726
|
+
}
|
|
15727
|
+
function stopWatchers(sessions, onlyPid, stillOwns = () => true, startedBefore) {
|
|
15728
|
+
if (sessions ? !sessions.length : onlyPid === void 0) return [];
|
|
15729
|
+
const command = WINDOWS3 ? "powershell.exe" : "ps";
|
|
15730
|
+
const args = WINDOWS3 ? ["-NoProfile", "-NonInteractive", "-Command", "Get-CimInstance Win32_Process | Select-Object ProcessId,CommandLine,@{Name='StartedAt';Expression={([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds()}} | ConvertTo-Json -Compress"] : ["-A", "-ww", "-o", "pid=,lstart=,command="];
|
|
15731
|
+
const out = spawnSync4(command, args, { encoding: "utf8", timeout: 5e3, maxBuffer: 16 * 1024 * 1024, windowsHide: true, env: { ...process.env, LC_ALL: "C" } });
|
|
15732
|
+
if (out.status !== 0) return [];
|
|
15733
|
+
let procs;
|
|
15734
|
+
try {
|
|
15735
|
+
procs = WINDOWS3 ? [].concat(JSON.parse(out.stdout)).map((p) => ({ pid: p.ProcessId, command: p.CommandLine ?? "", startedAt: p.StartedAt })) : out.stdout.split("\n").flatMap((line) => {
|
|
15736
|
+
const m = /^\s*(\d+)\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d{4})\s+(.*)$/.exec(line);
|
|
15737
|
+
return m ? [{ pid: Number(m[1]), command: m[3], startedAt: Date.parse(m[2]) }] : [];
|
|
15738
|
+
});
|
|
15739
|
+
} catch {
|
|
15740
|
+
return [];
|
|
15741
|
+
}
|
|
15742
|
+
const known2 = sessions && new Set(sessions);
|
|
15743
|
+
const stopped2 = [];
|
|
15744
|
+
for (const proc of procs) {
|
|
15745
|
+
if (proc.pid === process.pid || onlyPid !== void 0 && proc.pid !== onlyPid) continue;
|
|
15746
|
+
if (startedBefore !== void 0 && (!Number.isFinite(proc.startedAt) || proc.startedAt > startedBefore)) continue;
|
|
15747
|
+
const match = /^"?[^\n]*?\bnode(?:\.exe)?"?\s+(?:"[^\n"]+\.mjs"|\S+\.mjs)\s+__watch\s+(\S+)(?:\s|$)/.exec(proc.command);
|
|
15748
|
+
const args2 = `${proc.command} `;
|
|
15749
|
+
const names4 = (flag, value) => args2.includes(` ${flag} ${value} `) || args2.includes(` ${flag} "${value}" `);
|
|
15750
|
+
const board2 = names4("--board", KANBAN) || KANBAN === path44.join(REPO_ROOT, "docs", "kanban") && names4("--dir", REPO_ROOT);
|
|
15751
|
+
if (!match || known2 && !known2.has(match[1]) || !board2 || !stillOwns()) continue;
|
|
15752
|
+
if (WINDOWS3) {
|
|
15753
|
+
if (killTreeOnWindows(proc.pid)) stopped2.push(proc.pid);
|
|
15754
|
+
} else {
|
|
15755
|
+
try {
|
|
15756
|
+
process.kill(-proc.pid, "SIGKILL");
|
|
15757
|
+
stopped2.push(proc.pid);
|
|
15758
|
+
} catch {
|
|
15759
|
+
}
|
|
15760
|
+
}
|
|
15761
|
+
}
|
|
15762
|
+
return stopped2;
|
|
15763
|
+
}
|
|
15764
|
+
function startedUnder(list, root, since) {
|
|
15765
|
+
const self = list.procs.find((p) => p.pid === root);
|
|
15766
|
+
const from = since ?? self?.startedAt;
|
|
15767
|
+
if (from === void 0 || list.bootedAt > from) return [];
|
|
15768
|
+
if (self && self.startedAt > from) return [];
|
|
15769
|
+
const found2 = self ? [root] : [];
|
|
15770
|
+
const seen = /* @__PURE__ */ new Set([root]);
|
|
15771
|
+
const queue = [{ pid: root, startedAt: from }];
|
|
15772
|
+
for (const parent of queue) {
|
|
15773
|
+
for (const p of list.procs) {
|
|
15774
|
+
if (p.ppid !== parent.pid || seen.has(p.pid) || p.startedAt < parent.startedAt) continue;
|
|
15775
|
+
seen.add(p.pid);
|
|
15776
|
+
found2.push(p.pid);
|
|
15777
|
+
queue.push(p);
|
|
15778
|
+
}
|
|
15779
|
+
}
|
|
15780
|
+
return found2.reverse();
|
|
15781
|
+
}
|
|
15782
|
+
function readProcList(out) {
|
|
15783
|
+
const procs = [];
|
|
15784
|
+
let bootedAt;
|
|
15785
|
+
for (const line of out.split(/\r?\n/)) {
|
|
15786
|
+
const boot = /^boot (\d+)$/.exec(line.trim());
|
|
15787
|
+
if (boot) bootedAt = Number(boot[1]);
|
|
15788
|
+
const m = /^(\d+) (\d+) (\d+)$/.exec(line.trim());
|
|
15789
|
+
if (m) procs.push({ pid: Number(m[1]), ppid: Number(m[2]), startedAt: Number(m[3]) });
|
|
15790
|
+
}
|
|
15791
|
+
return bootedAt !== void 0 && procs.length ? { procs, bootedAt } : void 0;
|
|
15792
|
+
}
|
|
15793
|
+
function procsOnWindows() {
|
|
15794
|
+
const ps = spawnSync4("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", LIST_PROCS], {
|
|
15795
|
+
encoding: "utf8",
|
|
15796
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
15797
|
+
timeout: 5e3,
|
|
15798
|
+
windowsHide: true
|
|
15799
|
+
});
|
|
15800
|
+
return ps.status === 0 && ps.stdout ? readProcList(ps.stdout) : void 0;
|
|
15801
|
+
}
|
|
15802
|
+
function killUnderOnWindows(pid, since) {
|
|
15803
|
+
if (!WINDOWS3 || !pid) return;
|
|
15804
|
+
const list = procsOnWindows();
|
|
15805
|
+
if (!list) return;
|
|
15806
|
+
const pids = startedUnder(list, pid, since).filter((p) => p !== process.pid);
|
|
15807
|
+
if (!pids.length) return;
|
|
15808
|
+
spawnSync4("taskkill", ["/F", ...pids.flatMap((p) => ["/PID", String(p)])], { stdio: "ignore", windowsHide: true, timeout: 1e4 });
|
|
15809
|
+
}
|
|
15810
|
+
function killMarked(mark, agent) {
|
|
15811
|
+
if (WINDOWS3) {
|
|
15812
|
+
if (agent) killUnderOnWindows(agent.pid, agent.since);
|
|
15813
|
+
return;
|
|
15814
|
+
}
|
|
15815
|
+
signal(marked(mark), "SIGKILL");
|
|
15816
|
+
}
|
|
15817
|
+
function killTreeOnWindows(pid) {
|
|
15818
|
+
if (!WINDOWS3 || !pid) return false;
|
|
15819
|
+
const out = spawnSync4("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true, timeout: 1e4 });
|
|
15820
|
+
return out.status === 0;
|
|
15821
|
+
}
|
|
15822
|
+
function later(ms, fn) {
|
|
15823
|
+
const t = setTimeout(fn, ms);
|
|
15824
|
+
if (typeof t.unref === "function") t.unref();
|
|
15825
|
+
}
|
|
15826
|
+
function endAgent(child, mark, graceMs) {
|
|
15827
|
+
const term = (sig) => {
|
|
15828
|
+
try {
|
|
15829
|
+
child.kill(sig);
|
|
15830
|
+
} catch {
|
|
15831
|
+
}
|
|
15832
|
+
};
|
|
15833
|
+
if (killTreeOnWindows(child.pid)) return;
|
|
15834
|
+
killUnderOnWindows(child.pid);
|
|
15835
|
+
const seen = new Set(marked(mark, child.pid ? [child.pid] : []));
|
|
15836
|
+
term("SIGTERM");
|
|
15837
|
+
signal([...seen].filter((pid) => pid !== child.pid), "SIGTERM");
|
|
15838
|
+
const round = (left) => {
|
|
15839
|
+
const pids = marked(mark, [...seen]);
|
|
15840
|
+
signal(pids, "SIGKILL");
|
|
15841
|
+
for (const pid of pids) seen.add(pid);
|
|
15842
|
+
if (pids.length && left > 1) later(KILL_ROUND_MS, () => round(left - 1));
|
|
15843
|
+
};
|
|
15844
|
+
later(graceMs, () => {
|
|
15845
|
+
term("SIGKILL");
|
|
15846
|
+
round(KILL_ROUNDS);
|
|
15847
|
+
});
|
|
15848
|
+
}
|
|
15849
|
+
function killRunning() {
|
|
15850
|
+
for (const [child, mark] of running) {
|
|
15851
|
+
if (killTreeOnWindows(child.pid)) continue;
|
|
15852
|
+
killUnderOnWindows(child.pid);
|
|
15853
|
+
signal(marked(mark, child.pid ? [child.pid] : []), "SIGKILL");
|
|
15854
|
+
try {
|
|
15855
|
+
child.kill("SIGKILL");
|
|
15856
|
+
} catch {
|
|
15857
|
+
}
|
|
15858
|
+
}
|
|
15859
|
+
running.clear();
|
|
15860
|
+
}
|
|
15861
|
+
function unlisten() {
|
|
15862
|
+
for (const [sig, fn] of listening) process.removeListener(sig, fn);
|
|
15863
|
+
listening.clear();
|
|
15864
|
+
}
|
|
15865
|
+
function onQuit(sig) {
|
|
15866
|
+
killRunning();
|
|
15867
|
+
unlisten();
|
|
15868
|
+
if (process.listenerCount(sig) === 0) process.exit(128 + os6.constants.signals[sig]);
|
|
15869
|
+
}
|
|
15870
|
+
function trackAgent(child, mark) {
|
|
15871
|
+
if (!child.pid) return;
|
|
15872
|
+
running.set(child, mark);
|
|
15873
|
+
child.once("close", () => {
|
|
15874
|
+
running.delete(child);
|
|
15875
|
+
if (!running.size) unlisten();
|
|
15876
|
+
});
|
|
15877
|
+
for (const sig of QUIT_SIGNALS) {
|
|
15878
|
+
if (listening.has(sig) || sig === "SIGINT" && interruptIsTheCallers) continue;
|
|
15879
|
+
const fn = () => onQuit(sig);
|
|
15880
|
+
listening.set(sig, fn);
|
|
15881
|
+
process.on(sig, fn);
|
|
15882
|
+
}
|
|
15883
|
+
}
|
|
15884
|
+
var runMark, stopMark, markEnv, WINDOWS3, KILL_ROUND_MS, KILL_ROUNDS, LIST_PROCS, running, QUIT_SIGNALS, listening, interruptIsTheCallers;
|
|
15885
|
+
var init_stop = __esm({
|
|
15886
|
+
"../cli/src/lib/agent/stop.ts"() {
|
|
15887
|
+
"use strict";
|
|
15888
|
+
init_env();
|
|
15889
|
+
init_paths();
|
|
15890
|
+
runMark = (sessionId) => ({ name: RUN_ENV, value: sessionId });
|
|
15891
|
+
stopMark = () => ({ name: STOP_ENV, value: randomUUID5() });
|
|
15892
|
+
markEnv = (env2, mark) => ({ ...env2, [mark.name]: mark.value });
|
|
15893
|
+
WINDOWS3 = process.platform === "win32";
|
|
15894
|
+
KILL_ROUND_MS = 300;
|
|
15895
|
+
KILL_ROUNDS = 3;
|
|
15896
|
+
LIST_PROCS = [
|
|
15897
|
+
"$ErrorActionPreference = 'Stop'",
|
|
15898
|
+
"Get-CimInstance Win32_Process | ForEach-Object { if ($_.CreationDate) { '{0} {1} {2}' -f $_.ProcessId, $_.ParentProcessId, ([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds() } }",
|
|
15899
|
+
"'boot {0}' -f ([DateTimeOffset](Get-CimInstance Win32_OperatingSystem).LastBootUpTime).ToUnixTimeMilliseconds()"
|
|
15900
|
+
].join("; ");
|
|
15901
|
+
running = /* @__PURE__ */ new Map();
|
|
15902
|
+
QUIT_SIGNALS = ["SIGTERM", "SIGINT", "SIGHUP"];
|
|
15903
|
+
listening = /* @__PURE__ */ new Map();
|
|
15904
|
+
interruptIsTheCallers = false;
|
|
15905
|
+
}
|
|
15906
|
+
});
|
|
15907
|
+
|
|
15641
15908
|
// ../cli/src/lib/agent/pause.ts
|
|
15642
15909
|
function deliveryState(delivery, questions) {
|
|
15643
15910
|
const landing = delivery.landing;
|
|
@@ -15826,12 +16093,12 @@ var init_runner = __esm({
|
|
|
15826
16093
|
});
|
|
15827
16094
|
|
|
15828
16095
|
// ../cli/src/lib/agent/rules.ts
|
|
15829
|
-
import
|
|
15830
|
-
import
|
|
16096
|
+
import fs40 from "node:fs";
|
|
16097
|
+
import path45 from "node:path";
|
|
15831
16098
|
function ruleFile(agent) {
|
|
15832
16099
|
if (!agent || hiddenRole(agent)) return "";
|
|
15833
16100
|
try {
|
|
15834
|
-
return
|
|
16101
|
+
return fs40.readFileSync(rulePath(agent), "utf8").trim();
|
|
15835
16102
|
} catch {
|
|
15836
16103
|
return "";
|
|
15837
16104
|
}
|
|
@@ -15846,11 +16113,11 @@ function setAgentRule(agent, text5) {
|
|
|
15846
16113
|
const rule = String(text5 ?? "").trim();
|
|
15847
16114
|
try {
|
|
15848
16115
|
if (!rule) {
|
|
15849
|
-
|
|
16116
|
+
fs40.rmSync(rulePath(agent), { force: true });
|
|
15850
16117
|
return { ok: true };
|
|
15851
16118
|
}
|
|
15852
|
-
|
|
15853
|
-
|
|
16119
|
+
fs40.mkdirSync(RULES, { recursive: true });
|
|
16120
|
+
fs40.writeFileSync(rulePath(agent), `${rule}
|
|
15854
16121
|
`);
|
|
15855
16122
|
return { ok: true };
|
|
15856
16123
|
} catch (err) {
|
|
@@ -15898,7 +16165,7 @@ function migrateFlowRules() {
|
|
|
15898
16165
|
adoptRenamedRules();
|
|
15899
16166
|
let here;
|
|
15900
16167
|
try {
|
|
15901
|
-
here = new Set(
|
|
16168
|
+
here = new Set(fs40.readdirSync(RULES));
|
|
15902
16169
|
} catch {
|
|
15903
16170
|
return [];
|
|
15904
16171
|
}
|
|
@@ -15909,9 +16176,9 @@ function migrateFlowRules() {
|
|
|
15909
16176
|
if (!from.length) continue;
|
|
15910
16177
|
const parts = [ruleFile(owner.name), ...from.map(ruleFile)].filter(Boolean);
|
|
15911
16178
|
try {
|
|
15912
|
-
if (parts.length)
|
|
16179
|
+
if (parts.length) fs40.writeFileSync(rulePath(owner.name), `${parts.join("\n\n")}
|
|
15913
16180
|
`);
|
|
15914
|
-
for (const flow of from)
|
|
16181
|
+
for (const flow of from) fs40.rmSync(rulePath(flow), { force: true });
|
|
15915
16182
|
} catch (err) {
|
|
15916
16183
|
notes.push(`could not move the ${from.join(", ")} rule(s) onto \`${owner.name}\`: ${String(err)}`);
|
|
15917
16184
|
continue;
|
|
@@ -15925,7 +16192,7 @@ function migrateFlowRules() {
|
|
|
15925
16192
|
function adoptRenamedRules() {
|
|
15926
16193
|
let here;
|
|
15927
16194
|
try {
|
|
15928
|
-
here =
|
|
16195
|
+
here = fs40.readdirSync(RULES);
|
|
15929
16196
|
} catch {
|
|
15930
16197
|
return;
|
|
15931
16198
|
}
|
|
@@ -15935,13 +16202,13 @@ function adoptRenamedRules() {
|
|
|
15935
16202
|
const now = RENAMED_ROLES[was] ?? canonicalSpecAgent(was);
|
|
15936
16203
|
if (now === was) continue;
|
|
15937
16204
|
try {
|
|
15938
|
-
if (!
|
|
16205
|
+
if (!fs40.existsSync(rulePath(now))) fs40.renameSync(rulePath(was), rulePath(now));
|
|
15939
16206
|
else if (FOLDED_SPEC_AGENTS.has(was)) {
|
|
15940
16207
|
const kept2 = ruleFile(now);
|
|
15941
16208
|
const carried = ruleFile(was);
|
|
15942
|
-
if (carried && !kept2.includes(carried))
|
|
16209
|
+
if (carried && !kept2.includes(carried)) fs40.writeFileSync(rulePath(now), `${[kept2, carried].filter(Boolean).join("\n\n")}
|
|
15943
16210
|
`);
|
|
15944
|
-
|
|
16211
|
+
fs40.rmSync(rulePath(was), { force: true });
|
|
15945
16212
|
}
|
|
15946
16213
|
} catch {
|
|
15947
16214
|
}
|
|
@@ -15958,7 +16225,7 @@ var init_rules2 = __esm({
|
|
|
15958
16225
|
init_runner();
|
|
15959
16226
|
init_workflows();
|
|
15960
16227
|
init_types3();
|
|
15961
|
-
rulePath = (agent) =>
|
|
16228
|
+
rulePath = (agent) => path45.join(RULES, `${agent}.md`);
|
|
15962
16229
|
notOnTheRoster = (name) => `"${name}" is not an agent on this board. It has: ${agentNames().join(", ")}.`;
|
|
15963
16230
|
flowForRequest = (req) => req.refineRound !== void 0 && REFINE_ACTIONS.has(req.action) ? flowByCommand("refine") : flowByAction(req.action);
|
|
15964
16231
|
ruleOwnerSays = (owner) => `\`${owner.name}\` agent carries one rule of its own${owner.role ? ", on every flow it runs" : ""}`;
|
|
@@ -16043,8 +16310,8 @@ var init_source = __esm({
|
|
|
16043
16310
|
});
|
|
16044
16311
|
|
|
16045
16312
|
// ../cli/src/lib/card-sources.ts
|
|
16046
|
-
import
|
|
16047
|
-
import
|
|
16313
|
+
import fs41 from "node:fs";
|
|
16314
|
+
import path46 from "node:path";
|
|
16048
16315
|
function planById(id) {
|
|
16049
16316
|
const found2 = readPlan(`plans/${id}-.md`);
|
|
16050
16317
|
return found2?.text.trim() ? found2 : null;
|
|
@@ -16054,8 +16321,8 @@ function cardLink(id) {
|
|
|
16054
16321
|
const found2 = open3 ?? locateArchived(id);
|
|
16055
16322
|
if (!found2) return null;
|
|
16056
16323
|
try {
|
|
16057
|
-
const file = found2.kind === "group" ?
|
|
16058
|
-
const title = parseFrontmatter(
|
|
16324
|
+
const file = found2.kind === "group" ? path46.join(found2.target, "root.md") : found2.target;
|
|
16325
|
+
const title = parseFrontmatter(fs41.readFileSync(file, "utf8")).meta?.title ?? "";
|
|
16059
16326
|
return { kind: "card", ref: String(id), title, ...open3 ? {} : { archived: true } };
|
|
16060
16327
|
} catch {
|
|
16061
16328
|
return null;
|
|
@@ -16080,7 +16347,7 @@ function link(ref, items) {
|
|
|
16080
16347
|
if (ref.kind === "plan") return planLink(ref.id);
|
|
16081
16348
|
if (ref.kind === "card") return cardLink(ref.id);
|
|
16082
16349
|
if (ref.kind === "url") return urlLink(ref.url);
|
|
16083
|
-
const item = items().find((signal2) =>
|
|
16350
|
+
const item = items().find((signal2) => path46.basename(signal2.relPath) === ref.file);
|
|
16084
16351
|
return item ? itemLink(item) : null;
|
|
16085
16352
|
}
|
|
16086
16353
|
function cardSources(meta, body) {
|
|
@@ -16121,12 +16388,12 @@ var init_card_sources = __esm({
|
|
|
16121
16388
|
});
|
|
16122
16389
|
|
|
16123
16390
|
// ../cli/src/lib/view/read.ts
|
|
16124
|
-
import
|
|
16125
|
-
import
|
|
16391
|
+
import fs42 from "node:fs";
|
|
16392
|
+
import path47 from "node:path";
|
|
16126
16393
|
function readCard(file, relFromTodo) {
|
|
16127
|
-
const id = idPrefix(
|
|
16394
|
+
const id = idPrefix(path47.basename(relFromTodo));
|
|
16128
16395
|
if (id === null) {
|
|
16129
|
-
const parts = relFromTodo.split(
|
|
16396
|
+
const parts = relFromTodo.split(path47.sep);
|
|
16130
16397
|
const folderId = idPrefix(parts[parts.length - 2] || "");
|
|
16131
16398
|
if (folderId === null) return null;
|
|
16132
16399
|
return buildCard(folderId, file, relFromTodo);
|
|
@@ -16134,10 +16401,10 @@ function readCard(file, relFromTodo) {
|
|
|
16134
16401
|
return buildCard(id, file, relFromTodo);
|
|
16135
16402
|
}
|
|
16136
16403
|
function buildCard(id, file, relFromTodo) {
|
|
16137
|
-
const text5 =
|
|
16404
|
+
const text5 = fs42.readFileSync(file, "utf8");
|
|
16138
16405
|
const { meta, body } = parseFrontmatter(text5);
|
|
16139
16406
|
if (!meta) return null;
|
|
16140
|
-
const relPath = relFromTodo.split(
|
|
16407
|
+
const relPath = relFromTodo.split(path47.sep).join("/");
|
|
16141
16408
|
const flow = workflowFor(meta.workflow);
|
|
16142
16409
|
const plan = !!meta.workflow && archivesOnApproval(flow);
|
|
16143
16410
|
const stages = cardStages(meta, flow).length;
|
|
@@ -16146,7 +16413,7 @@ function buildCard(id, file, relFromTodo) {
|
|
|
16146
16413
|
// Derived from the file exactly as it is on disk (lib/board/revision.ts), so a card
|
|
16147
16414
|
// read here and a card read by the contract agree on what version this is.
|
|
16148
16415
|
revision: revisionOf(text5),
|
|
16149
|
-
updatedAt:
|
|
16416
|
+
updatedAt: fs42.statSync(file).mtime.toISOString(),
|
|
16150
16417
|
relPath,
|
|
16151
16418
|
title: meta.title,
|
|
16152
16419
|
priority: meta.priority,
|
|
@@ -16167,22 +16434,22 @@ function buildCard(id, file, relFromTodo) {
|
|
|
16167
16434
|
};
|
|
16168
16435
|
}
|
|
16169
16436
|
function readGroup2(folderName) {
|
|
16170
|
-
const groupDir =
|
|
16171
|
-
const rootFile =
|
|
16172
|
-
if (!
|
|
16173
|
-
const root = readCard(rootFile,
|
|
16437
|
+
const groupDir = path47.join(TODO, folderName);
|
|
16438
|
+
const rootFile = path47.join(groupDir, "root.md");
|
|
16439
|
+
if (!fs42.existsSync(rootFile)) return null;
|
|
16440
|
+
const root = readCard(rootFile, path47.join(folderName, "root.md"));
|
|
16174
16441
|
if (!root) return null;
|
|
16175
16442
|
root.isGroup = true;
|
|
16176
16443
|
const { total, resolved: resolved2 } = subtaskLines(root.body);
|
|
16177
16444
|
root.subtaskLines = { total, resolved: resolved2 };
|
|
16178
16445
|
const subCards = [];
|
|
16179
16446
|
const recurse = (dir, relDir) => {
|
|
16180
|
-
for (const entry of
|
|
16181
|
-
const childRel =
|
|
16447
|
+
for (const entry of fs42.readdirSync(dir, { withFileTypes: true })) {
|
|
16448
|
+
const childRel = path47.join(relDir, entry.name);
|
|
16182
16449
|
if (entry.isDirectory()) {
|
|
16183
|
-
recurse(
|
|
16450
|
+
recurse(path47.join(dir, entry.name), childRel);
|
|
16184
16451
|
} else if (entry.name.endsWith(".md") && entry.name !== "README.md" && entry.name !== "root.md") {
|
|
16185
|
-
const c = readCard(
|
|
16452
|
+
const c = readCard(path47.join(dir, entry.name), childRel);
|
|
16186
16453
|
if (c) {
|
|
16187
16454
|
c.parent = { id: root.id, title: root.title };
|
|
16188
16455
|
subCards.push(c);
|
|
@@ -16203,12 +16470,12 @@ function readGroup2(folderName) {
|
|
|
16203
16470
|
return { root, subCards };
|
|
16204
16471
|
}
|
|
16205
16472
|
function standaloneCards(dirRel) {
|
|
16206
|
-
const dir = dirRel ?
|
|
16207
|
-
if (!
|
|
16473
|
+
const dir = dirRel ? path47.join(TODO, dirRel) : TODO;
|
|
16474
|
+
if (!fs42.existsSync(dir)) return [];
|
|
16208
16475
|
const cards = [];
|
|
16209
|
-
for (const entry of
|
|
16476
|
+
for (const entry of fs42.readdirSync(dir, { withFileTypes: true })) {
|
|
16210
16477
|
if (entry.isFile() && entry.name.endsWith(".md") && entry.name !== "README.md") {
|
|
16211
|
-
const c = readCard(
|
|
16478
|
+
const c = readCard(path47.join(dir, entry.name), path47.join(dirRel, entry.name));
|
|
16212
16479
|
if (c) cards.push(c);
|
|
16213
16480
|
}
|
|
16214
16481
|
}
|
|
@@ -16220,7 +16487,7 @@ function collectCards() {
|
|
|
16220
16487
|
const top = standaloneCards("");
|
|
16221
16488
|
board2.push(...top);
|
|
16222
16489
|
every.push(...top);
|
|
16223
|
-
for (const entry of
|
|
16490
|
+
for (const entry of fs42.readdirSync(TODO, { withFileTypes: true })) {
|
|
16224
16491
|
if (!entry.isDirectory()) continue;
|
|
16225
16492
|
if (isGroupFolder(entry.name)) {
|
|
16226
16493
|
const g = readGroup2(entry.name);
|
|
@@ -16252,7 +16519,7 @@ function findCard(id) {
|
|
|
16252
16519
|
}
|
|
16253
16520
|
function attachSources(card) {
|
|
16254
16521
|
try {
|
|
16255
|
-
const { meta, body } = parseFrontmatter(
|
|
16522
|
+
const { meta, body } = parseFrontmatter(fs42.readFileSync(path47.join(TODO, card.relPath), "utf8"));
|
|
16256
16523
|
const sources = meta ? cardSources(meta, body) : [];
|
|
16257
16524
|
if (sources.length) card.sources = sources;
|
|
16258
16525
|
} catch {
|
|
@@ -16440,8 +16707,8 @@ var init_review = __esm({
|
|
|
16440
16707
|
});
|
|
16441
16708
|
|
|
16442
16709
|
// ../cli/src/lib/agent/deliveries.ts
|
|
16443
|
-
import
|
|
16444
|
-
import
|
|
16710
|
+
import fs43 from "node:fs";
|
|
16711
|
+
import path48 from "node:path";
|
|
16445
16712
|
function writeAudit(delivery, runs) {
|
|
16446
16713
|
const known2 = new Map(readAudit(delivery.deliveryId)?.sessions.map((s) => [s.sessionId, s]) ?? []);
|
|
16447
16714
|
const sessions = delivery.sessions.flatMap((id) => {
|
|
@@ -16469,10 +16736,10 @@ function writeAudit(delivery, runs) {
|
|
|
16469
16736
|
}
|
|
16470
16737
|
function writeAuditFile(record) {
|
|
16471
16738
|
try {
|
|
16472
|
-
|
|
16739
|
+
fs43.mkdirSync(DELIVERIES, { recursive: true });
|
|
16473
16740
|
const tmp = `${auditPath(record.deliveryId)}.tmp`;
|
|
16474
|
-
|
|
16475
|
-
|
|
16741
|
+
fs43.writeFileSync(tmp, JSON.stringify(record, null, 2) + "\n");
|
|
16742
|
+
fs43.renameSync(tmp, auditPath(record.deliveryId));
|
|
16476
16743
|
return true;
|
|
16477
16744
|
} catch {
|
|
16478
16745
|
return false;
|
|
@@ -16480,7 +16747,7 @@ function writeAuditFile(record) {
|
|
|
16480
16747
|
}
|
|
16481
16748
|
function readAudit(deliveryId) {
|
|
16482
16749
|
try {
|
|
16483
|
-
const data = JSON.parse(
|
|
16750
|
+
const data = JSON.parse(fs43.readFileSync(auditPath(deliveryId), "utf8"));
|
|
16484
16751
|
return { sessions: Array.isArray(data?.sessions) ? data.sessions : [] };
|
|
16485
16752
|
} catch {
|
|
16486
16753
|
return null;
|
|
@@ -16495,7 +16762,7 @@ function syncAudit(deliveryId, just) {
|
|
|
16495
16762
|
}
|
|
16496
16763
|
function wholeAudit(file) {
|
|
16497
16764
|
try {
|
|
16498
|
-
const data = JSON.parse(
|
|
16765
|
+
const data = JSON.parse(fs43.readFileSync(file, "utf8"));
|
|
16499
16766
|
return typeof data?.deliveryId === "string" && Array.isArray(data.sessions) ? data : null;
|
|
16500
16767
|
} catch {
|
|
16501
16768
|
return null;
|
|
@@ -16508,14 +16775,14 @@ function activeAudit(file) {
|
|
|
16508
16775
|
function allAudits() {
|
|
16509
16776
|
let names4;
|
|
16510
16777
|
try {
|
|
16511
|
-
names4 =
|
|
16778
|
+
names4 = fs43.readdirSync(DELIVERIES);
|
|
16512
16779
|
} catch {
|
|
16513
16780
|
return [];
|
|
16514
16781
|
}
|
|
16515
16782
|
const out = [];
|
|
16516
16783
|
for (const name of names4) {
|
|
16517
16784
|
if (!name.endsWith(".json")) continue;
|
|
16518
|
-
const audit = wholeAudit(
|
|
16785
|
+
const audit = wholeAudit(path48.join(DELIVERIES, name));
|
|
16519
16786
|
if (audit) out.push(audit);
|
|
16520
16787
|
}
|
|
16521
16788
|
return out;
|
|
@@ -16626,11 +16893,11 @@ function resumeRefusal(delivery, store) {
|
|
|
16626
16893
|
);
|
|
16627
16894
|
}
|
|
16628
16895
|
if (!worktreeExists(delivery.worktree)) {
|
|
16629
|
-
const
|
|
16896
|
+
const path88 = delivery.worktree ?? "";
|
|
16630
16897
|
return refusal(
|
|
16631
16898
|
"deliveryWorktreeGone",
|
|
16632
|
-
`delivery ${id}'s worktree ${
|
|
16633
|
-
{ id, task, path:
|
|
16899
|
+
`delivery ${id}'s worktree ${path88} is gone, so there is nothing to carry on. ${rebuild}`,
|
|
16900
|
+
{ id, task, path: path88, command: discard }
|
|
16634
16901
|
);
|
|
16635
16902
|
}
|
|
16636
16903
|
if (!branchExists(delivery.branch)) {
|
|
@@ -16683,11 +16950,12 @@ function tidyCheckout(deliveryId) {
|
|
|
16683
16950
|
const store = readStore();
|
|
16684
16951
|
const row = store.deliveries.find((d) => d.deliveryId === deliveryId) ?? auditOnly(deliveryId);
|
|
16685
16952
|
if (!row || row.status === "active") return NOTHING;
|
|
16686
|
-
if (!row.worktree && !row.branch) return NOTHING;
|
|
16687
|
-
if (isResumable(row)) return { removed: false, kept: true };
|
|
16688
16953
|
if (store.runs.some((r) => r.deliveryId === deliveryId && runIsLive(r))) {
|
|
16689
16954
|
return { removed: false, kept: false, error: `a run of delivery ${deliveryId} is still going`, reason: "deliveryRunGoing", args: { id: deliveryId } };
|
|
16690
16955
|
}
|
|
16956
|
+
if (row.status !== "finished" || Date.now() - (row.endedAt ?? 0) > 3e4) stopWatchers(row.sessions);
|
|
16957
|
+
if (!row.worktree && !row.branch) return NOTHING;
|
|
16958
|
+
if (isResumable(row)) return { removed: false, kept: true };
|
|
16691
16959
|
const removed = removeWorktree(row.worktree, row.branch, true);
|
|
16692
16960
|
if (!removed.ok) return { removed: false, kept: false, error: removed.error };
|
|
16693
16961
|
forgetCheckout(deliveryId);
|
|
@@ -16710,7 +16978,10 @@ function forgetCheckout(deliveryId) {
|
|
|
16710
16978
|
function sweepCheckouts() {
|
|
16711
16979
|
const seen = /* @__PURE__ */ new Set();
|
|
16712
16980
|
const complaints = [];
|
|
16713
|
-
const
|
|
16981
|
+
const store = readStore();
|
|
16982
|
+
const rows = [...store.deliveries, ...allAudits().flatMap((a) => auditRow(a) ?? [])];
|
|
16983
|
+
const ended = rows.filter((row) => row.status !== "active" && (row.status !== "finished" || Date.now() - (row.endedAt ?? 0) > 3e4) && !store.runs.some((r) => r.deliveryId === row.deliveryId && runIsLive(r)));
|
|
16984
|
+
stopWatchers([...new Set(ended.flatMap((row) => row.sessions))]);
|
|
16714
16985
|
for (const row of rows) {
|
|
16715
16986
|
if (seen.has(row.deliveryId) || row.status === "active" || !row.worktree) continue;
|
|
16716
16987
|
seen.add(row.deliveryId);
|
|
@@ -16720,21 +16991,27 @@ function sweepCheckouts() {
|
|
|
16720
16991
|
dropEmptyWorktreeFolders();
|
|
16721
16992
|
return complaints;
|
|
16722
16993
|
}
|
|
16723
|
-
function resumeRecord(id) {
|
|
16724
|
-
const
|
|
16994
|
+
function resumeRecord(id, execution) {
|
|
16995
|
+
const continueActive = !!execution;
|
|
16996
|
+
const found2 = (continueActive ? namedDelivery(id) : void 0) ?? endedDelivery(id);
|
|
16725
16997
|
if (!found2) return { ok: false, ...unknownDelivery(id) };
|
|
16726
16998
|
const refused = resumeRefusal(found2);
|
|
16727
|
-
if (refused) return { ok: false, ...refused };
|
|
16999
|
+
if (refused && !(continueActive && found2.status === "active")) return { ok: false, ...refused };
|
|
16728
17000
|
const out = withStore((store) => {
|
|
16729
17001
|
const audit = wholeAudit(auditPath(found2.deliveryId));
|
|
16730
|
-
const
|
|
17002
|
+
const live = store.deliveries.find((d) => d.deliveryId === found2.deliveryId);
|
|
17003
|
+
const row = continueActive && live?.status === "active" ? live : (audit && auditRow(audit)) ?? live;
|
|
16731
17004
|
if (!row) return { ok: false, ...unknownDelivery(id) };
|
|
16732
17005
|
const again = resumeRefusal(row, store);
|
|
16733
|
-
if (
|
|
17006
|
+
if (continueActive && row.status === "active") {
|
|
17007
|
+
if (store.runs.some((r) => r.deliveryId === row.deliveryId && runIsLive(r))) {
|
|
17008
|
+
return { ok: false, ...refusal("deliveryRunGoing", "the delivery still has a running agent; stop it before continuing") };
|
|
17009
|
+
}
|
|
17010
|
+
} else if (again) return { ok: false, ...again };
|
|
16734
17011
|
const at = Date.now();
|
|
16735
17012
|
const resumed = {
|
|
16736
17013
|
...row,
|
|
16737
|
-
cloudExecution: executionOf() ?? row.cloudExecution,
|
|
17014
|
+
cloudExecution: execution ?? executionOf() ?? row.cloudExecution,
|
|
16738
17015
|
status: "active",
|
|
16739
17016
|
endedAt: void 0,
|
|
16740
17017
|
steps: [...row.steps, { step: "resume", at }],
|
|
@@ -16763,10 +17040,10 @@ function carryOnFrom(deliveryId) {
|
|
|
16763
17040
|
function approvedRequirements(cardId2) {
|
|
16764
17041
|
const found2 = locate(cardId2);
|
|
16765
17042
|
if (!found2) return "";
|
|
16766
|
-
const file = found2.kind === "group" ?
|
|
17043
|
+
const file = found2.kind === "group" ? path48.join(found2.target, "root.md") : found2.target;
|
|
16767
17044
|
let text5;
|
|
16768
17045
|
try {
|
|
16769
|
-
text5 =
|
|
17046
|
+
text5 = fs43.readFileSync(file, "utf8");
|
|
16770
17047
|
} catch {
|
|
16771
17048
|
return "";
|
|
16772
17049
|
}
|
|
@@ -16796,8 +17073,8 @@ function cardMeta2(cardId2) {
|
|
|
16796
17073
|
try {
|
|
16797
17074
|
const found2 = locate(cardId2);
|
|
16798
17075
|
if (!found2) return null;
|
|
16799
|
-
const file = found2.kind === "group" ?
|
|
16800
|
-
return parseFrontmatter(
|
|
17076
|
+
const file = found2.kind === "group" ? path48.join(found2.target, "root.md") : found2.target;
|
|
17077
|
+
return parseFrontmatter(fs43.readFileSync(file, "utf8")).meta;
|
|
16801
17078
|
} catch {
|
|
16802
17079
|
return null;
|
|
16803
17080
|
}
|
|
@@ -17046,6 +17323,7 @@ var init_deliveries = __esm({
|
|
|
17046
17323
|
init_complete();
|
|
17047
17324
|
init_outputs();
|
|
17048
17325
|
init_env();
|
|
17326
|
+
init_stop();
|
|
17049
17327
|
init_flows();
|
|
17050
17328
|
init_pause();
|
|
17051
17329
|
init_rules2();
|
|
@@ -17055,7 +17333,7 @@ var init_deliveries = __esm({
|
|
|
17055
17333
|
init_store();
|
|
17056
17334
|
init_types3();
|
|
17057
17335
|
init_worktree();
|
|
17058
|
-
auditPath = (deliveryId) =>
|
|
17336
|
+
auditPath = (deliveryId) => path48.join(DELIVERIES, `${deliveryId}.json`);
|
|
17059
17337
|
activeAudits = () => allAudits().filter((a) => a.status === "active");
|
|
17060
17338
|
auditRow = (audit) => readDeliveryRow({ ...audit, sessions: audit.sessions.map((s) => s.sessionId) });
|
|
17061
17339
|
lastSessionRunning = (audit) => audit.sessions[audit.sessions.length - 1]?.status === "running";
|
|
@@ -17083,6 +17361,41 @@ var init_deliveries = __esm({
|
|
|
17083
17361
|
}
|
|
17084
17362
|
});
|
|
17085
17363
|
|
|
17364
|
+
// ../cli/src/lib/agent/landing-lock.ts
|
|
17365
|
+
import fs44 from "node:fs";
|
|
17366
|
+
import path49 from "node:path";
|
|
17367
|
+
function lockLanding() {
|
|
17368
|
+
const dir = path49.join(SESSIONS_DIR, ".landing.lock");
|
|
17369
|
+
try {
|
|
17370
|
+
const entries = fs44.readdirSync(dir);
|
|
17371
|
+
const token3 = entries.length === 1 ? entries[0] : "";
|
|
17372
|
+
const match = /^(\d+)-[a-f0-9-]{36}$/.exec(token3);
|
|
17373
|
+
const stat = fs44.statSync(dir);
|
|
17374
|
+
if (match && Date.now() - stat.mtimeMs > landingLimits.staleMs) {
|
|
17375
|
+
stopWatchers(void 0, Number(match[1]), () => {
|
|
17376
|
+
const now = fs44.readdirSync(dir);
|
|
17377
|
+
return now.length === 1 && now[0] === token3 && Date.now() - fs44.statSync(dir).mtimeMs > landingLimits.staleMs;
|
|
17378
|
+
}, stat.birthtimeMs || stat.mtimeMs);
|
|
17379
|
+
}
|
|
17380
|
+
} catch {
|
|
17381
|
+
}
|
|
17382
|
+
return tryLock(dir);
|
|
17383
|
+
}
|
|
17384
|
+
function renewLandingLock() {
|
|
17385
|
+
const now = /* @__PURE__ */ new Date();
|
|
17386
|
+
fs44.utimesSync(path49.join(SESSIONS_DIR, ".landing.lock"), now, now);
|
|
17387
|
+
}
|
|
17388
|
+
var landingLimits;
|
|
17389
|
+
var init_landing_lock = __esm({
|
|
17390
|
+
"../cli/src/lib/agent/landing-lock.ts"() {
|
|
17391
|
+
"use strict";
|
|
17392
|
+
init_lock();
|
|
17393
|
+
init_paths();
|
|
17394
|
+
init_stop();
|
|
17395
|
+
landingLimits = { staleMs: 5 * 6e4 };
|
|
17396
|
+
}
|
|
17397
|
+
});
|
|
17398
|
+
|
|
17086
17399
|
// ../cli/src/lib/agent/answers.ts
|
|
17087
17400
|
function answerOutcome(delivery) {
|
|
17088
17401
|
return pendingAnswers(delivery).some((a) => a.outcome === "changed") ? "changed" : "unchanged";
|
|
@@ -17147,8 +17460,8 @@ var init_retry = __esm({
|
|
|
17147
17460
|
});
|
|
17148
17461
|
|
|
17149
17462
|
// ../cli/src/lib/agent/landing.ts
|
|
17150
|
-
import
|
|
17151
|
-
import
|
|
17463
|
+
import fs45 from "node:fs";
|
|
17464
|
+
import path50 from "node:path";
|
|
17152
17465
|
function waitingOut(delivery, now = Date.now()) {
|
|
17153
17466
|
const landing = delivery.landing;
|
|
17154
17467
|
if (!landing) return false;
|
|
@@ -17284,8 +17597,9 @@ function noteQueue(held4) {
|
|
|
17284
17597
|
}
|
|
17285
17598
|
async function advanceLanding() {
|
|
17286
17599
|
let release;
|
|
17600
|
+
let current;
|
|
17287
17601
|
try {
|
|
17288
|
-
release =
|
|
17602
|
+
release = lockLanding();
|
|
17289
17603
|
if (!release) return null;
|
|
17290
17604
|
const owed = await owedRestart();
|
|
17291
17605
|
if (owed) return owed;
|
|
@@ -17300,11 +17614,19 @@ async function advanceLanding() {
|
|
|
17300
17614
|
return null;
|
|
17301
17615
|
}
|
|
17302
17616
|
tried.add(picked.deliveryId);
|
|
17617
|
+
current = picked;
|
|
17618
|
+
renewLandingLock();
|
|
17303
17619
|
const step = await landStep(picked);
|
|
17304
17620
|
if (step.start) return step.start;
|
|
17305
17621
|
if (!step.done) return null;
|
|
17306
17622
|
}
|
|
17307
|
-
} catch {
|
|
17623
|
+
} catch (error) {
|
|
17624
|
+
if (current && error instanceof GitTimeoutError) {
|
|
17625
|
+
giveUpSlot(current, error.message);
|
|
17626
|
+
patchLanding(current.deliveryId, (landing) => {
|
|
17627
|
+
landing.retryAt = Date.now() + 3e4;
|
|
17628
|
+
});
|
|
17629
|
+
}
|
|
17308
17630
|
return null;
|
|
17309
17631
|
} finally {
|
|
17310
17632
|
release?.();
|
|
@@ -17621,7 +17943,7 @@ async function settleAlreadyLanded(delivery) {
|
|
|
17621
17943
|
return true;
|
|
17622
17944
|
}
|
|
17623
17945
|
function repairLanding() {
|
|
17624
|
-
const release =
|
|
17946
|
+
const release = lockLanding();
|
|
17625
17947
|
if (!release) return [];
|
|
17626
17948
|
try {
|
|
17627
17949
|
return repairIdleLanding();
|
|
@@ -17654,7 +17976,7 @@ var init_landing = __esm({
|
|
|
17654
17976
|
init_execution();
|
|
17655
17977
|
init_board();
|
|
17656
17978
|
init_io();
|
|
17657
|
-
|
|
17979
|
+
init_landing_lock();
|
|
17658
17980
|
init_paths();
|
|
17659
17981
|
init_answers();
|
|
17660
17982
|
init_commit_mode();
|
|
@@ -17695,9 +18017,9 @@ var init_landing = __esm({
|
|
|
17695
18017
|
sameDir = (a, b) => {
|
|
17696
18018
|
const real2 = (dir) => {
|
|
17697
18019
|
try {
|
|
17698
|
-
return
|
|
18020
|
+
return fs45.realpathSync(dir);
|
|
17699
18021
|
} catch {
|
|
17700
|
-
return
|
|
18022
|
+
return path50.resolve(dir);
|
|
17701
18023
|
}
|
|
17702
18024
|
};
|
|
17703
18025
|
return real2(a) === real2(b);
|
|
@@ -17706,35 +18028,35 @@ var init_landing = __esm({
|
|
|
17706
18028
|
});
|
|
17707
18029
|
|
|
17708
18030
|
// ../cli/src/lib/agent/pictures.ts
|
|
17709
|
-
import
|
|
17710
|
-
import
|
|
18031
|
+
import fs46 from "node:fs";
|
|
18032
|
+
import path51 from "node:path";
|
|
17711
18033
|
function claimRunPictures(box2, sessionId, names4 = []) {
|
|
17712
18034
|
if (!box2 || !freeBox(box2) || names4.length === 0) return [];
|
|
17713
18035
|
const from = pictureBox(box2);
|
|
17714
18036
|
const to = pictureBox(sessionId);
|
|
17715
18037
|
try {
|
|
17716
|
-
|
|
17717
|
-
|
|
18038
|
+
fs46.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
18039
|
+
fs46.renameSync(from, to);
|
|
17718
18040
|
} catch {
|
|
17719
18041
|
return [];
|
|
17720
18042
|
}
|
|
17721
|
-
return names4.filter(pictureName).map((name) =>
|
|
18043
|
+
return names4.filter(pictureName).map((name) => path51.join(to, name)).filter((file) => fs46.existsSync(file));
|
|
17722
18044
|
}
|
|
17723
18045
|
function returnRunPictures(sessionId, box2) {
|
|
17724
18046
|
if (!box2 || !boxId(box2)) return;
|
|
17725
18047
|
try {
|
|
17726
|
-
|
|
18048
|
+
fs46.renameSync(pictureBox(sessionId), pictureBox(box2));
|
|
17727
18049
|
} catch {
|
|
17728
18050
|
}
|
|
17729
18051
|
}
|
|
17730
18052
|
function moveRunPictures(from, to, files2 = []) {
|
|
17731
18053
|
if (files2.length === 0) return [];
|
|
17732
18054
|
try {
|
|
17733
|
-
|
|
18055
|
+
fs46.renameSync(pictureBox(from), pictureBox(to));
|
|
17734
18056
|
} catch {
|
|
17735
18057
|
return files2;
|
|
17736
18058
|
}
|
|
17737
|
-
return files2.map((file) =>
|
|
18059
|
+
return files2.map((file) => path51.join(pictureBox(to), path51.basename(file)));
|
|
17738
18060
|
}
|
|
17739
18061
|
var pictureName, boxId, freeBox, pictureBox;
|
|
17740
18062
|
var init_pictures = __esm({
|
|
@@ -17743,14 +18065,14 @@ var init_pictures = __esm({
|
|
|
17743
18065
|
init_paths();
|
|
17744
18066
|
pictureName = (name) => /^[0-9a-f-]{36}\.[a-z0-9]{2,5}$/.test(name);
|
|
17745
18067
|
boxId = (box2) => /^[0-9a-f-]{36}$/.test(box2);
|
|
17746
|
-
freeBox = (box2) => boxId(box2) && !
|
|
17747
|
-
pictureBox = (box2) =>
|
|
18068
|
+
freeBox = (box2) => boxId(box2) && !fs46.existsSync(path51.join(SESSIONS_DIR, `${box2}.log`));
|
|
18069
|
+
pictureBox = (box2) => path51.join(SESSIONS_DIR, `${box2}.images`);
|
|
17748
18070
|
}
|
|
17749
18071
|
});
|
|
17750
18072
|
|
|
17751
18073
|
// ../cli/src/lib/memories.ts
|
|
17752
|
-
import
|
|
17753
|
-
import
|
|
18074
|
+
import fs47 from "node:fs";
|
|
18075
|
+
import path52 from "node:path";
|
|
17754
18076
|
function readMemory(text5, builtIn, folder) {
|
|
17755
18077
|
const { meta, body } = splitFrontmatter(text5);
|
|
17756
18078
|
if (meta === null) return null;
|
|
@@ -17770,7 +18092,7 @@ function readMemory(text5, builtIn, folder) {
|
|
|
17770
18092
|
rules: body.trim(),
|
|
17771
18093
|
text: text5,
|
|
17772
18094
|
builtIn,
|
|
17773
|
-
dir:
|
|
18095
|
+
dir: path52.join(MEMORY, folder)
|
|
17774
18096
|
};
|
|
17775
18097
|
}
|
|
17776
18098
|
function globalMemories() {
|
|
@@ -17781,14 +18103,14 @@ function globalMemories() {
|
|
|
17781
18103
|
}
|
|
17782
18104
|
let folders2 = [];
|
|
17783
18105
|
try {
|
|
17784
|
-
folders2 =
|
|
18106
|
+
folders2 = fs47.readdirSync(MEMORY, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
17785
18107
|
} catch {
|
|
17786
18108
|
}
|
|
17787
18109
|
for (const name of folders2) {
|
|
17788
18110
|
if (name in BUNDLED_MEMORY_FILES || name === RESERVED_MEMORY_DIR) continue;
|
|
17789
|
-
const file =
|
|
17790
|
-
if (!
|
|
17791
|
-
const memory = readMemory(
|
|
18111
|
+
const file = path52.join(MEMORY, name, GLOBAL_MEMORY_FILE);
|
|
18112
|
+
if (!fs47.existsSync(file)) continue;
|
|
18113
|
+
const memory = readMemory(fs47.readFileSync(file, "utf8"), false, name);
|
|
17792
18114
|
if (memory) out.push(memory);
|
|
17793
18115
|
}
|
|
17794
18116
|
return out;
|
|
@@ -17844,16 +18166,16 @@ var init_facts = __esm({
|
|
|
17844
18166
|
});
|
|
17845
18167
|
|
|
17846
18168
|
// ../cli/src/lib/view/archive.ts
|
|
17847
|
-
import
|
|
17848
|
-
import
|
|
18169
|
+
import fs48 from "node:fs";
|
|
18170
|
+
import path53 from "node:path";
|
|
17849
18171
|
function archivedId(file) {
|
|
17850
|
-
const own = idPrefix(
|
|
17851
|
-
return own !== null ? own : idPrefix(
|
|
18172
|
+
const own = idPrefix(path53.basename(file));
|
|
18173
|
+
return own !== null ? own : idPrefix(path53.basename(path53.dirname(file)));
|
|
17852
18174
|
}
|
|
17853
18175
|
function readFile(file) {
|
|
17854
18176
|
const id = archivedId(file);
|
|
17855
18177
|
if (id === null) return null;
|
|
17856
|
-
const { meta, body } = parseFrontmatter(
|
|
18178
|
+
const { meta, body } = parseFrontmatter(fs48.readFileSync(file, "utf8"));
|
|
17857
18179
|
if (!meta) return null;
|
|
17858
18180
|
return {
|
|
17859
18181
|
row: {
|
|
@@ -17861,15 +18183,15 @@ function readFile(file) {
|
|
|
17861
18183
|
title: meta.title,
|
|
17862
18184
|
archived: meta.archived,
|
|
17863
18185
|
rejected: meta.rejected,
|
|
17864
|
-
relPath: rel(file).split(
|
|
18186
|
+
relPath: rel(file).split(path53.sep).join("/")
|
|
17865
18187
|
},
|
|
17866
18188
|
body: body.replace(/^\n+/, "").replace(/\s+$/, ""),
|
|
17867
18189
|
meta
|
|
17868
18190
|
};
|
|
17869
18191
|
}
|
|
17870
18192
|
function readArchive() {
|
|
17871
|
-
const relPath = rel(ARCHIVE).split(
|
|
17872
|
-
if (!
|
|
18193
|
+
const relPath = rel(ARCHIVE).split(path53.sep).join("/");
|
|
18194
|
+
if (!fs48.existsSync(ARCHIVE)) return { relPath, cards: [] };
|
|
17873
18195
|
const cards = walkMd(ARCHIVE).map(readFile).filter((found2) => found2 !== null).map((found2) => found2.row);
|
|
17874
18196
|
cards.sort((a, b) => b.id - a.id);
|
|
17875
18197
|
return { relPath, cards };
|
|
@@ -17884,7 +18206,7 @@ var init_archive = __esm({
|
|
|
17884
18206
|
});
|
|
17885
18207
|
|
|
17886
18208
|
// ../cli/src/lib/agent/prompts.ts
|
|
17887
|
-
import
|
|
18209
|
+
import path54 from "node:path";
|
|
17888
18210
|
function resumePrompt(deliveryId, cardId2, action) {
|
|
17889
18211
|
if (!deliveryId) return RESUME_PROMPT;
|
|
17890
18212
|
if (action === "conflict") {
|
|
@@ -17928,7 +18250,7 @@ function buildAsk(rawReq, notes = []) {
|
|
|
17928
18250
|
}
|
|
17929
18251
|
function cardFileOf(id) {
|
|
17930
18252
|
const found2 = id === void 0 ? null : locate(id);
|
|
17931
|
-
return found2 ? found2.kind === "group" ?
|
|
18253
|
+
return found2 ? found2.kind === "group" ? path54.join(found2.target, "root.md") : found2.target : "";
|
|
17932
18254
|
}
|
|
17933
18255
|
function realPaths(text5, req) {
|
|
17934
18256
|
if (req.action !== "scheduled" && req.action !== "sub") return text5;
|
|
@@ -18021,7 +18343,7 @@ function frozenRules(req) {
|
|
|
18021
18343
|
function archivedCardFile(id) {
|
|
18022
18344
|
const found2 = locateArchived(id);
|
|
18023
18345
|
if (!found2) return `${rel(ARCHIVE)}/`;
|
|
18024
|
-
return rel(found2.kind === "group" ?
|
|
18346
|
+
return rel(found2.kind === "group" ? path54.join(found2.target, "root.md") : found2.target);
|
|
18025
18347
|
}
|
|
18026
18348
|
function shippedLines(cardId2) {
|
|
18027
18349
|
const delivery = endedDelivery(`${cardId2}`);
|
|
@@ -18192,7 +18514,7 @@ function actionPrompt(req, command, notes) {
|
|
|
18192
18514
|
case "spec": {
|
|
18193
18515
|
const agent = findSpecAgent(req.specAgent ?? "");
|
|
18194
18516
|
const found2 = req.id === void 0 ? null : locate(req.id);
|
|
18195
|
-
const cardFile3 = found2 ? rel(found2.kind === "group" ?
|
|
18517
|
+
const cardFile3 = found2 ? rel(found2.kind === "group" ? path54.join(found2.target, "root.md") : found2.target) : `task #${req.id}`;
|
|
18196
18518
|
const own = agent ? specAgentInstructions(agent) : null;
|
|
18197
18519
|
if (own) notes.push(...own.notes);
|
|
18198
18520
|
const memory = agent ? agentMemoryBlock(agent) : "";
|
|
@@ -18305,288 +18627,81 @@ ${shared}` : ""
|
|
|
18305
18627
|
req.notes ? `Extra notes: ${req.notes}` : "",
|
|
18306
18628
|
`Don't ask me questions with human-in-the-loop. Leave any questions as open questions.`
|
|
18307
18629
|
].filter(Boolean).join(" ");
|
|
18308
|
-
case "resolve":
|
|
18309
|
-
return [
|
|
18310
|
-
`${kb}. Apply my answers to the open questions on task ${req.id} ${named2} following \`akb guide resolve\`.`,
|
|
18311
|
-
carriedNote(req),
|
|
18312
|
-
answeredNote(req.id, command),
|
|
18313
|
-
req.notes ? `Extra notes: ${req.notes}` : ""
|
|
18314
|
-
].filter(Boolean).join(" ");
|
|
18315
|
-
}
|
|
18316
|
-
}
|
|
18317
|
-
function discardedCardsPrompt(cards = []) {
|
|
18318
|
-
if (!cards.length) return "";
|
|
18319
|
-
return "The user discarded the cards listed below. Their absence is intentional. Do not restore or recreate them, including under new IDs or filenames. Continue the remaining work from the current board, preserving the other cards.\n\n" + cards.map((c) => `- #${c.id}: ${c.path}`).join("\n");
|
|
18320
|
-
}
|
|
18321
|
-
var RESUME_PROMPT, DELIVERY_RESUME, DELIVERY_RESUME_CARD, RESTARTABLE, RESTART_LEAD, restartable, NO_IMPLEMENT, BOARD_FOLDER, CARD_FILE, PROJECT_FOLDER, PLANNING_FOR, SPEC_SELECTOR_FOR, reflectedCards;
|
|
18322
|
-
var init_prompts = __esm({
|
|
18323
|
-
"../cli/src/lib/agent/prompts.ts"() {
|
|
18324
|
-
"use strict";
|
|
18325
|
-
init_cards();
|
|
18326
|
-
init_memory();
|
|
18327
|
-
init_memories();
|
|
18328
|
-
init_paths();
|
|
18329
|
-
init_agents();
|
|
18330
|
-
init_command();
|
|
18331
|
-
init_deliveries();
|
|
18332
|
-
init_env();
|
|
18333
|
-
init_facts();
|
|
18334
|
-
init_outputs();
|
|
18335
|
-
init_archive();
|
|
18336
|
-
init_flows();
|
|
18337
|
-
init_resolve();
|
|
18338
|
-
init_runner();
|
|
18339
|
-
init_workflows();
|
|
18340
|
-
init_catalog2();
|
|
18341
|
-
init_roles();
|
|
18342
|
-
init_rules2();
|
|
18343
|
-
init_settings2();
|
|
18344
|
-
init_types3();
|
|
18345
|
-
RESUME_PROMPT = [
|
|
18346
|
-
`Continue. The previous run ended before it finished the task.`,
|
|
18347
|
-
`Pick up where you left off and carry it through.`
|
|
18348
|
-
].join(" ");
|
|
18349
|
-
DELIVERY_RESUME = [
|
|
18350
|
-
`Continue delivery %s. The previous run ended before the delivery finished.`,
|
|
18351
|
-
`Re-enter the flow from the top and check each step's precondition before you do it \u2014 work that is already done is done, so do not repeat it.`
|
|
18352
|
-
].join(" ");
|
|
18353
|
-
DELIVERY_RESUME_CARD = `Build the card as the delivery holds it, not as the file reads now: \`%c\` prints the approved copy.`;
|
|
18354
|
-
RESTARTABLE = /* @__PURE__ */ new Set([
|
|
18355
|
-
"implement",
|
|
18356
|
-
"clarify",
|
|
18357
|
-
"resolve",
|
|
18358
|
-
"approve",
|
|
18359
|
-
"archive",
|
|
18360
|
-
"spec",
|
|
18361
|
-
"conflict",
|
|
18362
|
-
"scheduled"
|
|
18363
|
-
]);
|
|
18364
|
-
RESTART_LEAD = [
|
|
18365
|
-
`The session you were continuing is gone, and this one holds nothing of it.`,
|
|
18366
|
-
`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.`
|
|
18367
|
-
].join(" ");
|
|
18368
|
-
restartable = (run2) => (run2.cardId !== null || !!run2.deliveryId) && RESTARTABLE.has(run2.action);
|
|
18369
|
-
NO_IMPLEMENT = `(Unless the request explicitly asks for implementation, don't implement it; revising the card's drafts is not implementing.)`;
|
|
18370
|
-
BOARD_FOLDER = "<board folder>";
|
|
18371
|
-
CARD_FILE = "<card file>";
|
|
18372
|
-
PROJECT_FOLDER = "<project folder>";
|
|
18373
|
-
PLANNING_FOR = /* @__PURE__ */ new Set(["create", "clarify", "resolve", "approve", "edit"]);
|
|
18374
|
-
SPEC_SELECTOR_FOR = /* @__PURE__ */ new Set(["clarify", "resolve", "approve", "edit"]);
|
|
18375
|
-
reflectedCards = (req) => req.cards ?? (req.id !== void 0 ? [req.id] : []);
|
|
18376
|
-
}
|
|
18377
|
-
});
|
|
18378
|
-
|
|
18379
|
-
// ../cli/src/lib/agent/stop.ts
|
|
18380
|
-
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
18381
|
-
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
18382
|
-
import fs47 from "node:fs";
|
|
18383
|
-
import os6 from "node:os";
|
|
18384
|
-
function markedInProc(entry, strict = false) {
|
|
18385
|
-
const pids = [];
|
|
18386
|
-
for (const name of fs47.readdirSync("/proc")) {
|
|
18387
|
-
if (!/^\d+$/.test(name)) continue;
|
|
18388
|
-
try {
|
|
18389
|
-
if (fs47.readFileSync(`/proc/${name}/environ`, "utf8").split("\0").includes(entry)) pids.push(Number(name));
|
|
18390
|
-
} catch (error) {
|
|
18391
|
-
if (strict && !["ENOENT", "ESRCH"].includes(error.code ?? "")) {
|
|
18392
|
-
const stat = fs47.statSync(`/proc/${name}`, { throwIfNoEntry: false });
|
|
18393
|
-
if (stat?.uid === process.getuid?.()) throw error;
|
|
18394
|
-
}
|
|
18395
|
-
}
|
|
18396
|
-
}
|
|
18397
|
-
return pids;
|
|
18398
|
-
}
|
|
18399
|
-
function markedInPs(entry, roots) {
|
|
18400
|
-
const ps = spawnSync4("ps", ["-A", "-E", "-ww", "-o", "pid=,ppid=,command="], {
|
|
18401
|
-
encoding: "utf8",
|
|
18402
|
-
maxBuffer: 64 * 1024 * 1024,
|
|
18403
|
-
timeout: 5e3
|
|
18404
|
-
});
|
|
18405
|
-
if (ps.status !== 0 || !ps.stdout) throw new Error("\u65E0\u6CD5\u67E5\u8BE2\u8FDB\u7A0B\u6811\u3002");
|
|
18406
|
-
const children = /* @__PURE__ */ new Map();
|
|
18407
|
-
const listed2 = /* @__PURE__ */ new Set();
|
|
18408
|
-
const found2 = /* @__PURE__ */ new Set();
|
|
18409
|
-
for (const line of ps.stdout.split("\n")) {
|
|
18410
|
-
const m = /^\s*(\d+)\s+(\d+)\s(.*)$/.exec(line);
|
|
18411
|
-
if (!m) continue;
|
|
18412
|
-
const pid = Number(m[1]);
|
|
18413
|
-
const ppid = Number(m[2]);
|
|
18414
|
-
listed2.add(pid);
|
|
18415
|
-
children.set(ppid, [...children.get(ppid) ?? [], pid]);
|
|
18416
|
-
if (`${m[3]} `.includes(` ${entry} `)) found2.add(pid);
|
|
18417
|
-
}
|
|
18418
|
-
const queue = [...found2, ...roots.filter((pid) => listed2.has(pid))];
|
|
18419
|
-
for (const pid of queue) {
|
|
18420
|
-
found2.add(pid);
|
|
18421
|
-
for (const child of children.get(pid) ?? []) if (!found2.has(child)) queue.push(child);
|
|
18422
|
-
}
|
|
18423
|
-
return [...found2];
|
|
18424
|
-
}
|
|
18425
|
-
function marked(mark, roots = []) {
|
|
18426
|
-
if (WINDOWS3 || !mark.value) return [];
|
|
18427
|
-
const entry = `${mark.name}=${mark.value}`;
|
|
18428
|
-
try {
|
|
18429
|
-
const pids = process.platform === "linux" ? markedInProc(entry) : markedInPs(entry, roots);
|
|
18430
|
-
return pids.filter((pid) => pid !== process.pid);
|
|
18431
|
-
} catch {
|
|
18432
|
-
return [];
|
|
18433
|
-
}
|
|
18434
|
-
}
|
|
18435
|
-
function signal(pids, sig) {
|
|
18436
|
-
for (const pid of pids) {
|
|
18437
|
-
try {
|
|
18438
|
-
process.kill(pid, sig);
|
|
18439
|
-
} catch {
|
|
18440
|
-
}
|
|
18441
|
-
}
|
|
18442
|
-
}
|
|
18443
|
-
function startedUnder(list, root, since) {
|
|
18444
|
-
const self = list.procs.find((p) => p.pid === root);
|
|
18445
|
-
const from = since ?? self?.startedAt;
|
|
18446
|
-
if (from === void 0 || list.bootedAt > from) return [];
|
|
18447
|
-
if (self && self.startedAt > from) return [];
|
|
18448
|
-
const found2 = self ? [root] : [];
|
|
18449
|
-
const seen = /* @__PURE__ */ new Set([root]);
|
|
18450
|
-
const queue = [{ pid: root, startedAt: from }];
|
|
18451
|
-
for (const parent of queue) {
|
|
18452
|
-
for (const p of list.procs) {
|
|
18453
|
-
if (p.ppid !== parent.pid || seen.has(p.pid) || p.startedAt < parent.startedAt) continue;
|
|
18454
|
-
seen.add(p.pid);
|
|
18455
|
-
found2.push(p.pid);
|
|
18456
|
-
queue.push(p);
|
|
18457
|
-
}
|
|
18458
|
-
}
|
|
18459
|
-
return found2.reverse();
|
|
18460
|
-
}
|
|
18461
|
-
function readProcList(out) {
|
|
18462
|
-
const procs = [];
|
|
18463
|
-
let bootedAt;
|
|
18464
|
-
for (const line of out.split(/\r?\n/)) {
|
|
18465
|
-
const boot = /^boot (\d+)$/.exec(line.trim());
|
|
18466
|
-
if (boot) bootedAt = Number(boot[1]);
|
|
18467
|
-
const m = /^(\d+) (\d+) (\d+)$/.exec(line.trim());
|
|
18468
|
-
if (m) procs.push({ pid: Number(m[1]), ppid: Number(m[2]), startedAt: Number(m[3]) });
|
|
18469
|
-
}
|
|
18470
|
-
return bootedAt !== void 0 && procs.length ? { procs, bootedAt } : void 0;
|
|
18471
|
-
}
|
|
18472
|
-
function procsOnWindows() {
|
|
18473
|
-
const ps = spawnSync4("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", LIST_PROCS], {
|
|
18474
|
-
encoding: "utf8",
|
|
18475
|
-
maxBuffer: 64 * 1024 * 1024,
|
|
18476
|
-
timeout: 5e3,
|
|
18477
|
-
windowsHide: true
|
|
18478
|
-
});
|
|
18479
|
-
return ps.status === 0 && ps.stdout ? readProcList(ps.stdout) : void 0;
|
|
18480
|
-
}
|
|
18481
|
-
function killUnderOnWindows(pid, since) {
|
|
18482
|
-
if (!WINDOWS3 || !pid) return;
|
|
18483
|
-
const list = procsOnWindows();
|
|
18484
|
-
if (!list) return;
|
|
18485
|
-
const pids = startedUnder(list, pid, since).filter((p) => p !== process.pid);
|
|
18486
|
-
if (!pids.length) return;
|
|
18487
|
-
spawnSync4("taskkill", ["/F", ...pids.flatMap((p) => ["/PID", String(p)])], { stdio: "ignore", windowsHide: true, timeout: 1e4 });
|
|
18488
|
-
}
|
|
18489
|
-
function killMarked(mark, agent) {
|
|
18490
|
-
if (WINDOWS3) {
|
|
18491
|
-
if (agent) killUnderOnWindows(agent.pid, agent.since);
|
|
18492
|
-
return;
|
|
18493
|
-
}
|
|
18494
|
-
signal(marked(mark), "SIGKILL");
|
|
18495
|
-
}
|
|
18496
|
-
function killTreeOnWindows(pid) {
|
|
18497
|
-
if (!WINDOWS3 || !pid) return false;
|
|
18498
|
-
const out = spawnSync4("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true, timeout: 1e4 });
|
|
18499
|
-
return out.status === 0;
|
|
18500
|
-
}
|
|
18501
|
-
function later(ms, fn) {
|
|
18502
|
-
const t = setTimeout(fn, ms);
|
|
18503
|
-
if (typeof t.unref === "function") t.unref();
|
|
18504
|
-
}
|
|
18505
|
-
function endAgent(child, mark, graceMs) {
|
|
18506
|
-
const term = (sig) => {
|
|
18507
|
-
try {
|
|
18508
|
-
child.kill(sig);
|
|
18509
|
-
} catch {
|
|
18510
|
-
}
|
|
18511
|
-
};
|
|
18512
|
-
if (killTreeOnWindows(child.pid)) return;
|
|
18513
|
-
killUnderOnWindows(child.pid);
|
|
18514
|
-
const seen = new Set(marked(mark, child.pid ? [child.pid] : []));
|
|
18515
|
-
term("SIGTERM");
|
|
18516
|
-
signal([...seen].filter((pid) => pid !== child.pid), "SIGTERM");
|
|
18517
|
-
const round = (left) => {
|
|
18518
|
-
const pids = marked(mark, [...seen]);
|
|
18519
|
-
signal(pids, "SIGKILL");
|
|
18520
|
-
for (const pid of pids) seen.add(pid);
|
|
18521
|
-
if (pids.length && left > 1) later(KILL_ROUND_MS, () => round(left - 1));
|
|
18522
|
-
};
|
|
18523
|
-
later(graceMs, () => {
|
|
18524
|
-
term("SIGKILL");
|
|
18525
|
-
round(KILL_ROUNDS);
|
|
18526
|
-
});
|
|
18527
|
-
}
|
|
18528
|
-
function killRunning() {
|
|
18529
|
-
for (const [child, mark] of running) {
|
|
18530
|
-
if (killTreeOnWindows(child.pid)) continue;
|
|
18531
|
-
killUnderOnWindows(child.pid);
|
|
18532
|
-
signal(marked(mark, child.pid ? [child.pid] : []), "SIGKILL");
|
|
18533
|
-
try {
|
|
18534
|
-
child.kill("SIGKILL");
|
|
18535
|
-
} catch {
|
|
18536
|
-
}
|
|
18630
|
+
case "resolve":
|
|
18631
|
+
return [
|
|
18632
|
+
`${kb}. Apply my answers to the open questions on task ${req.id} ${named2} following \`akb guide resolve\`.`,
|
|
18633
|
+
carriedNote(req),
|
|
18634
|
+
answeredNote(req.id, command),
|
|
18635
|
+
req.notes ? `Extra notes: ${req.notes}` : ""
|
|
18636
|
+
].filter(Boolean).join(" ");
|
|
18537
18637
|
}
|
|
18538
|
-
running.clear();
|
|
18539
|
-
}
|
|
18540
|
-
function unlisten() {
|
|
18541
|
-
for (const [sig, fn] of listening) process.removeListener(sig, fn);
|
|
18542
|
-
listening.clear();
|
|
18543
|
-
}
|
|
18544
|
-
function onQuit(sig) {
|
|
18545
|
-
killRunning();
|
|
18546
|
-
unlisten();
|
|
18547
|
-
if (process.listenerCount(sig) === 0) process.exit(128 + os6.constants.signals[sig]);
|
|
18548
18638
|
}
|
|
18549
|
-
function
|
|
18550
|
-
if (!
|
|
18551
|
-
|
|
18552
|
-
child.once("close", () => {
|
|
18553
|
-
running.delete(child);
|
|
18554
|
-
if (!running.size) unlisten();
|
|
18555
|
-
});
|
|
18556
|
-
for (const sig of QUIT_SIGNALS) {
|
|
18557
|
-
if (listening.has(sig) || sig === "SIGINT" && interruptIsTheCallers) continue;
|
|
18558
|
-
const fn = () => onQuit(sig);
|
|
18559
|
-
listening.set(sig, fn);
|
|
18560
|
-
process.on(sig, fn);
|
|
18561
|
-
}
|
|
18639
|
+
function discardedCardsPrompt(cards = []) {
|
|
18640
|
+
if (!cards.length) return "";
|
|
18641
|
+
return "The user discarded the cards listed below. Their absence is intentional. Do not restore or recreate them, including under new IDs or filenames. Continue the remaining work from the current board, preserving the other cards.\n\n" + cards.map((c) => `- #${c.id}: ${c.path}`).join("\n");
|
|
18562
18642
|
}
|
|
18563
|
-
var
|
|
18564
|
-
var
|
|
18565
|
-
"../cli/src/lib/agent/
|
|
18643
|
+
var RESUME_PROMPT, DELIVERY_RESUME, DELIVERY_RESUME_CARD, RESTARTABLE, RESTART_LEAD, restartable, NO_IMPLEMENT, BOARD_FOLDER, CARD_FILE, PROJECT_FOLDER, PLANNING_FOR, SPEC_SELECTOR_FOR, reflectedCards;
|
|
18644
|
+
var init_prompts = __esm({
|
|
18645
|
+
"../cli/src/lib/agent/prompts.ts"() {
|
|
18566
18646
|
"use strict";
|
|
18647
|
+
init_cards();
|
|
18648
|
+
init_memory();
|
|
18649
|
+
init_memories();
|
|
18650
|
+
init_paths();
|
|
18651
|
+
init_agents();
|
|
18652
|
+
init_command();
|
|
18653
|
+
init_deliveries();
|
|
18567
18654
|
init_env();
|
|
18568
|
-
|
|
18569
|
-
|
|
18570
|
-
|
|
18571
|
-
|
|
18572
|
-
|
|
18573
|
-
|
|
18574
|
-
|
|
18575
|
-
|
|
18576
|
-
|
|
18577
|
-
|
|
18578
|
-
|
|
18579
|
-
|
|
18580
|
-
|
|
18581
|
-
|
|
18582
|
-
|
|
18655
|
+
init_facts();
|
|
18656
|
+
init_outputs();
|
|
18657
|
+
init_archive();
|
|
18658
|
+
init_flows();
|
|
18659
|
+
init_resolve();
|
|
18660
|
+
init_runner();
|
|
18661
|
+
init_workflows();
|
|
18662
|
+
init_catalog2();
|
|
18663
|
+
init_roles();
|
|
18664
|
+
init_rules2();
|
|
18665
|
+
init_settings2();
|
|
18666
|
+
init_types3();
|
|
18667
|
+
RESUME_PROMPT = [
|
|
18668
|
+
`Continue. The previous run ended before it finished the task.`,
|
|
18669
|
+
`Pick up where you left off and carry it through.`
|
|
18670
|
+
].join(" ");
|
|
18671
|
+
DELIVERY_RESUME = [
|
|
18672
|
+
`Continue delivery %s. The previous run ended before the delivery finished.`,
|
|
18673
|
+
`Re-enter the flow from the top and check each step's precondition before you do it \u2014 work that is already done is done, so do not repeat it.`
|
|
18674
|
+
].join(" ");
|
|
18675
|
+
DELIVERY_RESUME_CARD = `Build the card as the delivery holds it, not as the file reads now: \`%c\` prints the approved copy.`;
|
|
18676
|
+
RESTARTABLE = /* @__PURE__ */ new Set([
|
|
18677
|
+
"implement",
|
|
18678
|
+
"clarify",
|
|
18679
|
+
"resolve",
|
|
18680
|
+
"approve",
|
|
18681
|
+
"archive",
|
|
18682
|
+
"spec",
|
|
18683
|
+
"conflict",
|
|
18684
|
+
"scheduled"
|
|
18685
|
+
]);
|
|
18686
|
+
RESTART_LEAD = [
|
|
18687
|
+
`The session you were continuing is gone, and this one holds nothing of it.`,
|
|
18688
|
+
`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.`
|
|
18689
|
+
].join(" ");
|
|
18690
|
+
restartable = (run2) => (run2.cardId !== null || !!run2.deliveryId) && RESTARTABLE.has(run2.action);
|
|
18691
|
+
NO_IMPLEMENT = `(Unless the request explicitly asks for implementation, don't implement it; revising the card's drafts is not implementing.)`;
|
|
18692
|
+
BOARD_FOLDER = "<board folder>";
|
|
18693
|
+
CARD_FILE = "<card file>";
|
|
18694
|
+
PROJECT_FOLDER = "<project folder>";
|
|
18695
|
+
PLANNING_FOR = /* @__PURE__ */ new Set(["create", "clarify", "resolve", "approve", "edit"]);
|
|
18696
|
+
SPEC_SELECTOR_FOR = /* @__PURE__ */ new Set(["clarify", "resolve", "approve", "edit"]);
|
|
18697
|
+
reflectedCards = (req) => req.cards ?? (req.id !== void 0 ? [req.id] : []);
|
|
18583
18698
|
}
|
|
18584
18699
|
});
|
|
18585
18700
|
|
|
18586
18701
|
// ../cli/src/lib/agent/sessions.ts
|
|
18587
18702
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
18588
|
-
import
|
|
18589
|
-
import
|
|
18703
|
+
import fs49 from "node:fs";
|
|
18704
|
+
import path55 from "node:path";
|
|
18590
18705
|
function reap(runs, reaped = [], restore = []) {
|
|
18591
18706
|
let changed = false;
|
|
18592
18707
|
const now = Date.now();
|
|
@@ -18606,7 +18721,7 @@ function reap(runs, reaped = [], restore = []) {
|
|
|
18606
18721
|
}
|
|
18607
18722
|
function stampDuration(run2, endedAt) {
|
|
18608
18723
|
try {
|
|
18609
|
-
|
|
18724
|
+
fs49.appendFileSync(run2.logPath, durationLine(endedAt - run2.startedAt));
|
|
18610
18725
|
} catch {
|
|
18611
18726
|
}
|
|
18612
18727
|
}
|
|
@@ -18620,7 +18735,7 @@ function cardNow(cardId2) {
|
|
|
18620
18735
|
try {
|
|
18621
18736
|
const file = cardFile(cardId2);
|
|
18622
18737
|
if (!file) return null;
|
|
18623
|
-
const { meta } = parseFrontmatter(
|
|
18738
|
+
const { meta } = parseFrontmatter(fs49.readFileSync(file, "utf8"));
|
|
18624
18739
|
if (!meta) return null;
|
|
18625
18740
|
return { status: meta.status || "todo", questions: openOf(meta.questions).length, title: meta.title };
|
|
18626
18741
|
} catch {
|
|
@@ -18757,9 +18872,9 @@ function leftBoardOnLanding(id) {
|
|
|
18757
18872
|
function archivedRejected(id) {
|
|
18758
18873
|
const filed = locateArchived(id);
|
|
18759
18874
|
if (!filed) return null;
|
|
18760
|
-
const file = filed.kind === "group" ?
|
|
18875
|
+
const file = filed.kind === "group" ? path55.join(filed.target, "root.md") : filed.target;
|
|
18761
18876
|
try {
|
|
18762
|
-
return !!parseFrontmatter(
|
|
18877
|
+
return !!parseFrontmatter(fs49.readFileSync(file, "utf8")).meta?.rejected;
|
|
18763
18878
|
} catch {
|
|
18764
18879
|
return false;
|
|
18765
18880
|
}
|
|
@@ -19016,7 +19131,7 @@ function openRun(req, prompt, notes = [], sessionId = randomUUID6(), said3) {
|
|
|
19016
19131
|
return { run: record, spec };
|
|
19017
19132
|
}
|
|
19018
19133
|
function planningSession(cardId2, cwd = REPO_ROOT) {
|
|
19019
|
-
const here = (dir) => !dir ||
|
|
19134
|
+
const here = (dir) => !dir || path55.resolve(dir) === cwd;
|
|
19020
19135
|
const ended = readRuns().filter((r) => r.status === "done").sort((a, b) => b.startedAt - a.startedAt);
|
|
19021
19136
|
const of = (run3, fork) => {
|
|
19022
19137
|
if (!here(run3.cwd)) return { none: "elsewhere" };
|
|
@@ -19085,7 +19200,9 @@ function requestOf(record) {
|
|
|
19085
19200
|
pictures: record.pictures
|
|
19086
19201
|
};
|
|
19087
19202
|
}
|
|
19088
|
-
async function openResume(id,
|
|
19203
|
+
async function openResume(id, options) {
|
|
19204
|
+
const build = typeof options === "object" ? options : void 0;
|
|
19205
|
+
const requireSession = options === true || !!build;
|
|
19089
19206
|
const restore = [];
|
|
19090
19207
|
const runs = withRuns((all2) => {
|
|
19091
19208
|
reap(all2, [], restore);
|
|
@@ -19110,7 +19227,7 @@ async function openResume(id, build) {
|
|
|
19110
19227
|
if (refused) return refused;
|
|
19111
19228
|
}
|
|
19112
19229
|
const retrying = lacksSession(prev);
|
|
19113
|
-
if (
|
|
19230
|
+
if (requireSession && (!resumeSessionId(prev) || !resumesUnder(prev.harness))) {
|
|
19114
19231
|
return refusal("runNoSession", "\u539F\u6784\u5EFA\u6CA1\u6709\u53EF\u6062\u590D\u7684\u4F1A\u8BDD\u6807\u8BC6\uFF0C\u6216\u539F Agent \u4E0D\u652F\u6301\u7EED\u8DD1\uFF1B\u672C\u6B21\u672A\u542F\u52A8\u65B0\u4F1A\u8BDD\u3002");
|
|
19115
19232
|
}
|
|
19116
19233
|
const ask2 = retrying && prev.status !== "stopped" ? retryAsk(prev) : void 0;
|
|
@@ -19249,8 +19366,8 @@ async function resumeHeld(prev, plan, sessionId, resuming, fresh) {
|
|
|
19249
19366
|
clearAsks(prev.sessionId);
|
|
19250
19367
|
try {
|
|
19251
19368
|
if (prev.action !== "create") {
|
|
19252
|
-
|
|
19253
|
-
|
|
19369
|
+
fs49.unlinkSync(prev.logPath);
|
|
19370
|
+
fs49.rmSync(eventsPathOf(prev.sessionId), { force: true });
|
|
19254
19371
|
}
|
|
19255
19372
|
} catch {
|
|
19256
19373
|
}
|
|
@@ -19274,8 +19391,8 @@ function forgetRun(sessionId) {
|
|
|
19274
19391
|
if (!gone) return;
|
|
19275
19392
|
clearAsks(sessionId);
|
|
19276
19393
|
try {
|
|
19277
|
-
|
|
19278
|
-
|
|
19394
|
+
fs49.unlinkSync(gone.logPath);
|
|
19395
|
+
fs49.rmSync(eventsPathOf(gone.sessionId), { force: true });
|
|
19279
19396
|
} catch {
|
|
19280
19397
|
}
|
|
19281
19398
|
}
|
|
@@ -19304,14 +19421,14 @@ function noteSpecRefusals(sessionId, refused) {
|
|
|
19304
19421
|
}
|
|
19305
19422
|
function clearAsks(sessionId) {
|
|
19306
19423
|
try {
|
|
19307
|
-
|
|
19424
|
+
fs49.unlinkSync(asksPathOf(sessionId));
|
|
19308
19425
|
} catch {
|
|
19309
19426
|
}
|
|
19310
19427
|
}
|
|
19311
19428
|
function readAsks(sessionId) {
|
|
19312
19429
|
let data;
|
|
19313
19430
|
try {
|
|
19314
|
-
data = JSON.parse(
|
|
19431
|
+
data = JSON.parse(fs49.readFileSync(asksPathOf(sessionId), "utf8"));
|
|
19315
19432
|
} catch {
|
|
19316
19433
|
return { asks: [], refines: [], refused: [] };
|
|
19317
19434
|
}
|
|
@@ -19338,10 +19455,10 @@ function readAsks(sessionId) {
|
|
|
19338
19455
|
return { asks: asks.filter((a) => !discarded.has(a.cardId)), refines: refines.filter((a) => !discarded.has(a.cardId)), refused };
|
|
19339
19456
|
}
|
|
19340
19457
|
function writeAsks(sessionId, file) {
|
|
19341
|
-
|
|
19458
|
+
fs49.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
19342
19459
|
const tmp = `${asksPathOf(sessionId)}.tmp`;
|
|
19343
|
-
|
|
19344
|
-
|
|
19460
|
+
fs49.writeFileSync(tmp, JSON.stringify(file, null, 2) + "\n");
|
|
19461
|
+
fs49.renameSync(tmp, asksPathOf(sessionId));
|
|
19345
19462
|
}
|
|
19346
19463
|
function markSpawned(sessionId, pid) {
|
|
19347
19464
|
patch(sessionId, (r) => {
|
|
@@ -19429,7 +19546,10 @@ async function cancelDelivery(id) {
|
|
|
19429
19546
|
if (!id.trim()) return { ok: false, ...refusal("deliveryUnnamed", "name the delivery to cancel", { action: "cancel" }) };
|
|
19430
19547
|
const delivery = namedDelivery(id);
|
|
19431
19548
|
if (!delivery) return { ok: false, ...unknownDelivery(id) };
|
|
19432
|
-
if (delivery.status !== "active")
|
|
19549
|
+
if (delivery.status !== "active") {
|
|
19550
|
+
tidyCheckout(delivery.deliveryId);
|
|
19551
|
+
return { ok: true, deliveryId: delivery.deliveryId };
|
|
19552
|
+
}
|
|
19433
19553
|
endDelivery(delivery.deliveryId, "cancelled");
|
|
19434
19554
|
const live = readRuns().find((r) => r.status === "running" && r.deliveryId === delivery.deliveryId && r.action !== "sub");
|
|
19435
19555
|
if (live) await stopRun(live.sessionId);
|
|
@@ -19454,7 +19574,9 @@ async function runIsDown(sessionId) {
|
|
|
19454
19574
|
}
|
|
19455
19575
|
async function resumeDelivery(id) {
|
|
19456
19576
|
if (!id.trim()) return { ok: false, ...refusal("deliveryUnnamed", "name the delivery to carry on", { action: "resume" }) };
|
|
19457
|
-
const
|
|
19577
|
+
const execution = executionOf();
|
|
19578
|
+
if (execution && !await executionHeld(execution)) return { ok: false, error: "\u65E0\u6CD5\u786E\u8BA4\u672C\u6B21\u7EED\u8DD1\u7684\u6267\u884C\u6743\uFF0C\u4EA4\u4ED8\u4FDD\u6301\u539F\u72B6\u3002" };
|
|
19579
|
+
const put = resumeRecord(id, execution);
|
|
19458
19580
|
if (!put.ok) return put;
|
|
19459
19581
|
const delivery = put.delivery;
|
|
19460
19582
|
if (await settleAlreadyLanded(delivery)) {
|
|
@@ -19495,12 +19617,12 @@ async function discardDelivery(id) {
|
|
|
19495
19617
|
return { ok: true, deliveryId: delivery.deliveryId };
|
|
19496
19618
|
}
|
|
19497
19619
|
function writeSpec(spec) {
|
|
19498
|
-
|
|
19499
|
-
|
|
19620
|
+
fs49.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
19621
|
+
fs49.writeFileSync(specPathOf(spec.sessionId), JSON.stringify(spec, null, 2) + "\n");
|
|
19500
19622
|
}
|
|
19501
19623
|
function readSpec(sessionId) {
|
|
19502
19624
|
try {
|
|
19503
|
-
const spec = JSON.parse(
|
|
19625
|
+
const spec = JSON.parse(fs49.readFileSync(specPathOf(sessionId), "utf8"));
|
|
19504
19626
|
return spec && Array.isArray(spec.plan?.argv) ? spec : null;
|
|
19505
19627
|
} catch {
|
|
19506
19628
|
return null;
|
|
@@ -19508,28 +19630,28 @@ function readSpec(sessionId) {
|
|
|
19508
19630
|
}
|
|
19509
19631
|
function dropSpec(sessionId) {
|
|
19510
19632
|
try {
|
|
19511
|
-
|
|
19633
|
+
fs49.unlinkSync(specPathOf(sessionId));
|
|
19512
19634
|
} catch {
|
|
19513
19635
|
}
|
|
19514
19636
|
}
|
|
19515
19637
|
async function acquireIndexLock(giveUp) {
|
|
19516
19638
|
const until = Date.now() + INDEX_WAIT_MS;
|
|
19517
|
-
const holder =
|
|
19639
|
+
const holder = path55.join(INDEX_LOCK2, "holder");
|
|
19518
19640
|
for (; ; ) {
|
|
19519
19641
|
if (giveUp()) return null;
|
|
19520
19642
|
try {
|
|
19521
|
-
|
|
19522
|
-
|
|
19523
|
-
return () =>
|
|
19643
|
+
fs49.mkdirSync(INDEX_LOCK2, { recursive: false });
|
|
19644
|
+
fs49.writeFileSync(holder, String(process.pid));
|
|
19645
|
+
return () => fs49.rmSync(INDEX_LOCK2, { recursive: true, force: true });
|
|
19524
19646
|
} catch (err) {
|
|
19525
19647
|
const code = err.code;
|
|
19526
19648
|
if (code === "ENOENT") {
|
|
19527
|
-
|
|
19649
|
+
fs49.mkdirSync(path55.dirname(INDEX_LOCK2), { recursive: true });
|
|
19528
19650
|
continue;
|
|
19529
19651
|
}
|
|
19530
19652
|
if (code !== "EEXIST") throw err;
|
|
19531
19653
|
if (!pidAlive(Number(readHolder(holder)))) {
|
|
19532
|
-
|
|
19654
|
+
fs49.rmSync(INDEX_LOCK2, { recursive: true, force: true });
|
|
19533
19655
|
continue;
|
|
19534
19656
|
}
|
|
19535
19657
|
if (Date.now() > until) return null;
|
|
@@ -19539,7 +19661,7 @@ async function acquireIndexLock(giveUp) {
|
|
|
19539
19661
|
}
|
|
19540
19662
|
function readHolder(file) {
|
|
19541
19663
|
try {
|
|
19542
|
-
return
|
|
19664
|
+
return fs49.readFileSync(file, "utf8").trim();
|
|
19543
19665
|
} catch {
|
|
19544
19666
|
return String(process.pid);
|
|
19545
19667
|
}
|
|
@@ -19548,6 +19670,7 @@ var INDEX_ACTIONS, SINGLETON_ACTIONS, VERB, SINGLETON_BUSY, RUN_STATUS, specPath
|
|
|
19548
19670
|
var init_sessions = __esm({
|
|
19549
19671
|
"../cli/src/lib/agent/sessions.ts"() {
|
|
19550
19672
|
"use strict";
|
|
19673
|
+
init_execution();
|
|
19551
19674
|
init_creation();
|
|
19552
19675
|
init_cards();
|
|
19553
19676
|
init_frontmatter();
|
|
@@ -19630,8 +19753,8 @@ var init_sessions = __esm({
|
|
|
19630
19753
|
conflict: "implementing",
|
|
19631
19754
|
hook: "implementing"
|
|
19632
19755
|
};
|
|
19633
|
-
specPathOf = (sessionId) =>
|
|
19634
|
-
asksPathOf = (sessionId) =>
|
|
19756
|
+
specPathOf = (sessionId) => path55.join(SESSIONS_DIR, `${sessionId}.plan.json`);
|
|
19757
|
+
asksPathOf = (sessionId) => path55.join(SESSIONS_DIR, `${sessionId}.asks.json`);
|
|
19635
19758
|
carriesSession = (r) => !!(r.resumedFrom || r.continues && !r.continues.fork);
|
|
19636
19759
|
canPickUp = (r) => (r.status === "error" || r.status === "interrupted" || r.status === "stopped") && !(r.action === "create" && readRuns().some((next) => next.resumedFrom === r.sessionId));
|
|
19637
19760
|
neverStarted = (r) => r.unspawned === true || !!r.errorWhy?.some((why) => why.kind === "notInstalled");
|
|
@@ -19765,8 +19888,8 @@ var init_questions = __esm({
|
|
|
19765
19888
|
});
|
|
19766
19889
|
|
|
19767
19890
|
// ../cli/src/lib/agent/workflow-cards.ts
|
|
19768
|
-
import
|
|
19769
|
-
import
|
|
19891
|
+
import fs50 from "node:fs";
|
|
19892
|
+
import path56 from "node:path";
|
|
19770
19893
|
function cardsOnWorkflow(id) {
|
|
19771
19894
|
const wanted = id || DEFAULT_WORKFLOW;
|
|
19772
19895
|
const ids = [];
|
|
@@ -19777,12 +19900,12 @@ function cardsOnWorkflow(id) {
|
|
|
19777
19900
|
return [];
|
|
19778
19901
|
}
|
|
19779
19902
|
for (const file of files2) {
|
|
19780
|
-
const base =
|
|
19781
|
-
const card = base === "root.md" ? idPrefix(
|
|
19903
|
+
const base = path56.basename(file);
|
|
19904
|
+
const card = base === "root.md" ? idPrefix(path56.basename(path56.dirname(file))) : idPrefix(base);
|
|
19782
19905
|
if (card === null) continue;
|
|
19783
19906
|
let meta;
|
|
19784
19907
|
try {
|
|
19785
|
-
meta = parseFrontmatter(
|
|
19908
|
+
meta = parseFrontmatter(fs50.readFileSync(file, "utf8")).meta;
|
|
19786
19909
|
} catch {
|
|
19787
19910
|
continue;
|
|
19788
19911
|
}
|
|
@@ -19840,12 +19963,12 @@ var init_workflow_cards = __esm({
|
|
|
19840
19963
|
});
|
|
19841
19964
|
|
|
19842
19965
|
// ../cli/src/lib/readme.ts
|
|
19843
|
-
import
|
|
19844
|
-
import
|
|
19966
|
+
import fs51 from "node:fs";
|
|
19967
|
+
import path57 from "node:path";
|
|
19845
19968
|
function stripReadmeRefs(target) {
|
|
19846
|
-
if (!
|
|
19969
|
+
if (!fs51.existsSync(README)) return [];
|
|
19847
19970
|
const needle = target.kind === "group" ? `](${target.rel}/` : `](${target.rel})`;
|
|
19848
|
-
const lines =
|
|
19971
|
+
const lines = fs51.readFileSync(README, "utf8").split("\n");
|
|
19849
19972
|
const out = [];
|
|
19850
19973
|
const removed = [];
|
|
19851
19974
|
let i = 0;
|
|
@@ -19867,20 +19990,20 @@ function stripReadmeRefs(target) {
|
|
|
19867
19990
|
out.push(line);
|
|
19868
19991
|
i++;
|
|
19869
19992
|
}
|
|
19870
|
-
if (removed.length)
|
|
19993
|
+
if (removed.length) fs51.writeFileSync(README, out.join("\n"));
|
|
19871
19994
|
return removed;
|
|
19872
19995
|
}
|
|
19873
19996
|
function addReadmeRef(id, title, relPath) {
|
|
19874
|
-
if (!
|
|
19875
|
-
const link2 = relPath.split(
|
|
19997
|
+
if (!fs51.existsSync(README)) return false;
|
|
19998
|
+
const link2 = relPath.split(path57.sep).join("/");
|
|
19876
19999
|
const bullet = `- [#${id} ${title}](${link2})`;
|
|
19877
20000
|
const heading = `## ${TASKS_HEADING}`;
|
|
19878
|
-
let lines =
|
|
20001
|
+
let lines = fs51.readFileSync(README, "utf8").split("\n");
|
|
19879
20002
|
const hi = lines.findIndex((l) => l.trim().toLowerCase() === heading.toLowerCase());
|
|
19880
20003
|
if (hi === -1) {
|
|
19881
20004
|
while (lines.length && lines[lines.length - 1].trim() === "") lines.pop();
|
|
19882
20005
|
lines.push("", heading, "", bullet);
|
|
19883
|
-
|
|
20006
|
+
fs51.writeFileSync(README, lines.join("\n") + "\n");
|
|
19884
20007
|
return true;
|
|
19885
20008
|
}
|
|
19886
20009
|
let end2 = hi + 1;
|
|
@@ -19894,15 +20017,15 @@ function addReadmeRef(id, title, relPath) {
|
|
|
19894
20017
|
const at = lastBullet !== -1 ? lastBullet + 1 : lines[hi + 1] === "" ? hi + 2 : hi + 1;
|
|
19895
20018
|
lines.splice(at, 0, bullet);
|
|
19896
20019
|
}
|
|
19897
|
-
|
|
20020
|
+
fs51.writeFileSync(README, lines.join("\n"));
|
|
19898
20021
|
return true;
|
|
19899
20022
|
}
|
|
19900
20023
|
function repointReadmeLink(id, oldRel, newRel, title) {
|
|
19901
|
-
if (!
|
|
19902
|
-
const oldLink = oldRel.split(
|
|
19903
|
-
const newLink = newRel.split(
|
|
20024
|
+
if (!fs51.existsSync(README)) return false;
|
|
20025
|
+
const oldLink = oldRel.split(path57.sep).join("/");
|
|
20026
|
+
const newLink = newRel.split(path57.sep).join("/");
|
|
19904
20027
|
const linkRe = new RegExp(`\\[#${id}\\b[^\\]]*\\]\\(${escapeRegex(oldLink)}\\)`);
|
|
19905
|
-
const lines =
|
|
20028
|
+
const lines = fs51.readFileSync(README, "utf8").split("\n");
|
|
19906
20029
|
let changed = false;
|
|
19907
20030
|
for (let i = 0; i < lines.length; i++) {
|
|
19908
20031
|
if (!lines[i].includes(`](${oldLink})`)) continue;
|
|
@@ -19912,7 +20035,7 @@ function repointReadmeLink(id, oldRel, newRel, title) {
|
|
|
19912
20035
|
changed = true;
|
|
19913
20036
|
}
|
|
19914
20037
|
}
|
|
19915
|
-
if (changed)
|
|
20038
|
+
if (changed) fs51.writeFileSync(README, lines.join("\n"));
|
|
19916
20039
|
return changed;
|
|
19917
20040
|
}
|
|
19918
20041
|
var isTableRow, isBulletStart, bulletIndent, TASKS_HEADING, escapeRegex;
|
|
@@ -19929,14 +20052,14 @@ var init_readme = __esm({
|
|
|
19929
20052
|
});
|
|
19930
20053
|
|
|
19931
20054
|
// ../cli/src/lib/reconcile.ts
|
|
19932
|
-
import
|
|
19933
|
-
import
|
|
20055
|
+
import fs52 from "node:fs";
|
|
20056
|
+
import path58 from "node:path";
|
|
19934
20057
|
function liveIds() {
|
|
19935
20058
|
const ids = /* @__PURE__ */ new Set();
|
|
19936
20059
|
for (const file of walkMd(TODO)) {
|
|
19937
|
-
const base =
|
|
20060
|
+
const base = path58.basename(file);
|
|
19938
20061
|
if (base === "README.md") continue;
|
|
19939
|
-
const id = base === "root.md" ? idPrefix(
|
|
20062
|
+
const id = base === "root.md" ? idPrefix(path58.basename(path58.dirname(file))) : idPrefix(base);
|
|
19940
20063
|
if (id != null) ids.add(id);
|
|
19941
20064
|
}
|
|
19942
20065
|
return ids;
|
|
@@ -19944,16 +20067,16 @@ function liveIds() {
|
|
|
19944
20067
|
function indexableCards() {
|
|
19945
20068
|
const out = [];
|
|
19946
20069
|
for (const dir of walkDirs(TODO)) {
|
|
19947
|
-
const id = idPrefix(
|
|
19948
|
-
if (id != null &&
|
|
19949
|
-
out.push({ id, rel:
|
|
20070
|
+
const id = idPrefix(path58.basename(dir));
|
|
20071
|
+
if (id != null && fs52.existsSync(path58.join(dir, "root.md"))) {
|
|
20072
|
+
out.push({ id, rel: path58.join(path58.relative(TODO, dir), "root.md").split(path58.sep).join("/") });
|
|
19950
20073
|
}
|
|
19951
20074
|
}
|
|
19952
20075
|
for (const file of walkMd(TODO)) {
|
|
19953
|
-
const base =
|
|
20076
|
+
const base = path58.basename(file);
|
|
19954
20077
|
if (base === "README.md" || base === "root.md") continue;
|
|
19955
|
-
const relTODO =
|
|
19956
|
-
const segs = relTODO.split(
|
|
20078
|
+
const relTODO = path58.relative(TODO, file);
|
|
20079
|
+
const segs = relTODO.split(path58.sep);
|
|
19957
20080
|
if (segs.length !== 1) continue;
|
|
19958
20081
|
const id = idPrefix(base);
|
|
19959
20082
|
if (id != null) out.push({ id, rel: segs.join("/") });
|
|
@@ -19962,7 +20085,7 @@ function indexableCards() {
|
|
|
19962
20085
|
}
|
|
19963
20086
|
function readmeComplaints(fix) {
|
|
19964
20087
|
const said3 = [];
|
|
19965
|
-
const lines =
|
|
20088
|
+
const lines = fs52.readFileSync(README, "utf8").split("\n");
|
|
19966
20089
|
const indexed = /* @__PURE__ */ new Set();
|
|
19967
20090
|
let fixed = 0;
|
|
19968
20091
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -19971,13 +20094,13 @@ function readmeComplaints(fix) {
|
|
|
19971
20094
|
const id = Number(m[1]);
|
|
19972
20095
|
const linkPath = m[2];
|
|
19973
20096
|
indexed.add(id);
|
|
19974
|
-
if (
|
|
20097
|
+
if (fs52.existsSync(path58.join(TODO, linkPath))) continue;
|
|
19975
20098
|
const found2 = locate(id);
|
|
19976
20099
|
if (!found2) {
|
|
19977
20100
|
said3.push(`README links #${id} \u2192 ${linkPath}, but no card with that id exists (removed by hand?). Drop the entry or restore the file.`);
|
|
19978
20101
|
continue;
|
|
19979
20102
|
}
|
|
19980
|
-
const want = (found2.kind === "group" ?
|
|
20103
|
+
const want = (found2.kind === "group" ? path58.join(found2.rel, "root.md") : found2.rel).split(path58.sep).join("/");
|
|
19981
20104
|
if (want === linkPath) continue;
|
|
19982
20105
|
if (!fix) {
|
|
19983
20106
|
said3.push(`README links #${id} \u2192 ${linkPath}, which isn't there any more; the card is at ${want}.`);
|
|
@@ -19987,7 +20110,7 @@ function readmeComplaints(fix) {
|
|
|
19987
20110
|
said3.push(`README link #${id} pointed at missing ${linkPath} \u2192 repointed to ${want}.`);
|
|
19988
20111
|
fixed++;
|
|
19989
20112
|
}
|
|
19990
|
-
if (fixed)
|
|
20113
|
+
if (fixed) fs52.writeFileSync(README, lines.join("\n"));
|
|
19991
20114
|
for (const c of indexableCards()) {
|
|
19992
20115
|
if (!indexed.has(c.id)) {
|
|
19993
20116
|
said3.push(`card ${c.rel} (#${c.id}) is not in the README index. Add it under "## ${TASKS_HEADING}".`);
|
|
@@ -19999,11 +20122,11 @@ function crossRefComplaints() {
|
|
|
19999
20122
|
const said3 = [];
|
|
20000
20123
|
const live = liveIds();
|
|
20001
20124
|
for (const file of walkMd(TODO)) {
|
|
20002
|
-
const base =
|
|
20125
|
+
const base = path58.basename(file);
|
|
20003
20126
|
if (base === "README.md") continue;
|
|
20004
|
-
const ownerId = base === "root.md" ? idPrefix(
|
|
20127
|
+
const ownerId = base === "root.md" ? idPrefix(path58.basename(path58.dirname(file))) : idPrefix(base);
|
|
20005
20128
|
if (ownerId == null) continue;
|
|
20006
|
-
const { meta } = parseFrontmatter(
|
|
20129
|
+
const { meta } = parseFrontmatter(fs52.readFileSync(file, "utf8"));
|
|
20007
20130
|
if (!meta) continue;
|
|
20008
20131
|
for (const field2 of ["blocked_by", "related"]) {
|
|
20009
20132
|
for (const ref of meta[field2] || []) {
|
|
@@ -20016,11 +20139,11 @@ function crossRefComplaints() {
|
|
|
20016
20139
|
return said3;
|
|
20017
20140
|
}
|
|
20018
20141
|
function reconcileBoard() {
|
|
20019
|
-
if (!
|
|
20142
|
+
if (!fs52.existsSync(README)) return;
|
|
20020
20143
|
for (const line of [...readmeComplaints(true), ...crossRefComplaints()]) warn2(line);
|
|
20021
20144
|
}
|
|
20022
20145
|
function boardComplaints() {
|
|
20023
|
-
if (!
|
|
20146
|
+
if (!fs52.existsSync(README)) return [];
|
|
20024
20147
|
try {
|
|
20025
20148
|
return [...readmeComplaints(false), ...crossRefComplaints()];
|
|
20026
20149
|
} catch {
|
|
@@ -20040,8 +20163,8 @@ var init_reconcile = __esm({
|
|
|
20040
20163
|
});
|
|
20041
20164
|
|
|
20042
20165
|
// ../cli/src/commands/card.ts
|
|
20043
|
-
import
|
|
20044
|
-
import
|
|
20166
|
+
import fs53 from "node:fs";
|
|
20167
|
+
import path59 from "node:path";
|
|
20045
20168
|
function defaultBody() {
|
|
20046
20169
|
return [
|
|
20047
20170
|
"<\u7528\u4E00\u5C0F\u6BB5\u8BDD\u5148\u8BB2\u53D1\u751F\u4E86\u4EC0\u4E48\u3001\u7528\u6237\u9047\u5230\u4EC0\u4E48\uFF0C\u518D\u8BB2\u8FD9\u5F20\u5361\u8981\u8FBE\u5230\u7684\u7ED3\u679C\u3002>",
|
|
@@ -20067,7 +20190,7 @@ function bodyFromFile(opts) {
|
|
|
20067
20190
|
if (opts.body === false) die("pass --body-file or --no-body, not both");
|
|
20068
20191
|
let text5;
|
|
20069
20192
|
try {
|
|
20070
|
-
text5 =
|
|
20193
|
+
text5 = fs53.readFileSync(opts.bodyFile, "utf8");
|
|
20071
20194
|
} catch {
|
|
20072
20195
|
die(`can't read ${opts.bodyFile} \u2014 write the body to a file, then pass its path`, { kind: "needs-input" });
|
|
20073
20196
|
}
|
|
@@ -20099,20 +20222,20 @@ function cmdCreate(opts) {
|
|
|
20099
20222
|
const start = opts.id;
|
|
20100
20223
|
const slug2 = slugify(opts.slug !== void 0 ? opts.slug : title);
|
|
20101
20224
|
const fileRel = `${start}-${slug2}.md`;
|
|
20102
|
-
const file =
|
|
20225
|
+
const file = path59.join(TODO, fileRel);
|
|
20103
20226
|
const held4 = locate(start) ?? locateArchived(start);
|
|
20104
|
-
if (held4 &&
|
|
20227
|
+
if (held4 && path59.basename(held4.target) === `${start}-${slug2}.md`) {
|
|
20105
20228
|
say(start);
|
|
20106
20229
|
say(` ${rel(held4.target)} was already written`);
|
|
20107
20230
|
return { id: start, ids: [start], title, file: rel(held4.target), indexed: false };
|
|
20108
20231
|
}
|
|
20109
20232
|
if (held4) die(`#${start} is already a card here (${rel(held4.target)})`, { kind: "id-taken", id: start });
|
|
20110
|
-
if (
|
|
20233
|
+
if (fs53.existsSync(file)) die(`${rel(file)} already exists \u2014 pick a different --slug`);
|
|
20111
20234
|
countBoardEvent(triage ? "cards_created_proposed" : "cards_created_asked");
|
|
20112
20235
|
const meta = { title, priority, status: "todo", blocked_by, related, modules, workflow, triage, source, questions };
|
|
20113
20236
|
const scaffolded = !written && opts.body !== false;
|
|
20114
20237
|
const body = written ?? (scaffolded ? defaultBody() : "");
|
|
20115
|
-
|
|
20238
|
+
fs53.writeFileSync(file, serializeFrontmatter(meta) + "\n\n" + body);
|
|
20116
20239
|
const indexed = addReadmeRef(start, title, fileRel);
|
|
20117
20240
|
say(start);
|
|
20118
20241
|
say(
|
|
@@ -20150,8 +20273,8 @@ function cmdUpdate(id, flags) {
|
|
|
20150
20273
|
if (creating) die(creating, { kind: "card-being-created", id });
|
|
20151
20274
|
const found2 = locate(id);
|
|
20152
20275
|
if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
|
|
20153
|
-
const file = found2.kind === "group" ?
|
|
20154
|
-
const { meta, body } = parseFrontmatter(
|
|
20276
|
+
const file = found2.kind === "group" ? path59.join(found2.target, "root.md") : found2.target;
|
|
20277
|
+
const { meta, body } = parseFrontmatter(fs53.readFileSync(file, "utf8"));
|
|
20155
20278
|
if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
|
|
20156
20279
|
if (flags.blockedBy !== void 0 && flags.addBlockedBy !== void 0) die("--blocked-by replaces the list and --add-blocked-by appends to it \u2014 pass one");
|
|
20157
20280
|
if (flags.related !== void 0 && flags.addRelated !== void 0) die("--related replaces the list and --add-related appends to it \u2014 pass one");
|
|
@@ -20231,26 +20354,26 @@ function cmdUpdate(id, flags) {
|
|
|
20231
20354
|
meta.status = "todo";
|
|
20232
20355
|
changes.push("status\u2192todo (open questions)");
|
|
20233
20356
|
}
|
|
20234
|
-
const curRel =
|
|
20357
|
+
const curRel = path59.relative(TODO, file);
|
|
20235
20358
|
const isSubtask = found2.kind === "file" && enclosingGroupRoot(file) !== null;
|
|
20236
|
-
let base =
|
|
20359
|
+
let base = path59.basename(file);
|
|
20237
20360
|
if (flags.slug !== void 0) {
|
|
20238
20361
|
if (found2.kind === "group") die("renaming a group root by script is not supported");
|
|
20239
20362
|
base = `${id}-${slugify(flags.slug)}.md`;
|
|
20240
20363
|
}
|
|
20241
|
-
const destRel =
|
|
20242
|
-
const dest =
|
|
20364
|
+
const destRel = path59.join(path59.dirname(curRel), base);
|
|
20365
|
+
const dest = path59.join(TODO, destRel);
|
|
20243
20366
|
const moving = dest !== file;
|
|
20244
|
-
if (moving &&
|
|
20245
|
-
|
|
20246
|
-
if (moving)
|
|
20367
|
+
if (moving && fs53.existsSync(dest)) die(`${rel(dest)} already exists`);
|
|
20368
|
+
fs53.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
|
|
20369
|
+
if (moving) fs53.renameSync(file, dest);
|
|
20247
20370
|
if (isSubtask) {
|
|
20248
20371
|
if (moving || changes.includes("title")) repointReadmeLink(id, curRel, destRel, meta.title);
|
|
20249
|
-
if (moving) changes.push(`renamed \u2192 ${destRel.split(
|
|
20372
|
+
if (moving) changes.push(`renamed \u2192 ${destRel.split(path59.sep).join("/")}`);
|
|
20250
20373
|
} else if (moving) {
|
|
20251
20374
|
stripReadmeRefs({ kind: "file", rel: curRel });
|
|
20252
20375
|
addReadmeRef(id, meta.title, destRel);
|
|
20253
|
-
changes.push(`renamed \u2192 ${destRel.split(
|
|
20376
|
+
changes.push(`renamed \u2192 ${destRel.split(path59.sep).join("/")}`);
|
|
20254
20377
|
} else if (changes.includes("title")) {
|
|
20255
20378
|
stripReadmeRefs({ kind: "file", rel: curRel });
|
|
20256
20379
|
addReadmeRef(id, meta.title, curRel);
|
|
@@ -20262,8 +20385,8 @@ function cmdUpdateQuestions(id, input) {
|
|
|
20262
20385
|
const ops = readQuestionOps(input.ops ?? []);
|
|
20263
20386
|
const found2 = locate(id);
|
|
20264
20387
|
if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
|
|
20265
|
-
const file = found2.kind === "group" ?
|
|
20266
|
-
const { meta, body } = parseFrontmatter(
|
|
20388
|
+
const file = found2.kind === "group" ? path59.join(found2.target, "root.md") : found2.target;
|
|
20389
|
+
const { meta, body } = parseFrontmatter(fs53.readFileSync(file, "utf8"));
|
|
20267
20390
|
if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
|
|
20268
20391
|
if (insideRun() && ops.some((op) => op.kind === "skip" || op.kind === "unskip")) {
|
|
20269
20392
|
die("only the user skips or reopens a question \u2014 a run leaves questions for them to answer");
|
|
@@ -20336,7 +20459,7 @@ function cmdUpdateQuestions(id, input) {
|
|
|
20336
20459
|
meta.status = "todo";
|
|
20337
20460
|
changes.push("status\u2192todo (open questions)");
|
|
20338
20461
|
}
|
|
20339
|
-
|
|
20462
|
+
fs53.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
|
|
20340
20463
|
for (const kind of closed2) countBoardEvent(kind);
|
|
20341
20464
|
settleDelivery2(id, skipped, unskipped);
|
|
20342
20465
|
say(
|
|
@@ -20410,13 +20533,13 @@ var init_config_template = __esm({
|
|
|
20410
20533
|
});
|
|
20411
20534
|
|
|
20412
20535
|
// ../cli/src/lib/agent/local.ts
|
|
20413
|
-
import
|
|
20414
|
-
import
|
|
20536
|
+
import fs54 from "node:fs";
|
|
20537
|
+
import path60 from "node:path";
|
|
20415
20538
|
function takeLocalModels(board2) {
|
|
20416
|
-
const file =
|
|
20539
|
+
const file = path60.join(board2, ".local.json");
|
|
20417
20540
|
let held4;
|
|
20418
20541
|
try {
|
|
20419
|
-
held4 = configBlock(JSON.parse(
|
|
20542
|
+
held4 = configBlock(JSON.parse(fs54.readFileSync(file, "utf8")));
|
|
20420
20543
|
} catch {
|
|
20421
20544
|
return {};
|
|
20422
20545
|
}
|
|
@@ -20434,7 +20557,7 @@ function takeLocalModels(board2) {
|
|
|
20434
20557
|
}
|
|
20435
20558
|
if (!Object.keys(agents).length) return out;
|
|
20436
20559
|
delete held4.agents;
|
|
20437
|
-
|
|
20560
|
+
fs54.writeFileSync(file, JSON.stringify(held4, null, 2) + "\n");
|
|
20438
20561
|
return out;
|
|
20439
20562
|
}
|
|
20440
20563
|
var LOCAL_IGNORE_LINE;
|
|
@@ -20495,12 +20618,12 @@ var init_identity2 = __esm({
|
|
|
20495
20618
|
});
|
|
20496
20619
|
|
|
20497
20620
|
// ../cli/src/lib/signals/migrate.ts
|
|
20498
|
-
import
|
|
20499
|
-
import
|
|
20621
|
+
import fs55 from "node:fs";
|
|
20622
|
+
import path61 from "node:path";
|
|
20500
20623
|
function readHandled() {
|
|
20501
20624
|
let text5;
|
|
20502
20625
|
try {
|
|
20503
|
-
text5 =
|
|
20626
|
+
text5 = fs55.readFileSync(SIGNALS_OLD_HANDLED, "utf8");
|
|
20504
20627
|
} catch {
|
|
20505
20628
|
return [];
|
|
20506
20629
|
}
|
|
@@ -20517,57 +20640,57 @@ function readHandled() {
|
|
|
20517
20640
|
return held4;
|
|
20518
20641
|
}
|
|
20519
20642
|
function lift(from, into) {
|
|
20520
|
-
|
|
20521
|
-
|
|
20643
|
+
fs55.mkdirSync(into, { recursive: true });
|
|
20644
|
+
fs55.renameSync(from, path61.join(into, freeName(into, path61.basename(from))));
|
|
20522
20645
|
}
|
|
20523
20646
|
function repoint(dir, from, into) {
|
|
20524
20647
|
let entries;
|
|
20525
20648
|
try {
|
|
20526
|
-
entries =
|
|
20649
|
+
entries = fs55.readdirSync(dir, { withFileTypes: true });
|
|
20527
20650
|
} catch {
|
|
20528
20651
|
return;
|
|
20529
20652
|
}
|
|
20530
20653
|
for (const entry of entries) {
|
|
20531
20654
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
20532
|
-
const file =
|
|
20533
|
-
const text5 =
|
|
20534
|
-
if (text5.includes(from))
|
|
20655
|
+
const file = path61.join(dir, entry.name);
|
|
20656
|
+
const text5 = fs55.readFileSync(file, "utf8");
|
|
20657
|
+
if (text5.includes(from)) fs55.writeFileSync(file, text5.split(from).join(into));
|
|
20535
20658
|
}
|
|
20536
20659
|
}
|
|
20537
20660
|
function dropIfEmpty2(dir) {
|
|
20538
20661
|
try {
|
|
20539
|
-
|
|
20662
|
+
fs55.rmdirSync(dir);
|
|
20540
20663
|
} catch {
|
|
20541
20664
|
}
|
|
20542
20665
|
}
|
|
20543
20666
|
function liftInbox() {
|
|
20544
|
-
if (!
|
|
20545
|
-
const oldFiles =
|
|
20546
|
-
if (
|
|
20547
|
-
for (const entry of
|
|
20548
|
-
if (entry.isFile()) lift(
|
|
20667
|
+
if (!fs55.existsSync(SIGNALS_OLD_INBOX)) return;
|
|
20668
|
+
const oldFiles = path61.join(SIGNALS_OLD_INBOX, "files");
|
|
20669
|
+
if (fs55.existsSync(oldFiles)) {
|
|
20670
|
+
for (const entry of fs55.readdirSync(oldFiles, { withFileTypes: true })) {
|
|
20671
|
+
if (entry.isFile()) lift(path61.join(oldFiles, entry.name), SIGNALS_FILES);
|
|
20549
20672
|
}
|
|
20550
20673
|
dropIfEmpty2(oldFiles);
|
|
20551
20674
|
}
|
|
20552
20675
|
const held4 = new Set(readFolder(TRIAGE).map((signal2) => signal2.sourceId));
|
|
20553
|
-
for (const entry of
|
|
20676
|
+
for (const entry of fs55.readdirSync(SIGNALS_OLD_INBOX, { withFileTypes: true })) {
|
|
20554
20677
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
20555
|
-
const from =
|
|
20678
|
+
const from = path61.join(SIGNALS_OLD_INBOX, entry.name);
|
|
20556
20679
|
const item = parse(from);
|
|
20557
20680
|
if (item && held4.has(item.sourceId)) {
|
|
20558
|
-
|
|
20681
|
+
fs55.rmSync(from, { force: true });
|
|
20559
20682
|
continue;
|
|
20560
20683
|
}
|
|
20561
20684
|
if (item) held4.add(item.sourceId);
|
|
20562
20685
|
lift(from, TRIAGE);
|
|
20563
20686
|
}
|
|
20564
|
-
const wasAt = asWritten(
|
|
20687
|
+
const wasAt = asWritten(path61.join(SIGNALS_OLD_INBOX, "files"));
|
|
20565
20688
|
const nowAt = asWritten(SIGNALS_FILES);
|
|
20566
20689
|
for (const dir of [TRIAGE, SIGNALS_ARCHIVED, SIGNALS_DISMISSED]) repoint(dir, wasAt, nowAt);
|
|
20567
20690
|
dropIfEmpty2(SIGNALS_OLD_INBOX);
|
|
20568
20691
|
}
|
|
20569
20692
|
function liftHandled() {
|
|
20570
|
-
if (!
|
|
20693
|
+
if (!fs55.existsSync(SIGNALS_OLD_HANDLED)) return;
|
|
20571
20694
|
const lines = readHandled();
|
|
20572
20695
|
const held4 = /* @__PURE__ */ new Set([
|
|
20573
20696
|
...readFolder(TRIAGE).map((signal2) => signal2.sourceId),
|
|
@@ -20577,7 +20700,7 @@ function liftHandled() {
|
|
|
20577
20700
|
for (const { sourceId, when } of lines) {
|
|
20578
20701
|
if (held4.has(sourceId)) continue;
|
|
20579
20702
|
held4.add(sourceId);
|
|
20580
|
-
|
|
20703
|
+
fs55.mkdirSync(SIGNALS_DISMISSED, { recursive: true });
|
|
20581
20704
|
const name = freeName(SIGNALS_DISMISSED, fileName("", sourceId));
|
|
20582
20705
|
const front = [
|
|
20583
20706
|
"---",
|
|
@@ -20587,13 +20710,13 @@ function liftHandled() {
|
|
|
20587
20710
|
"---",
|
|
20588
20711
|
""
|
|
20589
20712
|
];
|
|
20590
|
-
|
|
20713
|
+
fs55.writeFileSync(path61.join(SIGNALS_DISMISSED, name), `${front.join("\n")}
|
|
20591
20714
|
`);
|
|
20592
20715
|
}
|
|
20593
|
-
|
|
20716
|
+
fs55.rmSync(SIGNALS_OLD_HANDLED, { force: true });
|
|
20594
20717
|
}
|
|
20595
20718
|
function migrateTriage() {
|
|
20596
|
-
if (!
|
|
20719
|
+
if (!fs55.existsSync(SIGNALS_OLD_INBOX) && !fs55.existsSync(SIGNALS_OLD_HANDLED)) return;
|
|
20597
20720
|
try {
|
|
20598
20721
|
liftInbox();
|
|
20599
20722
|
liftHandled();
|
|
@@ -20608,13 +20731,13 @@ var init_migrate = __esm({
|
|
|
20608
20731
|
init_yaml();
|
|
20609
20732
|
init_inbox();
|
|
20610
20733
|
HANDLED_LINE = /^-\s+(.+?)\s+—\s+(.+)$/;
|
|
20611
|
-
asWritten = (dir) => `${rel(dir).split(
|
|
20734
|
+
asWritten = (dir) => `${rel(dir).split(path61.sep).join("/")}/`;
|
|
20612
20735
|
}
|
|
20613
20736
|
});
|
|
20614
20737
|
|
|
20615
20738
|
// ../cli/src/lib/signals/add.ts
|
|
20616
|
-
import
|
|
20617
|
-
import
|
|
20739
|
+
import fs56 from "node:fs";
|
|
20740
|
+
import path62 from "node:path";
|
|
20618
20741
|
import { createHash as createHash4 } from "node:crypto";
|
|
20619
20742
|
function linkTitle(url) {
|
|
20620
20743
|
let at;
|
|
@@ -20627,7 +20750,7 @@ function linkTitle(url) {
|
|
|
20627
20750
|
return oneLine(where) || url.slice(0, TITLE_MAX);
|
|
20628
20751
|
}
|
|
20629
20752
|
function asText(file) {
|
|
20630
|
-
const ext =
|
|
20753
|
+
const ext = path62.extname(file.name).toLowerCase();
|
|
20631
20754
|
if (!file.type.startsWith("text/") && file.type !== "application/json" && !TEXT_EXTENSIONS.has(ext)) return null;
|
|
20632
20755
|
try {
|
|
20633
20756
|
const text5 = new TextDecoder("utf-8", { fatal: true }).decode(file.data);
|
|
@@ -20637,9 +20760,9 @@ function asText(file) {
|
|
|
20637
20760
|
}
|
|
20638
20761
|
}
|
|
20639
20762
|
function fileName2(name, data) {
|
|
20640
|
-
const base =
|
|
20641
|
-
const ext =
|
|
20642
|
-
const word =
|
|
20763
|
+
const base = path62.basename(name);
|
|
20764
|
+
const ext = path62.extname(base).toLowerCase().slice(0, 12);
|
|
20765
|
+
const word = path62.basename(base, path62.extname(base)).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40) || "file";
|
|
20643
20766
|
const hash2 = createHash4("sha256").update(data).digest("hex").slice(0, 8);
|
|
20644
20767
|
return `${word}-${hash2}${ext}`;
|
|
20645
20768
|
}
|
|
@@ -20648,9 +20771,9 @@ function addToInbox(drop) {
|
|
|
20648
20771
|
const typed = (drop.text ?? "").trim();
|
|
20649
20772
|
const file = drop.file;
|
|
20650
20773
|
if (!typed && !file) return { ok: false, error: "nothing to add \u2014 paste a link or some text, or drop a file." };
|
|
20651
|
-
if (file && file.data.length === 0) return { ok: false, error: `${
|
|
20774
|
+
if (file && file.data.length === 0) return { ok: false, error: `${path62.basename(file.name)} is empty.` };
|
|
20652
20775
|
if (file && file.data.length > MAX_BYTES) {
|
|
20653
|
-
return { ok: false, error: `${
|
|
20776
|
+
return { ok: false, error: `${path62.basename(file.name)} is over ${MAX_BYTES / 1024 / 1024} MB \u2014 too big for the board.` };
|
|
20654
20777
|
}
|
|
20655
20778
|
const read4 = describe(typed, file);
|
|
20656
20779
|
const keep2 = read4.keep;
|
|
@@ -20672,8 +20795,8 @@ ${incoming.summary}`);
|
|
|
20672
20795
|
if (seen.status === "pending" || seen.status === "archived") return { ok: false, error: `${sayHit(seen)}.` };
|
|
20673
20796
|
try {
|
|
20674
20797
|
if (keep2) {
|
|
20675
|
-
|
|
20676
|
-
|
|
20798
|
+
fs56.mkdirSync(SIGNALS_FILES, { recursive: true });
|
|
20799
|
+
fs56.writeFileSync(keep2.at, keep2.data);
|
|
20677
20800
|
}
|
|
20678
20801
|
return { ok: true, signal: writeSignal({ ...incoming, sourceId }, formatStamp(/* @__PURE__ */ new Date()), drop.slug) };
|
|
20679
20802
|
} catch (e) {
|
|
@@ -20687,7 +20810,7 @@ function describe(typed, file) {
|
|
|
20687
20810
|
return only ? [only] : [];
|
|
20688
20811
|
};
|
|
20689
20812
|
if (file) {
|
|
20690
|
-
const name =
|
|
20813
|
+
const name = path62.basename(file.name);
|
|
20691
20814
|
const text5 = asText(file);
|
|
20692
20815
|
const note = typed ? `
|
|
20693
20816
|
|
|
@@ -20699,7 +20822,7 @@ ${typed}` : "";
|
|
|
20699
20822
|
const at = keepAt(file);
|
|
20700
20823
|
return {
|
|
20701
20824
|
incoming: {
|
|
20702
|
-
title:
|
|
20825
|
+
title: path62.basename(name, path62.extname(name)) || name,
|
|
20703
20826
|
summary: `${name} \u2014 ${boardRel2(at)}${note}`,
|
|
20704
20827
|
...named2
|
|
20705
20828
|
},
|
|
@@ -20737,73 +20860,73 @@ var init_add = __esm({
|
|
|
20737
20860
|
MAX_BYTES = 25 * 1024 * 1024;
|
|
20738
20861
|
TITLE_MAX = 120;
|
|
20739
20862
|
TEXT_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".markdown", ".txt", ".text", ".csv", ".json", ".yml", ".yaml", ".log"]);
|
|
20740
|
-
boardRel2 = (file) => rel(file).split(
|
|
20863
|
+
boardRel2 = (file) => rel(file).split(path62.sep).join("/");
|
|
20741
20864
|
oneLine = (text5) => {
|
|
20742
20865
|
const first = text5.split("\n").find((line) => line.trim()) ?? "";
|
|
20743
20866
|
const clean = first.replace(/^#+\s*/, "").trim();
|
|
20744
20867
|
return clean.length > TITLE_MAX ? `${clean.slice(0, TITLE_MAX - 1).trimEnd()}\u2026` : clean;
|
|
20745
20868
|
};
|
|
20746
|
-
keepAt = (file) =>
|
|
20869
|
+
keepAt = (file) => path62.join(SIGNALS_FILES, fileName2(file.name, file.data));
|
|
20747
20870
|
}
|
|
20748
20871
|
});
|
|
20749
20872
|
|
|
20750
20873
|
// ../cli/src/lib/recurring.ts
|
|
20751
|
-
import
|
|
20752
|
-
import
|
|
20874
|
+
import fs57 from "node:fs";
|
|
20875
|
+
import path63 from "node:path";
|
|
20753
20876
|
function migratePruneMemoryCard() {
|
|
20754
20877
|
const dir = recurringDir();
|
|
20755
20878
|
let names4;
|
|
20756
20879
|
try {
|
|
20757
|
-
names4 =
|
|
20880
|
+
names4 = fs57.readdirSync(dir);
|
|
20758
20881
|
} catch {
|
|
20759
20882
|
return null;
|
|
20760
20883
|
}
|
|
20761
20884
|
const name = names4.find((entry) => entry.endsWith(`-${PRUNE_SLUG}.md`));
|
|
20762
20885
|
if (!name) return null;
|
|
20763
|
-
const file =
|
|
20886
|
+
const file = path63.join(dir, name);
|
|
20764
20887
|
try {
|
|
20765
|
-
const { meta } = parseFrontmatter(
|
|
20888
|
+
const { meta } = parseFrontmatter(fs57.readFileSync(file, "utf8"));
|
|
20766
20889
|
const cadence = meta?.cadence;
|
|
20767
20890
|
if (typeof cadence === "string") adoptMemoryPruneCadence(cadence);
|
|
20768
20891
|
} catch {
|
|
20769
20892
|
}
|
|
20770
|
-
|
|
20893
|
+
fs57.rmSync(file, { force: true });
|
|
20771
20894
|
return rel(file);
|
|
20772
20895
|
}
|
|
20773
20896
|
function legacyCards() {
|
|
20774
20897
|
const dir = recurringDir();
|
|
20775
|
-
if (!
|
|
20898
|
+
if (!fs57.existsSync(dir)) return [];
|
|
20776
20899
|
const cards = [];
|
|
20777
|
-
for (const entry of
|
|
20900
|
+
for (const entry of fs57.readdirSync(dir, { withFileTypes: true })) {
|
|
20778
20901
|
const id = idPrefix(entry.name);
|
|
20779
20902
|
if (id === null) continue;
|
|
20780
|
-
const full =
|
|
20903
|
+
const full = path63.join(dir, entry.name);
|
|
20781
20904
|
const slug2 = entry.name.replace(/\.md$/, "").replace(/^\d+-?/, "");
|
|
20782
20905
|
if (entry.isFile() && entry.name.endsWith(".md")) cards.push({ id, slug: slug2, file: full });
|
|
20783
|
-
else if (entry.isDirectory() &&
|
|
20784
|
-
cards.push({ id, slug: slug2, file:
|
|
20906
|
+
else if (entry.isDirectory() && fs57.existsSync(path63.join(full, "root.md"))) {
|
|
20907
|
+
cards.push({ id, slug: slug2, file: path63.join(full, "root.md"), folder: full });
|
|
20785
20908
|
}
|
|
20786
20909
|
}
|
|
20787
20910
|
return cards.sort((a, b) => a.id - b.id);
|
|
20788
20911
|
}
|
|
20789
20912
|
function mergeInto2(from, to, skip = []) {
|
|
20790
|
-
|
|
20791
|
-
for (const entry of
|
|
20913
|
+
fs57.mkdirSync(to, { recursive: true });
|
|
20914
|
+
for (const entry of fs57.readdirSync(from, { withFileTypes: true })) {
|
|
20792
20915
|
if (skip.includes(entry.name)) continue;
|
|
20793
|
-
const source =
|
|
20794
|
-
const target =
|
|
20795
|
-
if (!
|
|
20796
|
-
else if (entry.isDirectory() &&
|
|
20916
|
+
const source = path63.join(from, entry.name);
|
|
20917
|
+
const target = path63.join(to, entry.name);
|
|
20918
|
+
if (!fs57.existsSync(target)) fs57.renameSync(source, target);
|
|
20919
|
+
else if (entry.isDirectory() && fs57.statSync(target).isDirectory()) mergeInto2(source, target);
|
|
20797
20920
|
}
|
|
20798
|
-
if (isEmpty(from))
|
|
20921
|
+
if (isEmpty(from)) fs57.rmSync(from, { recursive: true, force: true });
|
|
20799
20922
|
}
|
|
20800
20923
|
function moveSideFolders() {
|
|
20801
20924
|
const dir = recurringDir();
|
|
20802
|
-
for (const entry of
|
|
20925
|
+
for (const entry of fs57.readdirSync(dir, { withFileTypes: true })) {
|
|
20803
20926
|
if (!entry.isDirectory() || idPrefix(entry.name) !== null) continue;
|
|
20804
|
-
const from =
|
|
20805
|
-
if (isEmpty(from))
|
|
20806
|
-
else mergeInto2(from,
|
|
20927
|
+
const from = path63.join(dir, entry.name);
|
|
20928
|
+
if (isEmpty(from)) fs57.rmSync(from, { recursive: true, force: true });
|
|
20929
|
+
else mergeInto2(from, path63.join(AGENTS, entry.name));
|
|
20807
20930
|
}
|
|
20808
20931
|
}
|
|
20809
20932
|
function sections(body) {
|
|
@@ -20853,8 +20976,8 @@ function agentName(card, text5) {
|
|
|
20853
20976
|
const known2 = /* @__PURE__ */ new Set([...specAgentCatalog().agents.map((a) => a.name), ...ROLE_NAMES]);
|
|
20854
20977
|
for (const name of [slug2, `${slug2}-${card.id}`]) {
|
|
20855
20978
|
if (!AGENT_NAME.test(name)) continue;
|
|
20856
|
-
const file =
|
|
20857
|
-
const written =
|
|
20979
|
+
const file = path63.join(AGENTS, name, AGENT_FILE3);
|
|
20980
|
+
const written = fs57.existsSync(file) ? fs57.readFileSync(file, "utf8") : null;
|
|
20858
20981
|
if (written === text5(name)) return name;
|
|
20859
20982
|
if (written === null && !known2.has(name)) return name;
|
|
20860
20983
|
}
|
|
@@ -20871,11 +20994,11 @@ ${q.options.map((o) => `- ${o}`).join("\n")}` : "";
|
|
|
20871
20994
|
function removeCard(card) {
|
|
20872
20995
|
dropCrossRefs(card.id);
|
|
20873
20996
|
const target = card.folder ?? card.file;
|
|
20874
|
-
stripReadmeRefs({ kind: card.folder ? "group" : "file", rel:
|
|
20875
|
-
|
|
20997
|
+
stripReadmeRefs({ kind: card.folder ? "group" : "file", rel: path63.relative(TODO, target).split(path63.sep).join("/") });
|
|
20998
|
+
fs57.rmSync(target, { recursive: true, force: true });
|
|
20876
20999
|
}
|
|
20877
21000
|
function migrateCard(card) {
|
|
20878
|
-
const { meta, body } = parseFrontmatter(
|
|
21001
|
+
const { meta, body } = parseFrontmatter(fs57.readFileSync(card.file, "utf8"));
|
|
20879
21002
|
if (!meta) throw new Error("its frontmatter cannot be read");
|
|
20880
21003
|
const raw = meta;
|
|
20881
21004
|
const said3 = (key) => typeof raw[key] === "string" ? raw[key].trim() : "";
|
|
@@ -20885,19 +21008,19 @@ function migrateCard(card) {
|
|
|
20885
21008
|
if (!flow) throw new Error("this board has no workflow to put it in");
|
|
20886
21009
|
const text5 = (name2) => agentText(name2, meta.title || card.slug, body);
|
|
20887
21010
|
const name = agentName(card, text5);
|
|
20888
|
-
const dir =
|
|
20889
|
-
const file =
|
|
20890
|
-
const madeDir = !
|
|
20891
|
-
const wrote = !
|
|
21011
|
+
const dir = path63.join(AGENTS, name);
|
|
21012
|
+
const file = path63.join(dir, AGENT_FILE3);
|
|
21013
|
+
const madeDir = !fs57.existsSync(dir);
|
|
21014
|
+
const wrote = !fs57.existsSync(file);
|
|
20892
21015
|
try {
|
|
20893
|
-
|
|
20894
|
-
if (wrote)
|
|
21016
|
+
fs57.mkdirSync(dir, { recursive: true });
|
|
21017
|
+
if (wrote) fs57.writeFileSync(file, text5(name));
|
|
20895
21018
|
const adopted = adoptWorkflowScheduled(flow.id, name, { cadence, lastRun });
|
|
20896
21019
|
if (!adopted.ok) throw new Error(adopted.error);
|
|
20897
21020
|
questionsToTriage(meta, name);
|
|
20898
21021
|
} catch (e) {
|
|
20899
|
-
if (madeDir)
|
|
20900
|
-
else if (wrote)
|
|
21022
|
+
if (madeDir) fs57.rmSync(dir, { recursive: true, force: true });
|
|
21023
|
+
else if (wrote) fs57.rmSync(file, { force: true });
|
|
20901
21024
|
throw e;
|
|
20902
21025
|
}
|
|
20903
21026
|
if (card.folder) mergeInto2(card.folder, dir, ["root.md"]);
|
|
@@ -20907,7 +21030,7 @@ function migrateCard(card) {
|
|
|
20907
21030
|
function migrateRecurringCards() {
|
|
20908
21031
|
const done2 = { agents: [], removed: [], failed: [] };
|
|
20909
21032
|
const dir = recurringDir();
|
|
20910
|
-
if (!
|
|
21033
|
+
if (!fs57.existsSync(dir)) return done2;
|
|
20911
21034
|
migratePruneMemoryCard();
|
|
20912
21035
|
moveSideFolders();
|
|
20913
21036
|
for (const card of legacyCards()) {
|
|
@@ -20920,7 +21043,7 @@ function migrateRecurringCards() {
|
|
|
20920
21043
|
done2.failed.push({ card: rel(card.file), why: e instanceof Error ? e.message : String(e) });
|
|
20921
21044
|
}
|
|
20922
21045
|
}
|
|
20923
|
-
if (isEmpty(dir))
|
|
21046
|
+
if (isEmpty(dir)) fs57.rmSync(dir, { recursive: true, force: true });
|
|
20924
21047
|
return done2;
|
|
20925
21048
|
}
|
|
20926
21049
|
function recurringMigrationLines(done2) {
|
|
@@ -20953,14 +21076,14 @@ var init_recurring = __esm({
|
|
|
20953
21076
|
PRUNE_SLUG = "prune-the-memory";
|
|
20954
21077
|
FETCH_SLUG = "fetch-triage-items";
|
|
20955
21078
|
AGENT_FILE3 = "AGENT.md";
|
|
20956
|
-
recurringDir = () =>
|
|
20957
|
-
isEmpty = (dir) =>
|
|
21079
|
+
recurringDir = () => path63.join(TODO, LEGACY_RECURRING);
|
|
21080
|
+
isEmpty = (dir) => fs57.readdirSync(dir).every((name) => name === ".DS_Store");
|
|
20958
21081
|
SECTION = /^##\s+(.+?)\s*$/;
|
|
20959
21082
|
}
|
|
20960
21083
|
});
|
|
20961
21084
|
|
|
20962
21085
|
// ../cli/src/commands/init.ts
|
|
20963
|
-
import
|
|
21086
|
+
import fs58 from "node:fs";
|
|
20964
21087
|
function boardReadme() {
|
|
20965
21088
|
return `# Board
|
|
20966
21089
|
|
|
@@ -20973,20 +21096,20 @@ _(none)_
|
|
|
20973
21096
|
`;
|
|
20974
21097
|
}
|
|
20975
21098
|
function writeConfigIfMissing() {
|
|
20976
|
-
if (
|
|
20977
|
-
|
|
20978
|
-
|
|
21099
|
+
if (fs58.existsSync(CONFIG)) return false;
|
|
21100
|
+
fs58.mkdirSync(KANBAN, { recursive: true });
|
|
21101
|
+
fs58.writeFileSync(CONFIG, CONFIG_TEMPLATE);
|
|
20979
21102
|
return true;
|
|
20980
21103
|
}
|
|
20981
21104
|
function writeModulesIfMissing() {
|
|
20982
|
-
if (
|
|
20983
|
-
|
|
20984
|
-
|
|
21105
|
+
if (fs58.existsSync(MODULES_MD)) return false;
|
|
21106
|
+
fs58.mkdirSync(KANBAN, { recursive: true });
|
|
21107
|
+
fs58.writeFileSync(MODULES_MD, MODULES_TEMPLATE);
|
|
20985
21108
|
return true;
|
|
20986
21109
|
}
|
|
20987
21110
|
function writeGitignoreIfMissing() {
|
|
20988
|
-
|
|
20989
|
-
const text5 =
|
|
21111
|
+
fs58.mkdirSync(KANBAN, { recursive: true });
|
|
21112
|
+
const text5 = fs58.existsSync(KANBAN_GITIGNORE) ? fs58.readFileSync(KANBAN_GITIGNORE, "utf8") : "";
|
|
20990
21113
|
const has = (rule) => text5.split("\n").some((line) => line.trim() === rule.line);
|
|
20991
21114
|
const missing = IGNORE_RULES.filter((rule) => !has(rule));
|
|
20992
21115
|
if (!missing.length) return false;
|
|
@@ -20994,37 +21117,37 @@ function writeGitignoreIfMissing() {
|
|
|
20994
21117
|
${rule.line}
|
|
20995
21118
|
`).join("");
|
|
20996
21119
|
const separator = !text5 || text5.endsWith("\n") ? "" : "\n";
|
|
20997
|
-
|
|
21120
|
+
fs58.writeFileSync(KANBAN_GITIGNORE, `${text5}${separator}${block}`);
|
|
20998
21121
|
return true;
|
|
20999
21122
|
}
|
|
21000
21123
|
function writeSkeletonIfMissing() {
|
|
21001
21124
|
const added = [];
|
|
21002
|
-
if (!
|
|
21003
|
-
|
|
21125
|
+
if (!fs58.existsSync(TODO)) {
|
|
21126
|
+
fs58.mkdirSync(TODO, { recursive: true });
|
|
21004
21127
|
added.push(`${rel(TODO)}/`);
|
|
21005
21128
|
}
|
|
21006
|
-
if (!
|
|
21007
|
-
|
|
21129
|
+
if (!fs58.existsSync(README)) {
|
|
21130
|
+
fs58.writeFileSync(README, boardReadme());
|
|
21008
21131
|
added.push(rel(README));
|
|
21009
21132
|
}
|
|
21010
21133
|
return added;
|
|
21011
21134
|
}
|
|
21012
21135
|
function retireReleases() {
|
|
21013
21136
|
const done2 = [];
|
|
21014
|
-
if (
|
|
21015
|
-
|
|
21137
|
+
if (fs58.existsSync(RELEASES)) {
|
|
21138
|
+
fs58.rmSync(RELEASES);
|
|
21016
21139
|
done2.push(`deleted ${rel(RELEASES)}`);
|
|
21017
21140
|
}
|
|
21018
21141
|
let cards = 0;
|
|
21019
21142
|
for (const dir of [TODO, ARCHIVE]) {
|
|
21020
|
-
if (!
|
|
21143
|
+
if (!fs58.existsSync(dir)) continue;
|
|
21021
21144
|
for (const file of walkMd(dir)) {
|
|
21022
|
-
const text5 =
|
|
21145
|
+
const text5 = fs58.readFileSync(file, "utf8");
|
|
21023
21146
|
const end2 = text5.startsWith("---\n") ? text5.indexOf("\n---", 3) : -1;
|
|
21024
21147
|
if (end2 < 0) continue;
|
|
21025
21148
|
const head = text5.slice(0, end2).replace(/\nrelease:[^\n]*/, "");
|
|
21026
21149
|
if (head.length === end2) continue;
|
|
21027
|
-
|
|
21150
|
+
fs58.writeFileSync(file, head + text5.slice(end2));
|
|
21028
21151
|
cards++;
|
|
21029
21152
|
}
|
|
21030
21153
|
}
|
|
@@ -21032,7 +21155,7 @@ function retireReleases() {
|
|
|
21032
21155
|
return done2.join(", ");
|
|
21033
21156
|
}
|
|
21034
21157
|
function cmdInit() {
|
|
21035
|
-
if (
|
|
21158
|
+
if (fs58.existsSync(KANBAN)) {
|
|
21036
21159
|
const added = [
|
|
21037
21160
|
...writeSkeletonIfMissing(),
|
|
21038
21161
|
writeConfigIfMissing() && rel(CONFIG),
|
|
@@ -21067,8 +21190,8 @@ function cmdInit() {
|
|
|
21067
21190
|
}
|
|
21068
21191
|
return { board: rel(KANBAN), created: false, added, memory_paths: scaffolded.map((s) => rel(s.dir)) };
|
|
21069
21192
|
}
|
|
21070
|
-
|
|
21071
|
-
|
|
21193
|
+
fs58.mkdirSync(TODO, { recursive: true });
|
|
21194
|
+
fs58.writeFileSync(README, boardReadme());
|
|
21072
21195
|
writeConfigIfMissing();
|
|
21073
21196
|
scaffoldProjectMemory();
|
|
21074
21197
|
writeModulesIfMissing();
|
|
@@ -21157,25 +21280,25 @@ var init_scheduled = __esm({
|
|
|
21157
21280
|
});
|
|
21158
21281
|
|
|
21159
21282
|
// ../cli/src/lib/card-age.ts
|
|
21160
|
-
import
|
|
21161
|
-
import
|
|
21283
|
+
import fs59 from "node:fs";
|
|
21284
|
+
import path64 from "node:path";
|
|
21162
21285
|
function staleAfterIn(config) {
|
|
21163
21286
|
const days = Number(LINE.exec(config)?.[1]);
|
|
21164
21287
|
return Number.isInteger(days) && days > 0 ? days : STALE_AFTER_DEFAULT;
|
|
21165
21288
|
}
|
|
21166
21289
|
function staleAfter() {
|
|
21167
|
-
if (!
|
|
21168
|
-
return staleAfterIn(
|
|
21290
|
+
if (!fs59.existsSync(CONFIG)) return STALE_AFTER_DEFAULT;
|
|
21291
|
+
return staleAfterIn(fs59.readFileSync(CONFIG, "utf8"));
|
|
21169
21292
|
}
|
|
21170
21293
|
function todoPathspec(top) {
|
|
21171
|
-
const from =
|
|
21172
|
-
if (!from || from.startsWith("..") ||
|
|
21173
|
-
return from.split(
|
|
21294
|
+
const from = path64.relative(real(top), real(TODO));
|
|
21295
|
+
if (!from || from.startsWith("..") || path64.isAbsolute(from)) return null;
|
|
21296
|
+
return from.split(path64.sep).join("/");
|
|
21174
21297
|
}
|
|
21175
21298
|
function cardFile2(spec, printed) {
|
|
21176
21299
|
const p = unquotePath(printed);
|
|
21177
21300
|
if (!p.startsWith(`${spec}/`)) return null;
|
|
21178
|
-
return
|
|
21301
|
+
return path64.join(TODO, ...p.slice(spec.length + 1).split("/"));
|
|
21179
21302
|
}
|
|
21180
21303
|
function dirtyCards(top, spec) {
|
|
21181
21304
|
const out = git(["status", "--porcelain", "--untracked-files=all", "--", spec], top);
|
|
@@ -21222,9 +21345,9 @@ var init_card_age = __esm({
|
|
|
21222
21345
|
gitTop = () => git(["rev-parse", "--show-toplevel"])?.trim() || null;
|
|
21223
21346
|
real = (p) => {
|
|
21224
21347
|
try {
|
|
21225
|
-
return
|
|
21348
|
+
return fs59.realpathSync(p);
|
|
21226
21349
|
} catch {
|
|
21227
|
-
return
|
|
21350
|
+
return path64.resolve(p);
|
|
21228
21351
|
}
|
|
21229
21352
|
};
|
|
21230
21353
|
unquotePath = (line) => {
|
|
@@ -21259,8 +21382,8 @@ var init_card_holds = __esm({
|
|
|
21259
21382
|
});
|
|
21260
21383
|
|
|
21261
21384
|
// ../cli/src/commands/list.ts
|
|
21262
|
-
import
|
|
21263
|
-
import
|
|
21385
|
+
import fs60 from "node:fs";
|
|
21386
|
+
import path65 from "node:path";
|
|
21264
21387
|
function summaryLine(body) {
|
|
21265
21388
|
const lines = body.split("\n");
|
|
21266
21389
|
let i = 0;
|
|
@@ -21276,13 +21399,13 @@ function summaryLine(body) {
|
|
|
21276
21399
|
function openRows() {
|
|
21277
21400
|
const rows = [];
|
|
21278
21401
|
for (const file of walkMd(TODO)) {
|
|
21279
|
-
const base =
|
|
21402
|
+
const base = path65.basename(file);
|
|
21280
21403
|
if (base === "README.md") continue;
|
|
21281
21404
|
const isRoot = base === "root.md";
|
|
21282
|
-
const id = isRoot ? idPrefix(
|
|
21405
|
+
const id = isRoot ? idPrefix(path65.basename(path65.dirname(file))) : idPrefix(base);
|
|
21283
21406
|
if (id == null) continue;
|
|
21284
|
-
if (isLegacyRecurring(
|
|
21285
|
-
const { meta, body } = parseFrontmatter(
|
|
21407
|
+
if (isLegacyRecurring(path65.relative(TODO, file))) continue;
|
|
21408
|
+
const { meta, body } = parseFrontmatter(fs60.readFileSync(file, "utf8"));
|
|
21286
21409
|
rows.push({
|
|
21287
21410
|
id,
|
|
21288
21411
|
file,
|
|
@@ -21324,15 +21447,15 @@ function cmdStale(rows, all2, scope, mod) {
|
|
|
21324
21447
|
}
|
|
21325
21448
|
function archivedFiles() {
|
|
21326
21449
|
const files2 = /* @__PURE__ */ new Map();
|
|
21327
|
-
if (!
|
|
21450
|
+
if (!fs60.existsSync(ARCHIVE)) return files2;
|
|
21328
21451
|
for (const file of walkMd(ARCHIVE)) {
|
|
21329
|
-
const id = idPrefix(
|
|
21452
|
+
const id = idPrefix(path65.basename(file));
|
|
21330
21453
|
if (id !== null) files2.set(id, file);
|
|
21331
21454
|
}
|
|
21332
21455
|
for (const dir of walkDirs(ARCHIVE)) {
|
|
21333
|
-
const id = idPrefix(
|
|
21334
|
-
const root =
|
|
21335
|
-
if (id !== null &&
|
|
21456
|
+
const id = idPrefix(path65.basename(dir));
|
|
21457
|
+
const root = path65.join(dir, "root.md");
|
|
21458
|
+
if (id !== null && fs60.existsSync(root)) files2.set(id, root);
|
|
21336
21459
|
}
|
|
21337
21460
|
return files2;
|
|
21338
21461
|
}
|
|
@@ -21443,14 +21566,14 @@ var init_list = __esm({
|
|
|
21443
21566
|
});
|
|
21444
21567
|
|
|
21445
21568
|
// ../cli/src/commands/misc.ts
|
|
21446
|
-
import
|
|
21447
|
-
import
|
|
21569
|
+
import fs61 from "node:fs";
|
|
21570
|
+
import path66 from "node:path";
|
|
21448
21571
|
function extractOldMeta(text5, file) {
|
|
21449
21572
|
const grab = (re) => {
|
|
21450
21573
|
const m = text5.match(re);
|
|
21451
21574
|
return m ? m[1].trim() : null;
|
|
21452
21575
|
};
|
|
21453
|
-
const title = grab(/^#\s+(.+)$/m) || slugify(
|
|
21576
|
+
const title = grab(/^#\s+(.+)$/m) || slugify(path66.basename(file, ".md").replace(/^\d+-/, "")).replace(/-/g, " ");
|
|
21454
21577
|
const norm = (raw) => {
|
|
21455
21578
|
const v = (raw || "").toLowerCase();
|
|
21456
21579
|
return LEVELS.includes(v) ? v : "med";
|
|
@@ -21477,11 +21600,11 @@ function stripOldHeader(text5) {
|
|
|
21477
21600
|
}
|
|
21478
21601
|
function cmdMigrate(opts) {
|
|
21479
21602
|
const dry = opts.dryRun === true;
|
|
21480
|
-
const files2 = walkMd(TODO).filter((f) =>
|
|
21603
|
+
const files2 = walkMd(TODO).filter((f) => path66.basename(f) !== "README.md");
|
|
21481
21604
|
let changed = 0;
|
|
21482
21605
|
let skipped = 0;
|
|
21483
21606
|
for (const file of files2) {
|
|
21484
|
-
const text5 =
|
|
21607
|
+
const text5 = fs61.readFileSync(file, "utf8");
|
|
21485
21608
|
if (text5.trimStart().startsWith("---")) {
|
|
21486
21609
|
skipped++;
|
|
21487
21610
|
continue;
|
|
@@ -21490,7 +21613,7 @@ function cmdMigrate(opts) {
|
|
|
21490
21613
|
const out = serializeFrontmatter(meta) + "\n\n" + stripOldHeader(text5).replace(/^\n+/, "");
|
|
21491
21614
|
if (dry) say(`would migrate ${rel(file)} (priority=${meta.priority})`);
|
|
21492
21615
|
else {
|
|
21493
|
-
|
|
21616
|
+
fs61.writeFileSync(file, out.endsWith("\n") ? out : out + "\n");
|
|
21494
21617
|
say(`migrated ${rel(file)}`);
|
|
21495
21618
|
}
|
|
21496
21619
|
changed++;
|
|
@@ -21511,10 +21634,10 @@ var init_misc = __esm({
|
|
|
21511
21634
|
});
|
|
21512
21635
|
|
|
21513
21636
|
// ../cli/src/lib/group-close.ts
|
|
21514
|
-
import
|
|
21637
|
+
import fs62 from "node:fs";
|
|
21515
21638
|
function groupCloses(rootFile) {
|
|
21516
21639
|
try {
|
|
21517
|
-
return groupCloseOf(
|
|
21640
|
+
return groupCloseOf(fs62.readFileSync(rootFile, "utf8"));
|
|
21518
21641
|
} catch {
|
|
21519
21642
|
return false;
|
|
21520
21643
|
}
|
|
@@ -21527,12 +21650,12 @@ var init_group_close = __esm({
|
|
|
21527
21650
|
});
|
|
21528
21651
|
|
|
21529
21652
|
// ../cli/src/commands/remove.ts
|
|
21530
|
-
import
|
|
21531
|
-
import
|
|
21653
|
+
import fs63 from "node:fs";
|
|
21654
|
+
import path67 from "node:path";
|
|
21532
21655
|
function leavingIds(id, found2) {
|
|
21533
21656
|
const ids = /* @__PURE__ */ new Set([id]);
|
|
21534
21657
|
if (found2.kind === "group") {
|
|
21535
|
-
const names4 = [...walkMd(found2.target), ...walkDirs(found2.target)].map((f) =>
|
|
21658
|
+
const names4 = [...walkMd(found2.target), ...walkDirs(found2.target)].map((f) => path67.basename(f));
|
|
21536
21659
|
for (const name of names4) {
|
|
21537
21660
|
const n = idPrefix(name);
|
|
21538
21661
|
if (n !== null) ids.add(n);
|
|
@@ -21544,10 +21667,10 @@ function findMentions(ids) {
|
|
|
21544
21667
|
const hits = [];
|
|
21545
21668
|
const re = new RegExp(ids.map((id) => `#${id}(?!\\d)`).join("|"));
|
|
21546
21669
|
for (const dir of [TODO, MEMORY]) {
|
|
21547
|
-
if (!
|
|
21670
|
+
if (!fs63.existsSync(dir)) continue;
|
|
21548
21671
|
for (const file of walkMd(dir)) {
|
|
21549
|
-
if (
|
|
21550
|
-
const lines =
|
|
21672
|
+
if (path67.basename(file) === "README.md") continue;
|
|
21673
|
+
const lines = fs63.readFileSync(file, "utf8").split("\n");
|
|
21551
21674
|
const fmEnd = frontmatterEnd(lines);
|
|
21552
21675
|
lines.forEach((line, i) => {
|
|
21553
21676
|
if (!re.test(line)) return;
|
|
@@ -21563,12 +21686,12 @@ function findMentions(ids) {
|
|
|
21563
21686
|
return hits;
|
|
21564
21687
|
}
|
|
21565
21688
|
function leavingCards(id, found2) {
|
|
21566
|
-
const own = found2.kind === "group" ?
|
|
21689
|
+
const own = found2.kind === "group" ? path67.join(found2.target, "root.md") : found2.target;
|
|
21567
21690
|
const cards = [{ id, file: own }];
|
|
21568
21691
|
if (found2.kind !== "group") return cards;
|
|
21569
21692
|
for (const file of walkMd(found2.target)) {
|
|
21570
21693
|
if (file === own) continue;
|
|
21571
|
-
const n = idPrefix(
|
|
21694
|
+
const n = idPrefix(path67.basename(file));
|
|
21572
21695
|
if (n !== null) cards.push({ id: n, file });
|
|
21573
21696
|
}
|
|
21574
21697
|
return cards;
|
|
@@ -21577,10 +21700,10 @@ function stampLeaving(cards, rejection) {
|
|
|
21577
21700
|
const day = formatDay();
|
|
21578
21701
|
const stamp4 = rejection && !rejection.discard ? { reason: rejection.reason, at: (/* @__PURE__ */ new Date()).toISOString(), by: insideRun() ? "agent" : "user" } : rejection && { reason: rejection.reason };
|
|
21579
21702
|
for (const card of cards) {
|
|
21580
|
-
if (!
|
|
21581
|
-
const text5 =
|
|
21703
|
+
if (!fs63.existsSync(card.file)) continue;
|
|
21704
|
+
const text5 = fs63.readFileSync(card.file, "utf8");
|
|
21582
21705
|
const stamped = stampLeavingIn(text5, day, stamp4);
|
|
21583
|
-
if (stamped !== text5)
|
|
21706
|
+
if (stamped !== text5) fs63.writeFileSync(card.file, stamped);
|
|
21584
21707
|
}
|
|
21585
21708
|
}
|
|
21586
21709
|
function unfinishedDelivery(id) {
|
|
@@ -21596,7 +21719,7 @@ function unfinishedDelivery(id) {
|
|
|
21596
21719
|
}[gap];
|
|
21597
21720
|
return `#${id} is not finished: ${what}.`;
|
|
21598
21721
|
}
|
|
21599
|
-
const missing = planDeliverables(card.body).filter((src) => src.startsWith(".assets/")).find((src) => !
|
|
21722
|
+
const missing = planDeliverables(card.body).filter((src) => src.startsWith(".assets/")).find((src) => !fs63.existsSync(path67.join(ASSETS, src.slice(".assets/".length))));
|
|
21600
21723
|
return missing ? `#${id} is not finished: ${missing} is not on this machine.` : null;
|
|
21601
21724
|
}
|
|
21602
21725
|
function agentDecisions(text5) {
|
|
@@ -21623,8 +21746,8 @@ function removeCard2(id, metric, options) {
|
|
|
21623
21746
|
const unfinished = metric === "completed" ? unfinishedDelivery(id) : null;
|
|
21624
21747
|
if (unfinished) die(unfinished, { kind: "plan-delivery-unfinished" });
|
|
21625
21748
|
const dest = options.cleanupDiscarded && locateArchived(id) ? null : archiveDest(found2);
|
|
21626
|
-
const cardFile3 = found2.kind === "group" ?
|
|
21627
|
-
const cardText =
|
|
21749
|
+
const cardFile3 = found2.kind === "group" ? path67.join(found2.target, "root.md") : found2.target;
|
|
21750
|
+
const cardText = fs63.existsSync(cardFile3) ? fs63.readFileSync(cardFile3, "utf8") : "";
|
|
21628
21751
|
const { meta: cardMeta3 } = parseFrontmatter(cardText);
|
|
21629
21752
|
const alsoRemoved = found2.kind === "group" ? walkMd(found2.target).filter((f) => f !== cardFile3).map((f) => rel(f)).sort() : [];
|
|
21630
21753
|
const leftIds = leavingIds(id, found2);
|
|
@@ -21655,10 +21778,10 @@ function removeCard2(id, metric, options) {
|
|
|
21655
21778
|
const groupRoot = found2.kind === "file" && !options.closing ? enclosingGroupRoot(found2.target) : null;
|
|
21656
21779
|
if (dest) {
|
|
21657
21780
|
stampLeaving(leaving, metric === "rejected" ? { reason: options.reason?.trim() ?? "", discard: options.discard === true } : null);
|
|
21658
|
-
|
|
21659
|
-
|
|
21781
|
+
fs63.mkdirSync(ARCHIVE, { recursive: true });
|
|
21782
|
+
fs63.renameSync(found2.target, dest);
|
|
21660
21783
|
} else {
|
|
21661
|
-
|
|
21784
|
+
fs63.rmSync(found2.target, { recursive: true, force: true });
|
|
21662
21785
|
}
|
|
21663
21786
|
const removedRefs = stripReadmeRefs(found2);
|
|
21664
21787
|
let marked2 = null;
|
|
@@ -21707,14 +21830,14 @@ function removeCard2(id, metric, options) {
|
|
|
21707
21830
|
};
|
|
21708
21831
|
}
|
|
21709
21832
|
function closeGroup(rootFile) {
|
|
21710
|
-
const rootId = idPrefix(
|
|
21833
|
+
const rootId = idPrefix(path67.basename(path67.dirname(rootFile)));
|
|
21711
21834
|
if (rootId === null) return null;
|
|
21712
21835
|
if (runnerProject(KANBAN)) return null;
|
|
21713
21836
|
if (!groupCloses(rootFile)) return null;
|
|
21714
21837
|
say(`
|
|
21715
21838
|
every subtask line on #${rootId} is resolved \u2014 closing the group:`);
|
|
21716
21839
|
try {
|
|
21717
|
-
const { meta, body } = parseFrontmatter(
|
|
21840
|
+
const { meta, body } = parseFrontmatter(fs63.readFileSync(rootFile, "utf8"));
|
|
21718
21841
|
const res = cmdRemove(rootId, "completed", { closing: true });
|
|
21719
21842
|
return {
|
|
21720
21843
|
id: rootId,
|
|
@@ -21771,7 +21894,7 @@ function cleanupDiscardedCards(sessionId) {
|
|
|
21771
21894
|
if (card.pending) throw new Error(`Discarding #${card.id} did not finish. Retry the discard.`);
|
|
21772
21895
|
while (locate(card.id)) cmdRemove(card.id, "rejected", { discard: true, cleanupDiscarded: true });
|
|
21773
21896
|
dropCrossRefs(card.id);
|
|
21774
|
-
stripReadmeRefs({ kind: "file", rel:
|
|
21897
|
+
stripReadmeRefs({ kind: "file", rel: path67.relative(TODO, path67.resolve(REPO_ROOT, card.path)).split(path67.sep).join("/") });
|
|
21775
21898
|
}
|
|
21776
21899
|
return discarded.map((c) => c.id);
|
|
21777
21900
|
});
|
|
@@ -21857,8 +21980,8 @@ var init_memory_file = __esm({
|
|
|
21857
21980
|
});
|
|
21858
21981
|
|
|
21859
21982
|
// ../cli/src/commands/rule.ts
|
|
21860
|
-
import
|
|
21861
|
-
import
|
|
21983
|
+
import fs64 from "node:fs";
|
|
21984
|
+
import path68 from "node:path";
|
|
21862
21985
|
function cmdRule(askedName, flags) {
|
|
21863
21986
|
const agent = askedName.trim();
|
|
21864
21987
|
if (!agentRoster().some((a) => a.name === agent)) {
|
|
@@ -21867,7 +21990,7 @@ function cmdRule(askedName, flags) {
|
|
|
21867
21990
|
const rule = readRuleText(flags.file, flags.text);
|
|
21868
21991
|
const res = setAgentRule(agent, rule);
|
|
21869
21992
|
if (!res.ok) die(res.error);
|
|
21870
|
-
const file = rel(
|
|
21993
|
+
const file = rel(path68.join(RULES, `${agent}.md`));
|
|
21871
21994
|
say(rule ? `wrote the \`${agent}\` rule (${file})` : `cleared the \`${agent}\` rule`);
|
|
21872
21995
|
return { agent, file, cleared: !rule };
|
|
21873
21996
|
}
|
|
@@ -21875,7 +21998,7 @@ function readRuleText(file, text5) {
|
|
|
21875
21998
|
if (file !== void 0 && text5 !== void 0) die("pass --file or --text, not both");
|
|
21876
21999
|
if (file !== void 0) {
|
|
21877
22000
|
try {
|
|
21878
|
-
return
|
|
22001
|
+
return fs64.readFileSync(file, "utf8").trim();
|
|
21879
22002
|
} catch {
|
|
21880
22003
|
die(`can't read ${file} \u2014 write the rule to a file, then pass its path`);
|
|
21881
22004
|
}
|
|
@@ -22002,22 +22125,22 @@ var init_storyboard = __esm({
|
|
|
22002
22125
|
});
|
|
22003
22126
|
|
|
22004
22127
|
// ../cli/src/lib/spec-contract.ts
|
|
22005
|
-
import
|
|
22006
|
-
import
|
|
22128
|
+
import fs65 from "node:fs";
|
|
22129
|
+
import path69 from "node:path";
|
|
22007
22130
|
function snapshotSpecs() {
|
|
22008
22131
|
const out = /* @__PURE__ */ new Map();
|
|
22009
|
-
if (!
|
|
22132
|
+
if (!fs65.existsSync(TODO)) return out;
|
|
22010
22133
|
for (const file of walkMd(TODO)) {
|
|
22011
|
-
if (isLegacyRecurring(
|
|
22012
|
-
const id = idPrefix(
|
|
22013
|
-
if (id !== null) out.set(file, { id, text:
|
|
22134
|
+
if (isLegacyRecurring(path69.relative(TODO, file))) continue;
|
|
22135
|
+
const id = idPrefix(path69.basename(file) === "root.md" ? path69.basename(path69.dirname(file)) : path69.basename(file));
|
|
22136
|
+
if (id !== null) out.set(file, { id, text: fs65.readFileSync(file, "utf8") });
|
|
22014
22137
|
}
|
|
22015
22138
|
return out;
|
|
22016
22139
|
}
|
|
22017
22140
|
function enclosingGroups(file) {
|
|
22018
22141
|
const ids = [];
|
|
22019
22142
|
for (let root = enclosingGroupRoot(file); root; root = enclosingGroupRoot(root)) {
|
|
22020
|
-
const id = idPrefix(
|
|
22143
|
+
const id = idPrefix(path69.basename(path69.dirname(root)));
|
|
22021
22144
|
if (id !== null) ids.push(id);
|
|
22022
22145
|
}
|
|
22023
22146
|
return ids;
|
|
@@ -22186,9 +22309,9 @@ function validateSpec(file, text5, id) {
|
|
|
22186
22309
|
}
|
|
22187
22310
|
function inside(dir, name) {
|
|
22188
22311
|
try {
|
|
22189
|
-
const root =
|
|
22190
|
-
const real2 =
|
|
22191
|
-
return real2.startsWith(root +
|
|
22312
|
+
const root = fs65.realpathSync(dir);
|
|
22313
|
+
const real2 = fs65.realpathSync(path69.join(dir, name));
|
|
22314
|
+
return real2.startsWith(root + path69.sep) && fs65.statSync(real2).isFile() ? real2 : null;
|
|
22192
22315
|
} catch {
|
|
22193
22316
|
return null;
|
|
22194
22317
|
}
|
|
@@ -22206,7 +22329,7 @@ function variantsProblem(raw, src, id) {
|
|
|
22206
22329
|
if (files2.length < 2 || !files2.every((f) => typeof f === "string" && f)) return `variants needs at least two labels, each naming a file. ${shape}`;
|
|
22207
22330
|
if (!files2.includes(src)) return `src="${src}" is not one of the variants. Set src to the version shown first.`;
|
|
22208
22331
|
if (id === void 0) return null;
|
|
22209
|
-
const dir =
|
|
22332
|
+
const dir = path69.join(ASSETS, String(id));
|
|
22210
22333
|
for (const f of files2) {
|
|
22211
22334
|
const parts = f.split("/");
|
|
22212
22335
|
if (parts.length !== 3 || parts[0] !== ".assets" || parts[1] !== String(id) || !inside(dir, parts[2])) {
|
|
@@ -22216,7 +22339,7 @@ function variantsProblem(raw, src, id) {
|
|
|
22216
22339
|
return null;
|
|
22217
22340
|
}
|
|
22218
22341
|
function compareProblem(attrs, id) {
|
|
22219
|
-
const dir =
|
|
22342
|
+
const dir = path69.join(ASSETS, String(id));
|
|
22220
22343
|
const src = (message) => ({ rule: "compare-src", message });
|
|
22221
22344
|
const shots = (message) => ({ rule: "compare-shots", message });
|
|
22222
22345
|
for (const side of ["a", "b"]) {
|
|
@@ -22236,7 +22359,7 @@ function compareProblem(attrs, id) {
|
|
|
22236
22359
|
const file = inside(dir, named2.name);
|
|
22237
22360
|
if (!file) return { message: `Compare shots ${at} names no file in this card's asset folder. Write the JSON there or fix the path.` };
|
|
22238
22361
|
try {
|
|
22239
|
-
return { data: JSON.parse(
|
|
22362
|
+
return { data: JSON.parse(fs65.readFileSync(file, "utf8")) };
|
|
22240
22363
|
} catch {
|
|
22241
22364
|
return { message: `${at} is not valid JSON.` };
|
|
22242
22365
|
}
|
|
@@ -22256,7 +22379,7 @@ function compareProblem(attrs, id) {
|
|
|
22256
22379
|
}
|
|
22257
22380
|
function validateStoryboards(file, id, text5) {
|
|
22258
22381
|
const errors = [];
|
|
22259
|
-
const dir =
|
|
22382
|
+
const dir = path69.join(ASSETS, String(id));
|
|
22260
22383
|
for (const marker of storyboardMarkers(text5)) {
|
|
22261
22384
|
const named2 = assetName(marker.src, id, ["json"]);
|
|
22262
22385
|
const add = (message) => errors.push({ file: rel(file), line: marker.line, rule: "storyboard-src", message });
|
|
@@ -22405,8 +22528,8 @@ var init_mockups = __esm({
|
|
|
22405
22528
|
});
|
|
22406
22529
|
|
|
22407
22530
|
// ../cli/src/commands/spec-write.ts
|
|
22408
|
-
import
|
|
22409
|
-
import
|
|
22531
|
+
import fs66 from "node:fs";
|
|
22532
|
+
import path70 from "node:path";
|
|
22410
22533
|
function cmdSpecWrite(id, askedName, flags) {
|
|
22411
22534
|
const agent = findSpecAgent(askedName);
|
|
22412
22535
|
if (!agent) die(notAnAgent(askedName), { kind: "no-such-spec-agent", specAgent: askedName });
|
|
@@ -22414,11 +22537,11 @@ function cmdSpecWrite(id, askedName, flags) {
|
|
|
22414
22537
|
const section = readSection(flags.file, flags.text);
|
|
22415
22538
|
const found2 = locate(id);
|
|
22416
22539
|
if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
|
|
22417
|
-
const file = found2.kind === "group" ?
|
|
22418
|
-
const { meta, body } = parseFrontmatter(
|
|
22540
|
+
const file = found2.kind === "group" ? path70.join(found2.target, "root.md") : found2.target;
|
|
22541
|
+
const { meta, body } = parseFrontmatter(fs66.readFileSync(file, "utf8"));
|
|
22419
22542
|
if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
|
|
22420
22543
|
const { body: next, replaced } = splice(body, name, section);
|
|
22421
|
-
|
|
22544
|
+
fs66.writeFileSync(file, serializeFrontmatter(meta) + "\n" + next);
|
|
22422
22545
|
say(`${replaced ? "rewrote" : "wrote"} the \`${name}\` section on #${id} (${rel(file)})`);
|
|
22423
22546
|
return { id, specAgent: name, replaced, file: rel(file) };
|
|
22424
22547
|
}
|
|
@@ -22427,7 +22550,7 @@ function readSection(file, text5) {
|
|
|
22427
22550
|
let raw;
|
|
22428
22551
|
if (file !== void 0) {
|
|
22429
22552
|
try {
|
|
22430
|
-
raw =
|
|
22553
|
+
raw = fs66.readFileSync(file, "utf8");
|
|
22431
22554
|
} catch {
|
|
22432
22555
|
die(`can't read ${file} \u2014 write your section to a file, then pass its path`);
|
|
22433
22556
|
}
|
|
@@ -22621,7 +22744,7 @@ function localBoard() {
|
|
|
22621
22744
|
const no = checkWrite({ board: true }, env2, boardRevision());
|
|
22622
22745
|
if (no) return no;
|
|
22623
22746
|
const res = await resumeDelivery(deliveryId);
|
|
22624
|
-
if (!res.ok) return opRefused(new Error(res.error || "the delivery could not be carried on"));
|
|
22747
|
+
if (!res.ok) return { ...opRefused(new Error(res.error || "the delivery could not be carried on")), reason: res.reason, args: res.args };
|
|
22625
22748
|
return opOk(boardRevision(), { deliveryId: res.deliveryId, landed: res.landed, carryOn: res.carryOn });
|
|
22626
22749
|
},
|
|
22627
22750
|
async discardDelivery(deliveryId, env2) {
|
|
@@ -22893,11 +23016,11 @@ var init_shared = __esm({
|
|
|
22893
23016
|
});
|
|
22894
23017
|
|
|
22895
23018
|
// ../cli/src/commands/goal.ts
|
|
22896
|
-
import
|
|
23019
|
+
import fs67 from "node:fs";
|
|
22897
23020
|
function load() {
|
|
22898
23021
|
const file = goalsFile();
|
|
22899
23022
|
if (!file) die("Goals exist only inside a WildCrane goal discussion.");
|
|
22900
|
-
const held4 = JSON.parse(
|
|
23023
|
+
const held4 = JSON.parse(fs67.readFileSync(file, "utf8"));
|
|
22901
23024
|
if (held4.goals !== void 0 && held4.ideas !== void 0 && JSON.stringify(held4.goals) !== JSON.stringify(held4.ideas)) die("goals and ideas do not match.");
|
|
22902
23025
|
const goals = held4.goals ?? held4.ideas;
|
|
22903
23026
|
if (!Array.isArray(goals)) die("The goals array is missing or invalid.");
|
|
@@ -22922,7 +23045,7 @@ function splitBody2(text5) {
|
|
|
22922
23045
|
}
|
|
22923
23046
|
function readBody(bodyFile) {
|
|
22924
23047
|
if (!bodyFile) die("--body-file is required; write the goal in a writable temporary file first.");
|
|
22925
|
-
const text5 =
|
|
23048
|
+
const text5 = fs67.readFileSync(bodyFile, "utf8");
|
|
22926
23049
|
if (!text5.trim()) die("The goal must not be empty.");
|
|
22927
23050
|
return splitBody2(text5);
|
|
22928
23051
|
}
|
|
@@ -22934,7 +23057,7 @@ function named(held4, id) {
|
|
|
22934
23057
|
function cmdGoal(args, opts) {
|
|
22935
23058
|
const sub = args[0] ?? "";
|
|
22936
23059
|
const { file, held: held4 } = load();
|
|
22937
|
-
const write2 = () =>
|
|
23060
|
+
const write2 = () => fs67.writeFileSync(file, `${JSON.stringify({ workflows: held4.workflows, ideas: held4.goals }, null, 2)}
|
|
22938
23061
|
`);
|
|
22939
23062
|
if (sub === "new") {
|
|
22940
23063
|
const title = opts.title?.trim();
|
|
@@ -23004,8 +23127,8 @@ __export(board_exports, {
|
|
|
23004
23127
|
buildBoardProgram: () => buildBoardProgram
|
|
23005
23128
|
});
|
|
23006
23129
|
import crypto4 from "node:crypto";
|
|
23007
|
-
import
|
|
23008
|
-
import
|
|
23130
|
+
import fs68 from "node:fs";
|
|
23131
|
+
import path71 from "node:path";
|
|
23009
23132
|
async function dispatch(move2, cmd, args, opts, cli) {
|
|
23010
23133
|
const ctx = ctxOf(cmd, cli.program);
|
|
23011
23134
|
const data = await runAction(ctx, {}, async () => {
|
|
@@ -23044,7 +23167,7 @@ async function cloudCard(task, opts) {
|
|
|
23044
23167
|
let request;
|
|
23045
23168
|
if (typeof opts.bodyFile === "string") {
|
|
23046
23169
|
try {
|
|
23047
|
-
request =
|
|
23170
|
+
request = fs68.readFileSync(opts.bodyFile, "utf8");
|
|
23048
23171
|
} catch {
|
|
23049
23172
|
}
|
|
23050
23173
|
}
|
|
@@ -23074,7 +23197,7 @@ async function cloudCard(task, opts) {
|
|
|
23074
23197
|
const file = creationReceipt(`${checked.value}:${requestId}`, slot);
|
|
23075
23198
|
const journal = readCreation(file, said3);
|
|
23076
23199
|
if (!journal.made) {
|
|
23077
|
-
const key = `akb-${
|
|
23200
|
+
const key = `akb-${path71.basename(file, ".json").slice(0, 40)}`;
|
|
23078
23201
|
const made = await createProjectTask(checked.value, journal.said, key);
|
|
23079
23202
|
if (!made.ok) throw new BoardError(creationError(`Cloud did not make the task: ${made.error}`, triage), { kind: "refused" });
|
|
23080
23203
|
journal.made = made.value;
|
|
@@ -23219,16 +23342,16 @@ var init_board2 = __esm({
|
|
|
23219
23342
|
});
|
|
23220
23343
|
|
|
23221
23344
|
// ../cli/src/lib/board-cli.ts
|
|
23222
|
-
import
|
|
23223
|
-
import
|
|
23345
|
+
import fs69 from "node:fs";
|
|
23346
|
+
import path72 from "node:path";
|
|
23224
23347
|
function useBoard(found2, dirNamed) {
|
|
23225
|
-
if (!found2.named && found2.board ===
|
|
23348
|
+
if (!found2.named && found2.board === path72.join(found2.root, "docs", "kanban")) setBoardRoot(found2.root, dirNamed);
|
|
23226
23349
|
else setBoardDir(found2.board, found2.root);
|
|
23227
23350
|
useProjectState();
|
|
23228
23351
|
}
|
|
23229
23352
|
function whole(found2, move2) {
|
|
23230
23353
|
if (move2 === MAKES_A_BOARD) return found2;
|
|
23231
|
-
if (!
|
|
23354
|
+
if (!fs69.existsSync(path72.join(found2.board, "todo"))) {
|
|
23232
23355
|
die(`the board in ${found2.board} has no todo/ \u2014 run \`init\` to add what is missing.`, {
|
|
23233
23356
|
kind: "board-incomplete",
|
|
23234
23357
|
dir: found2.root
|
|
@@ -23237,13 +23360,13 @@ function whole(found2, move2) {
|
|
|
23237
23360
|
return found2;
|
|
23238
23361
|
}
|
|
23239
23362
|
function findBoardUpward(from, move2) {
|
|
23240
|
-
let dir =
|
|
23363
|
+
let dir = path72.resolve(from);
|
|
23241
23364
|
for (; ; ) {
|
|
23242
|
-
if (hasBoard(dir)) return { root: dir, board:
|
|
23365
|
+
if (hasBoard(dir)) return { root: dir, board: path72.join(dir, "docs", "kanban") };
|
|
23243
23366
|
if (isBoardDir(dir)) return { root: projectRootOf(dir), board: dir };
|
|
23244
|
-
const kept2 =
|
|
23245
|
-
if (move2 !== MAKES_A_BOARD &&
|
|
23246
|
-
const up =
|
|
23367
|
+
const kept2 = path72.join(dir, ".akb", "kanban");
|
|
23368
|
+
if (move2 !== MAKES_A_BOARD && fs69.existsSync(path72.join(kept2, "todo"))) return { root: dir, board: kept2 };
|
|
23369
|
+
const up = path72.dirname(dir);
|
|
23247
23370
|
if (up === dir) return null;
|
|
23248
23371
|
dir = up;
|
|
23249
23372
|
}
|
|
@@ -23251,11 +23374,11 @@ function findBoardUpward(from, move2) {
|
|
|
23251
23374
|
function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
|
|
23252
23375
|
const named2 = board2 ?? boardFromEnv();
|
|
23253
23376
|
if (named2) {
|
|
23254
|
-
const folder =
|
|
23255
|
-
if (!
|
|
23377
|
+
const folder = path72.resolve(cwd, named2);
|
|
23378
|
+
if (!fs69.existsSync(folder) && move2 !== MAKES_A_BOARD) {
|
|
23256
23379
|
die(`no such folder: ${folder}`, { kind: "no-such-folder", dir: folder });
|
|
23257
23380
|
}
|
|
23258
|
-
if (
|
|
23381
|
+
if (fs69.existsSync(folder) && !isBoardDir(folder) && move2 !== MAKES_A_BOARD) {
|
|
23259
23382
|
die(`${folder} is not a board \u2014 it has no todo/ and config.md. ${installHint}\u3002`, {
|
|
23260
23383
|
kind: "no-board",
|
|
23261
23384
|
dir: folder
|
|
@@ -23264,24 +23387,24 @@ function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
|
|
|
23264
23387
|
return { root: projectRootOf(folder), board: folder, named: true };
|
|
23265
23388
|
}
|
|
23266
23389
|
if (dir !== null) {
|
|
23267
|
-
const root =
|
|
23268
|
-
if (!
|
|
23269
|
-
const kept2 =
|
|
23270
|
-
if (!hasBoard(root) && move2 !== MAKES_A_BOARD &&
|
|
23390
|
+
const root = path72.resolve(dir);
|
|
23391
|
+
if (!fs69.existsSync(root)) die(`no such folder: ${root}`, { kind: "no-such-folder", dir: root });
|
|
23392
|
+
const kept2 = path72.join(root, ".akb", "kanban");
|
|
23393
|
+
if (!hasBoard(root) && move2 !== MAKES_A_BOARD && fs69.existsSync(path72.join(kept2, "todo"))) return whole({ root, board: kept2 }, move2);
|
|
23271
23394
|
if (!hasBoard(root) && move2 !== MAKES_A_BOARD) {
|
|
23272
23395
|
die(`no board in ${root} \u2014 it has no docs/kanban/. ${installHint}\u3002`, {
|
|
23273
23396
|
kind: "no-board",
|
|
23274
23397
|
dir: root
|
|
23275
23398
|
});
|
|
23276
23399
|
}
|
|
23277
|
-
return whole({ root, board:
|
|
23400
|
+
return whole({ root, board: path72.join(root, "docs", "kanban") }, move2);
|
|
23278
23401
|
}
|
|
23279
23402
|
const found2 = findBoardUpward(cwd, move2);
|
|
23280
23403
|
if (found2) return whole(found2, move2);
|
|
23281
|
-
if (move2 === MAKES_A_BOARD) return { root:
|
|
23404
|
+
if (move2 === MAKES_A_BOARD) return { root: path72.resolve(cwd), board: path72.join(path72.resolve(cwd), "docs", "kanban") };
|
|
23282
23405
|
die(
|
|
23283
|
-
`no board here. Looked in ${
|
|
23284
|
-
{ kind: "no-board", dir:
|
|
23406
|
+
`no board here. Looked in ${path72.resolve(cwd)} and every folder above it for docs/kanban/. ${installHint}\uFF0C\u6216\u7528 --board \u6307\u5B9A\u5DF2\u6709\u770B\u677F\u3002`,
|
|
23407
|
+
{ kind: "no-board", dir: path72.resolve(cwd) }
|
|
23285
23408
|
);
|
|
23286
23409
|
}
|
|
23287
23410
|
function editDistance(a, b) {
|
|
@@ -23363,8 +23486,8 @@ var init_board_cli = __esm({
|
|
|
23363
23486
|
init_io();
|
|
23364
23487
|
init_pointer();
|
|
23365
23488
|
MAKES_A_BOARD = "init";
|
|
23366
|
-
hasBoard = (dir) =>
|
|
23367
|
-
isBoardDir = (dir) =>
|
|
23489
|
+
hasBoard = (dir) => fs69.existsSync(path72.join(dir, "docs", "kanban"));
|
|
23490
|
+
isBoardDir = (dir) => fs69.existsSync(path72.join(dir, "todo")) && fs69.existsSync(path72.join(dir, "config.md"));
|
|
23368
23491
|
boardFromEnv = () => {
|
|
23369
23492
|
const named2 = process.env.AI4KANBAN_BOARD?.trim();
|
|
23370
23493
|
return named2 ? named2 : null;
|
|
@@ -23631,8 +23754,8 @@ var init_guide = __esm({
|
|
|
23631
23754
|
});
|
|
23632
23755
|
|
|
23633
23756
|
// ../cli/src/lib/agent/approve.ts
|
|
23634
|
-
import
|
|
23635
|
-
import
|
|
23757
|
+
import fs75 from "node:fs";
|
|
23758
|
+
import path78 from "node:path";
|
|
23636
23759
|
function approvalRequest(req) {
|
|
23637
23760
|
const id = req.id;
|
|
23638
23761
|
const card = id === void 0 ? null : findCard(id);
|
|
@@ -23650,12 +23773,12 @@ function advancePhase(id) {
|
|
|
23650
23773
|
const card = findCard(id);
|
|
23651
23774
|
const found2 = locate(id);
|
|
23652
23775
|
if (!card || !found2 || !awaitingApproval(card)) return { error: `#${id} is no longer waiting on approval.` };
|
|
23653
|
-
const file = found2.kind === "group" ?
|
|
23654
|
-
const { meta, body } = parseFrontmatter(
|
|
23776
|
+
const file = found2.kind === "group" ? path78.join(found2.target, "root.md") : found2.target;
|
|
23777
|
+
const { meta, body } = parseFrontmatter(fs75.readFileSync(file, "utf8"));
|
|
23655
23778
|
if (!meta) return { error: `#${id} has no frontmatter` };
|
|
23656
23779
|
meta.phase = card.stage + 1;
|
|
23657
23780
|
meta.status = "todo";
|
|
23658
|
-
|
|
23781
|
+
fs75.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
|
|
23659
23782
|
return null;
|
|
23660
23783
|
}
|
|
23661
23784
|
var init_approve = __esm({
|
|
@@ -23759,8 +23882,8 @@ function open2(req, sessionId) {
|
|
|
23759
23882
|
markSpawned(run2.sessionId, pid);
|
|
23760
23883
|
return { run: run2, spawned: pid !== void 0 };
|
|
23761
23884
|
}
|
|
23762
|
-
async function startResume(id,
|
|
23763
|
-
const opened = await openResume(id,
|
|
23885
|
+
async function startResume(id, options) {
|
|
23886
|
+
const opened = await openResume(id, options);
|
|
23764
23887
|
if ("error" in opened) return opened;
|
|
23765
23888
|
const pid = spawnWatcher(opened.run.sessionId);
|
|
23766
23889
|
markSpawned(opened.run.sessionId, pid);
|
|
@@ -23786,16 +23909,16 @@ var init_start = __esm({
|
|
|
23786
23909
|
});
|
|
23787
23910
|
|
|
23788
23911
|
// ../cli/src/lib/agent/task-chats.ts
|
|
23789
|
-
import
|
|
23790
|
-
import
|
|
23912
|
+
import fs76 from "node:fs";
|
|
23913
|
+
import path79 from "node:path";
|
|
23791
23914
|
function taskChatFile(task, home3) {
|
|
23792
23915
|
if (!TASK.test(task)) throw new Error(`${task} is not a task id.`);
|
|
23793
|
-
return
|
|
23916
|
+
return path79.join(taskChatsDir(home3), `${task}.jsonl`);
|
|
23794
23917
|
}
|
|
23795
23918
|
function readTaskChat(task, home3) {
|
|
23796
23919
|
let text5;
|
|
23797
23920
|
try {
|
|
23798
|
-
text5 =
|
|
23921
|
+
text5 = fs76.readFileSync(taskChatFile(task, home3), "utf8");
|
|
23799
23922
|
} catch {
|
|
23800
23923
|
return [];
|
|
23801
23924
|
}
|
|
@@ -23816,13 +23939,13 @@ var init_task_chats = __esm({
|
|
|
23816
23939
|
"use strict";
|
|
23817
23940
|
init_home();
|
|
23818
23941
|
TASK = /^[\w-]{1,100}$/;
|
|
23819
|
-
taskChatsDir = (home3 = machineHome()) =>
|
|
23942
|
+
taskChatsDir = (home3 = machineHome()) => path79.join(home3, "chats");
|
|
23820
23943
|
}
|
|
23821
23944
|
});
|
|
23822
23945
|
|
|
23823
23946
|
// ../cli/src/lib/agent/memory-review.ts
|
|
23824
|
-
import
|
|
23825
|
-
import
|
|
23947
|
+
import fs77 from "node:fs";
|
|
23948
|
+
import path80 from "node:path";
|
|
23826
23949
|
function taskChatsToReview(tasks) {
|
|
23827
23950
|
const out = [];
|
|
23828
23951
|
for (const ref of [...new Set(tasks)]) {
|
|
@@ -23844,8 +23967,8 @@ function readArchived2(id) {
|
|
|
23844
23967
|
const found2 = Number.isInteger(id) && id > 0 ? locateArchived(id) : null;
|
|
23845
23968
|
if (!found2) return null;
|
|
23846
23969
|
try {
|
|
23847
|
-
const file = found2.kind === "group" ?
|
|
23848
|
-
const { meta, body } = parseFrontmatter(
|
|
23970
|
+
const file = found2.kind === "group" ? path80.join(found2.target, "root.md") : found2.target;
|
|
23971
|
+
const { meta, body } = parseFrontmatter(fs77.readFileSync(file, "utf8"));
|
|
23849
23972
|
if (!meta) return null;
|
|
23850
23973
|
return { id, title: meta.title, modules: meta.modules ?? [], rejected: meta.rejected, body };
|
|
23851
23974
|
} catch {
|
|
@@ -23914,17 +24037,17 @@ var init_memory_review = __esm({
|
|
|
23914
24037
|
});
|
|
23915
24038
|
|
|
23916
24039
|
// ../cli/src/lib/agent/dismissal-review.ts
|
|
23917
|
-
import
|
|
23918
|
-
import
|
|
24040
|
+
import fs78 from "node:fs";
|
|
24041
|
+
import path81 from "node:path";
|
|
23919
24042
|
function rejectionsToReview() {
|
|
23920
|
-
const dir =
|
|
23921
|
-
if (!
|
|
24043
|
+
const dir = path81.join(ARCHIVE, REJECTIONS_TO_REVIEW);
|
|
24044
|
+
if (!fs78.existsSync(dir)) return [];
|
|
23922
24045
|
const found2 = [];
|
|
23923
24046
|
for (const file of walkMd(dir)) {
|
|
23924
24047
|
const id = archivedId(file);
|
|
23925
24048
|
if (id === null) continue;
|
|
23926
24049
|
try {
|
|
23927
|
-
const { meta } = parseFrontmatter(
|
|
24050
|
+
const { meta } = parseFrontmatter(fs78.readFileSync(file, "utf8"));
|
|
23928
24051
|
if (!meta?.rejected || meta.rejected_by !== "user" || !meta.rejected_reason.trim()) continue;
|
|
23929
24052
|
found2.push({ id, title: meta.title, file: rel(file), reason: meta.rejected_reason.trim(), rejectedAt: meta.rejected_at });
|
|
23930
24053
|
} catch {
|
|
@@ -23954,7 +24077,7 @@ function citedSources(text5) {
|
|
|
23954
24077
|
function withdrawnSources() {
|
|
23955
24078
|
const cited = [...new Set(plannerCopies("rejected.md").flatMap((file) => {
|
|
23956
24079
|
try {
|
|
23957
|
-
return citedSources(
|
|
24080
|
+
return citedSources(fs78.readFileSync(file, "utf8"));
|
|
23958
24081
|
} catch {
|
|
23959
24082
|
return [];
|
|
23960
24083
|
}
|
|
@@ -23979,8 +24102,8 @@ var init_dismissal_review = __esm({
|
|
|
23979
24102
|
});
|
|
23980
24103
|
|
|
23981
24104
|
// ../cli/src/lib/agent/flow.ts
|
|
23982
|
-
import
|
|
23983
|
-
import
|
|
24105
|
+
import fs79 from "node:fs";
|
|
24106
|
+
import path82 from "node:path";
|
|
23984
24107
|
function readCard2(id, home3 = "board") {
|
|
23985
24108
|
const found2 = home3 === "archive" ? locateArchived(id) : locate(id);
|
|
23986
24109
|
if (!found2) {
|
|
@@ -23989,10 +24112,10 @@ function readCard2(id, home3 = "board") {
|
|
|
23989
24112
|
{ kind: "card-not-found", id }
|
|
23990
24113
|
);
|
|
23991
24114
|
}
|
|
23992
|
-
const file = found2.kind === "group" ?
|
|
24115
|
+
const file = found2.kind === "group" ? path82.join(found2.target, "root.md") : found2.target;
|
|
23993
24116
|
let text5;
|
|
23994
24117
|
try {
|
|
23995
|
-
text5 =
|
|
24118
|
+
text5 = fs79.readFileSync(file, "utf8");
|
|
23996
24119
|
} catch {
|
|
23997
24120
|
die(`#${id} is on the board but ${rel(file)} can't be read.`, { kind: "card-unreadable", id });
|
|
23998
24121
|
}
|
|
@@ -24116,7 +24239,7 @@ function workspaceField(delivery) {
|
|
|
24116
24239
|
if (!delivery?.worktree) return [];
|
|
24117
24240
|
return field("workspace", [
|
|
24118
24241
|
`write code in ${delivery.worktree} \u2014 this delivery's own worktree, on branch ${delivery.branch}.`,
|
|
24119
|
-
|
|
24242
|
+
path82.resolve(process.cwd()).startsWith(worktreeDir(delivery.worktree)) ? `it is your working folder already; the board's own files are NOT in it and never go on that branch.` : `cd into it first \u2014 you are in the project checkout, and code written here is not part of this build. The board's own files are NOT in it and never go on that branch.`,
|
|
24120
24243
|
`every board command names the project's own copy: \`${boardCommandFor()} <command>\`.`,
|
|
24121
24244
|
`${rel(REPO_ROOT)} is the project \u2014 the card, the memory files and the docs are changed there, not here.`
|
|
24122
24245
|
]);
|
|
@@ -24855,7 +24978,7 @@ __export(watch_exports, {
|
|
|
24855
24978
|
watchRun: () => watchRun
|
|
24856
24979
|
});
|
|
24857
24980
|
import { spawn as spawn4 } from "node:child_process";
|
|
24858
|
-
import
|
|
24981
|
+
import fs80 from "node:fs";
|
|
24859
24982
|
async function watchRun(sessionId, resume = startResume) {
|
|
24860
24983
|
const spec = readSpec(sessionId);
|
|
24861
24984
|
const run2 = peekRun(sessionId);
|
|
@@ -24891,11 +25014,11 @@ async function watchRun(sessionId, resume = startResume) {
|
|
|
24891
25014
|
const record = claimed ?? run2;
|
|
24892
25015
|
const sub = record.action === "sub";
|
|
24893
25016
|
if (claim) await setCardStatus(claim.cardId, claim.status);
|
|
24894
|
-
|
|
24895
|
-
const logFile =
|
|
25017
|
+
fs80.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
25018
|
+
const logFile = fs80.createWriteStream(record.logPath, { flags: "a" });
|
|
24896
25019
|
logFile.on("error", () => {
|
|
24897
25020
|
});
|
|
24898
|
-
const eventsFile =
|
|
25021
|
+
const eventsFile = fs80.createWriteStream(eventsPathOf(sessionId), { flags: "a" });
|
|
24899
25022
|
eventsFile.on("error", () => {
|
|
24900
25023
|
});
|
|
24901
25024
|
const event2 = (e) => eventsFile.write(`${JSON.stringify(e)}
|
|
@@ -25483,7 +25606,7 @@ var init_watch = __esm({
|
|
|
25483
25606
|
});
|
|
25484
25607
|
|
|
25485
25608
|
// ../cli/src/kanban.ts
|
|
25486
|
-
import
|
|
25609
|
+
import fs86 from "node:fs";
|
|
25487
25610
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
25488
25611
|
|
|
25489
25612
|
// ../cli/src/lib/cli/agent.ts
|
|
@@ -25972,8 +26095,8 @@ async function testAgent(args) {
|
|
|
25972
26095
|
}
|
|
25973
26096
|
|
|
25974
26097
|
// ../cli/src/commands/cloud.ts
|
|
25975
|
-
import
|
|
25976
|
-
import
|
|
26098
|
+
import fs71 from "node:fs";
|
|
26099
|
+
import path74 from "node:path";
|
|
25977
26100
|
|
|
25978
26101
|
// ../cli/src/lib/cloud/avatar.ts
|
|
25979
26102
|
init_session();
|
|
@@ -26021,13 +26144,13 @@ init_config();
|
|
|
26021
26144
|
init_home();
|
|
26022
26145
|
init_config();
|
|
26023
26146
|
init_session();
|
|
26024
|
-
import
|
|
26025
|
-
import
|
|
26147
|
+
import fs70 from "node:fs";
|
|
26148
|
+
import path73 from "node:path";
|
|
26026
26149
|
var FRESH_MS = 10 * 6e4;
|
|
26027
26150
|
var GRACE_MS = 7 * 24 * 60 * 6e4;
|
|
26028
|
-
var heldFile = () =>
|
|
26151
|
+
var heldFile = () => path73.join(machineHome(), "pro.json");
|
|
26029
26152
|
function forgetPro() {
|
|
26030
|
-
|
|
26153
|
+
fs70.rmSync(heldFile(), { force: true });
|
|
26031
26154
|
}
|
|
26032
26155
|
|
|
26033
26156
|
// ../cli/src/lib/cloud/account.ts
|
|
@@ -26204,10 +26327,10 @@ async function cloudImage(opts, program2) {
|
|
|
26204
26327
|
const prompt = opts.prompt.trim();
|
|
26205
26328
|
if (!prompt) usage("Missing the prompt.", line);
|
|
26206
26329
|
const references = opts.refs.map((file) => {
|
|
26207
|
-
const type = REF_TYPES[
|
|
26330
|
+
const type = REF_TYPES[path74.extname(file).toLowerCase()];
|
|
26208
26331
|
if (!type) usage(`--ref ${file}: use a PNG, JPEG or WebP file.`, line);
|
|
26209
26332
|
try {
|
|
26210
|
-
return `data:${type};base64,${
|
|
26333
|
+
return `data:${type};base64,${fs71.readFileSync(file).toString("base64")}`;
|
|
26211
26334
|
} catch (e) {
|
|
26212
26335
|
usage(`--ref ${file}: ${e instanceof Error ? e.message : String(e)}`, line);
|
|
26213
26336
|
}
|
|
@@ -26313,31 +26436,31 @@ async function generate(g) {
|
|
|
26313
26436
|
refuse(g.failed, g.fallback);
|
|
26314
26437
|
}
|
|
26315
26438
|
const out = g.outFor?.(res, g.out) ?? g.out;
|
|
26316
|
-
|
|
26317
|
-
|
|
26439
|
+
fs71.mkdirSync(path74.dirname(path74.resolve(out)), { recursive: true });
|
|
26440
|
+
fs71.writeFileSync(out, Buffer.from(await res.arrayBuffer()));
|
|
26318
26441
|
say(`${out} (${g.label(res)})`);
|
|
26319
26442
|
return { out };
|
|
26320
26443
|
}
|
|
26321
26444
|
|
|
26322
26445
|
// ../cli/src/commands/cloud-import.ts
|
|
26323
26446
|
import crypto5 from "node:crypto";
|
|
26324
|
-
import
|
|
26325
|
-
import
|
|
26447
|
+
import fs73 from "node:fs";
|
|
26448
|
+
import path76 from "node:path";
|
|
26326
26449
|
|
|
26327
26450
|
// ../cloud/src/board-files.ts
|
|
26328
26451
|
var ROOTS = ["todo/", ".archive/", "memory/", "workflows/", "triage/", "assets/", "rules/"];
|
|
26329
26452
|
var SETTINGS = ["modules.md", "config.md"];
|
|
26330
|
-
var isSettingsPath = (
|
|
26453
|
+
var isSettingsPath = (path88) => SETTINGS.includes(path88) || /^rules\/(?:workflows\/)?[^/]+\.md$/.test(path88);
|
|
26331
26454
|
var BOARD_FILE_LIMIT = 256 * 1024;
|
|
26332
|
-
function isBoardPath(
|
|
26333
|
-
if (!
|
|
26334
|
-
if (
|
|
26335
|
-
if (
|
|
26336
|
-
if (
|
|
26337
|
-
if (SETTINGS.includes(
|
|
26338
|
-
if (
|
|
26339
|
-
if (
|
|
26340
|
-
return ROOTS.some((root) =>
|
|
26455
|
+
function isBoardPath(path88) {
|
|
26456
|
+
if (!path88 || path88.length > 500 || path88.startsWith("/") || path88.includes("\\") || path88.includes("\0")) return false;
|
|
26457
|
+
if (path88.split("/").some((part) => !part || part === "." || part === "..")) return false;
|
|
26458
|
+
if (path88 === "todo/README.md" || path88.split("/").includes(".sync")) return false;
|
|
26459
|
+
if (path88.startsWith("workflows/")) return true;
|
|
26460
|
+
if (SETTINGS.includes(path88)) return true;
|
|
26461
|
+
if (path88.startsWith("rules/")) return isSettingsPath(path88);
|
|
26462
|
+
if (path88.startsWith("assets/")) return path88.endsWith(".json");
|
|
26463
|
+
return ROOTS.some((root) => path88.startsWith(root)) && path88.endsWith(".md");
|
|
26341
26464
|
}
|
|
26342
26465
|
var workflowFolder = (id) => id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
26343
26466
|
|
|
@@ -26441,8 +26564,8 @@ init_bundled();
|
|
|
26441
26564
|
// ../cli/src/lib/agents/shared-memory.ts
|
|
26442
26565
|
init_bundled();
|
|
26443
26566
|
init_parse();
|
|
26444
|
-
import
|
|
26445
|
-
import
|
|
26567
|
+
import fs72 from "node:fs";
|
|
26568
|
+
import path75 from "node:path";
|
|
26446
26569
|
function sharedMemory(board2) {
|
|
26447
26570
|
const declared = /* @__PURE__ */ new Map();
|
|
26448
26571
|
const take = (folder, text5, builtIn) => {
|
|
@@ -26454,16 +26577,16 @@ function sharedMemory(board2) {
|
|
|
26454
26577
|
const at = /^([^/]+)\/AGENT\.md$/.exec(key);
|
|
26455
26578
|
if (at) take(at[1], text5, true);
|
|
26456
26579
|
}
|
|
26457
|
-
const root =
|
|
26580
|
+
const root = path75.join(board2, "agents");
|
|
26458
26581
|
let folders2 = [];
|
|
26459
26582
|
try {
|
|
26460
|
-
folders2 =
|
|
26583
|
+
folders2 = fs72.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
|
|
26461
26584
|
} catch {
|
|
26462
26585
|
}
|
|
26463
26586
|
for (const folder of folders2.sort()) {
|
|
26464
26587
|
let text5 = null;
|
|
26465
26588
|
try {
|
|
26466
|
-
text5 =
|
|
26589
|
+
text5 = fs72.readFileSync(path75.join(root, folder, "AGENT.md"), "utf8");
|
|
26467
26590
|
} catch {
|
|
26468
26591
|
}
|
|
26469
26592
|
take(folder, text5, false);
|
|
@@ -26552,24 +26675,24 @@ async function findProject(asked) {
|
|
|
26552
26675
|
}
|
|
26553
26676
|
throw new Refused(`No project of yours has the id or prefix "${asked}".`, "not_found");
|
|
26554
26677
|
}
|
|
26555
|
-
var read = (file) =>
|
|
26678
|
+
var read = (file) => fs73.readFileSync(file, "utf8");
|
|
26556
26679
|
var bytes2 = (text5) => Buffer.byteLength(text5, "utf8");
|
|
26557
26680
|
function files(dir) {
|
|
26558
|
-
if (!
|
|
26681
|
+
if (!fs73.existsSync(dir)) return [];
|
|
26559
26682
|
const out = [];
|
|
26560
|
-
for (const entry of
|
|
26683
|
+
for (const entry of fs73.readdirSync(dir, { withFileTypes: true })) {
|
|
26561
26684
|
if (entry.name.startsWith(".")) continue;
|
|
26562
|
-
const full =
|
|
26685
|
+
const full = path76.join(dir, entry.name);
|
|
26563
26686
|
if (entry.isDirectory()) out.push(...files(full));
|
|
26564
26687
|
else if (entry.isFile()) out.push(full);
|
|
26565
26688
|
}
|
|
26566
26689
|
return out;
|
|
26567
26690
|
}
|
|
26568
|
-
var posix2 = (p) => p.split(
|
|
26691
|
+
var posix2 = (p) => p.split(path76.sep).join("/");
|
|
26569
26692
|
function openCards() {
|
|
26570
26693
|
const out = [];
|
|
26571
26694
|
for (const file of walkMd(TODO)) {
|
|
26572
|
-
const rel2 = posix2(
|
|
26695
|
+
const rel2 = posix2(path76.relative(TODO, file));
|
|
26573
26696
|
if (rel2 === "README.md") continue;
|
|
26574
26697
|
const parts = rel2.split("/");
|
|
26575
26698
|
const text5 = read(file);
|
|
@@ -26603,14 +26726,14 @@ function bodyProblems(text5) {
|
|
|
26603
26726
|
return out;
|
|
26604
26727
|
}
|
|
26605
26728
|
function packWorkflow(flow) {
|
|
26606
|
-
const own =
|
|
26729
|
+
const own = path76.join(WORKFLOWS, flow.id);
|
|
26607
26730
|
const out = {};
|
|
26608
|
-
for (const file of files(own)) out[posix2(
|
|
26731
|
+
for (const file of files(own)) out[posix2(path76.relative(own, file))] = read(file);
|
|
26609
26732
|
const named2 = [...flow.previews, ...flow.land, ...flow.scheduled].map((h) => h.agent).filter((a) => !LAND_ROLES.includes(a));
|
|
26610
26733
|
for (const agent of new Set(named2)) {
|
|
26611
|
-
const dir =
|
|
26612
|
-
if (
|
|
26613
|
-
for (const file of files(dir)) out[`agents/${agent}/${posix2(
|
|
26734
|
+
const dir = path76.join(AGENTS, agent);
|
|
26735
|
+
if (fs73.existsSync(path76.join(dir, "AGENT.md"))) {
|
|
26736
|
+
for (const file of files(dir)) out[`agents/${agent}/${posix2(path76.relative(dir, file))}`] = read(file);
|
|
26614
26737
|
continue;
|
|
26615
26738
|
}
|
|
26616
26739
|
for (const [key, text5] of Object.entries(BUNDLED_AGENT_FILES)) {
|
|
@@ -26662,8 +26785,8 @@ function draftsOf(text5) {
|
|
|
26662
26785
|
const at = /^\.(assets|mockups)\/(.+)$/.exec(name);
|
|
26663
26786
|
if (!at) continue;
|
|
26664
26787
|
const base = at[1] === "assets" ? ASSETS : MOCKUPS;
|
|
26665
|
-
const file =
|
|
26666
|
-
if (file.startsWith(base +
|
|
26788
|
+
const file = path76.resolve(base, at[2]);
|
|
26789
|
+
if (file.startsWith(base + path76.sep) && fs73.statSync(file, { throwIfNoEntry: false })?.isFile()) out.set(name, file);
|
|
26667
26790
|
}
|
|
26668
26791
|
return [...out].map(([name, file]) => ({ name, file }));
|
|
26669
26792
|
}
|
|
@@ -26697,9 +26820,9 @@ async function uploadDrafts(task, drafts) {
|
|
|
26697
26820
|
const assets = {};
|
|
26698
26821
|
for (const { name, file } of drafts) {
|
|
26699
26822
|
if (!card.body.includes(name)) continue;
|
|
26700
|
-
const ext =
|
|
26823
|
+
const ext = path76.extname(file).toLowerCase();
|
|
26701
26824
|
const type = TYPES[ext] ?? "application/octet-stream";
|
|
26702
|
-
const data =
|
|
26825
|
+
const data = fs73.readFileSync(file);
|
|
26703
26826
|
const artifact = await api("POST", `/v1/tasks/${task}/artifacts`, { type: kindOf(ext), name: name.slice(0, 200) });
|
|
26704
26827
|
const upload = await api("POST", `/v1/artifacts/${artifact.id}/uploads`, { content_type: type, size: Math.max(data.length, 1) });
|
|
26705
26828
|
await api("PUT", `/v1/uploads/${upload.id}`, void 0, { bytes: data, type });
|
|
@@ -26742,10 +26865,10 @@ usage: ${program2} cloud import --project <id or prefix> [--dry-run]`, { kind: "
|
|
|
26742
26865
|
async function run(asked, dryRun) {
|
|
26743
26866
|
const project = await findProject(asked);
|
|
26744
26867
|
const cloudFlows = await all(`/v1/workflows?workspace=${encodeURIComponent(project.workspace)}`);
|
|
26745
|
-
const board2 = crypto5.createHash("sha256").update(
|
|
26868
|
+
const board2 = crypto5.createHash("sha256").update(fs73.realpathSync(KANBAN)).digest("hex").slice(0, 16);
|
|
26746
26869
|
const cards = openCards();
|
|
26747
26870
|
const problems = [];
|
|
26748
|
-
const where = (file) => posix2(
|
|
26871
|
+
const where = (file) => posix2(path76.relative(KANBAN, file));
|
|
26749
26872
|
const own = workflows().filter((w) => !w.builtIn);
|
|
26750
26873
|
const packed = /* @__PURE__ */ new Map();
|
|
26751
26874
|
for (const flow of own) {
|
|
@@ -26772,7 +26895,7 @@ async function run(asked, dryRun) {
|
|
|
26772
26895
|
const root = `projects/${project.id}/`;
|
|
26773
26896
|
const boardFiles = [];
|
|
26774
26897
|
for (const file of [...files(MEMORY), ...files(RULES), CONFIG, MODULES_MD]) {
|
|
26775
|
-
if (!
|
|
26898
|
+
if (!fs73.existsSync(file)) continue;
|
|
26776
26899
|
const rel2 = where(file);
|
|
26777
26900
|
if (!isBoardPath(rel2)) continue;
|
|
26778
26901
|
const content = read(file);
|
|
@@ -26883,16 +27006,16 @@ async function run(asked, dryRun) {
|
|
|
26883
27006
|
}
|
|
26884
27007
|
function leftLocal(own) {
|
|
26885
27008
|
const out = [];
|
|
26886
|
-
const count4 = (dir) =>
|
|
27009
|
+
const count4 = (dir) => fs73.existsSync(dir) ? walkMd(dir).length : 0;
|
|
26887
27010
|
const archived = count4(ARCHIVE);
|
|
26888
27011
|
if (archived) out.push(`.archive/ (${archived})`);
|
|
26889
27012
|
const triage = count4(TRIAGE);
|
|
26890
27013
|
if (triage) out.push(`triage/ (${triage})`);
|
|
26891
|
-
if (
|
|
26892
|
-
if (
|
|
27014
|
+
if (fs73.existsSync(DELIVERIES)) out.push("deliveries/");
|
|
27015
|
+
if (fs73.existsSync(path76.join(KANBAN, "metrics.csv"))) out.push("metrics.csv");
|
|
26893
27016
|
const named2 = new Set(own.flatMap((w) => [...w.previews, ...w.land, ...w.scheduled].map((h) => h.agent)));
|
|
26894
|
-
if (
|
|
26895
|
-
for (const e of
|
|
27017
|
+
if (fs73.existsSync(AGENTS)) {
|
|
27018
|
+
for (const e of fs73.readdirSync(AGENTS, { withFileTypes: true })) {
|
|
26896
27019
|
if (e.isDirectory() && !e.name.startsWith(".") && !named2.has(e.name)) out.push(`agents/${e.name}/`);
|
|
26897
27020
|
}
|
|
26898
27021
|
}
|
|
@@ -26910,8 +27033,8 @@ init_session();
|
|
|
26910
27033
|
init_io();
|
|
26911
27034
|
init_paths();
|
|
26912
27035
|
import { createHash as createHash5 } from "node:crypto";
|
|
26913
|
-
import
|
|
26914
|
-
import
|
|
27036
|
+
import fs74 from "node:fs";
|
|
27037
|
+
import path77 from "node:path";
|
|
26915
27038
|
var SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
26916
27039
|
var CAPTURED = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
26917
27040
|
var MAX_BYTES3 = 50 * 1024 * 1024;
|
|
@@ -27010,10 +27133,10 @@ This template has no preview.`);
|
|
|
27010
27133
|
const written = [];
|
|
27011
27134
|
for (const p of previews) {
|
|
27012
27135
|
const bytes3 = Buffer.from(await (await ask(`/${at}/previews/${route(p.path)}`, {}, WITHOUT)).arrayBuffer());
|
|
27013
|
-
const file =
|
|
27014
|
-
const out =
|
|
27015
|
-
|
|
27016
|
-
|
|
27136
|
+
const file = path77.basename(p.path);
|
|
27137
|
+
const out = path77.join(opts.to, file.startsWith(`${name}-`) ? file : `${name}-${file}`);
|
|
27138
|
+
fs74.mkdirSync(opts.to, { recursive: true });
|
|
27139
|
+
fs74.writeFileSync(out, bytes3);
|
|
27017
27140
|
written.push(out);
|
|
27018
27141
|
say(out);
|
|
27019
27142
|
}
|
|
@@ -27026,15 +27149,15 @@ async function templateGet(name, opts, program2) {
|
|
|
27026
27149
|
const { template } = await asJson(await ask(`/${at}`, {}, WITHOUT));
|
|
27027
27150
|
const fetched = [];
|
|
27028
27151
|
for (const f of template.files) {
|
|
27029
|
-
const out =
|
|
27030
|
-
if (!out.startsWith(
|
|
27152
|
+
const out = path77.resolve(to, f.path);
|
|
27153
|
+
if (!out.startsWith(path77.resolve(to) + path77.sep)) refuse2(T.corrupt, WITHOUT);
|
|
27031
27154
|
const bytes3 = Buffer.from(await (await ask(`/${at}/files/${route(f.path)}`, {}, WITHOUT)).arrayBuffer());
|
|
27032
27155
|
if (bytes3.byteLength !== f.size || createHash5("sha256").update(bytes3).digest("hex") !== f.sha256) {
|
|
27033
27156
|
refuse2(T.corrupt, WITHOUT);
|
|
27034
27157
|
}
|
|
27035
27158
|
fetched.push({ out, bytes: bytes3 });
|
|
27036
27159
|
}
|
|
27037
|
-
const taken = fetched.filter((f) =>
|
|
27160
|
+
const taken = fetched.filter((f) => fs74.existsSync(f.out)).map((f) => path77.relative(to, f.out));
|
|
27038
27161
|
if (taken.length > 0) {
|
|
27039
27162
|
refuse2({
|
|
27040
27163
|
zh: `\u76EE\u6807\u6587\u4EF6\u5939\u5DF2\u6709\u540C\u540D\u6587\u4EF6\uFF1A${taken.join("\u3001")}\u3002\u8BF7\u6362\u4E00\u4E2A\u6587\u4EF6\u5939\uFF0C\u6216\u5148\u79FB\u8D70\u8FD9\u4E9B\u6587\u4EF6\u3002`,
|
|
@@ -27042,8 +27165,8 @@ async function templateGet(name, opts, program2) {
|
|
|
27042
27165
|
});
|
|
27043
27166
|
}
|
|
27044
27167
|
for (const f of fetched) {
|
|
27045
|
-
|
|
27046
|
-
|
|
27168
|
+
fs74.mkdirSync(path77.dirname(f.out), { recursive: true });
|
|
27169
|
+
fs74.writeFileSync(f.out, f.bytes);
|
|
27047
27170
|
}
|
|
27048
27171
|
say(`\u5DF2\u53D6\u56DE ${name}\uFF1A${fetched.length} \u4E2A\u6587\u4EF6\uFF0C\u4F4D\u4E8E ${to}
|
|
27049
27172
|
Fetched ${name}: ${fetched.length} files into ${to}`);
|
|
@@ -27061,25 +27184,25 @@ async function templateSource(name, opts) {
|
|
|
27061
27184
|
say(`evidence: ${e.path}`);
|
|
27062
27185
|
continue;
|
|
27063
27186
|
}
|
|
27064
|
-
const out =
|
|
27065
|
-
|
|
27066
|
-
|
|
27187
|
+
const out = path77.join(opts.to, `${name}-${path77.basename(e.path)}`);
|
|
27188
|
+
fs74.mkdirSync(opts.to, { recursive: true });
|
|
27189
|
+
fs74.writeFileSync(out, Buffer.from(await (await ask(`/${at}/evidence/${route(e.path)}`)).arrayBuffer()));
|
|
27067
27190
|
written.push(out);
|
|
27068
27191
|
say(out);
|
|
27069
27192
|
}
|
|
27070
27193
|
return { name, source, evidence: written.length > 0 ? written : evidence.map((e) => e.path) };
|
|
27071
27194
|
}
|
|
27072
27195
|
async function templatePublish(dir, opts) {
|
|
27073
|
-
const root =
|
|
27074
|
-
const manifest =
|
|
27075
|
-
if (!
|
|
27076
|
-
const meta = readTemplateMeta(
|
|
27196
|
+
const root = path77.resolve(dir);
|
|
27197
|
+
const manifest = path77.join(root, "template.md");
|
|
27198
|
+
if (!fs74.existsSync(manifest)) invalid("\u6587\u4EF6\u5939\u9876\u5C42\u7F3A\u5C11 template.md\u3002", "template.md is missing from the folder\u2019s top level.");
|
|
27199
|
+
const meta = readTemplateMeta(fs74.readFileSync(manifest, "utf8"));
|
|
27077
27200
|
const files2 = walk2(root);
|
|
27078
|
-
const total = files2.reduce((sum, f) => sum +
|
|
27201
|
+
const total = files2.reduce((sum, f) => sum + fs74.statSync(path77.join(root, f)).size, 0);
|
|
27079
27202
|
if (total > MAX_BYTES3) invalid("\u6A21\u677F\u6587\u4EF6\u5408\u8BA1\u8D85\u8FC7 50 MB\u3002", "the template\u2019s files exceed 50 MB.");
|
|
27080
27203
|
for (const extra of [...opts.preview, ...opts.evidence]) {
|
|
27081
|
-
if (!
|
|
27082
|
-
if (
|
|
27204
|
+
if (!fs74.existsSync(extra)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${extra}\u3002`, `${extra} does not exist.`);
|
|
27205
|
+
if (path77.resolve(extra).startsWith(root + path77.sep)) {
|
|
27083
27206
|
invalid(
|
|
27084
27207
|
`${extra} \u5728\u6A21\u677F\u6587\u4EF6\u5939\u5185\uFF1B\u9884\u89C8\u56FE\u4E0E\u6765\u6E90\u51ED\u8BC1\u9700\u653E\u5728\u6A21\u677F\u6587\u4EF6\u5939\u4E4B\u5916\u3002`,
|
|
27085
27208
|
`${extra} is inside the template folder; keep previews and evidence outside it.`
|
|
@@ -27091,10 +27214,10 @@ async function templatePublish(dir, opts) {
|
|
|
27091
27214
|
}
|
|
27092
27215
|
const form = new FormData();
|
|
27093
27216
|
form.set("meta", JSON.stringify({ ...meta, replace: !!opts.replace }));
|
|
27094
|
-
const add = (kind, name, file) => form.append(`${kind}:${name}`, new Blob([
|
|
27095
|
-
for (const f of files2) add("files", f,
|
|
27096
|
-
for (const f of opts.preview) add("previews",
|
|
27097
|
-
for (const f of opts.evidence) add("evidence",
|
|
27217
|
+
const add = (kind, name, file) => form.append(`${kind}:${name}`, new Blob([fs74.readFileSync(file)]), path77.basename(name));
|
|
27218
|
+
for (const f of files2) add("files", f, path77.join(root, f));
|
|
27219
|
+
for (const f of opts.preview) add("previews", path77.basename(f), f);
|
|
27220
|
+
for (const f of opts.evidence) add("evidence", path77.basename(f), f);
|
|
27098
27221
|
const done2 = await asJson(await ask("", { method: "POST", body: form }));
|
|
27099
27222
|
say(
|
|
27100
27223
|
done2.replaced ? `\u5DF2\u66FF\u6362 ${done2.name}\uFF1A${files2.length} \u4E2A\u6587\u4EF6\u3002
|
|
@@ -27110,11 +27233,11 @@ Deleted ${name} from the catalog.`);
|
|
|
27110
27233
|
return { name, deleted: true };
|
|
27111
27234
|
}
|
|
27112
27235
|
async function templateShowcase(name, dir) {
|
|
27113
|
-
const file =
|
|
27114
|
-
if (!
|
|
27236
|
+
const file = path77.join(dir, "showcase.json");
|
|
27237
|
+
if (!fs74.existsSync(file)) invalid(`${dir} \u4E2D\u7F3A\u5C11 showcase.json\u3002`, `showcase.json is missing from ${dir}.`);
|
|
27115
27238
|
let meta;
|
|
27116
27239
|
try {
|
|
27117
|
-
meta = JSON.parse(
|
|
27240
|
+
meta = JSON.parse(fs74.readFileSync(file, "utf8"));
|
|
27118
27241
|
} catch {
|
|
27119
27242
|
return invalid("showcase.json \u4E0D\u662F\u6709\u6548\u7684 JSON\u3002", "showcase.json is not valid JSON.");
|
|
27120
27243
|
}
|
|
@@ -27126,9 +27249,9 @@ async function templateShowcase(name, dir) {
|
|
|
27126
27249
|
const form = new FormData();
|
|
27127
27250
|
form.set("meta", JSON.stringify(meta));
|
|
27128
27251
|
for (const p of new Set(named2)) {
|
|
27129
|
-
const at =
|
|
27130
|
-
if (!
|
|
27131
|
-
form.append(`media:${p}`, new Blob([
|
|
27252
|
+
const at = path77.join(dir, p);
|
|
27253
|
+
if (!fs74.existsSync(at)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${at}\u3002`, `${at} does not exist.`);
|
|
27254
|
+
form.append(`media:${p}`, new Blob([fs74.readFileSync(at)]), p);
|
|
27132
27255
|
}
|
|
27133
27256
|
const done2 = await asJson(await ask(`/${encodeURIComponent(name)}/showcase`, { method: "PUT", body: form }));
|
|
27134
27257
|
say(`\u5DF2\u66F4\u65B0 ${name} \u7684\u5C55\u793A\u8D44\u6599\uFF1A${done2.files} \u4E2A\u6587\u4EF6\u3002
|
|
@@ -27137,7 +27260,7 @@ Updated ${name}'s showcase: ${done2.files} files.`);
|
|
|
27137
27260
|
}
|
|
27138
27261
|
function walk2(root, sub = "") {
|
|
27139
27262
|
const out = [];
|
|
27140
|
-
for (const entry of
|
|
27263
|
+
for (const entry of fs74.readdirSync(path77.join(root, sub), { withFileTypes: true })) {
|
|
27141
27264
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
27142
27265
|
const rel2 = sub ? `${sub}/${entry.name}` : entry.name;
|
|
27143
27266
|
if (entry.isDirectory()) out.push(...walk2(root, rel2));
|
|
@@ -27422,7 +27545,7 @@ init_opencode();
|
|
|
27422
27545
|
init_locate();
|
|
27423
27546
|
init_store();
|
|
27424
27547
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
27425
|
-
import
|
|
27548
|
+
import path83 from "node:path";
|
|
27426
27549
|
var AGENTS2 = {
|
|
27427
27550
|
claude: "claude-code",
|
|
27428
27551
|
codex: "codex",
|
|
@@ -27437,10 +27560,10 @@ var AGENTS2 = {
|
|
|
27437
27560
|
var RUNTIMES = /* @__PURE__ */ new Set(["node", "bun", "deno"]);
|
|
27438
27561
|
function agentOf2(args) {
|
|
27439
27562
|
const words = args.trim().split(/\s+/);
|
|
27440
|
-
const first =
|
|
27563
|
+
const first = path83.basename(words[0] ?? "");
|
|
27441
27564
|
const script = RUNTIMES.has(first) ? words[1] ?? "" : words[0] ?? "";
|
|
27442
27565
|
if (script.includes("/@anthropic-ai/claude-code/")) return "claude-code";
|
|
27443
|
-
return AGENTS2[
|
|
27566
|
+
return AGENTS2[path83.basename(script).replace(/\.(m?js|cjs|exe)$/, "")];
|
|
27444
27567
|
}
|
|
27445
27568
|
function processChain() {
|
|
27446
27569
|
if (process.platform === "win32") return [];
|
|
@@ -27504,8 +27627,8 @@ init_tasks();
|
|
|
27504
27627
|
init_read();
|
|
27505
27628
|
init_rules();
|
|
27506
27629
|
init_board();
|
|
27507
|
-
import
|
|
27508
|
-
import
|
|
27630
|
+
import fs81 from "node:fs";
|
|
27631
|
+
import path84 from "node:path";
|
|
27509
27632
|
var FOLLOW_MS = 400;
|
|
27510
27633
|
async function cmdStartRun(action, args, opts, program2 = "akb") {
|
|
27511
27634
|
const { req, follow: follow2, print } = readRequest(action, args, opts);
|
|
@@ -27705,13 +27828,13 @@ function readRequest(action, args, opts) {
|
|
|
27705
27828
|
return { req, follow: follow2, print };
|
|
27706
27829
|
}
|
|
27707
27830
|
async function cmdResume(id, opts) {
|
|
27708
|
-
const opened = await startResume(id ?? "last");
|
|
27831
|
+
const opened = await startResume(id ?? "last", opts.requireSession);
|
|
27709
27832
|
if ("error" in opened) die(opened.error, { kind: "run-refused" });
|
|
27710
27833
|
const { run: run2 } = opened;
|
|
27711
27834
|
if (!opened.spawned) die(`couldn't start a process for run ${run2.sessionId}`, { kind: "spawn-failed" });
|
|
27712
27835
|
const within = run2.deliveryId ? ` in delivery ${run2.deliveryId}` : "";
|
|
27713
27836
|
say(run2.resumedFrom ? `continuing ${short(run2.resumedFrom)} \u2014 run ${run2.sessionId}${within}` : `trying again \u2014 run ${run2.sessionId}${within}`);
|
|
27714
|
-
if (opts.follow === true) return await followStarted(run2.sessionId, "akb");
|
|
27837
|
+
if (opts.follow === true) return await followStarted(run2.sessionId, "akb", opts.events === true);
|
|
27715
27838
|
return { sessionId: run2.sessionId, resumedFrom: run2.resumedFrom };
|
|
27716
27839
|
}
|
|
27717
27840
|
async function cmdCancel(named2) {
|
|
@@ -27725,7 +27848,10 @@ async function cmdCancel(named2) {
|
|
|
27725
27848
|
}
|
|
27726
27849
|
async function cmdResumeDelivery(named2) {
|
|
27727
27850
|
const res = await resumeBoardDelivery(named2);
|
|
27728
|
-
if (!res.ok) die(res.error ?? "that delivery could not be carried on", {
|
|
27851
|
+
if (!res.ok) die(res.error ?? "that delivery could not be carried on", {
|
|
27852
|
+
kind: "run-refused",
|
|
27853
|
+
...res.kind === "refused" ? { reason: res.reason, args: res.args } : {}
|
|
27854
|
+
});
|
|
27729
27855
|
if (res.landed) {
|
|
27730
27856
|
say(`delivery ${res.deliveryId} is done \u2014 its changes were already on the target branch.`);
|
|
27731
27857
|
return { deliveryId: res.deliveryId, landed: true };
|
|
@@ -27802,7 +27928,7 @@ async function cmdSubStart(words, opts) {
|
|
|
27802
27928
|
let prompt = words.join(" ");
|
|
27803
27929
|
if (opts.file) {
|
|
27804
27930
|
try {
|
|
27805
|
-
prompt =
|
|
27931
|
+
prompt = fs81.readFileSync(path84.resolve(opts.file), "utf8");
|
|
27806
27932
|
} catch {
|
|
27807
27933
|
die(`--file: cannot read ${opts.file}`, { kind: "run-refused" });
|
|
27808
27934
|
}
|
|
@@ -27916,7 +28042,7 @@ async function followRun(sessionId, already = "", program2 = "akb", events = fal
|
|
|
27916
28042
|
last = await followOne(current, printed, events);
|
|
27917
28043
|
seen.add(current.sessionId);
|
|
27918
28044
|
printed = 0;
|
|
27919
|
-
const deliveryId2 =
|
|
28045
|
+
const deliveryId2 = first.deliveryId ?? last.deliveryId;
|
|
27920
28046
|
const next = nextInFlow(first, current.sessionId, seen) ?? (deliveryId2 ? await moveDelivery(deliveryId2, idle) : void 0);
|
|
27921
28047
|
if (next === void 0) break;
|
|
27922
28048
|
if (next === null) {
|
|
@@ -27937,7 +28063,7 @@ async function followRun(sessionId, already = "", program2 = "akb", events = fal
|
|
|
27937
28063
|
}
|
|
27938
28064
|
say("");
|
|
27939
28065
|
say(runLine(last, program2));
|
|
27940
|
-
const deliveryId =
|
|
28066
|
+
const deliveryId = first.deliveryId ?? last.deliveryId;
|
|
27941
28067
|
const delivery = deliveryId ? deliveryOutcome(deliveryId) : void 0;
|
|
27942
28068
|
if (delivery) say(`delivery ${delivery.id}: ${delivery.line}`);
|
|
27943
28069
|
const failed = last.status !== "done" ? `run ${short(last.sessionId)} ended ${last.status}${last.error ? `: ${last.error}` : ""}` : delivery && delivery.status !== "finished" ? `delivery ${delivery.id} did not land${idle.timedOut ? ` \u2014 nothing moved it for ${ago(follow.idleMs)}` : ""}: ${delivery.line}` : void 0;
|
|
@@ -28003,7 +28129,7 @@ async function followOne(view, already, events) {
|
|
|
28003
28129
|
}
|
|
28004
28130
|
function nextInFlow(first, after2, seen) {
|
|
28005
28131
|
const runs = readRuns();
|
|
28006
|
-
const next = runs.filter((r) => !seen.has(r.sessionId) && r.action !== "sub" && r.startedAt >= first.startedAt).filter((r) => r.resumedFrom === after2 || !!first.flowId && r.flowId === first.flowId).sort((a, b) => a.startedAt - b.startedAt)[0];
|
|
28132
|
+
const next = runs.filter((r) => !seen.has(r.sessionId) && r.action !== "sub" && r.startedAt >= first.startedAt).filter((r) => !first.deliveryId || !r.deliveryId || r.deliveryId === first.deliveryId).filter((r) => r.resumedFrom === after2 || !!first.flowId && r.flowId === first.flowId).sort((a, b) => a.startedAt - b.startedAt)[0];
|
|
28007
28133
|
return next ? { ...next } : void 0;
|
|
28008
28134
|
}
|
|
28009
28135
|
async function moveDelivery(deliveryId, idle) {
|
|
@@ -28023,7 +28149,7 @@ async function moveDelivery(deliveryId, idle) {
|
|
|
28023
28149
|
const ask2 = await advanceLanding();
|
|
28024
28150
|
if (!ask2) return null;
|
|
28025
28151
|
const started = await startRun(ask2);
|
|
28026
|
-
return "error" in started ? null : { ...started.run };
|
|
28152
|
+
return "error" in started || started.run.deliveryId !== deliveryId ? null : { ...started.run };
|
|
28027
28153
|
}
|
|
28028
28154
|
function deliveryOutcome(deliveryId) {
|
|
28029
28155
|
const delivery = findDelivery(deliveryId);
|
|
@@ -28093,7 +28219,7 @@ async function cmdWatch(sessionId) {
|
|
|
28093
28219
|
// ../cli/src/commands/triage.ts
|
|
28094
28220
|
init_scheduled();
|
|
28095
28221
|
init_workflows();
|
|
28096
|
-
import
|
|
28222
|
+
import fs83 from "node:fs";
|
|
28097
28223
|
|
|
28098
28224
|
// ../cli/src/lib/signals/index.ts
|
|
28099
28225
|
init_cards();
|
|
@@ -28103,16 +28229,16 @@ init_yaml();
|
|
|
28103
28229
|
// ../cli/src/lib/signals/config.ts
|
|
28104
28230
|
init_settings2();
|
|
28105
28231
|
init_paths();
|
|
28106
|
-
import
|
|
28107
|
-
import
|
|
28232
|
+
import fs82 from "node:fs";
|
|
28233
|
+
import path85 from "node:path";
|
|
28108
28234
|
var TOKEN_KEY = "TRIAGE_ENDPOINT_TOKEN";
|
|
28109
28235
|
var ENDPOINT_SETTING = "Triage endpoint";
|
|
28110
28236
|
var LINE2 = /^- \*\*Triage endpoint\*\*\s*[—-]\s*(.+)$/m;
|
|
28111
|
-
var boardRel3 = (file) => rel(file).split(
|
|
28237
|
+
var boardRel3 = (file) => rel(file).split(path85.sep).join("/");
|
|
28112
28238
|
function signalEndpoint() {
|
|
28113
28239
|
let text5;
|
|
28114
28240
|
try {
|
|
28115
|
-
text5 =
|
|
28241
|
+
text5 = fs82.readFileSync(CONFIG, "utf8");
|
|
28116
28242
|
} catch {
|
|
28117
28243
|
return "";
|
|
28118
28244
|
}
|
|
@@ -28288,7 +28414,7 @@ async function cmdTriageAdd(opts) {
|
|
|
28288
28414
|
let body = opts.text;
|
|
28289
28415
|
if (opts.file !== void 0) {
|
|
28290
28416
|
try {
|
|
28291
|
-
body =
|
|
28417
|
+
body = fs83.readFileSync(opts.file, "utf8");
|
|
28292
28418
|
} catch {
|
|
28293
28419
|
die(`can't read ${opts.file} \u2014 write the item to a file, then pass its path`, { kind: "needs-input" });
|
|
28294
28420
|
}
|
|
@@ -28720,7 +28846,7 @@ function declareRunning(run2, cli) {
|
|
|
28720
28846
|
});
|
|
28721
28847
|
verb("resume").argument("[id]", RUN_ID, String).summary("continue a run that failed, was cut off or was stopped").description(
|
|
28722
28848
|
"The one command that reaches past the board: it picks the coding agent's own session back up \u2014 the conversation it was having, with everything it had already read \u2014 while the board counts the work as a fresh run. Before resuming a creation with no card, check sign-in, project access and connection. A refusal starts no run and leaves triage unchanged. Keep the triage source and request identity; reuse any task already created in Cloud. Repairing an existing card creates no other task. Example: akb run resume abc12345"
|
|
28723
|
-
).option("-f, --follow", "watch its log instead of returning").action(async function(...vals) {
|
|
28849
|
+
).option("-f, --follow", "watch its log instead of returning").option("--events", "with --follow: print its structured events, one JSON object per line").option("--require-session", "refuse if the original conversation cannot be resumed").action(async function(...vals) {
|
|
28724
28850
|
const [id] = positional(vals);
|
|
28725
28851
|
await onBoard2(this, cli, () => cmdResume(id, this.opts()));
|
|
28726
28852
|
});
|
|
@@ -28852,8 +28978,8 @@ function declareAgent(program2, cli) {
|
|
|
28852
28978
|
}
|
|
28853
28979
|
|
|
28854
28980
|
// ../cli/src/lib/cli/setup.ts
|
|
28855
|
-
import
|
|
28856
|
-
import
|
|
28981
|
+
import fs85 from "node:fs";
|
|
28982
|
+
import path87 from "node:path";
|
|
28857
28983
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
28858
28984
|
|
|
28859
28985
|
// ../cli/src/commands/update.ts
|
|
@@ -28867,8 +28993,8 @@ init_paths();
|
|
|
28867
28993
|
init_memory();
|
|
28868
28994
|
init_yaml2();
|
|
28869
28995
|
init_version();
|
|
28870
|
-
import
|
|
28871
|
-
import
|
|
28996
|
+
import fs84 from "node:fs";
|
|
28997
|
+
import path86 from "node:path";
|
|
28872
28998
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
28873
28999
|
var NAME = "wildcrane";
|
|
28874
29000
|
var REPO = "https://github.com/ai4kanban/ai4kanban";
|
|
@@ -28890,10 +29016,10 @@ var Report = class {
|
|
|
28890
29016
|
for (const line of this.notes) say(` ! ${line}`);
|
|
28891
29017
|
}
|
|
28892
29018
|
};
|
|
28893
|
-
var read3 = (file) =>
|
|
29019
|
+
var read3 = (file) => fs84.existsSync(file) ? fs84.readFileSync(file, "utf8") : null;
|
|
28894
29020
|
function statOf(p) {
|
|
28895
29021
|
try {
|
|
28896
|
-
return
|
|
29022
|
+
return fs84.lstatSync(p);
|
|
28897
29023
|
} catch {
|
|
28898
29024
|
return null;
|
|
28899
29025
|
}
|
|
@@ -28918,10 +29044,10 @@ async function cmdUpdate2(ctx) {
|
|
|
28918
29044
|
return { updated: ctx.dir, from, to: SKILL_VERSION };
|
|
28919
29045
|
}
|
|
28920
29046
|
async function repairBoard(root, report3, named2) {
|
|
28921
|
-
const legacy2 =
|
|
28922
|
-
const kept2 =
|
|
28923
|
-
const board2 = named2 ?? (
|
|
28924
|
-
if (!
|
|
29047
|
+
const legacy2 = path86.join(root, "docs", "kanban");
|
|
29048
|
+
const kept2 = path86.join(root, ".akb", "kanban");
|
|
29049
|
+
const board2 = named2 ?? (fs84.existsSync(legacy2) ? legacy2 : kept2);
|
|
29050
|
+
if (!fs84.existsSync(board2)) {
|
|
28925
29051
|
report3.notes.push("\u672A\u627E\u5230\u770B\u677F\uFF0C\u8BF7\u901A\u8FC7\u63D2\u4EF6\u8FDE\u63A5\u4E91\u7AEF\u9879\u76EE\u3002");
|
|
28926
29052
|
return false;
|
|
28927
29053
|
}
|
|
@@ -28952,20 +29078,20 @@ function moveRuntimes(root, board2, report3) {
|
|
|
28952
29078
|
}
|
|
28953
29079
|
}
|
|
28954
29080
|
function moveLegacyMemory(board2, report3) {
|
|
28955
|
-
const memory =
|
|
29081
|
+
const memory = path86.join(board2, "memory");
|
|
28956
29082
|
const moved = [];
|
|
28957
29083
|
for (const name of MEMORY_FILES) {
|
|
28958
|
-
const from =
|
|
29084
|
+
const from = path86.join(board2, name);
|
|
28959
29085
|
const st = statOf(from);
|
|
28960
29086
|
if (!st || !st.isFile()) continue;
|
|
28961
|
-
|
|
28962
|
-
const to =
|
|
29087
|
+
fs84.mkdirSync(memory, { recursive: true });
|
|
29088
|
+
const to = path86.join(memory, name);
|
|
28963
29089
|
const there = read3(to);
|
|
28964
29090
|
if (there === null) {
|
|
28965
|
-
|
|
29091
|
+
fs84.renameSync(from, to);
|
|
28966
29092
|
moved.push(name);
|
|
28967
29093
|
} else if (there === read3(from)) {
|
|
28968
|
-
|
|
29094
|
+
fs84.rmSync(from);
|
|
28969
29095
|
moved.push(name);
|
|
28970
29096
|
} else {
|
|
28971
29097
|
report3.notes.push(`both docs/kanban/${name} and docs/kanban/memory/${name} exist and differ \u2014 merge them by hand`);
|
|
@@ -28974,23 +29100,23 @@ function moveLegacyMemory(board2, report3) {
|
|
|
28974
29100
|
if (moved.length) report3.did.push(`moved the memory set into docs/kanban/memory/: ${moved.join(", ")}`);
|
|
28975
29101
|
}
|
|
28976
29102
|
function dropRecordFile(board2) {
|
|
28977
|
-
const file =
|
|
28978
|
-
if (!
|
|
28979
|
-
|
|
29103
|
+
const file = path86.join(board2, "record.csv");
|
|
29104
|
+
if (!fs84.existsSync(file)) return null;
|
|
29105
|
+
fs84.rmSync(file);
|
|
28980
29106
|
return "removed docs/kanban/record.csv (the planning scores it fed are gone)";
|
|
28981
29107
|
}
|
|
28982
29108
|
function dropModuleGoals(board2, report3) {
|
|
28983
|
-
const memory =
|
|
28984
|
-
if (!
|
|
28985
|
-
const root = read3(
|
|
29109
|
+
const memory = path86.join(board2, "memory");
|
|
29110
|
+
if (!fs84.existsSync(memory)) return;
|
|
29111
|
+
const root = read3(path86.join(memory, "goal.md"));
|
|
28986
29112
|
let dropped = 0;
|
|
28987
|
-
for (const entry of
|
|
29113
|
+
for (const entry of fs84.readdirSync(memory, { withFileTypes: true })) {
|
|
28988
29114
|
if (!entry.isDirectory()) continue;
|
|
28989
|
-
const file =
|
|
29115
|
+
const file = path86.join(memory, entry.name, "goal.md");
|
|
28990
29116
|
const text5 = read3(file);
|
|
28991
29117
|
if (text5 === null) continue;
|
|
28992
29118
|
if (text5 === root || text5.includes(UNFILLED) || !goalWords(text5)) {
|
|
28993
|
-
|
|
29119
|
+
fs84.rmSync(file);
|
|
28994
29120
|
dropped++;
|
|
28995
29121
|
} else {
|
|
28996
29122
|
report3.notes.push(
|
|
@@ -29012,10 +29138,10 @@ var PLAN_LEADS = {
|
|
|
29012
29138
|
var GONE_LEADS = ["software-planner", "planner"];
|
|
29013
29139
|
function planLeads(board2) {
|
|
29014
29140
|
const leads = { ...PLAN_LEADS };
|
|
29015
|
-
const root =
|
|
29016
|
-
for (const entry of
|
|
29141
|
+
const root = path86.join(board2, "workflows");
|
|
29142
|
+
for (const entry of fs84.existsSync(root) ? fs84.readdirSync(root, { withFileTypes: true }) : []) {
|
|
29017
29143
|
if (!entry.isDirectory()) continue;
|
|
29018
|
-
const meta = splitFrontmatter(read3(
|
|
29144
|
+
const meta = splitFrontmatter(read3(path86.join(root, entry.name, "WORKFLOW.md")) ?? "").meta;
|
|
29019
29145
|
const stages = meta === null ? void 0 : parseYamlBlock(meta).stages;
|
|
29020
29146
|
const plan = stages && typeof stages === "object" && !Array.isArray(stages) ? stages.plan : void 0;
|
|
29021
29147
|
const lead = plan && typeof plan === "object" && !Array.isArray(plan) && typeof plan.lead === "string" ? plan.lead.trim() : "";
|
|
@@ -29024,30 +29150,30 @@ function planLeads(board2) {
|
|
|
29024
29150
|
return leads;
|
|
29025
29151
|
}
|
|
29026
29152
|
function movePlanRules(board2, report3) {
|
|
29027
|
-
const rules =
|
|
29153
|
+
const rules = path86.join(board2, "rules");
|
|
29028
29154
|
const moved = [];
|
|
29029
29155
|
for (const [id, leads] of Object.entries(planLeads(board2))) {
|
|
29030
29156
|
for (const lead of leads) {
|
|
29031
|
-
const from =
|
|
29157
|
+
const from = path86.join(rules, `${lead}.md`);
|
|
29032
29158
|
const rule = read3(from)?.trim();
|
|
29033
29159
|
if (!rule) continue;
|
|
29034
|
-
const to =
|
|
29160
|
+
const to = path86.join(rules, "workflows", `${id}.md`);
|
|
29035
29161
|
const there = read3(to)?.trim() ?? "";
|
|
29036
29162
|
if (!there.includes(rule)) {
|
|
29037
|
-
|
|
29038
|
-
|
|
29163
|
+
fs84.mkdirSync(path86.dirname(to), { recursive: true });
|
|
29164
|
+
fs84.writeFileSync(to, `${[there, rule].filter(Boolean).join("\n\n")}
|
|
29039
29165
|
`);
|
|
29040
29166
|
moved.push(`${lead} \u2192 ${id}`);
|
|
29041
29167
|
}
|
|
29042
29168
|
}
|
|
29043
29169
|
}
|
|
29044
|
-
for (const lead of GONE_LEADS)
|
|
29170
|
+
for (const lead of GONE_LEADS) fs84.rmSync(path86.join(rules, `${lead}.md`), { force: true });
|
|
29045
29171
|
if (moved.length) report3.did.push(`made each plan agent's rule its workflow's card rule (docs/kanban/rules/workflows/): ${moved.join(", ")}`);
|
|
29046
29172
|
}
|
|
29047
29173
|
function noteOldWorkflows(board2, report3) {
|
|
29048
29174
|
const old = (dir, file, keys) => {
|
|
29049
|
-
const root =
|
|
29050
|
-
return (
|
|
29175
|
+
const root = path86.join(board2, dir);
|
|
29176
|
+
return (fs84.existsSync(root) ? fs84.readdirSync(root, { withFileTypes: true }) : []).filter((e) => e.isDirectory() && keys.test(splitFrontmatter(read3(path86.join(root, e.name, file)) ?? "").meta ?? "")).map((e) => `docs/kanban/${dir}/${e.name}/${file}`);
|
|
29051
29177
|
};
|
|
29052
29178
|
const files2 = [...old("workflows", "WORKFLOW.md", /^(stages|delivers):/m), ...old("agents", "AGENT.md", /^\s+(hook|lead):/m)];
|
|
29053
29179
|
if (files2.length) {
|
|
@@ -29057,7 +29183,7 @@ function noteOldWorkflows(board2, report3) {
|
|
|
29057
29183
|
}
|
|
29058
29184
|
}
|
|
29059
29185
|
function checkConfig(board2, report3) {
|
|
29060
|
-
const current = read3(
|
|
29186
|
+
const current = read3(path86.join(board2, "config.md"));
|
|
29061
29187
|
if (!current) return;
|
|
29062
29188
|
const added = missingConfigKeys(current);
|
|
29063
29189
|
if (added.length) {
|
|
@@ -29067,7 +29193,7 @@ function checkConfig(board2, report3) {
|
|
|
29067
29193
|
}
|
|
29068
29194
|
}
|
|
29069
29195
|
function checkModules(board2, report3) {
|
|
29070
|
-
const map2 = read3(
|
|
29196
|
+
const map2 = read3(path86.join(board2, "modules.md"));
|
|
29071
29197
|
if (map2 && map2.includes(UNFILLED)) {
|
|
29072
29198
|
report3.notes.push(
|
|
29073
29199
|
"docs/kanban/modules.md is still blank \u2014 write it from the repo (`akb guide module-map`)"
|
|
@@ -29121,14 +29247,14 @@ function declareSetup(program2, cli) {
|
|
|
29121
29247
|
program2.version(SKILL_VERSION, "-v, --version", "print this version").addHelpText("beforeAll", spelled(cli.program));
|
|
29122
29248
|
const dirOption = (cmd) => cmd.option("--dir <path>", "the project to work on (default: the current folder)").option("--json", "answer as one JSON object instead of prose");
|
|
29123
29249
|
const where = (cmd) => {
|
|
29124
|
-
const dir =
|
|
29125
|
-
if (!
|
|
29250
|
+
const dir = path87.resolve(String(cmd.optsWithGlobals().dir ?? cli.cwd));
|
|
29251
|
+
if (!fs85.existsSync(dir)) throw new BoardError(`no such folder: ${dir}`, { kind: "no-such-folder", dir });
|
|
29126
29252
|
return dir;
|
|
29127
29253
|
};
|
|
29128
29254
|
dirOption(program2.command("update")).summary("\u4FEE\u590D\u5DF2\u6709\u770B\u677F\u5E76\u68C0\u67E5 CLI \u65B0\u7248\u672C").description("\u4FEE\u590D\u5DF2\u6709\u770B\u677F\u5E76\u63D0\u793A CLI \u5347\u7EA7\u547D\u4EE4\u3002\u6CA1\u6709\u770B\u677F\u65F6\uFF0C\u8BF7\u901A\u8FC7\u63D2\u4EF6\u8FDE\u63A5\u4E91\u7AEF\u9879\u76EE\u3002").action(async function() {
|
|
29129
29255
|
const dir = where(this);
|
|
29130
29256
|
const named2 = this.optsWithGlobals().board ?? process.env.AI4KANBAN_BOARD?.trim();
|
|
29131
|
-
const board2 = named2 ?
|
|
29257
|
+
const board2 = named2 ? path87.resolve(cli.cwd, named2) : null;
|
|
29132
29258
|
await runAction(ctxOf(this, cli.program), {}, () => cmdUpdate2({ dir, program: cli.program, board: board2 }));
|
|
29133
29259
|
});
|
|
29134
29260
|
program2.command("__rules", { hidden: true }).action(function() {
|
|
@@ -29172,7 +29298,7 @@ function invokedDirectly() {
|
|
|
29172
29298
|
const entry = process.argv[1];
|
|
29173
29299
|
if (!entry) return false;
|
|
29174
29300
|
try {
|
|
29175
|
-
return
|
|
29301
|
+
return fs86.realpathSync(entry) === fs86.realpathSync(SELF2);
|
|
29176
29302
|
} catch {
|
|
29177
29303
|
return false;
|
|
29178
29304
|
}
|