wildcrane-runner 0.6.13 → 0.6.15
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 +1256 -1135
- package/dist/akb/package.json +1 -1
- package/dist/launcher.mjs +2 -2
- package/dist/runner.mjs +33 -9
- 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.12 — built by runner/scripts/build.mjs. Do not edit.
|
|
3
3
|
import { createRequire as __ai4kanbanRequire } from 'node:module'
|
|
4
4
|
const require = __ai4kanbanRequire(import.meta.url)
|
|
5
5
|
var __create = Object.create;
|
|
@@ -1167,8 +1167,8 @@ var require_command = __commonJS({
|
|
|
1167
1167
|
"../cli/node_modules/commander/lib/command.js"(exports) {
|
|
1168
1168
|
var EventEmitter = __require("node:events").EventEmitter;
|
|
1169
1169
|
var childProcess = __require("node:child_process");
|
|
1170
|
-
var
|
|
1171
|
-
var
|
|
1170
|
+
var path87 = __require("node:path");
|
|
1171
|
+
var fs87 = __require("node:fs");
|
|
1172
1172
|
var process2 = __require("node:process");
|
|
1173
1173
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
1174
1174
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -2149,7 +2149,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2149
2149
|
* @param {string} subcommandName
|
|
2150
2150
|
*/
|
|
2151
2151
|
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
|
|
2152
|
-
if (
|
|
2152
|
+
if (fs87.existsSync(executableFile)) return;
|
|
2153
2153
|
const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
|
|
2154
2154
|
const executableMissing = `'${executableFile}' does not exist
|
|
2155
2155
|
- if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
@@ -2167,11 +2167,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2167
2167
|
let launchWithNode = false;
|
|
2168
2168
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
2169
2169
|
function findFile(baseDir, baseName) {
|
|
2170
|
-
const localBin =
|
|
2171
|
-
if (
|
|
2172
|
-
if (sourceExt.includes(
|
|
2170
|
+
const localBin = path87.resolve(baseDir, baseName);
|
|
2171
|
+
if (fs87.existsSync(localBin)) return localBin;
|
|
2172
|
+
if (sourceExt.includes(path87.extname(baseName))) return void 0;
|
|
2173
2173
|
const foundExt = sourceExt.find(
|
|
2174
|
-
(ext) =>
|
|
2174
|
+
(ext) => fs87.existsSync(`${localBin}${ext}`)
|
|
2175
2175
|
);
|
|
2176
2176
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
2177
2177
|
return void 0;
|
|
@@ -2183,21 +2183,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2183
2183
|
if (this._scriptPath) {
|
|
2184
2184
|
let resolvedScriptPath;
|
|
2185
2185
|
try {
|
|
2186
|
-
resolvedScriptPath =
|
|
2186
|
+
resolvedScriptPath = fs87.realpathSync(this._scriptPath);
|
|
2187
2187
|
} catch {
|
|
2188
2188
|
resolvedScriptPath = this._scriptPath;
|
|
2189
2189
|
}
|
|
2190
|
-
executableDir =
|
|
2191
|
-
|
|
2190
|
+
executableDir = path87.resolve(
|
|
2191
|
+
path87.dirname(resolvedScriptPath),
|
|
2192
2192
|
executableDir
|
|
2193
2193
|
);
|
|
2194
2194
|
}
|
|
2195
2195
|
if (executableDir) {
|
|
2196
2196
|
let localFile = findFile(executableDir, executableFile);
|
|
2197
2197
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
2198
|
-
const legacyName =
|
|
2198
|
+
const legacyName = path87.basename(
|
|
2199
2199
|
this._scriptPath,
|
|
2200
|
-
|
|
2200
|
+
path87.extname(this._scriptPath)
|
|
2201
2201
|
);
|
|
2202
2202
|
if (legacyName !== this._name) {
|
|
2203
2203
|
localFile = findFile(
|
|
@@ -2208,7 +2208,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2208
2208
|
}
|
|
2209
2209
|
executableFile = localFile || executableFile;
|
|
2210
2210
|
}
|
|
2211
|
-
launchWithNode = sourceExt.includes(
|
|
2211
|
+
launchWithNode = sourceExt.includes(path87.extname(executableFile));
|
|
2212
2212
|
let proc;
|
|
2213
2213
|
if (process2.platform !== "win32") {
|
|
2214
2214
|
if (launchWithNode) {
|
|
@@ -3055,7 +3055,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3055
3055
|
* @return {Command}
|
|
3056
3056
|
*/
|
|
3057
3057
|
nameFromFilename(filename) {
|
|
3058
|
-
this._name =
|
|
3058
|
+
this._name = path87.basename(filename, path87.extname(filename));
|
|
3059
3059
|
return this;
|
|
3060
3060
|
}
|
|
3061
3061
|
/**
|
|
@@ -3069,9 +3069,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3069
3069
|
* @param {string} [path]
|
|
3070
3070
|
* @return {(string|null|Command)}
|
|
3071
3071
|
*/
|
|
3072
|
-
executableDir(
|
|
3073
|
-
if (
|
|
3074
|
-
this._executableDir =
|
|
3072
|
+
executableDir(path88) {
|
|
3073
|
+
if (path88 === void 0) return this._executableDir;
|
|
3074
|
+
this._executableDir = path88;
|
|
3075
3075
|
return this;
|
|
3076
3076
|
}
|
|
3077
3077
|
/**
|
|
@@ -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
|
}
|
|
@@ -4057,42 +3871,42 @@ async function takeLock() {
|
|
|
4057
3871
|
const until = Date.now() + LOCK_WAIT_MS;
|
|
4058
3872
|
for (; ; ) {
|
|
4059
3873
|
try {
|
|
4060
|
-
|
|
4061
|
-
|
|
3874
|
+
fs4.mkdirSync(machineHome(), { recursive: true, mode: 448 });
|
|
3875
|
+
fs4.mkdirSync(lockDir());
|
|
4062
3876
|
try {
|
|
4063
|
-
|
|
3877
|
+
fs4.writeFileSync(lockOwner(), `${process.pid}
|
|
4064
3878
|
`);
|
|
4065
3879
|
} catch {
|
|
4066
3880
|
}
|
|
4067
3881
|
return () => {
|
|
4068
3882
|
try {
|
|
4069
|
-
|
|
3883
|
+
fs4.rmSync(lockDir(), { recursive: true, force: true });
|
|
4070
3884
|
} catch {
|
|
4071
3885
|
}
|
|
4072
3886
|
};
|
|
4073
3887
|
} catch (err) {
|
|
4074
3888
|
if (err.code !== "EEXIST") throw err;
|
|
4075
3889
|
if (stale()) {
|
|
4076
|
-
|
|
3890
|
+
fs4.rmSync(lockDir(), { recursive: true, force: true });
|
|
4077
3891
|
continue;
|
|
4078
3892
|
}
|
|
4079
3893
|
if (Date.now() > until) return () => {
|
|
4080
3894
|
};
|
|
4081
|
-
await
|
|
3895
|
+
await sleep(LOCK_RETRY_MS);
|
|
4082
3896
|
}
|
|
4083
3897
|
}
|
|
4084
3898
|
}
|
|
4085
3899
|
function stale() {
|
|
4086
3900
|
let held4;
|
|
4087
3901
|
try {
|
|
4088
|
-
const pid = Number(
|
|
3902
|
+
const pid = Number(fs4.readFileSync(lockOwner(), "utf8").trim());
|
|
4089
3903
|
held4 = Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
4090
3904
|
} catch {
|
|
4091
3905
|
held4 = void 0;
|
|
4092
3906
|
}
|
|
4093
3907
|
if (held4 !== void 0 && !alive(held4)) return true;
|
|
4094
3908
|
try {
|
|
4095
|
-
return Date.now() -
|
|
3909
|
+
return Date.now() - fs4.statSync(lockDir()).mtimeMs > LOCK_STALE_MS;
|
|
4096
3910
|
} catch {
|
|
4097
3911
|
return false;
|
|
4098
3912
|
}
|
|
@@ -4105,7 +3919,7 @@ function alive(pid) {
|
|
|
4105
3919
|
return e.code === "EPERM";
|
|
4106
3920
|
}
|
|
4107
3921
|
}
|
|
4108
|
-
var REFRESH_MARGIN_MS, LOCK_STALE_MS, LOCK_WAIT_MS, LOCK_RETRY_MS, sessionFile, lockDir, lockOwner, expiringSoon,
|
|
3922
|
+
var REFRESH_MARGIN_MS, LOCK_STALE_MS, LOCK_WAIT_MS, LOCK_RETRY_MS, sessionFile, lockDir, lockOwner, expiringSoon, sleep;
|
|
4109
3923
|
var init_session = __esm({
|
|
4110
3924
|
"../cli/src/lib/cloud/session.ts"() {
|
|
4111
3925
|
"use strict";
|
|
@@ -4115,11 +3929,234 @@ var init_session = __esm({
|
|
|
4115
3929
|
LOCK_STALE_MS = 3e4;
|
|
4116
3930
|
LOCK_WAIT_MS = 2e4;
|
|
4117
3931
|
LOCK_RETRY_MS = 50;
|
|
4118
|
-
sessionFile = () =>
|
|
4119
|
-
lockDir = () =>
|
|
4120
|
-
lockOwner = () =>
|
|
3932
|
+
sessionFile = () => path5.join(machineHome(), "session.json");
|
|
3933
|
+
lockDir = () => path5.join(machineHome(), "session.lock");
|
|
3934
|
+
lockOwner = () => path5.join(lockDir(), "owner");
|
|
4121
3935
|
expiringSoon = (session) => session.expiresAt - REFRESH_MARGIN_MS <= Date.now();
|
|
4122
|
-
|
|
3936
|
+
sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
3937
|
+
}
|
|
3938
|
+
});
|
|
3939
|
+
|
|
3940
|
+
// ../cli/src/lib/cloud/execution.ts
|
|
3941
|
+
function executionOf(value = process.env.AI4KANBAN_EXECUTION) {
|
|
3942
|
+
try {
|
|
3943
|
+
const held4 = typeof value === "string" ? JSON.parse(value) : value;
|
|
3944
|
+
if (held4 && UUID.test(held4.job) && UUID.test(held4.machine)) return { job: held4.job, machine: held4.machine };
|
|
3945
|
+
} catch {
|
|
3946
|
+
}
|
|
3947
|
+
return void 0;
|
|
3948
|
+
}
|
|
3949
|
+
async function executionHeld(execution) {
|
|
3950
|
+
if (!execution) return true;
|
|
3951
|
+
try {
|
|
3952
|
+
const auth = await accessToken();
|
|
3953
|
+
if (!auth.ok) return false;
|
|
3954
|
+
const response = await fetch(`${cloudEndpoints().api}/v1/runner/jobs/${execution.job}/renew`, {
|
|
3955
|
+
method: "POST",
|
|
3956
|
+
headers: { authorization: `Bearer ${auth.token}`, "content-type": "application/json" },
|
|
3957
|
+
body: JSON.stringify({ machineId: execution.machine }),
|
|
3958
|
+
signal: AbortSignal.timeout(2e4)
|
|
3959
|
+
});
|
|
3960
|
+
if (!response.ok) return false;
|
|
3961
|
+
const held4 = await response.json();
|
|
3962
|
+
return held4.renewed === true && held4.stop !== true;
|
|
3963
|
+
} catch {
|
|
3964
|
+
return false;
|
|
3965
|
+
}
|
|
3966
|
+
}
|
|
3967
|
+
var UUID;
|
|
3968
|
+
var init_execution = __esm({
|
|
3969
|
+
"../cli/src/lib/cloud/execution.ts"() {
|
|
3970
|
+
"use strict";
|
|
3971
|
+
init_config();
|
|
3972
|
+
init_session();
|
|
3973
|
+
UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3974
|
+
}
|
|
3975
|
+
});
|
|
3976
|
+
|
|
3977
|
+
// ../cli/src/lib/lock.ts
|
|
3978
|
+
import { randomUUID } from "node:crypto";
|
|
3979
|
+
import fs5 from "node:fs";
|
|
3980
|
+
import path6 from "node:path";
|
|
3981
|
+
function sleep2(ms) {
|
|
3982
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
3983
|
+
}
|
|
3984
|
+
function ageOf(dir) {
|
|
3985
|
+
try {
|
|
3986
|
+
return Date.now() - fs5.statSync(dir).mtimeMs;
|
|
3987
|
+
} catch {
|
|
3988
|
+
return 0;
|
|
3989
|
+
}
|
|
3990
|
+
}
|
|
3991
|
+
function pidAlive(pid) {
|
|
3992
|
+
if (!pid) return false;
|
|
3993
|
+
try {
|
|
3994
|
+
process.kill(pid, 0);
|
|
3995
|
+
return true;
|
|
3996
|
+
} catch (e) {
|
|
3997
|
+
return e.code === "EPERM";
|
|
3998
|
+
}
|
|
3999
|
+
}
|
|
4000
|
+
function ownerOf(dir) {
|
|
4001
|
+
try {
|
|
4002
|
+
const pid = Number(fs5.readFileSync(path6.join(dir, OWNER), "utf8").trim());
|
|
4003
|
+
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
4004
|
+
} catch {
|
|
4005
|
+
return void 0;
|
|
4006
|
+
}
|
|
4007
|
+
}
|
|
4008
|
+
function claimBreak(dir) {
|
|
4009
|
+
try {
|
|
4010
|
+
fs5.mkdirSync(path6.join(dir, BREAKING));
|
|
4011
|
+
return true;
|
|
4012
|
+
} catch {
|
|
4013
|
+
return false;
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
function removeClaim(dir, token3) {
|
|
4017
|
+
if (token3) {
|
|
4018
|
+
try {
|
|
4019
|
+
fs5.unlinkSync(path6.join(dir, token3));
|
|
4020
|
+
} catch (err) {
|
|
4021
|
+
if (err.code !== "ENOENT") throw err;
|
|
4022
|
+
}
|
|
4023
|
+
}
|
|
4024
|
+
try {
|
|
4025
|
+
fs5.rmdirSync(dir);
|
|
4026
|
+
return true;
|
|
4027
|
+
} catch (err) {
|
|
4028
|
+
const code = err.code;
|
|
4029
|
+
if (code === "ENOENT") return true;
|
|
4030
|
+
if (code === "ENOTEMPTY" || code === "EEXIST") return false;
|
|
4031
|
+
throw err;
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
4034
|
+
function tryLock(dir) {
|
|
4035
|
+
fs5.mkdirSync(path6.dirname(dir), { recursive: true });
|
|
4036
|
+
const token3 = `${process.pid}-${randomUUID()}`;
|
|
4037
|
+
const candidate = fs5.mkdtempSync(`${dir}.`);
|
|
4038
|
+
try {
|
|
4039
|
+
fs5.writeFileSync(path6.join(candidate, token3), "");
|
|
4040
|
+
for (; ; ) {
|
|
4041
|
+
try {
|
|
4042
|
+
fs5.renameSync(candidate, dir);
|
|
4043
|
+
return () => {
|
|
4044
|
+
removeClaim(dir, token3);
|
|
4045
|
+
};
|
|
4046
|
+
} catch (err) {
|
|
4047
|
+
const code = err.code;
|
|
4048
|
+
if (code !== "ENOTEMPTY" && code !== "EEXIST") throw err;
|
|
4049
|
+
}
|
|
4050
|
+
let entries;
|
|
4051
|
+
try {
|
|
4052
|
+
entries = fs5.readdirSync(dir);
|
|
4053
|
+
} catch (err) {
|
|
4054
|
+
if (err.code === "ENOENT") continue;
|
|
4055
|
+
throw err;
|
|
4056
|
+
}
|
|
4057
|
+
if (entries.length) {
|
|
4058
|
+
if (entries.length !== 1 || !/^\d+-[a-f0-9-]{36}$/.test(entries[0])) return void 0;
|
|
4059
|
+
const owner = Number(entries[0].split("-")[0]);
|
|
4060
|
+
if (pidAlive(owner)) return void 0;
|
|
4061
|
+
}
|
|
4062
|
+
if (!removeClaim(dir, entries[0])) return void 0;
|
|
4063
|
+
}
|
|
4064
|
+
} finally {
|
|
4065
|
+
fs5.rmSync(candidate, { recursive: true, force: true });
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
function withLock(dir, what, fn) {
|
|
4069
|
+
const until = Date.now() + WAIT_MS;
|
|
4070
|
+
let held4;
|
|
4071
|
+
for (; ; ) {
|
|
4072
|
+
try {
|
|
4073
|
+
fs5.mkdirSync(dir, { recursive: false });
|
|
4074
|
+
try {
|
|
4075
|
+
fs5.writeFileSync(path6.join(dir, OWNER), `${process.pid}
|
|
4076
|
+
`);
|
|
4077
|
+
} catch {
|
|
4078
|
+
}
|
|
4079
|
+
break;
|
|
4080
|
+
} catch (err) {
|
|
4081
|
+
const code = err.code;
|
|
4082
|
+
if (code === "ENOENT") {
|
|
4083
|
+
fs5.mkdirSync(path6.dirname(dir), { recursive: true });
|
|
4084
|
+
continue;
|
|
4085
|
+
}
|
|
4086
|
+
if (code !== "EEXIST") throw err;
|
|
4087
|
+
if (ageOf(dir) > STALE_MS) {
|
|
4088
|
+
fs5.rmSync(dir, { recursive: true, force: true });
|
|
4089
|
+
continue;
|
|
4090
|
+
}
|
|
4091
|
+
held4 = ownerOf(dir);
|
|
4092
|
+
if (held4 !== void 0 && !pidAlive(held4) && claimBreak(dir)) {
|
|
4093
|
+
fs5.rmSync(dir, { recursive: true, force: true });
|
|
4094
|
+
continue;
|
|
4095
|
+
}
|
|
4096
|
+
if (Date.now() > until) {
|
|
4097
|
+
die(
|
|
4098
|
+
`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.`,
|
|
4099
|
+
"board-busy"
|
|
4100
|
+
);
|
|
4101
|
+
}
|
|
4102
|
+
sleep2(RETRY_MS);
|
|
4103
|
+
}
|
|
4104
|
+
}
|
|
4105
|
+
try {
|
|
4106
|
+
return fn();
|
|
4107
|
+
} finally {
|
|
4108
|
+
const owner = ownerOf(dir);
|
|
4109
|
+
if (owner === void 0 || owner === process.pid) fs5.rmSync(dir, { recursive: true, force: true });
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
function withBoardLock(fn) {
|
|
4113
|
+
if (!fs5.existsSync(KANBAN)) return fn();
|
|
4114
|
+
if (depth > 0) return fn();
|
|
4115
|
+
try {
|
|
4116
|
+
ensureAkbDir();
|
|
4117
|
+
} catch {
|
|
4118
|
+
}
|
|
4119
|
+
depth++;
|
|
4120
|
+
try {
|
|
4121
|
+
return withLock(LOCK, "writing this board", fn);
|
|
4122
|
+
} finally {
|
|
4123
|
+
depth--;
|
|
4124
|
+
}
|
|
4125
|
+
}
|
|
4126
|
+
var WAIT_MS, STALE_MS, RETRY_MS, OWNER, BREAKING, depth;
|
|
4127
|
+
var init_lock = __esm({
|
|
4128
|
+
"../cli/src/lib/lock.ts"() {
|
|
4129
|
+
"use strict";
|
|
4130
|
+
init_paths();
|
|
4131
|
+
WAIT_MS = 1e4;
|
|
4132
|
+
STALE_MS = 6e4;
|
|
4133
|
+
RETRY_MS = 25;
|
|
4134
|
+
OWNER = "owner";
|
|
4135
|
+
BREAKING = "breaking";
|
|
4136
|
+
depth = 0;
|
|
4137
|
+
}
|
|
4138
|
+
});
|
|
4139
|
+
|
|
4140
|
+
// ../cli/src/lib/agent/creation-lock.ts
|
|
4141
|
+
import path7 from "node:path";
|
|
4142
|
+
function withCreationLock(fn) {
|
|
4143
|
+
if (depth2) return fn();
|
|
4144
|
+
return withLock(path7.join(SESSIONS_DIR, "creation.lock"), "changing card creation", () => {
|
|
4145
|
+
depth2++;
|
|
4146
|
+
try {
|
|
4147
|
+
return fn();
|
|
4148
|
+
} finally {
|
|
4149
|
+
depth2--;
|
|
4150
|
+
}
|
|
4151
|
+
});
|
|
4152
|
+
}
|
|
4153
|
+
var depth2;
|
|
4154
|
+
var init_creation_lock = __esm({
|
|
4155
|
+
"../cli/src/lib/agent/creation-lock.ts"() {
|
|
4156
|
+
"use strict";
|
|
4157
|
+
init_lock();
|
|
4158
|
+
init_paths();
|
|
4159
|
+
depth2 = 0;
|
|
4123
4160
|
}
|
|
4124
4161
|
});
|
|
4125
4162
|
|
|
@@ -5477,7 +5514,7 @@ var init_package = __esm({
|
|
|
5477
5514
|
"../cli/package.json"() {
|
|
5478
5515
|
package_default = {
|
|
5479
5516
|
name: "wildcrane",
|
|
5480
|
-
version: "0.11.
|
|
5517
|
+
version: "0.11.12",
|
|
5481
5518
|
akbContract: 5,
|
|
5482
5519
|
description: "WildCrane's `akb` command \u2014 scaffold the Markdown board under docs/kanban/, put an agent on a card, and carry the flows the agent works by.",
|
|
5483
5520
|
keywords: [
|
|
@@ -8316,10 +8353,10 @@ function findRollout(threadId) {
|
|
|
8316
8353
|
}
|
|
8317
8354
|
for (const name of names4) {
|
|
8318
8355
|
if (!name.startsWith("rollout-") || !name.endsWith(tail2)) continue;
|
|
8319
|
-
const
|
|
8356
|
+
const path87 = join(dir, name);
|
|
8320
8357
|
try {
|
|
8321
|
-
const at = statSync(
|
|
8322
|
-
if (!newest2 || at > newest2.at) newest2 = { path:
|
|
8358
|
+
const at = statSync(path87).mtimeMs;
|
|
8359
|
+
if (!newest2 || at > newest2.at) newest2 = { path: path87, at };
|
|
8323
8360
|
} catch {
|
|
8324
8361
|
}
|
|
8325
8362
|
}
|
|
@@ -8327,11 +8364,11 @@ function findRollout(threadId) {
|
|
|
8327
8364
|
return newest2?.path;
|
|
8328
8365
|
}
|
|
8329
8366
|
function readCodexRunFacts(threadId) {
|
|
8330
|
-
const
|
|
8331
|
-
if (!
|
|
8367
|
+
const path87 = findRollout(threadId);
|
|
8368
|
+
if (!path87) return void 0;
|
|
8332
8369
|
let raw;
|
|
8333
8370
|
try {
|
|
8334
|
-
raw = readFileSync(
|
|
8371
|
+
raw = readFileSync(path87, "utf8");
|
|
8335
8372
|
} catch {
|
|
8336
8373
|
return void 0;
|
|
8337
8374
|
}
|
|
@@ -8459,8 +8496,8 @@ function renderCompleted(item) {
|
|
|
8459
8496
|
const changes = Array.isArray(item.changes) ? item.changes : [];
|
|
8460
8497
|
return changes.map((raw) => {
|
|
8461
8498
|
const change2 = obj(raw);
|
|
8462
|
-
const
|
|
8463
|
-
return
|
|
8499
|
+
const path87 = str2(change2.path);
|
|
8500
|
+
return path87 ? `\u23FA ${str2(change2.kind) || "change"}(${path87})
|
|
8464
8501
|
` : "";
|
|
8465
8502
|
}).join("");
|
|
8466
8503
|
}
|
|
@@ -8719,10 +8756,10 @@ function readKimiRunFacts(sessionId) {
|
|
|
8719
8756
|
const total = { input: 0, cacheCreation: 0, cacheRead: 0, output: 0 };
|
|
8720
8757
|
let model;
|
|
8721
8758
|
let holding = 0;
|
|
8722
|
-
for (const { path:
|
|
8759
|
+
for (const { path: path87, main } of wireFiles(entry.sessionDir)) {
|
|
8723
8760
|
let raw;
|
|
8724
8761
|
try {
|
|
8725
|
-
raw = readFileSync2(
|
|
8762
|
+
raw = readFileSync2(path87, "utf8");
|
|
8726
8763
|
} catch {
|
|
8727
8764
|
continue;
|
|
8728
8765
|
}
|
|
@@ -9003,8 +9040,8 @@ var init_tail = __esm({
|
|
|
9003
9040
|
import fs24 from "node:fs";
|
|
9004
9041
|
function whereHint(update2, title) {
|
|
9005
9042
|
const locations = Array.isArray(update2.locations) ? update2.locations : [];
|
|
9006
|
-
const
|
|
9007
|
-
return
|
|
9043
|
+
const path87 = str2(obj(locations[0]).path);
|
|
9044
|
+
return path87 && !title.includes(path87) ? hint(path87) : "";
|
|
9008
9045
|
}
|
|
9009
9046
|
function saidBy(update2) {
|
|
9010
9047
|
const content = Array.isArray(update2.content) ? update2.content : [];
|
|
@@ -9947,27 +9984,40 @@ import crypto3 from "node:crypto";
|
|
|
9947
9984
|
import fs25 from "node:fs";
|
|
9948
9985
|
import os4 from "node:os";
|
|
9949
9986
|
import path28 from "node:path";
|
|
9987
|
+
function spawnGit(args, options) {
|
|
9988
|
+
const out = spawnSync3("git", args, {
|
|
9989
|
+
...options,
|
|
9990
|
+
encoding: "utf8",
|
|
9991
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
9992
|
+
timeout: gitLimits.timeoutMs,
|
|
9993
|
+
killSignal: "SIGKILL"
|
|
9994
|
+
});
|
|
9995
|
+
if (out.error?.code === "ETIMEDOUT") throw new GitTimeoutError(`Git ${args[0]} timed out after ${gitLimits.timeoutMs / 1e3}s; the command was killed.`);
|
|
9996
|
+
return out;
|
|
9997
|
+
}
|
|
9950
9998
|
function git(args, cwd = REPO_ROOT) {
|
|
9951
9999
|
try {
|
|
9952
|
-
const out =
|
|
10000
|
+
const out = spawnGit(args, { cwd, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true });
|
|
9953
10001
|
if (out.error || out.status !== 0) return null;
|
|
9954
10002
|
return out.stdout;
|
|
9955
|
-
} catch {
|
|
10003
|
+
} catch (error) {
|
|
10004
|
+
if (error instanceof GitTimeoutError) throw error;
|
|
9956
10005
|
return null;
|
|
9957
10006
|
}
|
|
9958
10007
|
}
|
|
9959
10008
|
function gitDiff(args, cwd = REPO_ROOT) {
|
|
9960
10009
|
try {
|
|
9961
|
-
const out =
|
|
10010
|
+
const out = spawnGit(args, { cwd, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true });
|
|
9962
10011
|
if (out.error || out.status !== 0 && out.status !== 1) return null;
|
|
9963
10012
|
return out.stdout;
|
|
9964
|
-
} catch {
|
|
10013
|
+
} catch (error) {
|
|
10014
|
+
if (error instanceof GitTimeoutError) throw error;
|
|
9965
10015
|
return null;
|
|
9966
10016
|
}
|
|
9967
10017
|
}
|
|
9968
10018
|
function tryGit(args, cwd = REPO_ROOT, env2) {
|
|
9969
10019
|
try {
|
|
9970
|
-
const out =
|
|
10020
|
+
const out = spawnGit(args, {
|
|
9971
10021
|
cwd,
|
|
9972
10022
|
encoding: "utf8",
|
|
9973
10023
|
maxBuffer: MAX_OUTPUT,
|
|
@@ -9978,6 +10028,7 @@ function tryGit(args, cwd = REPO_ROOT, env2) {
|
|
|
9978
10028
|
if (out.status !== 0) return { ok: false, why: (out.stderr || out.stdout || "").trim() };
|
|
9979
10029
|
return { ok: true };
|
|
9980
10030
|
} catch (e) {
|
|
10031
|
+
if (e instanceof GitTimeoutError) throw e;
|
|
9981
10032
|
return { ok: false, why: e instanceof Error ? e.message : String(e) };
|
|
9982
10033
|
}
|
|
9983
10034
|
}
|
|
@@ -9986,7 +10037,7 @@ function boardManagedPaths() {
|
|
|
9986
10037
|
}
|
|
9987
10038
|
function outsideBoardForAdd(cwd = REPO_ROOT) {
|
|
9988
10039
|
const kept2 = boardManagedPaths().filter(
|
|
9989
|
-
(p) =>
|
|
10040
|
+
(p) => spawnGit(["check-ignore", "-q", p], { cwd, windowsHide: true }).status !== 0
|
|
9990
10041
|
);
|
|
9991
10042
|
return ["--", ".", ...kept2.map((p) => `:(exclude)${p}`)];
|
|
9992
10043
|
}
|
|
@@ -10106,10 +10157,10 @@ function treeMark(base, cwd = REPO_ROOT) {
|
|
|
10106
10157
|
const index = path28.join(ensureAkbDir(), `index-${process.pid}-${Date.now()}`);
|
|
10107
10158
|
try {
|
|
10108
10159
|
const env2 = { ...process.env, GIT_INDEX_FILE: index };
|
|
10109
|
-
const run2 = (args) =>
|
|
10160
|
+
const run2 = (args) => spawnGit(args, { cwd, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true, env: env2 }).status === 0;
|
|
10110
10161
|
if (!run2(["read-tree", base])) return null;
|
|
10111
10162
|
if (!run2(["add", "-A", ...outsideBoardForAdd(cwd)])) return null;
|
|
10112
|
-
const tree =
|
|
10163
|
+
const tree = spawnGit(["write-tree"], {
|
|
10113
10164
|
cwd,
|
|
10114
10165
|
encoding: "utf8",
|
|
10115
10166
|
maxBuffer: MAX_OUTPUT,
|
|
@@ -10117,7 +10168,8 @@ function treeMark(base, cwd = REPO_ROOT) {
|
|
|
10117
10168
|
env: env2
|
|
10118
10169
|
});
|
|
10119
10170
|
return tree.status === 0 ? tree.stdout.trim() || null : null;
|
|
10120
|
-
} catch {
|
|
10171
|
+
} catch (error) {
|
|
10172
|
+
if (error instanceof GitTimeoutError) throw error;
|
|
10121
10173
|
return null;
|
|
10122
10174
|
} finally {
|
|
10123
10175
|
try {
|
|
@@ -10128,23 +10180,26 @@ function treeMark(base, cwd = REPO_ROOT) {
|
|
|
10128
10180
|
}
|
|
10129
10181
|
function reverseApplies(patch2, ref, cwd = REPO_ROOT) {
|
|
10130
10182
|
if (!patch2.trim()) return false;
|
|
10131
|
-
const
|
|
10183
|
+
const folder = fs25.mkdtempSync(path28.join(os4.tmpdir(), `akb-apply-${process.pid}-`));
|
|
10184
|
+
const index = path28.join(folder, "index");
|
|
10185
|
+
const patchFile = path28.join(folder, "patch");
|
|
10132
10186
|
const env2 = { GIT_INDEX_FILE: index };
|
|
10133
10187
|
try {
|
|
10188
|
+
fs25.writeFileSync(patchFile, patch2);
|
|
10134
10189
|
if (!tryGit(["read-tree", ref], cwd, env2).ok) return false;
|
|
10135
|
-
const out =
|
|
10190
|
+
const out = spawnGit(["apply", "--cached", "--reverse", "--check", patchFile], {
|
|
10136
10191
|
cwd,
|
|
10137
|
-
input: patch2,
|
|
10138
10192
|
encoding: "utf8",
|
|
10139
10193
|
maxBuffer: MAX_OUTPUT,
|
|
10140
10194
|
windowsHide: true,
|
|
10141
10195
|
env: { ...process.env, ...env2 }
|
|
10142
10196
|
});
|
|
10143
10197
|
return !out.error && out.status === 0;
|
|
10144
|
-
} catch {
|
|
10198
|
+
} catch (error) {
|
|
10199
|
+
if (error instanceof GitTimeoutError) throw error;
|
|
10145
10200
|
return false;
|
|
10146
10201
|
} finally {
|
|
10147
|
-
fs25.rmSync(
|
|
10202
|
+
fs25.rmSync(folder, { recursive: true, force: true });
|
|
10148
10203
|
}
|
|
10149
10204
|
}
|
|
10150
10205
|
function squashOnto(dir, base, message) {
|
|
@@ -10226,12 +10281,15 @@ function moveBranchRef(branch, commit, expect) {
|
|
|
10226
10281
|
if (branchTip(branch) !== expect) return { moved: true };
|
|
10227
10282
|
return { ok: false, error: `couldn't move ${branch} to ${commit.slice(0, 12)}: ${done2.why ?? "git refused"}` };
|
|
10228
10283
|
}
|
|
10229
|
-
var MAX_OUTPUT, scripted, posix, outsideBoard, deliveryBranch, deliveryWorktree, worktreeDir, inGitRepo, headCommit, worktreeExists, branchExists, pruneWorktreeMetadata, branchTip, isAncestor, changedPaths, branchPatch, lastCommitTouching, continueRebase, abortRebase, conflictedPaths, headingKind;
|
|
10284
|
+
var MAX_OUTPUT, gitLimits, GitTimeoutError, scripted, posix, outsideBoard, deliveryBranch, deliveryWorktree, worktreeDir, inGitRepo, headCommit, worktreeExists, branchExists, pruneWorktreeMetadata, branchTip, isAncestor, changedPaths, branchPatch, lastCommitTouching, continueRebase, abortRebase, conflictedPaths, headingKind;
|
|
10230
10285
|
var init_worktree = __esm({
|
|
10231
10286
|
"../cli/src/lib/agent/worktree.ts"() {
|
|
10232
10287
|
"use strict";
|
|
10233
10288
|
init_paths();
|
|
10234
10289
|
MAX_OUTPUT = 64 * 1024 * 1024;
|
|
10290
|
+
gitLimits = { timeoutMs: 3e4 };
|
|
10291
|
+
GitTimeoutError = class extends Error {
|
|
10292
|
+
};
|
|
10235
10293
|
scripted = (args, cwd) => tryGit(["-c", "core.hooksPath=/dev/null", ...args], cwd, { GIT_EDITOR: "true", GIT_SEQUENCE_EDITOR: "true" });
|
|
10236
10294
|
posix = (p) => p.split(path28.sep).join("/");
|
|
10237
10295
|
outsideBoard = () => [
|
|
@@ -10248,7 +10306,7 @@ var init_worktree = __esm({
|
|
|
10248
10306
|
branchExists = (branch) => !!branch && git(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`]) !== null;
|
|
10249
10307
|
pruneWorktreeMetadata = () => void git(["worktree", "prune"]);
|
|
10250
10308
|
branchTip = (branch) => git(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`])?.trim() || null;
|
|
10251
|
-
isAncestor = (older, newer, cwd = REPO_ROOT) =>
|
|
10309
|
+
isAncestor = (older, newer, cwd = REPO_ROOT) => spawnGit(["merge-base", "--is-ancestor", older, newer], { cwd, windowsHide: true }).status === 0;
|
|
10252
10310
|
changedPaths = (base, branch, cwd = REPO_ROOT) => {
|
|
10253
10311
|
const out = git(["diff", "--name-only", base, branch, ...outsideBoard()], cwd);
|
|
10254
10312
|
return out === null ? null : out.split("\n").filter(Boolean);
|
|
@@ -11556,49 +11614,12 @@ function withSystemLanguage(harness, argv, env2, line) {
|
|
|
11556
11614
|
}
|
|
11557
11615
|
var init_run_settings = __esm({
|
|
11558
11616
|
"../cli/src/lib/agent/run-settings.ts"() {
|
|
11559
|
-
"use strict";
|
|
11560
|
-
init_argv();
|
|
11561
|
-
init_env();
|
|
11562
|
-
init_language();
|
|
11563
|
-
init_types2();
|
|
11564
|
-
init_providers();
|
|
11565
|
-
}
|
|
11566
|
-
});
|
|
11567
|
-
|
|
11568
|
-
// ../cli/src/lib/cloud/execution.ts
|
|
11569
|
-
function executionOf(value = process.env.AI4KANBAN_EXECUTION) {
|
|
11570
|
-
try {
|
|
11571
|
-
const held4 = typeof value === "string" ? JSON.parse(value) : value;
|
|
11572
|
-
if (held4 && UUID.test(held4.job) && UUID.test(held4.machine)) return { job: held4.job, machine: held4.machine };
|
|
11573
|
-
} catch {
|
|
11574
|
-
}
|
|
11575
|
-
return void 0;
|
|
11576
|
-
}
|
|
11577
|
-
async function executionHeld(execution) {
|
|
11578
|
-
if (!execution) return true;
|
|
11579
|
-
try {
|
|
11580
|
-
const auth = await accessToken();
|
|
11581
|
-
if (!auth.ok) return false;
|
|
11582
|
-
const response = await fetch(`${cloudEndpoints().api}/v1/runner/jobs/${execution.job}/renew`, {
|
|
11583
|
-
method: "POST",
|
|
11584
|
-
headers: { authorization: `Bearer ${auth.token}`, "content-type": "application/json" },
|
|
11585
|
-
body: JSON.stringify({ machineId: execution.machine }),
|
|
11586
|
-
signal: AbortSignal.timeout(2e4)
|
|
11587
|
-
});
|
|
11588
|
-
if (!response.ok) return false;
|
|
11589
|
-
const held4 = await response.json();
|
|
11590
|
-
return held4.renewed === true && held4.stop !== true;
|
|
11591
|
-
} catch {
|
|
11592
|
-
return false;
|
|
11593
|
-
}
|
|
11594
|
-
}
|
|
11595
|
-
var UUID;
|
|
11596
|
-
var init_execution = __esm({
|
|
11597
|
-
"../cli/src/lib/cloud/execution.ts"() {
|
|
11598
|
-
"use strict";
|
|
11599
|
-
init_config();
|
|
11600
|
-
init_session();
|
|
11601
|
-
UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
11617
|
+
"use strict";
|
|
11618
|
+
init_argv();
|
|
11619
|
+
init_env();
|
|
11620
|
+
init_language();
|
|
11621
|
+
init_types2();
|
|
11622
|
+
init_providers();
|
|
11602
11623
|
}
|
|
11603
11624
|
});
|
|
11604
11625
|
|
|
@@ -15546,6 +15567,252 @@ var init_complete = __esm({
|
|
|
15546
15567
|
}
|
|
15547
15568
|
});
|
|
15548
15569
|
|
|
15570
|
+
// ../cli/src/lib/agent/stop.ts
|
|
15571
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
15572
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
15573
|
+
import fs39 from "node:fs";
|
|
15574
|
+
import os6 from "node:os";
|
|
15575
|
+
import path43 from "node:path";
|
|
15576
|
+
function markedInProc(entry, strict = false) {
|
|
15577
|
+
const pids = [];
|
|
15578
|
+
for (const name of fs39.readdirSync("/proc")) {
|
|
15579
|
+
if (!/^\d+$/.test(name)) continue;
|
|
15580
|
+
try {
|
|
15581
|
+
if (fs39.readFileSync(`/proc/${name}/environ`, "utf8").split("\0").includes(entry)) pids.push(Number(name));
|
|
15582
|
+
} catch (error) {
|
|
15583
|
+
if (strict && !["ENOENT", "ESRCH"].includes(error.code ?? "")) {
|
|
15584
|
+
const stat = fs39.statSync(`/proc/${name}`, { throwIfNoEntry: false });
|
|
15585
|
+
if (stat?.uid === process.getuid?.()) throw error;
|
|
15586
|
+
}
|
|
15587
|
+
}
|
|
15588
|
+
}
|
|
15589
|
+
return pids;
|
|
15590
|
+
}
|
|
15591
|
+
function markedInPs(entry, roots) {
|
|
15592
|
+
const ps = spawnSync4("ps", ["-A", "-E", "-ww", "-o", "pid=,ppid=,command="], {
|
|
15593
|
+
encoding: "utf8",
|
|
15594
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
15595
|
+
timeout: 5e3
|
|
15596
|
+
});
|
|
15597
|
+
if (ps.status !== 0 || !ps.stdout) throw new Error("\u65E0\u6CD5\u67E5\u8BE2\u8FDB\u7A0B\u6811\u3002");
|
|
15598
|
+
const children = /* @__PURE__ */ new Map();
|
|
15599
|
+
const listed2 = /* @__PURE__ */ new Set();
|
|
15600
|
+
const found2 = /* @__PURE__ */ new Set();
|
|
15601
|
+
for (const line of ps.stdout.split("\n")) {
|
|
15602
|
+
const m = /^\s*(\d+)\s+(\d+)\s(.*)$/.exec(line);
|
|
15603
|
+
if (!m) continue;
|
|
15604
|
+
const pid = Number(m[1]);
|
|
15605
|
+
const ppid = Number(m[2]);
|
|
15606
|
+
listed2.add(pid);
|
|
15607
|
+
children.set(ppid, [...children.get(ppid) ?? [], pid]);
|
|
15608
|
+
if (`${m[3]} `.includes(` ${entry} `)) found2.add(pid);
|
|
15609
|
+
}
|
|
15610
|
+
const queue = [...found2, ...roots.filter((pid) => listed2.has(pid))];
|
|
15611
|
+
for (const pid of queue) {
|
|
15612
|
+
found2.add(pid);
|
|
15613
|
+
for (const child of children.get(pid) ?? []) if (!found2.has(child)) queue.push(child);
|
|
15614
|
+
}
|
|
15615
|
+
return [...found2];
|
|
15616
|
+
}
|
|
15617
|
+
function marked(mark, roots = []) {
|
|
15618
|
+
if (WINDOWS3 || !mark.value) return [];
|
|
15619
|
+
const entry = `${mark.name}=${mark.value}`;
|
|
15620
|
+
try {
|
|
15621
|
+
const pids = process.platform === "linux" ? markedInProc(entry) : markedInPs(entry, roots);
|
|
15622
|
+
return pids.filter((pid) => pid !== process.pid);
|
|
15623
|
+
} catch {
|
|
15624
|
+
return [];
|
|
15625
|
+
}
|
|
15626
|
+
}
|
|
15627
|
+
function signal(pids, sig) {
|
|
15628
|
+
for (const pid of pids) {
|
|
15629
|
+
try {
|
|
15630
|
+
process.kill(pid, sig);
|
|
15631
|
+
} catch {
|
|
15632
|
+
}
|
|
15633
|
+
}
|
|
15634
|
+
}
|
|
15635
|
+
function stopWatchers(sessions, onlyPid, stillOwns = () => true, startedBefore) {
|
|
15636
|
+
if (sessions ? !sessions.length : onlyPid === void 0) return [];
|
|
15637
|
+
const command = WINDOWS3 ? "powershell.exe" : "ps";
|
|
15638
|
+
const args = WINDOWS3 ? ["-NoProfile", "-NonInteractive", "-Command", "Get-CimInstance Win32_Process | Select-Object ProcessId,CommandLine,@{Name='StartedAt';Expression={([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds()}} | ConvertTo-Json -Compress"] : ["-A", "-ww", "-o", "pid=,lstart=,command="];
|
|
15639
|
+
const out = spawnSync4(command, args, { encoding: "utf8", timeout: 5e3, maxBuffer: 16 * 1024 * 1024, windowsHide: true, env: { ...process.env, LC_ALL: "C" } });
|
|
15640
|
+
if (out.status !== 0) return [];
|
|
15641
|
+
let procs;
|
|
15642
|
+
try {
|
|
15643
|
+
procs = WINDOWS3 ? [].concat(JSON.parse(out.stdout)).map((p) => ({ pid: p.ProcessId, command: p.CommandLine ?? "", startedAt: p.StartedAt })) : out.stdout.split("\n").flatMap((line) => {
|
|
15644
|
+
const m = /^\s*(\d+)\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d{4})\s+(.*)$/.exec(line);
|
|
15645
|
+
return m ? [{ pid: Number(m[1]), command: m[3], startedAt: Date.parse(m[2]) }] : [];
|
|
15646
|
+
});
|
|
15647
|
+
} catch {
|
|
15648
|
+
return [];
|
|
15649
|
+
}
|
|
15650
|
+
const known2 = sessions && new Set(sessions);
|
|
15651
|
+
const stopped2 = [];
|
|
15652
|
+
for (const proc of procs) {
|
|
15653
|
+
if (proc.pid === process.pid || onlyPid !== void 0 && proc.pid !== onlyPid) continue;
|
|
15654
|
+
if (startedBefore !== void 0 && (!Number.isFinite(proc.startedAt) || proc.startedAt > startedBefore)) continue;
|
|
15655
|
+
const match = /^"?[^\n]*?\bnode(?:\.exe)?"?\s+(?:"[^\n"]+\.mjs"|\S+\.mjs)\s+__watch\s+(\S+)(?:\s|$)/.exec(proc.command);
|
|
15656
|
+
const args2 = `${proc.command} `;
|
|
15657
|
+
const names4 = (flag, value) => args2.includes(` ${flag} ${value} `) || args2.includes(` ${flag} "${value}" `);
|
|
15658
|
+
const board2 = names4("--board", KANBAN) || KANBAN === path43.join(REPO_ROOT, "docs", "kanban") && names4("--dir", REPO_ROOT);
|
|
15659
|
+
if (!match || known2 && !known2.has(match[1]) || !board2 || !stillOwns()) continue;
|
|
15660
|
+
if (WINDOWS3) {
|
|
15661
|
+
if (killTreeOnWindows(proc.pid)) stopped2.push(proc.pid);
|
|
15662
|
+
} else {
|
|
15663
|
+
try {
|
|
15664
|
+
process.kill(-proc.pid, "SIGKILL");
|
|
15665
|
+
stopped2.push(proc.pid);
|
|
15666
|
+
} catch {
|
|
15667
|
+
}
|
|
15668
|
+
}
|
|
15669
|
+
}
|
|
15670
|
+
return stopped2;
|
|
15671
|
+
}
|
|
15672
|
+
function startedUnder(list, root, since) {
|
|
15673
|
+
const self = list.procs.find((p) => p.pid === root);
|
|
15674
|
+
const from = since ?? self?.startedAt;
|
|
15675
|
+
if (from === void 0 || list.bootedAt > from) return [];
|
|
15676
|
+
if (self && self.startedAt > from) return [];
|
|
15677
|
+
const found2 = self ? [root] : [];
|
|
15678
|
+
const seen = /* @__PURE__ */ new Set([root]);
|
|
15679
|
+
const queue = [{ pid: root, startedAt: from }];
|
|
15680
|
+
for (const parent of queue) {
|
|
15681
|
+
for (const p of list.procs) {
|
|
15682
|
+
if (p.ppid !== parent.pid || seen.has(p.pid) || p.startedAt < parent.startedAt) continue;
|
|
15683
|
+
seen.add(p.pid);
|
|
15684
|
+
found2.push(p.pid);
|
|
15685
|
+
queue.push(p);
|
|
15686
|
+
}
|
|
15687
|
+
}
|
|
15688
|
+
return found2.reverse();
|
|
15689
|
+
}
|
|
15690
|
+
function readProcList(out) {
|
|
15691
|
+
const procs = [];
|
|
15692
|
+
let bootedAt;
|
|
15693
|
+
for (const line of out.split(/\r?\n/)) {
|
|
15694
|
+
const boot = /^boot (\d+)$/.exec(line.trim());
|
|
15695
|
+
if (boot) bootedAt = Number(boot[1]);
|
|
15696
|
+
const m = /^(\d+) (\d+) (\d+)$/.exec(line.trim());
|
|
15697
|
+
if (m) procs.push({ pid: Number(m[1]), ppid: Number(m[2]), startedAt: Number(m[3]) });
|
|
15698
|
+
}
|
|
15699
|
+
return bootedAt !== void 0 && procs.length ? { procs, bootedAt } : void 0;
|
|
15700
|
+
}
|
|
15701
|
+
function procsOnWindows() {
|
|
15702
|
+
const ps = spawnSync4("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", LIST_PROCS], {
|
|
15703
|
+
encoding: "utf8",
|
|
15704
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
15705
|
+
timeout: 5e3,
|
|
15706
|
+
windowsHide: true
|
|
15707
|
+
});
|
|
15708
|
+
return ps.status === 0 && ps.stdout ? readProcList(ps.stdout) : void 0;
|
|
15709
|
+
}
|
|
15710
|
+
function killUnderOnWindows(pid, since) {
|
|
15711
|
+
if (!WINDOWS3 || !pid) return;
|
|
15712
|
+
const list = procsOnWindows();
|
|
15713
|
+
if (!list) return;
|
|
15714
|
+
const pids = startedUnder(list, pid, since).filter((p) => p !== process.pid);
|
|
15715
|
+
if (!pids.length) return;
|
|
15716
|
+
spawnSync4("taskkill", ["/F", ...pids.flatMap((p) => ["/PID", String(p)])], { stdio: "ignore", windowsHide: true, timeout: 1e4 });
|
|
15717
|
+
}
|
|
15718
|
+
function killMarked(mark, agent) {
|
|
15719
|
+
if (WINDOWS3) {
|
|
15720
|
+
if (agent) killUnderOnWindows(agent.pid, agent.since);
|
|
15721
|
+
return;
|
|
15722
|
+
}
|
|
15723
|
+
signal(marked(mark), "SIGKILL");
|
|
15724
|
+
}
|
|
15725
|
+
function killTreeOnWindows(pid) {
|
|
15726
|
+
if (!WINDOWS3 || !pid) return false;
|
|
15727
|
+
const out = spawnSync4("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true, timeout: 1e4 });
|
|
15728
|
+
return out.status === 0;
|
|
15729
|
+
}
|
|
15730
|
+
function later(ms, fn) {
|
|
15731
|
+
const t = setTimeout(fn, ms);
|
|
15732
|
+
if (typeof t.unref === "function") t.unref();
|
|
15733
|
+
}
|
|
15734
|
+
function endAgent(child, mark, graceMs) {
|
|
15735
|
+
const term = (sig) => {
|
|
15736
|
+
try {
|
|
15737
|
+
child.kill(sig);
|
|
15738
|
+
} catch {
|
|
15739
|
+
}
|
|
15740
|
+
};
|
|
15741
|
+
if (killTreeOnWindows(child.pid)) return;
|
|
15742
|
+
killUnderOnWindows(child.pid);
|
|
15743
|
+
const seen = new Set(marked(mark, child.pid ? [child.pid] : []));
|
|
15744
|
+
term("SIGTERM");
|
|
15745
|
+
signal([...seen].filter((pid) => pid !== child.pid), "SIGTERM");
|
|
15746
|
+
const round = (left) => {
|
|
15747
|
+
const pids = marked(mark, [...seen]);
|
|
15748
|
+
signal(pids, "SIGKILL");
|
|
15749
|
+
for (const pid of pids) seen.add(pid);
|
|
15750
|
+
if (pids.length && left > 1) later(KILL_ROUND_MS, () => round(left - 1));
|
|
15751
|
+
};
|
|
15752
|
+
later(graceMs, () => {
|
|
15753
|
+
term("SIGKILL");
|
|
15754
|
+
round(KILL_ROUNDS);
|
|
15755
|
+
});
|
|
15756
|
+
}
|
|
15757
|
+
function killRunning() {
|
|
15758
|
+
for (const [child, mark] of running) {
|
|
15759
|
+
if (killTreeOnWindows(child.pid)) continue;
|
|
15760
|
+
killUnderOnWindows(child.pid);
|
|
15761
|
+
signal(marked(mark, child.pid ? [child.pid] : []), "SIGKILL");
|
|
15762
|
+
try {
|
|
15763
|
+
child.kill("SIGKILL");
|
|
15764
|
+
} catch {
|
|
15765
|
+
}
|
|
15766
|
+
}
|
|
15767
|
+
running.clear();
|
|
15768
|
+
}
|
|
15769
|
+
function unlisten() {
|
|
15770
|
+
for (const [sig, fn] of listening) process.removeListener(sig, fn);
|
|
15771
|
+
listening.clear();
|
|
15772
|
+
}
|
|
15773
|
+
function onQuit(sig) {
|
|
15774
|
+
killRunning();
|
|
15775
|
+
unlisten();
|
|
15776
|
+
if (process.listenerCount(sig) === 0) process.exit(128 + os6.constants.signals[sig]);
|
|
15777
|
+
}
|
|
15778
|
+
function trackAgent(child, mark) {
|
|
15779
|
+
if (!child.pid) return;
|
|
15780
|
+
running.set(child, mark);
|
|
15781
|
+
child.once("close", () => {
|
|
15782
|
+
running.delete(child);
|
|
15783
|
+
if (!running.size) unlisten();
|
|
15784
|
+
});
|
|
15785
|
+
for (const sig of QUIT_SIGNALS) {
|
|
15786
|
+
if (listening.has(sig) || sig === "SIGINT" && interruptIsTheCallers) continue;
|
|
15787
|
+
const fn = () => onQuit(sig);
|
|
15788
|
+
listening.set(sig, fn);
|
|
15789
|
+
process.on(sig, fn);
|
|
15790
|
+
}
|
|
15791
|
+
}
|
|
15792
|
+
var runMark, stopMark, markEnv, WINDOWS3, KILL_ROUND_MS, KILL_ROUNDS, LIST_PROCS, running, QUIT_SIGNALS, listening, interruptIsTheCallers;
|
|
15793
|
+
var init_stop = __esm({
|
|
15794
|
+
"../cli/src/lib/agent/stop.ts"() {
|
|
15795
|
+
"use strict";
|
|
15796
|
+
init_env();
|
|
15797
|
+
init_paths();
|
|
15798
|
+
runMark = (sessionId) => ({ name: RUN_ENV, value: sessionId });
|
|
15799
|
+
stopMark = () => ({ name: STOP_ENV, value: randomUUID5() });
|
|
15800
|
+
markEnv = (env2, mark) => ({ ...env2, [mark.name]: mark.value });
|
|
15801
|
+
WINDOWS3 = process.platform === "win32";
|
|
15802
|
+
KILL_ROUND_MS = 300;
|
|
15803
|
+
KILL_ROUNDS = 3;
|
|
15804
|
+
LIST_PROCS = [
|
|
15805
|
+
"$ErrorActionPreference = 'Stop'",
|
|
15806
|
+
"Get-CimInstance Win32_Process | ForEach-Object { if ($_.CreationDate) { '{0} {1} {2}' -f $_.ProcessId, $_.ParentProcessId, ([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds() } }",
|
|
15807
|
+
"'boot {0}' -f ([DateTimeOffset](Get-CimInstance Win32_OperatingSystem).LastBootUpTime).ToUnixTimeMilliseconds()"
|
|
15808
|
+
].join("; ");
|
|
15809
|
+
running = /* @__PURE__ */ new Map();
|
|
15810
|
+
QUIT_SIGNALS = ["SIGTERM", "SIGINT", "SIGHUP"];
|
|
15811
|
+
listening = /* @__PURE__ */ new Map();
|
|
15812
|
+
interruptIsTheCallers = false;
|
|
15813
|
+
}
|
|
15814
|
+
});
|
|
15815
|
+
|
|
15549
15816
|
// ../cli/src/lib/agent/pause.ts
|
|
15550
15817
|
function deliveryState(delivery, questions) {
|
|
15551
15818
|
const landing = delivery.landing;
|
|
@@ -15734,12 +16001,12 @@ var init_runner = __esm({
|
|
|
15734
16001
|
});
|
|
15735
16002
|
|
|
15736
16003
|
// ../cli/src/lib/agent/rules.ts
|
|
15737
|
-
import
|
|
15738
|
-
import
|
|
16004
|
+
import fs40 from "node:fs";
|
|
16005
|
+
import path44 from "node:path";
|
|
15739
16006
|
function ruleFile(agent) {
|
|
15740
16007
|
if (!agent || hiddenRole(agent)) return "";
|
|
15741
16008
|
try {
|
|
15742
|
-
return
|
|
16009
|
+
return fs40.readFileSync(rulePath(agent), "utf8").trim();
|
|
15743
16010
|
} catch {
|
|
15744
16011
|
return "";
|
|
15745
16012
|
}
|
|
@@ -15754,11 +16021,11 @@ function setAgentRule(agent, text5) {
|
|
|
15754
16021
|
const rule = String(text5 ?? "").trim();
|
|
15755
16022
|
try {
|
|
15756
16023
|
if (!rule) {
|
|
15757
|
-
|
|
16024
|
+
fs40.rmSync(rulePath(agent), { force: true });
|
|
15758
16025
|
return { ok: true };
|
|
15759
16026
|
}
|
|
15760
|
-
|
|
15761
|
-
|
|
16027
|
+
fs40.mkdirSync(RULES, { recursive: true });
|
|
16028
|
+
fs40.writeFileSync(rulePath(agent), `${rule}
|
|
15762
16029
|
`);
|
|
15763
16030
|
return { ok: true };
|
|
15764
16031
|
} catch (err) {
|
|
@@ -15806,7 +16073,7 @@ function migrateFlowRules() {
|
|
|
15806
16073
|
adoptRenamedRules();
|
|
15807
16074
|
let here;
|
|
15808
16075
|
try {
|
|
15809
|
-
here = new Set(
|
|
16076
|
+
here = new Set(fs40.readdirSync(RULES));
|
|
15810
16077
|
} catch {
|
|
15811
16078
|
return [];
|
|
15812
16079
|
}
|
|
@@ -15817,9 +16084,9 @@ function migrateFlowRules() {
|
|
|
15817
16084
|
if (!from.length) continue;
|
|
15818
16085
|
const parts = [ruleFile(owner.name), ...from.map(ruleFile)].filter(Boolean);
|
|
15819
16086
|
try {
|
|
15820
|
-
if (parts.length)
|
|
16087
|
+
if (parts.length) fs40.writeFileSync(rulePath(owner.name), `${parts.join("\n\n")}
|
|
15821
16088
|
`);
|
|
15822
|
-
for (const flow of from)
|
|
16089
|
+
for (const flow of from) fs40.rmSync(rulePath(flow), { force: true });
|
|
15823
16090
|
} catch (err) {
|
|
15824
16091
|
notes.push(`could not move the ${from.join(", ")} rule(s) onto \`${owner.name}\`: ${String(err)}`);
|
|
15825
16092
|
continue;
|
|
@@ -15833,7 +16100,7 @@ function migrateFlowRules() {
|
|
|
15833
16100
|
function adoptRenamedRules() {
|
|
15834
16101
|
let here;
|
|
15835
16102
|
try {
|
|
15836
|
-
here =
|
|
16103
|
+
here = fs40.readdirSync(RULES);
|
|
15837
16104
|
} catch {
|
|
15838
16105
|
return;
|
|
15839
16106
|
}
|
|
@@ -15843,13 +16110,13 @@ function adoptRenamedRules() {
|
|
|
15843
16110
|
const now = RENAMED_ROLES[was] ?? canonicalSpecAgent(was);
|
|
15844
16111
|
if (now === was) continue;
|
|
15845
16112
|
try {
|
|
15846
|
-
if (!
|
|
16113
|
+
if (!fs40.existsSync(rulePath(now))) fs40.renameSync(rulePath(was), rulePath(now));
|
|
15847
16114
|
else if (FOLDED_SPEC_AGENTS.has(was)) {
|
|
15848
16115
|
const kept2 = ruleFile(now);
|
|
15849
16116
|
const carried = ruleFile(was);
|
|
15850
|
-
if (carried && !kept2.includes(carried))
|
|
16117
|
+
if (carried && !kept2.includes(carried)) fs40.writeFileSync(rulePath(now), `${[kept2, carried].filter(Boolean).join("\n\n")}
|
|
15851
16118
|
`);
|
|
15852
|
-
|
|
16119
|
+
fs40.rmSync(rulePath(was), { force: true });
|
|
15853
16120
|
}
|
|
15854
16121
|
} catch {
|
|
15855
16122
|
}
|
|
@@ -15866,7 +16133,7 @@ var init_rules2 = __esm({
|
|
|
15866
16133
|
init_runner();
|
|
15867
16134
|
init_workflows();
|
|
15868
16135
|
init_types3();
|
|
15869
|
-
rulePath = (agent) =>
|
|
16136
|
+
rulePath = (agent) => path44.join(RULES, `${agent}.md`);
|
|
15870
16137
|
notOnTheRoster = (name) => `"${name}" is not an agent on this board. It has: ${agentNames().join(", ")}.`;
|
|
15871
16138
|
flowForRequest = (req) => req.refineRound !== void 0 && REFINE_ACTIONS.has(req.action) ? flowByCommand("refine") : flowByAction(req.action);
|
|
15872
16139
|
ruleOwnerSays = (owner) => `\`${owner.name}\` agent carries one rule of its own${owner.role ? ", on every flow it runs" : ""}`;
|
|
@@ -15951,8 +16218,8 @@ var init_source = __esm({
|
|
|
15951
16218
|
});
|
|
15952
16219
|
|
|
15953
16220
|
// ../cli/src/lib/card-sources.ts
|
|
15954
|
-
import
|
|
15955
|
-
import
|
|
16221
|
+
import fs41 from "node:fs";
|
|
16222
|
+
import path45 from "node:path";
|
|
15956
16223
|
function planById(id) {
|
|
15957
16224
|
const found2 = readPlan(`plans/${id}-.md`);
|
|
15958
16225
|
return found2?.text.trim() ? found2 : null;
|
|
@@ -15962,8 +16229,8 @@ function cardLink(id) {
|
|
|
15962
16229
|
const found2 = open3 ?? locateArchived(id);
|
|
15963
16230
|
if (!found2) return null;
|
|
15964
16231
|
try {
|
|
15965
|
-
const file = found2.kind === "group" ?
|
|
15966
|
-
const title = parseFrontmatter(
|
|
16232
|
+
const file = found2.kind === "group" ? path45.join(found2.target, "root.md") : found2.target;
|
|
16233
|
+
const title = parseFrontmatter(fs41.readFileSync(file, "utf8")).meta?.title ?? "";
|
|
15967
16234
|
return { kind: "card", ref: String(id), title, ...open3 ? {} : { archived: true } };
|
|
15968
16235
|
} catch {
|
|
15969
16236
|
return null;
|
|
@@ -15988,7 +16255,7 @@ function link(ref, items) {
|
|
|
15988
16255
|
if (ref.kind === "plan") return planLink(ref.id);
|
|
15989
16256
|
if (ref.kind === "card") return cardLink(ref.id);
|
|
15990
16257
|
if (ref.kind === "url") return urlLink(ref.url);
|
|
15991
|
-
const item = items().find((signal2) =>
|
|
16258
|
+
const item = items().find((signal2) => path45.basename(signal2.relPath) === ref.file);
|
|
15992
16259
|
return item ? itemLink(item) : null;
|
|
15993
16260
|
}
|
|
15994
16261
|
function cardSources(meta, body) {
|
|
@@ -16029,12 +16296,12 @@ var init_card_sources = __esm({
|
|
|
16029
16296
|
});
|
|
16030
16297
|
|
|
16031
16298
|
// ../cli/src/lib/view/read.ts
|
|
16032
|
-
import
|
|
16033
|
-
import
|
|
16299
|
+
import fs42 from "node:fs";
|
|
16300
|
+
import path46 from "node:path";
|
|
16034
16301
|
function readCard(file, relFromTodo) {
|
|
16035
|
-
const id = idPrefix(
|
|
16302
|
+
const id = idPrefix(path46.basename(relFromTodo));
|
|
16036
16303
|
if (id === null) {
|
|
16037
|
-
const parts = relFromTodo.split(
|
|
16304
|
+
const parts = relFromTodo.split(path46.sep);
|
|
16038
16305
|
const folderId = idPrefix(parts[parts.length - 2] || "");
|
|
16039
16306
|
if (folderId === null) return null;
|
|
16040
16307
|
return buildCard(folderId, file, relFromTodo);
|
|
@@ -16042,10 +16309,10 @@ function readCard(file, relFromTodo) {
|
|
|
16042
16309
|
return buildCard(id, file, relFromTodo);
|
|
16043
16310
|
}
|
|
16044
16311
|
function buildCard(id, file, relFromTodo) {
|
|
16045
|
-
const text5 =
|
|
16312
|
+
const text5 = fs42.readFileSync(file, "utf8");
|
|
16046
16313
|
const { meta, body } = parseFrontmatter(text5);
|
|
16047
16314
|
if (!meta) return null;
|
|
16048
|
-
const relPath = relFromTodo.split(
|
|
16315
|
+
const relPath = relFromTodo.split(path46.sep).join("/");
|
|
16049
16316
|
const flow = workflowFor(meta.workflow);
|
|
16050
16317
|
const plan = !!meta.workflow && archivesOnApproval(flow);
|
|
16051
16318
|
const stages = cardStages(meta, flow).length;
|
|
@@ -16054,7 +16321,7 @@ function buildCard(id, file, relFromTodo) {
|
|
|
16054
16321
|
// Derived from the file exactly as it is on disk (lib/board/revision.ts), so a card
|
|
16055
16322
|
// read here and a card read by the contract agree on what version this is.
|
|
16056
16323
|
revision: revisionOf(text5),
|
|
16057
|
-
updatedAt:
|
|
16324
|
+
updatedAt: fs42.statSync(file).mtime.toISOString(),
|
|
16058
16325
|
relPath,
|
|
16059
16326
|
title: meta.title,
|
|
16060
16327
|
priority: meta.priority,
|
|
@@ -16075,22 +16342,22 @@ function buildCard(id, file, relFromTodo) {
|
|
|
16075
16342
|
};
|
|
16076
16343
|
}
|
|
16077
16344
|
function readGroup2(folderName) {
|
|
16078
|
-
const groupDir =
|
|
16079
|
-
const rootFile =
|
|
16080
|
-
if (!
|
|
16081
|
-
const root = readCard(rootFile,
|
|
16345
|
+
const groupDir = path46.join(TODO, folderName);
|
|
16346
|
+
const rootFile = path46.join(groupDir, "root.md");
|
|
16347
|
+
if (!fs42.existsSync(rootFile)) return null;
|
|
16348
|
+
const root = readCard(rootFile, path46.join(folderName, "root.md"));
|
|
16082
16349
|
if (!root) return null;
|
|
16083
16350
|
root.isGroup = true;
|
|
16084
16351
|
const { total, resolved: resolved2 } = subtaskLines(root.body);
|
|
16085
16352
|
root.subtaskLines = { total, resolved: resolved2 };
|
|
16086
16353
|
const subCards = [];
|
|
16087
16354
|
const recurse = (dir, relDir) => {
|
|
16088
|
-
for (const entry of
|
|
16089
|
-
const childRel =
|
|
16355
|
+
for (const entry of fs42.readdirSync(dir, { withFileTypes: true })) {
|
|
16356
|
+
const childRel = path46.join(relDir, entry.name);
|
|
16090
16357
|
if (entry.isDirectory()) {
|
|
16091
|
-
recurse(
|
|
16358
|
+
recurse(path46.join(dir, entry.name), childRel);
|
|
16092
16359
|
} else if (entry.name.endsWith(".md") && entry.name !== "README.md" && entry.name !== "root.md") {
|
|
16093
|
-
const c = readCard(
|
|
16360
|
+
const c = readCard(path46.join(dir, entry.name), childRel);
|
|
16094
16361
|
if (c) {
|
|
16095
16362
|
c.parent = { id: root.id, title: root.title };
|
|
16096
16363
|
subCards.push(c);
|
|
@@ -16111,12 +16378,12 @@ function readGroup2(folderName) {
|
|
|
16111
16378
|
return { root, subCards };
|
|
16112
16379
|
}
|
|
16113
16380
|
function standaloneCards(dirRel) {
|
|
16114
|
-
const dir = dirRel ?
|
|
16115
|
-
if (!
|
|
16381
|
+
const dir = dirRel ? path46.join(TODO, dirRel) : TODO;
|
|
16382
|
+
if (!fs42.existsSync(dir)) return [];
|
|
16116
16383
|
const cards = [];
|
|
16117
|
-
for (const entry of
|
|
16384
|
+
for (const entry of fs42.readdirSync(dir, { withFileTypes: true })) {
|
|
16118
16385
|
if (entry.isFile() && entry.name.endsWith(".md") && entry.name !== "README.md") {
|
|
16119
|
-
const c = readCard(
|
|
16386
|
+
const c = readCard(path46.join(dir, entry.name), path46.join(dirRel, entry.name));
|
|
16120
16387
|
if (c) cards.push(c);
|
|
16121
16388
|
}
|
|
16122
16389
|
}
|
|
@@ -16128,7 +16395,7 @@ function collectCards() {
|
|
|
16128
16395
|
const top = standaloneCards("");
|
|
16129
16396
|
board2.push(...top);
|
|
16130
16397
|
every.push(...top);
|
|
16131
|
-
for (const entry of
|
|
16398
|
+
for (const entry of fs42.readdirSync(TODO, { withFileTypes: true })) {
|
|
16132
16399
|
if (!entry.isDirectory()) continue;
|
|
16133
16400
|
if (isGroupFolder(entry.name)) {
|
|
16134
16401
|
const g = readGroup2(entry.name);
|
|
@@ -16160,7 +16427,7 @@ function findCard(id) {
|
|
|
16160
16427
|
}
|
|
16161
16428
|
function attachSources(card) {
|
|
16162
16429
|
try {
|
|
16163
|
-
const { meta, body } = parseFrontmatter(
|
|
16430
|
+
const { meta, body } = parseFrontmatter(fs42.readFileSync(path46.join(TODO, card.relPath), "utf8"));
|
|
16164
16431
|
const sources = meta ? cardSources(meta, body) : [];
|
|
16165
16432
|
if (sources.length) card.sources = sources;
|
|
16166
16433
|
} catch {
|
|
@@ -16348,8 +16615,8 @@ var init_review = __esm({
|
|
|
16348
16615
|
});
|
|
16349
16616
|
|
|
16350
16617
|
// ../cli/src/lib/agent/deliveries.ts
|
|
16351
|
-
import
|
|
16352
|
-
import
|
|
16618
|
+
import fs43 from "node:fs";
|
|
16619
|
+
import path47 from "node:path";
|
|
16353
16620
|
function writeAudit(delivery, runs) {
|
|
16354
16621
|
const known2 = new Map(readAudit(delivery.deliveryId)?.sessions.map((s) => [s.sessionId, s]) ?? []);
|
|
16355
16622
|
const sessions = delivery.sessions.flatMap((id) => {
|
|
@@ -16377,10 +16644,10 @@ function writeAudit(delivery, runs) {
|
|
|
16377
16644
|
}
|
|
16378
16645
|
function writeAuditFile(record) {
|
|
16379
16646
|
try {
|
|
16380
|
-
|
|
16647
|
+
fs43.mkdirSync(DELIVERIES, { recursive: true });
|
|
16381
16648
|
const tmp = `${auditPath(record.deliveryId)}.tmp`;
|
|
16382
|
-
|
|
16383
|
-
|
|
16649
|
+
fs43.writeFileSync(tmp, JSON.stringify(record, null, 2) + "\n");
|
|
16650
|
+
fs43.renameSync(tmp, auditPath(record.deliveryId));
|
|
16384
16651
|
return true;
|
|
16385
16652
|
} catch {
|
|
16386
16653
|
return false;
|
|
@@ -16388,7 +16655,7 @@ function writeAuditFile(record) {
|
|
|
16388
16655
|
}
|
|
16389
16656
|
function readAudit(deliveryId) {
|
|
16390
16657
|
try {
|
|
16391
|
-
const data = JSON.parse(
|
|
16658
|
+
const data = JSON.parse(fs43.readFileSync(auditPath(deliveryId), "utf8"));
|
|
16392
16659
|
return { sessions: Array.isArray(data?.sessions) ? data.sessions : [] };
|
|
16393
16660
|
} catch {
|
|
16394
16661
|
return null;
|
|
@@ -16403,7 +16670,7 @@ function syncAudit(deliveryId, just) {
|
|
|
16403
16670
|
}
|
|
16404
16671
|
function wholeAudit(file) {
|
|
16405
16672
|
try {
|
|
16406
|
-
const data = JSON.parse(
|
|
16673
|
+
const data = JSON.parse(fs43.readFileSync(file, "utf8"));
|
|
16407
16674
|
return typeof data?.deliveryId === "string" && Array.isArray(data.sessions) ? data : null;
|
|
16408
16675
|
} catch {
|
|
16409
16676
|
return null;
|
|
@@ -16416,14 +16683,14 @@ function activeAudit(file) {
|
|
|
16416
16683
|
function allAudits() {
|
|
16417
16684
|
let names4;
|
|
16418
16685
|
try {
|
|
16419
|
-
names4 =
|
|
16686
|
+
names4 = fs43.readdirSync(DELIVERIES);
|
|
16420
16687
|
} catch {
|
|
16421
16688
|
return [];
|
|
16422
16689
|
}
|
|
16423
16690
|
const out = [];
|
|
16424
16691
|
for (const name of names4) {
|
|
16425
16692
|
if (!name.endsWith(".json")) continue;
|
|
16426
|
-
const audit = wholeAudit(
|
|
16693
|
+
const audit = wholeAudit(path47.join(DELIVERIES, name));
|
|
16427
16694
|
if (audit) out.push(audit);
|
|
16428
16695
|
}
|
|
16429
16696
|
return out;
|
|
@@ -16534,11 +16801,11 @@ function resumeRefusal(delivery, store) {
|
|
|
16534
16801
|
);
|
|
16535
16802
|
}
|
|
16536
16803
|
if (!worktreeExists(delivery.worktree)) {
|
|
16537
|
-
const
|
|
16804
|
+
const path87 = delivery.worktree ?? "";
|
|
16538
16805
|
return refusal(
|
|
16539
16806
|
"deliveryWorktreeGone",
|
|
16540
|
-
`delivery ${id}'s worktree ${
|
|
16541
|
-
{ id, task, path:
|
|
16807
|
+
`delivery ${id}'s worktree ${path87} is gone, so there is nothing to carry on. ${rebuild}`,
|
|
16808
|
+
{ id, task, path: path87, command: discard }
|
|
16542
16809
|
);
|
|
16543
16810
|
}
|
|
16544
16811
|
if (!branchExists(delivery.branch)) {
|
|
@@ -16591,11 +16858,12 @@ function tidyCheckout(deliveryId) {
|
|
|
16591
16858
|
const store = readStore();
|
|
16592
16859
|
const row = store.deliveries.find((d) => d.deliveryId === deliveryId) ?? auditOnly(deliveryId);
|
|
16593
16860
|
if (!row || row.status === "active") return NOTHING;
|
|
16594
|
-
if (!row.worktree && !row.branch) return NOTHING;
|
|
16595
|
-
if (isResumable(row)) return { removed: false, kept: true };
|
|
16596
16861
|
if (store.runs.some((r) => r.deliveryId === deliveryId && runIsLive(r))) {
|
|
16597
16862
|
return { removed: false, kept: false, error: `a run of delivery ${deliveryId} is still going`, reason: "deliveryRunGoing", args: { id: deliveryId } };
|
|
16598
16863
|
}
|
|
16864
|
+
if (row.status !== "finished" || Date.now() - (row.endedAt ?? 0) > 3e4) stopWatchers(row.sessions);
|
|
16865
|
+
if (!row.worktree && !row.branch) return NOTHING;
|
|
16866
|
+
if (isResumable(row)) return { removed: false, kept: true };
|
|
16599
16867
|
const removed = removeWorktree(row.worktree, row.branch, true);
|
|
16600
16868
|
if (!removed.ok) return { removed: false, kept: false, error: removed.error };
|
|
16601
16869
|
forgetCheckout(deliveryId);
|
|
@@ -16618,7 +16886,10 @@ function forgetCheckout(deliveryId) {
|
|
|
16618
16886
|
function sweepCheckouts() {
|
|
16619
16887
|
const seen = /* @__PURE__ */ new Set();
|
|
16620
16888
|
const complaints = [];
|
|
16621
|
-
const
|
|
16889
|
+
const store = readStore();
|
|
16890
|
+
const rows = [...store.deliveries, ...allAudits().flatMap((a) => auditRow(a) ?? [])];
|
|
16891
|
+
const ended = rows.filter((row) => row.status !== "active" && (row.status !== "finished" || Date.now() - (row.endedAt ?? 0) > 3e4) && !store.runs.some((r) => r.deliveryId === row.deliveryId && runIsLive(r)));
|
|
16892
|
+
stopWatchers([...new Set(ended.flatMap((row) => row.sessions))]);
|
|
16622
16893
|
for (const row of rows) {
|
|
16623
16894
|
if (seen.has(row.deliveryId) || row.status === "active" || !row.worktree) continue;
|
|
16624
16895
|
seen.add(row.deliveryId);
|
|
@@ -16628,21 +16899,27 @@ function sweepCheckouts() {
|
|
|
16628
16899
|
dropEmptyWorktreeFolders();
|
|
16629
16900
|
return complaints;
|
|
16630
16901
|
}
|
|
16631
|
-
function resumeRecord(id) {
|
|
16632
|
-
const
|
|
16902
|
+
function resumeRecord(id, execution) {
|
|
16903
|
+
const continueActive = !!execution;
|
|
16904
|
+
const found2 = (continueActive ? namedDelivery(id) : void 0) ?? endedDelivery(id);
|
|
16633
16905
|
if (!found2) return { ok: false, ...unknownDelivery(id) };
|
|
16634
16906
|
const refused = resumeRefusal(found2);
|
|
16635
|
-
if (refused) return { ok: false, ...refused };
|
|
16907
|
+
if (refused && !(continueActive && found2.status === "active")) return { ok: false, ...refused };
|
|
16636
16908
|
const out = withStore((store) => {
|
|
16637
16909
|
const audit = wholeAudit(auditPath(found2.deliveryId));
|
|
16638
|
-
const
|
|
16910
|
+
const live = store.deliveries.find((d) => d.deliveryId === found2.deliveryId);
|
|
16911
|
+
const row = continueActive && live?.status === "active" ? live : (audit && auditRow(audit)) ?? live;
|
|
16639
16912
|
if (!row) return { ok: false, ...unknownDelivery(id) };
|
|
16640
16913
|
const again = resumeRefusal(row, store);
|
|
16641
|
-
if (
|
|
16914
|
+
if (continueActive && row.status === "active") {
|
|
16915
|
+
if (store.runs.some((r) => r.deliveryId === row.deliveryId && runIsLive(r))) {
|
|
16916
|
+
return { ok: false, ...refusal("deliveryRunGoing", "the delivery still has a running agent; stop it before continuing") };
|
|
16917
|
+
}
|
|
16918
|
+
} else if (again) return { ok: false, ...again };
|
|
16642
16919
|
const at = Date.now();
|
|
16643
16920
|
const resumed = {
|
|
16644
16921
|
...row,
|
|
16645
|
-
cloudExecution: executionOf() ?? row.cloudExecution,
|
|
16922
|
+
cloudExecution: execution ?? executionOf() ?? row.cloudExecution,
|
|
16646
16923
|
status: "active",
|
|
16647
16924
|
endedAt: void 0,
|
|
16648
16925
|
steps: [...row.steps, { step: "resume", at }],
|
|
@@ -16671,10 +16948,10 @@ function carryOnFrom(deliveryId) {
|
|
|
16671
16948
|
function approvedRequirements(cardId2) {
|
|
16672
16949
|
const found2 = locate(cardId2);
|
|
16673
16950
|
if (!found2) return "";
|
|
16674
|
-
const file = found2.kind === "group" ?
|
|
16951
|
+
const file = found2.kind === "group" ? path47.join(found2.target, "root.md") : found2.target;
|
|
16675
16952
|
let text5;
|
|
16676
16953
|
try {
|
|
16677
|
-
text5 =
|
|
16954
|
+
text5 = fs43.readFileSync(file, "utf8");
|
|
16678
16955
|
} catch {
|
|
16679
16956
|
return "";
|
|
16680
16957
|
}
|
|
@@ -16704,8 +16981,8 @@ function cardMeta2(cardId2) {
|
|
|
16704
16981
|
try {
|
|
16705
16982
|
const found2 = locate(cardId2);
|
|
16706
16983
|
if (!found2) return null;
|
|
16707
|
-
const file = found2.kind === "group" ?
|
|
16708
|
-
return parseFrontmatter(
|
|
16984
|
+
const file = found2.kind === "group" ? path47.join(found2.target, "root.md") : found2.target;
|
|
16985
|
+
return parseFrontmatter(fs43.readFileSync(file, "utf8")).meta;
|
|
16709
16986
|
} catch {
|
|
16710
16987
|
return null;
|
|
16711
16988
|
}
|
|
@@ -16954,6 +17231,7 @@ var init_deliveries = __esm({
|
|
|
16954
17231
|
init_complete();
|
|
16955
17232
|
init_outputs();
|
|
16956
17233
|
init_env();
|
|
17234
|
+
init_stop();
|
|
16957
17235
|
init_flows();
|
|
16958
17236
|
init_pause();
|
|
16959
17237
|
init_rules2();
|
|
@@ -16963,7 +17241,7 @@ var init_deliveries = __esm({
|
|
|
16963
17241
|
init_store();
|
|
16964
17242
|
init_types3();
|
|
16965
17243
|
init_worktree();
|
|
16966
|
-
auditPath = (deliveryId) =>
|
|
17244
|
+
auditPath = (deliveryId) => path47.join(DELIVERIES, `${deliveryId}.json`);
|
|
16967
17245
|
activeAudits = () => allAudits().filter((a) => a.status === "active");
|
|
16968
17246
|
auditRow = (audit) => readDeliveryRow({ ...audit, sessions: audit.sessions.map((s) => s.sessionId) });
|
|
16969
17247
|
lastSessionRunning = (audit) => audit.sessions[audit.sessions.length - 1]?.status === "running";
|
|
@@ -16991,6 +17269,41 @@ var init_deliveries = __esm({
|
|
|
16991
17269
|
}
|
|
16992
17270
|
});
|
|
16993
17271
|
|
|
17272
|
+
// ../cli/src/lib/agent/landing-lock.ts
|
|
17273
|
+
import fs44 from "node:fs";
|
|
17274
|
+
import path48 from "node:path";
|
|
17275
|
+
function lockLanding() {
|
|
17276
|
+
const dir = path48.join(SESSIONS_DIR, ".landing.lock");
|
|
17277
|
+
try {
|
|
17278
|
+
const entries = fs44.readdirSync(dir);
|
|
17279
|
+
const token3 = entries.length === 1 ? entries[0] : "";
|
|
17280
|
+
const match = /^(\d+)-[a-f0-9-]{36}$/.exec(token3);
|
|
17281
|
+
const stat = fs44.statSync(dir);
|
|
17282
|
+
if (match && Date.now() - stat.mtimeMs > landingLimits.staleMs) {
|
|
17283
|
+
stopWatchers(void 0, Number(match[1]), () => {
|
|
17284
|
+
const now = fs44.readdirSync(dir);
|
|
17285
|
+
return now.length === 1 && now[0] === token3 && Date.now() - fs44.statSync(dir).mtimeMs > landingLimits.staleMs;
|
|
17286
|
+
}, stat.birthtimeMs || stat.mtimeMs);
|
|
17287
|
+
}
|
|
17288
|
+
} catch {
|
|
17289
|
+
}
|
|
17290
|
+
return tryLock(dir);
|
|
17291
|
+
}
|
|
17292
|
+
function renewLandingLock() {
|
|
17293
|
+
const now = /* @__PURE__ */ new Date();
|
|
17294
|
+
fs44.utimesSync(path48.join(SESSIONS_DIR, ".landing.lock"), now, now);
|
|
17295
|
+
}
|
|
17296
|
+
var landingLimits;
|
|
17297
|
+
var init_landing_lock = __esm({
|
|
17298
|
+
"../cli/src/lib/agent/landing-lock.ts"() {
|
|
17299
|
+
"use strict";
|
|
17300
|
+
init_lock();
|
|
17301
|
+
init_paths();
|
|
17302
|
+
init_stop();
|
|
17303
|
+
landingLimits = { staleMs: 5 * 6e4 };
|
|
17304
|
+
}
|
|
17305
|
+
});
|
|
17306
|
+
|
|
16994
17307
|
// ../cli/src/lib/agent/answers.ts
|
|
16995
17308
|
function answerOutcome(delivery) {
|
|
16996
17309
|
return pendingAnswers(delivery).some((a) => a.outcome === "changed") ? "changed" : "unchanged";
|
|
@@ -17055,8 +17368,8 @@ var init_retry = __esm({
|
|
|
17055
17368
|
});
|
|
17056
17369
|
|
|
17057
17370
|
// ../cli/src/lib/agent/landing.ts
|
|
17058
|
-
import
|
|
17059
|
-
import
|
|
17371
|
+
import fs45 from "node:fs";
|
|
17372
|
+
import path49 from "node:path";
|
|
17060
17373
|
function waitingOut(delivery, now = Date.now()) {
|
|
17061
17374
|
const landing = delivery.landing;
|
|
17062
17375
|
if (!landing) return false;
|
|
@@ -17192,8 +17505,9 @@ function noteQueue(held4) {
|
|
|
17192
17505
|
}
|
|
17193
17506
|
async function advanceLanding() {
|
|
17194
17507
|
let release;
|
|
17508
|
+
let current;
|
|
17195
17509
|
try {
|
|
17196
|
-
release =
|
|
17510
|
+
release = lockLanding();
|
|
17197
17511
|
if (!release) return null;
|
|
17198
17512
|
const owed = await owedRestart();
|
|
17199
17513
|
if (owed) return owed;
|
|
@@ -17208,11 +17522,19 @@ async function advanceLanding() {
|
|
|
17208
17522
|
return null;
|
|
17209
17523
|
}
|
|
17210
17524
|
tried.add(picked.deliveryId);
|
|
17525
|
+
current = picked;
|
|
17526
|
+
renewLandingLock();
|
|
17211
17527
|
const step = await landStep(picked);
|
|
17212
17528
|
if (step.start) return step.start;
|
|
17213
17529
|
if (!step.done) return null;
|
|
17214
17530
|
}
|
|
17215
|
-
} catch {
|
|
17531
|
+
} catch (error) {
|
|
17532
|
+
if (current && error instanceof GitTimeoutError) {
|
|
17533
|
+
giveUpSlot(current, error.message);
|
|
17534
|
+
patchLanding(current.deliveryId, (landing) => {
|
|
17535
|
+
landing.retryAt = Date.now() + 3e4;
|
|
17536
|
+
});
|
|
17537
|
+
}
|
|
17216
17538
|
return null;
|
|
17217
17539
|
} finally {
|
|
17218
17540
|
release?.();
|
|
@@ -17529,7 +17851,7 @@ async function settleAlreadyLanded(delivery) {
|
|
|
17529
17851
|
return true;
|
|
17530
17852
|
}
|
|
17531
17853
|
function repairLanding() {
|
|
17532
|
-
const release =
|
|
17854
|
+
const release = lockLanding();
|
|
17533
17855
|
if (!release) return [];
|
|
17534
17856
|
try {
|
|
17535
17857
|
return repairIdleLanding();
|
|
@@ -17562,7 +17884,7 @@ var init_landing = __esm({
|
|
|
17562
17884
|
init_execution();
|
|
17563
17885
|
init_board();
|
|
17564
17886
|
init_io();
|
|
17565
|
-
|
|
17887
|
+
init_landing_lock();
|
|
17566
17888
|
init_paths();
|
|
17567
17889
|
init_answers();
|
|
17568
17890
|
init_commit_mode();
|
|
@@ -17603,9 +17925,9 @@ var init_landing = __esm({
|
|
|
17603
17925
|
sameDir = (a, b) => {
|
|
17604
17926
|
const real2 = (dir) => {
|
|
17605
17927
|
try {
|
|
17606
|
-
return
|
|
17928
|
+
return fs45.realpathSync(dir);
|
|
17607
17929
|
} catch {
|
|
17608
|
-
return
|
|
17930
|
+
return path49.resolve(dir);
|
|
17609
17931
|
}
|
|
17610
17932
|
};
|
|
17611
17933
|
return real2(a) === real2(b);
|
|
@@ -17614,35 +17936,35 @@ var init_landing = __esm({
|
|
|
17614
17936
|
});
|
|
17615
17937
|
|
|
17616
17938
|
// ../cli/src/lib/agent/pictures.ts
|
|
17617
|
-
import
|
|
17618
|
-
import
|
|
17939
|
+
import fs46 from "node:fs";
|
|
17940
|
+
import path50 from "node:path";
|
|
17619
17941
|
function claimRunPictures(box2, sessionId, names4 = []) {
|
|
17620
17942
|
if (!box2 || !freeBox(box2) || names4.length === 0) return [];
|
|
17621
17943
|
const from = pictureBox(box2);
|
|
17622
17944
|
const to = pictureBox(sessionId);
|
|
17623
17945
|
try {
|
|
17624
|
-
|
|
17625
|
-
|
|
17946
|
+
fs46.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
17947
|
+
fs46.renameSync(from, to);
|
|
17626
17948
|
} catch {
|
|
17627
17949
|
return [];
|
|
17628
17950
|
}
|
|
17629
|
-
return names4.filter(pictureName).map((name) =>
|
|
17951
|
+
return names4.filter(pictureName).map((name) => path50.join(to, name)).filter((file) => fs46.existsSync(file));
|
|
17630
17952
|
}
|
|
17631
17953
|
function returnRunPictures(sessionId, box2) {
|
|
17632
17954
|
if (!box2 || !boxId(box2)) return;
|
|
17633
17955
|
try {
|
|
17634
|
-
|
|
17956
|
+
fs46.renameSync(pictureBox(sessionId), pictureBox(box2));
|
|
17635
17957
|
} catch {
|
|
17636
17958
|
}
|
|
17637
17959
|
}
|
|
17638
17960
|
function moveRunPictures(from, to, files2 = []) {
|
|
17639
17961
|
if (files2.length === 0) return [];
|
|
17640
17962
|
try {
|
|
17641
|
-
|
|
17963
|
+
fs46.renameSync(pictureBox(from), pictureBox(to));
|
|
17642
17964
|
} catch {
|
|
17643
17965
|
return files2;
|
|
17644
17966
|
}
|
|
17645
|
-
return files2.map((file) =>
|
|
17967
|
+
return files2.map((file) => path50.join(pictureBox(to), path50.basename(file)));
|
|
17646
17968
|
}
|
|
17647
17969
|
var pictureName, boxId, freeBox, pictureBox;
|
|
17648
17970
|
var init_pictures = __esm({
|
|
@@ -17651,14 +17973,14 @@ var init_pictures = __esm({
|
|
|
17651
17973
|
init_paths();
|
|
17652
17974
|
pictureName = (name) => /^[0-9a-f-]{36}\.[a-z0-9]{2,5}$/.test(name);
|
|
17653
17975
|
boxId = (box2) => /^[0-9a-f-]{36}$/.test(box2);
|
|
17654
|
-
freeBox = (box2) => boxId(box2) && !
|
|
17655
|
-
pictureBox = (box2) =>
|
|
17976
|
+
freeBox = (box2) => boxId(box2) && !fs46.existsSync(path50.join(SESSIONS_DIR, `${box2}.log`));
|
|
17977
|
+
pictureBox = (box2) => path50.join(SESSIONS_DIR, `${box2}.images`);
|
|
17656
17978
|
}
|
|
17657
17979
|
});
|
|
17658
17980
|
|
|
17659
17981
|
// ../cli/src/lib/memories.ts
|
|
17660
|
-
import
|
|
17661
|
-
import
|
|
17982
|
+
import fs47 from "node:fs";
|
|
17983
|
+
import path51 from "node:path";
|
|
17662
17984
|
function readMemory(text5, builtIn, folder) {
|
|
17663
17985
|
const { meta, body } = splitFrontmatter(text5);
|
|
17664
17986
|
if (meta === null) return null;
|
|
@@ -17678,7 +18000,7 @@ function readMemory(text5, builtIn, folder) {
|
|
|
17678
18000
|
rules: body.trim(),
|
|
17679
18001
|
text: text5,
|
|
17680
18002
|
builtIn,
|
|
17681
|
-
dir:
|
|
18003
|
+
dir: path51.join(MEMORY, folder)
|
|
17682
18004
|
};
|
|
17683
18005
|
}
|
|
17684
18006
|
function globalMemories() {
|
|
@@ -17689,14 +18011,14 @@ function globalMemories() {
|
|
|
17689
18011
|
}
|
|
17690
18012
|
let folders2 = [];
|
|
17691
18013
|
try {
|
|
17692
|
-
folders2 =
|
|
18014
|
+
folders2 = fs47.readdirSync(MEMORY, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
17693
18015
|
} catch {
|
|
17694
18016
|
}
|
|
17695
18017
|
for (const name of folders2) {
|
|
17696
18018
|
if (name in BUNDLED_MEMORY_FILES || name === RESERVED_MEMORY_DIR) continue;
|
|
17697
|
-
const file =
|
|
17698
|
-
if (!
|
|
17699
|
-
const memory = readMemory(
|
|
18019
|
+
const file = path51.join(MEMORY, name, GLOBAL_MEMORY_FILE);
|
|
18020
|
+
if (!fs47.existsSync(file)) continue;
|
|
18021
|
+
const memory = readMemory(fs47.readFileSync(file, "utf8"), false, name);
|
|
17700
18022
|
if (memory) out.push(memory);
|
|
17701
18023
|
}
|
|
17702
18024
|
return out;
|
|
@@ -17752,16 +18074,16 @@ var init_facts = __esm({
|
|
|
17752
18074
|
});
|
|
17753
18075
|
|
|
17754
18076
|
// ../cli/src/lib/view/archive.ts
|
|
17755
|
-
import
|
|
17756
|
-
import
|
|
18077
|
+
import fs48 from "node:fs";
|
|
18078
|
+
import path52 from "node:path";
|
|
17757
18079
|
function archivedId(file) {
|
|
17758
|
-
const own = idPrefix(
|
|
17759
|
-
return own !== null ? own : idPrefix(
|
|
18080
|
+
const own = idPrefix(path52.basename(file));
|
|
18081
|
+
return own !== null ? own : idPrefix(path52.basename(path52.dirname(file)));
|
|
17760
18082
|
}
|
|
17761
18083
|
function readFile(file) {
|
|
17762
18084
|
const id = archivedId(file);
|
|
17763
18085
|
if (id === null) return null;
|
|
17764
|
-
const { meta, body } = parseFrontmatter(
|
|
18086
|
+
const { meta, body } = parseFrontmatter(fs48.readFileSync(file, "utf8"));
|
|
17765
18087
|
if (!meta) return null;
|
|
17766
18088
|
return {
|
|
17767
18089
|
row: {
|
|
@@ -17769,15 +18091,15 @@ function readFile(file) {
|
|
|
17769
18091
|
title: meta.title,
|
|
17770
18092
|
archived: meta.archived,
|
|
17771
18093
|
rejected: meta.rejected,
|
|
17772
|
-
relPath: rel(file).split(
|
|
18094
|
+
relPath: rel(file).split(path52.sep).join("/")
|
|
17773
18095
|
},
|
|
17774
18096
|
body: body.replace(/^\n+/, "").replace(/\s+$/, ""),
|
|
17775
18097
|
meta
|
|
17776
18098
|
};
|
|
17777
18099
|
}
|
|
17778
18100
|
function readArchive() {
|
|
17779
|
-
const relPath = rel(ARCHIVE).split(
|
|
17780
|
-
if (!
|
|
18101
|
+
const relPath = rel(ARCHIVE).split(path52.sep).join("/");
|
|
18102
|
+
if (!fs48.existsSync(ARCHIVE)) return { relPath, cards: [] };
|
|
17781
18103
|
const cards = walkMd(ARCHIVE).map(readFile).filter((found2) => found2 !== null).map((found2) => found2.row);
|
|
17782
18104
|
cards.sort((a, b) => b.id - a.id);
|
|
17783
18105
|
return { relPath, cards };
|
|
@@ -17792,7 +18114,7 @@ var init_archive = __esm({
|
|
|
17792
18114
|
});
|
|
17793
18115
|
|
|
17794
18116
|
// ../cli/src/lib/agent/prompts.ts
|
|
17795
|
-
import
|
|
18117
|
+
import path53 from "node:path";
|
|
17796
18118
|
function resumePrompt(deliveryId, cardId2, action) {
|
|
17797
18119
|
if (!deliveryId) return RESUME_PROMPT;
|
|
17798
18120
|
if (action === "conflict") {
|
|
@@ -17836,7 +18158,7 @@ function buildAsk(rawReq, notes = []) {
|
|
|
17836
18158
|
}
|
|
17837
18159
|
function cardFileOf(id) {
|
|
17838
18160
|
const found2 = id === void 0 ? null : locate(id);
|
|
17839
|
-
return found2 ? found2.kind === "group" ?
|
|
18161
|
+
return found2 ? found2.kind === "group" ? path53.join(found2.target, "root.md") : found2.target : "";
|
|
17840
18162
|
}
|
|
17841
18163
|
function realPaths(text5, req) {
|
|
17842
18164
|
if (req.action !== "scheduled" && req.action !== "sub") return text5;
|
|
@@ -17929,7 +18251,7 @@ function frozenRules(req) {
|
|
|
17929
18251
|
function archivedCardFile(id) {
|
|
17930
18252
|
const found2 = locateArchived(id);
|
|
17931
18253
|
if (!found2) return `${rel(ARCHIVE)}/`;
|
|
17932
|
-
return rel(found2.kind === "group" ?
|
|
18254
|
+
return rel(found2.kind === "group" ? path53.join(found2.target, "root.md") : found2.target);
|
|
17933
18255
|
}
|
|
17934
18256
|
function shippedLines(cardId2) {
|
|
17935
18257
|
const delivery = endedDelivery(`${cardId2}`);
|
|
@@ -18100,7 +18422,7 @@ function actionPrompt(req, command, notes) {
|
|
|
18100
18422
|
case "spec": {
|
|
18101
18423
|
const agent = findSpecAgent(req.specAgent ?? "");
|
|
18102
18424
|
const found2 = req.id === void 0 ? null : locate(req.id);
|
|
18103
|
-
const cardFile3 = found2 ? rel(found2.kind === "group" ?
|
|
18425
|
+
const cardFile3 = found2 ? rel(found2.kind === "group" ? path53.join(found2.target, "root.md") : found2.target) : `task #${req.id}`;
|
|
18104
18426
|
const own = agent ? specAgentInstructions(agent) : null;
|
|
18105
18427
|
if (own) notes.push(...own.notes);
|
|
18106
18428
|
const memory = agent ? agentMemoryBlock(agent) : "";
|
|
@@ -18212,289 +18534,82 @@ ${shared}` : ""
|
|
|
18212
18534
|
`${kb}. Task ${req.id} ${named2} has been approved: write the previews that were waiting on that Approve from the card as it stands.`,
|
|
18213
18535
|
req.notes ? `Extra notes: ${req.notes}` : "",
|
|
18214
18536
|
`Don't ask me questions with human-in-the-loop. Leave any questions as open questions.`
|
|
18215
|
-
].filter(Boolean).join(" ");
|
|
18216
|
-
case "resolve":
|
|
18217
|
-
return [
|
|
18218
|
-
`${kb}. Apply my answers to the open questions on task ${req.id} ${named2} following \`akb guide resolve\`.`,
|
|
18219
|
-
carriedNote(req),
|
|
18220
|
-
answeredNote(req.id, command),
|
|
18221
|
-
req.notes ? `Extra notes: ${req.notes}` : ""
|
|
18222
|
-
].filter(Boolean).join(" ");
|
|
18223
|
-
}
|
|
18224
|
-
}
|
|
18225
|
-
function discardedCardsPrompt(cards = []) {
|
|
18226
|
-
if (!cards.length) return "";
|
|
18227
|
-
return "The user discarded the cards listed below. Their absence is intentional. Do not restore or recreate them, including under new IDs or filenames. Continue the remaining work from the current board, preserving the other cards.\n\n" + cards.map((c) => `- #${c.id}: ${c.path}`).join("\n");
|
|
18228
|
-
}
|
|
18229
|
-
var RESUME_PROMPT, DELIVERY_RESUME, DELIVERY_RESUME_CARD, RESTARTABLE, RESTART_LEAD, restartable, NO_IMPLEMENT, BOARD_FOLDER, CARD_FILE, PROJECT_FOLDER, PLANNING_FOR, SPEC_SELECTOR_FOR, reflectedCards;
|
|
18230
|
-
var init_prompts = __esm({
|
|
18231
|
-
"../cli/src/lib/agent/prompts.ts"() {
|
|
18232
|
-
"use strict";
|
|
18233
|
-
init_cards();
|
|
18234
|
-
init_memory();
|
|
18235
|
-
init_memories();
|
|
18236
|
-
init_paths();
|
|
18237
|
-
init_agents();
|
|
18238
|
-
init_command();
|
|
18239
|
-
init_deliveries();
|
|
18240
|
-
init_env();
|
|
18241
|
-
init_facts();
|
|
18242
|
-
init_outputs();
|
|
18243
|
-
init_archive();
|
|
18244
|
-
init_flows();
|
|
18245
|
-
init_resolve();
|
|
18246
|
-
init_runner();
|
|
18247
|
-
init_workflows();
|
|
18248
|
-
init_catalog2();
|
|
18249
|
-
init_roles();
|
|
18250
|
-
init_rules2();
|
|
18251
|
-
init_settings2();
|
|
18252
|
-
init_types3();
|
|
18253
|
-
RESUME_PROMPT = [
|
|
18254
|
-
`Continue. The previous run ended before it finished the task.`,
|
|
18255
|
-
`Pick up where you left off and carry it through.`
|
|
18256
|
-
].join(" ");
|
|
18257
|
-
DELIVERY_RESUME = [
|
|
18258
|
-
`Continue delivery %s. The previous run ended before the delivery finished.`,
|
|
18259
|
-
`Re-enter the flow from the top and check each step's precondition before you do it \u2014 work that is already done is done, so do not repeat it.`
|
|
18260
|
-
].join(" ");
|
|
18261
|
-
DELIVERY_RESUME_CARD = `Build the card as the delivery holds it, not as the file reads now: \`%c\` prints the approved copy.`;
|
|
18262
|
-
RESTARTABLE = /* @__PURE__ */ new Set([
|
|
18263
|
-
"implement",
|
|
18264
|
-
"clarify",
|
|
18265
|
-
"resolve",
|
|
18266
|
-
"approve",
|
|
18267
|
-
"archive",
|
|
18268
|
-
"spec",
|
|
18269
|
-
"conflict",
|
|
18270
|
-
"scheduled"
|
|
18271
|
-
]);
|
|
18272
|
-
RESTART_LEAD = [
|
|
18273
|
-
`The session you were continuing is gone, and this one holds nothing of it.`,
|
|
18274
|
-
`Do the task below from the top, checking each step's precondition before you do it \u2014 work an earlier run already finished is done, so don't repeat it.`
|
|
18275
|
-
].join(" ");
|
|
18276
|
-
restartable = (run2) => (run2.cardId !== null || !!run2.deliveryId) && RESTARTABLE.has(run2.action);
|
|
18277
|
-
NO_IMPLEMENT = `(Unless the request explicitly asks for implementation, don't implement it; revising the card's drafts is not implementing.)`;
|
|
18278
|
-
BOARD_FOLDER = "<board folder>";
|
|
18279
|
-
CARD_FILE = "<card file>";
|
|
18280
|
-
PROJECT_FOLDER = "<project folder>";
|
|
18281
|
-
PLANNING_FOR = /* @__PURE__ */ new Set(["create", "clarify", "resolve", "approve", "edit"]);
|
|
18282
|
-
SPEC_SELECTOR_FOR = /* @__PURE__ */ new Set(["clarify", "resolve", "approve", "edit"]);
|
|
18283
|
-
reflectedCards = (req) => req.cards ?? (req.id !== void 0 ? [req.id] : []);
|
|
18284
|
-
}
|
|
18285
|
-
});
|
|
18286
|
-
|
|
18287
|
-
// ../cli/src/lib/agent/stop.ts
|
|
18288
|
-
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
18289
|
-
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
18290
|
-
import fs47 from "node:fs";
|
|
18291
|
-
import os6 from "node:os";
|
|
18292
|
-
function markedInProc(entry, strict = false) {
|
|
18293
|
-
const pids = [];
|
|
18294
|
-
for (const name of fs47.readdirSync("/proc")) {
|
|
18295
|
-
if (!/^\d+$/.test(name)) continue;
|
|
18296
|
-
try {
|
|
18297
|
-
if (fs47.readFileSync(`/proc/${name}/environ`, "utf8").split("\0").includes(entry)) pids.push(Number(name));
|
|
18298
|
-
} catch (error) {
|
|
18299
|
-
if (strict && !["ENOENT", "ESRCH"].includes(error.code ?? "")) {
|
|
18300
|
-
const stat = fs47.statSync(`/proc/${name}`, { throwIfNoEntry: false });
|
|
18301
|
-
if (stat?.uid === process.getuid?.()) throw error;
|
|
18302
|
-
}
|
|
18303
|
-
}
|
|
18304
|
-
}
|
|
18305
|
-
return pids;
|
|
18306
|
-
}
|
|
18307
|
-
function markedInPs(entry, roots) {
|
|
18308
|
-
const ps = spawnSync4("ps", ["-A", "-E", "-ww", "-o", "pid=,ppid=,command="], {
|
|
18309
|
-
encoding: "utf8",
|
|
18310
|
-
maxBuffer: 64 * 1024 * 1024,
|
|
18311
|
-
timeout: 5e3
|
|
18312
|
-
});
|
|
18313
|
-
if (ps.status !== 0 || !ps.stdout) throw new Error("\u65E0\u6CD5\u67E5\u8BE2\u8FDB\u7A0B\u6811\u3002");
|
|
18314
|
-
const children = /* @__PURE__ */ new Map();
|
|
18315
|
-
const listed2 = /* @__PURE__ */ new Set();
|
|
18316
|
-
const found2 = /* @__PURE__ */ new Set();
|
|
18317
|
-
for (const line of ps.stdout.split("\n")) {
|
|
18318
|
-
const m = /^\s*(\d+)\s+(\d+)\s(.*)$/.exec(line);
|
|
18319
|
-
if (!m) continue;
|
|
18320
|
-
const pid = Number(m[1]);
|
|
18321
|
-
const ppid = Number(m[2]);
|
|
18322
|
-
listed2.add(pid);
|
|
18323
|
-
children.set(ppid, [...children.get(ppid) ?? [], pid]);
|
|
18324
|
-
if (`${m[3]} `.includes(` ${entry} `)) found2.add(pid);
|
|
18325
|
-
}
|
|
18326
|
-
const queue = [...found2, ...roots.filter((pid) => listed2.has(pid))];
|
|
18327
|
-
for (const pid of queue) {
|
|
18328
|
-
found2.add(pid);
|
|
18329
|
-
for (const child of children.get(pid) ?? []) if (!found2.has(child)) queue.push(child);
|
|
18330
|
-
}
|
|
18331
|
-
return [...found2];
|
|
18332
|
-
}
|
|
18333
|
-
function marked(mark, roots = []) {
|
|
18334
|
-
if (WINDOWS3 || !mark.value) return [];
|
|
18335
|
-
const entry = `${mark.name}=${mark.value}`;
|
|
18336
|
-
try {
|
|
18337
|
-
const pids = process.platform === "linux" ? markedInProc(entry) : markedInPs(entry, roots);
|
|
18338
|
-
return pids.filter((pid) => pid !== process.pid);
|
|
18339
|
-
} catch {
|
|
18340
|
-
return [];
|
|
18341
|
-
}
|
|
18342
|
-
}
|
|
18343
|
-
function signal(pids, sig) {
|
|
18344
|
-
for (const pid of pids) {
|
|
18345
|
-
try {
|
|
18346
|
-
process.kill(pid, sig);
|
|
18347
|
-
} catch {
|
|
18348
|
-
}
|
|
18349
|
-
}
|
|
18350
|
-
}
|
|
18351
|
-
function startedUnder(list, root, since) {
|
|
18352
|
-
const self = list.procs.find((p) => p.pid === root);
|
|
18353
|
-
const from = since ?? self?.startedAt;
|
|
18354
|
-
if (from === void 0 || list.bootedAt > from) return [];
|
|
18355
|
-
if (self && self.startedAt > from) return [];
|
|
18356
|
-
const found2 = self ? [root] : [];
|
|
18357
|
-
const seen = /* @__PURE__ */ new Set([root]);
|
|
18358
|
-
const queue = [{ pid: root, startedAt: from }];
|
|
18359
|
-
for (const parent of queue) {
|
|
18360
|
-
for (const p of list.procs) {
|
|
18361
|
-
if (p.ppid !== parent.pid || seen.has(p.pid) || p.startedAt < parent.startedAt) continue;
|
|
18362
|
-
seen.add(p.pid);
|
|
18363
|
-
found2.push(p.pid);
|
|
18364
|
-
queue.push(p);
|
|
18365
|
-
}
|
|
18366
|
-
}
|
|
18367
|
-
return found2.reverse();
|
|
18368
|
-
}
|
|
18369
|
-
function readProcList(out) {
|
|
18370
|
-
const procs = [];
|
|
18371
|
-
let bootedAt;
|
|
18372
|
-
for (const line of out.split(/\r?\n/)) {
|
|
18373
|
-
const boot = /^boot (\d+)$/.exec(line.trim());
|
|
18374
|
-
if (boot) bootedAt = Number(boot[1]);
|
|
18375
|
-
const m = /^(\d+) (\d+) (\d+)$/.exec(line.trim());
|
|
18376
|
-
if (m) procs.push({ pid: Number(m[1]), ppid: Number(m[2]), startedAt: Number(m[3]) });
|
|
18377
|
-
}
|
|
18378
|
-
return bootedAt !== void 0 && procs.length ? { procs, bootedAt } : void 0;
|
|
18379
|
-
}
|
|
18380
|
-
function procsOnWindows() {
|
|
18381
|
-
const ps = spawnSync4("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", LIST_PROCS], {
|
|
18382
|
-
encoding: "utf8",
|
|
18383
|
-
maxBuffer: 64 * 1024 * 1024,
|
|
18384
|
-
timeout: 5e3,
|
|
18385
|
-
windowsHide: true
|
|
18386
|
-
});
|
|
18387
|
-
return ps.status === 0 && ps.stdout ? readProcList(ps.stdout) : void 0;
|
|
18388
|
-
}
|
|
18389
|
-
function killUnderOnWindows(pid, since) {
|
|
18390
|
-
if (!WINDOWS3 || !pid) return;
|
|
18391
|
-
const list = procsOnWindows();
|
|
18392
|
-
if (!list) return;
|
|
18393
|
-
const pids = startedUnder(list, pid, since).filter((p) => p !== process.pid);
|
|
18394
|
-
if (!pids.length) return;
|
|
18395
|
-
spawnSync4("taskkill", ["/F", ...pids.flatMap((p) => ["/PID", String(p)])], { stdio: "ignore", windowsHide: true, timeout: 1e4 });
|
|
18396
|
-
}
|
|
18397
|
-
function killMarked(mark, agent) {
|
|
18398
|
-
if (WINDOWS3) {
|
|
18399
|
-
if (agent) killUnderOnWindows(agent.pid, agent.since);
|
|
18400
|
-
return;
|
|
18401
|
-
}
|
|
18402
|
-
signal(marked(mark), "SIGKILL");
|
|
18403
|
-
}
|
|
18404
|
-
function killTreeOnWindows(pid) {
|
|
18405
|
-
if (!WINDOWS3 || !pid) return false;
|
|
18406
|
-
const out = spawnSync4("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true, timeout: 1e4 });
|
|
18407
|
-
return out.status === 0;
|
|
18408
|
-
}
|
|
18409
|
-
function later(ms, fn) {
|
|
18410
|
-
const t = setTimeout(fn, ms);
|
|
18411
|
-
if (typeof t.unref === "function") t.unref();
|
|
18412
|
-
}
|
|
18413
|
-
function endAgent(child, mark, graceMs) {
|
|
18414
|
-
const term = (sig) => {
|
|
18415
|
-
try {
|
|
18416
|
-
child.kill(sig);
|
|
18417
|
-
} catch {
|
|
18418
|
-
}
|
|
18419
|
-
};
|
|
18420
|
-
if (killTreeOnWindows(child.pid)) return;
|
|
18421
|
-
killUnderOnWindows(child.pid);
|
|
18422
|
-
const seen = new Set(marked(mark, child.pid ? [child.pid] : []));
|
|
18423
|
-
term("SIGTERM");
|
|
18424
|
-
signal([...seen].filter((pid) => pid !== child.pid), "SIGTERM");
|
|
18425
|
-
const round = (left) => {
|
|
18426
|
-
const pids = marked(mark, [...seen]);
|
|
18427
|
-
signal(pids, "SIGKILL");
|
|
18428
|
-
for (const pid of pids) seen.add(pid);
|
|
18429
|
-
if (pids.length && left > 1) later(KILL_ROUND_MS, () => round(left - 1));
|
|
18430
|
-
};
|
|
18431
|
-
later(graceMs, () => {
|
|
18432
|
-
term("SIGKILL");
|
|
18433
|
-
round(KILL_ROUNDS);
|
|
18434
|
-
});
|
|
18435
|
-
}
|
|
18436
|
-
function killRunning() {
|
|
18437
|
-
for (const [child, mark] of running) {
|
|
18438
|
-
if (killTreeOnWindows(child.pid)) continue;
|
|
18439
|
-
killUnderOnWindows(child.pid);
|
|
18440
|
-
signal(marked(mark, child.pid ? [child.pid] : []), "SIGKILL");
|
|
18441
|
-
try {
|
|
18442
|
-
child.kill("SIGKILL");
|
|
18443
|
-
} catch {
|
|
18444
|
-
}
|
|
18537
|
+
].filter(Boolean).join(" ");
|
|
18538
|
+
case "resolve":
|
|
18539
|
+
return [
|
|
18540
|
+
`${kb}. Apply my answers to the open questions on task ${req.id} ${named2} following \`akb guide resolve\`.`,
|
|
18541
|
+
carriedNote(req),
|
|
18542
|
+
answeredNote(req.id, command),
|
|
18543
|
+
req.notes ? `Extra notes: ${req.notes}` : ""
|
|
18544
|
+
].filter(Boolean).join(" ");
|
|
18445
18545
|
}
|
|
18446
|
-
running.clear();
|
|
18447
|
-
}
|
|
18448
|
-
function unlisten() {
|
|
18449
|
-
for (const [sig, fn] of listening) process.removeListener(sig, fn);
|
|
18450
|
-
listening.clear();
|
|
18451
|
-
}
|
|
18452
|
-
function onQuit(sig) {
|
|
18453
|
-
killRunning();
|
|
18454
|
-
unlisten();
|
|
18455
|
-
if (process.listenerCount(sig) === 0) process.exit(128 + os6.constants.signals[sig]);
|
|
18456
18546
|
}
|
|
18457
|
-
function
|
|
18458
|
-
if (!
|
|
18459
|
-
|
|
18460
|
-
child.once("close", () => {
|
|
18461
|
-
running.delete(child);
|
|
18462
|
-
if (!running.size) unlisten();
|
|
18463
|
-
});
|
|
18464
|
-
for (const sig of QUIT_SIGNALS) {
|
|
18465
|
-
if (listening.has(sig) || sig === "SIGINT" && interruptIsTheCallers) continue;
|
|
18466
|
-
const fn = () => onQuit(sig);
|
|
18467
|
-
listening.set(sig, fn);
|
|
18468
|
-
process.on(sig, fn);
|
|
18469
|
-
}
|
|
18547
|
+
function discardedCardsPrompt(cards = []) {
|
|
18548
|
+
if (!cards.length) return "";
|
|
18549
|
+
return "The user discarded the cards listed below. Their absence is intentional. Do not restore or recreate them, including under new IDs or filenames. Continue the remaining work from the current board, preserving the other cards.\n\n" + cards.map((c) => `- #${c.id}: ${c.path}`).join("\n");
|
|
18470
18550
|
}
|
|
18471
|
-
var
|
|
18472
|
-
var
|
|
18473
|
-
"../cli/src/lib/agent/
|
|
18551
|
+
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;
|
|
18552
|
+
var init_prompts = __esm({
|
|
18553
|
+
"../cli/src/lib/agent/prompts.ts"() {
|
|
18474
18554
|
"use strict";
|
|
18555
|
+
init_cards();
|
|
18556
|
+
init_memory();
|
|
18557
|
+
init_memories();
|
|
18558
|
+
init_paths();
|
|
18559
|
+
init_agents();
|
|
18560
|
+
init_command();
|
|
18561
|
+
init_deliveries();
|
|
18475
18562
|
init_env();
|
|
18476
|
-
|
|
18477
|
-
|
|
18478
|
-
|
|
18479
|
-
|
|
18480
|
-
|
|
18481
|
-
|
|
18482
|
-
|
|
18483
|
-
|
|
18484
|
-
|
|
18485
|
-
|
|
18486
|
-
|
|
18487
|
-
|
|
18488
|
-
|
|
18489
|
-
|
|
18490
|
-
|
|
18563
|
+
init_facts();
|
|
18564
|
+
init_outputs();
|
|
18565
|
+
init_archive();
|
|
18566
|
+
init_flows();
|
|
18567
|
+
init_resolve();
|
|
18568
|
+
init_runner();
|
|
18569
|
+
init_workflows();
|
|
18570
|
+
init_catalog2();
|
|
18571
|
+
init_roles();
|
|
18572
|
+
init_rules2();
|
|
18573
|
+
init_settings2();
|
|
18574
|
+
init_types3();
|
|
18575
|
+
RESUME_PROMPT = [
|
|
18576
|
+
`Continue. The previous run ended before it finished the task.`,
|
|
18577
|
+
`Pick up where you left off and carry it through.`
|
|
18578
|
+
].join(" ");
|
|
18579
|
+
DELIVERY_RESUME = [
|
|
18580
|
+
`Continue delivery %s. The previous run ended before the delivery finished.`,
|
|
18581
|
+
`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.`
|
|
18582
|
+
].join(" ");
|
|
18583
|
+
DELIVERY_RESUME_CARD = `Build the card as the delivery holds it, not as the file reads now: \`%c\` prints the approved copy.`;
|
|
18584
|
+
RESTARTABLE = /* @__PURE__ */ new Set([
|
|
18585
|
+
"implement",
|
|
18586
|
+
"clarify",
|
|
18587
|
+
"resolve",
|
|
18588
|
+
"approve",
|
|
18589
|
+
"archive",
|
|
18590
|
+
"spec",
|
|
18591
|
+
"conflict",
|
|
18592
|
+
"scheduled"
|
|
18593
|
+
]);
|
|
18594
|
+
RESTART_LEAD = [
|
|
18595
|
+
`The session you were continuing is gone, and this one holds nothing of it.`,
|
|
18596
|
+
`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.`
|
|
18597
|
+
].join(" ");
|
|
18598
|
+
restartable = (run2) => (run2.cardId !== null || !!run2.deliveryId) && RESTARTABLE.has(run2.action);
|
|
18599
|
+
NO_IMPLEMENT = `(Unless the request explicitly asks for implementation, don't implement it; revising the card's drafts is not implementing.)`;
|
|
18600
|
+
BOARD_FOLDER = "<board folder>";
|
|
18601
|
+
CARD_FILE = "<card file>";
|
|
18602
|
+
PROJECT_FOLDER = "<project folder>";
|
|
18603
|
+
PLANNING_FOR = /* @__PURE__ */ new Set(["create", "clarify", "resolve", "approve", "edit"]);
|
|
18604
|
+
SPEC_SELECTOR_FOR = /* @__PURE__ */ new Set(["clarify", "resolve", "approve", "edit"]);
|
|
18605
|
+
reflectedCards = (req) => req.cards ?? (req.id !== void 0 ? [req.id] : []);
|
|
18491
18606
|
}
|
|
18492
18607
|
});
|
|
18493
18608
|
|
|
18494
18609
|
// ../cli/src/lib/agent/sessions.ts
|
|
18495
18610
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
18496
|
-
import
|
|
18497
|
-
import
|
|
18611
|
+
import fs49 from "node:fs";
|
|
18612
|
+
import path54 from "node:path";
|
|
18498
18613
|
function reap(runs, reaped = [], restore = []) {
|
|
18499
18614
|
let changed = false;
|
|
18500
18615
|
const now = Date.now();
|
|
@@ -18514,7 +18629,7 @@ function reap(runs, reaped = [], restore = []) {
|
|
|
18514
18629
|
}
|
|
18515
18630
|
function stampDuration(run2, endedAt) {
|
|
18516
18631
|
try {
|
|
18517
|
-
|
|
18632
|
+
fs49.appendFileSync(run2.logPath, durationLine(endedAt - run2.startedAt));
|
|
18518
18633
|
} catch {
|
|
18519
18634
|
}
|
|
18520
18635
|
}
|
|
@@ -18528,7 +18643,7 @@ function cardNow(cardId2) {
|
|
|
18528
18643
|
try {
|
|
18529
18644
|
const file = cardFile(cardId2);
|
|
18530
18645
|
if (!file) return null;
|
|
18531
|
-
const { meta } = parseFrontmatter(
|
|
18646
|
+
const { meta } = parseFrontmatter(fs49.readFileSync(file, "utf8"));
|
|
18532
18647
|
if (!meta) return null;
|
|
18533
18648
|
return { status: meta.status || "todo", questions: openOf(meta.questions).length, title: meta.title };
|
|
18534
18649
|
} catch {
|
|
@@ -18665,9 +18780,9 @@ function leftBoardOnLanding(id) {
|
|
|
18665
18780
|
function archivedRejected(id) {
|
|
18666
18781
|
const filed = locateArchived(id);
|
|
18667
18782
|
if (!filed) return null;
|
|
18668
|
-
const file = filed.kind === "group" ?
|
|
18783
|
+
const file = filed.kind === "group" ? path54.join(filed.target, "root.md") : filed.target;
|
|
18669
18784
|
try {
|
|
18670
|
-
return !!parseFrontmatter(
|
|
18785
|
+
return !!parseFrontmatter(fs49.readFileSync(file, "utf8")).meta?.rejected;
|
|
18671
18786
|
} catch {
|
|
18672
18787
|
return false;
|
|
18673
18788
|
}
|
|
@@ -18924,7 +19039,7 @@ function openRun(req, prompt, notes = [], sessionId = randomUUID6(), said3) {
|
|
|
18924
19039
|
return { run: record, spec };
|
|
18925
19040
|
}
|
|
18926
19041
|
function planningSession(cardId2, cwd = REPO_ROOT) {
|
|
18927
|
-
const here = (dir) => !dir ||
|
|
19042
|
+
const here = (dir) => !dir || path54.resolve(dir) === cwd;
|
|
18928
19043
|
const ended = readRuns().filter((r) => r.status === "done").sort((a, b) => b.startedAt - a.startedAt);
|
|
18929
19044
|
const of = (run3, fork) => {
|
|
18930
19045
|
if (!here(run3.cwd)) return { none: "elsewhere" };
|
|
@@ -19146,8 +19261,8 @@ async function resumeHeld(prev, plan, sessionId, resuming, fresh) {
|
|
|
19146
19261
|
clearAsks(prev.sessionId);
|
|
19147
19262
|
try {
|
|
19148
19263
|
if (prev.action !== "create") {
|
|
19149
|
-
|
|
19150
|
-
|
|
19264
|
+
fs49.unlinkSync(prev.logPath);
|
|
19265
|
+
fs49.rmSync(eventsPathOf(prev.sessionId), { force: true });
|
|
19151
19266
|
}
|
|
19152
19267
|
} catch {
|
|
19153
19268
|
}
|
|
@@ -19171,8 +19286,8 @@ function forgetRun(sessionId) {
|
|
|
19171
19286
|
if (!gone) return;
|
|
19172
19287
|
clearAsks(sessionId);
|
|
19173
19288
|
try {
|
|
19174
|
-
|
|
19175
|
-
|
|
19289
|
+
fs49.unlinkSync(gone.logPath);
|
|
19290
|
+
fs49.rmSync(eventsPathOf(gone.sessionId), { force: true });
|
|
19176
19291
|
} catch {
|
|
19177
19292
|
}
|
|
19178
19293
|
}
|
|
@@ -19201,14 +19316,14 @@ function noteSpecRefusals(sessionId, refused) {
|
|
|
19201
19316
|
}
|
|
19202
19317
|
function clearAsks(sessionId) {
|
|
19203
19318
|
try {
|
|
19204
|
-
|
|
19319
|
+
fs49.unlinkSync(asksPathOf(sessionId));
|
|
19205
19320
|
} catch {
|
|
19206
19321
|
}
|
|
19207
19322
|
}
|
|
19208
19323
|
function readAsks(sessionId) {
|
|
19209
19324
|
let data;
|
|
19210
19325
|
try {
|
|
19211
|
-
data = JSON.parse(
|
|
19326
|
+
data = JSON.parse(fs49.readFileSync(asksPathOf(sessionId), "utf8"));
|
|
19212
19327
|
} catch {
|
|
19213
19328
|
return { asks: [], refines: [], refused: [] };
|
|
19214
19329
|
}
|
|
@@ -19235,10 +19350,10 @@ function readAsks(sessionId) {
|
|
|
19235
19350
|
return { asks: asks.filter((a) => !discarded.has(a.cardId)), refines: refines.filter((a) => !discarded.has(a.cardId)), refused };
|
|
19236
19351
|
}
|
|
19237
19352
|
function writeAsks(sessionId, file) {
|
|
19238
|
-
|
|
19353
|
+
fs49.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
19239
19354
|
const tmp = `${asksPathOf(sessionId)}.tmp`;
|
|
19240
|
-
|
|
19241
|
-
|
|
19355
|
+
fs49.writeFileSync(tmp, JSON.stringify(file, null, 2) + "\n");
|
|
19356
|
+
fs49.renameSync(tmp, asksPathOf(sessionId));
|
|
19242
19357
|
}
|
|
19243
19358
|
function markSpawned(sessionId, pid) {
|
|
19244
19359
|
patch(sessionId, (r) => {
|
|
@@ -19326,7 +19441,10 @@ async function cancelDelivery(id) {
|
|
|
19326
19441
|
if (!id.trim()) return { ok: false, ...refusal("deliveryUnnamed", "name the delivery to cancel", { action: "cancel" }) };
|
|
19327
19442
|
const delivery = namedDelivery(id);
|
|
19328
19443
|
if (!delivery) return { ok: false, ...unknownDelivery(id) };
|
|
19329
|
-
if (delivery.status !== "active")
|
|
19444
|
+
if (delivery.status !== "active") {
|
|
19445
|
+
tidyCheckout(delivery.deliveryId);
|
|
19446
|
+
return { ok: true, deliveryId: delivery.deliveryId };
|
|
19447
|
+
}
|
|
19330
19448
|
endDelivery(delivery.deliveryId, "cancelled");
|
|
19331
19449
|
const live = readRuns().find((r) => r.status === "running" && r.deliveryId === delivery.deliveryId && r.action !== "sub");
|
|
19332
19450
|
if (live) await stopRun(live.sessionId);
|
|
@@ -19351,7 +19469,9 @@ async function runIsDown(sessionId) {
|
|
|
19351
19469
|
}
|
|
19352
19470
|
async function resumeDelivery(id) {
|
|
19353
19471
|
if (!id.trim()) return { ok: false, ...refusal("deliveryUnnamed", "name the delivery to carry on", { action: "resume" }) };
|
|
19354
|
-
const
|
|
19472
|
+
const execution = executionOf();
|
|
19473
|
+
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" };
|
|
19474
|
+
const put = resumeRecord(id, execution);
|
|
19355
19475
|
if (!put.ok) return put;
|
|
19356
19476
|
const delivery = put.delivery;
|
|
19357
19477
|
if (await settleAlreadyLanded(delivery)) {
|
|
@@ -19392,12 +19512,12 @@ async function discardDelivery(id) {
|
|
|
19392
19512
|
return { ok: true, deliveryId: delivery.deliveryId };
|
|
19393
19513
|
}
|
|
19394
19514
|
function writeSpec(spec) {
|
|
19395
|
-
|
|
19396
|
-
|
|
19515
|
+
fs49.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
19516
|
+
fs49.writeFileSync(specPathOf(spec.sessionId), JSON.stringify(spec, null, 2) + "\n");
|
|
19397
19517
|
}
|
|
19398
19518
|
function readSpec(sessionId) {
|
|
19399
19519
|
try {
|
|
19400
|
-
const spec = JSON.parse(
|
|
19520
|
+
const spec = JSON.parse(fs49.readFileSync(specPathOf(sessionId), "utf8"));
|
|
19401
19521
|
return spec && Array.isArray(spec.plan?.argv) ? spec : null;
|
|
19402
19522
|
} catch {
|
|
19403
19523
|
return null;
|
|
@@ -19405,28 +19525,28 @@ function readSpec(sessionId) {
|
|
|
19405
19525
|
}
|
|
19406
19526
|
function dropSpec(sessionId) {
|
|
19407
19527
|
try {
|
|
19408
|
-
|
|
19528
|
+
fs49.unlinkSync(specPathOf(sessionId));
|
|
19409
19529
|
} catch {
|
|
19410
19530
|
}
|
|
19411
19531
|
}
|
|
19412
19532
|
async function acquireIndexLock(giveUp) {
|
|
19413
19533
|
const until = Date.now() + INDEX_WAIT_MS;
|
|
19414
|
-
const holder =
|
|
19534
|
+
const holder = path54.join(INDEX_LOCK2, "holder");
|
|
19415
19535
|
for (; ; ) {
|
|
19416
19536
|
if (giveUp()) return null;
|
|
19417
19537
|
try {
|
|
19418
|
-
|
|
19419
|
-
|
|
19420
|
-
return () =>
|
|
19538
|
+
fs49.mkdirSync(INDEX_LOCK2, { recursive: false });
|
|
19539
|
+
fs49.writeFileSync(holder, String(process.pid));
|
|
19540
|
+
return () => fs49.rmSync(INDEX_LOCK2, { recursive: true, force: true });
|
|
19421
19541
|
} catch (err) {
|
|
19422
19542
|
const code = err.code;
|
|
19423
19543
|
if (code === "ENOENT") {
|
|
19424
|
-
|
|
19544
|
+
fs49.mkdirSync(path54.dirname(INDEX_LOCK2), { recursive: true });
|
|
19425
19545
|
continue;
|
|
19426
19546
|
}
|
|
19427
19547
|
if (code !== "EEXIST") throw err;
|
|
19428
19548
|
if (!pidAlive(Number(readHolder(holder)))) {
|
|
19429
|
-
|
|
19549
|
+
fs49.rmSync(INDEX_LOCK2, { recursive: true, force: true });
|
|
19430
19550
|
continue;
|
|
19431
19551
|
}
|
|
19432
19552
|
if (Date.now() > until) return null;
|
|
@@ -19436,7 +19556,7 @@ async function acquireIndexLock(giveUp) {
|
|
|
19436
19556
|
}
|
|
19437
19557
|
function readHolder(file) {
|
|
19438
19558
|
try {
|
|
19439
|
-
return
|
|
19559
|
+
return fs49.readFileSync(file, "utf8").trim();
|
|
19440
19560
|
} catch {
|
|
19441
19561
|
return String(process.pid);
|
|
19442
19562
|
}
|
|
@@ -19445,6 +19565,7 @@ var INDEX_ACTIONS, SINGLETON_ACTIONS, VERB, SINGLETON_BUSY, RUN_STATUS, specPath
|
|
|
19445
19565
|
var init_sessions = __esm({
|
|
19446
19566
|
"../cli/src/lib/agent/sessions.ts"() {
|
|
19447
19567
|
"use strict";
|
|
19568
|
+
init_execution();
|
|
19448
19569
|
init_creation();
|
|
19449
19570
|
init_cards();
|
|
19450
19571
|
init_frontmatter();
|
|
@@ -19527,8 +19648,8 @@ var init_sessions = __esm({
|
|
|
19527
19648
|
conflict: "implementing",
|
|
19528
19649
|
hook: "implementing"
|
|
19529
19650
|
};
|
|
19530
|
-
specPathOf = (sessionId) =>
|
|
19531
|
-
asksPathOf = (sessionId) =>
|
|
19651
|
+
specPathOf = (sessionId) => path54.join(SESSIONS_DIR, `${sessionId}.plan.json`);
|
|
19652
|
+
asksPathOf = (sessionId) => path54.join(SESSIONS_DIR, `${sessionId}.asks.json`);
|
|
19532
19653
|
carriesSession = (r) => !!(r.resumedFrom || r.continues && !r.continues.fork);
|
|
19533
19654
|
canPickUp = (r) => (r.status === "error" || r.status === "interrupted" || r.status === "stopped") && !(r.action === "create" && readRuns().some((next) => next.resumedFrom === r.sessionId));
|
|
19534
19655
|
neverStarted = (r) => r.unspawned === true || !!r.errorWhy?.some((why) => why.kind === "notInstalled");
|
|
@@ -19662,8 +19783,8 @@ var init_questions = __esm({
|
|
|
19662
19783
|
});
|
|
19663
19784
|
|
|
19664
19785
|
// ../cli/src/lib/agent/workflow-cards.ts
|
|
19665
|
-
import
|
|
19666
|
-
import
|
|
19786
|
+
import fs50 from "node:fs";
|
|
19787
|
+
import path55 from "node:path";
|
|
19667
19788
|
function cardsOnWorkflow(id) {
|
|
19668
19789
|
const wanted = id || DEFAULT_WORKFLOW;
|
|
19669
19790
|
const ids = [];
|
|
@@ -19674,12 +19795,12 @@ function cardsOnWorkflow(id) {
|
|
|
19674
19795
|
return [];
|
|
19675
19796
|
}
|
|
19676
19797
|
for (const file of files2) {
|
|
19677
|
-
const base =
|
|
19678
|
-
const card = base === "root.md" ? idPrefix(
|
|
19798
|
+
const base = path55.basename(file);
|
|
19799
|
+
const card = base === "root.md" ? idPrefix(path55.basename(path55.dirname(file))) : idPrefix(base);
|
|
19679
19800
|
if (card === null) continue;
|
|
19680
19801
|
let meta;
|
|
19681
19802
|
try {
|
|
19682
|
-
meta = parseFrontmatter(
|
|
19803
|
+
meta = parseFrontmatter(fs50.readFileSync(file, "utf8")).meta;
|
|
19683
19804
|
} catch {
|
|
19684
19805
|
continue;
|
|
19685
19806
|
}
|
|
@@ -19737,12 +19858,12 @@ var init_workflow_cards = __esm({
|
|
|
19737
19858
|
});
|
|
19738
19859
|
|
|
19739
19860
|
// ../cli/src/lib/readme.ts
|
|
19740
|
-
import
|
|
19741
|
-
import
|
|
19861
|
+
import fs51 from "node:fs";
|
|
19862
|
+
import path56 from "node:path";
|
|
19742
19863
|
function stripReadmeRefs(target) {
|
|
19743
|
-
if (!
|
|
19864
|
+
if (!fs51.existsSync(README)) return [];
|
|
19744
19865
|
const needle = target.kind === "group" ? `](${target.rel}/` : `](${target.rel})`;
|
|
19745
|
-
const lines =
|
|
19866
|
+
const lines = fs51.readFileSync(README, "utf8").split("\n");
|
|
19746
19867
|
const out = [];
|
|
19747
19868
|
const removed = [];
|
|
19748
19869
|
let i = 0;
|
|
@@ -19764,20 +19885,20 @@ function stripReadmeRefs(target) {
|
|
|
19764
19885
|
out.push(line);
|
|
19765
19886
|
i++;
|
|
19766
19887
|
}
|
|
19767
|
-
if (removed.length)
|
|
19888
|
+
if (removed.length) fs51.writeFileSync(README, out.join("\n"));
|
|
19768
19889
|
return removed;
|
|
19769
19890
|
}
|
|
19770
19891
|
function addReadmeRef(id, title, relPath) {
|
|
19771
|
-
if (!
|
|
19772
|
-
const link2 = relPath.split(
|
|
19892
|
+
if (!fs51.existsSync(README)) return false;
|
|
19893
|
+
const link2 = relPath.split(path56.sep).join("/");
|
|
19773
19894
|
const bullet = `- [#${id} ${title}](${link2})`;
|
|
19774
19895
|
const heading = `## ${TASKS_HEADING}`;
|
|
19775
|
-
let lines =
|
|
19896
|
+
let lines = fs51.readFileSync(README, "utf8").split("\n");
|
|
19776
19897
|
const hi = lines.findIndex((l) => l.trim().toLowerCase() === heading.toLowerCase());
|
|
19777
19898
|
if (hi === -1) {
|
|
19778
19899
|
while (lines.length && lines[lines.length - 1].trim() === "") lines.pop();
|
|
19779
19900
|
lines.push("", heading, "", bullet);
|
|
19780
|
-
|
|
19901
|
+
fs51.writeFileSync(README, lines.join("\n") + "\n");
|
|
19781
19902
|
return true;
|
|
19782
19903
|
}
|
|
19783
19904
|
let end2 = hi + 1;
|
|
@@ -19791,15 +19912,15 @@ function addReadmeRef(id, title, relPath) {
|
|
|
19791
19912
|
const at = lastBullet !== -1 ? lastBullet + 1 : lines[hi + 1] === "" ? hi + 2 : hi + 1;
|
|
19792
19913
|
lines.splice(at, 0, bullet);
|
|
19793
19914
|
}
|
|
19794
|
-
|
|
19915
|
+
fs51.writeFileSync(README, lines.join("\n"));
|
|
19795
19916
|
return true;
|
|
19796
19917
|
}
|
|
19797
19918
|
function repointReadmeLink(id, oldRel, newRel, title) {
|
|
19798
|
-
if (!
|
|
19799
|
-
const oldLink = oldRel.split(
|
|
19800
|
-
const newLink = newRel.split(
|
|
19919
|
+
if (!fs51.existsSync(README)) return false;
|
|
19920
|
+
const oldLink = oldRel.split(path56.sep).join("/");
|
|
19921
|
+
const newLink = newRel.split(path56.sep).join("/");
|
|
19801
19922
|
const linkRe = new RegExp(`\\[#${id}\\b[^\\]]*\\]\\(${escapeRegex(oldLink)}\\)`);
|
|
19802
|
-
const lines =
|
|
19923
|
+
const lines = fs51.readFileSync(README, "utf8").split("\n");
|
|
19803
19924
|
let changed = false;
|
|
19804
19925
|
for (let i = 0; i < lines.length; i++) {
|
|
19805
19926
|
if (!lines[i].includes(`](${oldLink})`)) continue;
|
|
@@ -19809,7 +19930,7 @@ function repointReadmeLink(id, oldRel, newRel, title) {
|
|
|
19809
19930
|
changed = true;
|
|
19810
19931
|
}
|
|
19811
19932
|
}
|
|
19812
|
-
if (changed)
|
|
19933
|
+
if (changed) fs51.writeFileSync(README, lines.join("\n"));
|
|
19813
19934
|
return changed;
|
|
19814
19935
|
}
|
|
19815
19936
|
var isTableRow, isBulletStart, bulletIndent, TASKS_HEADING, escapeRegex;
|
|
@@ -19826,14 +19947,14 @@ var init_readme = __esm({
|
|
|
19826
19947
|
});
|
|
19827
19948
|
|
|
19828
19949
|
// ../cli/src/lib/reconcile.ts
|
|
19829
|
-
import
|
|
19830
|
-
import
|
|
19950
|
+
import fs52 from "node:fs";
|
|
19951
|
+
import path57 from "node:path";
|
|
19831
19952
|
function liveIds() {
|
|
19832
19953
|
const ids = /* @__PURE__ */ new Set();
|
|
19833
19954
|
for (const file of walkMd(TODO)) {
|
|
19834
|
-
const base =
|
|
19955
|
+
const base = path57.basename(file);
|
|
19835
19956
|
if (base === "README.md") continue;
|
|
19836
|
-
const id = base === "root.md" ? idPrefix(
|
|
19957
|
+
const id = base === "root.md" ? idPrefix(path57.basename(path57.dirname(file))) : idPrefix(base);
|
|
19837
19958
|
if (id != null) ids.add(id);
|
|
19838
19959
|
}
|
|
19839
19960
|
return ids;
|
|
@@ -19841,16 +19962,16 @@ function liveIds() {
|
|
|
19841
19962
|
function indexableCards() {
|
|
19842
19963
|
const out = [];
|
|
19843
19964
|
for (const dir of walkDirs(TODO)) {
|
|
19844
|
-
const id = idPrefix(
|
|
19845
|
-
if (id != null &&
|
|
19846
|
-
out.push({ id, rel:
|
|
19965
|
+
const id = idPrefix(path57.basename(dir));
|
|
19966
|
+
if (id != null && fs52.existsSync(path57.join(dir, "root.md"))) {
|
|
19967
|
+
out.push({ id, rel: path57.join(path57.relative(TODO, dir), "root.md").split(path57.sep).join("/") });
|
|
19847
19968
|
}
|
|
19848
19969
|
}
|
|
19849
19970
|
for (const file of walkMd(TODO)) {
|
|
19850
|
-
const base =
|
|
19971
|
+
const base = path57.basename(file);
|
|
19851
19972
|
if (base === "README.md" || base === "root.md") continue;
|
|
19852
|
-
const relTODO =
|
|
19853
|
-
const segs = relTODO.split(
|
|
19973
|
+
const relTODO = path57.relative(TODO, file);
|
|
19974
|
+
const segs = relTODO.split(path57.sep);
|
|
19854
19975
|
if (segs.length !== 1) continue;
|
|
19855
19976
|
const id = idPrefix(base);
|
|
19856
19977
|
if (id != null) out.push({ id, rel: segs.join("/") });
|
|
@@ -19859,7 +19980,7 @@ function indexableCards() {
|
|
|
19859
19980
|
}
|
|
19860
19981
|
function readmeComplaints(fix) {
|
|
19861
19982
|
const said3 = [];
|
|
19862
|
-
const lines =
|
|
19983
|
+
const lines = fs52.readFileSync(README, "utf8").split("\n");
|
|
19863
19984
|
const indexed = /* @__PURE__ */ new Set();
|
|
19864
19985
|
let fixed = 0;
|
|
19865
19986
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -19868,13 +19989,13 @@ function readmeComplaints(fix) {
|
|
|
19868
19989
|
const id = Number(m[1]);
|
|
19869
19990
|
const linkPath = m[2];
|
|
19870
19991
|
indexed.add(id);
|
|
19871
|
-
if (
|
|
19992
|
+
if (fs52.existsSync(path57.join(TODO, linkPath))) continue;
|
|
19872
19993
|
const found2 = locate(id);
|
|
19873
19994
|
if (!found2) {
|
|
19874
19995
|
said3.push(`README links #${id} \u2192 ${linkPath}, but no card with that id exists (removed by hand?). Drop the entry or restore the file.`);
|
|
19875
19996
|
continue;
|
|
19876
19997
|
}
|
|
19877
|
-
const want = (found2.kind === "group" ?
|
|
19998
|
+
const want = (found2.kind === "group" ? path57.join(found2.rel, "root.md") : found2.rel).split(path57.sep).join("/");
|
|
19878
19999
|
if (want === linkPath) continue;
|
|
19879
20000
|
if (!fix) {
|
|
19880
20001
|
said3.push(`README links #${id} \u2192 ${linkPath}, which isn't there any more; the card is at ${want}.`);
|
|
@@ -19884,7 +20005,7 @@ function readmeComplaints(fix) {
|
|
|
19884
20005
|
said3.push(`README link #${id} pointed at missing ${linkPath} \u2192 repointed to ${want}.`);
|
|
19885
20006
|
fixed++;
|
|
19886
20007
|
}
|
|
19887
|
-
if (fixed)
|
|
20008
|
+
if (fixed) fs52.writeFileSync(README, lines.join("\n"));
|
|
19888
20009
|
for (const c of indexableCards()) {
|
|
19889
20010
|
if (!indexed.has(c.id)) {
|
|
19890
20011
|
said3.push(`card ${c.rel} (#${c.id}) is not in the README index. Add it under "## ${TASKS_HEADING}".`);
|
|
@@ -19896,11 +20017,11 @@ function crossRefComplaints() {
|
|
|
19896
20017
|
const said3 = [];
|
|
19897
20018
|
const live = liveIds();
|
|
19898
20019
|
for (const file of walkMd(TODO)) {
|
|
19899
|
-
const base =
|
|
20020
|
+
const base = path57.basename(file);
|
|
19900
20021
|
if (base === "README.md") continue;
|
|
19901
|
-
const ownerId = base === "root.md" ? idPrefix(
|
|
20022
|
+
const ownerId = base === "root.md" ? idPrefix(path57.basename(path57.dirname(file))) : idPrefix(base);
|
|
19902
20023
|
if (ownerId == null) continue;
|
|
19903
|
-
const { meta } = parseFrontmatter(
|
|
20024
|
+
const { meta } = parseFrontmatter(fs52.readFileSync(file, "utf8"));
|
|
19904
20025
|
if (!meta) continue;
|
|
19905
20026
|
for (const field2 of ["blocked_by", "related"]) {
|
|
19906
20027
|
for (const ref of meta[field2] || []) {
|
|
@@ -19913,11 +20034,11 @@ function crossRefComplaints() {
|
|
|
19913
20034
|
return said3;
|
|
19914
20035
|
}
|
|
19915
20036
|
function reconcileBoard() {
|
|
19916
|
-
if (!
|
|
20037
|
+
if (!fs52.existsSync(README)) return;
|
|
19917
20038
|
for (const line of [...readmeComplaints(true), ...crossRefComplaints()]) warn2(line);
|
|
19918
20039
|
}
|
|
19919
20040
|
function boardComplaints() {
|
|
19920
|
-
if (!
|
|
20041
|
+
if (!fs52.existsSync(README)) return [];
|
|
19921
20042
|
try {
|
|
19922
20043
|
return [...readmeComplaints(false), ...crossRefComplaints()];
|
|
19923
20044
|
} catch {
|
|
@@ -19937,8 +20058,8 @@ var init_reconcile = __esm({
|
|
|
19937
20058
|
});
|
|
19938
20059
|
|
|
19939
20060
|
// ../cli/src/commands/card.ts
|
|
19940
|
-
import
|
|
19941
|
-
import
|
|
20061
|
+
import fs53 from "node:fs";
|
|
20062
|
+
import path58 from "node:path";
|
|
19942
20063
|
function defaultBody() {
|
|
19943
20064
|
return [
|
|
19944
20065
|
"<\u7528\u4E00\u5C0F\u6BB5\u8BDD\u5148\u8BB2\u53D1\u751F\u4E86\u4EC0\u4E48\u3001\u7528\u6237\u9047\u5230\u4EC0\u4E48\uFF0C\u518D\u8BB2\u8FD9\u5F20\u5361\u8981\u8FBE\u5230\u7684\u7ED3\u679C\u3002>",
|
|
@@ -19964,7 +20085,7 @@ function bodyFromFile(opts) {
|
|
|
19964
20085
|
if (opts.body === false) die("pass --body-file or --no-body, not both");
|
|
19965
20086
|
let text5;
|
|
19966
20087
|
try {
|
|
19967
|
-
text5 =
|
|
20088
|
+
text5 = fs53.readFileSync(opts.bodyFile, "utf8");
|
|
19968
20089
|
} catch {
|
|
19969
20090
|
die(`can't read ${opts.bodyFile} \u2014 write the body to a file, then pass its path`, { kind: "needs-input" });
|
|
19970
20091
|
}
|
|
@@ -19996,20 +20117,20 @@ function cmdCreate(opts) {
|
|
|
19996
20117
|
const start = opts.id;
|
|
19997
20118
|
const slug2 = slugify(opts.slug !== void 0 ? opts.slug : title);
|
|
19998
20119
|
const fileRel = `${start}-${slug2}.md`;
|
|
19999
|
-
const file =
|
|
20120
|
+
const file = path58.join(TODO, fileRel);
|
|
20000
20121
|
const held4 = locate(start) ?? locateArchived(start);
|
|
20001
|
-
if (held4 &&
|
|
20122
|
+
if (held4 && path58.basename(held4.target) === `${start}-${slug2}.md`) {
|
|
20002
20123
|
say(start);
|
|
20003
20124
|
say(` ${rel(held4.target)} was already written`);
|
|
20004
20125
|
return { id: start, ids: [start], title, file: rel(held4.target), indexed: false };
|
|
20005
20126
|
}
|
|
20006
20127
|
if (held4) die(`#${start} is already a card here (${rel(held4.target)})`, { kind: "id-taken", id: start });
|
|
20007
|
-
if (
|
|
20128
|
+
if (fs53.existsSync(file)) die(`${rel(file)} already exists \u2014 pick a different --slug`);
|
|
20008
20129
|
countBoardEvent(triage ? "cards_created_proposed" : "cards_created_asked");
|
|
20009
20130
|
const meta = { title, priority, status: "todo", blocked_by, related, modules, workflow, triage, source, questions };
|
|
20010
20131
|
const scaffolded = !written && opts.body !== false;
|
|
20011
20132
|
const body = written ?? (scaffolded ? defaultBody() : "");
|
|
20012
|
-
|
|
20133
|
+
fs53.writeFileSync(file, serializeFrontmatter(meta) + "\n\n" + body);
|
|
20013
20134
|
const indexed = addReadmeRef(start, title, fileRel);
|
|
20014
20135
|
say(start);
|
|
20015
20136
|
say(
|
|
@@ -20047,8 +20168,8 @@ function cmdUpdate(id, flags) {
|
|
|
20047
20168
|
if (creating) die(creating, { kind: "card-being-created", id });
|
|
20048
20169
|
const found2 = locate(id);
|
|
20049
20170
|
if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
|
|
20050
|
-
const file = found2.kind === "group" ?
|
|
20051
|
-
const { meta, body } = parseFrontmatter(
|
|
20171
|
+
const file = found2.kind === "group" ? path58.join(found2.target, "root.md") : found2.target;
|
|
20172
|
+
const { meta, body } = parseFrontmatter(fs53.readFileSync(file, "utf8"));
|
|
20052
20173
|
if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
|
|
20053
20174
|
if (flags.blockedBy !== void 0 && flags.addBlockedBy !== void 0) die("--blocked-by replaces the list and --add-blocked-by appends to it \u2014 pass one");
|
|
20054
20175
|
if (flags.related !== void 0 && flags.addRelated !== void 0) die("--related replaces the list and --add-related appends to it \u2014 pass one");
|
|
@@ -20128,26 +20249,26 @@ function cmdUpdate(id, flags) {
|
|
|
20128
20249
|
meta.status = "todo";
|
|
20129
20250
|
changes.push("status\u2192todo (open questions)");
|
|
20130
20251
|
}
|
|
20131
|
-
const curRel =
|
|
20252
|
+
const curRel = path58.relative(TODO, file);
|
|
20132
20253
|
const isSubtask = found2.kind === "file" && enclosingGroupRoot(file) !== null;
|
|
20133
|
-
let base =
|
|
20254
|
+
let base = path58.basename(file);
|
|
20134
20255
|
if (flags.slug !== void 0) {
|
|
20135
20256
|
if (found2.kind === "group") die("renaming a group root by script is not supported");
|
|
20136
20257
|
base = `${id}-${slugify(flags.slug)}.md`;
|
|
20137
20258
|
}
|
|
20138
|
-
const destRel =
|
|
20139
|
-
const dest =
|
|
20259
|
+
const destRel = path58.join(path58.dirname(curRel), base);
|
|
20260
|
+
const dest = path58.join(TODO, destRel);
|
|
20140
20261
|
const moving = dest !== file;
|
|
20141
|
-
if (moving &&
|
|
20142
|
-
|
|
20143
|
-
if (moving)
|
|
20262
|
+
if (moving && fs53.existsSync(dest)) die(`${rel(dest)} already exists`);
|
|
20263
|
+
fs53.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
|
|
20264
|
+
if (moving) fs53.renameSync(file, dest);
|
|
20144
20265
|
if (isSubtask) {
|
|
20145
20266
|
if (moving || changes.includes("title")) repointReadmeLink(id, curRel, destRel, meta.title);
|
|
20146
|
-
if (moving) changes.push(`renamed \u2192 ${destRel.split(
|
|
20267
|
+
if (moving) changes.push(`renamed \u2192 ${destRel.split(path58.sep).join("/")}`);
|
|
20147
20268
|
} else if (moving) {
|
|
20148
20269
|
stripReadmeRefs({ kind: "file", rel: curRel });
|
|
20149
20270
|
addReadmeRef(id, meta.title, destRel);
|
|
20150
|
-
changes.push(`renamed \u2192 ${destRel.split(
|
|
20271
|
+
changes.push(`renamed \u2192 ${destRel.split(path58.sep).join("/")}`);
|
|
20151
20272
|
} else if (changes.includes("title")) {
|
|
20152
20273
|
stripReadmeRefs({ kind: "file", rel: curRel });
|
|
20153
20274
|
addReadmeRef(id, meta.title, curRel);
|
|
@@ -20159,8 +20280,8 @@ function cmdUpdateQuestions(id, input) {
|
|
|
20159
20280
|
const ops = readQuestionOps(input.ops ?? []);
|
|
20160
20281
|
const found2 = locate(id);
|
|
20161
20282
|
if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
|
|
20162
|
-
const file = found2.kind === "group" ?
|
|
20163
|
-
const { meta, body } = parseFrontmatter(
|
|
20283
|
+
const file = found2.kind === "group" ? path58.join(found2.target, "root.md") : found2.target;
|
|
20284
|
+
const { meta, body } = parseFrontmatter(fs53.readFileSync(file, "utf8"));
|
|
20164
20285
|
if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
|
|
20165
20286
|
if (insideRun() && ops.some((op) => op.kind === "skip" || op.kind === "unskip")) {
|
|
20166
20287
|
die("only the user skips or reopens a question \u2014 a run leaves questions for them to answer");
|
|
@@ -20233,7 +20354,7 @@ function cmdUpdateQuestions(id, input) {
|
|
|
20233
20354
|
meta.status = "todo";
|
|
20234
20355
|
changes.push("status\u2192todo (open questions)");
|
|
20235
20356
|
}
|
|
20236
|
-
|
|
20357
|
+
fs53.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
|
|
20237
20358
|
for (const kind of closed2) countBoardEvent(kind);
|
|
20238
20359
|
settleDelivery2(id, skipped, unskipped);
|
|
20239
20360
|
say(
|
|
@@ -20307,13 +20428,13 @@ var init_config_template = __esm({
|
|
|
20307
20428
|
});
|
|
20308
20429
|
|
|
20309
20430
|
// ../cli/src/lib/agent/local.ts
|
|
20310
|
-
import
|
|
20311
|
-
import
|
|
20431
|
+
import fs54 from "node:fs";
|
|
20432
|
+
import path59 from "node:path";
|
|
20312
20433
|
function takeLocalModels(board2) {
|
|
20313
|
-
const file =
|
|
20434
|
+
const file = path59.join(board2, ".local.json");
|
|
20314
20435
|
let held4;
|
|
20315
20436
|
try {
|
|
20316
|
-
held4 = configBlock(JSON.parse(
|
|
20437
|
+
held4 = configBlock(JSON.parse(fs54.readFileSync(file, "utf8")));
|
|
20317
20438
|
} catch {
|
|
20318
20439
|
return {};
|
|
20319
20440
|
}
|
|
@@ -20331,7 +20452,7 @@ function takeLocalModels(board2) {
|
|
|
20331
20452
|
}
|
|
20332
20453
|
if (!Object.keys(agents).length) return out;
|
|
20333
20454
|
delete held4.agents;
|
|
20334
|
-
|
|
20455
|
+
fs54.writeFileSync(file, JSON.stringify(held4, null, 2) + "\n");
|
|
20335
20456
|
return out;
|
|
20336
20457
|
}
|
|
20337
20458
|
var LOCAL_IGNORE_LINE;
|
|
@@ -20392,12 +20513,12 @@ var init_identity2 = __esm({
|
|
|
20392
20513
|
});
|
|
20393
20514
|
|
|
20394
20515
|
// ../cli/src/lib/signals/migrate.ts
|
|
20395
|
-
import
|
|
20396
|
-
import
|
|
20516
|
+
import fs55 from "node:fs";
|
|
20517
|
+
import path60 from "node:path";
|
|
20397
20518
|
function readHandled() {
|
|
20398
20519
|
let text5;
|
|
20399
20520
|
try {
|
|
20400
|
-
text5 =
|
|
20521
|
+
text5 = fs55.readFileSync(SIGNALS_OLD_HANDLED, "utf8");
|
|
20401
20522
|
} catch {
|
|
20402
20523
|
return [];
|
|
20403
20524
|
}
|
|
@@ -20414,57 +20535,57 @@ function readHandled() {
|
|
|
20414
20535
|
return held4;
|
|
20415
20536
|
}
|
|
20416
20537
|
function lift(from, into) {
|
|
20417
|
-
|
|
20418
|
-
|
|
20538
|
+
fs55.mkdirSync(into, { recursive: true });
|
|
20539
|
+
fs55.renameSync(from, path60.join(into, freeName(into, path60.basename(from))));
|
|
20419
20540
|
}
|
|
20420
20541
|
function repoint(dir, from, into) {
|
|
20421
20542
|
let entries;
|
|
20422
20543
|
try {
|
|
20423
|
-
entries =
|
|
20544
|
+
entries = fs55.readdirSync(dir, { withFileTypes: true });
|
|
20424
20545
|
} catch {
|
|
20425
20546
|
return;
|
|
20426
20547
|
}
|
|
20427
20548
|
for (const entry of entries) {
|
|
20428
20549
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
20429
|
-
const file =
|
|
20430
|
-
const text5 =
|
|
20431
|
-
if (text5.includes(from))
|
|
20550
|
+
const file = path60.join(dir, entry.name);
|
|
20551
|
+
const text5 = fs55.readFileSync(file, "utf8");
|
|
20552
|
+
if (text5.includes(from)) fs55.writeFileSync(file, text5.split(from).join(into));
|
|
20432
20553
|
}
|
|
20433
20554
|
}
|
|
20434
20555
|
function dropIfEmpty2(dir) {
|
|
20435
20556
|
try {
|
|
20436
|
-
|
|
20557
|
+
fs55.rmdirSync(dir);
|
|
20437
20558
|
} catch {
|
|
20438
20559
|
}
|
|
20439
20560
|
}
|
|
20440
20561
|
function liftInbox() {
|
|
20441
|
-
if (!
|
|
20442
|
-
const oldFiles =
|
|
20443
|
-
if (
|
|
20444
|
-
for (const entry of
|
|
20445
|
-
if (entry.isFile()) lift(
|
|
20562
|
+
if (!fs55.existsSync(SIGNALS_OLD_INBOX)) return;
|
|
20563
|
+
const oldFiles = path60.join(SIGNALS_OLD_INBOX, "files");
|
|
20564
|
+
if (fs55.existsSync(oldFiles)) {
|
|
20565
|
+
for (const entry of fs55.readdirSync(oldFiles, { withFileTypes: true })) {
|
|
20566
|
+
if (entry.isFile()) lift(path60.join(oldFiles, entry.name), SIGNALS_FILES);
|
|
20446
20567
|
}
|
|
20447
20568
|
dropIfEmpty2(oldFiles);
|
|
20448
20569
|
}
|
|
20449
20570
|
const held4 = new Set(readFolder(TRIAGE).map((signal2) => signal2.sourceId));
|
|
20450
|
-
for (const entry of
|
|
20571
|
+
for (const entry of fs55.readdirSync(SIGNALS_OLD_INBOX, { withFileTypes: true })) {
|
|
20451
20572
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
20452
|
-
const from =
|
|
20573
|
+
const from = path60.join(SIGNALS_OLD_INBOX, entry.name);
|
|
20453
20574
|
const item = parse(from);
|
|
20454
20575
|
if (item && held4.has(item.sourceId)) {
|
|
20455
|
-
|
|
20576
|
+
fs55.rmSync(from, { force: true });
|
|
20456
20577
|
continue;
|
|
20457
20578
|
}
|
|
20458
20579
|
if (item) held4.add(item.sourceId);
|
|
20459
20580
|
lift(from, TRIAGE);
|
|
20460
20581
|
}
|
|
20461
|
-
const wasAt = asWritten(
|
|
20582
|
+
const wasAt = asWritten(path60.join(SIGNALS_OLD_INBOX, "files"));
|
|
20462
20583
|
const nowAt = asWritten(SIGNALS_FILES);
|
|
20463
20584
|
for (const dir of [TRIAGE, SIGNALS_ARCHIVED, SIGNALS_DISMISSED]) repoint(dir, wasAt, nowAt);
|
|
20464
20585
|
dropIfEmpty2(SIGNALS_OLD_INBOX);
|
|
20465
20586
|
}
|
|
20466
20587
|
function liftHandled() {
|
|
20467
|
-
if (!
|
|
20588
|
+
if (!fs55.existsSync(SIGNALS_OLD_HANDLED)) return;
|
|
20468
20589
|
const lines = readHandled();
|
|
20469
20590
|
const held4 = /* @__PURE__ */ new Set([
|
|
20470
20591
|
...readFolder(TRIAGE).map((signal2) => signal2.sourceId),
|
|
@@ -20474,7 +20595,7 @@ function liftHandled() {
|
|
|
20474
20595
|
for (const { sourceId, when } of lines) {
|
|
20475
20596
|
if (held4.has(sourceId)) continue;
|
|
20476
20597
|
held4.add(sourceId);
|
|
20477
|
-
|
|
20598
|
+
fs55.mkdirSync(SIGNALS_DISMISSED, { recursive: true });
|
|
20478
20599
|
const name = freeName(SIGNALS_DISMISSED, fileName("", sourceId));
|
|
20479
20600
|
const front = [
|
|
20480
20601
|
"---",
|
|
@@ -20484,13 +20605,13 @@ function liftHandled() {
|
|
|
20484
20605
|
"---",
|
|
20485
20606
|
""
|
|
20486
20607
|
];
|
|
20487
|
-
|
|
20608
|
+
fs55.writeFileSync(path60.join(SIGNALS_DISMISSED, name), `${front.join("\n")}
|
|
20488
20609
|
`);
|
|
20489
20610
|
}
|
|
20490
|
-
|
|
20611
|
+
fs55.rmSync(SIGNALS_OLD_HANDLED, { force: true });
|
|
20491
20612
|
}
|
|
20492
20613
|
function migrateTriage() {
|
|
20493
|
-
if (!
|
|
20614
|
+
if (!fs55.existsSync(SIGNALS_OLD_INBOX) && !fs55.existsSync(SIGNALS_OLD_HANDLED)) return;
|
|
20494
20615
|
try {
|
|
20495
20616
|
liftInbox();
|
|
20496
20617
|
liftHandled();
|
|
@@ -20505,13 +20626,13 @@ var init_migrate = __esm({
|
|
|
20505
20626
|
init_yaml();
|
|
20506
20627
|
init_inbox();
|
|
20507
20628
|
HANDLED_LINE = /^-\s+(.+?)\s+—\s+(.+)$/;
|
|
20508
|
-
asWritten = (dir) => `${rel(dir).split(
|
|
20629
|
+
asWritten = (dir) => `${rel(dir).split(path60.sep).join("/")}/`;
|
|
20509
20630
|
}
|
|
20510
20631
|
});
|
|
20511
20632
|
|
|
20512
20633
|
// ../cli/src/lib/signals/add.ts
|
|
20513
|
-
import
|
|
20514
|
-
import
|
|
20634
|
+
import fs56 from "node:fs";
|
|
20635
|
+
import path61 from "node:path";
|
|
20515
20636
|
import { createHash as createHash3 } from "node:crypto";
|
|
20516
20637
|
function linkTitle(url) {
|
|
20517
20638
|
let at;
|
|
@@ -20524,7 +20645,7 @@ function linkTitle(url) {
|
|
|
20524
20645
|
return oneLine(where) || url.slice(0, TITLE_MAX);
|
|
20525
20646
|
}
|
|
20526
20647
|
function asText(file) {
|
|
20527
|
-
const ext =
|
|
20648
|
+
const ext = path61.extname(file.name).toLowerCase();
|
|
20528
20649
|
if (!file.type.startsWith("text/") && file.type !== "application/json" && !TEXT_EXTENSIONS.has(ext)) return null;
|
|
20529
20650
|
try {
|
|
20530
20651
|
const text5 = new TextDecoder("utf-8", { fatal: true }).decode(file.data);
|
|
@@ -20534,9 +20655,9 @@ function asText(file) {
|
|
|
20534
20655
|
}
|
|
20535
20656
|
}
|
|
20536
20657
|
function fileName2(name, data) {
|
|
20537
|
-
const base =
|
|
20538
|
-
const ext =
|
|
20539
|
-
const word =
|
|
20658
|
+
const base = path61.basename(name);
|
|
20659
|
+
const ext = path61.extname(base).toLowerCase().slice(0, 12);
|
|
20660
|
+
const word = path61.basename(base, path61.extname(base)).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40) || "file";
|
|
20540
20661
|
const hash2 = createHash3("sha256").update(data).digest("hex").slice(0, 8);
|
|
20541
20662
|
return `${word}-${hash2}${ext}`;
|
|
20542
20663
|
}
|
|
@@ -20545,9 +20666,9 @@ function addToInbox(drop) {
|
|
|
20545
20666
|
const typed = (drop.text ?? "").trim();
|
|
20546
20667
|
const file = drop.file;
|
|
20547
20668
|
if (!typed && !file) return { ok: false, error: "nothing to add \u2014 paste a link or some text, or drop a file." };
|
|
20548
|
-
if (file && file.data.length === 0) return { ok: false, error: `${
|
|
20669
|
+
if (file && file.data.length === 0) return { ok: false, error: `${path61.basename(file.name)} is empty.` };
|
|
20549
20670
|
if (file && file.data.length > MAX_BYTES) {
|
|
20550
|
-
return { ok: false, error: `${
|
|
20671
|
+
return { ok: false, error: `${path61.basename(file.name)} is over ${MAX_BYTES / 1024 / 1024} MB \u2014 too big for the board.` };
|
|
20551
20672
|
}
|
|
20552
20673
|
const read4 = describe(typed, file);
|
|
20553
20674
|
const keep2 = read4.keep;
|
|
@@ -20569,8 +20690,8 @@ ${incoming.summary}`);
|
|
|
20569
20690
|
if (seen.status === "pending" || seen.status === "archived") return { ok: false, error: `${sayHit(seen)}.` };
|
|
20570
20691
|
try {
|
|
20571
20692
|
if (keep2) {
|
|
20572
|
-
|
|
20573
|
-
|
|
20693
|
+
fs56.mkdirSync(SIGNALS_FILES, { recursive: true });
|
|
20694
|
+
fs56.writeFileSync(keep2.at, keep2.data);
|
|
20574
20695
|
}
|
|
20575
20696
|
return { ok: true, signal: writeSignal({ ...incoming, sourceId }, formatStamp(/* @__PURE__ */ new Date()), drop.slug) };
|
|
20576
20697
|
} catch (e) {
|
|
@@ -20584,7 +20705,7 @@ function describe(typed, file) {
|
|
|
20584
20705
|
return only ? [only] : [];
|
|
20585
20706
|
};
|
|
20586
20707
|
if (file) {
|
|
20587
|
-
const name =
|
|
20708
|
+
const name = path61.basename(file.name);
|
|
20588
20709
|
const text5 = asText(file);
|
|
20589
20710
|
const note = typed ? `
|
|
20590
20711
|
|
|
@@ -20596,7 +20717,7 @@ ${typed}` : "";
|
|
|
20596
20717
|
const at = keepAt(file);
|
|
20597
20718
|
return {
|
|
20598
20719
|
incoming: {
|
|
20599
|
-
title:
|
|
20720
|
+
title: path61.basename(name, path61.extname(name)) || name,
|
|
20600
20721
|
summary: `${name} \u2014 ${boardRel2(at)}${note}`,
|
|
20601
20722
|
...named2
|
|
20602
20723
|
},
|
|
@@ -20634,73 +20755,73 @@ var init_add = __esm({
|
|
|
20634
20755
|
MAX_BYTES = 25 * 1024 * 1024;
|
|
20635
20756
|
TITLE_MAX = 120;
|
|
20636
20757
|
TEXT_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".markdown", ".txt", ".text", ".csv", ".json", ".yml", ".yaml", ".log"]);
|
|
20637
|
-
boardRel2 = (file) => rel(file).split(
|
|
20758
|
+
boardRel2 = (file) => rel(file).split(path61.sep).join("/");
|
|
20638
20759
|
oneLine = (text5) => {
|
|
20639
20760
|
const first = text5.split("\n").find((line) => line.trim()) ?? "";
|
|
20640
20761
|
const clean = first.replace(/^#+\s*/, "").trim();
|
|
20641
20762
|
return clean.length > TITLE_MAX ? `${clean.slice(0, TITLE_MAX - 1).trimEnd()}\u2026` : clean;
|
|
20642
20763
|
};
|
|
20643
|
-
keepAt = (file) =>
|
|
20764
|
+
keepAt = (file) => path61.join(SIGNALS_FILES, fileName2(file.name, file.data));
|
|
20644
20765
|
}
|
|
20645
20766
|
});
|
|
20646
20767
|
|
|
20647
20768
|
// ../cli/src/lib/recurring.ts
|
|
20648
|
-
import
|
|
20649
|
-
import
|
|
20769
|
+
import fs57 from "node:fs";
|
|
20770
|
+
import path62 from "node:path";
|
|
20650
20771
|
function migratePruneMemoryCard() {
|
|
20651
20772
|
const dir = recurringDir();
|
|
20652
20773
|
let names4;
|
|
20653
20774
|
try {
|
|
20654
|
-
names4 =
|
|
20775
|
+
names4 = fs57.readdirSync(dir);
|
|
20655
20776
|
} catch {
|
|
20656
20777
|
return null;
|
|
20657
20778
|
}
|
|
20658
20779
|
const name = names4.find((entry) => entry.endsWith(`-${PRUNE_SLUG}.md`));
|
|
20659
20780
|
if (!name) return null;
|
|
20660
|
-
const file =
|
|
20781
|
+
const file = path62.join(dir, name);
|
|
20661
20782
|
try {
|
|
20662
|
-
const { meta } = parseFrontmatter(
|
|
20783
|
+
const { meta } = parseFrontmatter(fs57.readFileSync(file, "utf8"));
|
|
20663
20784
|
const cadence = meta?.cadence;
|
|
20664
20785
|
if (typeof cadence === "string") adoptMemoryPruneCadence(cadence);
|
|
20665
20786
|
} catch {
|
|
20666
20787
|
}
|
|
20667
|
-
|
|
20788
|
+
fs57.rmSync(file, { force: true });
|
|
20668
20789
|
return rel(file);
|
|
20669
20790
|
}
|
|
20670
20791
|
function legacyCards() {
|
|
20671
20792
|
const dir = recurringDir();
|
|
20672
|
-
if (!
|
|
20793
|
+
if (!fs57.existsSync(dir)) return [];
|
|
20673
20794
|
const cards = [];
|
|
20674
|
-
for (const entry of
|
|
20795
|
+
for (const entry of fs57.readdirSync(dir, { withFileTypes: true })) {
|
|
20675
20796
|
const id = idPrefix(entry.name);
|
|
20676
20797
|
if (id === null) continue;
|
|
20677
|
-
const full =
|
|
20798
|
+
const full = path62.join(dir, entry.name);
|
|
20678
20799
|
const slug2 = entry.name.replace(/\.md$/, "").replace(/^\d+-?/, "");
|
|
20679
20800
|
if (entry.isFile() && entry.name.endsWith(".md")) cards.push({ id, slug: slug2, file: full });
|
|
20680
|
-
else if (entry.isDirectory() &&
|
|
20681
|
-
cards.push({ id, slug: slug2, file:
|
|
20801
|
+
else if (entry.isDirectory() && fs57.existsSync(path62.join(full, "root.md"))) {
|
|
20802
|
+
cards.push({ id, slug: slug2, file: path62.join(full, "root.md"), folder: full });
|
|
20682
20803
|
}
|
|
20683
20804
|
}
|
|
20684
20805
|
return cards.sort((a, b) => a.id - b.id);
|
|
20685
20806
|
}
|
|
20686
20807
|
function mergeInto2(from, to, skip = []) {
|
|
20687
|
-
|
|
20688
|
-
for (const entry of
|
|
20808
|
+
fs57.mkdirSync(to, { recursive: true });
|
|
20809
|
+
for (const entry of fs57.readdirSync(from, { withFileTypes: true })) {
|
|
20689
20810
|
if (skip.includes(entry.name)) continue;
|
|
20690
|
-
const source =
|
|
20691
|
-
const target =
|
|
20692
|
-
if (!
|
|
20693
|
-
else if (entry.isDirectory() &&
|
|
20811
|
+
const source = path62.join(from, entry.name);
|
|
20812
|
+
const target = path62.join(to, entry.name);
|
|
20813
|
+
if (!fs57.existsSync(target)) fs57.renameSync(source, target);
|
|
20814
|
+
else if (entry.isDirectory() && fs57.statSync(target).isDirectory()) mergeInto2(source, target);
|
|
20694
20815
|
}
|
|
20695
|
-
if (isEmpty(from))
|
|
20816
|
+
if (isEmpty(from)) fs57.rmSync(from, { recursive: true, force: true });
|
|
20696
20817
|
}
|
|
20697
20818
|
function moveSideFolders() {
|
|
20698
20819
|
const dir = recurringDir();
|
|
20699
|
-
for (const entry of
|
|
20820
|
+
for (const entry of fs57.readdirSync(dir, { withFileTypes: true })) {
|
|
20700
20821
|
if (!entry.isDirectory() || idPrefix(entry.name) !== null) continue;
|
|
20701
|
-
const from =
|
|
20702
|
-
if (isEmpty(from))
|
|
20703
|
-
else mergeInto2(from,
|
|
20822
|
+
const from = path62.join(dir, entry.name);
|
|
20823
|
+
if (isEmpty(from)) fs57.rmSync(from, { recursive: true, force: true });
|
|
20824
|
+
else mergeInto2(from, path62.join(AGENTS, entry.name));
|
|
20704
20825
|
}
|
|
20705
20826
|
}
|
|
20706
20827
|
function sections(body) {
|
|
@@ -20750,8 +20871,8 @@ function agentName(card, text5) {
|
|
|
20750
20871
|
const known2 = /* @__PURE__ */ new Set([...specAgentCatalog().agents.map((a) => a.name), ...ROLE_NAMES]);
|
|
20751
20872
|
for (const name of [slug2, `${slug2}-${card.id}`]) {
|
|
20752
20873
|
if (!AGENT_NAME.test(name)) continue;
|
|
20753
|
-
const file =
|
|
20754
|
-
const written =
|
|
20874
|
+
const file = path62.join(AGENTS, name, AGENT_FILE3);
|
|
20875
|
+
const written = fs57.existsSync(file) ? fs57.readFileSync(file, "utf8") : null;
|
|
20755
20876
|
if (written === text5(name)) return name;
|
|
20756
20877
|
if (written === null && !known2.has(name)) return name;
|
|
20757
20878
|
}
|
|
@@ -20768,11 +20889,11 @@ ${q.options.map((o) => `- ${o}`).join("\n")}` : "";
|
|
|
20768
20889
|
function removeCard(card) {
|
|
20769
20890
|
dropCrossRefs(card.id);
|
|
20770
20891
|
const target = card.folder ?? card.file;
|
|
20771
|
-
stripReadmeRefs({ kind: card.folder ? "group" : "file", rel:
|
|
20772
|
-
|
|
20892
|
+
stripReadmeRefs({ kind: card.folder ? "group" : "file", rel: path62.relative(TODO, target).split(path62.sep).join("/") });
|
|
20893
|
+
fs57.rmSync(target, { recursive: true, force: true });
|
|
20773
20894
|
}
|
|
20774
20895
|
function migrateCard(card) {
|
|
20775
|
-
const { meta, body } = parseFrontmatter(
|
|
20896
|
+
const { meta, body } = parseFrontmatter(fs57.readFileSync(card.file, "utf8"));
|
|
20776
20897
|
if (!meta) throw new Error("its frontmatter cannot be read");
|
|
20777
20898
|
const raw = meta;
|
|
20778
20899
|
const said3 = (key) => typeof raw[key] === "string" ? raw[key].trim() : "";
|
|
@@ -20782,19 +20903,19 @@ function migrateCard(card) {
|
|
|
20782
20903
|
if (!flow) throw new Error("this board has no workflow to put it in");
|
|
20783
20904
|
const text5 = (name2) => agentText(name2, meta.title || card.slug, body);
|
|
20784
20905
|
const name = agentName(card, text5);
|
|
20785
|
-
const dir =
|
|
20786
|
-
const file =
|
|
20787
|
-
const madeDir = !
|
|
20788
|
-
const wrote = !
|
|
20906
|
+
const dir = path62.join(AGENTS, name);
|
|
20907
|
+
const file = path62.join(dir, AGENT_FILE3);
|
|
20908
|
+
const madeDir = !fs57.existsSync(dir);
|
|
20909
|
+
const wrote = !fs57.existsSync(file);
|
|
20789
20910
|
try {
|
|
20790
|
-
|
|
20791
|
-
if (wrote)
|
|
20911
|
+
fs57.mkdirSync(dir, { recursive: true });
|
|
20912
|
+
if (wrote) fs57.writeFileSync(file, text5(name));
|
|
20792
20913
|
const adopted = adoptWorkflowScheduled(flow.id, name, { cadence, lastRun });
|
|
20793
20914
|
if (!adopted.ok) throw new Error(adopted.error);
|
|
20794
20915
|
questionsToTriage(meta, name);
|
|
20795
20916
|
} catch (e) {
|
|
20796
|
-
if (madeDir)
|
|
20797
|
-
else if (wrote)
|
|
20917
|
+
if (madeDir) fs57.rmSync(dir, { recursive: true, force: true });
|
|
20918
|
+
else if (wrote) fs57.rmSync(file, { force: true });
|
|
20798
20919
|
throw e;
|
|
20799
20920
|
}
|
|
20800
20921
|
if (card.folder) mergeInto2(card.folder, dir, ["root.md"]);
|
|
@@ -20804,7 +20925,7 @@ function migrateCard(card) {
|
|
|
20804
20925
|
function migrateRecurringCards() {
|
|
20805
20926
|
const done2 = { agents: [], removed: [], failed: [] };
|
|
20806
20927
|
const dir = recurringDir();
|
|
20807
|
-
if (!
|
|
20928
|
+
if (!fs57.existsSync(dir)) return done2;
|
|
20808
20929
|
migratePruneMemoryCard();
|
|
20809
20930
|
moveSideFolders();
|
|
20810
20931
|
for (const card of legacyCards()) {
|
|
@@ -20817,7 +20938,7 @@ function migrateRecurringCards() {
|
|
|
20817
20938
|
done2.failed.push({ card: rel(card.file), why: e instanceof Error ? e.message : String(e) });
|
|
20818
20939
|
}
|
|
20819
20940
|
}
|
|
20820
|
-
if (isEmpty(dir))
|
|
20941
|
+
if (isEmpty(dir)) fs57.rmSync(dir, { recursive: true, force: true });
|
|
20821
20942
|
return done2;
|
|
20822
20943
|
}
|
|
20823
20944
|
function recurringMigrationLines(done2) {
|
|
@@ -20850,14 +20971,14 @@ var init_recurring = __esm({
|
|
|
20850
20971
|
PRUNE_SLUG = "prune-the-memory";
|
|
20851
20972
|
FETCH_SLUG = "fetch-triage-items";
|
|
20852
20973
|
AGENT_FILE3 = "AGENT.md";
|
|
20853
|
-
recurringDir = () =>
|
|
20854
|
-
isEmpty = (dir) =>
|
|
20974
|
+
recurringDir = () => path62.join(TODO, LEGACY_RECURRING);
|
|
20975
|
+
isEmpty = (dir) => fs57.readdirSync(dir).every((name) => name === ".DS_Store");
|
|
20855
20976
|
SECTION = /^##\s+(.+?)\s*$/;
|
|
20856
20977
|
}
|
|
20857
20978
|
});
|
|
20858
20979
|
|
|
20859
20980
|
// ../cli/src/commands/init.ts
|
|
20860
|
-
import
|
|
20981
|
+
import fs58 from "node:fs";
|
|
20861
20982
|
function boardReadme() {
|
|
20862
20983
|
return `# Board
|
|
20863
20984
|
|
|
@@ -20870,20 +20991,20 @@ _(none)_
|
|
|
20870
20991
|
`;
|
|
20871
20992
|
}
|
|
20872
20993
|
function writeConfigIfMissing() {
|
|
20873
|
-
if (
|
|
20874
|
-
|
|
20875
|
-
|
|
20994
|
+
if (fs58.existsSync(CONFIG)) return false;
|
|
20995
|
+
fs58.mkdirSync(KANBAN, { recursive: true });
|
|
20996
|
+
fs58.writeFileSync(CONFIG, CONFIG_TEMPLATE);
|
|
20876
20997
|
return true;
|
|
20877
20998
|
}
|
|
20878
20999
|
function writeModulesIfMissing() {
|
|
20879
|
-
if (
|
|
20880
|
-
|
|
20881
|
-
|
|
21000
|
+
if (fs58.existsSync(MODULES_MD)) return false;
|
|
21001
|
+
fs58.mkdirSync(KANBAN, { recursive: true });
|
|
21002
|
+
fs58.writeFileSync(MODULES_MD, MODULES_TEMPLATE);
|
|
20882
21003
|
return true;
|
|
20883
21004
|
}
|
|
20884
21005
|
function writeGitignoreIfMissing() {
|
|
20885
|
-
|
|
20886
|
-
const text5 =
|
|
21006
|
+
fs58.mkdirSync(KANBAN, { recursive: true });
|
|
21007
|
+
const text5 = fs58.existsSync(KANBAN_GITIGNORE) ? fs58.readFileSync(KANBAN_GITIGNORE, "utf8") : "";
|
|
20887
21008
|
const has = (rule) => text5.split("\n").some((line) => line.trim() === rule.line);
|
|
20888
21009
|
const missing = IGNORE_RULES.filter((rule) => !has(rule));
|
|
20889
21010
|
if (!missing.length) return false;
|
|
@@ -20891,37 +21012,37 @@ function writeGitignoreIfMissing() {
|
|
|
20891
21012
|
${rule.line}
|
|
20892
21013
|
`).join("");
|
|
20893
21014
|
const separator = !text5 || text5.endsWith("\n") ? "" : "\n";
|
|
20894
|
-
|
|
21015
|
+
fs58.writeFileSync(KANBAN_GITIGNORE, `${text5}${separator}${block}`);
|
|
20895
21016
|
return true;
|
|
20896
21017
|
}
|
|
20897
21018
|
function writeSkeletonIfMissing() {
|
|
20898
21019
|
const added = [];
|
|
20899
|
-
if (!
|
|
20900
|
-
|
|
21020
|
+
if (!fs58.existsSync(TODO)) {
|
|
21021
|
+
fs58.mkdirSync(TODO, { recursive: true });
|
|
20901
21022
|
added.push(`${rel(TODO)}/`);
|
|
20902
21023
|
}
|
|
20903
|
-
if (!
|
|
20904
|
-
|
|
21024
|
+
if (!fs58.existsSync(README)) {
|
|
21025
|
+
fs58.writeFileSync(README, boardReadme());
|
|
20905
21026
|
added.push(rel(README));
|
|
20906
21027
|
}
|
|
20907
21028
|
return added;
|
|
20908
21029
|
}
|
|
20909
21030
|
function retireReleases() {
|
|
20910
21031
|
const done2 = [];
|
|
20911
|
-
if (
|
|
20912
|
-
|
|
21032
|
+
if (fs58.existsSync(RELEASES)) {
|
|
21033
|
+
fs58.rmSync(RELEASES);
|
|
20913
21034
|
done2.push(`deleted ${rel(RELEASES)}`);
|
|
20914
21035
|
}
|
|
20915
21036
|
let cards = 0;
|
|
20916
21037
|
for (const dir of [TODO, ARCHIVE]) {
|
|
20917
|
-
if (!
|
|
21038
|
+
if (!fs58.existsSync(dir)) continue;
|
|
20918
21039
|
for (const file of walkMd(dir)) {
|
|
20919
|
-
const text5 =
|
|
21040
|
+
const text5 = fs58.readFileSync(file, "utf8");
|
|
20920
21041
|
const end2 = text5.startsWith("---\n") ? text5.indexOf("\n---", 3) : -1;
|
|
20921
21042
|
if (end2 < 0) continue;
|
|
20922
21043
|
const head = text5.slice(0, end2).replace(/\nrelease:[^\n]*/, "");
|
|
20923
21044
|
if (head.length === end2) continue;
|
|
20924
|
-
|
|
21045
|
+
fs58.writeFileSync(file, head + text5.slice(end2));
|
|
20925
21046
|
cards++;
|
|
20926
21047
|
}
|
|
20927
21048
|
}
|
|
@@ -20929,7 +21050,7 @@ function retireReleases() {
|
|
|
20929
21050
|
return done2.join(", ");
|
|
20930
21051
|
}
|
|
20931
21052
|
function cmdInit() {
|
|
20932
|
-
if (
|
|
21053
|
+
if (fs58.existsSync(KANBAN)) {
|
|
20933
21054
|
const added = [
|
|
20934
21055
|
...writeSkeletonIfMissing(),
|
|
20935
21056
|
writeConfigIfMissing() && rel(CONFIG),
|
|
@@ -20964,8 +21085,8 @@ function cmdInit() {
|
|
|
20964
21085
|
}
|
|
20965
21086
|
return { board: rel(KANBAN), created: false, added, memory_paths: scaffolded.map((s) => rel(s.dir)) };
|
|
20966
21087
|
}
|
|
20967
|
-
|
|
20968
|
-
|
|
21088
|
+
fs58.mkdirSync(TODO, { recursive: true });
|
|
21089
|
+
fs58.writeFileSync(README, boardReadme());
|
|
20969
21090
|
writeConfigIfMissing();
|
|
20970
21091
|
scaffoldProjectMemory();
|
|
20971
21092
|
writeModulesIfMissing();
|
|
@@ -21054,25 +21175,25 @@ var init_scheduled = __esm({
|
|
|
21054
21175
|
});
|
|
21055
21176
|
|
|
21056
21177
|
// ../cli/src/lib/card-age.ts
|
|
21057
|
-
import
|
|
21058
|
-
import
|
|
21178
|
+
import fs59 from "node:fs";
|
|
21179
|
+
import path63 from "node:path";
|
|
21059
21180
|
function staleAfterIn(config) {
|
|
21060
21181
|
const days = Number(LINE.exec(config)?.[1]);
|
|
21061
21182
|
return Number.isInteger(days) && days > 0 ? days : STALE_AFTER_DEFAULT;
|
|
21062
21183
|
}
|
|
21063
21184
|
function staleAfter() {
|
|
21064
|
-
if (!
|
|
21065
|
-
return staleAfterIn(
|
|
21185
|
+
if (!fs59.existsSync(CONFIG)) return STALE_AFTER_DEFAULT;
|
|
21186
|
+
return staleAfterIn(fs59.readFileSync(CONFIG, "utf8"));
|
|
21066
21187
|
}
|
|
21067
21188
|
function todoPathspec(top) {
|
|
21068
|
-
const from =
|
|
21069
|
-
if (!from || from.startsWith("..") ||
|
|
21070
|
-
return from.split(
|
|
21189
|
+
const from = path63.relative(real(top), real(TODO));
|
|
21190
|
+
if (!from || from.startsWith("..") || path63.isAbsolute(from)) return null;
|
|
21191
|
+
return from.split(path63.sep).join("/");
|
|
21071
21192
|
}
|
|
21072
21193
|
function cardFile2(spec, printed) {
|
|
21073
21194
|
const p = unquotePath(printed);
|
|
21074
21195
|
if (!p.startsWith(`${spec}/`)) return null;
|
|
21075
|
-
return
|
|
21196
|
+
return path63.join(TODO, ...p.slice(spec.length + 1).split("/"));
|
|
21076
21197
|
}
|
|
21077
21198
|
function dirtyCards(top, spec) {
|
|
21078
21199
|
const out = git(["status", "--porcelain", "--untracked-files=all", "--", spec], top);
|
|
@@ -21119,9 +21240,9 @@ var init_card_age = __esm({
|
|
|
21119
21240
|
gitTop = () => git(["rev-parse", "--show-toplevel"])?.trim() || null;
|
|
21120
21241
|
real = (p) => {
|
|
21121
21242
|
try {
|
|
21122
|
-
return
|
|
21243
|
+
return fs59.realpathSync(p);
|
|
21123
21244
|
} catch {
|
|
21124
|
-
return
|
|
21245
|
+
return path63.resolve(p);
|
|
21125
21246
|
}
|
|
21126
21247
|
};
|
|
21127
21248
|
unquotePath = (line) => {
|
|
@@ -21156,8 +21277,8 @@ var init_card_holds = __esm({
|
|
|
21156
21277
|
});
|
|
21157
21278
|
|
|
21158
21279
|
// ../cli/src/commands/list.ts
|
|
21159
|
-
import
|
|
21160
|
-
import
|
|
21280
|
+
import fs60 from "node:fs";
|
|
21281
|
+
import path64 from "node:path";
|
|
21161
21282
|
function summaryLine(body) {
|
|
21162
21283
|
const lines = body.split("\n");
|
|
21163
21284
|
let i = 0;
|
|
@@ -21173,13 +21294,13 @@ function summaryLine(body) {
|
|
|
21173
21294
|
function openRows() {
|
|
21174
21295
|
const rows = [];
|
|
21175
21296
|
for (const file of walkMd(TODO)) {
|
|
21176
|
-
const base =
|
|
21297
|
+
const base = path64.basename(file);
|
|
21177
21298
|
if (base === "README.md") continue;
|
|
21178
21299
|
const isRoot = base === "root.md";
|
|
21179
|
-
const id = isRoot ? idPrefix(
|
|
21300
|
+
const id = isRoot ? idPrefix(path64.basename(path64.dirname(file))) : idPrefix(base);
|
|
21180
21301
|
if (id == null) continue;
|
|
21181
|
-
if (isLegacyRecurring(
|
|
21182
|
-
const { meta, body } = parseFrontmatter(
|
|
21302
|
+
if (isLegacyRecurring(path64.relative(TODO, file))) continue;
|
|
21303
|
+
const { meta, body } = parseFrontmatter(fs60.readFileSync(file, "utf8"));
|
|
21183
21304
|
rows.push({
|
|
21184
21305
|
id,
|
|
21185
21306
|
file,
|
|
@@ -21221,15 +21342,15 @@ function cmdStale(rows, all2, scope, mod) {
|
|
|
21221
21342
|
}
|
|
21222
21343
|
function archivedFiles() {
|
|
21223
21344
|
const files2 = /* @__PURE__ */ new Map();
|
|
21224
|
-
if (!
|
|
21345
|
+
if (!fs60.existsSync(ARCHIVE)) return files2;
|
|
21225
21346
|
for (const file of walkMd(ARCHIVE)) {
|
|
21226
|
-
const id = idPrefix(
|
|
21347
|
+
const id = idPrefix(path64.basename(file));
|
|
21227
21348
|
if (id !== null) files2.set(id, file);
|
|
21228
21349
|
}
|
|
21229
21350
|
for (const dir of walkDirs(ARCHIVE)) {
|
|
21230
|
-
const id = idPrefix(
|
|
21231
|
-
const root =
|
|
21232
|
-
if (id !== null &&
|
|
21351
|
+
const id = idPrefix(path64.basename(dir));
|
|
21352
|
+
const root = path64.join(dir, "root.md");
|
|
21353
|
+
if (id !== null && fs60.existsSync(root)) files2.set(id, root);
|
|
21233
21354
|
}
|
|
21234
21355
|
return files2;
|
|
21235
21356
|
}
|
|
@@ -21340,14 +21461,14 @@ var init_list = __esm({
|
|
|
21340
21461
|
});
|
|
21341
21462
|
|
|
21342
21463
|
// ../cli/src/commands/misc.ts
|
|
21343
|
-
import
|
|
21344
|
-
import
|
|
21464
|
+
import fs61 from "node:fs";
|
|
21465
|
+
import path65 from "node:path";
|
|
21345
21466
|
function extractOldMeta(text5, file) {
|
|
21346
21467
|
const grab = (re) => {
|
|
21347
21468
|
const m = text5.match(re);
|
|
21348
21469
|
return m ? m[1].trim() : null;
|
|
21349
21470
|
};
|
|
21350
|
-
const title = grab(/^#\s+(.+)$/m) || slugify(
|
|
21471
|
+
const title = grab(/^#\s+(.+)$/m) || slugify(path65.basename(file, ".md").replace(/^\d+-/, "")).replace(/-/g, " ");
|
|
21351
21472
|
const norm = (raw) => {
|
|
21352
21473
|
const v = (raw || "").toLowerCase();
|
|
21353
21474
|
return LEVELS.includes(v) ? v : "med";
|
|
@@ -21374,11 +21495,11 @@ function stripOldHeader(text5) {
|
|
|
21374
21495
|
}
|
|
21375
21496
|
function cmdMigrate(opts) {
|
|
21376
21497
|
const dry = opts.dryRun === true;
|
|
21377
|
-
const files2 = walkMd(TODO).filter((f) =>
|
|
21498
|
+
const files2 = walkMd(TODO).filter((f) => path65.basename(f) !== "README.md");
|
|
21378
21499
|
let changed = 0;
|
|
21379
21500
|
let skipped = 0;
|
|
21380
21501
|
for (const file of files2) {
|
|
21381
|
-
const text5 =
|
|
21502
|
+
const text5 = fs61.readFileSync(file, "utf8");
|
|
21382
21503
|
if (text5.trimStart().startsWith("---")) {
|
|
21383
21504
|
skipped++;
|
|
21384
21505
|
continue;
|
|
@@ -21387,7 +21508,7 @@ function cmdMigrate(opts) {
|
|
|
21387
21508
|
const out = serializeFrontmatter(meta) + "\n\n" + stripOldHeader(text5).replace(/^\n+/, "");
|
|
21388
21509
|
if (dry) say(`would migrate ${rel(file)} (priority=${meta.priority})`);
|
|
21389
21510
|
else {
|
|
21390
|
-
|
|
21511
|
+
fs61.writeFileSync(file, out.endsWith("\n") ? out : out + "\n");
|
|
21391
21512
|
say(`migrated ${rel(file)}`);
|
|
21392
21513
|
}
|
|
21393
21514
|
changed++;
|
|
@@ -21408,10 +21529,10 @@ var init_misc = __esm({
|
|
|
21408
21529
|
});
|
|
21409
21530
|
|
|
21410
21531
|
// ../cli/src/lib/group-close.ts
|
|
21411
|
-
import
|
|
21532
|
+
import fs62 from "node:fs";
|
|
21412
21533
|
function groupCloses(rootFile) {
|
|
21413
21534
|
try {
|
|
21414
|
-
return groupCloseOf(
|
|
21535
|
+
return groupCloseOf(fs62.readFileSync(rootFile, "utf8"));
|
|
21415
21536
|
} catch {
|
|
21416
21537
|
return false;
|
|
21417
21538
|
}
|
|
@@ -21424,12 +21545,12 @@ var init_group_close = __esm({
|
|
|
21424
21545
|
});
|
|
21425
21546
|
|
|
21426
21547
|
// ../cli/src/commands/remove.ts
|
|
21427
|
-
import
|
|
21428
|
-
import
|
|
21548
|
+
import fs63 from "node:fs";
|
|
21549
|
+
import path66 from "node:path";
|
|
21429
21550
|
function leavingIds(id, found2) {
|
|
21430
21551
|
const ids = /* @__PURE__ */ new Set([id]);
|
|
21431
21552
|
if (found2.kind === "group") {
|
|
21432
|
-
const names4 = [...walkMd(found2.target), ...walkDirs(found2.target)].map((f) =>
|
|
21553
|
+
const names4 = [...walkMd(found2.target), ...walkDirs(found2.target)].map((f) => path66.basename(f));
|
|
21433
21554
|
for (const name of names4) {
|
|
21434
21555
|
const n = idPrefix(name);
|
|
21435
21556
|
if (n !== null) ids.add(n);
|
|
@@ -21441,10 +21562,10 @@ function findMentions(ids) {
|
|
|
21441
21562
|
const hits = [];
|
|
21442
21563
|
const re = new RegExp(ids.map((id) => `#${id}(?!\\d)`).join("|"));
|
|
21443
21564
|
for (const dir of [TODO, MEMORY]) {
|
|
21444
|
-
if (!
|
|
21565
|
+
if (!fs63.existsSync(dir)) continue;
|
|
21445
21566
|
for (const file of walkMd(dir)) {
|
|
21446
|
-
if (
|
|
21447
|
-
const lines =
|
|
21567
|
+
if (path66.basename(file) === "README.md") continue;
|
|
21568
|
+
const lines = fs63.readFileSync(file, "utf8").split("\n");
|
|
21448
21569
|
const fmEnd = frontmatterEnd(lines);
|
|
21449
21570
|
lines.forEach((line, i) => {
|
|
21450
21571
|
if (!re.test(line)) return;
|
|
@@ -21460,12 +21581,12 @@ function findMentions(ids) {
|
|
|
21460
21581
|
return hits;
|
|
21461
21582
|
}
|
|
21462
21583
|
function leavingCards(id, found2) {
|
|
21463
|
-
const own = found2.kind === "group" ?
|
|
21584
|
+
const own = found2.kind === "group" ? path66.join(found2.target, "root.md") : found2.target;
|
|
21464
21585
|
const cards = [{ id, file: own }];
|
|
21465
21586
|
if (found2.kind !== "group") return cards;
|
|
21466
21587
|
for (const file of walkMd(found2.target)) {
|
|
21467
21588
|
if (file === own) continue;
|
|
21468
|
-
const n = idPrefix(
|
|
21589
|
+
const n = idPrefix(path66.basename(file));
|
|
21469
21590
|
if (n !== null) cards.push({ id: n, file });
|
|
21470
21591
|
}
|
|
21471
21592
|
return cards;
|
|
@@ -21474,10 +21595,10 @@ function stampLeaving(cards, rejection) {
|
|
|
21474
21595
|
const day = formatDay();
|
|
21475
21596
|
const stamp4 = rejection && !rejection.discard ? { reason: rejection.reason, at: (/* @__PURE__ */ new Date()).toISOString(), by: insideRun() ? "agent" : "user" } : rejection && { reason: rejection.reason };
|
|
21476
21597
|
for (const card of cards) {
|
|
21477
|
-
if (!
|
|
21478
|
-
const text5 =
|
|
21598
|
+
if (!fs63.existsSync(card.file)) continue;
|
|
21599
|
+
const text5 = fs63.readFileSync(card.file, "utf8");
|
|
21479
21600
|
const stamped = stampLeavingIn(text5, day, stamp4);
|
|
21480
|
-
if (stamped !== text5)
|
|
21601
|
+
if (stamped !== text5) fs63.writeFileSync(card.file, stamped);
|
|
21481
21602
|
}
|
|
21482
21603
|
}
|
|
21483
21604
|
function unfinishedDelivery(id) {
|
|
@@ -21493,7 +21614,7 @@ function unfinishedDelivery(id) {
|
|
|
21493
21614
|
}[gap];
|
|
21494
21615
|
return `#${id} is not finished: ${what}.`;
|
|
21495
21616
|
}
|
|
21496
|
-
const missing = planDeliverables(card.body).filter((src) => src.startsWith(".assets/")).find((src) => !
|
|
21617
|
+
const missing = planDeliverables(card.body).filter((src) => src.startsWith(".assets/")).find((src) => !fs63.existsSync(path66.join(ASSETS, src.slice(".assets/".length))));
|
|
21497
21618
|
return missing ? `#${id} is not finished: ${missing} is not on this machine.` : null;
|
|
21498
21619
|
}
|
|
21499
21620
|
function agentDecisions(text5) {
|
|
@@ -21520,8 +21641,8 @@ function removeCard2(id, metric, options) {
|
|
|
21520
21641
|
const unfinished = metric === "completed" ? unfinishedDelivery(id) : null;
|
|
21521
21642
|
if (unfinished) die(unfinished, { kind: "plan-delivery-unfinished" });
|
|
21522
21643
|
const dest = options.cleanupDiscarded && locateArchived(id) ? null : archiveDest(found2);
|
|
21523
|
-
const cardFile3 = found2.kind === "group" ?
|
|
21524
|
-
const cardText =
|
|
21644
|
+
const cardFile3 = found2.kind === "group" ? path66.join(found2.target, "root.md") : found2.target;
|
|
21645
|
+
const cardText = fs63.existsSync(cardFile3) ? fs63.readFileSync(cardFile3, "utf8") : "";
|
|
21525
21646
|
const { meta: cardMeta3 } = parseFrontmatter(cardText);
|
|
21526
21647
|
const alsoRemoved = found2.kind === "group" ? walkMd(found2.target).filter((f) => f !== cardFile3).map((f) => rel(f)).sort() : [];
|
|
21527
21648
|
const leftIds = leavingIds(id, found2);
|
|
@@ -21552,10 +21673,10 @@ function removeCard2(id, metric, options) {
|
|
|
21552
21673
|
const groupRoot = found2.kind === "file" && !options.closing ? enclosingGroupRoot(found2.target) : null;
|
|
21553
21674
|
if (dest) {
|
|
21554
21675
|
stampLeaving(leaving, metric === "rejected" ? { reason: options.reason?.trim() ?? "", discard: options.discard === true } : null);
|
|
21555
|
-
|
|
21556
|
-
|
|
21676
|
+
fs63.mkdirSync(ARCHIVE, { recursive: true });
|
|
21677
|
+
fs63.renameSync(found2.target, dest);
|
|
21557
21678
|
} else {
|
|
21558
|
-
|
|
21679
|
+
fs63.rmSync(found2.target, { recursive: true, force: true });
|
|
21559
21680
|
}
|
|
21560
21681
|
const removedRefs = stripReadmeRefs(found2);
|
|
21561
21682
|
let marked2 = null;
|
|
@@ -21604,14 +21725,14 @@ function removeCard2(id, metric, options) {
|
|
|
21604
21725
|
};
|
|
21605
21726
|
}
|
|
21606
21727
|
function closeGroup(rootFile) {
|
|
21607
|
-
const rootId = idPrefix(
|
|
21728
|
+
const rootId = idPrefix(path66.basename(path66.dirname(rootFile)));
|
|
21608
21729
|
if (rootId === null) return null;
|
|
21609
21730
|
if (runnerProject(KANBAN)) return null;
|
|
21610
21731
|
if (!groupCloses(rootFile)) return null;
|
|
21611
21732
|
say(`
|
|
21612
21733
|
every subtask line on #${rootId} is resolved \u2014 closing the group:`);
|
|
21613
21734
|
try {
|
|
21614
|
-
const { meta, body } = parseFrontmatter(
|
|
21735
|
+
const { meta, body } = parseFrontmatter(fs63.readFileSync(rootFile, "utf8"));
|
|
21615
21736
|
const res = cmdRemove(rootId, "completed", { closing: true });
|
|
21616
21737
|
return {
|
|
21617
21738
|
id: rootId,
|
|
@@ -21668,7 +21789,7 @@ function cleanupDiscardedCards(sessionId) {
|
|
|
21668
21789
|
if (card.pending) throw new Error(`Discarding #${card.id} did not finish. Retry the discard.`);
|
|
21669
21790
|
while (locate(card.id)) cmdRemove(card.id, "rejected", { discard: true, cleanupDiscarded: true });
|
|
21670
21791
|
dropCrossRefs(card.id);
|
|
21671
|
-
stripReadmeRefs({ kind: "file", rel:
|
|
21792
|
+
stripReadmeRefs({ kind: "file", rel: path66.relative(TODO, path66.resolve(REPO_ROOT, card.path)).split(path66.sep).join("/") });
|
|
21672
21793
|
}
|
|
21673
21794
|
return discarded.map((c) => c.id);
|
|
21674
21795
|
});
|
|
@@ -21754,8 +21875,8 @@ var init_memory_file = __esm({
|
|
|
21754
21875
|
});
|
|
21755
21876
|
|
|
21756
21877
|
// ../cli/src/commands/rule.ts
|
|
21757
|
-
import
|
|
21758
|
-
import
|
|
21878
|
+
import fs64 from "node:fs";
|
|
21879
|
+
import path67 from "node:path";
|
|
21759
21880
|
function cmdRule(askedName, flags) {
|
|
21760
21881
|
const agent = askedName.trim();
|
|
21761
21882
|
if (!agentRoster().some((a) => a.name === agent)) {
|
|
@@ -21764,7 +21885,7 @@ function cmdRule(askedName, flags) {
|
|
|
21764
21885
|
const rule = readRuleText(flags.file, flags.text);
|
|
21765
21886
|
const res = setAgentRule(agent, rule);
|
|
21766
21887
|
if (!res.ok) die(res.error);
|
|
21767
|
-
const file = rel(
|
|
21888
|
+
const file = rel(path67.join(RULES, `${agent}.md`));
|
|
21768
21889
|
say(rule ? `wrote the \`${agent}\` rule (${file})` : `cleared the \`${agent}\` rule`);
|
|
21769
21890
|
return { agent, file, cleared: !rule };
|
|
21770
21891
|
}
|
|
@@ -21772,7 +21893,7 @@ function readRuleText(file, text5) {
|
|
|
21772
21893
|
if (file !== void 0 && text5 !== void 0) die("pass --file or --text, not both");
|
|
21773
21894
|
if (file !== void 0) {
|
|
21774
21895
|
try {
|
|
21775
|
-
return
|
|
21896
|
+
return fs64.readFileSync(file, "utf8").trim();
|
|
21776
21897
|
} catch {
|
|
21777
21898
|
die(`can't read ${file} \u2014 write the rule to a file, then pass its path`);
|
|
21778
21899
|
}
|
|
@@ -21899,22 +22020,22 @@ var init_storyboard = __esm({
|
|
|
21899
22020
|
});
|
|
21900
22021
|
|
|
21901
22022
|
// ../cli/src/lib/spec-contract.ts
|
|
21902
|
-
import
|
|
21903
|
-
import
|
|
22023
|
+
import fs65 from "node:fs";
|
|
22024
|
+
import path68 from "node:path";
|
|
21904
22025
|
function snapshotSpecs() {
|
|
21905
22026
|
const out = /* @__PURE__ */ new Map();
|
|
21906
|
-
if (!
|
|
22027
|
+
if (!fs65.existsSync(TODO)) return out;
|
|
21907
22028
|
for (const file of walkMd(TODO)) {
|
|
21908
|
-
if (isLegacyRecurring(
|
|
21909
|
-
const id = idPrefix(
|
|
21910
|
-
if (id !== null) out.set(file, { id, text:
|
|
22029
|
+
if (isLegacyRecurring(path68.relative(TODO, file))) continue;
|
|
22030
|
+
const id = idPrefix(path68.basename(file) === "root.md" ? path68.basename(path68.dirname(file)) : path68.basename(file));
|
|
22031
|
+
if (id !== null) out.set(file, { id, text: fs65.readFileSync(file, "utf8") });
|
|
21911
22032
|
}
|
|
21912
22033
|
return out;
|
|
21913
22034
|
}
|
|
21914
22035
|
function enclosingGroups(file) {
|
|
21915
22036
|
const ids = [];
|
|
21916
22037
|
for (let root = enclosingGroupRoot(file); root; root = enclosingGroupRoot(root)) {
|
|
21917
|
-
const id = idPrefix(
|
|
22038
|
+
const id = idPrefix(path68.basename(path68.dirname(root)));
|
|
21918
22039
|
if (id !== null) ids.push(id);
|
|
21919
22040
|
}
|
|
21920
22041
|
return ids;
|
|
@@ -22083,9 +22204,9 @@ function validateSpec(file, text5, id) {
|
|
|
22083
22204
|
}
|
|
22084
22205
|
function inside(dir, name) {
|
|
22085
22206
|
try {
|
|
22086
|
-
const root =
|
|
22087
|
-
const real2 =
|
|
22088
|
-
return real2.startsWith(root +
|
|
22207
|
+
const root = fs65.realpathSync(dir);
|
|
22208
|
+
const real2 = fs65.realpathSync(path68.join(dir, name));
|
|
22209
|
+
return real2.startsWith(root + path68.sep) && fs65.statSync(real2).isFile() ? real2 : null;
|
|
22089
22210
|
} catch {
|
|
22090
22211
|
return null;
|
|
22091
22212
|
}
|
|
@@ -22103,7 +22224,7 @@ function variantsProblem(raw, src, id) {
|
|
|
22103
22224
|
if (files2.length < 2 || !files2.every((f) => typeof f === "string" && f)) return `variants needs at least two labels, each naming a file. ${shape}`;
|
|
22104
22225
|
if (!files2.includes(src)) return `src="${src}" is not one of the variants. Set src to the version shown first.`;
|
|
22105
22226
|
if (id === void 0) return null;
|
|
22106
|
-
const dir =
|
|
22227
|
+
const dir = path68.join(ASSETS, String(id));
|
|
22107
22228
|
for (const f of files2) {
|
|
22108
22229
|
const parts = f.split("/");
|
|
22109
22230
|
if (parts.length !== 3 || parts[0] !== ".assets" || parts[1] !== String(id) || !inside(dir, parts[2])) {
|
|
@@ -22113,7 +22234,7 @@ function variantsProblem(raw, src, id) {
|
|
|
22113
22234
|
return null;
|
|
22114
22235
|
}
|
|
22115
22236
|
function compareProblem(attrs, id) {
|
|
22116
|
-
const dir =
|
|
22237
|
+
const dir = path68.join(ASSETS, String(id));
|
|
22117
22238
|
const src = (message) => ({ rule: "compare-src", message });
|
|
22118
22239
|
const shots = (message) => ({ rule: "compare-shots", message });
|
|
22119
22240
|
for (const side of ["a", "b"]) {
|
|
@@ -22133,7 +22254,7 @@ function compareProblem(attrs, id) {
|
|
|
22133
22254
|
const file = inside(dir, named2.name);
|
|
22134
22255
|
if (!file) return { message: `Compare shots ${at} names no file in this card's asset folder. Write the JSON there or fix the path.` };
|
|
22135
22256
|
try {
|
|
22136
|
-
return { data: JSON.parse(
|
|
22257
|
+
return { data: JSON.parse(fs65.readFileSync(file, "utf8")) };
|
|
22137
22258
|
} catch {
|
|
22138
22259
|
return { message: `${at} is not valid JSON.` };
|
|
22139
22260
|
}
|
|
@@ -22153,7 +22274,7 @@ function compareProblem(attrs, id) {
|
|
|
22153
22274
|
}
|
|
22154
22275
|
function validateStoryboards(file, id, text5) {
|
|
22155
22276
|
const errors = [];
|
|
22156
|
-
const dir =
|
|
22277
|
+
const dir = path68.join(ASSETS, String(id));
|
|
22157
22278
|
for (const marker of storyboardMarkers(text5)) {
|
|
22158
22279
|
const named2 = assetName(marker.src, id, ["json"]);
|
|
22159
22280
|
const add = (message) => errors.push({ file: rel(file), line: marker.line, rule: "storyboard-src", message });
|
|
@@ -22302,8 +22423,8 @@ var init_mockups = __esm({
|
|
|
22302
22423
|
});
|
|
22303
22424
|
|
|
22304
22425
|
// ../cli/src/commands/spec-write.ts
|
|
22305
|
-
import
|
|
22306
|
-
import
|
|
22426
|
+
import fs66 from "node:fs";
|
|
22427
|
+
import path69 from "node:path";
|
|
22307
22428
|
function cmdSpecWrite(id, askedName, flags) {
|
|
22308
22429
|
const agent = findSpecAgent(askedName);
|
|
22309
22430
|
if (!agent) die(notAnAgent(askedName), { kind: "no-such-spec-agent", specAgent: askedName });
|
|
@@ -22311,11 +22432,11 @@ function cmdSpecWrite(id, askedName, flags) {
|
|
|
22311
22432
|
const section = readSection(flags.file, flags.text);
|
|
22312
22433
|
const found2 = locate(id);
|
|
22313
22434
|
if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
|
|
22314
|
-
const file = found2.kind === "group" ?
|
|
22315
|
-
const { meta, body } = parseFrontmatter(
|
|
22435
|
+
const file = found2.kind === "group" ? path69.join(found2.target, "root.md") : found2.target;
|
|
22436
|
+
const { meta, body } = parseFrontmatter(fs66.readFileSync(file, "utf8"));
|
|
22316
22437
|
if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
|
|
22317
22438
|
const { body: next, replaced } = splice(body, name, section);
|
|
22318
|
-
|
|
22439
|
+
fs66.writeFileSync(file, serializeFrontmatter(meta) + "\n" + next);
|
|
22319
22440
|
say(`${replaced ? "rewrote" : "wrote"} the \`${name}\` section on #${id} (${rel(file)})`);
|
|
22320
22441
|
return { id, specAgent: name, replaced, file: rel(file) };
|
|
22321
22442
|
}
|
|
@@ -22324,7 +22445,7 @@ function readSection(file, text5) {
|
|
|
22324
22445
|
let raw;
|
|
22325
22446
|
if (file !== void 0) {
|
|
22326
22447
|
try {
|
|
22327
|
-
raw =
|
|
22448
|
+
raw = fs66.readFileSync(file, "utf8");
|
|
22328
22449
|
} catch {
|
|
22329
22450
|
die(`can't read ${file} \u2014 write your section to a file, then pass its path`);
|
|
22330
22451
|
}
|
|
@@ -22790,11 +22911,11 @@ var init_shared = __esm({
|
|
|
22790
22911
|
});
|
|
22791
22912
|
|
|
22792
22913
|
// ../cli/src/commands/goal.ts
|
|
22793
|
-
import
|
|
22914
|
+
import fs67 from "node:fs";
|
|
22794
22915
|
function load() {
|
|
22795
22916
|
const file = goalsFile();
|
|
22796
22917
|
if (!file) die("Goals exist only inside a WildCrane goal discussion.");
|
|
22797
|
-
const held4 = JSON.parse(
|
|
22918
|
+
const held4 = JSON.parse(fs67.readFileSync(file, "utf8"));
|
|
22798
22919
|
if (held4.goals !== void 0 && held4.ideas !== void 0 && JSON.stringify(held4.goals) !== JSON.stringify(held4.ideas)) die("goals and ideas do not match.");
|
|
22799
22920
|
const goals = held4.goals ?? held4.ideas;
|
|
22800
22921
|
if (!Array.isArray(goals)) die("The goals array is missing or invalid.");
|
|
@@ -22819,7 +22940,7 @@ function splitBody2(text5) {
|
|
|
22819
22940
|
}
|
|
22820
22941
|
function readBody(bodyFile) {
|
|
22821
22942
|
if (!bodyFile) die("--body-file is required; write the goal in a writable temporary file first.");
|
|
22822
|
-
const text5 =
|
|
22943
|
+
const text5 = fs67.readFileSync(bodyFile, "utf8");
|
|
22823
22944
|
if (!text5.trim()) die("The goal must not be empty.");
|
|
22824
22945
|
return splitBody2(text5);
|
|
22825
22946
|
}
|
|
@@ -22831,7 +22952,7 @@ function named(held4, id) {
|
|
|
22831
22952
|
function cmdGoal(args, opts) {
|
|
22832
22953
|
const sub = args[0] ?? "";
|
|
22833
22954
|
const { file, held: held4 } = load();
|
|
22834
|
-
const write2 = () =>
|
|
22955
|
+
const write2 = () => fs67.writeFileSync(file, `${JSON.stringify({ workflows: held4.workflows, ideas: held4.goals }, null, 2)}
|
|
22835
22956
|
`);
|
|
22836
22957
|
if (sub === "new") {
|
|
22837
22958
|
const title = opts.title?.trim();
|
|
@@ -22901,8 +23022,8 @@ __export(board_exports, {
|
|
|
22901
23022
|
buildBoardProgram: () => buildBoardProgram
|
|
22902
23023
|
});
|
|
22903
23024
|
import crypto4 from "node:crypto";
|
|
22904
|
-
import
|
|
22905
|
-
import
|
|
23025
|
+
import fs68 from "node:fs";
|
|
23026
|
+
import path70 from "node:path";
|
|
22906
23027
|
async function dispatch(move2, cmd, args, opts, cli) {
|
|
22907
23028
|
const ctx = ctxOf(cmd, cli.program);
|
|
22908
23029
|
const data = await runAction(ctx, {}, async () => {
|
|
@@ -22941,7 +23062,7 @@ async function cloudCard(task, opts) {
|
|
|
22941
23062
|
let request;
|
|
22942
23063
|
if (typeof opts.bodyFile === "string") {
|
|
22943
23064
|
try {
|
|
22944
|
-
request =
|
|
23065
|
+
request = fs68.readFileSync(opts.bodyFile, "utf8");
|
|
22945
23066
|
} catch {
|
|
22946
23067
|
}
|
|
22947
23068
|
}
|
|
@@ -22971,7 +23092,7 @@ async function cloudCard(task, opts) {
|
|
|
22971
23092
|
const file = creationReceipt(`${checked.value}:${requestId}`, slot);
|
|
22972
23093
|
const journal = readCreation(file, said3);
|
|
22973
23094
|
if (!journal.made) {
|
|
22974
|
-
const key = `akb-${
|
|
23095
|
+
const key = `akb-${path70.basename(file, ".json").slice(0, 40)}`;
|
|
22975
23096
|
const made = await createProjectTask(checked.value, journal.said, key);
|
|
22976
23097
|
if (!made.ok) throw new BoardError(creationError(`Cloud did not make the task: ${made.error}`, triage), { kind: "refused" });
|
|
22977
23098
|
journal.made = made.value;
|
|
@@ -23116,16 +23237,16 @@ var init_board2 = __esm({
|
|
|
23116
23237
|
});
|
|
23117
23238
|
|
|
23118
23239
|
// ../cli/src/lib/board-cli.ts
|
|
23119
|
-
import
|
|
23120
|
-
import
|
|
23240
|
+
import fs69 from "node:fs";
|
|
23241
|
+
import path71 from "node:path";
|
|
23121
23242
|
function useBoard(found2, dirNamed) {
|
|
23122
|
-
if (!found2.named && found2.board ===
|
|
23243
|
+
if (!found2.named && found2.board === path71.join(found2.root, "docs", "kanban")) setBoardRoot(found2.root, dirNamed);
|
|
23123
23244
|
else setBoardDir(found2.board, found2.root);
|
|
23124
23245
|
useProjectState();
|
|
23125
23246
|
}
|
|
23126
23247
|
function whole(found2, move2) {
|
|
23127
23248
|
if (move2 === MAKES_A_BOARD) return found2;
|
|
23128
|
-
if (!
|
|
23249
|
+
if (!fs69.existsSync(path71.join(found2.board, "todo"))) {
|
|
23129
23250
|
die(`the board in ${found2.board} has no todo/ \u2014 run \`init\` to add what is missing.`, {
|
|
23130
23251
|
kind: "board-incomplete",
|
|
23131
23252
|
dir: found2.root
|
|
@@ -23134,13 +23255,13 @@ function whole(found2, move2) {
|
|
|
23134
23255
|
return found2;
|
|
23135
23256
|
}
|
|
23136
23257
|
function findBoardUpward(from, move2) {
|
|
23137
|
-
let dir =
|
|
23258
|
+
let dir = path71.resolve(from);
|
|
23138
23259
|
for (; ; ) {
|
|
23139
|
-
if (hasBoard(dir)) return { root: dir, board:
|
|
23260
|
+
if (hasBoard(dir)) return { root: dir, board: path71.join(dir, "docs", "kanban") };
|
|
23140
23261
|
if (isBoardDir(dir)) return { root: projectRootOf(dir), board: dir };
|
|
23141
|
-
const kept2 =
|
|
23142
|
-
if (move2 !== MAKES_A_BOARD &&
|
|
23143
|
-
const up =
|
|
23262
|
+
const kept2 = path71.join(dir, ".akb", "kanban");
|
|
23263
|
+
if (move2 !== MAKES_A_BOARD && fs69.existsSync(path71.join(kept2, "todo"))) return { root: dir, board: kept2 };
|
|
23264
|
+
const up = path71.dirname(dir);
|
|
23144
23265
|
if (up === dir) return null;
|
|
23145
23266
|
dir = up;
|
|
23146
23267
|
}
|
|
@@ -23148,11 +23269,11 @@ function findBoardUpward(from, move2) {
|
|
|
23148
23269
|
function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
|
|
23149
23270
|
const named2 = board2 ?? boardFromEnv();
|
|
23150
23271
|
if (named2) {
|
|
23151
|
-
const folder =
|
|
23152
|
-
if (!
|
|
23272
|
+
const folder = path71.resolve(cwd, named2);
|
|
23273
|
+
if (!fs69.existsSync(folder) && move2 !== MAKES_A_BOARD) {
|
|
23153
23274
|
die(`no such folder: ${folder}`, { kind: "no-such-folder", dir: folder });
|
|
23154
23275
|
}
|
|
23155
|
-
if (
|
|
23276
|
+
if (fs69.existsSync(folder) && !isBoardDir(folder) && move2 !== MAKES_A_BOARD) {
|
|
23156
23277
|
die(`${folder} is not a board \u2014 it has no todo/ and config.md. ${installHint}\u3002`, {
|
|
23157
23278
|
kind: "no-board",
|
|
23158
23279
|
dir: folder
|
|
@@ -23161,24 +23282,24 @@ function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
|
|
|
23161
23282
|
return { root: projectRootOf(folder), board: folder, named: true };
|
|
23162
23283
|
}
|
|
23163
23284
|
if (dir !== null) {
|
|
23164
|
-
const root =
|
|
23165
|
-
if (!
|
|
23166
|
-
const kept2 =
|
|
23167
|
-
if (!hasBoard(root) && move2 !== MAKES_A_BOARD &&
|
|
23285
|
+
const root = path71.resolve(dir);
|
|
23286
|
+
if (!fs69.existsSync(root)) die(`no such folder: ${root}`, { kind: "no-such-folder", dir: root });
|
|
23287
|
+
const kept2 = path71.join(root, ".akb", "kanban");
|
|
23288
|
+
if (!hasBoard(root) && move2 !== MAKES_A_BOARD && fs69.existsSync(path71.join(kept2, "todo"))) return whole({ root, board: kept2 }, move2);
|
|
23168
23289
|
if (!hasBoard(root) && move2 !== MAKES_A_BOARD) {
|
|
23169
23290
|
die(`no board in ${root} \u2014 it has no docs/kanban/. ${installHint}\u3002`, {
|
|
23170
23291
|
kind: "no-board",
|
|
23171
23292
|
dir: root
|
|
23172
23293
|
});
|
|
23173
23294
|
}
|
|
23174
|
-
return whole({ root, board:
|
|
23295
|
+
return whole({ root, board: path71.join(root, "docs", "kanban") }, move2);
|
|
23175
23296
|
}
|
|
23176
23297
|
const found2 = findBoardUpward(cwd, move2);
|
|
23177
23298
|
if (found2) return whole(found2, move2);
|
|
23178
|
-
if (move2 === MAKES_A_BOARD) return { root:
|
|
23299
|
+
if (move2 === MAKES_A_BOARD) return { root: path71.resolve(cwd), board: path71.join(path71.resolve(cwd), "docs", "kanban") };
|
|
23179
23300
|
die(
|
|
23180
|
-
`no board here. Looked in ${
|
|
23181
|
-
{ kind: "no-board", dir:
|
|
23301
|
+
`no board here. Looked in ${path71.resolve(cwd)} and every folder above it for docs/kanban/. ${installHint}\uFF0C\u6216\u7528 --board \u6307\u5B9A\u5DF2\u6709\u770B\u677F\u3002`,
|
|
23302
|
+
{ kind: "no-board", dir: path71.resolve(cwd) }
|
|
23182
23303
|
);
|
|
23183
23304
|
}
|
|
23184
23305
|
function editDistance(a, b) {
|
|
@@ -23260,8 +23381,8 @@ var init_board_cli = __esm({
|
|
|
23260
23381
|
init_io();
|
|
23261
23382
|
init_pointer();
|
|
23262
23383
|
MAKES_A_BOARD = "init";
|
|
23263
|
-
hasBoard = (dir) =>
|
|
23264
|
-
isBoardDir = (dir) =>
|
|
23384
|
+
hasBoard = (dir) => fs69.existsSync(path71.join(dir, "docs", "kanban"));
|
|
23385
|
+
isBoardDir = (dir) => fs69.existsSync(path71.join(dir, "todo")) && fs69.existsSync(path71.join(dir, "config.md"));
|
|
23265
23386
|
boardFromEnv = () => {
|
|
23266
23387
|
const named2 = process.env.AI4KANBAN_BOARD?.trim();
|
|
23267
23388
|
return named2 ? named2 : null;
|
|
@@ -23528,8 +23649,8 @@ var init_guide = __esm({
|
|
|
23528
23649
|
});
|
|
23529
23650
|
|
|
23530
23651
|
// ../cli/src/lib/agent/approve.ts
|
|
23531
|
-
import
|
|
23532
|
-
import
|
|
23652
|
+
import fs75 from "node:fs";
|
|
23653
|
+
import path77 from "node:path";
|
|
23533
23654
|
function approvalRequest(req) {
|
|
23534
23655
|
const id = req.id;
|
|
23535
23656
|
const card = id === void 0 ? null : findCard(id);
|
|
@@ -23547,12 +23668,12 @@ function advancePhase(id) {
|
|
|
23547
23668
|
const card = findCard(id);
|
|
23548
23669
|
const found2 = locate(id);
|
|
23549
23670
|
if (!card || !found2 || !awaitingApproval(card)) return { error: `#${id} is no longer waiting on approval.` };
|
|
23550
|
-
const file = found2.kind === "group" ?
|
|
23551
|
-
const { meta, body } = parseFrontmatter(
|
|
23671
|
+
const file = found2.kind === "group" ? path77.join(found2.target, "root.md") : found2.target;
|
|
23672
|
+
const { meta, body } = parseFrontmatter(fs75.readFileSync(file, "utf8"));
|
|
23552
23673
|
if (!meta) return { error: `#${id} has no frontmatter` };
|
|
23553
23674
|
meta.phase = card.stage + 1;
|
|
23554
23675
|
meta.status = "todo";
|
|
23555
|
-
|
|
23676
|
+
fs75.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
|
|
23556
23677
|
return null;
|
|
23557
23678
|
}
|
|
23558
23679
|
var init_approve = __esm({
|
|
@@ -23676,16 +23797,16 @@ var init_start = __esm({
|
|
|
23676
23797
|
});
|
|
23677
23798
|
|
|
23678
23799
|
// ../cli/src/lib/agent/task-chats.ts
|
|
23679
|
-
import
|
|
23680
|
-
import
|
|
23800
|
+
import fs76 from "node:fs";
|
|
23801
|
+
import path78 from "node:path";
|
|
23681
23802
|
function taskChatFile(task, home3) {
|
|
23682
23803
|
if (!TASK.test(task)) throw new Error(`${task} is not a task id.`);
|
|
23683
|
-
return
|
|
23804
|
+
return path78.join(taskChatsDir(home3), `${task}.jsonl`);
|
|
23684
23805
|
}
|
|
23685
23806
|
function readTaskChat(task, home3) {
|
|
23686
23807
|
let text5;
|
|
23687
23808
|
try {
|
|
23688
|
-
text5 =
|
|
23809
|
+
text5 = fs76.readFileSync(taskChatFile(task, home3), "utf8");
|
|
23689
23810
|
} catch {
|
|
23690
23811
|
return [];
|
|
23691
23812
|
}
|
|
@@ -23706,13 +23827,13 @@ var init_task_chats = __esm({
|
|
|
23706
23827
|
"use strict";
|
|
23707
23828
|
init_home();
|
|
23708
23829
|
TASK = /^[\w-]{1,100}$/;
|
|
23709
|
-
taskChatsDir = (home3 = machineHome()) =>
|
|
23830
|
+
taskChatsDir = (home3 = machineHome()) => path78.join(home3, "chats");
|
|
23710
23831
|
}
|
|
23711
23832
|
});
|
|
23712
23833
|
|
|
23713
23834
|
// ../cli/src/lib/agent/memory-review.ts
|
|
23714
|
-
import
|
|
23715
|
-
import
|
|
23835
|
+
import fs77 from "node:fs";
|
|
23836
|
+
import path79 from "node:path";
|
|
23716
23837
|
function taskChatsToReview(tasks) {
|
|
23717
23838
|
const out = [];
|
|
23718
23839
|
for (const ref of [...new Set(tasks)]) {
|
|
@@ -23734,8 +23855,8 @@ function readArchived2(id) {
|
|
|
23734
23855
|
const found2 = Number.isInteger(id) && id > 0 ? locateArchived(id) : null;
|
|
23735
23856
|
if (!found2) return null;
|
|
23736
23857
|
try {
|
|
23737
|
-
const file = found2.kind === "group" ?
|
|
23738
|
-
const { meta, body } = parseFrontmatter(
|
|
23858
|
+
const file = found2.kind === "group" ? path79.join(found2.target, "root.md") : found2.target;
|
|
23859
|
+
const { meta, body } = parseFrontmatter(fs77.readFileSync(file, "utf8"));
|
|
23739
23860
|
if (!meta) return null;
|
|
23740
23861
|
return { id, title: meta.title, modules: meta.modules ?? [], rejected: meta.rejected, body };
|
|
23741
23862
|
} catch {
|
|
@@ -23804,17 +23925,17 @@ var init_memory_review = __esm({
|
|
|
23804
23925
|
});
|
|
23805
23926
|
|
|
23806
23927
|
// ../cli/src/lib/agent/dismissal-review.ts
|
|
23807
|
-
import
|
|
23808
|
-
import
|
|
23928
|
+
import fs78 from "node:fs";
|
|
23929
|
+
import path80 from "node:path";
|
|
23809
23930
|
function rejectionsToReview() {
|
|
23810
|
-
const dir =
|
|
23811
|
-
if (!
|
|
23931
|
+
const dir = path80.join(ARCHIVE, REJECTIONS_TO_REVIEW);
|
|
23932
|
+
if (!fs78.existsSync(dir)) return [];
|
|
23812
23933
|
const found2 = [];
|
|
23813
23934
|
for (const file of walkMd(dir)) {
|
|
23814
23935
|
const id = archivedId(file);
|
|
23815
23936
|
if (id === null) continue;
|
|
23816
23937
|
try {
|
|
23817
|
-
const { meta } = parseFrontmatter(
|
|
23938
|
+
const { meta } = parseFrontmatter(fs78.readFileSync(file, "utf8"));
|
|
23818
23939
|
if (!meta?.rejected || meta.rejected_by !== "user" || !meta.rejected_reason.trim()) continue;
|
|
23819
23940
|
found2.push({ id, title: meta.title, file: rel(file), reason: meta.rejected_reason.trim(), rejectedAt: meta.rejected_at });
|
|
23820
23941
|
} catch {
|
|
@@ -23844,7 +23965,7 @@ function citedSources(text5) {
|
|
|
23844
23965
|
function withdrawnSources() {
|
|
23845
23966
|
const cited = [...new Set(plannerCopies("rejected.md").flatMap((file) => {
|
|
23846
23967
|
try {
|
|
23847
|
-
return citedSources(
|
|
23968
|
+
return citedSources(fs78.readFileSync(file, "utf8"));
|
|
23848
23969
|
} catch {
|
|
23849
23970
|
return [];
|
|
23850
23971
|
}
|
|
@@ -23869,8 +23990,8 @@ var init_dismissal_review = __esm({
|
|
|
23869
23990
|
});
|
|
23870
23991
|
|
|
23871
23992
|
// ../cli/src/lib/agent/flow.ts
|
|
23872
|
-
import
|
|
23873
|
-
import
|
|
23993
|
+
import fs79 from "node:fs";
|
|
23994
|
+
import path81 from "node:path";
|
|
23874
23995
|
function readCard2(id, home3 = "board") {
|
|
23875
23996
|
const found2 = home3 === "archive" ? locateArchived(id) : locate(id);
|
|
23876
23997
|
if (!found2) {
|
|
@@ -23879,10 +24000,10 @@ function readCard2(id, home3 = "board") {
|
|
|
23879
24000
|
{ kind: "card-not-found", id }
|
|
23880
24001
|
);
|
|
23881
24002
|
}
|
|
23882
|
-
const file = found2.kind === "group" ?
|
|
24003
|
+
const file = found2.kind === "group" ? path81.join(found2.target, "root.md") : found2.target;
|
|
23883
24004
|
let text5;
|
|
23884
24005
|
try {
|
|
23885
|
-
text5 =
|
|
24006
|
+
text5 = fs79.readFileSync(file, "utf8");
|
|
23886
24007
|
} catch {
|
|
23887
24008
|
die(`#${id} is on the board but ${rel(file)} can't be read.`, { kind: "card-unreadable", id });
|
|
23888
24009
|
}
|
|
@@ -24006,7 +24127,7 @@ function workspaceField(delivery) {
|
|
|
24006
24127
|
if (!delivery?.worktree) return [];
|
|
24007
24128
|
return field("workspace", [
|
|
24008
24129
|
`write code in ${delivery.worktree} \u2014 this delivery's own worktree, on branch ${delivery.branch}.`,
|
|
24009
|
-
|
|
24130
|
+
path81.resolve(process.cwd()).startsWith(worktreeDir(delivery.worktree)) ? `it is your working folder already; the board's own files are NOT in it and never go on that branch.` : `cd into it first \u2014 you are in the project checkout, and code written here is not part of this build. The board's own files are NOT in it and never go on that branch.`,
|
|
24010
24131
|
`every board command names the project's own copy: \`${boardCommandFor()} <command>\`.`,
|
|
24011
24132
|
`${rel(REPO_ROOT)} is the project \u2014 the card, the memory files and the docs are changed there, not here.`
|
|
24012
24133
|
]);
|
|
@@ -24750,7 +24871,7 @@ __export(watch_exports, {
|
|
|
24750
24871
|
watchRun: () => watchRun
|
|
24751
24872
|
});
|
|
24752
24873
|
import { spawn as spawn4 } from "node:child_process";
|
|
24753
|
-
import
|
|
24874
|
+
import fs80 from "node:fs";
|
|
24754
24875
|
async function watchRun(sessionId, resume = startResume) {
|
|
24755
24876
|
const spec = readSpec(sessionId);
|
|
24756
24877
|
const run2 = peekRun(sessionId);
|
|
@@ -24786,11 +24907,11 @@ async function watchRun(sessionId, resume = startResume) {
|
|
|
24786
24907
|
const record = claimed ?? run2;
|
|
24787
24908
|
const sub = record.action === "sub";
|
|
24788
24909
|
if (claim) await setCardStatus(claim.cardId, claim.status);
|
|
24789
|
-
|
|
24790
|
-
const logFile =
|
|
24910
|
+
fs80.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
24911
|
+
const logFile = fs80.createWriteStream(record.logPath, { flags: "a" });
|
|
24791
24912
|
logFile.on("error", () => {
|
|
24792
24913
|
});
|
|
24793
|
-
const eventsFile =
|
|
24914
|
+
const eventsFile = fs80.createWriteStream(eventsPathOf(sessionId), { flags: "a" });
|
|
24794
24915
|
eventsFile.on("error", () => {
|
|
24795
24916
|
});
|
|
24796
24917
|
const event2 = (e) => eventsFile.write(`${JSON.stringify(e)}
|
|
@@ -25379,7 +25500,7 @@ var init_watch = __esm({
|
|
|
25379
25500
|
});
|
|
25380
25501
|
|
|
25381
25502
|
// ../cli/src/kanban.ts
|
|
25382
|
-
import
|
|
25503
|
+
import fs86 from "node:fs";
|
|
25383
25504
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
25384
25505
|
|
|
25385
25506
|
// ../cli/src/lib/cli/agent.ts
|
|
@@ -25868,8 +25989,8 @@ async function testAgent(args) {
|
|
|
25868
25989
|
}
|
|
25869
25990
|
|
|
25870
25991
|
// ../cli/src/commands/cloud.ts
|
|
25871
|
-
import
|
|
25872
|
-
import
|
|
25992
|
+
import fs71 from "node:fs";
|
|
25993
|
+
import path73 from "node:path";
|
|
25873
25994
|
|
|
25874
25995
|
// ../cli/src/lib/cloud/avatar.ts
|
|
25875
25996
|
init_session();
|
|
@@ -25917,13 +26038,13 @@ init_config();
|
|
|
25917
26038
|
init_home();
|
|
25918
26039
|
init_config();
|
|
25919
26040
|
init_session();
|
|
25920
|
-
import
|
|
25921
|
-
import
|
|
26041
|
+
import fs70 from "node:fs";
|
|
26042
|
+
import path72 from "node:path";
|
|
25922
26043
|
var FRESH_MS = 10 * 6e4;
|
|
25923
26044
|
var GRACE_MS = 7 * 24 * 60 * 6e4;
|
|
25924
|
-
var heldFile = () =>
|
|
26045
|
+
var heldFile = () => path72.join(machineHome(), "pro.json");
|
|
25925
26046
|
function forgetPro() {
|
|
25926
|
-
|
|
26047
|
+
fs70.rmSync(heldFile(), { force: true });
|
|
25927
26048
|
}
|
|
25928
26049
|
|
|
25929
26050
|
// ../cli/src/lib/cloud/account.ts
|
|
@@ -26100,10 +26221,10 @@ async function cloudImage(opts, program2) {
|
|
|
26100
26221
|
const prompt = opts.prompt.trim();
|
|
26101
26222
|
if (!prompt) usage("Missing the prompt.", line);
|
|
26102
26223
|
const references = opts.refs.map((file) => {
|
|
26103
|
-
const type = REF_TYPES[
|
|
26224
|
+
const type = REF_TYPES[path73.extname(file).toLowerCase()];
|
|
26104
26225
|
if (!type) usage(`--ref ${file}: use a PNG, JPEG or WebP file.`, line);
|
|
26105
26226
|
try {
|
|
26106
|
-
return `data:${type};base64,${
|
|
26227
|
+
return `data:${type};base64,${fs71.readFileSync(file).toString("base64")}`;
|
|
26107
26228
|
} catch (e) {
|
|
26108
26229
|
usage(`--ref ${file}: ${e instanceof Error ? e.message : String(e)}`, line);
|
|
26109
26230
|
}
|
|
@@ -26209,31 +26330,31 @@ async function generate(g) {
|
|
|
26209
26330
|
refuse(g.failed, g.fallback);
|
|
26210
26331
|
}
|
|
26211
26332
|
const out = g.outFor?.(res, g.out) ?? g.out;
|
|
26212
|
-
|
|
26213
|
-
|
|
26333
|
+
fs71.mkdirSync(path73.dirname(path73.resolve(out)), { recursive: true });
|
|
26334
|
+
fs71.writeFileSync(out, Buffer.from(await res.arrayBuffer()));
|
|
26214
26335
|
say(`${out} (${g.label(res)})`);
|
|
26215
26336
|
return { out };
|
|
26216
26337
|
}
|
|
26217
26338
|
|
|
26218
26339
|
// ../cli/src/commands/cloud-import.ts
|
|
26219
26340
|
import crypto5 from "node:crypto";
|
|
26220
|
-
import
|
|
26221
|
-
import
|
|
26341
|
+
import fs73 from "node:fs";
|
|
26342
|
+
import path75 from "node:path";
|
|
26222
26343
|
|
|
26223
26344
|
// ../cloud/src/board-files.ts
|
|
26224
26345
|
var ROOTS = ["todo/", ".archive/", "memory/", "workflows/", "triage/", "assets/", "rules/"];
|
|
26225
26346
|
var SETTINGS = ["modules.md", "config.md"];
|
|
26226
|
-
var isSettingsPath = (
|
|
26347
|
+
var isSettingsPath = (path87) => SETTINGS.includes(path87) || /^rules\/(?:workflows\/)?[^/]+\.md$/.test(path87);
|
|
26227
26348
|
var BOARD_FILE_LIMIT = 256 * 1024;
|
|
26228
|
-
function isBoardPath(
|
|
26229
|
-
if (!
|
|
26230
|
-
if (
|
|
26231
|
-
if (
|
|
26232
|
-
if (
|
|
26233
|
-
if (SETTINGS.includes(
|
|
26234
|
-
if (
|
|
26235
|
-
if (
|
|
26236
|
-
return ROOTS.some((root) =>
|
|
26349
|
+
function isBoardPath(path87) {
|
|
26350
|
+
if (!path87 || path87.length > 500 || path87.startsWith("/") || path87.includes("\\") || path87.includes("\0")) return false;
|
|
26351
|
+
if (path87.split("/").some((part) => !part || part === "." || part === "..")) return false;
|
|
26352
|
+
if (path87 === "todo/README.md" || path87.split("/").includes(".sync")) return false;
|
|
26353
|
+
if (path87.startsWith("workflows/")) return true;
|
|
26354
|
+
if (SETTINGS.includes(path87)) return true;
|
|
26355
|
+
if (path87.startsWith("rules/")) return isSettingsPath(path87);
|
|
26356
|
+
if (path87.startsWith("assets/")) return path87.endsWith(".json");
|
|
26357
|
+
return ROOTS.some((root) => path87.startsWith(root)) && path87.endsWith(".md");
|
|
26237
26358
|
}
|
|
26238
26359
|
var workflowFolder = (id) => id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
26239
26360
|
|
|
@@ -26337,8 +26458,8 @@ init_bundled();
|
|
|
26337
26458
|
// ../cli/src/lib/agents/shared-memory.ts
|
|
26338
26459
|
init_bundled();
|
|
26339
26460
|
init_parse();
|
|
26340
|
-
import
|
|
26341
|
-
import
|
|
26461
|
+
import fs72 from "node:fs";
|
|
26462
|
+
import path74 from "node:path";
|
|
26342
26463
|
function sharedMemory(board2) {
|
|
26343
26464
|
const declared = /* @__PURE__ */ new Map();
|
|
26344
26465
|
const take = (folder, text5, builtIn) => {
|
|
@@ -26350,16 +26471,16 @@ function sharedMemory(board2) {
|
|
|
26350
26471
|
const at = /^([^/]+)\/AGENT\.md$/.exec(key);
|
|
26351
26472
|
if (at) take(at[1], text5, true);
|
|
26352
26473
|
}
|
|
26353
|
-
const root =
|
|
26474
|
+
const root = path74.join(board2, "agents");
|
|
26354
26475
|
let folders2 = [];
|
|
26355
26476
|
try {
|
|
26356
|
-
folders2 =
|
|
26477
|
+
folders2 = fs72.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
|
|
26357
26478
|
} catch {
|
|
26358
26479
|
}
|
|
26359
26480
|
for (const folder of folders2.sort()) {
|
|
26360
26481
|
let text5 = null;
|
|
26361
26482
|
try {
|
|
26362
|
-
text5 =
|
|
26483
|
+
text5 = fs72.readFileSync(path74.join(root, folder, "AGENT.md"), "utf8");
|
|
26363
26484
|
} catch {
|
|
26364
26485
|
}
|
|
26365
26486
|
take(folder, text5, false);
|
|
@@ -26448,24 +26569,24 @@ async function findProject(asked) {
|
|
|
26448
26569
|
}
|
|
26449
26570
|
throw new Refused(`No project of yours has the id or prefix "${asked}".`, "not_found");
|
|
26450
26571
|
}
|
|
26451
|
-
var read = (file) =>
|
|
26572
|
+
var read = (file) => fs73.readFileSync(file, "utf8");
|
|
26452
26573
|
var bytes2 = (text5) => Buffer.byteLength(text5, "utf8");
|
|
26453
26574
|
function files(dir) {
|
|
26454
|
-
if (!
|
|
26575
|
+
if (!fs73.existsSync(dir)) return [];
|
|
26455
26576
|
const out = [];
|
|
26456
|
-
for (const entry of
|
|
26577
|
+
for (const entry of fs73.readdirSync(dir, { withFileTypes: true })) {
|
|
26457
26578
|
if (entry.name.startsWith(".")) continue;
|
|
26458
|
-
const full =
|
|
26579
|
+
const full = path75.join(dir, entry.name);
|
|
26459
26580
|
if (entry.isDirectory()) out.push(...files(full));
|
|
26460
26581
|
else if (entry.isFile()) out.push(full);
|
|
26461
26582
|
}
|
|
26462
26583
|
return out;
|
|
26463
26584
|
}
|
|
26464
|
-
var posix2 = (p) => p.split(
|
|
26585
|
+
var posix2 = (p) => p.split(path75.sep).join("/");
|
|
26465
26586
|
function openCards() {
|
|
26466
26587
|
const out = [];
|
|
26467
26588
|
for (const file of walkMd(TODO)) {
|
|
26468
|
-
const rel2 = posix2(
|
|
26589
|
+
const rel2 = posix2(path75.relative(TODO, file));
|
|
26469
26590
|
if (rel2 === "README.md") continue;
|
|
26470
26591
|
const parts = rel2.split("/");
|
|
26471
26592
|
const text5 = read(file);
|
|
@@ -26499,14 +26620,14 @@ function bodyProblems(text5) {
|
|
|
26499
26620
|
return out;
|
|
26500
26621
|
}
|
|
26501
26622
|
function packWorkflow(flow) {
|
|
26502
|
-
const own =
|
|
26623
|
+
const own = path75.join(WORKFLOWS, flow.id);
|
|
26503
26624
|
const out = {};
|
|
26504
|
-
for (const file of files(own)) out[posix2(
|
|
26625
|
+
for (const file of files(own)) out[posix2(path75.relative(own, file))] = read(file);
|
|
26505
26626
|
const named2 = [...flow.previews, ...flow.land, ...flow.scheduled].map((h) => h.agent).filter((a) => !LAND_ROLES.includes(a));
|
|
26506
26627
|
for (const agent of new Set(named2)) {
|
|
26507
|
-
const dir =
|
|
26508
|
-
if (
|
|
26509
|
-
for (const file of files(dir)) out[`agents/${agent}/${posix2(
|
|
26628
|
+
const dir = path75.join(AGENTS, agent);
|
|
26629
|
+
if (fs73.existsSync(path75.join(dir, "AGENT.md"))) {
|
|
26630
|
+
for (const file of files(dir)) out[`agents/${agent}/${posix2(path75.relative(dir, file))}`] = read(file);
|
|
26510
26631
|
continue;
|
|
26511
26632
|
}
|
|
26512
26633
|
for (const [key, text5] of Object.entries(BUNDLED_AGENT_FILES)) {
|
|
@@ -26558,8 +26679,8 @@ function draftsOf(text5) {
|
|
|
26558
26679
|
const at = /^\.(assets|mockups)\/(.+)$/.exec(name);
|
|
26559
26680
|
if (!at) continue;
|
|
26560
26681
|
const base = at[1] === "assets" ? ASSETS : MOCKUPS;
|
|
26561
|
-
const file =
|
|
26562
|
-
if (file.startsWith(base +
|
|
26682
|
+
const file = path75.resolve(base, at[2]);
|
|
26683
|
+
if (file.startsWith(base + path75.sep) && fs73.statSync(file, { throwIfNoEntry: false })?.isFile()) out.set(name, file);
|
|
26563
26684
|
}
|
|
26564
26685
|
return [...out].map(([name, file]) => ({ name, file }));
|
|
26565
26686
|
}
|
|
@@ -26593,9 +26714,9 @@ async function uploadDrafts(task, drafts) {
|
|
|
26593
26714
|
const assets = {};
|
|
26594
26715
|
for (const { name, file } of drafts) {
|
|
26595
26716
|
if (!card.body.includes(name)) continue;
|
|
26596
|
-
const ext =
|
|
26717
|
+
const ext = path75.extname(file).toLowerCase();
|
|
26597
26718
|
const type = TYPES[ext] ?? "application/octet-stream";
|
|
26598
|
-
const data =
|
|
26719
|
+
const data = fs73.readFileSync(file);
|
|
26599
26720
|
const artifact = await api("POST", `/v1/tasks/${task}/artifacts`, { type: kindOf(ext), name: name.slice(0, 200) });
|
|
26600
26721
|
const upload = await api("POST", `/v1/artifacts/${artifact.id}/uploads`, { content_type: type, size: Math.max(data.length, 1) });
|
|
26601
26722
|
await api("PUT", `/v1/uploads/${upload.id}`, void 0, { bytes: data, type });
|
|
@@ -26638,10 +26759,10 @@ usage: ${program2} cloud import --project <id or prefix> [--dry-run]`, { kind: "
|
|
|
26638
26759
|
async function run(asked, dryRun) {
|
|
26639
26760
|
const project = await findProject(asked);
|
|
26640
26761
|
const cloudFlows = await all(`/v1/workflows?workspace=${encodeURIComponent(project.workspace)}`);
|
|
26641
|
-
const board2 = crypto5.createHash("sha256").update(
|
|
26762
|
+
const board2 = crypto5.createHash("sha256").update(fs73.realpathSync(KANBAN)).digest("hex").slice(0, 16);
|
|
26642
26763
|
const cards = openCards();
|
|
26643
26764
|
const problems = [];
|
|
26644
|
-
const where = (file) => posix2(
|
|
26765
|
+
const where = (file) => posix2(path75.relative(KANBAN, file));
|
|
26645
26766
|
const own = workflows().filter((w) => !w.builtIn);
|
|
26646
26767
|
const packed = /* @__PURE__ */ new Map();
|
|
26647
26768
|
for (const flow of own) {
|
|
@@ -26668,7 +26789,7 @@ async function run(asked, dryRun) {
|
|
|
26668
26789
|
const root = `projects/${project.id}/`;
|
|
26669
26790
|
const boardFiles = [];
|
|
26670
26791
|
for (const file of [...files(MEMORY), ...files(RULES), CONFIG, MODULES_MD]) {
|
|
26671
|
-
if (!
|
|
26792
|
+
if (!fs73.existsSync(file)) continue;
|
|
26672
26793
|
const rel2 = where(file);
|
|
26673
26794
|
if (!isBoardPath(rel2)) continue;
|
|
26674
26795
|
const content = read(file);
|
|
@@ -26779,16 +26900,16 @@ async function run(asked, dryRun) {
|
|
|
26779
26900
|
}
|
|
26780
26901
|
function leftLocal(own) {
|
|
26781
26902
|
const out = [];
|
|
26782
|
-
const count4 = (dir) =>
|
|
26903
|
+
const count4 = (dir) => fs73.existsSync(dir) ? walkMd(dir).length : 0;
|
|
26783
26904
|
const archived = count4(ARCHIVE);
|
|
26784
26905
|
if (archived) out.push(`.archive/ (${archived})`);
|
|
26785
26906
|
const triage = count4(TRIAGE);
|
|
26786
26907
|
if (triage) out.push(`triage/ (${triage})`);
|
|
26787
|
-
if (
|
|
26788
|
-
if (
|
|
26908
|
+
if (fs73.existsSync(DELIVERIES)) out.push("deliveries/");
|
|
26909
|
+
if (fs73.existsSync(path75.join(KANBAN, "metrics.csv"))) out.push("metrics.csv");
|
|
26789
26910
|
const named2 = new Set(own.flatMap((w) => [...w.previews, ...w.land, ...w.scheduled].map((h) => h.agent)));
|
|
26790
|
-
if (
|
|
26791
|
-
for (const e of
|
|
26911
|
+
if (fs73.existsSync(AGENTS)) {
|
|
26912
|
+
for (const e of fs73.readdirSync(AGENTS, { withFileTypes: true })) {
|
|
26792
26913
|
if (e.isDirectory() && !e.name.startsWith(".") && !named2.has(e.name)) out.push(`agents/${e.name}/`);
|
|
26793
26914
|
}
|
|
26794
26915
|
}
|
|
@@ -26806,8 +26927,8 @@ init_session();
|
|
|
26806
26927
|
init_io();
|
|
26807
26928
|
init_paths();
|
|
26808
26929
|
import { createHash as createHash4 } from "node:crypto";
|
|
26809
|
-
import
|
|
26810
|
-
import
|
|
26930
|
+
import fs74 from "node:fs";
|
|
26931
|
+
import path76 from "node:path";
|
|
26811
26932
|
var SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
26812
26933
|
var CAPTURED = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
26813
26934
|
var MAX_BYTES3 = 50 * 1024 * 1024;
|
|
@@ -26906,10 +27027,10 @@ This template has no preview.`);
|
|
|
26906
27027
|
const written = [];
|
|
26907
27028
|
for (const p of previews) {
|
|
26908
27029
|
const bytes3 = Buffer.from(await (await ask(`/${at}/previews/${route(p.path)}`, {}, WITHOUT)).arrayBuffer());
|
|
26909
|
-
const file =
|
|
26910
|
-
const out =
|
|
26911
|
-
|
|
26912
|
-
|
|
27030
|
+
const file = path76.basename(p.path);
|
|
27031
|
+
const out = path76.join(opts.to, file.startsWith(`${name}-`) ? file : `${name}-${file}`);
|
|
27032
|
+
fs74.mkdirSync(opts.to, { recursive: true });
|
|
27033
|
+
fs74.writeFileSync(out, bytes3);
|
|
26913
27034
|
written.push(out);
|
|
26914
27035
|
say(out);
|
|
26915
27036
|
}
|
|
@@ -26922,15 +27043,15 @@ async function templateGet(name, opts, program2) {
|
|
|
26922
27043
|
const { template } = await asJson(await ask(`/${at}`, {}, WITHOUT));
|
|
26923
27044
|
const fetched = [];
|
|
26924
27045
|
for (const f of template.files) {
|
|
26925
|
-
const out =
|
|
26926
|
-
if (!out.startsWith(
|
|
27046
|
+
const out = path76.resolve(to, f.path);
|
|
27047
|
+
if (!out.startsWith(path76.resolve(to) + path76.sep)) refuse2(T.corrupt, WITHOUT);
|
|
26927
27048
|
const bytes3 = Buffer.from(await (await ask(`/${at}/files/${route(f.path)}`, {}, WITHOUT)).arrayBuffer());
|
|
26928
27049
|
if (bytes3.byteLength !== f.size || createHash4("sha256").update(bytes3).digest("hex") !== f.sha256) {
|
|
26929
27050
|
refuse2(T.corrupt, WITHOUT);
|
|
26930
27051
|
}
|
|
26931
27052
|
fetched.push({ out, bytes: bytes3 });
|
|
26932
27053
|
}
|
|
26933
|
-
const taken = fetched.filter((f) =>
|
|
27054
|
+
const taken = fetched.filter((f) => fs74.existsSync(f.out)).map((f) => path76.relative(to, f.out));
|
|
26934
27055
|
if (taken.length > 0) {
|
|
26935
27056
|
refuse2({
|
|
26936
27057
|
zh: `\u76EE\u6807\u6587\u4EF6\u5939\u5DF2\u6709\u540C\u540D\u6587\u4EF6\uFF1A${taken.join("\u3001")}\u3002\u8BF7\u6362\u4E00\u4E2A\u6587\u4EF6\u5939\uFF0C\u6216\u5148\u79FB\u8D70\u8FD9\u4E9B\u6587\u4EF6\u3002`,
|
|
@@ -26938,8 +27059,8 @@ async function templateGet(name, opts, program2) {
|
|
|
26938
27059
|
});
|
|
26939
27060
|
}
|
|
26940
27061
|
for (const f of fetched) {
|
|
26941
|
-
|
|
26942
|
-
|
|
27062
|
+
fs74.mkdirSync(path76.dirname(f.out), { recursive: true });
|
|
27063
|
+
fs74.writeFileSync(f.out, f.bytes);
|
|
26943
27064
|
}
|
|
26944
27065
|
say(`\u5DF2\u53D6\u56DE ${name}\uFF1A${fetched.length} \u4E2A\u6587\u4EF6\uFF0C\u4F4D\u4E8E ${to}
|
|
26945
27066
|
Fetched ${name}: ${fetched.length} files into ${to}`);
|
|
@@ -26957,25 +27078,25 @@ async function templateSource(name, opts) {
|
|
|
26957
27078
|
say(`evidence: ${e.path}`);
|
|
26958
27079
|
continue;
|
|
26959
27080
|
}
|
|
26960
|
-
const out =
|
|
26961
|
-
|
|
26962
|
-
|
|
27081
|
+
const out = path76.join(opts.to, `${name}-${path76.basename(e.path)}`);
|
|
27082
|
+
fs74.mkdirSync(opts.to, { recursive: true });
|
|
27083
|
+
fs74.writeFileSync(out, Buffer.from(await (await ask(`/${at}/evidence/${route(e.path)}`)).arrayBuffer()));
|
|
26963
27084
|
written.push(out);
|
|
26964
27085
|
say(out);
|
|
26965
27086
|
}
|
|
26966
27087
|
return { name, source, evidence: written.length > 0 ? written : evidence.map((e) => e.path) };
|
|
26967
27088
|
}
|
|
26968
27089
|
async function templatePublish(dir, opts) {
|
|
26969
|
-
const root =
|
|
26970
|
-
const manifest =
|
|
26971
|
-
if (!
|
|
26972
|
-
const meta = readTemplateMeta(
|
|
27090
|
+
const root = path76.resolve(dir);
|
|
27091
|
+
const manifest = path76.join(root, "template.md");
|
|
27092
|
+
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.");
|
|
27093
|
+
const meta = readTemplateMeta(fs74.readFileSync(manifest, "utf8"));
|
|
26973
27094
|
const files2 = walk2(root);
|
|
26974
|
-
const total = files2.reduce((sum, f) => sum +
|
|
27095
|
+
const total = files2.reduce((sum, f) => sum + fs74.statSync(path76.join(root, f)).size, 0);
|
|
26975
27096
|
if (total > MAX_BYTES3) invalid("\u6A21\u677F\u6587\u4EF6\u5408\u8BA1\u8D85\u8FC7 50 MB\u3002", "the template\u2019s files exceed 50 MB.");
|
|
26976
27097
|
for (const extra of [...opts.preview, ...opts.evidence]) {
|
|
26977
|
-
if (!
|
|
26978
|
-
if (
|
|
27098
|
+
if (!fs74.existsSync(extra)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${extra}\u3002`, `${extra} does not exist.`);
|
|
27099
|
+
if (path76.resolve(extra).startsWith(root + path76.sep)) {
|
|
26979
27100
|
invalid(
|
|
26980
27101
|
`${extra} \u5728\u6A21\u677F\u6587\u4EF6\u5939\u5185\uFF1B\u9884\u89C8\u56FE\u4E0E\u6765\u6E90\u51ED\u8BC1\u9700\u653E\u5728\u6A21\u677F\u6587\u4EF6\u5939\u4E4B\u5916\u3002`,
|
|
26981
27102
|
`${extra} is inside the template folder; keep previews and evidence outside it.`
|
|
@@ -26987,10 +27108,10 @@ async function templatePublish(dir, opts) {
|
|
|
26987
27108
|
}
|
|
26988
27109
|
const form = new FormData();
|
|
26989
27110
|
form.set("meta", JSON.stringify({ ...meta, replace: !!opts.replace }));
|
|
26990
|
-
const add = (kind, name, file) => form.append(`${kind}:${name}`, new Blob([
|
|
26991
|
-
for (const f of files2) add("files", f,
|
|
26992
|
-
for (const f of opts.preview) add("previews",
|
|
26993
|
-
for (const f of opts.evidence) add("evidence",
|
|
27111
|
+
const add = (kind, name, file) => form.append(`${kind}:${name}`, new Blob([fs74.readFileSync(file)]), path76.basename(name));
|
|
27112
|
+
for (const f of files2) add("files", f, path76.join(root, f));
|
|
27113
|
+
for (const f of opts.preview) add("previews", path76.basename(f), f);
|
|
27114
|
+
for (const f of opts.evidence) add("evidence", path76.basename(f), f);
|
|
26994
27115
|
const done2 = await asJson(await ask("", { method: "POST", body: form }));
|
|
26995
27116
|
say(
|
|
26996
27117
|
done2.replaced ? `\u5DF2\u66FF\u6362 ${done2.name}\uFF1A${files2.length} \u4E2A\u6587\u4EF6\u3002
|
|
@@ -27006,11 +27127,11 @@ Deleted ${name} from the catalog.`);
|
|
|
27006
27127
|
return { name, deleted: true };
|
|
27007
27128
|
}
|
|
27008
27129
|
async function templateShowcase(name, dir) {
|
|
27009
|
-
const file =
|
|
27010
|
-
if (!
|
|
27130
|
+
const file = path76.join(dir, "showcase.json");
|
|
27131
|
+
if (!fs74.existsSync(file)) invalid(`${dir} \u4E2D\u7F3A\u5C11 showcase.json\u3002`, `showcase.json is missing from ${dir}.`);
|
|
27011
27132
|
let meta;
|
|
27012
27133
|
try {
|
|
27013
|
-
meta = JSON.parse(
|
|
27134
|
+
meta = JSON.parse(fs74.readFileSync(file, "utf8"));
|
|
27014
27135
|
} catch {
|
|
27015
27136
|
return invalid("showcase.json \u4E0D\u662F\u6709\u6548\u7684 JSON\u3002", "showcase.json is not valid JSON.");
|
|
27016
27137
|
}
|
|
@@ -27022,9 +27143,9 @@ async function templateShowcase(name, dir) {
|
|
|
27022
27143
|
const form = new FormData();
|
|
27023
27144
|
form.set("meta", JSON.stringify(meta));
|
|
27024
27145
|
for (const p of new Set(named2)) {
|
|
27025
|
-
const at =
|
|
27026
|
-
if (!
|
|
27027
|
-
form.append(`media:${p}`, new Blob([
|
|
27146
|
+
const at = path76.join(dir, p);
|
|
27147
|
+
if (!fs74.existsSync(at)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${at}\u3002`, `${at} does not exist.`);
|
|
27148
|
+
form.append(`media:${p}`, new Blob([fs74.readFileSync(at)]), p);
|
|
27028
27149
|
}
|
|
27029
27150
|
const done2 = await asJson(await ask(`/${encodeURIComponent(name)}/showcase`, { method: "PUT", body: form }));
|
|
27030
27151
|
say(`\u5DF2\u66F4\u65B0 ${name} \u7684\u5C55\u793A\u8D44\u6599\uFF1A${done2.files} \u4E2A\u6587\u4EF6\u3002
|
|
@@ -27033,7 +27154,7 @@ Updated ${name}'s showcase: ${done2.files} files.`);
|
|
|
27033
27154
|
}
|
|
27034
27155
|
function walk2(root, sub = "") {
|
|
27035
27156
|
const out = [];
|
|
27036
|
-
for (const entry of
|
|
27157
|
+
for (const entry of fs74.readdirSync(path76.join(root, sub), { withFileTypes: true })) {
|
|
27037
27158
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
27038
27159
|
const rel2 = sub ? `${sub}/${entry.name}` : entry.name;
|
|
27039
27160
|
if (entry.isDirectory()) out.push(...walk2(root, rel2));
|
|
@@ -27318,7 +27439,7 @@ init_opencode();
|
|
|
27318
27439
|
init_locate();
|
|
27319
27440
|
init_store();
|
|
27320
27441
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
27321
|
-
import
|
|
27442
|
+
import path82 from "node:path";
|
|
27322
27443
|
var AGENTS2 = {
|
|
27323
27444
|
claude: "claude-code",
|
|
27324
27445
|
codex: "codex",
|
|
@@ -27333,10 +27454,10 @@ var AGENTS2 = {
|
|
|
27333
27454
|
var RUNTIMES = /* @__PURE__ */ new Set(["node", "bun", "deno"]);
|
|
27334
27455
|
function agentOf2(args) {
|
|
27335
27456
|
const words = args.trim().split(/\s+/);
|
|
27336
|
-
const first =
|
|
27457
|
+
const first = path82.basename(words[0] ?? "");
|
|
27337
27458
|
const script = RUNTIMES.has(first) ? words[1] ?? "" : words[0] ?? "";
|
|
27338
27459
|
if (script.includes("/@anthropic-ai/claude-code/")) return "claude-code";
|
|
27339
|
-
return AGENTS2[
|
|
27460
|
+
return AGENTS2[path82.basename(script).replace(/\.(m?js|cjs|exe)$/, "")];
|
|
27340
27461
|
}
|
|
27341
27462
|
function processChain() {
|
|
27342
27463
|
if (process.platform === "win32") return [];
|
|
@@ -27400,8 +27521,8 @@ init_tasks();
|
|
|
27400
27521
|
init_read();
|
|
27401
27522
|
init_rules();
|
|
27402
27523
|
init_board();
|
|
27403
|
-
import
|
|
27404
|
-
import
|
|
27524
|
+
import fs81 from "node:fs";
|
|
27525
|
+
import path83 from "node:path";
|
|
27405
27526
|
var FOLLOW_MS = 400;
|
|
27406
27527
|
async function cmdStartRun(action, args, opts, program2 = "akb") {
|
|
27407
27528
|
const { req, follow: follow2, print } = readRequest(action, args, opts);
|
|
@@ -27691,7 +27812,7 @@ async function cmdSubStart(words, opts) {
|
|
|
27691
27812
|
let prompt = words.join(" ");
|
|
27692
27813
|
if (opts.file) {
|
|
27693
27814
|
try {
|
|
27694
|
-
prompt =
|
|
27815
|
+
prompt = fs81.readFileSync(path83.resolve(opts.file), "utf8");
|
|
27695
27816
|
} catch {
|
|
27696
27817
|
die(`--file: cannot read ${opts.file}`, { kind: "run-refused" });
|
|
27697
27818
|
}
|
|
@@ -27982,7 +28103,7 @@ async function cmdWatch(sessionId) {
|
|
|
27982
28103
|
// ../cli/src/commands/triage.ts
|
|
27983
28104
|
init_scheduled();
|
|
27984
28105
|
init_workflows();
|
|
27985
|
-
import
|
|
28106
|
+
import fs83 from "node:fs";
|
|
27986
28107
|
|
|
27987
28108
|
// ../cli/src/lib/signals/index.ts
|
|
27988
28109
|
init_cards();
|
|
@@ -27992,16 +28113,16 @@ init_yaml();
|
|
|
27992
28113
|
// ../cli/src/lib/signals/config.ts
|
|
27993
28114
|
init_settings2();
|
|
27994
28115
|
init_paths();
|
|
27995
|
-
import
|
|
27996
|
-
import
|
|
28116
|
+
import fs82 from "node:fs";
|
|
28117
|
+
import path84 from "node:path";
|
|
27997
28118
|
var TOKEN_KEY = "TRIAGE_ENDPOINT_TOKEN";
|
|
27998
28119
|
var ENDPOINT_SETTING = "Triage endpoint";
|
|
27999
28120
|
var LINE2 = /^- \*\*Triage endpoint\*\*\s*[—-]\s*(.+)$/m;
|
|
28000
|
-
var boardRel3 = (file) => rel(file).split(
|
|
28121
|
+
var boardRel3 = (file) => rel(file).split(path84.sep).join("/");
|
|
28001
28122
|
function signalEndpoint() {
|
|
28002
28123
|
let text5;
|
|
28003
28124
|
try {
|
|
28004
|
-
text5 =
|
|
28125
|
+
text5 = fs82.readFileSync(CONFIG, "utf8");
|
|
28005
28126
|
} catch {
|
|
28006
28127
|
return "";
|
|
28007
28128
|
}
|
|
@@ -28177,7 +28298,7 @@ async function cmdTriageAdd(opts) {
|
|
|
28177
28298
|
let body = opts.text;
|
|
28178
28299
|
if (opts.file !== void 0) {
|
|
28179
28300
|
try {
|
|
28180
|
-
body =
|
|
28301
|
+
body = fs83.readFileSync(opts.file, "utf8");
|
|
28181
28302
|
} catch {
|
|
28182
28303
|
die(`can't read ${opts.file} \u2014 write the item to a file, then pass its path`, { kind: "needs-input" });
|
|
28183
28304
|
}
|
|
@@ -28741,8 +28862,8 @@ function declareAgent(program2, cli) {
|
|
|
28741
28862
|
}
|
|
28742
28863
|
|
|
28743
28864
|
// ../cli/src/lib/cli/setup.ts
|
|
28744
|
-
import
|
|
28745
|
-
import
|
|
28865
|
+
import fs85 from "node:fs";
|
|
28866
|
+
import path86 from "node:path";
|
|
28746
28867
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
28747
28868
|
|
|
28748
28869
|
// ../cli/src/commands/update.ts
|
|
@@ -28756,8 +28877,8 @@ init_paths();
|
|
|
28756
28877
|
init_memory();
|
|
28757
28878
|
init_yaml2();
|
|
28758
28879
|
init_version();
|
|
28759
|
-
import
|
|
28760
|
-
import
|
|
28880
|
+
import fs84 from "node:fs";
|
|
28881
|
+
import path85 from "node:path";
|
|
28761
28882
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
28762
28883
|
var NAME = "wildcrane";
|
|
28763
28884
|
var REPO = "https://github.com/ai4kanban/ai4kanban";
|
|
@@ -28779,10 +28900,10 @@ var Report = class {
|
|
|
28779
28900
|
for (const line of this.notes) say(` ! ${line}`);
|
|
28780
28901
|
}
|
|
28781
28902
|
};
|
|
28782
|
-
var read3 = (file) =>
|
|
28903
|
+
var read3 = (file) => fs84.existsSync(file) ? fs84.readFileSync(file, "utf8") : null;
|
|
28783
28904
|
function statOf(p) {
|
|
28784
28905
|
try {
|
|
28785
|
-
return
|
|
28906
|
+
return fs84.lstatSync(p);
|
|
28786
28907
|
} catch {
|
|
28787
28908
|
return null;
|
|
28788
28909
|
}
|
|
@@ -28807,10 +28928,10 @@ async function cmdUpdate2(ctx) {
|
|
|
28807
28928
|
return { updated: ctx.dir, from, to: SKILL_VERSION };
|
|
28808
28929
|
}
|
|
28809
28930
|
async function repairBoard(root, report3, named2) {
|
|
28810
|
-
const legacy =
|
|
28811
|
-
const kept2 =
|
|
28812
|
-
const board2 = named2 ?? (
|
|
28813
|
-
if (!
|
|
28931
|
+
const legacy = path85.join(root, "docs", "kanban");
|
|
28932
|
+
const kept2 = path85.join(root, ".akb", "kanban");
|
|
28933
|
+
const board2 = named2 ?? (fs84.existsSync(legacy) ? legacy : kept2);
|
|
28934
|
+
if (!fs84.existsSync(board2)) {
|
|
28814
28935
|
report3.notes.push("\u672A\u627E\u5230\u770B\u677F\uFF0C\u8BF7\u901A\u8FC7\u63D2\u4EF6\u8FDE\u63A5\u4E91\u7AEF\u9879\u76EE\u3002");
|
|
28815
28936
|
return false;
|
|
28816
28937
|
}
|
|
@@ -28841,20 +28962,20 @@ function moveRuntimes(root, board2, report3) {
|
|
|
28841
28962
|
}
|
|
28842
28963
|
}
|
|
28843
28964
|
function moveLegacyMemory(board2, report3) {
|
|
28844
|
-
const memory =
|
|
28965
|
+
const memory = path85.join(board2, "memory");
|
|
28845
28966
|
const moved = [];
|
|
28846
28967
|
for (const name of MEMORY_FILES) {
|
|
28847
|
-
const from =
|
|
28968
|
+
const from = path85.join(board2, name);
|
|
28848
28969
|
const st = statOf(from);
|
|
28849
28970
|
if (!st || !st.isFile()) continue;
|
|
28850
|
-
|
|
28851
|
-
const to =
|
|
28971
|
+
fs84.mkdirSync(memory, { recursive: true });
|
|
28972
|
+
const to = path85.join(memory, name);
|
|
28852
28973
|
const there = read3(to);
|
|
28853
28974
|
if (there === null) {
|
|
28854
|
-
|
|
28975
|
+
fs84.renameSync(from, to);
|
|
28855
28976
|
moved.push(name);
|
|
28856
28977
|
} else if (there === read3(from)) {
|
|
28857
|
-
|
|
28978
|
+
fs84.rmSync(from);
|
|
28858
28979
|
moved.push(name);
|
|
28859
28980
|
} else {
|
|
28860
28981
|
report3.notes.push(`both docs/kanban/${name} and docs/kanban/memory/${name} exist and differ \u2014 merge them by hand`);
|
|
@@ -28863,23 +28984,23 @@ function moveLegacyMemory(board2, report3) {
|
|
|
28863
28984
|
if (moved.length) report3.did.push(`moved the memory set into docs/kanban/memory/: ${moved.join(", ")}`);
|
|
28864
28985
|
}
|
|
28865
28986
|
function dropRecordFile(board2) {
|
|
28866
|
-
const file =
|
|
28867
|
-
if (!
|
|
28868
|
-
|
|
28987
|
+
const file = path85.join(board2, "record.csv");
|
|
28988
|
+
if (!fs84.existsSync(file)) return null;
|
|
28989
|
+
fs84.rmSync(file);
|
|
28869
28990
|
return "removed docs/kanban/record.csv (the planning scores it fed are gone)";
|
|
28870
28991
|
}
|
|
28871
28992
|
function dropModuleGoals(board2, report3) {
|
|
28872
|
-
const memory =
|
|
28873
|
-
if (!
|
|
28874
|
-
const root = read3(
|
|
28993
|
+
const memory = path85.join(board2, "memory");
|
|
28994
|
+
if (!fs84.existsSync(memory)) return;
|
|
28995
|
+
const root = read3(path85.join(memory, "goal.md"));
|
|
28875
28996
|
let dropped = 0;
|
|
28876
|
-
for (const entry of
|
|
28997
|
+
for (const entry of fs84.readdirSync(memory, { withFileTypes: true })) {
|
|
28877
28998
|
if (!entry.isDirectory()) continue;
|
|
28878
|
-
const file =
|
|
28999
|
+
const file = path85.join(memory, entry.name, "goal.md");
|
|
28879
29000
|
const text5 = read3(file);
|
|
28880
29001
|
if (text5 === null) continue;
|
|
28881
29002
|
if (text5 === root || text5.includes(UNFILLED) || !goalWords(text5)) {
|
|
28882
|
-
|
|
29003
|
+
fs84.rmSync(file);
|
|
28883
29004
|
dropped++;
|
|
28884
29005
|
} else {
|
|
28885
29006
|
report3.notes.push(
|
|
@@ -28901,10 +29022,10 @@ var PLAN_LEADS = {
|
|
|
28901
29022
|
var GONE_LEADS = ["software-planner", "planner"];
|
|
28902
29023
|
function planLeads(board2) {
|
|
28903
29024
|
const leads = { ...PLAN_LEADS };
|
|
28904
|
-
const root =
|
|
28905
|
-
for (const entry of
|
|
29025
|
+
const root = path85.join(board2, "workflows");
|
|
29026
|
+
for (const entry of fs84.existsSync(root) ? fs84.readdirSync(root, { withFileTypes: true }) : []) {
|
|
28906
29027
|
if (!entry.isDirectory()) continue;
|
|
28907
|
-
const meta = splitFrontmatter(read3(
|
|
29028
|
+
const meta = splitFrontmatter(read3(path85.join(root, entry.name, "WORKFLOW.md")) ?? "").meta;
|
|
28908
29029
|
const stages = meta === null ? void 0 : parseYamlBlock(meta).stages;
|
|
28909
29030
|
const plan = stages && typeof stages === "object" && !Array.isArray(stages) ? stages.plan : void 0;
|
|
28910
29031
|
const lead = plan && typeof plan === "object" && !Array.isArray(plan) && typeof plan.lead === "string" ? plan.lead.trim() : "";
|
|
@@ -28913,30 +29034,30 @@ function planLeads(board2) {
|
|
|
28913
29034
|
return leads;
|
|
28914
29035
|
}
|
|
28915
29036
|
function movePlanRules(board2, report3) {
|
|
28916
|
-
const rules =
|
|
29037
|
+
const rules = path85.join(board2, "rules");
|
|
28917
29038
|
const moved = [];
|
|
28918
29039
|
for (const [id, leads] of Object.entries(planLeads(board2))) {
|
|
28919
29040
|
for (const lead of leads) {
|
|
28920
|
-
const from =
|
|
29041
|
+
const from = path85.join(rules, `${lead}.md`);
|
|
28921
29042
|
const rule = read3(from)?.trim();
|
|
28922
29043
|
if (!rule) continue;
|
|
28923
|
-
const to =
|
|
29044
|
+
const to = path85.join(rules, "workflows", `${id}.md`);
|
|
28924
29045
|
const there = read3(to)?.trim() ?? "";
|
|
28925
29046
|
if (!there.includes(rule)) {
|
|
28926
|
-
|
|
28927
|
-
|
|
29047
|
+
fs84.mkdirSync(path85.dirname(to), { recursive: true });
|
|
29048
|
+
fs84.writeFileSync(to, `${[there, rule].filter(Boolean).join("\n\n")}
|
|
28928
29049
|
`);
|
|
28929
29050
|
moved.push(`${lead} \u2192 ${id}`);
|
|
28930
29051
|
}
|
|
28931
29052
|
}
|
|
28932
29053
|
}
|
|
28933
|
-
for (const lead of GONE_LEADS)
|
|
29054
|
+
for (const lead of GONE_LEADS) fs84.rmSync(path85.join(rules, `${lead}.md`), { force: true });
|
|
28934
29055
|
if (moved.length) report3.did.push(`made each plan agent's rule its workflow's card rule (docs/kanban/rules/workflows/): ${moved.join(", ")}`);
|
|
28935
29056
|
}
|
|
28936
29057
|
function noteOldWorkflows(board2, report3) {
|
|
28937
29058
|
const old = (dir, file, keys) => {
|
|
28938
|
-
const root =
|
|
28939
|
-
return (
|
|
29059
|
+
const root = path85.join(board2, dir);
|
|
29060
|
+
return (fs84.existsSync(root) ? fs84.readdirSync(root, { withFileTypes: true }) : []).filter((e) => e.isDirectory() && keys.test(splitFrontmatter(read3(path85.join(root, e.name, file)) ?? "").meta ?? "")).map((e) => `docs/kanban/${dir}/${e.name}/${file}`);
|
|
28940
29061
|
};
|
|
28941
29062
|
const files2 = [...old("workflows", "WORKFLOW.md", /^(stages|delivers):/m), ...old("agents", "AGENT.md", /^\s+(hook|lead):/m)];
|
|
28942
29063
|
if (files2.length) {
|
|
@@ -28946,7 +29067,7 @@ function noteOldWorkflows(board2, report3) {
|
|
|
28946
29067
|
}
|
|
28947
29068
|
}
|
|
28948
29069
|
function checkConfig(board2, report3) {
|
|
28949
|
-
const current = read3(
|
|
29070
|
+
const current = read3(path85.join(board2, "config.md"));
|
|
28950
29071
|
if (!current) return;
|
|
28951
29072
|
const added = missingConfigKeys(current);
|
|
28952
29073
|
if (added.length) {
|
|
@@ -28956,7 +29077,7 @@ function checkConfig(board2, report3) {
|
|
|
28956
29077
|
}
|
|
28957
29078
|
}
|
|
28958
29079
|
function checkModules(board2, report3) {
|
|
28959
|
-
const map2 = read3(
|
|
29080
|
+
const map2 = read3(path85.join(board2, "modules.md"));
|
|
28960
29081
|
if (map2 && map2.includes(UNFILLED)) {
|
|
28961
29082
|
report3.notes.push(
|
|
28962
29083
|
"docs/kanban/modules.md is still blank \u2014 write it from the repo (`akb guide module-map`)"
|
|
@@ -29010,14 +29131,14 @@ function declareSetup(program2, cli) {
|
|
|
29010
29131
|
program2.version(SKILL_VERSION, "-v, --version", "print this version").addHelpText("beforeAll", spelled(cli.program));
|
|
29011
29132
|
const dirOption = (cmd) => cmd.option("--dir <path>", "the project to work on (default: the current folder)").option("--json", "answer as one JSON object instead of prose");
|
|
29012
29133
|
const where = (cmd) => {
|
|
29013
|
-
const dir =
|
|
29014
|
-
if (!
|
|
29134
|
+
const dir = path86.resolve(String(cmd.optsWithGlobals().dir ?? cli.cwd));
|
|
29135
|
+
if (!fs85.existsSync(dir)) throw new BoardError(`no such folder: ${dir}`, { kind: "no-such-folder", dir });
|
|
29015
29136
|
return dir;
|
|
29016
29137
|
};
|
|
29017
29138
|
dirOption(program2.command("update")).summary("\u4FEE\u590D\u5DF2\u6709\u770B\u677F\u5E76\u68C0\u67E5 CLI \u65B0\u7248\u672C").description("\u4FEE\u590D\u5DF2\u6709\u770B\u677F\u5E76\u63D0\u793A CLI \u5347\u7EA7\u547D\u4EE4\u3002\u6CA1\u6709\u770B\u677F\u65F6\uFF0C\u8BF7\u901A\u8FC7\u63D2\u4EF6\u8FDE\u63A5\u4E91\u7AEF\u9879\u76EE\u3002").action(async function() {
|
|
29018
29139
|
const dir = where(this);
|
|
29019
29140
|
const named2 = this.optsWithGlobals().board ?? process.env.AI4KANBAN_BOARD?.trim();
|
|
29020
|
-
const board2 = named2 ?
|
|
29141
|
+
const board2 = named2 ? path86.resolve(cli.cwd, named2) : null;
|
|
29021
29142
|
await runAction(ctxOf(this, cli.program), {}, () => cmdUpdate2({ dir, program: cli.program, board: board2 }));
|
|
29022
29143
|
});
|
|
29023
29144
|
program2.command("__rules", { hidden: true }).action(function() {
|
|
@@ -29061,7 +29182,7 @@ function invokedDirectly() {
|
|
|
29061
29182
|
const entry = process.argv[1];
|
|
29062
29183
|
if (!entry) return false;
|
|
29063
29184
|
try {
|
|
29064
|
-
return
|
|
29185
|
+
return fs86.realpathSync(entry) === fs86.realpathSync(SELF2);
|
|
29065
29186
|
} catch {
|
|
29066
29187
|
return false;
|
|
29067
29188
|
}
|