werkmeister 0.15.0 → 0.16.0
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/main.js +1005 -581
- package/dist/mcp.js +576 -248
- package/dist/sandbox-workspaces.vsix +0 -0
- package/infra/compose/scripts/sandbox-install-startup.sh +53 -4
- package/infra/skills/sb-pr/SKILL.md +2 -2
- package/infra/skills/sb-research/SKILL.md +3 -1
- package/infra/templates/docker-compose.feature.yml.njk +19 -3
- package/package.json +2 -2
package/dist/main.js
CHANGED
|
@@ -44,7 +44,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
44
44
|
var define_SANDBOX_BUILD_default;
|
|
45
45
|
var init_define_SANDBOX_BUILD = __esm({
|
|
46
46
|
"<define:__SANDBOX_BUILD__>"() {
|
|
47
|
-
define_SANDBOX_BUILD_default = { sha: "
|
|
47
|
+
define_SANDBOX_BUILD_default = { sha: "79cdadc2a1a4f3138deddeb36dbdcbfe124eccef", dirty: false };
|
|
48
48
|
}
|
|
49
49
|
});
|
|
50
50
|
|
|
@@ -1171,8 +1171,8 @@ var require_command = __commonJS({
|
|
|
1171
1171
|
init_define_SANDBOX_BUILD();
|
|
1172
1172
|
var EventEmitter2 = __require("node:events").EventEmitter;
|
|
1173
1173
|
var childProcess = __require("node:child_process");
|
|
1174
|
-
var
|
|
1175
|
-
var
|
|
1174
|
+
var path74 = __require("node:path");
|
|
1175
|
+
var fs66 = __require("node:fs");
|
|
1176
1176
|
var process4 = __require("node:process");
|
|
1177
1177
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
1178
1178
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -2153,7 +2153,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2153
2153
|
* @param {string} subcommandName
|
|
2154
2154
|
*/
|
|
2155
2155
|
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
|
|
2156
|
-
if (
|
|
2156
|
+
if (fs66.existsSync(executableFile)) return;
|
|
2157
2157
|
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";
|
|
2158
2158
|
const executableMissing = `'${executableFile}' does not exist
|
|
2159
2159
|
- if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
@@ -2171,11 +2171,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2171
2171
|
let launchWithNode = false;
|
|
2172
2172
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
2173
2173
|
function findFile(baseDir, baseName) {
|
|
2174
|
-
const localBin =
|
|
2175
|
-
if (
|
|
2176
|
-
if (sourceExt.includes(
|
|
2174
|
+
const localBin = path74.resolve(baseDir, baseName);
|
|
2175
|
+
if (fs66.existsSync(localBin)) return localBin;
|
|
2176
|
+
if (sourceExt.includes(path74.extname(baseName))) return void 0;
|
|
2177
2177
|
const foundExt = sourceExt.find(
|
|
2178
|
-
(ext) =>
|
|
2178
|
+
(ext) => fs66.existsSync(`${localBin}${ext}`)
|
|
2179
2179
|
);
|
|
2180
2180
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
2181
2181
|
return void 0;
|
|
@@ -2187,21 +2187,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2187
2187
|
if (this._scriptPath) {
|
|
2188
2188
|
let resolvedScriptPath;
|
|
2189
2189
|
try {
|
|
2190
|
-
resolvedScriptPath =
|
|
2190
|
+
resolvedScriptPath = fs66.realpathSync(this._scriptPath);
|
|
2191
2191
|
} catch {
|
|
2192
2192
|
resolvedScriptPath = this._scriptPath;
|
|
2193
2193
|
}
|
|
2194
|
-
executableDir =
|
|
2195
|
-
|
|
2194
|
+
executableDir = path74.resolve(
|
|
2195
|
+
path74.dirname(resolvedScriptPath),
|
|
2196
2196
|
executableDir
|
|
2197
2197
|
);
|
|
2198
2198
|
}
|
|
2199
2199
|
if (executableDir) {
|
|
2200
2200
|
let localFile = findFile(executableDir, executableFile);
|
|
2201
2201
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
2202
|
-
const legacyName =
|
|
2202
|
+
const legacyName = path74.basename(
|
|
2203
2203
|
this._scriptPath,
|
|
2204
|
-
|
|
2204
|
+
path74.extname(this._scriptPath)
|
|
2205
2205
|
);
|
|
2206
2206
|
if (legacyName !== this._name) {
|
|
2207
2207
|
localFile = findFile(
|
|
@@ -2212,7 +2212,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2212
2212
|
}
|
|
2213
2213
|
executableFile = localFile || executableFile;
|
|
2214
2214
|
}
|
|
2215
|
-
launchWithNode = sourceExt.includes(
|
|
2215
|
+
launchWithNode = sourceExt.includes(path74.extname(executableFile));
|
|
2216
2216
|
let proc;
|
|
2217
2217
|
if (process4.platform !== "win32") {
|
|
2218
2218
|
if (launchWithNode) {
|
|
@@ -3059,7 +3059,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3059
3059
|
* @return {Command}
|
|
3060
3060
|
*/
|
|
3061
3061
|
nameFromFilename(filename) {
|
|
3062
|
-
this._name =
|
|
3062
|
+
this._name = path74.basename(filename, path74.extname(filename));
|
|
3063
3063
|
return this;
|
|
3064
3064
|
}
|
|
3065
3065
|
/**
|
|
@@ -3073,9 +3073,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3073
3073
|
* @param {string} [path]
|
|
3074
3074
|
* @return {(string|null|Command)}
|
|
3075
3075
|
*/
|
|
3076
|
-
executableDir(
|
|
3077
|
-
if (
|
|
3078
|
-
this._executableDir =
|
|
3076
|
+
executableDir(path75) {
|
|
3077
|
+
if (path75 === void 0) return this._executableDir;
|
|
3078
|
+
this._executableDir = path75;
|
|
3079
3079
|
return this;
|
|
3080
3080
|
}
|
|
3081
3081
|
/**
|
|
@@ -3462,17 +3462,17 @@ var require_visit = __commonJS({
|
|
|
3462
3462
|
visit2.BREAK = BREAK;
|
|
3463
3463
|
visit2.SKIP = SKIP;
|
|
3464
3464
|
visit2.REMOVE = REMOVE;
|
|
3465
|
-
function visit_(key, node, visitor,
|
|
3466
|
-
const ctrl = callVisitor(key, node, visitor,
|
|
3465
|
+
function visit_(key, node, visitor, path74) {
|
|
3466
|
+
const ctrl = callVisitor(key, node, visitor, path74);
|
|
3467
3467
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
3468
|
-
replaceNode(key,
|
|
3469
|
-
return visit_(key, ctrl, visitor,
|
|
3468
|
+
replaceNode(key, path74, ctrl);
|
|
3469
|
+
return visit_(key, ctrl, visitor, path74);
|
|
3470
3470
|
}
|
|
3471
3471
|
if (typeof ctrl !== "symbol") {
|
|
3472
3472
|
if (identity.isCollection(node)) {
|
|
3473
|
-
|
|
3473
|
+
path74 = Object.freeze(path74.concat(node));
|
|
3474
3474
|
for (let i = 0; i < node.items.length; ++i) {
|
|
3475
|
-
const ci = visit_(i, node.items[i], visitor,
|
|
3475
|
+
const ci = visit_(i, node.items[i], visitor, path74);
|
|
3476
3476
|
if (typeof ci === "number")
|
|
3477
3477
|
i = ci - 1;
|
|
3478
3478
|
else if (ci === BREAK)
|
|
@@ -3483,13 +3483,13 @@ var require_visit = __commonJS({
|
|
|
3483
3483
|
}
|
|
3484
3484
|
}
|
|
3485
3485
|
} else if (identity.isPair(node)) {
|
|
3486
|
-
|
|
3487
|
-
const ck = visit_("key", node.key, visitor,
|
|
3486
|
+
path74 = Object.freeze(path74.concat(node));
|
|
3487
|
+
const ck = visit_("key", node.key, visitor, path74);
|
|
3488
3488
|
if (ck === BREAK)
|
|
3489
3489
|
return BREAK;
|
|
3490
3490
|
else if (ck === REMOVE)
|
|
3491
3491
|
node.key = null;
|
|
3492
|
-
const cv = visit_("value", node.value, visitor,
|
|
3492
|
+
const cv = visit_("value", node.value, visitor, path74);
|
|
3493
3493
|
if (cv === BREAK)
|
|
3494
3494
|
return BREAK;
|
|
3495
3495
|
else if (cv === REMOVE)
|
|
@@ -3510,17 +3510,17 @@ var require_visit = __commonJS({
|
|
|
3510
3510
|
visitAsync.BREAK = BREAK;
|
|
3511
3511
|
visitAsync.SKIP = SKIP;
|
|
3512
3512
|
visitAsync.REMOVE = REMOVE;
|
|
3513
|
-
async function visitAsync_(key, node, visitor,
|
|
3514
|
-
const ctrl = await callVisitor(key, node, visitor,
|
|
3513
|
+
async function visitAsync_(key, node, visitor, path74) {
|
|
3514
|
+
const ctrl = await callVisitor(key, node, visitor, path74);
|
|
3515
3515
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
3516
|
-
replaceNode(key,
|
|
3517
|
-
return visitAsync_(key, ctrl, visitor,
|
|
3516
|
+
replaceNode(key, path74, ctrl);
|
|
3517
|
+
return visitAsync_(key, ctrl, visitor, path74);
|
|
3518
3518
|
}
|
|
3519
3519
|
if (typeof ctrl !== "symbol") {
|
|
3520
3520
|
if (identity.isCollection(node)) {
|
|
3521
|
-
|
|
3521
|
+
path74 = Object.freeze(path74.concat(node));
|
|
3522
3522
|
for (let i = 0; i < node.items.length; ++i) {
|
|
3523
|
-
const ci = await visitAsync_(i, node.items[i], visitor,
|
|
3523
|
+
const ci = await visitAsync_(i, node.items[i], visitor, path74);
|
|
3524
3524
|
if (typeof ci === "number")
|
|
3525
3525
|
i = ci - 1;
|
|
3526
3526
|
else if (ci === BREAK)
|
|
@@ -3531,13 +3531,13 @@ var require_visit = __commonJS({
|
|
|
3531
3531
|
}
|
|
3532
3532
|
}
|
|
3533
3533
|
} else if (identity.isPair(node)) {
|
|
3534
|
-
|
|
3535
|
-
const ck = await visitAsync_("key", node.key, visitor,
|
|
3534
|
+
path74 = Object.freeze(path74.concat(node));
|
|
3535
|
+
const ck = await visitAsync_("key", node.key, visitor, path74);
|
|
3536
3536
|
if (ck === BREAK)
|
|
3537
3537
|
return BREAK;
|
|
3538
3538
|
else if (ck === REMOVE)
|
|
3539
3539
|
node.key = null;
|
|
3540
|
-
const cv = await visitAsync_("value", node.value, visitor,
|
|
3540
|
+
const cv = await visitAsync_("value", node.value, visitor, path74);
|
|
3541
3541
|
if (cv === BREAK)
|
|
3542
3542
|
return BREAK;
|
|
3543
3543
|
else if (cv === REMOVE)
|
|
@@ -3564,23 +3564,23 @@ var require_visit = __commonJS({
|
|
|
3564
3564
|
}
|
|
3565
3565
|
return visitor;
|
|
3566
3566
|
}
|
|
3567
|
-
function callVisitor(key, node, visitor,
|
|
3567
|
+
function callVisitor(key, node, visitor, path74) {
|
|
3568
3568
|
if (typeof visitor === "function")
|
|
3569
|
-
return visitor(key, node,
|
|
3569
|
+
return visitor(key, node, path74);
|
|
3570
3570
|
if (identity.isMap(node))
|
|
3571
|
-
return visitor.Map?.(key, node,
|
|
3571
|
+
return visitor.Map?.(key, node, path74);
|
|
3572
3572
|
if (identity.isSeq(node))
|
|
3573
|
-
return visitor.Seq?.(key, node,
|
|
3573
|
+
return visitor.Seq?.(key, node, path74);
|
|
3574
3574
|
if (identity.isPair(node))
|
|
3575
|
-
return visitor.Pair?.(key, node,
|
|
3575
|
+
return visitor.Pair?.(key, node, path74);
|
|
3576
3576
|
if (identity.isScalar(node))
|
|
3577
|
-
return visitor.Scalar?.(key, node,
|
|
3577
|
+
return visitor.Scalar?.(key, node, path74);
|
|
3578
3578
|
if (identity.isAlias(node))
|
|
3579
|
-
return visitor.Alias?.(key, node,
|
|
3579
|
+
return visitor.Alias?.(key, node, path74);
|
|
3580
3580
|
return void 0;
|
|
3581
3581
|
}
|
|
3582
|
-
function replaceNode(key,
|
|
3583
|
-
const parent =
|
|
3582
|
+
function replaceNode(key, path74, node) {
|
|
3583
|
+
const parent = path74[path74.length - 1];
|
|
3584
3584
|
if (identity.isCollection(parent)) {
|
|
3585
3585
|
parent.items[key] = node;
|
|
3586
3586
|
} else if (identity.isPair(parent)) {
|
|
@@ -4199,10 +4199,10 @@ var require_Collection = __commonJS({
|
|
|
4199
4199
|
var createNode = require_createNode();
|
|
4200
4200
|
var identity = require_identity();
|
|
4201
4201
|
var Node = require_Node();
|
|
4202
|
-
function collectionFromPath(schema,
|
|
4202
|
+
function collectionFromPath(schema, path74, value) {
|
|
4203
4203
|
let v = value;
|
|
4204
|
-
for (let i =
|
|
4205
|
-
const k =
|
|
4204
|
+
for (let i = path74.length - 1; i >= 0; --i) {
|
|
4205
|
+
const k = path74[i];
|
|
4206
4206
|
if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
|
|
4207
4207
|
const a = [];
|
|
4208
4208
|
a[k] = v;
|
|
@@ -4221,7 +4221,7 @@ var require_Collection = __commonJS({
|
|
|
4221
4221
|
sourceObjects: /* @__PURE__ */ new Map()
|
|
4222
4222
|
});
|
|
4223
4223
|
}
|
|
4224
|
-
var isEmptyPath = (
|
|
4224
|
+
var isEmptyPath = (path74) => path74 == null || typeof path74 === "object" && !!path74[Symbol.iterator]().next().done;
|
|
4225
4225
|
var Collection = class extends Node.NodeBase {
|
|
4226
4226
|
constructor(type, schema) {
|
|
4227
4227
|
super(type);
|
|
@@ -4251,11 +4251,11 @@ var require_Collection = __commonJS({
|
|
|
4251
4251
|
* be a Pair instance or a `{ key, value }` object, which may not have a key
|
|
4252
4252
|
* that already exists in the map.
|
|
4253
4253
|
*/
|
|
4254
|
-
addIn(
|
|
4255
|
-
if (isEmptyPath(
|
|
4254
|
+
addIn(path74, value) {
|
|
4255
|
+
if (isEmptyPath(path74))
|
|
4256
4256
|
this.add(value);
|
|
4257
4257
|
else {
|
|
4258
|
-
const [key, ...rest] =
|
|
4258
|
+
const [key, ...rest] = path74;
|
|
4259
4259
|
const node = this.get(key, true);
|
|
4260
4260
|
if (identity.isCollection(node))
|
|
4261
4261
|
node.addIn(rest, value);
|
|
@@ -4269,8 +4269,8 @@ var require_Collection = __commonJS({
|
|
|
4269
4269
|
* Removes a value from the collection.
|
|
4270
4270
|
* @returns `true` if the item was found and removed.
|
|
4271
4271
|
*/
|
|
4272
|
-
deleteIn(
|
|
4273
|
-
const [key, ...rest] =
|
|
4272
|
+
deleteIn(path74) {
|
|
4273
|
+
const [key, ...rest] = path74;
|
|
4274
4274
|
if (rest.length === 0)
|
|
4275
4275
|
return this.delete(key);
|
|
4276
4276
|
const node = this.get(key, true);
|
|
@@ -4284,8 +4284,8 @@ var require_Collection = __commonJS({
|
|
|
4284
4284
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
4285
4285
|
* `true` (collections are always returned intact).
|
|
4286
4286
|
*/
|
|
4287
|
-
getIn(
|
|
4288
|
-
const [key, ...rest] =
|
|
4287
|
+
getIn(path74, keepScalar) {
|
|
4288
|
+
const [key, ...rest] = path74;
|
|
4289
4289
|
const node = this.get(key, true);
|
|
4290
4290
|
if (rest.length === 0)
|
|
4291
4291
|
return !keepScalar && identity.isScalar(node) ? node.value : node;
|
|
@@ -4303,8 +4303,8 @@ var require_Collection = __commonJS({
|
|
|
4303
4303
|
/**
|
|
4304
4304
|
* Checks if the collection includes a value with the key `key`.
|
|
4305
4305
|
*/
|
|
4306
|
-
hasIn(
|
|
4307
|
-
const [key, ...rest] =
|
|
4306
|
+
hasIn(path74) {
|
|
4307
|
+
const [key, ...rest] = path74;
|
|
4308
4308
|
if (rest.length === 0)
|
|
4309
4309
|
return this.has(key);
|
|
4310
4310
|
const node = this.get(key, true);
|
|
@@ -4314,8 +4314,8 @@ var require_Collection = __commonJS({
|
|
|
4314
4314
|
* Sets a value in this collection. For `!!set`, `value` needs to be a
|
|
4315
4315
|
* boolean to add/remove the item from the set.
|
|
4316
4316
|
*/
|
|
4317
|
-
setIn(
|
|
4318
|
-
const [key, ...rest] =
|
|
4317
|
+
setIn(path74, value) {
|
|
4318
|
+
const [key, ...rest] = path74;
|
|
4319
4319
|
if (rest.length === 0) {
|
|
4320
4320
|
this.set(key, value);
|
|
4321
4321
|
} else {
|
|
@@ -6865,9 +6865,9 @@ var require_Document = __commonJS({
|
|
|
6865
6865
|
this.contents.add(value);
|
|
6866
6866
|
}
|
|
6867
6867
|
/** Adds a value to the document. */
|
|
6868
|
-
addIn(
|
|
6868
|
+
addIn(path74, value) {
|
|
6869
6869
|
if (assertCollection(this.contents))
|
|
6870
|
-
this.contents.addIn(
|
|
6870
|
+
this.contents.addIn(path74, value);
|
|
6871
6871
|
}
|
|
6872
6872
|
/**
|
|
6873
6873
|
* Create a new `Alias` node, ensuring that the target `node` has the required anchor.
|
|
@@ -6942,14 +6942,14 @@ var require_Document = __commonJS({
|
|
|
6942
6942
|
* Removes a value from the document.
|
|
6943
6943
|
* @returns `true` if the item was found and removed.
|
|
6944
6944
|
*/
|
|
6945
|
-
deleteIn(
|
|
6946
|
-
if (Collection.isEmptyPath(
|
|
6945
|
+
deleteIn(path74) {
|
|
6946
|
+
if (Collection.isEmptyPath(path74)) {
|
|
6947
6947
|
if (this.contents == null)
|
|
6948
6948
|
return false;
|
|
6949
6949
|
this.contents = null;
|
|
6950
6950
|
return true;
|
|
6951
6951
|
}
|
|
6952
|
-
return assertCollection(this.contents) ? this.contents.deleteIn(
|
|
6952
|
+
return assertCollection(this.contents) ? this.contents.deleteIn(path74) : false;
|
|
6953
6953
|
}
|
|
6954
6954
|
/**
|
|
6955
6955
|
* Returns item at `key`, or `undefined` if not found. By default unwraps
|
|
@@ -6964,10 +6964,10 @@ var require_Document = __commonJS({
|
|
|
6964
6964
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
6965
6965
|
* `true` (collections are always returned intact).
|
|
6966
6966
|
*/
|
|
6967
|
-
getIn(
|
|
6968
|
-
if (Collection.isEmptyPath(
|
|
6967
|
+
getIn(path74, keepScalar) {
|
|
6968
|
+
if (Collection.isEmptyPath(path74))
|
|
6969
6969
|
return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
|
|
6970
|
-
return identity.isCollection(this.contents) ? this.contents.getIn(
|
|
6970
|
+
return identity.isCollection(this.contents) ? this.contents.getIn(path74, keepScalar) : void 0;
|
|
6971
6971
|
}
|
|
6972
6972
|
/**
|
|
6973
6973
|
* Checks if the document includes a value with the key `key`.
|
|
@@ -6978,10 +6978,10 @@ var require_Document = __commonJS({
|
|
|
6978
6978
|
/**
|
|
6979
6979
|
* Checks if the document includes a value at `path`.
|
|
6980
6980
|
*/
|
|
6981
|
-
hasIn(
|
|
6982
|
-
if (Collection.isEmptyPath(
|
|
6981
|
+
hasIn(path74) {
|
|
6982
|
+
if (Collection.isEmptyPath(path74))
|
|
6983
6983
|
return this.contents !== void 0;
|
|
6984
|
-
return identity.isCollection(this.contents) ? this.contents.hasIn(
|
|
6984
|
+
return identity.isCollection(this.contents) ? this.contents.hasIn(path74) : false;
|
|
6985
6985
|
}
|
|
6986
6986
|
/**
|
|
6987
6987
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
@@ -6998,13 +6998,13 @@ var require_Document = __commonJS({
|
|
|
6998
6998
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
6999
6999
|
* boolean to add/remove the item from the set.
|
|
7000
7000
|
*/
|
|
7001
|
-
setIn(
|
|
7002
|
-
if (Collection.isEmptyPath(
|
|
7001
|
+
setIn(path74, value) {
|
|
7002
|
+
if (Collection.isEmptyPath(path74)) {
|
|
7003
7003
|
this.contents = value;
|
|
7004
7004
|
} else if (this.contents == null) {
|
|
7005
|
-
this.contents = Collection.collectionFromPath(this.schema, Array.from(
|
|
7005
|
+
this.contents = Collection.collectionFromPath(this.schema, Array.from(path74), value);
|
|
7006
7006
|
} else if (assertCollection(this.contents)) {
|
|
7007
|
-
this.contents.setIn(
|
|
7007
|
+
this.contents.setIn(path74, value);
|
|
7008
7008
|
}
|
|
7009
7009
|
}
|
|
7010
7010
|
/**
|
|
@@ -8984,9 +8984,9 @@ var require_cst_visit = __commonJS({
|
|
|
8984
8984
|
visit2.BREAK = BREAK;
|
|
8985
8985
|
visit2.SKIP = SKIP;
|
|
8986
8986
|
visit2.REMOVE = REMOVE;
|
|
8987
|
-
visit2.itemAtPath = (cst,
|
|
8987
|
+
visit2.itemAtPath = (cst, path74) => {
|
|
8988
8988
|
let item = cst;
|
|
8989
|
-
for (const [field, index] of
|
|
8989
|
+
for (const [field, index] of path74) {
|
|
8990
8990
|
const tok = item?.[field];
|
|
8991
8991
|
if (tok && "items" in tok) {
|
|
8992
8992
|
item = tok.items[index];
|
|
@@ -8995,23 +8995,23 @@ var require_cst_visit = __commonJS({
|
|
|
8995
8995
|
}
|
|
8996
8996
|
return item;
|
|
8997
8997
|
};
|
|
8998
|
-
visit2.parentCollection = (cst,
|
|
8999
|
-
const parent = visit2.itemAtPath(cst,
|
|
9000
|
-
const field =
|
|
8998
|
+
visit2.parentCollection = (cst, path74) => {
|
|
8999
|
+
const parent = visit2.itemAtPath(cst, path74.slice(0, -1));
|
|
9000
|
+
const field = path74[path74.length - 1][0];
|
|
9001
9001
|
const coll = parent?.[field];
|
|
9002
9002
|
if (coll && "items" in coll)
|
|
9003
9003
|
return coll;
|
|
9004
9004
|
throw new Error("Parent collection not found");
|
|
9005
9005
|
};
|
|
9006
|
-
function _visit(
|
|
9007
|
-
let ctrl = visitor(item,
|
|
9006
|
+
function _visit(path74, item, visitor) {
|
|
9007
|
+
let ctrl = visitor(item, path74);
|
|
9008
9008
|
if (typeof ctrl === "symbol")
|
|
9009
9009
|
return ctrl;
|
|
9010
9010
|
for (const field of ["key", "value"]) {
|
|
9011
9011
|
const token = item[field];
|
|
9012
9012
|
if (token && "items" in token) {
|
|
9013
9013
|
for (let i = 0; i < token.items.length; ++i) {
|
|
9014
|
-
const ci = _visit(Object.freeze(
|
|
9014
|
+
const ci = _visit(Object.freeze(path74.concat([[field, i]])), token.items[i], visitor);
|
|
9015
9015
|
if (typeof ci === "number")
|
|
9016
9016
|
i = ci - 1;
|
|
9017
9017
|
else if (ci === BREAK)
|
|
@@ -9022,10 +9022,10 @@ var require_cst_visit = __commonJS({
|
|
|
9022
9022
|
}
|
|
9023
9023
|
}
|
|
9024
9024
|
if (typeof ctrl === "function" && field === "key")
|
|
9025
|
-
ctrl = ctrl(item,
|
|
9025
|
+
ctrl = ctrl(item, path74);
|
|
9026
9026
|
}
|
|
9027
9027
|
}
|
|
9028
|
-
return typeof ctrl === "function" ? ctrl(item,
|
|
9028
|
+
return typeof ctrl === "function" ? ctrl(item, path74) : ctrl;
|
|
9029
9029
|
}
|
|
9030
9030
|
exports.visit = visit2;
|
|
9031
9031
|
}
|
|
@@ -10331,14 +10331,14 @@ var require_parser = __commonJS({
|
|
|
10331
10331
|
case "scalar":
|
|
10332
10332
|
case "single-quoted-scalar":
|
|
10333
10333
|
case "double-quoted-scalar": {
|
|
10334
|
-
const
|
|
10334
|
+
const fs66 = this.flowScalar(this.type);
|
|
10335
10335
|
if (atNextItem || it.value) {
|
|
10336
|
-
map.items.push({ start: start2, key:
|
|
10336
|
+
map.items.push({ start: start2, key: fs66, sep: [] });
|
|
10337
10337
|
this.onKeyLine = true;
|
|
10338
10338
|
} else if (it.sep) {
|
|
10339
|
-
this.stack.push(
|
|
10339
|
+
this.stack.push(fs66);
|
|
10340
10340
|
} else {
|
|
10341
|
-
Object.assign(it, { key:
|
|
10341
|
+
Object.assign(it, { key: fs66, sep: [] });
|
|
10342
10342
|
this.onKeyLine = true;
|
|
10343
10343
|
}
|
|
10344
10344
|
return;
|
|
@@ -10466,13 +10466,13 @@ var require_parser = __commonJS({
|
|
|
10466
10466
|
case "scalar":
|
|
10467
10467
|
case "single-quoted-scalar":
|
|
10468
10468
|
case "double-quoted-scalar": {
|
|
10469
|
-
const
|
|
10469
|
+
const fs66 = this.flowScalar(this.type);
|
|
10470
10470
|
if (!it || it.value)
|
|
10471
|
-
fc.items.push({ start: [], key:
|
|
10471
|
+
fc.items.push({ start: [], key: fs66, sep: [] });
|
|
10472
10472
|
else if (it.sep)
|
|
10473
|
-
this.stack.push(
|
|
10473
|
+
this.stack.push(fs66);
|
|
10474
10474
|
else
|
|
10475
|
-
Object.assign(it, { key:
|
|
10475
|
+
Object.assign(it, { key: fs66, sep: [] });
|
|
10476
10476
|
return;
|
|
10477
10477
|
}
|
|
10478
10478
|
case "flow-map-end":
|
|
@@ -10920,14 +10920,14 @@ function loadConfig() {
|
|
|
10920
10920
|
const data = (0, import_yaml.parse)(fs3.readFileSync(p, "utf8"));
|
|
10921
10921
|
return isDict(data) ? data : {};
|
|
10922
10922
|
}
|
|
10923
|
-
function registryPath(overrideKey, envName,
|
|
10923
|
+
function registryPath(overrideKey, envName, basename27) {
|
|
10924
10924
|
const override = loadConfig()["overrides"]?.[overrideKey];
|
|
10925
10925
|
if (override)
|
|
10926
10926
|
return path3.resolve(expandUser(String(override)));
|
|
10927
10927
|
const fromEnv = envVar(envName);
|
|
10928
10928
|
if (fromEnv)
|
|
10929
10929
|
return path3.resolve(expandUser(fromEnv));
|
|
10930
|
-
return path3.join(sandboxHome(),
|
|
10930
|
+
return path3.join(sandboxHome(), basename27);
|
|
10931
10931
|
}
|
|
10932
10932
|
function servicesYamlPath() {
|
|
10933
10933
|
return registryPath("services_yaml", "SERVICES_YAML", "services.yaml");
|
|
@@ -11163,7 +11163,7 @@ function monorepoRoot() {
|
|
|
11163
11163
|
}
|
|
11164
11164
|
return sandboxRoot();
|
|
11165
11165
|
}
|
|
11166
|
-
var import_yaml;
|
|
11166
|
+
var import_yaml, IDLE_SERVICE_TYPES;
|
|
11167
11167
|
var init_config = __esm({
|
|
11168
11168
|
"../../packages/sandbox-core/dist/config.js"() {
|
|
11169
11169
|
"use strict";
|
|
@@ -11171,6 +11171,7 @@ var init_config = __esm({
|
|
|
11171
11171
|
import_yaml = __toESM(require_dist(), 1);
|
|
11172
11172
|
init_dot_dir();
|
|
11173
11173
|
init_reporter();
|
|
11174
|
+
IDLE_SERVICE_TYPES = ["cli", "library", "extension"];
|
|
11174
11175
|
}
|
|
11175
11176
|
});
|
|
11176
11177
|
|
|
@@ -11845,6 +11846,8 @@ function fromGh(ref, i) {
|
|
|
11845
11846
|
ref,
|
|
11846
11847
|
title: i.title?.trim() || void 0,
|
|
11847
11848
|
status: titleCase(i.state),
|
|
11849
|
+
...i.state?.toUpperCase() === "CLOSED" ? { statusCategory: "done" } : {},
|
|
11850
|
+
labels: i.labels?.map((l) => l.name),
|
|
11848
11851
|
type: i.labels?.map((l) => l.name).join(", ") || void 0,
|
|
11849
11852
|
assignee: i.assignees?.map((a) => a.login).join(", ") || void 0,
|
|
11850
11853
|
description: i.body?.trim() || void 0,
|
|
@@ -11863,7 +11866,7 @@ async function view(ref, fields) {
|
|
|
11863
11866
|
return null;
|
|
11864
11867
|
}
|
|
11865
11868
|
}
|
|
11866
|
-
var GITHUB_ISSUE_PATTERN, FULL_RE, CLOSED, OPEN, STATUS_LABEL_COLOR, ghBin, titleCase, github;
|
|
11869
|
+
var GITHUB_ISSUE_PATTERN, FULL_RE, CLOSED, NOT_PLANNED, STATUS_PREFIX, OPEN, STATUS_LABEL_COLOR, ghBin, titleCase, github;
|
|
11867
11870
|
var init_github = __esm({
|
|
11868
11871
|
"../../packages/sandbox-core/dist/issue_trackers/github.js"() {
|
|
11869
11872
|
"use strict";
|
|
@@ -11874,7 +11877,9 @@ var init_github = __esm({
|
|
|
11874
11877
|
init_proc();
|
|
11875
11878
|
GITHUB_ISSUE_PATTERN = "^([A-Za-z0-9_.-]+/)?[A-Za-z0-9_.-]+#[0-9]+$";
|
|
11876
11879
|
FULL_RE = /^([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)#(\d+)$/;
|
|
11877
|
-
CLOSED = /* @__PURE__ */ new Set(["closed", "close", "done"]);
|
|
11880
|
+
CLOSED = /* @__PURE__ */ new Set(["closed", "close", "done", "completed"]);
|
|
11881
|
+
NOT_PLANNED = /* @__PURE__ */ new Set(["not planned", "abandoned"]);
|
|
11882
|
+
STATUS_PREFIX = "status:";
|
|
11878
11883
|
OPEN = /* @__PURE__ */ new Set(["open", "reopen", "reopened"]);
|
|
11879
11884
|
STATUS_LABEL_COLOR = "1D76DB";
|
|
11880
11885
|
ghBin = () => envVar("GH_BIN") || "gh";
|
|
@@ -11918,12 +11923,24 @@ var init_github = __esm({
|
|
|
11918
11923
|
if (status2) {
|
|
11919
11924
|
const s = status2.toLowerCase();
|
|
11920
11925
|
if (CLOSED.has(s))
|
|
11921
|
-
await gh(["issue", "close", n, "-R", repo]);
|
|
11926
|
+
await gh(["issue", "close", n, "-R", repo, "--reason", "completed"]);
|
|
11927
|
+
else if (NOT_PLANNED.has(s))
|
|
11928
|
+
await gh(["issue", "close", n, "-R", repo, "--reason", "not planned"]);
|
|
11922
11929
|
else if (OPEN.has(s))
|
|
11923
11930
|
await gh(["issue", "reopen", n, "-R", repo]);
|
|
11924
11931
|
else {
|
|
11932
|
+
const stale = s.startsWith(STATUS_PREFIX) ? ((await view(ref, "labels"))?.labels ?? []).filter((l) => l.toLowerCase().startsWith(STATUS_PREFIX) && l.toLowerCase() !== s) : [];
|
|
11925
11933
|
await gh(["label", "create", status2, "-R", repo, "--color", STATUS_LABEL_COLOR, "--force"]);
|
|
11926
|
-
await gh([
|
|
11934
|
+
await gh([
|
|
11935
|
+
"issue",
|
|
11936
|
+
"edit",
|
|
11937
|
+
n,
|
|
11938
|
+
"-R",
|
|
11939
|
+
repo,
|
|
11940
|
+
"--add-label",
|
|
11941
|
+
status2,
|
|
11942
|
+
...stale.length ? ["--remove-label", stale.join(",")] : []
|
|
11943
|
+
]);
|
|
11927
11944
|
}
|
|
11928
11945
|
}
|
|
11929
11946
|
if (update.comment)
|
|
@@ -12053,19 +12070,19 @@ async function updateIssueRest(key, update) {
|
|
|
12053
12070
|
const creds = await jiraCreds();
|
|
12054
12071
|
if (!creds)
|
|
12055
12072
|
return null;
|
|
12056
|
-
const
|
|
12073
|
+
const path74 = `/rest/api/2/issue/${encodeURIComponent(key)}`;
|
|
12057
12074
|
if (update.status) {
|
|
12058
12075
|
const want = update.status.trim().toLowerCase();
|
|
12059
|
-
const { transitions = [] } = await jiraGet(creds, `${
|
|
12076
|
+
const { transitions = [] } = await jiraGet(creds, `${path74}/transitions`);
|
|
12060
12077
|
const t = transitions.find((x) => x.to?.name?.toLowerCase() === want) ?? transitions.find((x) => x.name?.toLowerCase() === want);
|
|
12061
12078
|
if (!t) {
|
|
12062
12079
|
const names = transitions.map((x) => x.to?.name ?? x.name).filter(Boolean).join(", ");
|
|
12063
12080
|
throw new Error(`${key} has no transition to "${update.status}" (available: ${names || "none"})`);
|
|
12064
12081
|
}
|
|
12065
|
-
await jiraFetch(creds, `${
|
|
12082
|
+
await jiraFetch(creds, `${path74}/transitions`, { transition: { id: t.id } });
|
|
12066
12083
|
}
|
|
12067
12084
|
if (update.comment)
|
|
12068
|
-
await jiraFetch(creds, `${
|
|
12085
|
+
await jiraFetch(creds, `${path74}/comment`, { body: update.comment });
|
|
12069
12086
|
return true;
|
|
12070
12087
|
}
|
|
12071
12088
|
var JIRA_PLACEHOLDERS, isJiraPlaceholder, ISSUE_FIELDS;
|
|
@@ -12119,6 +12136,7 @@ function fromRest(r) {
|
|
|
12119
12136
|
ref: r.key,
|
|
12120
12137
|
title: str(f.summary),
|
|
12121
12138
|
status: str(f.status?.name),
|
|
12139
|
+
statusCategory: category(f.status?.statusCategory?.key),
|
|
12122
12140
|
type: str(f.issuetype?.name),
|
|
12123
12141
|
assignee: str(f.assignee?.displayName),
|
|
12124
12142
|
description: str(f.description),
|
|
@@ -12129,7 +12147,7 @@ function fromRest(r) {
|
|
|
12129
12147
|
async function fetchViaMcp(key) {
|
|
12130
12148
|
const parsed = await askClaudeJson(`You are a data extractor, not an agent. Do exactly two things and nothing else:
|
|
12131
12149
|
1. Fetch Jira issue ${key} INCLUDING ITS COMMENTS using getJiraIssue ${jiraSitePhrase()} \u2014 request fields ["summary","status","issuetype","assignee","description","comment"].
|
|
12132
|
-
2. Output ONLY a JSON object between <result> and </result> tags with keys "title","status","type","assignee","description" (strings, "" when unavailable) and "comments" (an array of {"author","created","body"}, oldest first). ` + READ_ONLY + "\nIf you cannot fetch the issue, output <result>{}</result> and nothing else.", { tools: true });
|
|
12150
|
+
2. Output ONLY a JSON object between <result> and </result> tags with keys "title","status","statusCategory" (the status category key: new, indeterminate or done), "type","assignee","description" (strings, "" when unavailable) and "comments" (an array of {"author","created","body"}, oldest first). ` + READ_ONLY + "\nIf you cannot fetch the issue, output <result>{}</result> and nothing else.", { tools: true });
|
|
12133
12151
|
if (!parsed || !str(parsed.title))
|
|
12134
12152
|
return null;
|
|
12135
12153
|
const comments = Array.isArray(parsed.comments) ? parsed.comments.flatMap((c) => {
|
|
@@ -12143,6 +12161,7 @@ async function fetchViaMcp(key) {
|
|
|
12143
12161
|
ref: key,
|
|
12144
12162
|
title: str(parsed.title),
|
|
12145
12163
|
status: str(parsed.status),
|
|
12164
|
+
statusCategory: category(parsed.statusCategory),
|
|
12146
12165
|
type: str(parsed.type),
|
|
12147
12166
|
assignee: str(parsed.assignee),
|
|
12148
12167
|
description: str(parsed.description),
|
|
@@ -12173,7 +12192,7 @@ ${update.comment}`
|
|
|
12173
12192
|
if (parsed?.ok !== true)
|
|
12174
12193
|
throw new Error(str(parsed?.error) ?? `Could not update ${key} through the Atlassian MCP`);
|
|
12175
12194
|
}
|
|
12176
|
-
var KEY_RE, str, READ_ONLY, SANDBOX_ISSUES_REPO, ISSUE_TYPES, jira;
|
|
12195
|
+
var KEY_RE, str, CATEGORIES, category, READ_ONLY, SANDBOX_ISSUES_REPO, ISSUE_TYPES, jira;
|
|
12177
12196
|
var init_jira = __esm({
|
|
12178
12197
|
"../../packages/sandbox-core/dist/issue_trackers/jira.js"() {
|
|
12179
12198
|
"use strict";
|
|
@@ -12183,6 +12202,8 @@ var init_jira = __esm({
|
|
|
12183
12202
|
init_jira_rest();
|
|
12184
12203
|
KEY_RE = /[A-Za-z][A-Za-z0-9]+-\d+/;
|
|
12185
12204
|
str = (v) => typeof v === "string" && v.trim() ? v.trim() : void 0;
|
|
12205
|
+
CATEGORIES = ["new", "indeterminate", "done"];
|
|
12206
|
+
category = (v) => CATEGORIES.find((c) => c === str(v)?.toLowerCase());
|
|
12186
12207
|
READ_ONLY = "Do not create, edit, transition, or comment on anything; write no prose outside the tags.";
|
|
12187
12208
|
SANDBOX_ISSUES_REPO = "jbbz-sandbox/werkmeister";
|
|
12188
12209
|
ISSUE_TYPES = { bug: "Bug", feature: "Story", task: "Task" };
|
|
@@ -12420,7 +12441,8 @@ function readAutonomous(workspaceRoot) {
|
|
|
12420
12441
|
return isTrue(readWorkspaceField(workspaceRoot, "autonomous"));
|
|
12421
12442
|
}
|
|
12422
12443
|
function readAutoStop(workspaceRoot) {
|
|
12423
|
-
|
|
12444
|
+
const value = readWorkspaceField(workspaceRoot, "auto_stop");
|
|
12445
|
+
return value === void 0 || value === null || value === "" ? readAutonomous(workspaceRoot) : isTrue(value);
|
|
12424
12446
|
}
|
|
12425
12447
|
function readModel(workspaceRoot) {
|
|
12426
12448
|
const model = readWorkspaceField(workspaceRoot, "model");
|
|
@@ -12795,7 +12817,7 @@ var init_workspace_prompts = __esm({
|
|
|
12795
12817
|
{
|
|
12796
12818
|
key: "auto_stop",
|
|
12797
12819
|
label: "Auto-stop on session end",
|
|
12798
|
-
help: "
|
|
12820
|
+
help: "On by default for autonomous workspaces, off otherwise. On: when a Claude session in the workspace ends and no other one is open there, a SessionEnd hook runs `sandbox stop --if-idle`, which stops this feature's containers (slot and volumes kept; `sandbox up` resumes). Shared infra is never stopped.",
|
|
12799
12821
|
boolean: true
|
|
12800
12822
|
},
|
|
12801
12823
|
{
|
|
@@ -13311,7 +13333,16 @@ var init_hooks_gen = __esm({
|
|
|
13311
13333
|
// No-arg forms act on the cwd's FEATURE; `autonomousRules` adds the named ones.
|
|
13312
13334
|
"Bash(sandbox up)",
|
|
13313
13335
|
"Bash(sandbox restart)",
|
|
13314
|
-
...[
|
|
13336
|
+
...[
|
|
13337
|
+
"status",
|
|
13338
|
+
"pr",
|
|
13339
|
+
"issue view",
|
|
13340
|
+
"issue start",
|
|
13341
|
+
"init-workspace",
|
|
13342
|
+
"skills",
|
|
13343
|
+
"standards",
|
|
13344
|
+
"notes context"
|
|
13345
|
+
].map((cmd) => `Bash(sandbox ${cmd}:*)`),
|
|
13315
13346
|
"Bash(docker ps:*)",
|
|
13316
13347
|
// Local build and test; no `npm install`, so a new dependency waits for a person.
|
|
13317
13348
|
...["npm ci", "npm run", "npm test", "npx", "node", "python3"].map((cmd) => `Bash(${cmd}:*)`),
|
|
@@ -13560,8 +13591,8 @@ Lifecycle: \`working \u2192 ready \u2192 pr-open \u2192 merged \u2192 shipped \u
|
|
|
13560
13591
|
| \`sandbox pr status\` | record merged / shipped / closed from GitHub and release tags |
|
|
13561
13592
|
| \`sandbox pr adopt <pr-number\\|url> [repo]\` | the fix landed outside this workspace: write the PR file from that PR, then record its status |
|
|
13562
13593
|
| \`sandbox pr adopt --no-pr "<reason>" [repo]\` | the fix is already on base with no PR: write a \`closed\` PR file with the reason |
|
|
13563
|
-
| \`sandbox close-out\` | after ship:
|
|
13564
|
-
| \`sandbox close-out --abandon "<reason>"\` | give up: record the reason, push and keep every branch, remove the workspace |
|
|
13594
|
+
| \`sandbox close-out\` | after ship: move the issue to done if shipping didn't, write \`closeout.md\`, remove the workspace, delete merged branches |
|
|
13595
|
+
| \`sandbox close-out --abandon "<reason>"\` | give up: record the reason, comment it on the issue, push and keep every branch, remove the workspace |
|
|
13565
13596
|
`;
|
|
13566
13597
|
}
|
|
13567
13598
|
var init_pr_rules = __esm({
|
|
@@ -14184,7 +14215,7 @@ var init_icm_templates = __esm({
|
|
|
14184
14215
|
"One commit per code repo touched, on that worktree's branch",
|
|
14185
14216
|
"`output/pr-<repo>.md` per repo"
|
|
14186
14217
|
],
|
|
14187
|
-
humanCheck: "Read each `pr-*.md`. Set `status: approved` on each one to open \u2014 that flip is the approval. Then `sandbox pr open` runs the gate and opens it. Merging is yours. After the release, `sandbox pr status`, then `sandbox close-out`.",
|
|
14218
|
+
humanCheck: "Read each `pr-*.md`. Set `status: approved` on each one to open \u2014 that flip is the approval. Then `sandbox pr open` runs the gate and opens it. Merging is yours. After the release, `sandbox pr status`, then ask the person before `sandbox close-out`.",
|
|
14188
14219
|
selfCheck: "Review each `pr-*.md` once against `../../BRIEF.md` and the live issue. Add `## Recommendations for reviewer` to its body \u2014 every decision and question collected on the way \u2014 then set `status: approved` yourself and run `sandbox pr open`. Stop at the PR URL. Merging is the person's."
|
|
14189
14220
|
})
|
|
14190
14221
|
};
|
|
@@ -14462,6 +14493,7 @@ function renderIssueRef(issue) {
|
|
|
14462
14493
|
"",
|
|
14463
14494
|
`${name}${title}. ${copy}`,
|
|
14464
14495
|
"Before each stage, re-read the live issue for newer direction: `sandbox issue view`",
|
|
14496
|
+
"Its status moves by itself (In Progress \u2192 In Review \u2192 Done): `docs/workspace-lifecycle.md` \xA7 Issue status in the sandbox repo.",
|
|
14465
14497
|
""
|
|
14466
14498
|
].join("\n");
|
|
14467
14499
|
}
|
|
@@ -14495,6 +14527,17 @@ function proposeDir(stages) {
|
|
|
14495
14527
|
const i = stages.findIndex((s) => s.name === "propose");
|
|
14496
14528
|
return i < 0 ? null : stageDirs(stages)[i];
|
|
14497
14529
|
}
|
|
14530
|
+
function restartLine(feature2, services) {
|
|
14531
|
+
const [servicesConfig] = loadSandboxServiceConfigs();
|
|
14532
|
+
const isIdle = (s) => IDLE_SERVICE_TYPES.includes(servicesConfig[s]?.type ?? "");
|
|
14533
|
+
const names = (list2) => list2.map((s) => `\`${feature2}-${s}\``).join(", ");
|
|
14534
|
+
const restarted = services.filter((s) => !isIdle(s));
|
|
14535
|
+
const idle = services.filter(isIdle);
|
|
14536
|
+
return [
|
|
14537
|
+
restarted.length ? restarted.map((s) => `\`docker restart ${feature2}-${s}\``).join(", ") : "",
|
|
14538
|
+
idle.length ? `${names(idle)} run no watcher, so a restart rebuilds nothing: build and test inside them with \`docker exec\` as \`.sandbox/sandbox-context.md\` \xA7 Test commands shows` : ""
|
|
14539
|
+
].filter(Boolean).join("; ");
|
|
14540
|
+
}
|
|
14498
14541
|
function vars(ctx, stages) {
|
|
14499
14542
|
const dirs = stageDirs(stages);
|
|
14500
14543
|
const feature2 = (ctx.meta["FEATURE"] ?? "").trim();
|
|
@@ -14503,8 +14546,7 @@ function vars(ctx, stages) {
|
|
|
14503
14546
|
return {
|
|
14504
14547
|
at: (name) => `../${dirs[stages.findIndex((s) => s.name === name)]}`,
|
|
14505
14548
|
repos: repos.length ? repos.map((r) => `\`../../${r}/\``).join(", ") : "the code worktrees in this workspace",
|
|
14506
|
-
|
|
14507
|
-
restart: feature2 && services.length ? services.map((s) => `\`docker restart ${feature2}-${s}\``).join(", ") : "restart whatever serves the change as `.sandbox/sandbox-context.md` describes",
|
|
14549
|
+
restart: feature2 && services.length ? restartLine(feature2, services) : "restart whatever serves the change as `.sandbox/sandbox-context.md` describes",
|
|
14508
14550
|
standards: "",
|
|
14509
14551
|
autonomous: isTrue(ctx.inputs["autonomous"])
|
|
14510
14552
|
};
|
|
@@ -14555,7 +14597,7 @@ function standardsRef(tier, r) {
|
|
|
14555
14597
|
const rows = tier === "all" ? r.active : r.active.filter((d) => universal.includes(d));
|
|
14556
14598
|
return `- Reference (every run): \`../../_shared/standards.md\` \u2014 rows ${[...rows, "ponytail"].map((d) => `\`${d}\``).join(", ")}`;
|
|
14557
14599
|
}
|
|
14558
|
-
function renderContract(dir, stage, v) {
|
|
14600
|
+
function renderContract(dir, stage, v, startsIssue = false) {
|
|
14559
14601
|
const c = stage.contract(v);
|
|
14560
14602
|
return [
|
|
14561
14603
|
`# ${dir} \u2014 ${stage.job}`,
|
|
@@ -14569,6 +14611,8 @@ function renderContract(dir, stage, v) {
|
|
|
14569
14611
|
`Do NOT load: ${c.doNotLoad}`,
|
|
14570
14612
|
"",
|
|
14571
14613
|
"## Process",
|
|
14614
|
+
// Step 0, so the contracts' own "steps 1–3" references keep their numbers.
|
|
14615
|
+
...startsIssue ? [`0. ${START_ISSUE}`] : [],
|
|
14572
14616
|
...c.process.map((p, i) => `${i + 1}. ${p}`),
|
|
14573
14617
|
"",
|
|
14574
14618
|
"## Outputs",
|
|
@@ -14615,7 +14659,7 @@ function renderClaudeMd(ctx, stages) {
|
|
|
14615
14659
|
...proposeDir(stages) ? [
|
|
14616
14660
|
"| asked to open or create a PR \u2014 including `/create-pr` or `finishing-a-development-branch` | `_shared/pr-rules.md`, then `sandbox pr open`, then Auto-fix on (rule 5) | PR URL is in the PR file and Auto-fix is on |"
|
|
14617
14661
|
] : [],
|
|
14618
|
-
"| a PR merged or released, or asked to close out | `sandbox pr status`, then `sandbox close-out` | closed out |",
|
|
14662
|
+
"| a PR merged or released, or asked to close out | `sandbox pr status`, then ask the person before `sandbox close-out` | closed out after the person says yes |",
|
|
14619
14663
|
"| asked for status | `ls stages/*/output/` | report which stages hold artifacts |",
|
|
14620
14664
|
"| containers / ports / routing broken | `.sandbox/sandbox-context.md` | \u2014 |",
|
|
14621
14665
|
"| you hit a bug or need to log a feature/task \u2014 in the `sandbox` tool or in a repo's code | `_shared/issues-report.md`: if this workspace's changes caused a bug, fix it here; otherwise file an issue where it belongs | fixed, or issue URL reported to the person |",
|
|
@@ -14695,10 +14739,7 @@ function render(ctx) {
|
|
|
14695
14739
|
if (propose2)
|
|
14696
14740
|
files["_shared/pr-rules.md"] = buildPrRulesMd(`stages/${propose2}/output/`, base.autonomous);
|
|
14697
14741
|
stages.forEach((stage, i) => {
|
|
14698
|
-
files[`stages/${dirs[i]}/CONTEXT.md`] = renderContract(dirs[i], stage, {
|
|
14699
|
-
...base,
|
|
14700
|
-
standards: standardsRef(stage.standards, r)
|
|
14701
|
-
});
|
|
14742
|
+
files[`stages/${dirs[i]}/CONTEXT.md`] = renderContract(dirs[i], stage, { ...base, standards: standardsRef(stage.standards, r) }, i === 0 && Boolean(issueIdOfMeta(ctx.meta)));
|
|
14702
14743
|
});
|
|
14703
14744
|
return files;
|
|
14704
14745
|
}
|
|
@@ -14768,7 +14809,7 @@ function init(ctx) {
|
|
|
14768
14809
|
}
|
|
14769
14810
|
icm.refresh(root);
|
|
14770
14811
|
}
|
|
14771
|
-
var PONYTAIL_ROW, ONE_RULE, NO_PROMPT, READ_ISSUE, PLACEHOLDER_BRIEF_RE, icm;
|
|
14812
|
+
var PONYTAIL_ROW, START_ISSUE, ONE_RULE, NO_PROMPT, READ_ISSUE, PLACEHOLDER_BRIEF_RE, icm;
|
|
14772
14813
|
var init_icm = __esm({
|
|
14773
14814
|
"../../packages/sandbox-core/dist/strategies/icm.js"() {
|
|
14774
14815
|
"use strict";
|
|
@@ -14786,6 +14827,7 @@ var init_icm = __esm({
|
|
|
14786
14827
|
init_icm_templates();
|
|
14787
14828
|
init_issue_section();
|
|
14788
14829
|
PONYTAIL_ROW = "| ponytail | already loaded: the ponytail plugin's SessionStart hook prints `PONYTAIL MODE ACTIVE` in every session. Invoke the `ponytail` skill only if that line is missing from your session context | any code edit |";
|
|
14830
|
+
START_ISSUE = "Run `sandbox issue start`: it moves the issue to In Progress, and does nothing when the issue is already past that or the tracker refuses.";
|
|
14789
14831
|
ONE_RULE = [
|
|
14790
14832
|
"## The one rule",
|
|
14791
14833
|
"",
|
|
@@ -15095,69 +15137,69 @@ var require_chownr = __commonJS({
|
|
|
15095
15137
|
"../../node_modules/chownr/chownr.js"(exports, module) {
|
|
15096
15138
|
"use strict";
|
|
15097
15139
|
init_define_SANDBOX_BUILD();
|
|
15098
|
-
var
|
|
15099
|
-
var
|
|
15100
|
-
var LCHOWN =
|
|
15101
|
-
var LCHOWNSYNC =
|
|
15102
|
-
var needEISDIRHandled =
|
|
15103
|
-
var lchownSync = (
|
|
15140
|
+
var fs66 = __require("fs");
|
|
15141
|
+
var path74 = __require("path");
|
|
15142
|
+
var LCHOWN = fs66.lchown ? "lchown" : "chown";
|
|
15143
|
+
var LCHOWNSYNC = fs66.lchownSync ? "lchownSync" : "chownSync";
|
|
15144
|
+
var needEISDIRHandled = fs66.lchown && !process.version.match(/v1[1-9]+\./) && !process.version.match(/v10\.[6-9]/);
|
|
15145
|
+
var lchownSync = (path75, uid, gid) => {
|
|
15104
15146
|
try {
|
|
15105
|
-
return
|
|
15147
|
+
return fs66[LCHOWNSYNC](path75, uid, gid);
|
|
15106
15148
|
} catch (er) {
|
|
15107
15149
|
if (er.code !== "ENOENT")
|
|
15108
15150
|
throw er;
|
|
15109
15151
|
}
|
|
15110
15152
|
};
|
|
15111
|
-
var chownSync = (
|
|
15153
|
+
var chownSync = (path75, uid, gid) => {
|
|
15112
15154
|
try {
|
|
15113
|
-
return
|
|
15155
|
+
return fs66.chownSync(path75, uid, gid);
|
|
15114
15156
|
} catch (er) {
|
|
15115
15157
|
if (er.code !== "ENOENT")
|
|
15116
15158
|
throw er;
|
|
15117
15159
|
}
|
|
15118
15160
|
};
|
|
15119
|
-
var handleEISDIR = needEISDIRHandled ? (
|
|
15161
|
+
var handleEISDIR = needEISDIRHandled ? (path75, uid, gid, cb) => (er) => {
|
|
15120
15162
|
if (!er || er.code !== "EISDIR")
|
|
15121
15163
|
cb(er);
|
|
15122
15164
|
else
|
|
15123
|
-
|
|
15165
|
+
fs66.chown(path75, uid, gid, cb);
|
|
15124
15166
|
} : (_, __, ___, cb) => cb;
|
|
15125
|
-
var handleEISDirSync = needEISDIRHandled ? (
|
|
15167
|
+
var handleEISDirSync = needEISDIRHandled ? (path75, uid, gid) => {
|
|
15126
15168
|
try {
|
|
15127
|
-
return lchownSync(
|
|
15169
|
+
return lchownSync(path75, uid, gid);
|
|
15128
15170
|
} catch (er) {
|
|
15129
15171
|
if (er.code !== "EISDIR")
|
|
15130
15172
|
throw er;
|
|
15131
|
-
chownSync(
|
|
15173
|
+
chownSync(path75, uid, gid);
|
|
15132
15174
|
}
|
|
15133
|
-
} : (
|
|
15175
|
+
} : (path75, uid, gid) => lchownSync(path75, uid, gid);
|
|
15134
15176
|
var nodeVersion = process.version;
|
|
15135
|
-
var readdir = (
|
|
15136
|
-
var
|
|
15177
|
+
var readdir = (path75, options, cb) => fs66.readdir(path75, options, cb);
|
|
15178
|
+
var readdirSync20 = (path75, options) => fs66.readdirSync(path75, options);
|
|
15137
15179
|
if (/^v4\./.test(nodeVersion))
|
|
15138
|
-
readdir = (
|
|
15180
|
+
readdir = (path75, options, cb) => fs66.readdir(path75, cb);
|
|
15139
15181
|
var chown = (cpath, uid, gid, cb) => {
|
|
15140
|
-
|
|
15182
|
+
fs66[LCHOWN](cpath, uid, gid, handleEISDIR(cpath, uid, gid, (er) => {
|
|
15141
15183
|
cb(er && er.code !== "ENOENT" ? er : null);
|
|
15142
15184
|
}));
|
|
15143
15185
|
};
|
|
15144
15186
|
var chownrKid = (p, child, uid, gid, cb) => {
|
|
15145
15187
|
if (typeof child === "string")
|
|
15146
|
-
return
|
|
15188
|
+
return fs66.lstat(path74.resolve(p, child), (er, stats) => {
|
|
15147
15189
|
if (er)
|
|
15148
15190
|
return cb(er.code !== "ENOENT" ? er : null);
|
|
15149
15191
|
stats.name = child;
|
|
15150
15192
|
chownrKid(p, stats, uid, gid, cb);
|
|
15151
15193
|
});
|
|
15152
15194
|
if (child.isDirectory()) {
|
|
15153
|
-
chownr(
|
|
15195
|
+
chownr(path74.resolve(p, child.name), uid, gid, (er) => {
|
|
15154
15196
|
if (er)
|
|
15155
15197
|
return cb(er);
|
|
15156
|
-
const cpath =
|
|
15198
|
+
const cpath = path74.resolve(p, child.name);
|
|
15157
15199
|
chown(cpath, uid, gid, cb);
|
|
15158
15200
|
});
|
|
15159
15201
|
} else {
|
|
15160
|
-
const cpath =
|
|
15202
|
+
const cpath = path74.resolve(p, child.name);
|
|
15161
15203
|
chown(cpath, uid, gid, cb);
|
|
15162
15204
|
}
|
|
15163
15205
|
};
|
|
@@ -15187,7 +15229,7 @@ var require_chownr = __commonJS({
|
|
|
15187
15229
|
var chownrKidSync = (p, child, uid, gid) => {
|
|
15188
15230
|
if (typeof child === "string") {
|
|
15189
15231
|
try {
|
|
15190
|
-
const stats =
|
|
15232
|
+
const stats = fs66.lstatSync(path74.resolve(p, child));
|
|
15191
15233
|
stats.name = child;
|
|
15192
15234
|
child = stats;
|
|
15193
15235
|
} catch (er) {
|
|
@@ -15198,13 +15240,13 @@ var require_chownr = __commonJS({
|
|
|
15198
15240
|
}
|
|
15199
15241
|
}
|
|
15200
15242
|
if (child.isDirectory())
|
|
15201
|
-
chownrSync(
|
|
15202
|
-
handleEISDirSync(
|
|
15243
|
+
chownrSync(path74.resolve(p, child.name), uid, gid);
|
|
15244
|
+
handleEISDirSync(path74.resolve(p, child.name), uid, gid);
|
|
15203
15245
|
};
|
|
15204
15246
|
var chownrSync = (p, uid, gid) => {
|
|
15205
15247
|
let children;
|
|
15206
15248
|
try {
|
|
15207
|
-
children =
|
|
15249
|
+
children = readdirSync20(p, { withFileTypes: true });
|
|
15208
15250
|
} catch (er) {
|
|
15209
15251
|
if (er.code === "ENOENT")
|
|
15210
15252
|
return;
|
|
@@ -15355,7 +15397,7 @@ var require_buffer_list = __commonJS({
|
|
|
15355
15397
|
}
|
|
15356
15398
|
}, {
|
|
15357
15399
|
key: "join",
|
|
15358
|
-
value: function
|
|
15400
|
+
value: function join53(s) {
|
|
15359
15401
|
if (this.length === 0) return "";
|
|
15360
15402
|
var p = this.head;
|
|
15361
15403
|
var ret = "" + p.data;
|
|
@@ -19200,9 +19242,9 @@ var require_pump = __commonJS({
|
|
|
19200
19242
|
init_define_SANDBOX_BUILD();
|
|
19201
19243
|
var once = require_once();
|
|
19202
19244
|
var eos = require_end_of_stream2();
|
|
19203
|
-
var
|
|
19245
|
+
var fs66;
|
|
19204
19246
|
try {
|
|
19205
|
-
|
|
19247
|
+
fs66 = __require("fs");
|
|
19206
19248
|
} catch (e) {
|
|
19207
19249
|
}
|
|
19208
19250
|
var noop = function() {
|
|
@@ -19213,8 +19255,8 @@ var require_pump = __commonJS({
|
|
|
19213
19255
|
};
|
|
19214
19256
|
var isFS = function(stream) {
|
|
19215
19257
|
if (!ancient) return false;
|
|
19216
|
-
if (!
|
|
19217
|
-
return (stream instanceof (
|
|
19258
|
+
if (!fs66) return false;
|
|
19259
|
+
return (stream instanceof (fs66.ReadStream || noop) || stream instanceof (fs66.WriteStream || noop)) && isFn(stream.close);
|
|
19218
19260
|
};
|
|
19219
19261
|
var isRequest = function(stream) {
|
|
19220
19262
|
return stream.setHeader && isFn(stream.abort);
|
|
@@ -19274,8 +19316,8 @@ var require_pump = __commonJS({
|
|
|
19274
19316
|
var require_mkdirp_classic = __commonJS({
|
|
19275
19317
|
"../../node_modules/mkdirp-classic/index.js"(exports, module) {
|
|
19276
19318
|
init_define_SANDBOX_BUILD();
|
|
19277
|
-
var
|
|
19278
|
-
var
|
|
19319
|
+
var path74 = __require("path");
|
|
19320
|
+
var fs66 = __require("fs");
|
|
19279
19321
|
var _0777 = parseInt("0777", 8);
|
|
19280
19322
|
module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP;
|
|
19281
19323
|
function mkdirP(p, opts, f, made) {
|
|
@@ -19286,14 +19328,14 @@ var require_mkdirp_classic = __commonJS({
|
|
|
19286
19328
|
opts = { mode: opts };
|
|
19287
19329
|
}
|
|
19288
19330
|
var mode = opts.mode;
|
|
19289
|
-
var xfs = opts.fs ||
|
|
19331
|
+
var xfs = opts.fs || fs66;
|
|
19290
19332
|
if (mode === void 0) {
|
|
19291
19333
|
mode = _0777 & ~process.umask();
|
|
19292
19334
|
}
|
|
19293
19335
|
if (!made) made = null;
|
|
19294
19336
|
var cb = f || function() {
|
|
19295
19337
|
};
|
|
19296
|
-
p =
|
|
19338
|
+
p = path74.resolve(p);
|
|
19297
19339
|
xfs.mkdir(p, mode, function(er) {
|
|
19298
19340
|
if (!er) {
|
|
19299
19341
|
made = made || p;
|
|
@@ -19301,7 +19343,7 @@ var require_mkdirp_classic = __commonJS({
|
|
|
19301
19343
|
}
|
|
19302
19344
|
switch (er.code) {
|
|
19303
19345
|
case "ENOENT":
|
|
19304
|
-
mkdirP(
|
|
19346
|
+
mkdirP(path74.dirname(p), opts, function(er2, made2) {
|
|
19305
19347
|
if (er2) cb(er2, made2);
|
|
19306
19348
|
else mkdirP(p, opts, cb, made2);
|
|
19307
19349
|
});
|
|
@@ -19323,19 +19365,19 @@ var require_mkdirp_classic = __commonJS({
|
|
|
19323
19365
|
opts = { mode: opts };
|
|
19324
19366
|
}
|
|
19325
19367
|
var mode = opts.mode;
|
|
19326
|
-
var xfs = opts.fs ||
|
|
19368
|
+
var xfs = opts.fs || fs66;
|
|
19327
19369
|
if (mode === void 0) {
|
|
19328
19370
|
mode = _0777 & ~process.umask();
|
|
19329
19371
|
}
|
|
19330
19372
|
if (!made) made = null;
|
|
19331
|
-
p =
|
|
19373
|
+
p = path74.resolve(p);
|
|
19332
19374
|
try {
|
|
19333
19375
|
xfs.mkdirSync(p, mode);
|
|
19334
19376
|
made = made || p;
|
|
19335
19377
|
} catch (err0) {
|
|
19336
19378
|
switch (err0.code) {
|
|
19337
19379
|
case "ENOENT":
|
|
19338
|
-
made = sync(
|
|
19380
|
+
made = sync(path74.dirname(p), opts, made);
|
|
19339
19381
|
sync(p, opts, made);
|
|
19340
19382
|
break;
|
|
19341
19383
|
// In the case of any other error, just see if there's a dir
|
|
@@ -19365,8 +19407,8 @@ var require_tar_fs = __commonJS({
|
|
|
19365
19407
|
var tar2 = require_tar_stream();
|
|
19366
19408
|
var pump = require_pump();
|
|
19367
19409
|
var mkdirp = require_mkdirp_classic();
|
|
19368
|
-
var
|
|
19369
|
-
var
|
|
19410
|
+
var fs66 = __require("fs");
|
|
19411
|
+
var path74 = __require("path");
|
|
19370
19412
|
var os21 = __require("os");
|
|
19371
19413
|
var win32 = os21.platform() === "win32";
|
|
19372
19414
|
var noop = function() {
|
|
@@ -19377,23 +19419,23 @@ var require_tar_fs = __commonJS({
|
|
|
19377
19419
|
var normalize = !win32 ? echo : function(name) {
|
|
19378
19420
|
return name.replace(/\\/g, "/").replace(/[:?<>|]/g, "_");
|
|
19379
19421
|
};
|
|
19380
|
-
var statAll = function(
|
|
19422
|
+
var statAll = function(fs67, stat, cwd, ignore, entries, sort) {
|
|
19381
19423
|
var queue = entries || ["."];
|
|
19382
19424
|
for (var i = 0; i < queue.length; i++) {
|
|
19383
|
-
if (!inCwd(
|
|
19425
|
+
if (!inCwd(path74.resolve(cwd, queue[i]), cwd)) throw new Error(queue[i] + " is not a valid path");
|
|
19384
19426
|
}
|
|
19385
19427
|
return function loop(callback) {
|
|
19386
19428
|
if (!queue.length) return callback();
|
|
19387
19429
|
var next = queue.shift();
|
|
19388
|
-
var nextAbs =
|
|
19389
|
-
stat.call(
|
|
19430
|
+
var nextAbs = path74.join(cwd, next);
|
|
19431
|
+
stat.call(fs67, nextAbs, function(err, stat2) {
|
|
19390
19432
|
if (err) return callback(err);
|
|
19391
19433
|
if (!stat2.isDirectory()) return callback(null, next, stat2);
|
|
19392
|
-
|
|
19434
|
+
fs67.readdir(nextAbs, function(err2, files) {
|
|
19393
19435
|
if (err2) return callback(err2);
|
|
19394
19436
|
if (sort) files.sort();
|
|
19395
19437
|
for (var i2 = 0; i2 < files.length; i2++) {
|
|
19396
|
-
if (!ignore(
|
|
19438
|
+
if (!ignore(path74.join(cwd, next, files[i2]))) queue.push(path74.join(next, files[i2]));
|
|
19397
19439
|
}
|
|
19398
19440
|
callback(null, next, stat2);
|
|
19399
19441
|
});
|
|
@@ -19404,7 +19446,7 @@ var require_tar_fs = __commonJS({
|
|
|
19404
19446
|
return function(header) {
|
|
19405
19447
|
header.name = header.name.split("/").slice(level).join("/");
|
|
19406
19448
|
var linkname = header.linkname;
|
|
19407
|
-
if (linkname && (header.type === "link" ||
|
|
19449
|
+
if (linkname && (header.type === "link" || path74.isAbsolute(linkname))) {
|
|
19408
19450
|
header.linkname = linkname.split("/").slice(level).join("/");
|
|
19409
19451
|
}
|
|
19410
19452
|
return map(header);
|
|
@@ -19413,7 +19455,7 @@ var require_tar_fs = __commonJS({
|
|
|
19413
19455
|
exports.pack = function(cwd, opts) {
|
|
19414
19456
|
if (!cwd) cwd = ".";
|
|
19415
19457
|
if (!opts) opts = {};
|
|
19416
|
-
var xfs = opts.fs ||
|
|
19458
|
+
var xfs = opts.fs || fs66;
|
|
19417
19459
|
var ignore = opts.ignore || opts.filter || noop;
|
|
19418
19460
|
var map = opts.map || noop;
|
|
19419
19461
|
var mapStream = opts.mapStream || echo;
|
|
@@ -19434,7 +19476,7 @@ var require_tar_fs = __commonJS({
|
|
|
19434
19476
|
fmode |= parseInt(222, 8);
|
|
19435
19477
|
}
|
|
19436
19478
|
var onsymlink = function(filename, header) {
|
|
19437
|
-
xfs.readlink(
|
|
19479
|
+
xfs.readlink(path74.join(cwd, filename), function(err, linkname) {
|
|
19438
19480
|
if (err) return pack2.destroy(err);
|
|
19439
19481
|
header.linkname = normalize(linkname);
|
|
19440
19482
|
pack2.entry(header, onnextentry);
|
|
@@ -19475,7 +19517,7 @@ var require_tar_fs = __commonJS({
|
|
|
19475
19517
|
}
|
|
19476
19518
|
var entry = pack2.entry(header, onnextentry);
|
|
19477
19519
|
if (!entry) return;
|
|
19478
|
-
var rs = mapStream(xfs.createReadStream(
|
|
19520
|
+
var rs = mapStream(xfs.createReadStream(path74.join(cwd, filename), { start: 0, end: header.size > 0 ? header.size - 1 : header.size }), header);
|
|
19479
19521
|
rs.on("error", function(err2) {
|
|
19480
19522
|
entry.destroy(err2);
|
|
19481
19523
|
});
|
|
@@ -19500,7 +19542,7 @@ var require_tar_fs = __commonJS({
|
|
|
19500
19542
|
exports.extract = function(cwd, opts) {
|
|
19501
19543
|
if (!cwd) cwd = ".";
|
|
19502
19544
|
if (!opts) opts = {};
|
|
19503
|
-
var xfs = opts.fs ||
|
|
19545
|
+
var xfs = opts.fs || fs66;
|
|
19504
19546
|
var ignore = opts.ignore || opts.filter || noop;
|
|
19505
19547
|
var map = opts.map || noop;
|
|
19506
19548
|
var mapStream = opts.mapStream || echo;
|
|
@@ -19553,14 +19595,14 @@ var require_tar_fs = __commonJS({
|
|
|
19553
19595
|
extract.on("entry", function(header, stream, next) {
|
|
19554
19596
|
header = map(header) || header;
|
|
19555
19597
|
header.name = normalize(header.name);
|
|
19556
|
-
var name =
|
|
19598
|
+
var name = path74.join(cwd, path74.join("/", header.name));
|
|
19557
19599
|
if (ignore(name, header)) {
|
|
19558
19600
|
stream.resume();
|
|
19559
19601
|
return next();
|
|
19560
19602
|
}
|
|
19561
19603
|
var stat = function(err) {
|
|
19562
19604
|
if (err) return next(err);
|
|
19563
|
-
if (
|
|
19605
|
+
if (path74.join(name, ".") === path74.join(cwd, ".")) return next();
|
|
19564
19606
|
utimes(name, header, function(err2) {
|
|
19565
19607
|
if (err2) return next(err2);
|
|
19566
19608
|
if (win32) return next();
|
|
@@ -19570,9 +19612,9 @@ var require_tar_fs = __commonJS({
|
|
|
19570
19612
|
var onsymlink = function() {
|
|
19571
19613
|
if (win32) return next();
|
|
19572
19614
|
xfs.unlink(name, function() {
|
|
19573
|
-
var dst =
|
|
19615
|
+
var dst = path74.resolve(path74.dirname(name), header.linkname);
|
|
19574
19616
|
if (!inCwd(dst, cwd)) return next(new Error(name + " is not a valid symlink"));
|
|
19575
|
-
validateNotSymlink(xfs, dst,
|
|
19617
|
+
validateNotSymlink(xfs, dst, path74.resolve(cwd), function(err, valid) {
|
|
19576
19618
|
if (err) return next(err);
|
|
19577
19619
|
if (!valid) return next(new Error(name + " is not a valid symlink"));
|
|
19578
19620
|
xfs.symlink(header.linkname, name, stat);
|
|
@@ -19582,7 +19624,7 @@ var require_tar_fs = __commonJS({
|
|
|
19582
19624
|
var onlink = function() {
|
|
19583
19625
|
if (win32) return next();
|
|
19584
19626
|
xfs.unlink(name, function() {
|
|
19585
|
-
var srcpath =
|
|
19627
|
+
var srcpath = path74.join(cwd, path74.join("/", header.linkname));
|
|
19586
19628
|
xfs.realpath(srcpath, function(err, dst) {
|
|
19587
19629
|
if (err || !inCwd(dst, cwd)) return next(new Error(name + " is not a valid hardlink"));
|
|
19588
19630
|
xfs.link(dst, name, function(err2) {
|
|
@@ -19622,8 +19664,8 @@ var require_tar_fs = __commonJS({
|
|
|
19622
19664
|
gid: header.gid
|
|
19623
19665
|
}, stat);
|
|
19624
19666
|
}
|
|
19625
|
-
var dir =
|
|
19626
|
-
validate(xfs, dir,
|
|
19667
|
+
var dir = path74.dirname(name);
|
|
19668
|
+
validate(xfs, dir, path74.join(cwd, "."), function(err, valid) {
|
|
19627
19669
|
if (err) return next(err);
|
|
19628
19670
|
if (!valid) return next(new Error(dir + " is not a valid path"));
|
|
19629
19671
|
mkdirfix(dir, {
|
|
@@ -19650,20 +19692,20 @@ var require_tar_fs = __commonJS({
|
|
|
19650
19692
|
if (opts.finish) extract.on("finish", opts.finish);
|
|
19651
19693
|
return extract;
|
|
19652
19694
|
};
|
|
19653
|
-
function validate(
|
|
19695
|
+
function validate(fs67, name, root, cb) {
|
|
19654
19696
|
if (name === root) return cb(null, true);
|
|
19655
|
-
|
|
19697
|
+
fs67.lstat(name, function(err, st) {
|
|
19656
19698
|
if (err && err.code !== "ENOENT") return cb(err);
|
|
19657
|
-
if (err || st.isDirectory()) return validate(
|
|
19699
|
+
if (err || st.isDirectory()) return validate(fs67, path74.join(name, ".."), root, cb);
|
|
19658
19700
|
cb(null, false);
|
|
19659
19701
|
});
|
|
19660
19702
|
}
|
|
19661
|
-
function validateNotSymlink(
|
|
19703
|
+
function validateNotSymlink(fs67, name, root, cb) {
|
|
19662
19704
|
if (name === root) return cb(null, true);
|
|
19663
|
-
if (!name.startsWith(root +
|
|
19664
|
-
|
|
19705
|
+
if (!name.startsWith(root + path74.sep)) return cb(null, false);
|
|
19706
|
+
fs67.lstat(name, function(err, st) {
|
|
19665
19707
|
if (err && err.code !== "ENOENT" && err.code !== "EPERM") return cb(err);
|
|
19666
|
-
if (err || !st.isSymbolicLink()) return validateNotSymlink(
|
|
19708
|
+
if (err || !st.isSymbolicLink()) return validateNotSymlink(fs67, path74.join(name, ".."), root, cb);
|
|
19667
19709
|
cb(null, false);
|
|
19668
19710
|
});
|
|
19669
19711
|
}
|
|
@@ -19677,8 +19719,8 @@ var require_tar_fs = __commonJS({
|
|
|
19677
19719
|
});
|
|
19678
19720
|
}
|
|
19679
19721
|
function inCwd(dst, cwd) {
|
|
19680
|
-
cwd =
|
|
19681
|
-
return cwd === dst || dst.startsWith(cwd +
|
|
19722
|
+
cwd = path74.resolve(cwd);
|
|
19723
|
+
return cwd === dst || dst.startsWith(cwd + path74.sep);
|
|
19682
19724
|
}
|
|
19683
19725
|
}
|
|
19684
19726
|
});
|
|
@@ -22548,11 +22590,11 @@ var require_lib = __commonJS({
|
|
|
22548
22590
|
function lookupEscape(ch) {
|
|
22549
22591
|
return escapeMap[ch];
|
|
22550
22592
|
}
|
|
22551
|
-
function _prettifyError(
|
|
22593
|
+
function _prettifyError(path74, withInternals, err) {
|
|
22552
22594
|
if (!err.Update) {
|
|
22553
22595
|
err = new _exports.TemplateError(err);
|
|
22554
22596
|
}
|
|
22555
|
-
err.Update(
|
|
22597
|
+
err.Update(path74);
|
|
22556
22598
|
if (!withInternals) {
|
|
22557
22599
|
var old = err;
|
|
22558
22600
|
err = new Error(old.message);
|
|
@@ -22613,8 +22655,8 @@ var require_lib = __commonJS({
|
|
|
22613
22655
|
err.lineno = lineno;
|
|
22614
22656
|
err.colno = colno;
|
|
22615
22657
|
err.firstUpdate = true;
|
|
22616
|
-
err.Update = function Update(
|
|
22617
|
-
var msg = "(" + (
|
|
22658
|
+
err.Update = function Update(path74) {
|
|
22659
|
+
var msg = "(" + (path74 || "unknown path") + ")";
|
|
22618
22660
|
if (this.firstUpdate) {
|
|
22619
22661
|
if (this.lineno && this.colno) {
|
|
22620
22662
|
msg += " [Line " + this.lineno + ", Column " + this.colno + "]";
|
|
@@ -26464,7 +26506,7 @@ var require_filters = __commonJS({
|
|
|
26464
26506
|
return r.copySafeness(str3, res);
|
|
26465
26507
|
}
|
|
26466
26508
|
_exports.indent = indent;
|
|
26467
|
-
function
|
|
26509
|
+
function join53(arr, del, attr) {
|
|
26468
26510
|
del = del || "";
|
|
26469
26511
|
if (attr) {
|
|
26470
26512
|
arr = lib.map(arr, function(v) {
|
|
@@ -26473,7 +26515,7 @@ var require_filters = __commonJS({
|
|
|
26473
26515
|
}
|
|
26474
26516
|
return arr.join(del);
|
|
26475
26517
|
}
|
|
26476
|
-
_exports.join =
|
|
26518
|
+
_exports.join = join53;
|
|
26477
26519
|
function last(arr) {
|
|
26478
26520
|
return arr[arr.length - 1];
|
|
26479
26521
|
}
|
|
@@ -26826,7 +26868,7 @@ var require_loader = __commonJS({
|
|
|
26826
26868
|
};
|
|
26827
26869
|
return _setPrototypeOf(o, p);
|
|
26828
26870
|
}
|
|
26829
|
-
var
|
|
26871
|
+
var path74 = __require("path");
|
|
26830
26872
|
var _require = require_object();
|
|
26831
26873
|
var EmitterObj = _require.EmitterObj;
|
|
26832
26874
|
module.exports = /* @__PURE__ */ function(_EmitterObj) {
|
|
@@ -26836,7 +26878,7 @@ var require_loader = __commonJS({
|
|
|
26836
26878
|
}
|
|
26837
26879
|
var _proto = Loader.prototype;
|
|
26838
26880
|
_proto.resolve = function resolve13(from, to) {
|
|
26839
|
-
return
|
|
26881
|
+
return path74.resolve(path74.dirname(from), to);
|
|
26840
26882
|
};
|
|
26841
26883
|
_proto.isRelative = function isRelative(filename) {
|
|
26842
26884
|
return filename.indexOf("./") === 0 || filename.indexOf("../") === 0;
|
|
@@ -26910,8 +26952,8 @@ var require_node_loaders = __commonJS({
|
|
|
26910
26952
|
};
|
|
26911
26953
|
return _setPrototypeOf(o, p);
|
|
26912
26954
|
}
|
|
26913
|
-
var
|
|
26914
|
-
var
|
|
26955
|
+
var fs66 = __require("fs");
|
|
26956
|
+
var path74 = __require("path");
|
|
26915
26957
|
var Loader = require_loader();
|
|
26916
26958
|
var _require = require_precompiled_loader();
|
|
26917
26959
|
var PrecompiledLoader = _require.PrecompiledLoader;
|
|
@@ -26929,7 +26971,7 @@ var require_node_loaders = __commonJS({
|
|
|
26929
26971
|
_this.noCache = !!opts.noCache;
|
|
26930
26972
|
if (searchPaths) {
|
|
26931
26973
|
searchPaths = Array.isArray(searchPaths) ? searchPaths : [searchPaths];
|
|
26932
|
-
_this.searchPaths = searchPaths.map(
|
|
26974
|
+
_this.searchPaths = searchPaths.map(path74.normalize);
|
|
26933
26975
|
} else {
|
|
26934
26976
|
_this.searchPaths = ["."];
|
|
26935
26977
|
}
|
|
@@ -26939,10 +26981,10 @@ var require_node_loaders = __commonJS({
|
|
|
26939
26981
|
} catch (e) {
|
|
26940
26982
|
throw new Error("watch requires chokidar to be installed");
|
|
26941
26983
|
}
|
|
26942
|
-
var paths = _this.searchPaths.filter(
|
|
26984
|
+
var paths = _this.searchPaths.filter(fs66.existsSync);
|
|
26943
26985
|
var watcher = chokidar.watch(paths);
|
|
26944
26986
|
watcher.on("all", function(event, fullname) {
|
|
26945
|
-
fullname =
|
|
26987
|
+
fullname = path74.resolve(fullname);
|
|
26946
26988
|
if (event === "change" && fullname in _this.pathsToNames) {
|
|
26947
26989
|
_this.emit("update", _this.pathsToNames[fullname], fullname);
|
|
26948
26990
|
}
|
|
@@ -26958,9 +27000,9 @@ var require_node_loaders = __commonJS({
|
|
|
26958
27000
|
var fullpath = null;
|
|
26959
27001
|
var paths = this.searchPaths;
|
|
26960
27002
|
for (var i = 0; i < paths.length; i++) {
|
|
26961
|
-
var basePath =
|
|
26962
|
-
var p =
|
|
26963
|
-
if (p.indexOf(basePath) === 0 &&
|
|
27003
|
+
var basePath = path74.resolve(paths[i]);
|
|
27004
|
+
var p = path74.resolve(paths[i], name);
|
|
27005
|
+
if (p.indexOf(basePath) === 0 && fs66.existsSync(p)) {
|
|
26964
27006
|
fullpath = p;
|
|
26965
27007
|
break;
|
|
26966
27008
|
}
|
|
@@ -26970,7 +27012,7 @@ var require_node_loaders = __commonJS({
|
|
|
26970
27012
|
}
|
|
26971
27013
|
this.pathsToNames[fullpath] = name;
|
|
26972
27014
|
var source = {
|
|
26973
|
-
src:
|
|
27015
|
+
src: fs66.readFileSync(fullpath, "utf-8"),
|
|
26974
27016
|
path: fullpath,
|
|
26975
27017
|
noCache: this.noCache
|
|
26976
27018
|
};
|
|
@@ -27022,7 +27064,7 @@ var require_node_loaders = __commonJS({
|
|
|
27022
27064
|
}
|
|
27023
27065
|
this.pathsToNames[fullpath] = name;
|
|
27024
27066
|
var source = {
|
|
27025
|
-
src:
|
|
27067
|
+
src: fs66.readFileSync(fullpath, "utf-8"),
|
|
27026
27068
|
path: fullpath,
|
|
27027
27069
|
noCache: this.noCache
|
|
27028
27070
|
};
|
|
@@ -27229,13 +27271,13 @@ var require_express_app = __commonJS({
|
|
|
27229
27271
|
"../../node_modules/nunjucks/src/express-app.js"(exports, module) {
|
|
27230
27272
|
"use strict";
|
|
27231
27273
|
init_define_SANDBOX_BUILD();
|
|
27232
|
-
var
|
|
27274
|
+
var path74 = __require("path");
|
|
27233
27275
|
module.exports = function express(env, app) {
|
|
27234
27276
|
function NunjucksView(name, opts) {
|
|
27235
27277
|
this.name = name;
|
|
27236
27278
|
this.path = name;
|
|
27237
27279
|
this.defaultEngine = opts.defaultEngine;
|
|
27238
|
-
this.ext =
|
|
27280
|
+
this.ext = path74.extname(name);
|
|
27239
27281
|
if (!this.ext && !this.defaultEngine) {
|
|
27240
27282
|
throw new Error("No default engine was specified and no extension was provided.");
|
|
27241
27283
|
}
|
|
@@ -27616,7 +27658,7 @@ var require_environment = __commonJS({
|
|
|
27616
27658
|
return _Obj2.apply(this, arguments) || this;
|
|
27617
27659
|
}
|
|
27618
27660
|
var _proto3 = Template2.prototype;
|
|
27619
|
-
_proto3.init = function init3(src, env,
|
|
27661
|
+
_proto3.init = function init3(src, env, path74, eagerCompile) {
|
|
27620
27662
|
this.env = env || new Environment();
|
|
27621
27663
|
if (lib.isObject(src)) {
|
|
27622
27664
|
switch (src.type) {
|
|
@@ -27634,7 +27676,7 @@ var require_environment = __commonJS({
|
|
|
27634
27676
|
} else {
|
|
27635
27677
|
throw new Error("src must be a string or an object describing the source");
|
|
27636
27678
|
}
|
|
27637
|
-
this.path =
|
|
27679
|
+
this.path = path74;
|
|
27638
27680
|
if (eagerCompile) {
|
|
27639
27681
|
try {
|
|
27640
27682
|
this._compile();
|
|
@@ -27786,8 +27828,8 @@ var require_precompile = __commonJS({
|
|
|
27786
27828
|
"../../node_modules/nunjucks/src/precompile.js"(exports, module) {
|
|
27787
27829
|
"use strict";
|
|
27788
27830
|
init_define_SANDBOX_BUILD();
|
|
27789
|
-
var
|
|
27790
|
-
var
|
|
27831
|
+
var fs66 = __require("fs");
|
|
27832
|
+
var path74 = __require("path");
|
|
27791
27833
|
var _require = require_lib();
|
|
27792
27834
|
var _prettifyError = _require._prettifyError;
|
|
27793
27835
|
var compiler = require_compiler();
|
|
@@ -27819,14 +27861,14 @@ var require_precompile = __commonJS({
|
|
|
27819
27861
|
if (opts.isString) {
|
|
27820
27862
|
return precompileString(input2, opts);
|
|
27821
27863
|
}
|
|
27822
|
-
var pathStats =
|
|
27864
|
+
var pathStats = fs66.existsSync(input2) && fs66.statSync(input2);
|
|
27823
27865
|
var precompiled = [];
|
|
27824
27866
|
var templates = [];
|
|
27825
27867
|
function addTemplates(dir) {
|
|
27826
|
-
|
|
27827
|
-
var filepath =
|
|
27828
|
-
var subpath = filepath.substr(
|
|
27829
|
-
var stat =
|
|
27868
|
+
fs66.readdirSync(dir).forEach(function(file) {
|
|
27869
|
+
var filepath = path74.join(dir, file);
|
|
27870
|
+
var subpath = filepath.substr(path74.join(input2, "/").length);
|
|
27871
|
+
var stat = fs66.statSync(filepath);
|
|
27830
27872
|
if (stat && stat.isDirectory()) {
|
|
27831
27873
|
subpath += "/";
|
|
27832
27874
|
if (!match(subpath, opts.exclude)) {
|
|
@@ -27838,13 +27880,13 @@ var require_precompile = __commonJS({
|
|
|
27838
27880
|
});
|
|
27839
27881
|
}
|
|
27840
27882
|
if (pathStats.isFile()) {
|
|
27841
|
-
precompiled.push(_precompile(
|
|
27883
|
+
precompiled.push(_precompile(fs66.readFileSync(input2, "utf-8"), opts.name || input2, env));
|
|
27842
27884
|
} else if (pathStats.isDirectory()) {
|
|
27843
27885
|
addTemplates(input2);
|
|
27844
27886
|
for (var i = 0; i < templates.length; i++) {
|
|
27845
|
-
var name = templates[i].replace(
|
|
27887
|
+
var name = templates[i].replace(path74.join(input2, "/"), "");
|
|
27846
27888
|
try {
|
|
27847
|
-
precompiled.push(_precompile(
|
|
27889
|
+
precompiled.push(_precompile(fs66.readFileSync(templates[i], "utf-8"), name, env));
|
|
27848
27890
|
} catch (e) {
|
|
27849
27891
|
if (opts.force) {
|
|
27850
27892
|
console.error(e);
|
|
@@ -28223,11 +28265,11 @@ var require_nunjucks = __commonJS({
|
|
|
28223
28265
|
reset: function reset() {
|
|
28224
28266
|
e = void 0;
|
|
28225
28267
|
},
|
|
28226
|
-
compile: function compile(src, env,
|
|
28268
|
+
compile: function compile(src, env, path74, eagerCompile) {
|
|
28227
28269
|
if (!e) {
|
|
28228
28270
|
configure();
|
|
28229
28271
|
}
|
|
28230
|
-
return new Template(src, env,
|
|
28272
|
+
return new Template(src, env, path74, eagerCompile);
|
|
28231
28273
|
},
|
|
28232
28274
|
render: function render2(name, ctx, cb) {
|
|
28233
28275
|
if (!e) {
|
|
@@ -28279,12 +28321,12 @@ function resolveSecrets(all, servicesConfig, serviceNames, overrides = {}) {
|
|
|
28279
28321
|
function getSecretsPath() {
|
|
28280
28322
|
return secretsPath();
|
|
28281
28323
|
}
|
|
28282
|
-
function loadSecrets(
|
|
28283
|
-
if (!(fs26.existsSync(
|
|
28324
|
+
function loadSecrets(path74) {
|
|
28325
|
+
if (!(fs26.existsSync(path74) && fs26.statSync(path74).isFile())) {
|
|
28284
28326
|
return {};
|
|
28285
28327
|
}
|
|
28286
28328
|
const result = {};
|
|
28287
|
-
for (const rawLine of fs26.readFileSync(
|
|
28329
|
+
for (const rawLine of fs26.readFileSync(path74, "utf8").split("\n")) {
|
|
28288
28330
|
const line = rawLine.trim();
|
|
28289
28331
|
if (!line || line.startsWith("#") || !line.includes("=")) {
|
|
28290
28332
|
continue;
|
|
@@ -28575,6 +28617,7 @@ function getTemplateOrNull(env, name) {
|
|
|
28575
28617
|
function composeHeader(feature2, source) {
|
|
28576
28618
|
return `# Sandbox docker-compose for feature: ${feature2}
|
|
28577
28619
|
# Generated by: ${source}
|
|
28620
|
+
# Regenerated by \`sandbox up\` whenever stale; do not hand-edit.
|
|
28578
28621
|
#
|
|
28579
28622
|
# Run from workspace root:
|
|
28580
28623
|
# docker compose up -d --build (start)
|
|
@@ -28742,6 +28785,7 @@ function renderFeatureCompose(feature2, slot, localServices, config, workspaceOv
|
|
|
28742
28785
|
base_port: portStart,
|
|
28743
28786
|
local_services: svcObjects,
|
|
28744
28787
|
services_config: servicesConfig,
|
|
28788
|
+
idle_types: IDLE_SERVICE_TYPES,
|
|
28745
28789
|
setup_configs: setupConfigs,
|
|
28746
28790
|
ports_config: portsConfig,
|
|
28747
28791
|
workspace_path: workspaceRoot,
|
|
@@ -29298,9 +29342,9 @@ async function generateWorkspaceCompose(feature2, servicesStr, workspaceRoot, te
|
|
|
29298
29342
|
continue;
|
|
29299
29343
|
}
|
|
29300
29344
|
const stripped = (svcDef["path"] ?? "").replace(/\/+$/, "");
|
|
29301
|
-
const
|
|
29302
|
-
if (
|
|
29303
|
-
pathAliases[
|
|
29345
|
+
const basename27 = stripped.includes("/") ? stripped.slice(stripped.lastIndexOf("/") + 1) : stripped;
|
|
29346
|
+
if (basename27 !== svcName) {
|
|
29347
|
+
pathAliases[basename27] = svcName;
|
|
29304
29348
|
}
|
|
29305
29349
|
}
|
|
29306
29350
|
const resolve13 = (name) => {
|
|
@@ -29482,6 +29526,18 @@ function refreshKubeconfig() {
|
|
|
29482
29526
|
}
|
|
29483
29527
|
return fs29.existsSync(kubeconfig) && fs29.statSync(kubeconfig).isFile();
|
|
29484
29528
|
}
|
|
29529
|
+
function clusterReachable() {
|
|
29530
|
+
try {
|
|
29531
|
+
return run(["kubectl", "--request-timeout=3s", "get", "--raw", "/readyz"], {
|
|
29532
|
+
capture: true,
|
|
29533
|
+
timeout: 20
|
|
29534
|
+
}).status === 0;
|
|
29535
|
+
} catch (e) {
|
|
29536
|
+
if (e instanceof ProcError)
|
|
29537
|
+
return false;
|
|
29538
|
+
throw e;
|
|
29539
|
+
}
|
|
29540
|
+
}
|
|
29485
29541
|
function ensurePortForwardsHealthy() {
|
|
29486
29542
|
const health = checkPortForwardHealth();
|
|
29487
29543
|
if (health["k8s-forwards"] ?? false) {
|
|
@@ -29493,6 +29549,10 @@ function ensurePortForwardsHealthy() {
|
|
|
29493
29549
|
error("Not restarting k8s-forwards without a kubeconfig file to mount");
|
|
29494
29550
|
return "failed";
|
|
29495
29551
|
}
|
|
29552
|
+
if (!clusterReachable()) {
|
|
29553
|
+
warn("No reachable k8s cluster \u2014 skipping port-forwards; services not run locally are unavailable");
|
|
29554
|
+
return "failed";
|
|
29555
|
+
}
|
|
29496
29556
|
const { argv, env } = composeInvocation();
|
|
29497
29557
|
try {
|
|
29498
29558
|
const result = run([...argv, "up", "-d", "--force-recreate", "k8s-forwards"], {
|
|
@@ -30070,7 +30130,8 @@ async function up(feature2, workspace, { signal } = {}) {
|
|
|
30070
30130
|
generateFeatureEnv(feature2, slot, localServices, config, workspace);
|
|
30071
30131
|
} else if (isFeatureComposeStale(feature2, slot, localServices, config, workspace)) {
|
|
30072
30132
|
staleCompose = true;
|
|
30073
|
-
warn(`${COMPOSE_FILE} predates the current
|
|
30133
|
+
warn(`${COMPOSE_FILE} predates the current template or registry \u2014 regenerating...`);
|
|
30134
|
+
generateFeatureCompose(feature2, slot, localServices, config, workspace, "sandbox up");
|
|
30074
30135
|
}
|
|
30075
30136
|
generateSupergraphConfig(feature2, slot, localServices, config, workspace);
|
|
30076
30137
|
raw(`
|
|
@@ -30169,27 +30230,119 @@ function refuseInsideOwnContainer(feature2, verb) {
|
|
|
30169
30230
|
Run it from the host: cd to the workspace and run 'sandbox ${verb}'.`, "INSIDE_OWN_CONTAINER");
|
|
30170
30231
|
}
|
|
30171
30232
|
}
|
|
30172
|
-
function
|
|
30233
|
+
function liveSessions(workspaceRoot, sessionsDir = path30.join(os8.homedir(), ".claude", "sessions")) {
|
|
30173
30234
|
let names;
|
|
30174
30235
|
try {
|
|
30175
30236
|
names = fs31.readdirSync(sessionsDir).filter((n) => n.endsWith(".json"));
|
|
30176
30237
|
} catch {
|
|
30177
30238
|
return [];
|
|
30178
30239
|
}
|
|
30179
|
-
const
|
|
30240
|
+
const sessions = [];
|
|
30180
30241
|
for (const name of names) {
|
|
30181
30242
|
try {
|
|
30182
30243
|
const s = JSON.parse(fs31.readFileSync(path30.join(sessionsDir, name), "utf8"));
|
|
30183
30244
|
const cwd = String(s.cwd ?? "");
|
|
30184
30245
|
const inWorkspace = cwd === workspaceRoot || cwd.startsWith(workspaceRoot + path30.sep);
|
|
30185
|
-
if (!inWorkspace ||
|
|
30246
|
+
if (!inWorkspace || !Number.isInteger(s.pid))
|
|
30186
30247
|
continue;
|
|
30187
30248
|
process.kill(s.pid, 0);
|
|
30188
|
-
|
|
30249
|
+
sessions.push({
|
|
30250
|
+
pid: s.pid,
|
|
30251
|
+
sessionId: typeof s.sessionId === "string" ? s.sessionId : null,
|
|
30252
|
+
status: typeof s.status === "string" ? s.status : null,
|
|
30253
|
+
statusUpdatedAt: Number.isFinite(s.statusUpdatedAt) ? s.statusUpdatedAt : null
|
|
30254
|
+
});
|
|
30189
30255
|
} catch {
|
|
30190
30256
|
}
|
|
30191
30257
|
}
|
|
30192
|
-
return
|
|
30258
|
+
return sessions;
|
|
30259
|
+
}
|
|
30260
|
+
function otherLiveSessions(workspaceRoot, sessionId, sessionsDir) {
|
|
30261
|
+
return liveSessions(workspaceRoot, sessionsDir).filter((s) => s.sessionId !== sessionId).map((s) => s.pid);
|
|
30262
|
+
}
|
|
30263
|
+
function busyReason(f, input2, cutoff) {
|
|
30264
|
+
const active = input2.sessions(f.workspace).some((s) => s.status !== "idle" || (s.statusUpdatedAt ?? Infinity) > cutoff);
|
|
30265
|
+
if (active)
|
|
30266
|
+
return "session active";
|
|
30267
|
+
if (f.startedAt.some((t) => t > cutoff))
|
|
30268
|
+
return `started < ${input2.minutes} min ago`;
|
|
30269
|
+
if ((input2.lastExec(f.workspace) ?? 0) > cutoff)
|
|
30270
|
+
return `gate ran < ${input2.minutes} min ago`;
|
|
30271
|
+
return null;
|
|
30272
|
+
}
|
|
30273
|
+
function idleStopMinutes() {
|
|
30274
|
+
const v = load()["overrides"]?.["idle_stop_minutes"];
|
|
30275
|
+
return Number.isInteger(v) && v >= 0 ? v : 30;
|
|
30276
|
+
}
|
|
30277
|
+
function lastExecMarker(workspaceRoot) {
|
|
30278
|
+
return path30.join(dotDir(workspaceRoot), "last-exec");
|
|
30279
|
+
}
|
|
30280
|
+
function mtimeMs(p) {
|
|
30281
|
+
try {
|
|
30282
|
+
return fs31.statSync(p).mtimeMs;
|
|
30283
|
+
} catch {
|
|
30284
|
+
return null;
|
|
30285
|
+
}
|
|
30286
|
+
}
|
|
30287
|
+
function runningFeatures(runCmd) {
|
|
30288
|
+
try {
|
|
30289
|
+
const ids = runCmd(["docker", "ps", "-q", "--filter", `label=${LABEL_FEATURE2}`]);
|
|
30290
|
+
if (ids.status !== 0)
|
|
30291
|
+
return null;
|
|
30292
|
+
const list2 = ids.stdout.split("\n").filter(Boolean);
|
|
30293
|
+
if (!list2.length)
|
|
30294
|
+
return [];
|
|
30295
|
+
const fmt = `{{index .Config.Labels "${LABEL_FEATURE2}"}} {{index .Config.Labels "${LABEL_WORKDIR}"}} {{.State.StartedAt}}`;
|
|
30296
|
+
const inspect = runCmd(["docker", "inspect", "--format", fmt, ...list2]);
|
|
30297
|
+
if (inspect.status !== 0)
|
|
30298
|
+
return null;
|
|
30299
|
+
const byFeature = /* @__PURE__ */ new Map();
|
|
30300
|
+
for (const line of inspect.stdout.split("\n").filter(Boolean)) {
|
|
30301
|
+
const [feature2, workspace, started] = line.split(" ");
|
|
30302
|
+
if (!feature2 || !workspace)
|
|
30303
|
+
continue;
|
|
30304
|
+
const f = byFeature.get(feature2) ?? { feature: feature2, workspace, startedAt: [] };
|
|
30305
|
+
f.startedAt.push(Date.parse(started));
|
|
30306
|
+
byFeature.set(feature2, f);
|
|
30307
|
+
}
|
|
30308
|
+
return [...byFeature.values()];
|
|
30309
|
+
} catch {
|
|
30310
|
+
return null;
|
|
30311
|
+
}
|
|
30312
|
+
}
|
|
30313
|
+
async function stopIdle(minutes, opts = {}) {
|
|
30314
|
+
const runCmd = opts.run ?? ((cmd) => run(cmd, { capture: true, timeout: 20 }));
|
|
30315
|
+
const found = runningFeatures(runCmd);
|
|
30316
|
+
if (!found)
|
|
30317
|
+
return { ok: true, lines: ["docker unavailable \u2014 nothing reaped"] };
|
|
30318
|
+
const running = found.filter((f) => !opts.only || f.feature === opts.only);
|
|
30319
|
+
if (!running.length)
|
|
30320
|
+
return { ok: true, lines: ["no running feature containers"] };
|
|
30321
|
+
const input2 = {
|
|
30322
|
+
minutes,
|
|
30323
|
+
now: opts.now ?? Date.now(),
|
|
30324
|
+
running,
|
|
30325
|
+
sessions: (ws) => liveSessions(ws, opts.sessionsDir),
|
|
30326
|
+
lastExec: (ws) => mtimeMs(lastExecMarker(ws))
|
|
30327
|
+
};
|
|
30328
|
+
const cutoff = input2.now - minutes * 6e4;
|
|
30329
|
+
const lines = [];
|
|
30330
|
+
let ok = true;
|
|
30331
|
+
for (const f of running) {
|
|
30332
|
+
const why = f.feature === "infra" ? "shared infra" : insideOwnContainer(f.feature) ? "running inside its own container" : busyReason(f, input2, cutoff);
|
|
30333
|
+
if (why) {
|
|
30334
|
+
lines.push(`${f.feature}: kept (${why})`);
|
|
30335
|
+
continue;
|
|
30336
|
+
}
|
|
30337
|
+
try {
|
|
30338
|
+
await (opts.stop ?? stop)(f.feature);
|
|
30339
|
+
lines.push(`${f.feature}: stopped (idle \u2265 ${minutes} min)`);
|
|
30340
|
+
} catch (e) {
|
|
30341
|
+
ok = false;
|
|
30342
|
+
lines.push(`${f.feature}: stop failed \u2014 ${e.message}`);
|
|
30343
|
+
}
|
|
30344
|
+
}
|
|
30345
|
+
return { ok, lines };
|
|
30193
30346
|
}
|
|
30194
30347
|
function idleStopFeature(input2) {
|
|
30195
30348
|
const { feature: feature2, workspaceRoot, sessionId, reason: reason2, entrypoint, sessionsDir } = input2;
|
|
@@ -30514,7 +30667,7 @@ async function teardownNotes(feature2) {
|
|
|
30514
30667
|
graduated
|
|
30515
30668
|
};
|
|
30516
30669
|
}
|
|
30517
|
-
var _CONTINUING_REASONS, REAL_PROXY_OPS;
|
|
30670
|
+
var _CONTINUING_REASONS, LABEL_FEATURE2, LABEL_WORKDIR, REAL_PROXY_OPS;
|
|
30518
30671
|
var init_lifecycle = __esm({
|
|
30519
30672
|
"../../packages/sandbox-core/dist/lifecycle.js"() {
|
|
30520
30673
|
"use strict";
|
|
@@ -30525,17 +30678,22 @@ var init_lifecycle = __esm({
|
|
|
30525
30678
|
init_compose_file();
|
|
30526
30679
|
init_config();
|
|
30527
30680
|
init_docker();
|
|
30681
|
+
init_dot_dir();
|
|
30528
30682
|
init_errors();
|
|
30529
30683
|
init_feature_routing();
|
|
30530
30684
|
init_generate();
|
|
30685
|
+
init_global_config();
|
|
30531
30686
|
init_graduate();
|
|
30532
30687
|
init_k8s();
|
|
30533
30688
|
init_notes();
|
|
30689
|
+
init_proc();
|
|
30534
30690
|
init_reporter();
|
|
30535
30691
|
init_secrets();
|
|
30536
30692
|
init_slots();
|
|
30537
30693
|
init_strategies();
|
|
30538
30694
|
_CONTINUING_REASONS = /* @__PURE__ */ new Set(["clear", "resume"]);
|
|
30695
|
+
LABEL_FEATURE2 = "com.sandbox.local.feature";
|
|
30696
|
+
LABEL_WORKDIR = "com.docker.compose.project.working_dir";
|
|
30539
30697
|
REAL_PROXY_OPS = {
|
|
30540
30698
|
generate: () => generateCaddyfile(),
|
|
30541
30699
|
slots: () => getAllSlots(),
|
|
@@ -31434,6 +31592,7 @@ async function run3(args) {
|
|
|
31434
31592
|
console.log(`
|
|
31435
31593
|
${Colors.BOLD}=== sandbox up: ${feature2} ===${Colors.RESET}
|
|
31436
31594
|
`);
|
|
31595
|
+
if (args.dev) process.env.SANDBOX_DEV = "1";
|
|
31437
31596
|
printSummary(await up(feature2, workspace, { signal: interruptSignal() }));
|
|
31438
31597
|
}
|
|
31439
31598
|
var init_up = __esm({
|
|
@@ -31954,6 +32113,17 @@ async function run8(args) {
|
|
|
31954
32113
|
stopIfIdle();
|
|
31955
32114
|
return;
|
|
31956
32115
|
}
|
|
32116
|
+
if (args.idle != null) {
|
|
32117
|
+
const minutes = Number(args.idle);
|
|
32118
|
+
if (!Number.isInteger(minutes) || minutes < 1) {
|
|
32119
|
+
error("--idle takes whole minutes \u2265 1");
|
|
32120
|
+
process.exit(1);
|
|
32121
|
+
}
|
|
32122
|
+
const result = await stopIdle(minutes, { only: args.feature ?? void 0 });
|
|
32123
|
+
console.log(result.lines.join("\n"));
|
|
32124
|
+
if (!result.ok) process.exit(1);
|
|
32125
|
+
return;
|
|
32126
|
+
}
|
|
31957
32127
|
const dotenv = loadDotenv(process.cwd());
|
|
31958
32128
|
const feature2 = args.feature || dotenv["FEATURE"];
|
|
31959
32129
|
if (!feature2) {
|
|
@@ -32565,16 +32735,19 @@ function discoverFragments(reposRoot) {
|
|
|
32565
32735
|
continue;
|
|
32566
32736
|
if (fs40.statSync(path39.join(reposRoot, repo, ".git"), { throwIfNoEntry: false })?.isFile())
|
|
32567
32737
|
continue;
|
|
32568
|
-
|
|
32569
|
-
try {
|
|
32570
|
-
doc = (0, import_yaml2.parse)(fs40.readFileSync(file, "utf8"));
|
|
32571
|
-
} catch (e) {
|
|
32572
|
-
throw new RegistryError(`${file}: ${e instanceof Error ? e.message : String(e)}`);
|
|
32573
|
-
}
|
|
32574
|
-
out.push({ repo, path: file, doc: isDict7(doc) ? doc : {} });
|
|
32738
|
+
out.push(readFragment(repo, file));
|
|
32575
32739
|
}
|
|
32576
32740
|
return out;
|
|
32577
32741
|
}
|
|
32742
|
+
function readFragment(repo, file) {
|
|
32743
|
+
let doc;
|
|
32744
|
+
try {
|
|
32745
|
+
doc = (0, import_yaml2.parse)(fs40.readFileSync(file, "utf8"));
|
|
32746
|
+
} catch (e) {
|
|
32747
|
+
throw new RegistryError(`${file}: ${e instanceof Error ? e.message : String(e)}`);
|
|
32748
|
+
}
|
|
32749
|
+
return { repo, path: file, doc: isDict7(doc) ? doc : {} };
|
|
32750
|
+
}
|
|
32578
32751
|
function expandPlaceholders(value, paths) {
|
|
32579
32752
|
if (typeof value === "string") {
|
|
32580
32753
|
return value.replace(PLACEHOLDER, (_m, key) => paths[key]);
|
|
@@ -32736,6 +32909,18 @@ function readLocalOverlay(localPath) {
|
|
|
32736
32909
|
throw new RegistryError(`${localPath}: must be a map of registry keys`);
|
|
32737
32910
|
return doc;
|
|
32738
32911
|
}
|
|
32912
|
+
function branchRegistry(repo, worktree, reposRoot = getReposRoot()) {
|
|
32913
|
+
const file = path39.join(worktree, FRAGMENT_REL);
|
|
32914
|
+
if (!fs40.existsSync(file))
|
|
32915
|
+
return null;
|
|
32916
|
+
const fragments = discoverFragments(reposRoot).filter((f) => f.repo !== repo);
|
|
32917
|
+
fragments.push(readFragment(repo, file));
|
|
32918
|
+
return mergeRegistry(fragments, {
|
|
32919
|
+
defaults: REGISTRY_DEFAULTS,
|
|
32920
|
+
local: readLocalOverlay(servicesLocalYamlPath()),
|
|
32921
|
+
paths: placeholderPaths()
|
|
32922
|
+
});
|
|
32923
|
+
}
|
|
32739
32924
|
function syncRegistry(opts = {}) {
|
|
32740
32925
|
const label = "config services.yaml";
|
|
32741
32926
|
const reposRoot = opts.reposRoot ?? getReposRoot();
|
|
@@ -37633,8 +37818,8 @@ var require_utils = __commonJS({
|
|
|
37633
37818
|
}
|
|
37634
37819
|
return ind;
|
|
37635
37820
|
}
|
|
37636
|
-
function removeDotSegments(
|
|
37637
|
-
let input2 =
|
|
37821
|
+
function removeDotSegments(path74) {
|
|
37822
|
+
let input2 = path74;
|
|
37638
37823
|
const output = [];
|
|
37639
37824
|
let nextSlash = -1;
|
|
37640
37825
|
let len = 0;
|
|
@@ -38044,8 +38229,8 @@ var require_schemes = __commonJS({
|
|
|
38044
38229
|
}
|
|
38045
38230
|
if (wsComponent.resourceName) {
|
|
38046
38231
|
const queryIndex = wsComponent.resourceName.indexOf("?");
|
|
38047
|
-
const
|
|
38048
|
-
wsComponent.path =
|
|
38232
|
+
const path74 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
|
|
38233
|
+
wsComponent.path = path74 && path74 !== "/" ? path74 : void 0;
|
|
38049
38234
|
wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
|
|
38050
38235
|
wsComponent.resourceName = void 0;
|
|
38051
38236
|
}
|
|
@@ -49517,10 +49702,10 @@ var require_lib3 = __commonJS({
|
|
|
49517
49702
|
exports.analyse = analyse;
|
|
49518
49703
|
var detectFile = (filepath, opts = {}) => new Promise((resolve13, reject) => {
|
|
49519
49704
|
let fd;
|
|
49520
|
-
const
|
|
49705
|
+
const fs66 = (0, node_1.default)();
|
|
49521
49706
|
const handler = (err, buffer) => {
|
|
49522
49707
|
if (fd) {
|
|
49523
|
-
|
|
49708
|
+
fs66.closeSync(fd);
|
|
49524
49709
|
}
|
|
49525
49710
|
if (err) {
|
|
49526
49711
|
reject(err);
|
|
@@ -49532,9 +49717,9 @@ var require_lib3 = __commonJS({
|
|
|
49532
49717
|
};
|
|
49533
49718
|
const sampleSize = (opts === null || opts === void 0 ? void 0 : opts.sampleSize) || 0;
|
|
49534
49719
|
if (sampleSize > 0) {
|
|
49535
|
-
fd =
|
|
49720
|
+
fd = fs66.openSync(filepath, "r");
|
|
49536
49721
|
let sample = Buffer.allocUnsafe(sampleSize);
|
|
49537
|
-
|
|
49722
|
+
fs66.read(fd, sample, 0, sampleSize, opts.offset, (err, bytesRead) => {
|
|
49538
49723
|
if (err) {
|
|
49539
49724
|
handler(err, null);
|
|
49540
49725
|
} else {
|
|
@@ -49546,22 +49731,22 @@ var require_lib3 = __commonJS({
|
|
|
49546
49731
|
});
|
|
49547
49732
|
return;
|
|
49548
49733
|
}
|
|
49549
|
-
|
|
49734
|
+
fs66.readFile(filepath, handler);
|
|
49550
49735
|
});
|
|
49551
49736
|
exports.detectFile = detectFile;
|
|
49552
49737
|
var detectFileSync = (filepath, opts = {}) => {
|
|
49553
|
-
const
|
|
49738
|
+
const fs66 = (0, node_1.default)();
|
|
49554
49739
|
if (opts && opts.sampleSize) {
|
|
49555
|
-
const fd =
|
|
49740
|
+
const fd = fs66.openSync(filepath, "r");
|
|
49556
49741
|
let sample = Buffer.allocUnsafe(opts.sampleSize);
|
|
49557
|
-
const bytesRead =
|
|
49742
|
+
const bytesRead = fs66.readSync(fd, sample, 0, opts.sampleSize, opts.offset);
|
|
49558
49743
|
if (bytesRead < opts.sampleSize) {
|
|
49559
49744
|
sample = sample.subarray(0, bytesRead);
|
|
49560
49745
|
}
|
|
49561
|
-
|
|
49746
|
+
fs66.closeSync(fd);
|
|
49562
49747
|
return (0, exports.detect)(sample);
|
|
49563
49748
|
}
|
|
49564
|
-
return (0, exports.detect)(
|
|
49749
|
+
return (0, exports.detect)(fs66.readFileSync(filepath));
|
|
49565
49750
|
};
|
|
49566
49751
|
exports.detectFileSync = detectFileSync;
|
|
49567
49752
|
exports.default = {
|
|
@@ -54328,8 +54513,8 @@ function parseRect(size, position) {
|
|
|
54328
54513
|
const [x, y] = (position ?? "60,60").split(",").map(Number);
|
|
54329
54514
|
return { width, height: height2, x, y };
|
|
54330
54515
|
}
|
|
54331
|
-
async function wdFetch(port,
|
|
54332
|
-
const r = await fetch(`http://127.0.0.1:${port}${
|
|
54516
|
+
async function wdFetch(port, path74, method, body) {
|
|
54517
|
+
const r = await fetch(`http://127.0.0.1:${port}${path74}`, {
|
|
54333
54518
|
method,
|
|
54334
54519
|
headers: body !== void 0 ? { "Content-Type": "application/json" } : void 0,
|
|
54335
54520
|
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
@@ -54337,7 +54522,7 @@ async function wdFetch(port, path73, method, body) {
|
|
|
54337
54522
|
const data = await r.json().catch(() => ({}));
|
|
54338
54523
|
if (!r.ok || data?.value?.error) {
|
|
54339
54524
|
const msg = data?.value?.message || data?.value?.error || `HTTP ${r.status}`;
|
|
54340
|
-
throw new Error(`safaridriver ${method} ${
|
|
54525
|
+
throw new Error(`safaridriver ${method} ${path74} failed: ${msg}`);
|
|
54341
54526
|
}
|
|
54342
54527
|
return data.value;
|
|
54343
54528
|
}
|
|
@@ -55175,11 +55360,131 @@ var init_issue_snapshot = __esm({
|
|
|
55175
55360
|
}
|
|
55176
55361
|
});
|
|
55177
55362
|
|
|
55363
|
+
// ../../packages/sandbox-core/dist/issue_status.js
|
|
55364
|
+
import * as fs49 from "node:fs";
|
|
55365
|
+
import * as path57 from "node:path";
|
|
55366
|
+
function resolveIssueStatuses(overrides, tracker) {
|
|
55367
|
+
const map = overrides["issue_statuses"] ?? {};
|
|
55368
|
+
const out = { ...DEFAULTS[tracker] };
|
|
55369
|
+
for (const point of ISSUE_POINTS) {
|
|
55370
|
+
const alias = ALIASES[point];
|
|
55371
|
+
if (Object.hasOwn(map, point))
|
|
55372
|
+
out[point] = blankToNull(map[point]);
|
|
55373
|
+
else if (alias && overrides[alias] !== void 0)
|
|
55374
|
+
out[point] = blankToNull(overrides[alias]);
|
|
55375
|
+
}
|
|
55376
|
+
return out;
|
|
55377
|
+
}
|
|
55378
|
+
function issuePosition(issue, statuses) {
|
|
55379
|
+
const names = new Set([issue.status, ...issue.labels ?? []].flatMap((n) => n ? [n.trim().toLowerCase()] : []));
|
|
55380
|
+
const is = (point) => {
|
|
55381
|
+
const s = statuses[point];
|
|
55382
|
+
return s !== null && names.has(s.toLowerCase());
|
|
55383
|
+
};
|
|
55384
|
+
if (issue.statusCategory === "done" || is("done"))
|
|
55385
|
+
return 4;
|
|
55386
|
+
if (is("merged"))
|
|
55387
|
+
return 3;
|
|
55388
|
+
if (is("in_review"))
|
|
55389
|
+
return 2;
|
|
55390
|
+
if (is("in_progress"))
|
|
55391
|
+
return 1;
|
|
55392
|
+
return issue.statusCategory === "indeterminate" ? 1 : 0;
|
|
55393
|
+
}
|
|
55394
|
+
async function moveIssue(tracker, ref, point, statuses, opts) {
|
|
55395
|
+
const status2 = statuses[point];
|
|
55396
|
+
try {
|
|
55397
|
+
if (status2 === null) {
|
|
55398
|
+
if (!opts.alwaysComment)
|
|
55399
|
+
return "";
|
|
55400
|
+
await tracker.updateIssue(ref, { comment: opts.comment });
|
|
55401
|
+
return `${ref} commented (no ${point} status configured)`;
|
|
55402
|
+
}
|
|
55403
|
+
const issue = await tracker.fetchIssue(ref);
|
|
55404
|
+
if (!issue)
|
|
55405
|
+
return `${ref} not moved to "${status2}": could not read its current status`;
|
|
55406
|
+
const pos = issuePosition(issue, statuses);
|
|
55407
|
+
if (!(RANK[point] > pos || opts.back && pos >= 2 && pos < 4)) {
|
|
55408
|
+
if (opts.alwaysComment)
|
|
55409
|
+
await tracker.updateIssue(ref, { comment: opts.comment });
|
|
55410
|
+
const label = issue.labels?.find((l) => ISSUE_POINTS.some((p) => statuses[p]?.toLowerCase() === l.toLowerCase()));
|
|
55411
|
+
return `${ref} already ${issue.status ?? "past that"}${label ? ` (${label})` : ""}`;
|
|
55412
|
+
}
|
|
55413
|
+
await tracker.updateIssue(ref, { status: status2, comment: opts.comment });
|
|
55414
|
+
return `${ref} \u2192 ${status2}`;
|
|
55415
|
+
} catch (e) {
|
|
55416
|
+
return `${ref} not moved to "${status2 ?? point}": ${e.message}`;
|
|
55417
|
+
}
|
|
55418
|
+
}
|
|
55419
|
+
function currentStage(ws) {
|
|
55420
|
+
let dirs;
|
|
55421
|
+
try {
|
|
55422
|
+
dirs = fs49.readdirSync(path57.join(ws, "stages")).filter((d) => /^\d+_/.test(d)).sort();
|
|
55423
|
+
} catch {
|
|
55424
|
+
return "research phase";
|
|
55425
|
+
}
|
|
55426
|
+
const empty = dirs.find((d) => {
|
|
55427
|
+
try {
|
|
55428
|
+
return !fs49.readdirSync(path57.join(ws, "stages", d, "output")).some((f) => !f.startsWith("."));
|
|
55429
|
+
} catch {
|
|
55430
|
+
return true;
|
|
55431
|
+
}
|
|
55432
|
+
});
|
|
55433
|
+
return `stage ${empty ?? dirs[0] ?? "01"}`;
|
|
55434
|
+
}
|
|
55435
|
+
async function startIssue(ws, tracker = issueTracker(), statuses = resolveIssueStatuses(load()["overrides"] ?? {}, loadIssueTracker())) {
|
|
55436
|
+
const ref = issueIdOf(loadConfigFile(ws, null));
|
|
55437
|
+
if (!ref)
|
|
55438
|
+
return "no issue";
|
|
55439
|
+
return moveIssue(tracker, ref, "in_progress", statuses, {
|
|
55440
|
+
comment: `Work started: ${currentStage(ws)} in workspace ${path57.basename(ws)}.`
|
|
55441
|
+
});
|
|
55442
|
+
}
|
|
55443
|
+
var ISSUE_POINTS, DEFAULTS, ALIASES, RANK, blankToNull;
|
|
55444
|
+
var init_issue_status = __esm({
|
|
55445
|
+
"../../packages/sandbox-core/dist/issue_status.js"() {
|
|
55446
|
+
"use strict";
|
|
55447
|
+
init_define_SANDBOX_BUILD();
|
|
55448
|
+
init_global_config();
|
|
55449
|
+
init_issue_tracker();
|
|
55450
|
+
init_workspace_prompts();
|
|
55451
|
+
ISSUE_POINTS = ["in_progress", "in_review", "merged", "done", "abandoned"];
|
|
55452
|
+
DEFAULTS = {
|
|
55453
|
+
jira: {
|
|
55454
|
+
in_progress: "In Progress",
|
|
55455
|
+
in_review: "In Review",
|
|
55456
|
+
merged: null,
|
|
55457
|
+
done: "Done",
|
|
55458
|
+
abandoned: null
|
|
55459
|
+
},
|
|
55460
|
+
github: {
|
|
55461
|
+
in_progress: "status: in progress",
|
|
55462
|
+
in_review: "status: in review",
|
|
55463
|
+
merged: null,
|
|
55464
|
+
done: "closed",
|
|
55465
|
+
abandoned: "not planned"
|
|
55466
|
+
}
|
|
55467
|
+
};
|
|
55468
|
+
ALIASES = {
|
|
55469
|
+
in_review: "issue_review_status",
|
|
55470
|
+
done: "issue_done_status"
|
|
55471
|
+
};
|
|
55472
|
+
RANK = {
|
|
55473
|
+
in_progress: 1,
|
|
55474
|
+
in_review: 2,
|
|
55475
|
+
merged: 3,
|
|
55476
|
+
done: 4,
|
|
55477
|
+
abandoned: 4
|
|
55478
|
+
};
|
|
55479
|
+
blankToNull = (v) => v === null || v === void 0 || String(v).trim() === "" ? null : String(v).trim();
|
|
55480
|
+
}
|
|
55481
|
+
});
|
|
55482
|
+
|
|
55178
55483
|
// ../../packages/sandbox-core/dist/pr_lifecycle.js
|
|
55179
55484
|
import { execFile } from "node:child_process";
|
|
55180
|
-
import * as
|
|
55485
|
+
import * as fs50 from "node:fs";
|
|
55181
55486
|
import * as os16 from "node:os";
|
|
55182
|
-
import * as
|
|
55487
|
+
import * as path58 from "node:path";
|
|
55183
55488
|
async function httpStatus(url) {
|
|
55184
55489
|
try {
|
|
55185
55490
|
return (await fetch(url, { redirect: "manual", signal: AbortSignal.timeout(5e3) })).status;
|
|
@@ -55196,27 +55501,51 @@ function defaultDeps() {
|
|
|
55196
55501
|
archetypes: loadArchetypes(),
|
|
55197
55502
|
repos: loadReposConfig(),
|
|
55198
55503
|
tracker: issueTracker(),
|
|
55199
|
-
|
|
55200
|
-
doneStatus: String(overrides["issue_done_status"] ?? (loadIssueTracker() === "github" ? "closed" : "Done")),
|
|
55504
|
+
statuses: resolveIssueStatuses(overrides, loadIssueTracker()),
|
|
55201
55505
|
commitRecord: commitAndPush,
|
|
55202
55506
|
remove: (name) => removeWorkspace(name, { closedOut: true }),
|
|
55203
55507
|
terminals: (ws) => openTerminalSessions(ws),
|
|
55204
55508
|
sourceOf: async (dir) => (await worktreeSources([dir]))[0] ?? null,
|
|
55205
|
-
session: (ws) => callbackFields(ws, process.env)
|
|
55509
|
+
session: (ws) => callbackFields(ws, process.env),
|
|
55510
|
+
branchRegistry: (repo, worktree) => branchRegistry(repo, worktree),
|
|
55511
|
+
stop: stopWorkspace,
|
|
55512
|
+
reapIdle: async () => {
|
|
55513
|
+
const minutes = idleStopMinutes();
|
|
55514
|
+
return minutes ? stopIdle(minutes) : { lines: [] };
|
|
55515
|
+
}
|
|
55206
55516
|
};
|
|
55207
55517
|
}
|
|
55518
|
+
async function stopWorkspace(ws) {
|
|
55519
|
+
const feature2 = readEnv(ws, "FEATURE");
|
|
55520
|
+
if (!feature2)
|
|
55521
|
+
return "containers already stopped";
|
|
55522
|
+
try {
|
|
55523
|
+
await stop(feature2);
|
|
55524
|
+
return "containers stopped (slot and volumes kept; `werkmeister up` resumes)";
|
|
55525
|
+
} catch (e) {
|
|
55526
|
+
if (e.code === "NOT_RUNNING")
|
|
55527
|
+
return "containers already stopped";
|
|
55528
|
+
return `could not stop containers \u2014 ${e.message}`;
|
|
55529
|
+
}
|
|
55530
|
+
}
|
|
55531
|
+
function touchExecMarker(ws) {
|
|
55532
|
+
try {
|
|
55533
|
+
fs50.writeFileSync(lastExecMarker(ws), "");
|
|
55534
|
+
} catch {
|
|
55535
|
+
}
|
|
55536
|
+
}
|
|
55208
55537
|
function prFileDir(ws) {
|
|
55209
55538
|
let names;
|
|
55210
55539
|
try {
|
|
55211
|
-
names =
|
|
55540
|
+
names = fs50.readdirSync(path58.join(ws, "stages")).sort();
|
|
55212
55541
|
} catch {
|
|
55213
|
-
return
|
|
55542
|
+
return path58.join(dotDir(ws), "notes");
|
|
55214
55543
|
}
|
|
55215
55544
|
const dir = names.find((n) => /^\d+_propose$/.test(n)) ?? names.find((n) => /^\d+_ship$/.test(n));
|
|
55216
|
-
return dir ?
|
|
55545
|
+
return dir ? path58.join(ws, "stages", dir, "output") : null;
|
|
55217
55546
|
}
|
|
55218
55547
|
function parsePrFile(p) {
|
|
55219
|
-
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(
|
|
55548
|
+
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(fs50.readFileSync(p, "utf8"));
|
|
55220
55549
|
let fm = null;
|
|
55221
55550
|
let yamlError = "";
|
|
55222
55551
|
try {
|
|
@@ -55230,7 +55559,7 @@ function parsePrFile(p) {
|
|
|
55230
55559
|
const heading2 = /^#\s+(.+)$/m.exec(m[2]);
|
|
55231
55560
|
return {
|
|
55232
55561
|
path: p,
|
|
55233
|
-
dir:
|
|
55562
|
+
dir: path58.basename(p).replace(/^pr-/, "").replace(/\.md$/, ""),
|
|
55234
55563
|
fm,
|
|
55235
55564
|
title: heading2 ? heading2[1].trim() : "",
|
|
55236
55565
|
body: heading2 ? m[2].slice(heading2.index + heading2[0].length).replace(/^\s*\n/, "") : m[2]
|
|
@@ -55242,10 +55571,10 @@ function readPrFiles(ws) {
|
|
|
55242
55571
|
return [];
|
|
55243
55572
|
let names = [];
|
|
55244
55573
|
try {
|
|
55245
|
-
names =
|
|
55574
|
+
names = fs50.readdirSync(dir).filter((n) => /^pr-.+\.md$/.test(n)).sort();
|
|
55246
55575
|
} catch {
|
|
55247
55576
|
}
|
|
55248
|
-
return names.map((n) => parsePrFile(
|
|
55577
|
+
return names.map((n) => parsePrFile(path58.join(dir, n)));
|
|
55249
55578
|
}
|
|
55250
55579
|
function writePrFields(file, fields) {
|
|
55251
55580
|
updateNoteStatus(file.path, fields);
|
|
@@ -55260,18 +55589,18 @@ function passTable(text) {
|
|
|
55260
55589
|
}
|
|
55261
55590
|
function stagesCheck(ws) {
|
|
55262
55591
|
const prDir = prFileDir(ws);
|
|
55263
|
-
if (!
|
|
55264
|
-
const open = openDecisions(ws).map((n) =>
|
|
55592
|
+
if (!fs50.existsSync(path58.join(ws, "stages"))) {
|
|
55593
|
+
const open = openDecisions(ws).map((n) => path58.basename(n.path));
|
|
55265
55594
|
return {
|
|
55266
55595
|
name: "stages",
|
|
55267
55596
|
ok: open.length === 0,
|
|
55268
55597
|
detail: open.length ? `open decision notes: ${open.join(", ")}` : "no open decisions"
|
|
55269
55598
|
};
|
|
55270
55599
|
}
|
|
55271
|
-
const own = prDir ?
|
|
55272
|
-
const empty =
|
|
55600
|
+
const own = prDir ? path58.basename(path58.dirname(prDir)) : "";
|
|
55601
|
+
const empty = fs50.readdirSync(path58.join(ws, "stages")).sort().filter((d) => d < own).filter((d) => {
|
|
55273
55602
|
try {
|
|
55274
|
-
return !
|
|
55603
|
+
return !fs50.readdirSync(path58.join(ws, "stages", d, "output")).some((f) => !f.startsWith("."));
|
|
55275
55604
|
} catch {
|
|
55276
55605
|
return true;
|
|
55277
55606
|
}
|
|
@@ -55283,14 +55612,14 @@ function stagesCheck(ws) {
|
|
|
55283
55612
|
};
|
|
55284
55613
|
}
|
|
55285
55614
|
function exerciseFile(ws) {
|
|
55286
|
-
if (!
|
|
55287
|
-
const p =
|
|
55288
|
-
return
|
|
55615
|
+
if (!fs50.existsSync(path58.join(ws, "stages"))) {
|
|
55616
|
+
const p = path58.join(dotDir(ws), "notes", "exercise.md");
|
|
55617
|
+
return fs50.existsSync(p) ? p : null;
|
|
55289
55618
|
}
|
|
55290
55619
|
for (const name of ["exercise.md", "verify.md"]) {
|
|
55291
|
-
for (const d of
|
|
55292
|
-
const p =
|
|
55293
|
-
if (
|
|
55620
|
+
for (const d of fs50.readdirSync(path58.join(ws, "stages")).sort()) {
|
|
55621
|
+
const p = path58.join(ws, "stages", d, "output", name);
|
|
55622
|
+
if (fs50.existsSync(p))
|
|
55294
55623
|
return p;
|
|
55295
55624
|
}
|
|
55296
55625
|
}
|
|
@@ -55318,7 +55647,7 @@ function mapTouched(files, repo) {
|
|
|
55318
55647
|
}
|
|
55319
55648
|
function readEnv(ws, key) {
|
|
55320
55649
|
try {
|
|
55321
|
-
for (const line of
|
|
55650
|
+
for (const line of fs50.readFileSync(path58.join(ws, ".env"), "utf8").split(/\r?\n/)) {
|
|
55322
55651
|
const eq = line.indexOf("=");
|
|
55323
55652
|
if (eq > 0 && line.slice(0, eq).trim() === key)
|
|
55324
55653
|
return line.slice(eq + 1).trim();
|
|
@@ -55332,14 +55661,24 @@ async function runGate(ws, file, deps = defaultDeps()) {
|
|
|
55332
55661
|
const add = (name, ok, detail) => checks.push({ name, ok, detail });
|
|
55333
55662
|
const table = passTable(file.body);
|
|
55334
55663
|
add("acceptance", table === null, table ? `PR file ${table}` : "all PASS");
|
|
55335
|
-
const dir =
|
|
55664
|
+
const dir = path58.join(ws, file.dir);
|
|
55336
55665
|
const status2 = await deps.run("git", ["-C", dir, "status", "--porcelain"]);
|
|
55337
55666
|
add("clean", status2.code === 0 && status2.stdout.trim() === "", status2.code ? status2.stderr.trim() : status2.stdout.trim() ? `uncommitted:
|
|
55338
55667
|
${status2.stdout.trimEnd()}` : "clean");
|
|
55668
|
+
let services = deps.services;
|
|
55669
|
+
let repo = deps.repos[file.dir] ?? {};
|
|
55670
|
+
try {
|
|
55671
|
+
const own = deps.branchRegistry(file.dir, dir);
|
|
55672
|
+
if (own) {
|
|
55673
|
+
services = own.services;
|
|
55674
|
+
repo = own.repos[file.dir] ?? {};
|
|
55675
|
+
}
|
|
55676
|
+
} catch (e) {
|
|
55677
|
+
add("registry", false, `branch fragment: ${e instanceof Error ? e.message : String(e)}`);
|
|
55678
|
+
}
|
|
55339
55679
|
const cfg = loadConfigFile(ws, null);
|
|
55340
55680
|
const feature2 = String(cfg["feature"] ?? readEnv(ws, "FEATURE")).trim();
|
|
55341
55681
|
const local = new Set(String(cfg["services"] ?? "").split(",").map((s) => s.trim()).filter(Boolean));
|
|
55342
|
-
const repo = deps.repos[file.dir] ?? {};
|
|
55343
55682
|
const ps = await deps.run("docker", ["ps", "--format", "{{.Names}}"]);
|
|
55344
55683
|
const running = new Set(ps.stdout.split("\n").map((s) => s.trim()).filter(Boolean));
|
|
55345
55684
|
const containerFor = (svc) => {
|
|
@@ -55357,7 +55696,7 @@ ${status2.stdout.trimEnd()}` : "clean");
|
|
|
55357
55696
|
const touched = diff.code === 0 ? mapTouched(diff.stdout.split("\n").filter(Boolean), repo) : [];
|
|
55358
55697
|
const skips = file.fm["skip_tests"] ?? {};
|
|
55359
55698
|
for (const svc of touched) {
|
|
55360
|
-
const cmd = serviceTestCommand(svc,
|
|
55699
|
+
const cmd = serviceTestCommand(svc, services[svc], deps.archetypes);
|
|
55361
55700
|
const why = reason(skips[svc]);
|
|
55362
55701
|
if (!cmd) {
|
|
55363
55702
|
add(`test ${svc}`, Boolean(why), why ? `skipped: ${why}` : "no test command registered \u2014 add `test:` to its registry entry, or `skip_tests: { " + svc + ": <reason> }`");
|
|
@@ -55368,16 +55707,17 @@ ${status2.stdout.trimEnd()}` : "clean");
|
|
|
55368
55707
|
add(`test ${svc}`, Boolean(why), why ? `skipped: ${why}` : "no running container for this repo \u2014 run `sandbox up`");
|
|
55369
55708
|
continue;
|
|
55370
55709
|
}
|
|
55710
|
+
touchExecMarker(ws);
|
|
55371
55711
|
const res = await deps.run("docker", ["exec", container, "sh", "-c", cmd]);
|
|
55372
55712
|
add(`test ${svc}`, res.code === 0, `${cmd} \u2192 exit ${res.code}${res.code ? `
|
|
55373
55713
|
${(res.stdout + res.stderr).trimEnd().split("\n").slice(-15).join("\n")}` : ""}`);
|
|
55374
55714
|
}
|
|
55375
55715
|
const evidence = exerciseFile(ws);
|
|
55376
|
-
const evidenceProblem = evidence ? passTable(
|
|
55377
|
-
add("exercise", evidenceProblem === null, evidence ? `${
|
|
55716
|
+
const evidenceProblem = evidence ? passTable(fs50.readFileSync(evidence, "utf8")) : "is missing";
|
|
55717
|
+
add("exercise", evidenceProblem === null, evidence ? `${path58.relative(ws, evidence)} ${evidenceProblem ?? "all PASS"}` : "no exercise.md \u2014 record the exercise checks first");
|
|
55378
55718
|
const slot = Number(readEnv(ws, "SANDBOX_SLOT"));
|
|
55379
55719
|
for (const svc of touched.filter((s) => local.has(s))) {
|
|
55380
|
-
const def =
|
|
55720
|
+
const def = services[svc] ?? {};
|
|
55381
55721
|
if (!running.has(`${feature2}-${svc}`)) {
|
|
55382
55722
|
add(`baseline ${svc}`, false, "container not running \u2014 run `sandbox up`");
|
|
55383
55723
|
continue;
|
|
@@ -55398,6 +55738,7 @@ ${(res.stdout + res.stderr).trimEnd().split("\n").slice(-15).join("\n")}` : ""}`
|
|
|
55398
55738
|
else if (!container)
|
|
55399
55739
|
add("release", false, "no running container for this repo \u2014 run `sandbox up`");
|
|
55400
55740
|
else {
|
|
55741
|
+
touchExecMarker(ws);
|
|
55401
55742
|
const res = await deps.run("docker", ["exec", container, "sh", "-c", repo["release_check"]]);
|
|
55402
55743
|
add("release", res.code === 0, `${repo["release_check"]} \u2192 exit ${res.code}${res.code ? `
|
|
55403
55744
|
${(res.stdout + res.stderr).trimEnd().split("\n").slice(-5).join("\n")}` : ""}`);
|
|
@@ -55444,7 +55785,7 @@ async function openPrs(ws, only, deps = defaultDeps()) {
|
|
|
55444
55785
|
lines.push(...formatGate(f.dir, g));
|
|
55445
55786
|
continue;
|
|
55446
55787
|
}
|
|
55447
|
-
const dir =
|
|
55788
|
+
const dir = path58.join(ws, f.dir);
|
|
55448
55789
|
const branch = String(f.fm["branch"] ?? "");
|
|
55449
55790
|
const push = await deps.run("git", ["-C", dir, "push", "-u", "origin", branch]);
|
|
55450
55791
|
if (push.code !== 0) {
|
|
@@ -55468,8 +55809,8 @@ async function openPrs(ws, only, deps = defaultDeps()) {
|
|
|
55468
55809
|
], dir);
|
|
55469
55810
|
let url = existing2.code === 0 ? existing2.stdout.trim() : "";
|
|
55470
55811
|
if (!url) {
|
|
55471
|
-
const bodyFile =
|
|
55472
|
-
|
|
55812
|
+
const bodyFile = path58.join(fs50.mkdtempSync(path58.join(os16.tmpdir(), "sandbox-pr-")), "body.md");
|
|
55813
|
+
fs50.writeFileSync(bodyFile, f.body);
|
|
55473
55814
|
const created = await deps.run("gh", [
|
|
55474
55815
|
"pr",
|
|
55475
55816
|
"create",
|
|
@@ -55484,7 +55825,7 @@ async function openPrs(ws, only, deps = defaultDeps()) {
|
|
|
55484
55825
|
"--body-file",
|
|
55485
55826
|
bodyFile
|
|
55486
55827
|
], dir);
|
|
55487
|
-
|
|
55828
|
+
fs50.rmSync(path58.dirname(bodyFile), { recursive: true, force: true });
|
|
55488
55829
|
url = created.stdout.trim().split("\n").pop() ?? "";
|
|
55489
55830
|
if (created.code !== 0 || !url.startsWith("http")) {
|
|
55490
55831
|
ok = false;
|
|
@@ -55493,53 +55834,76 @@ async function openPrs(ws, only, deps = defaultDeps()) {
|
|
|
55493
55834
|
}
|
|
55494
55835
|
}
|
|
55495
55836
|
writePrFields(f, { status: "open", url, ...await deps.session(ws) });
|
|
55496
|
-
opened.push(
|
|
55837
|
+
opened.push(path58.relative(ws, f.path));
|
|
55497
55838
|
lines.push(`${f.dir}: open ${url}`);
|
|
55498
55839
|
const ref = issueIdOf(loadConfigFile(ws, null));
|
|
55499
55840
|
if (ref) {
|
|
55500
|
-
|
|
55501
|
-
|
|
55502
|
-
}
|
|
55503
|
-
|
|
55504
|
-
|
|
55841
|
+
const moved = await moveIssue(deps.tracker, ref, "in_review", deps.statuses, {
|
|
55842
|
+
comment: `PR opened: ${url}`
|
|
55843
|
+
});
|
|
55844
|
+
if (moved)
|
|
55845
|
+
lines.push(` issue ${moved}`);
|
|
55505
55846
|
}
|
|
55506
55847
|
}
|
|
55507
55848
|
if (opened.length)
|
|
55508
55849
|
lines.push((await deps.commitRecord(ws, "chore: record opened PR(s)", opened)).message);
|
|
55850
|
+
if (ok && opened.length)
|
|
55851
|
+
lines.push(await deps.stop(ws));
|
|
55509
55852
|
return { ok, lines };
|
|
55510
55853
|
}
|
|
55511
55854
|
async function refreshPrStatus(ws, deps = defaultDeps()) {
|
|
55512
55855
|
const lines = [];
|
|
55513
55856
|
const changed = [];
|
|
55857
|
+
const seen = {
|
|
55858
|
+
shipped: [],
|
|
55859
|
+
merged: [],
|
|
55860
|
+
closed: [],
|
|
55861
|
+
changesRequested: [],
|
|
55862
|
+
reReview: []
|
|
55863
|
+
};
|
|
55514
55864
|
for (const f of readPrFiles(ws)) {
|
|
55515
55865
|
const before = statusOf(f);
|
|
55866
|
+
const reviewBefore = f.fm["review"];
|
|
55516
55867
|
if (before === "open" && f.fm["url"]) {
|
|
55517
55868
|
const v = await deps.run("gh", [
|
|
55518
55869
|
"pr",
|
|
55519
55870
|
"view",
|
|
55520
55871
|
String(f.fm["url"]),
|
|
55521
55872
|
"--json",
|
|
55522
|
-
"state,mergeCommit"
|
|
55873
|
+
"state,mergeCommit,reviewDecision,latestReviews"
|
|
55523
55874
|
]);
|
|
55524
55875
|
if (v.code !== 0) {
|
|
55525
55876
|
lines.push(`${f.dir}: gh pr view failed \u2014 ${v.stderr.trim()}`);
|
|
55526
55877
|
continue;
|
|
55527
55878
|
}
|
|
55528
55879
|
const view2 = JSON.parse(v.stdout);
|
|
55529
|
-
|
|
55880
|
+
const changesRequested = view2.reviewDecision === "CHANGES_REQUESTED" || (view2.latestReviews ?? []).some((r) => r.state === "CHANGES_REQUESTED");
|
|
55881
|
+
const url = String(f.fm["url"]);
|
|
55882
|
+
if (view2.state === "MERGED") {
|
|
55530
55883
|
writePrFields(f, { status: "merged", merge_sha: view2.mergeCommit?.oid ?? "" });
|
|
55531
|
-
|
|
55884
|
+
seen.merged.push(url);
|
|
55885
|
+
} else if (view2.state === "CLOSED") {
|
|
55532
55886
|
writePrFields(f, {
|
|
55533
55887
|
status: "closed",
|
|
55534
55888
|
reason: reason(f.fm["reason"]) || "closed on GitHub"
|
|
55535
55889
|
});
|
|
55890
|
+
seen.closed.push(url);
|
|
55891
|
+
} else if (changesRequested) {
|
|
55892
|
+
if (reviewBefore !== "changes_requested") {
|
|
55893
|
+
writePrFields(f, { review: "changes_requested" });
|
|
55894
|
+
seen.changesRequested.push(url);
|
|
55895
|
+
}
|
|
55896
|
+
} else if (reviewBefore === "changes_requested") {
|
|
55897
|
+
writePrFields(f, { review: void 0 });
|
|
55898
|
+
seen.reReview.push(url);
|
|
55899
|
+
}
|
|
55536
55900
|
}
|
|
55537
55901
|
if (statusOf(f) === "merged") {
|
|
55538
55902
|
const repo = deps.repos[f.dir] ?? {};
|
|
55539
55903
|
if (repo["ship"] !== "tag") {
|
|
55540
55904
|
writePrFields(f, { status: "shipped" });
|
|
55541
55905
|
} else {
|
|
55542
|
-
const dir =
|
|
55906
|
+
const dir = path58.join(ws, f.dir);
|
|
55543
55907
|
await deps.run("git", ["-C", dir, "fetch", "-q", "--tags", "origin"]);
|
|
55544
55908
|
const tags = await deps.run("git", [
|
|
55545
55909
|
"-C",
|
|
@@ -55557,16 +55921,43 @@ async function refreshPrStatus(ws, deps = defaultDeps()) {
|
|
|
55557
55921
|
}
|
|
55558
55922
|
}
|
|
55559
55923
|
const after = statusOf(f);
|
|
55560
|
-
if (after !==
|
|
55561
|
-
|
|
55924
|
+
if (after === "shipped" && before !== "shipped")
|
|
55925
|
+
seen.shipped.push(f);
|
|
55926
|
+
if (after !== before || f.fm["review"] !== reviewBefore)
|
|
55927
|
+
changed.push(path58.relative(ws, f.path));
|
|
55562
55928
|
lines.push(`${f.dir}: ${after}${f.fm["release"] ? ` (${f.fm["release"]})` : ""}${f.fm["url"] ? ` ${f.fm["url"]}` : ""}`);
|
|
55563
55929
|
}
|
|
55930
|
+
const moved = await moveIssueForPrs(ws, seen, deps);
|
|
55931
|
+
if (moved)
|
|
55932
|
+
lines.push(`issue ${moved}`);
|
|
55933
|
+
if (seen.merged.length || seen.shipped.length)
|
|
55934
|
+
lines.push(await deps.stop(ws));
|
|
55564
55935
|
if (changed.length)
|
|
55565
55936
|
lines.push((await deps.commitRecord(ws, "chore: record PR status", changed)).message);
|
|
55566
55937
|
return { lines, changed: changed.length > 0 };
|
|
55567
55938
|
}
|
|
55939
|
+
async function moveIssueForPrs(ws, seen, deps) {
|
|
55940
|
+
const anything = Object.values(seen).some((v) => v.length);
|
|
55941
|
+
const ref = anything ? issueIdOf(loadConfigFile(ws, null)) : "";
|
|
55942
|
+
if (!ref)
|
|
55943
|
+
return "";
|
|
55944
|
+
const files = readPrFiles(ws);
|
|
55945
|
+
const state = lifecycleOf(ws).state;
|
|
55946
|
+
const move = (point, comment, back = false) => moveIssue(deps.tracker, ref, point, deps.statuses, { comment, back });
|
|
55947
|
+
if (seen.shipped.length && state === "shipped")
|
|
55948
|
+
return move("done", shippedComment(files.filter((f) => statusOf(f) === "shipped")));
|
|
55949
|
+
if (seen.merged.length && files.every((f) => PAST_MERGE.has(statusOf(f))))
|
|
55950
|
+
return move("merged", `Merged: ${seen.merged.join(", ")}`);
|
|
55951
|
+
if (seen.changesRequested.length)
|
|
55952
|
+
return move("in_progress", `Changes requested: ${seen.changesRequested.join(", ")}`, true);
|
|
55953
|
+
if (seen.closed.length && !files.some((f) => statusOf(f) === "open"))
|
|
55954
|
+
return move("in_progress", `PR closed without merging: ${seen.closed.join(", ")}`, true);
|
|
55955
|
+
if (seen.reReview.length)
|
|
55956
|
+
return move("in_review", `Back in review: ${seen.reReview.join(", ")}`);
|
|
55957
|
+
return "";
|
|
55958
|
+
}
|
|
55568
55959
|
async function callbackFields(ws, env) {
|
|
55569
|
-
const session = env["CLAUDE_CODE_SESSION_ID"] || (await resolveSessionSource(
|
|
55960
|
+
const session = env["CLAUDE_CODE_SESSION_ID"] || (await resolveSessionSource(path58.basename(ws)).catch(() => null))?.sessionId;
|
|
55570
55961
|
if (!session)
|
|
55571
55962
|
return {};
|
|
55572
55963
|
const host = env["CLAUDE_CODE_HOST_SESSION_ID"];
|
|
@@ -55584,23 +55975,23 @@ function callbackMessage(status2, url, release) {
|
|
|
55584
55975
|
function takeLock(file) {
|
|
55585
55976
|
for (let i = 0; i < 2; i++) {
|
|
55586
55977
|
try {
|
|
55587
|
-
|
|
55978
|
+
fs50.closeSync(fs50.openSync(file, "wx"));
|
|
55588
55979
|
return true;
|
|
55589
55980
|
} catch (e) {
|
|
55590
55981
|
if (e.code !== "EEXIST")
|
|
55591
55982
|
throw e;
|
|
55592
55983
|
try {
|
|
55593
|
-
if (Date.now() -
|
|
55984
|
+
if (Date.now() - fs50.statSync(file).mtimeMs < LOCK_STALE_MS)
|
|
55594
55985
|
return false;
|
|
55595
|
-
|
|
55986
|
+
fs50.rmSync(file, { force: true });
|
|
55596
55987
|
} catch {
|
|
55597
55988
|
}
|
|
55598
55989
|
}
|
|
55599
55990
|
}
|
|
55600
55991
|
return false;
|
|
55601
55992
|
}
|
|
55602
|
-
async function claimCallbacks(workspaces, opts, deps = defaultDeps(), lockFile =
|
|
55603
|
-
|
|
55993
|
+
async function claimCallbacks(workspaces, opts, deps = defaultDeps(), lockFile = path58.join(sandboxHome(), "pr-callbacks.lock")) {
|
|
55994
|
+
fs50.mkdirSync(path58.dirname(lockFile), { recursive: true });
|
|
55604
55995
|
if (!takeLock(lockFile))
|
|
55605
55996
|
return { claimed: [], lines: ["another PR watch pass is running"] };
|
|
55606
55997
|
const claimed2 = [];
|
|
@@ -55619,7 +56010,7 @@ async function claimCallbacks(workspaces, opts, deps = defaultDeps(), lockFile =
|
|
|
55619
56010
|
continue;
|
|
55620
56011
|
if (before.some((f) => statusOf(f) === "open" || statusOf(f) === "merged"))
|
|
55621
56012
|
for (const l of (await refreshPrStatus(ws, deps)).lines)
|
|
55622
|
-
lines.push(`${
|
|
56013
|
+
lines.push(`${path58.basename(ws)}/${l}`);
|
|
55623
56014
|
const changed = [];
|
|
55624
56015
|
for (const f of callbacks()) {
|
|
55625
56016
|
const status2 = statusOf(f);
|
|
@@ -55637,23 +56028,23 @@ async function claimCallbacks(workspaces, opts, deps = defaultDeps(), lockFile =
|
|
|
55637
56028
|
message
|
|
55638
56029
|
});
|
|
55639
56030
|
writePrFields(f, SETTLED.has(status2) ? { session: void 0, host_session: void 0, notified: void 0 } : { notified: status2 });
|
|
55640
|
-
changed.push(
|
|
56031
|
+
changed.push(path58.relative(ws, f.path));
|
|
55641
56032
|
}
|
|
55642
56033
|
if (changed.length)
|
|
55643
56034
|
lines.push((await deps.commitRecord(ws, "chore: record PR callback", changed)).message);
|
|
55644
56035
|
}
|
|
55645
56036
|
} finally {
|
|
55646
|
-
|
|
56037
|
+
fs50.rmSync(lockFile, { force: true });
|
|
55647
56038
|
}
|
|
55648
56039
|
return { claimed: claimed2, lines };
|
|
55649
56040
|
}
|
|
55650
56041
|
async function deliverHeadless(cb) {
|
|
55651
|
-
const name =
|
|
56042
|
+
const name = path58.basename(cb.ws);
|
|
55652
56043
|
if ((await resolveSessionSource(name, cb.session)).sessionId !== cb.session)
|
|
55653
56044
|
throw new WorkspaceError(`session ${cb.session} is gone`);
|
|
55654
56045
|
await sendToClaudeSession(name, cb.message, cb.session);
|
|
55655
56046
|
}
|
|
55656
|
-
async function watchPrs(opts, deps = defaultDeps(), send = deliverHeadless) {
|
|
56047
|
+
async function watchPrs(opts, deps = defaultDeps(), send = deliverHeadless, idleLock = path58.join(sandboxHome(), "idle-stop.lock")) {
|
|
55657
56048
|
const { workspaces } = await listWorkspaces();
|
|
55658
56049
|
const { claimed: claimed2, lines } = await claimCallbacks(workspaces.map((w) => w.path), opts, deps);
|
|
55659
56050
|
const desktop = [];
|
|
@@ -55669,6 +56060,15 @@ async function watchPrs(opts, deps = defaultDeps(), send = deliverHeadless) {
|
|
|
55669
56060
|
lines.push(`${cb.dir}: could not resume session ${cb.session}: ${e.message}`);
|
|
55670
56061
|
}
|
|
55671
56062
|
}
|
|
56063
|
+
fs50.mkdirSync(path58.dirname(idleLock), { recursive: true });
|
|
56064
|
+
if (takeLock(idleLock)) {
|
|
56065
|
+
try {
|
|
56066
|
+
lines.push(...(await deps.reapIdle()).lines);
|
|
56067
|
+
} finally {
|
|
56068
|
+
fs50.rmSync(idleLock, { force: true });
|
|
56069
|
+
}
|
|
56070
|
+
} else
|
|
56071
|
+
lines.push("another idle-stop pass is running");
|
|
55672
56072
|
return { ok: true, lines, desktop };
|
|
55673
56073
|
}
|
|
55674
56074
|
async function adoptPr(ws, opts, deps = defaultDeps()) {
|
|
@@ -55682,8 +56082,8 @@ async function adoptPr(ws, opts, deps = defaultDeps()) {
|
|
|
55682
56082
|
throw new WorkspaceError(`Name the repo \u2014 this workspace has ${wts.map((w) => w.repo).join(", ") || "no code worktree"}.`);
|
|
55683
56083
|
repo = wts[0].repo;
|
|
55684
56084
|
}
|
|
55685
|
-
const file =
|
|
55686
|
-
if (
|
|
56085
|
+
const file = path58.join(dir, `pr-${repo}.md`);
|
|
56086
|
+
if (fs50.existsSync(file))
|
|
55687
56087
|
throw new WorkspaceError(`${file} already exists \u2014 remove it first to adopt.`);
|
|
55688
56088
|
let fm;
|
|
55689
56089
|
let title;
|
|
@@ -55695,7 +56095,7 @@ async function adoptPr(ws, opts, deps = defaultDeps()) {
|
|
|
55695
56095
|
} else {
|
|
55696
56096
|
if (!opts.pr)
|
|
55697
56097
|
throw new WorkspaceError("Give a PR number or URL, or --no-pr <reason>.");
|
|
55698
|
-
const v = await deps.run("gh", ["pr", "view", opts.pr, "--json", "url,headRefName,baseRefName,title,state,mergeCommit"],
|
|
56098
|
+
const v = await deps.run("gh", ["pr", "view", opts.pr, "--json", "url,headRefName,baseRefName,title,state,mergeCommit"], path58.join(ws, repo));
|
|
55699
56099
|
if (v.code !== 0)
|
|
55700
56100
|
throw new WorkspaceError(`gh pr view ${opts.pr} failed \u2014 ${v.stderr.trim()}`);
|
|
55701
56101
|
const pr = JSON.parse(v.stdout);
|
|
@@ -55713,7 +56113,7 @@ async function adoptPr(ws, opts, deps = defaultDeps()) {
|
|
|
55713
56113
|
};
|
|
55714
56114
|
title = pr.title;
|
|
55715
56115
|
}
|
|
55716
|
-
|
|
56116
|
+
fs50.writeFileSync(file, `---
|
|
55717
56117
|
${import_yaml14.default.stringify(fm)}---
|
|
55718
56118
|
# ${title}
|
|
55719
56119
|
|
|
@@ -55722,7 +56122,7 @@ Adopted with \`sandbox pr adopt\`: the work landed outside this workspace.
|
|
|
55722
56122
|
const lines = [`${repo}: adopted as ${fm["status"]}`];
|
|
55723
56123
|
if (fm["status"] !== "closed")
|
|
55724
56124
|
lines.push(...(await refreshPrStatus(ws, deps)).lines);
|
|
55725
|
-
lines.push((await deps.commitRecord(ws, "chore: adopt PR", [
|
|
56125
|
+
lines.push((await deps.commitRecord(ws, "chore: adopt PR", [path58.relative(ws, file)])).message);
|
|
55726
56126
|
return { ok: true, lines };
|
|
55727
56127
|
}
|
|
55728
56128
|
function lifecycleOf(ws) {
|
|
@@ -55738,7 +56138,7 @@ function lifecycleOf(ws) {
|
|
|
55738
56138
|
}));
|
|
55739
56139
|
const all = (set) => prs.every((p) => set.has(p.status));
|
|
55740
56140
|
let state;
|
|
55741
|
-
if (
|
|
56141
|
+
if (fs50.existsSync(path58.join(ws, "closeout.md")))
|
|
55742
56142
|
state = "closed-out";
|
|
55743
56143
|
else if (!prs.length)
|
|
55744
56144
|
state = "working";
|
|
@@ -55757,7 +56157,7 @@ function lifecycleOf(ws) {
|
|
|
55757
56157
|
return { state, prs };
|
|
55758
56158
|
}
|
|
55759
56159
|
async function removalBlockers(ws, worktrees, run34 = execRun) {
|
|
55760
|
-
if (
|
|
56160
|
+
if (fs50.existsSync(path58.join(ws, "closeout.md")))
|
|
55761
56161
|
return [];
|
|
55762
56162
|
const files = readPrFiles(ws);
|
|
55763
56163
|
const out = [];
|
|
@@ -55802,7 +56202,7 @@ function abandonWorkspace(ws, why) {
|
|
|
55802
56202
|
for (const f of files)
|
|
55803
56203
|
if (!SETTLED.has(statusOf(f)))
|
|
55804
56204
|
writePrFields(f, { status: "abandoned", reason: why });
|
|
55805
|
-
|
|
56205
|
+
fs50.writeFileSync(path58.join(ws, "closeout.md"), renderCloseout(files, "not closed (abandoned)", `Abandoned: ${why}`));
|
|
55806
56206
|
}
|
|
55807
56207
|
async function openTerminalSessions(ws, run34 = execRun) {
|
|
55808
56208
|
const ps = await run34("ps", ["-axo", "pid=,tty=,comm="]);
|
|
@@ -55811,7 +56211,7 @@ async function openTerminalSessions(ws, run34 = execRun) {
|
|
|
55811
56211
|
const ttys = /* @__PURE__ */ new Map();
|
|
55812
56212
|
for (const line of ps.stdout.split("\n")) {
|
|
55813
56213
|
const m = /^\s*(\d+)\s+(\S+)\s+(.+?)\s*$/.exec(line);
|
|
55814
|
-
if (m &&
|
|
56214
|
+
if (m && path58.basename(m[3]) === "claude" && m[2] !== "??" && m[2] !== "?")
|
|
55815
56215
|
ttys.set(m[1], m[2]);
|
|
55816
56216
|
}
|
|
55817
56217
|
if (!ttys.size)
|
|
@@ -55824,7 +56224,7 @@ async function openTerminalSessions(ws, run34 = execRun) {
|
|
|
55824
56224
|
pid = line.slice(1);
|
|
55825
56225
|
else if (line.startsWith("n") && ttys.has(pid)) {
|
|
55826
56226
|
const cwd = line.slice(1);
|
|
55827
|
-
if (cwd === ws || cwd.startsWith(ws +
|
|
56227
|
+
if (cwd === ws || cwd.startsWith(ws + path58.sep))
|
|
55828
56228
|
out.push(`terminal session: pid ${pid} on ${ttys.get(pid)} in ${cwd} \u2014 /exit it`);
|
|
55829
56229
|
}
|
|
55830
56230
|
}
|
|
@@ -55850,28 +56250,26 @@ async function closeOut(name, opts = {}, deps = defaultDeps()) {
|
|
|
55850
56250
|
const ref = issueIdOf(cfg);
|
|
55851
56251
|
let issueLine = "none";
|
|
55852
56252
|
if (ref) {
|
|
55853
|
-
|
|
55854
|
-
|
|
55855
|
-
|
|
56253
|
+
if (why) {
|
|
56254
|
+
issueLine = await moveIssue(deps.tracker, ref, "abandoned", deps.statuses, {
|
|
56255
|
+
comment: `Abandoned: ${why}`,
|
|
56256
|
+
alwaysComment: true
|
|
56257
|
+
});
|
|
56258
|
+
} else if (abandoned) {
|
|
56259
|
+
const current = (await deps.tracker.fetchIssue(ref))?.status ?? "";
|
|
55856
56260
|
issueLine = `${ref} left ${current || "as is"} (abandoned)`;
|
|
55857
|
-
} else if ([done, "closed", "done"].includes(current.toLowerCase())) {
|
|
55858
|
-
issueLine = `${ref} already ${current}`;
|
|
55859
56261
|
} else {
|
|
55860
|
-
|
|
55861
|
-
|
|
55862
|
-
|
|
55863
|
-
status: deps.doneStatus,
|
|
55864
|
-
comment: `Shipped: ${shipped.join("; ") || "no PRs"}`
|
|
55865
|
-
});
|
|
55866
|
-
issueLine = `${ref} \u2192 ${deps.doneStatus}`;
|
|
56262
|
+
issueLine = await moveIssue(deps.tracker, ref, "done", deps.statuses, {
|
|
56263
|
+
comment: shippedComment(readPrFiles(ws).filter((f) => statusOf(f) === "shipped"))
|
|
56264
|
+
}) || `${ref} left as is (no done status configured)`;
|
|
55867
56265
|
}
|
|
55868
56266
|
lines.push(`Issue: ${issueLine}`);
|
|
55869
56267
|
}
|
|
55870
56268
|
const files = readPrFiles(ws);
|
|
55871
|
-
|
|
56269
|
+
fs50.writeFileSync(path58.join(ws, "closeout.md"), renderCloseout(files, issueLine, why ? `Abandoned: ${why}` : void 0));
|
|
55872
56270
|
const branches = [];
|
|
55873
56271
|
for (const f of abandoned ? [] : files.filter((x) => (statusOf(x) === "merged" || statusOf(x) === "shipped") && !x.fm["adopted"])) {
|
|
55874
|
-
const src = await deps.sourceOf(
|
|
56272
|
+
const src = await deps.sourceOf(path58.join(ws, f.dir));
|
|
55875
56273
|
if (src && f.fm["branch"])
|
|
55876
56274
|
branches.push({ src, branch: String(f.fm["branch"]) });
|
|
55877
56275
|
}
|
|
@@ -55900,7 +56298,7 @@ async function closeOut(name, opts = {}, deps = defaultDeps()) {
|
|
|
55900
56298
|
const command = `sandbox close-out ${name}${why ? " --abandon" : ""}`;
|
|
55901
56299
|
return { command, output: lines.filter(Boolean).join("\n"), path: ws, terminals };
|
|
55902
56300
|
}
|
|
55903
|
-
var import_yaml14, SETTLED, PAST_MERGE, execRun, statusOf, reason, PASS_CELL, FAIL_CELL, LOCK_STALE_MS;
|
|
56301
|
+
var import_yaml14, SETTLED, PAST_MERGE, execRun, statusOf, reason, PASS_CELL, FAIL_CELL, shippedComment, LOCK_STALE_MS;
|
|
55904
56302
|
var init_pr_lifecycle = __esm({
|
|
55905
56303
|
"../../packages/sandbox-core/dist/pr_lifecycle.js"() {
|
|
55906
56304
|
"use strict";
|
|
@@ -55911,8 +56309,11 @@ var init_pr_lifecycle = __esm({
|
|
|
55911
56309
|
init_config();
|
|
55912
56310
|
init_global_config();
|
|
55913
56311
|
init_graduate();
|
|
56312
|
+
init_issue_status();
|
|
55914
56313
|
init_issue_tracker();
|
|
56314
|
+
init_lifecycle();
|
|
55915
56315
|
init_notes();
|
|
56316
|
+
init_registry_sync();
|
|
55916
56317
|
init_sandbox();
|
|
55917
56318
|
init_slots();
|
|
55918
56319
|
init_workspace_prompts();
|
|
@@ -55932,6 +56333,7 @@ var init_pr_lifecycle = __esm({
|
|
|
55932
56333
|
reason = (v) => typeof v === "string" ? v.trim() : "";
|
|
55933
56334
|
PASS_CELL = /\|\s*PASS\s*\|/;
|
|
55934
56335
|
FAIL_CELL = /\|\s*FAIL\s*\|/;
|
|
56336
|
+
shippedComment = (files) => `Shipped: ${files.map((f) => `${f.fm["release"] ?? "merge"} \u2014 ${f.fm["url"]}`).join("; ") || "no PRs"}`;
|
|
55935
56337
|
LOCK_STALE_MS = 15 * 6e4;
|
|
55936
56338
|
}
|
|
55937
56339
|
});
|
|
@@ -56746,12 +57148,12 @@ function parseTree(text, errors = [], options = ParseOptions.DEFAULT) {
|
|
|
56746
57148
|
}
|
|
56747
57149
|
return result;
|
|
56748
57150
|
}
|
|
56749
|
-
function findNodeAtLocation(root,
|
|
57151
|
+
function findNodeAtLocation(root, path74) {
|
|
56750
57152
|
if (!root) {
|
|
56751
57153
|
return void 0;
|
|
56752
57154
|
}
|
|
56753
57155
|
let node = root;
|
|
56754
|
-
for (let segment of
|
|
57156
|
+
for (let segment of path74) {
|
|
56755
57157
|
if (typeof segment === "string") {
|
|
56756
57158
|
if (node.type !== "object" || !Array.isArray(node.children)) {
|
|
56757
57159
|
return void 0;
|
|
@@ -57118,14 +57520,14 @@ var init_parser = __esm({
|
|
|
57118
57520
|
|
|
57119
57521
|
// ../../node_modules/jsonc-parser/lib/esm/impl/edit.js
|
|
57120
57522
|
function setProperty(text, originalPath, value, options) {
|
|
57121
|
-
const
|
|
57523
|
+
const path74 = originalPath.slice();
|
|
57122
57524
|
const errors = [];
|
|
57123
57525
|
const root = parseTree(text, errors);
|
|
57124
57526
|
let parent = void 0;
|
|
57125
57527
|
let lastSegment = void 0;
|
|
57126
|
-
while (
|
|
57127
|
-
lastSegment =
|
|
57128
|
-
parent = findNodeAtLocation(root,
|
|
57528
|
+
while (path74.length > 0) {
|
|
57529
|
+
lastSegment = path74.pop();
|
|
57530
|
+
parent = findNodeAtLocation(root, path74);
|
|
57129
57531
|
if (parent === void 0 && value !== void 0) {
|
|
57130
57532
|
if (typeof lastSegment === "string") {
|
|
57131
57533
|
value = { [lastSegment]: value };
|
|
@@ -57269,8 +57671,8 @@ var init_edit = __esm({
|
|
|
57269
57671
|
});
|
|
57270
57672
|
|
|
57271
57673
|
// ../../node_modules/jsonc-parser/lib/esm/main.js
|
|
57272
|
-
function modify(text,
|
|
57273
|
-
return setProperty(text,
|
|
57674
|
+
function modify(text, path74, value, options) {
|
|
57675
|
+
return setProperty(text, path74, value, options);
|
|
57274
57676
|
}
|
|
57275
57677
|
function applyEdits(text, edits) {
|
|
57276
57678
|
let sortedEdits = edits.slice(0).sort((a, b) => {
|
|
@@ -57352,20 +57754,20 @@ var init_main2 = __esm({
|
|
|
57352
57754
|
});
|
|
57353
57755
|
|
|
57354
57756
|
// ../../packages/sandbox-core/dist/plugin_check.js
|
|
57355
|
-
import * as
|
|
57757
|
+
import * as fs51 from "node:fs";
|
|
57356
57758
|
import * as os17 from "node:os";
|
|
57357
|
-
import * as
|
|
57759
|
+
import * as path59 from "node:path";
|
|
57358
57760
|
function defaultRepoPluginDir() {
|
|
57359
|
-
return
|
|
57761
|
+
return path59.join(sandboxRoot(), "plugins", PLUGIN_NAME);
|
|
57360
57762
|
}
|
|
57361
57763
|
function defaultClaudePluginsDir() {
|
|
57362
|
-
return
|
|
57764
|
+
return path59.join(os17.homedir(), ".claude", "plugins");
|
|
57363
57765
|
}
|
|
57364
57766
|
function repoPluginVersion(repoPluginDir) {
|
|
57365
|
-
const manifest =
|
|
57767
|
+
const manifest = path59.join(repoPluginDir, ".claude-plugin", "plugin.json");
|
|
57366
57768
|
let data;
|
|
57367
57769
|
try {
|
|
57368
|
-
data = JSON.parse(
|
|
57770
|
+
data = JSON.parse(fs51.readFileSync(manifest, "utf8"));
|
|
57369
57771
|
} catch {
|
|
57370
57772
|
return null;
|
|
57371
57773
|
}
|
|
@@ -57373,10 +57775,10 @@ function repoPluginVersion(repoPluginDir) {
|
|
|
57373
57775
|
return version ? String(version) : null;
|
|
57374
57776
|
}
|
|
57375
57777
|
function loadRegistry(claudePluginsDir) {
|
|
57376
|
-
const registry =
|
|
57778
|
+
const registry = path59.join(claudePluginsDir, "installed_plugins.json");
|
|
57377
57779
|
let data;
|
|
57378
57780
|
try {
|
|
57379
|
-
data = JSON.parse(
|
|
57781
|
+
data = JSON.parse(fs51.readFileSync(registry, "utf8"));
|
|
57380
57782
|
} catch {
|
|
57381
57783
|
return null;
|
|
57382
57784
|
}
|
|
@@ -57409,7 +57811,7 @@ function skillBodies(skillsDir) {
|
|
|
57409
57811
|
const bodies = /* @__PURE__ */ new Map();
|
|
57410
57812
|
let isDir6 = false;
|
|
57411
57813
|
try {
|
|
57412
|
-
isDir6 =
|
|
57814
|
+
isDir6 = fs51.statSync(skillsDir).isDirectory();
|
|
57413
57815
|
} catch {
|
|
57414
57816
|
isDir6 = false;
|
|
57415
57817
|
}
|
|
@@ -57418,14 +57820,14 @@ function skillBodies(skillsDir) {
|
|
|
57418
57820
|
}
|
|
57419
57821
|
let entries;
|
|
57420
57822
|
try {
|
|
57421
|
-
entries =
|
|
57823
|
+
entries = fs51.readdirSync(skillsDir, { withFileTypes: true });
|
|
57422
57824
|
} catch {
|
|
57423
57825
|
return bodies;
|
|
57424
57826
|
}
|
|
57425
57827
|
const names = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
57426
57828
|
for (const name of names) {
|
|
57427
57829
|
try {
|
|
57428
|
-
bodies.set(name,
|
|
57830
|
+
bodies.set(name, fs51.readFileSync(path59.join(skillsDir, name, "SKILL.md"), "utf8"));
|
|
57429
57831
|
} catch {
|
|
57430
57832
|
continue;
|
|
57431
57833
|
}
|
|
@@ -57459,7 +57861,7 @@ function pluginDriftWarnings(repoPluginDir, claudePluginsDir) {
|
|
|
57459
57861
|
}
|
|
57460
57862
|
let repoSkillsIsDir = false;
|
|
57461
57863
|
try {
|
|
57462
|
-
repoSkillsIsDir =
|
|
57864
|
+
repoSkillsIsDir = fs51.statSync(path59.join(repoPluginDir, "skills")).isDirectory();
|
|
57463
57865
|
} catch {
|
|
57464
57866
|
repoSkillsIsDir = false;
|
|
57465
57867
|
}
|
|
@@ -57480,7 +57882,7 @@ function pluginDriftWarnings(repoPluginDir, claudePluginsDir) {
|
|
|
57480
57882
|
`${PLUGIN_NAME} plugin is not installed \u2014 the ops skills (${PLUGIN_NAME}:diagnose, ${PLUGIN_NAME}:status, ${PLUGIN_NAME}:workspace, \u2026) will not surface in Claude Code sessions. Run \`w9r setup\` to install it (or use /plugin in Claude Code). The sb-* skills are unaffected \u2014 they are projected into each workspace by init-workspace.`
|
|
57481
57883
|
];
|
|
57482
57884
|
}
|
|
57483
|
-
const drift = skillDrift(
|
|
57885
|
+
const drift = skillDrift(path59.join(repoPluginDir, "skills"), path59.join(installPath, "skills"));
|
|
57484
57886
|
if (!drift.length) {
|
|
57485
57887
|
return [];
|
|
57486
57888
|
}
|
|
@@ -57506,15 +57908,15 @@ var init_plugin_check = __esm({
|
|
|
57506
57908
|
});
|
|
57507
57909
|
|
|
57508
57910
|
// ../../packages/sandbox-core/dist/test_run.js
|
|
57509
|
-
import * as
|
|
57911
|
+
import * as fs52 from "node:fs";
|
|
57510
57912
|
import { createRequire } from "node:module";
|
|
57511
|
-
import * as
|
|
57913
|
+
import * as path60 from "node:path";
|
|
57512
57914
|
function playwrightSearchDirs(workspace) {
|
|
57513
57915
|
const dirs = [workspace];
|
|
57514
57916
|
try {
|
|
57515
|
-
for (const entry of
|
|
57516
|
-
if (entry.isDirectory() &&
|
|
57517
|
-
dirs.push(
|
|
57917
|
+
for (const entry of fs52.readdirSync(workspace, { withFileTypes: true })) {
|
|
57918
|
+
if (entry.isDirectory() && fs52.existsSync(path60.join(workspace, entry.name, ".git"))) {
|
|
57919
|
+
dirs.push(path60.join(workspace, entry.name));
|
|
57518
57920
|
}
|
|
57519
57921
|
}
|
|
57520
57922
|
} catch {
|
|
@@ -57523,20 +57925,20 @@ function playwrightSearchDirs(workspace) {
|
|
|
57523
57925
|
}
|
|
57524
57926
|
function playwrightCwd(dirs) {
|
|
57525
57927
|
for (const dir of dirs) {
|
|
57526
|
-
if (!(
|
|
57928
|
+
if (!(fs52.existsSync(dir) && fs52.statSync(dir).isDirectory())) {
|
|
57527
57929
|
continue;
|
|
57528
57930
|
}
|
|
57529
|
-
if (CONFIG_NAMES.some((name) =>
|
|
57931
|
+
if (CONFIG_NAMES.some((name) => fs52.existsSync(path60.join(dir, name)))) {
|
|
57530
57932
|
return dir;
|
|
57531
57933
|
}
|
|
57532
57934
|
}
|
|
57533
57935
|
return null;
|
|
57534
57936
|
}
|
|
57535
57937
|
function noSuiteMessage(workspace) {
|
|
57536
|
-
const repoNames = playwrightSearchDirs(workspace).slice(1).map((d) =>
|
|
57938
|
+
const repoNames = playwrightSearchDirs(workspace).slice(1).map((d) => path60.basename(d));
|
|
57537
57939
|
let services = "";
|
|
57538
57940
|
try {
|
|
57539
|
-
services = /^SERVICES=(.*)$/m.exec(
|
|
57941
|
+
services = /^SERVICES=(.*)$/m.exec(fs52.readFileSync(path60.join(workspace, ".env"), "utf8"))?.[1] ?? "";
|
|
57540
57942
|
} catch {
|
|
57541
57943
|
}
|
|
57542
57944
|
const selected = services.split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -57550,8 +57952,8 @@ ${lines.join("\n")}` : "");
|
|
|
57550
57952
|
}
|
|
57551
57953
|
function installedPlaywrightVersion(cwd) {
|
|
57552
57954
|
try {
|
|
57553
|
-
const pkg = createRequire(
|
|
57554
|
-
return JSON.parse(
|
|
57955
|
+
const pkg = createRequire(path60.join(cwd, "package.json")).resolve("@playwright/test/package.json");
|
|
57956
|
+
return JSON.parse(fs52.readFileSync(pkg, "utf8")).version ?? null;
|
|
57555
57957
|
} catch {
|
|
57556
57958
|
return null;
|
|
57557
57959
|
}
|
|
@@ -57631,9 +58033,9 @@ var init_test_run = __esm({
|
|
|
57631
58033
|
});
|
|
57632
58034
|
|
|
57633
58035
|
// ../../packages/sandbox-core/dist/machine_setup.js
|
|
57634
|
-
import * as
|
|
58036
|
+
import * as fs53 from "node:fs";
|
|
57635
58037
|
import * as os18 from "node:os";
|
|
57636
|
-
import * as
|
|
58038
|
+
import * as path61 from "node:path";
|
|
57637
58039
|
function briefOutput(out, maxLines = 4) {
|
|
57638
58040
|
const lines = out.split("\n").map((l) => l.trimEnd()).filter((l) => l.trim() && !/^\s*[✔✓⣾⣽⣻⢿⡿⣟⣯⣷]/.test(l));
|
|
57639
58041
|
if (!lines.length)
|
|
@@ -57737,17 +58139,17 @@ function defaultInteractiveRunner(cmd) {
|
|
|
57737
58139
|
function readJson2(p) {
|
|
57738
58140
|
let data;
|
|
57739
58141
|
try {
|
|
57740
|
-
data = JSON.parse(
|
|
58142
|
+
data = JSON.parse(fs53.readFileSync(p, "utf8"));
|
|
57741
58143
|
} catch {
|
|
57742
58144
|
return null;
|
|
57743
58145
|
}
|
|
57744
58146
|
return typeof data === "object" && data !== null && !Array.isArray(data) ? data : null;
|
|
57745
58147
|
}
|
|
57746
58148
|
function knownMarketplaces(claudePluginsDir) {
|
|
57747
|
-
return readJson2(
|
|
58149
|
+
return readJson2(path61.join(claudePluginsDir, "known_marketplaces.json")) || {};
|
|
57748
58150
|
}
|
|
57749
58151
|
function installedVersion(claudePluginsDir, spec) {
|
|
57750
|
-
const registry = readJson2(
|
|
58152
|
+
const registry = readJson2(path61.join(claudePluginsDir, "installed_plugins.json")) || {};
|
|
57751
58153
|
const plugins = registry["plugins"];
|
|
57752
58154
|
if (typeof plugins !== "object" || plugins === null || Array.isArray(plugins)) {
|
|
57753
58155
|
return null;
|
|
@@ -57840,9 +58242,9 @@ function syncOptionalPlugin(spec, claudePluginsDir, runner, checkOnly, install)
|
|
|
57840
58242
|
}));
|
|
57841
58243
|
}
|
|
57842
58244
|
function syncRepo(reposRoot, spec, runner, checkOnly) {
|
|
57843
|
-
const dest =
|
|
58245
|
+
const dest = path61.join(reposRoot, spec.name);
|
|
57844
58246
|
const label = `repo ${spec.name}`;
|
|
57845
|
-
if (!
|
|
58247
|
+
if (!fs53.existsSync(dest)) {
|
|
57846
58248
|
if (checkOnly) {
|
|
57847
58249
|
return [
|
|
57848
58250
|
{
|
|
@@ -57861,7 +58263,7 @@ function syncRepo(reposRoot, spec, runner, checkOnly) {
|
|
|
57861
58263
|
const detail2 = notFound ? `${spec.url} is not reachable \u2014 it may not exist yet, or you may not be authenticated to GitHub (\`gh auth login\`, or check your git credential helper). ${spec.missingConsequence}` : `clone failed: ${briefOutput(out3)}`;
|
|
57862
58264
|
return [{ label, ok: false, optional: true, detail: detail2 }];
|
|
57863
58265
|
}
|
|
57864
|
-
if (!
|
|
58266
|
+
if (!fs53.existsSync(path61.join(dest, ".git"))) {
|
|
57865
58267
|
return [
|
|
57866
58268
|
{
|
|
57867
58269
|
label,
|
|
@@ -57894,8 +58296,8 @@ function syncPathDirs(checkOnly, dirs) {
|
|
|
57894
58296
|
continue;
|
|
57895
58297
|
const target = expandUser(value);
|
|
57896
58298
|
const label = `paths.${key}`;
|
|
57897
|
-
if (
|
|
57898
|
-
if (!
|
|
58299
|
+
if (fs53.existsSync(target)) {
|
|
58300
|
+
if (!fs53.statSync(target).isDirectory()) {
|
|
57899
58301
|
steps.push({ label, ok: false, detail: `${target} exists but is not a directory` });
|
|
57900
58302
|
} else {
|
|
57901
58303
|
steps.push({ label, ok: true, detail: target });
|
|
@@ -57907,7 +58309,7 @@ function syncPathDirs(checkOnly, dirs) {
|
|
|
57907
58309
|
continue;
|
|
57908
58310
|
}
|
|
57909
58311
|
try {
|
|
57910
|
-
|
|
58312
|
+
fs53.mkdirSync(target, { recursive: true });
|
|
57911
58313
|
steps.push({ label, ok: true, detail: `created ${target}` });
|
|
57912
58314
|
} catch (e) {
|
|
57913
58315
|
steps.push({ label, ok: false, detail: `could not create ${target}: ${e}` });
|
|
@@ -57916,7 +58318,7 @@ function syncPathDirs(checkOnly, dirs) {
|
|
|
57916
58318
|
return steps;
|
|
57917
58319
|
}
|
|
57918
58320
|
function syncRegistryConfigs(checkOnly, options = {}) {
|
|
57919
|
-
const legacyDir = options.legacyDir === void 0 ?
|
|
58321
|
+
const legacyDir = options.legacyDir === void 0 ? path61.join(sandboxRoot(), LEGACY_CONFIG_DIR) : options.legacyDir;
|
|
57920
58322
|
const files = [
|
|
57921
58323
|
[
|
|
57922
58324
|
"services.local.yaml",
|
|
@@ -57935,12 +58337,12 @@ function syncRegistryConfigs(checkOnly, options = {}) {
|
|
|
57935
58337
|
const steps = [];
|
|
57936
58338
|
for (const [name, dest, legacyName, content] of files) {
|
|
57937
58339
|
const label2 = `config ${name}`;
|
|
57938
|
-
if (
|
|
58340
|
+
if (fs53.existsSync(dest)) {
|
|
57939
58341
|
steps.push({ label: label2, ok: true, detail: `present at ${dest}` });
|
|
57940
58342
|
continue;
|
|
57941
58343
|
}
|
|
57942
|
-
const legacy = legacyDir && legacyName ?
|
|
57943
|
-
const migratable = legacy !== null &&
|
|
58344
|
+
const legacy = legacyDir && legacyName ? path61.join(legacyDir, legacyName) : null;
|
|
58345
|
+
const migratable = legacy !== null && fs53.existsSync(legacy);
|
|
57944
58346
|
if (checkOnly) {
|
|
57945
58347
|
steps.push({
|
|
57946
58348
|
label: label2,
|
|
@@ -57951,8 +58353,8 @@ function syncRegistryConfigs(checkOnly, options = {}) {
|
|
|
57951
58353
|
}
|
|
57952
58354
|
try {
|
|
57953
58355
|
if (migratable) {
|
|
57954
|
-
|
|
57955
|
-
|
|
58356
|
+
fs53.mkdirSync(path61.dirname(dest), { recursive: true });
|
|
58357
|
+
fs53.copyFileSync(legacy, dest);
|
|
57956
58358
|
steps.push({ label: label2, ok: true, detail: `migrated from ${legacy}` });
|
|
57957
58359
|
} else {
|
|
57958
58360
|
const result = seedFile(dest, content());
|
|
@@ -57964,7 +58366,7 @@ function syncRegistryConfigs(checkOnly, options = {}) {
|
|
|
57964
58366
|
}
|
|
57965
58367
|
const label = "config secrets.env";
|
|
57966
58368
|
const secrets = secretsPath();
|
|
57967
|
-
if (
|
|
58369
|
+
if (fs53.existsSync(secrets)) {
|
|
57968
58370
|
steps.push({ label, ok: true, detail: `present at ${secrets}` });
|
|
57969
58371
|
} else if (checkOnly) {
|
|
57970
58372
|
steps.push({ label, ok: true, detail: `absent \u2014 would write a template to ${secrets}` });
|
|
@@ -57985,11 +58387,11 @@ function syncRegistryConfigs(checkOnly, options = {}) {
|
|
|
57985
58387
|
function resolveCertScript(startDir) {
|
|
57986
58388
|
let dir = startDir ?? sandboxRoot();
|
|
57987
58389
|
for (let i = 0; i < 5; i++) {
|
|
57988
|
-
const candidate =
|
|
57989
|
-
if (
|
|
58390
|
+
const candidate = path61.join(dir, "bin", "generate-certs.sh");
|
|
58391
|
+
if (fs53.existsSync(candidate)) {
|
|
57990
58392
|
return candidate;
|
|
57991
58393
|
}
|
|
57992
|
-
const parent =
|
|
58394
|
+
const parent = path61.dirname(dir);
|
|
57993
58395
|
if (parent === dir)
|
|
57994
58396
|
break;
|
|
57995
58397
|
dir = parent;
|
|
@@ -58102,15 +58504,15 @@ ${JIRA_CONFIG_SNIPPET}`
|
|
|
58102
58504
|
];
|
|
58103
58505
|
}
|
|
58104
58506
|
function vscodeSettingsPath() {
|
|
58105
|
-
return process.platform === "darwin" ?
|
|
58507
|
+
return process.platform === "darwin" ? path61.join(os18.homedir(), "Library", "Application Support", "Code", "User", "settings.json") : path61.join(os18.homedir(), ".config", "Code", "User", "settings.json");
|
|
58106
58508
|
}
|
|
58107
|
-
function editorSchemaState(settingsPath = vscodeSettingsPath(), schemasDir =
|
|
58509
|
+
function editorSchemaState(settingsPath = vscodeSettingsPath(), schemasDir = path61.join(sandboxRoot(), "infra", "schemas")) {
|
|
58108
58510
|
const state = { present: false, set: {}, drop: [], text: "{}" };
|
|
58109
|
-
if (!
|
|
58511
|
+
if (!fs53.existsSync(path61.dirname(settingsPath)))
|
|
58110
58512
|
return state;
|
|
58111
58513
|
state.present = true;
|
|
58112
|
-
if (
|
|
58113
|
-
state.text =
|
|
58514
|
+
if (fs53.existsSync(settingsPath))
|
|
58515
|
+
state.text = fs53.readFileSync(settingsPath, "utf8");
|
|
58114
58516
|
const errors = [];
|
|
58115
58517
|
const data = parse7(state.text, errors, { allowTrailingComma: true });
|
|
58116
58518
|
if (errors.length) {
|
|
@@ -58120,7 +58522,7 @@ function editorSchemaState(settingsPath = vscodeSettingsPath(), schemasDir = pat
|
|
|
58120
58522
|
const mapped = data?.["yaml.schemas"] ?? {};
|
|
58121
58523
|
const covers = (v, glob) => v === glob || Array.isArray(v) && v.includes(glob);
|
|
58122
58524
|
for (const [file, glob] of Object.entries(EDITOR_SCHEMAS)) {
|
|
58123
|
-
const want =
|
|
58525
|
+
const want = path61.join(schemasDir, file);
|
|
58124
58526
|
if (!covers(mapped[want], glob))
|
|
58125
58527
|
state.set[want] = glob;
|
|
58126
58528
|
for (const [key, v] of Object.entries(mapped)) {
|
|
@@ -58165,11 +58567,11 @@ function syncEditorSchemas(checkOnly, install, settingsPath = vscodeSettingsPath
|
|
|
58165
58567
|
for (const [key, glob] of Object.entries(state.set)) {
|
|
58166
58568
|
text = applyEdits(text, modify(text, ["yaml.schemas", key], glob, fmt));
|
|
58167
58569
|
}
|
|
58168
|
-
|
|
58570
|
+
fs53.writeFileSync(settingsPath, text);
|
|
58169
58571
|
return [{ label, ok: true, optional: true, detail: `mapped in ${settingsPath}` }];
|
|
58170
58572
|
}
|
|
58171
58573
|
function claudeUserSettingsPath() {
|
|
58172
|
-
return
|
|
58574
|
+
return path61.join(os18.homedir(), ".claude", "settings.json");
|
|
58173
58575
|
}
|
|
58174
58576
|
function claudeStandardsDirState(settingsPath = claudeUserSettingsPath(), dir = standardsDirPath()) {
|
|
58175
58577
|
const state = {
|
|
@@ -58178,12 +58580,12 @@ function claudeStandardsDirState(settingsPath = claudeUserSettingsPath(), dir =
|
|
|
58178
58580
|
granted: false,
|
|
58179
58581
|
text: "{}"
|
|
58180
58582
|
};
|
|
58181
|
-
state.present =
|
|
58182
|
-
state.dirMissing = !(
|
|
58583
|
+
state.present = fs53.existsSync(path61.dirname(settingsPath));
|
|
58584
|
+
state.dirMissing = !(fs53.existsSync(dir) && fs53.statSync(dir).isDirectory());
|
|
58183
58585
|
if (!state.present || state.dirMissing)
|
|
58184
58586
|
return state;
|
|
58185
|
-
if (
|
|
58186
|
-
state.text =
|
|
58587
|
+
if (fs53.existsSync(settingsPath))
|
|
58588
|
+
state.text = fs53.readFileSync(settingsPath, "utf8");
|
|
58187
58589
|
const errors = [];
|
|
58188
58590
|
const data = parse7(state.text, errors, { allowTrailingComma: true });
|
|
58189
58591
|
if (errors.length) {
|
|
@@ -58195,8 +58597,8 @@ function claudeStandardsDirState(settingsPath = claudeUserSettingsPath(), dir =
|
|
|
58195
58597
|
state.error = "permissions.additionalDirectories is not an array";
|
|
58196
58598
|
return state;
|
|
58197
58599
|
}
|
|
58198
|
-
const want =
|
|
58199
|
-
state.granted = (dirs ?? []).some((d) =>
|
|
58600
|
+
const want = path61.resolve(dir);
|
|
58601
|
+
state.granted = (dirs ?? []).some((d) => path61.resolve(expandUser(String(d))) === want);
|
|
58200
58602
|
return state;
|
|
58201
58603
|
}
|
|
58202
58604
|
function syncClaudeStandardsDir(checkOnly, install, settingsPath = claudeUserSettingsPath(), dir = standardsDirPath()) {
|
|
@@ -58226,18 +58628,18 @@ function syncClaudeStandardsDir(checkOnly, install, settingsPath = claudeUserSet
|
|
|
58226
58628
|
...fmt,
|
|
58227
58629
|
isArrayInsertion: true
|
|
58228
58630
|
}) : modify(state.text, ["permissions", "additionalDirectories"], [dir], fmt);
|
|
58229
|
-
|
|
58631
|
+
fs53.writeFileSync(settingsPath, applyEdits(state.text, edits));
|
|
58230
58632
|
return [{ label, ok: true, optional: true, detail: `granted in ${settingsPath}` }];
|
|
58231
58633
|
}
|
|
58232
58634
|
function vscodeExtensionState(runner = defaultRunner, root = sandboxRoot()) {
|
|
58233
58635
|
const [rc, out] = runner(["code", "--list-extensions", "--show-versions"]);
|
|
58234
58636
|
const prefix = `${VSCODE_EXTENSION_ID}@`;
|
|
58235
58637
|
const line = rc === 0 ? out.split("\n").find((l) => l.trim().toLowerCase().startsWith(prefix)) : void 0;
|
|
58236
|
-
const vsix =
|
|
58638
|
+
const vsix = path61.join(root, BUNDLED_VSIX_REL);
|
|
58237
58639
|
return {
|
|
58238
58640
|
cli: rc === 0,
|
|
58239
58641
|
installed: line ? line.trim().slice(prefix.length) : null,
|
|
58240
|
-
bundled:
|
|
58642
|
+
bundled: fs53.existsSync(vsix) ? { path: vsix, version: readJson2(path61.join(root, "package.json"))?.version } : null
|
|
58241
58643
|
};
|
|
58242
58644
|
}
|
|
58243
58645
|
function syncVscodeExtension(checkOnly, install, state = vscodeExtensionState(), runner = defaultRunner) {
|
|
@@ -58391,7 +58793,7 @@ function syncPlaywright(runner, checkOnly, interactive) {
|
|
|
58391
58793
|
const dirs = [...out.matchAll(/Install location:\s*(\S.*)$/gm)].map((m) => m[1].trim());
|
|
58392
58794
|
if (rc2 !== 0 || dirs.length === 0)
|
|
58393
58795
|
return null;
|
|
58394
|
-
return dirs.filter((d) => !
|
|
58796
|
+
return dirs.filter((d) => !fs53.existsSync(path61.join(d, "INSTALLATION_COMPLETE")));
|
|
58395
58797
|
};
|
|
58396
58798
|
const before = missingDirs();
|
|
58397
58799
|
if (before === null) {
|
|
@@ -58435,8 +58837,8 @@ function syncPlaywright(runner, checkOnly, interactive) {
|
|
|
58435
58837
|
async function runSetup(options = {}) {
|
|
58436
58838
|
const checkOnly = options.checkOnly ?? false;
|
|
58437
58839
|
const reposRoot = options.reposRoot ?? getReposRoot();
|
|
58438
|
-
const claudePluginsDir = options.claudePluginsDir ??
|
|
58439
|
-
const repoPluginDir = options.repoPluginDir ??
|
|
58840
|
+
const claudePluginsDir = options.claudePluginsDir ?? path61.join(os18.homedir(), ".claude", "plugins");
|
|
58841
|
+
const repoPluginDir = options.repoPluginDir ?? path61.join(sandboxRoot(), "plugins", PLUGIN_NAME);
|
|
58440
58842
|
const runner = options.runner ?? defaultRunner;
|
|
58441
58843
|
const jira2 = options.jira ?? loadJira();
|
|
58442
58844
|
const steps = [];
|
|
@@ -58480,7 +58882,7 @@ async function runSetup(options = {}) {
|
|
|
58480
58882
|
detail: "no workspace repo configured and `gh` could not name one \u2014 set overrides.workspace_repo (or run `gh auth login`, then `sandbox setup`). Workspaces will fall back to plain directories."
|
|
58481
58883
|
});
|
|
58482
58884
|
}
|
|
58483
|
-
if (options.demo ||
|
|
58885
|
+
if (options.demo || fs53.existsSync(path61.join(reposRoot, DEMO_SERVICES_REPO))) {
|
|
58484
58886
|
steps.push(...syncRepo(reposRoot, DEMO_SERVICES_SPEC, runner, checkOnly));
|
|
58485
58887
|
}
|
|
58486
58888
|
steps.push(...syncRegistry({ reposRoot, checkOnly }));
|
|
@@ -58554,7 +58956,7 @@ var init_machine_setup = __esm({
|
|
|
58554
58956
|
url: DEMO_SERVICES_REPO_URL,
|
|
58555
58957
|
missingConsequence: "The demo backend/frontend services will not start until it is cloned there."
|
|
58556
58958
|
};
|
|
58557
|
-
LEGACY_CONFIG_DIR =
|
|
58959
|
+
LEGACY_CONFIG_DIR = path61.join("infra", "config");
|
|
58558
58960
|
JIRA_TOKEN_URL = "https://id.atlassian.com/manage-profile/security/api-tokens";
|
|
58559
58961
|
JIRA_REQUIRED_KEYS = ["base_url", "email", "api_token"];
|
|
58560
58962
|
JIRA_CONFIG_SNIPPET = "jira:\n base_url: https://your-org.atlassian.net\n email: <your-atlassian-login-email>\n api_token: PASTE_TOKEN_HERE";
|
|
@@ -58563,13 +58965,13 @@ var init_machine_setup = __esm({
|
|
|
58563
58965
|
"workspace.schema.json": "**/.sandbox/workspace.yml"
|
|
58564
58966
|
};
|
|
58565
58967
|
VSCODE_EXTENSION_ID = "jbbz.werkmeister-vscode";
|
|
58566
|
-
BUNDLED_VSIX_REL =
|
|
58968
|
+
BUNDLED_VSIX_REL = path61.join("dist", "sandbox-workspaces.vsix");
|
|
58567
58969
|
}
|
|
58568
58970
|
});
|
|
58569
58971
|
|
|
58570
58972
|
// ../../packages/sandbox-core/dist/mcp_gen.js
|
|
58571
|
-
import * as
|
|
58572
|
-
import * as
|
|
58973
|
+
import * as fs54 from "node:fs";
|
|
58974
|
+
import * as path62 from "node:path";
|
|
58573
58975
|
function resolveWorkspaceMcpServers(presentArchetypes2, archetypes) {
|
|
58574
58976
|
const names = /* @__PURE__ */ new Set();
|
|
58575
58977
|
for (const archName of Object.keys(presentArchetypes2)) {
|
|
@@ -58610,12 +59012,12 @@ function substituteVars(value, vars2) {
|
|
|
58610
59012
|
return value;
|
|
58611
59013
|
}
|
|
58612
59014
|
function writeWorkspaceMcp(workspaceRoot, serverNames, definitions, vars2 = {}) {
|
|
58613
|
-
const mcpPath =
|
|
58614
|
-
const isFile4 =
|
|
59015
|
+
const mcpPath = path62.join(workspaceRoot, ".mcp.json");
|
|
59016
|
+
const isFile4 = fs54.existsSync(mcpPath) && fs54.statSync(mcpPath).isFile();
|
|
58615
59017
|
let config = {};
|
|
58616
59018
|
if (isFile4) {
|
|
58617
59019
|
try {
|
|
58618
|
-
const existing2 = JSON.parse(
|
|
59020
|
+
const existing2 = JSON.parse(fs54.readFileSync(mcpPath, "utf8"));
|
|
58619
59021
|
if (typeof existing2 === "object" && existing2 !== null && !Array.isArray(existing2)) {
|
|
58620
59022
|
config = existing2;
|
|
58621
59023
|
} else {
|
|
@@ -58646,7 +59048,7 @@ function writeWorkspaceMcp(workspaceRoot, serverNames, definitions, vars2 = {})
|
|
|
58646
59048
|
if (legacy?.headers?.["X-Sandbox-Feature"] !== void 0)
|
|
58647
59049
|
delete servers[LEGACY_SERVER];
|
|
58648
59050
|
Object.assign(servers, wanted);
|
|
58649
|
-
|
|
59051
|
+
fs54.writeFileSync(mcpPath, JSON.stringify(config, null, 2) + "\n");
|
|
58650
59052
|
return mcpPath;
|
|
58651
59053
|
}
|
|
58652
59054
|
var LEGACY_SERVER;
|
|
@@ -58659,9 +59061,9 @@ var init_mcp_gen = __esm({
|
|
|
58659
59061
|
});
|
|
58660
59062
|
|
|
58661
59063
|
// ../../packages/sandbox-core/dist/workspace_init.js
|
|
58662
|
-
import * as
|
|
59064
|
+
import * as fs55 from "node:fs";
|
|
58663
59065
|
import * as os19 from "node:os";
|
|
58664
|
-
import * as
|
|
59066
|
+
import * as path63 from "node:path";
|
|
58665
59067
|
function fail2(message) {
|
|
58666
59068
|
error(message);
|
|
58667
59069
|
throw new WorkspaceInitError(message);
|
|
@@ -58684,7 +59086,7 @@ async function resolveIssueCopy(root, issueId, opts) {
|
|
|
58684
59086
|
}
|
|
58685
59087
|
function isDir4(p) {
|
|
58686
59088
|
try {
|
|
58687
|
-
return
|
|
59089
|
+
return fs55.statSync(p).isDirectory();
|
|
58688
59090
|
} catch {
|
|
58689
59091
|
return false;
|
|
58690
59092
|
}
|
|
@@ -58701,26 +59103,26 @@ function splitMultiValue(value) {
|
|
|
58701
59103
|
return value.split(",").map((s) => s.trim()).filter((s) => s);
|
|
58702
59104
|
}
|
|
58703
59105
|
function hasWorkspaceConfig(workspaceRoot, override) {
|
|
58704
|
-
if (override &&
|
|
59106
|
+
if (override && fs55.existsSync(expandUserPath(override))) {
|
|
58705
59107
|
return true;
|
|
58706
59108
|
}
|
|
58707
59109
|
const envOverride = envVar("WORKSPACE_CONFIG");
|
|
58708
|
-
if (envOverride &&
|
|
59110
|
+
if (envOverride && fs55.existsSync(expandUserPath(envOverride))) {
|
|
58709
59111
|
return true;
|
|
58710
59112
|
}
|
|
58711
59113
|
const sandbox = dotDir(workspaceRoot);
|
|
58712
|
-
return _WORKSPACE_CONFIG_NAMES.some((name) =>
|
|
59114
|
+
return _WORKSPACE_CONFIG_NAMES.some((name) => fs55.existsSync(path63.join(sandbox, name)));
|
|
58713
59115
|
}
|
|
58714
59116
|
function writeWorkspaceConfig(workspaceRoot, resolved) {
|
|
58715
59117
|
if (Object.keys(resolved).length === 0) {
|
|
58716
59118
|
return;
|
|
58717
59119
|
}
|
|
58718
59120
|
const sandboxDir = dotDir(workspaceRoot);
|
|
58719
|
-
const target =
|
|
58720
|
-
const legacies = _WORKSPACE_CONFIG_NAMES.slice(1).map((name) =>
|
|
59121
|
+
const target = path63.join(sandboxDir, _WORKSPACE_CONFIG_NAMES[0]);
|
|
59122
|
+
const legacies = _WORKSPACE_CONFIG_NAMES.slice(1).map((name) => path63.join(sandboxDir, name));
|
|
58721
59123
|
let existing2 = {};
|
|
58722
59124
|
for (const source of [target, ...legacies]) {
|
|
58723
|
-
if (
|
|
59125
|
+
if (fs55.existsSync(source) && fs55.statSync(source).isFile()) {
|
|
58724
59126
|
try {
|
|
58725
59127
|
const data = loadWorkspaceConfig(source);
|
|
58726
59128
|
if (typeof data === "object" && data !== null && !Array.isArray(data)) {
|
|
@@ -58742,21 +59144,23 @@ function writeWorkspaceConfig(workspaceRoot, resolved) {
|
|
|
58742
59144
|
}
|
|
58743
59145
|
if (field === "autonomous" || field === "auto_stop")
|
|
58744
59146
|
value = value === "true";
|
|
59147
|
+
if (field === "auto_stop" && value === false && !(field in existing2))
|
|
59148
|
+
continue;
|
|
58745
59149
|
merged[field] = value;
|
|
58746
59150
|
}
|
|
58747
59151
|
}
|
|
58748
59152
|
const body = YAML8.stringify(merged, { sortMapEntries: true, blockQuote: "literal" });
|
|
58749
59153
|
const serialized = _WORKSPACE_CONFIG_HEADER + body;
|
|
58750
|
-
if (
|
|
59154
|
+
if (fs55.existsSync(target) && fs55.readFileSync(target, "utf8") === serialized) {
|
|
58751
59155
|
return;
|
|
58752
59156
|
}
|
|
58753
|
-
|
|
58754
|
-
|
|
59157
|
+
fs55.mkdirSync(sandboxDir, { recursive: true });
|
|
59158
|
+
fs55.writeFileSync(target, serialized);
|
|
58755
59159
|
success(`Persisted inputs to ${target}`);
|
|
58756
59160
|
for (const legacy of legacies) {
|
|
58757
|
-
if (
|
|
58758
|
-
|
|
58759
|
-
info(`Removed legacy ${
|
|
59161
|
+
if (fs55.existsSync(legacy)) {
|
|
59162
|
+
fs55.unlinkSync(legacy);
|
|
59163
|
+
info(`Removed legacy ${path63.basename(legacy)} (superseded by workspace.yml)`);
|
|
58760
59164
|
}
|
|
58761
59165
|
}
|
|
58762
59166
|
}
|
|
@@ -58774,16 +59178,16 @@ function resolveReposRoot() {
|
|
|
58774
59178
|
function resolveWorkspacesRoot() {
|
|
58775
59179
|
const configured = loadPaths()["workspaces_root"];
|
|
58776
59180
|
if (configured) {
|
|
58777
|
-
return
|
|
59181
|
+
return path63.resolve(expandUser(configured));
|
|
58778
59182
|
}
|
|
58779
|
-
return
|
|
59183
|
+
return path63.resolve(path63.join(os19.homedir(), "workspace"));
|
|
58780
59184
|
}
|
|
58781
59185
|
function createWorkspaceRoot(workspaceDir, branch, force) {
|
|
58782
59186
|
const reposRoot = resolveReposRoot();
|
|
58783
|
-
const rootRepoSrc =
|
|
59187
|
+
const rootRepoSrc = path63.join(reposRoot, WORKSPACE_ROOT_REPO);
|
|
58784
59188
|
info(`Workspace: ${workspaceDir}`);
|
|
58785
59189
|
info(`Branch: ${branch}`);
|
|
58786
|
-
if (
|
|
59190
|
+
if (fs55.existsSync(path63.join(rootRepoSrc, ".git"))) {
|
|
58787
59191
|
try {
|
|
58788
59192
|
createWorktree(rootRepoSrc, workspaceDir, branch, { force });
|
|
58789
59193
|
} catch (exc) {
|
|
@@ -58792,11 +59196,11 @@ function createWorkspaceRoot(workspaceDir, branch, force) {
|
|
|
58792
59196
|
success(`Created workspace root (worktree of ${WORKSPACE_ROOT_REPO}) at ${workspaceDir}`);
|
|
58793
59197
|
} else {
|
|
58794
59198
|
warn(`No '${WORKSPACE_ROOT_REPO}' repo at ${rootRepoSrc} \u2014 workspace root will be a plain directory (no git history for notes/config).`);
|
|
58795
|
-
|
|
59199
|
+
fs55.mkdirSync(workspaceDir, { recursive: true });
|
|
58796
59200
|
}
|
|
58797
59201
|
}
|
|
58798
59202
|
function commitWorkspaceState(workspaceDir, workspaceName) {
|
|
58799
|
-
if (!
|
|
59203
|
+
if (!fs55.existsSync(path63.join(workspaceDir, ".git"))) {
|
|
58800
59204
|
return;
|
|
58801
59205
|
}
|
|
58802
59206
|
try {
|
|
@@ -58836,9 +59240,9 @@ function candidateWorkspaceRoots() {
|
|
|
58836
59240
|
const roots = [];
|
|
58837
59241
|
const configured = loadPaths()["workspaces_root"];
|
|
58838
59242
|
if (configured) {
|
|
58839
|
-
roots.push(
|
|
59243
|
+
roots.push(path63.resolve(expandUserPath(configured)));
|
|
58840
59244
|
}
|
|
58841
|
-
roots.push(
|
|
59245
|
+
roots.push(path63.join(os19.homedir(), "workspace"));
|
|
58842
59246
|
const seen = /* @__PURE__ */ new Set();
|
|
58843
59247
|
const unique = [];
|
|
58844
59248
|
for (const r of roots) {
|
|
@@ -58850,20 +59254,20 @@ function candidateWorkspaceRoots() {
|
|
|
58850
59254
|
return unique;
|
|
58851
59255
|
}
|
|
58852
59256
|
function findWorkspaceRoot2() {
|
|
58853
|
-
const cwd =
|
|
59257
|
+
const cwd = path63.resolve(process.cwd());
|
|
58854
59258
|
for (const root of candidateWorkspaceRoots()) {
|
|
58855
|
-
const rel =
|
|
58856
|
-
if (rel === ".." || rel.startsWith(".." +
|
|
59259
|
+
const rel = path63.relative(root, cwd);
|
|
59260
|
+
if (rel === ".." || rel.startsWith(".." + path63.sep) || path63.isAbsolute(rel)) {
|
|
58857
59261
|
continue;
|
|
58858
59262
|
}
|
|
58859
59263
|
if (rel === "") {
|
|
58860
59264
|
continue;
|
|
58861
59265
|
}
|
|
58862
|
-
return
|
|
59266
|
+
return path63.join(root, rel.split(path63.sep)[0]);
|
|
58863
59267
|
}
|
|
58864
59268
|
for (const directory of [cwd, ...ancestors(cwd)]) {
|
|
58865
|
-
if (
|
|
58866
|
-
return
|
|
59269
|
+
if (fs55.existsSync(path63.join(directory, ".git"))) {
|
|
59270
|
+
return path63.dirname(directory);
|
|
58867
59271
|
}
|
|
58868
59272
|
}
|
|
58869
59273
|
const rootsDisplay = candidateWorkspaceRoots().join(", ");
|
|
@@ -58874,7 +59278,7 @@ function ancestors(p) {
|
|
|
58874
59278
|
const result = [];
|
|
58875
59279
|
let cur = p;
|
|
58876
59280
|
while (true) {
|
|
58877
|
-
const parent =
|
|
59281
|
+
const parent = path63.dirname(cur);
|
|
58878
59282
|
if (parent === cur) {
|
|
58879
59283
|
break;
|
|
58880
59284
|
}
|
|
@@ -58893,16 +59297,16 @@ function getBranch(p) {
|
|
|
58893
59297
|
return result2.stdout.trim() || "unknown";
|
|
58894
59298
|
}
|
|
58895
59299
|
function discoverRepos(workspaceRoot) {
|
|
58896
|
-
return repoDirs(workspaceRoot).map((d) => [
|
|
59300
|
+
return repoDirs(workspaceRoot).map((d) => [path63.basename(d), getBranch(d)]);
|
|
58897
59301
|
}
|
|
58898
59302
|
function workspaceBranch(workspaceRoot) {
|
|
58899
|
-
if (
|
|
59303
|
+
if (fs55.existsSync(path63.join(workspaceRoot, ".git"))) {
|
|
58900
59304
|
const branch = getBranch(workspaceRoot);
|
|
58901
59305
|
if (branch && branch !== "unknown") {
|
|
58902
59306
|
return branch;
|
|
58903
59307
|
}
|
|
58904
59308
|
}
|
|
58905
|
-
return `${getuser()}/${
|
|
59309
|
+
return `${getuser()}/${path63.basename(workspaceRoot)}`;
|
|
58906
59310
|
}
|
|
58907
59311
|
function reconcileWorktrees(workspaceRoot, desiredRepos, force) {
|
|
58908
59312
|
const reposRoot = getReposRoot();
|
|
@@ -58911,12 +59315,12 @@ function reconcileWorktrees(workspaceRoot, desiredRepos, force) {
|
|
|
58911
59315
|
const present = new Set(discoverRepos(workspaceRoot).map(([name]) => name));
|
|
58912
59316
|
const toCreate = desired.filter((r) => !present.has(r));
|
|
58913
59317
|
const toRemove = [...present].filter((r) => !desiredSet.has(r)).sort();
|
|
58914
|
-
const missingSrc = toCreate.filter((r) => !isDir4(
|
|
59318
|
+
const missingSrc = toCreate.filter((r) => !isDir4(path63.join(reposRoot, r)));
|
|
58915
59319
|
if (missingSrc.length) {
|
|
58916
59320
|
fail2(`Source repo(s) not found under ${reposRoot}: ${missingSrc.join(", ")}. Clone them there (or fix \`repos\` in .sandbox/workspace.yml) and rerun.`);
|
|
58917
59321
|
}
|
|
58918
59322
|
if (toRemove.length && !force) {
|
|
58919
|
-
const dirty = toRemove.filter((r) => worktreeIsDirty(
|
|
59323
|
+
const dirty = toRemove.filter((r) => worktreeIsDirty(path63.join(workspaceRoot, r)));
|
|
58920
59324
|
if (dirty.length) {
|
|
58921
59325
|
fail2(`Refusing to remove worktree(s) with uncommitted changes: ${dirty.join(", ")}. Commit or stash them, or rerun with --force to discard them.`);
|
|
58922
59326
|
}
|
|
@@ -58926,7 +59330,7 @@ function reconcileWorktrees(workspaceRoot, desiredRepos, force) {
|
|
|
58926
59330
|
}
|
|
58927
59331
|
const branch = workspaceBranch(workspaceRoot);
|
|
58928
59332
|
for (const repo of toRemove) {
|
|
58929
|
-
const [ok, message] = removeWorktree(
|
|
59333
|
+
const [ok, message] = removeWorktree(path63.join(reposRoot, repo), path63.join(workspaceRoot, repo), branch);
|
|
58930
59334
|
if (ok) {
|
|
58931
59335
|
success(`Removed worktree '${repo}' (no longer in workspace.yml).`);
|
|
58932
59336
|
if (message) {
|
|
@@ -58939,8 +59343,8 @@ function reconcileWorktrees(workspaceRoot, desiredRepos, force) {
|
|
|
58939
59343
|
const created = [];
|
|
58940
59344
|
try {
|
|
58941
59345
|
for (const repo of toCreate) {
|
|
58942
|
-
const src =
|
|
58943
|
-
const dest =
|
|
59346
|
+
const src = path63.join(reposRoot, repo);
|
|
59347
|
+
const dest = path63.join(workspaceRoot, repo);
|
|
58944
59348
|
createWorktree(src, dest, branch, { force });
|
|
58945
59349
|
created.push([dest, src]);
|
|
58946
59350
|
}
|
|
@@ -58968,10 +59372,10 @@ function resolveTask(argsTask, workspaceName, readStdinTask2) {
|
|
|
58968
59372
|
return [slugToTask(workspaceName), {}];
|
|
58969
59373
|
}
|
|
58970
59374
|
function warnIfExisting(claudeMdPath) {
|
|
58971
|
-
if (!
|
|
59375
|
+
if (!fs55.existsSync(claudeMdPath)) {
|
|
58972
59376
|
return;
|
|
58973
59377
|
}
|
|
58974
|
-
const existing2 =
|
|
59378
|
+
const existing2 = fs55.readFileSync(claudeMdPath, "utf8");
|
|
58975
59379
|
if (existing2.includes("## Layout")) {
|
|
58976
59380
|
warn("CLAUDE.md already initialised \u2014 overwriting with fresh content.");
|
|
58977
59381
|
} else {
|
|
@@ -58994,7 +59398,7 @@ function logMeta(task, meta) {
|
|
|
58994
59398
|
}
|
|
58995
59399
|
async function initWorkspace(args, workspaceRootOverride, opts = {}) {
|
|
58996
59400
|
const workspaceRoot = workspaceRootOverride ?? findWorkspaceRoot2();
|
|
58997
|
-
const workspaceName =
|
|
59401
|
+
const workspaceName = path63.basename(workspaceRoot);
|
|
58998
59402
|
info(`Workspace root: ${workspaceRoot}`);
|
|
58999
59403
|
const inputs = resolveInputs(args, workspaceRoot);
|
|
59000
59404
|
const strategy = strategyById(parseStrategyId(inputs["context_strategy"]));
|
|
@@ -59023,7 +59427,7 @@ async function initWorkspace(args, workspaceRootOverride, opts = {}) {
|
|
|
59023
59427
|
}
|
|
59024
59428
|
reconcileWorktrees(workspaceRoot, desiredRepos, Boolean(args["force"]));
|
|
59025
59429
|
}
|
|
59026
|
-
const claudeMdPath =
|
|
59430
|
+
const claudeMdPath = path63.join(workspaceRoot, "CLAUDE.md");
|
|
59027
59431
|
warnIfExisting(claudeMdPath);
|
|
59028
59432
|
info("Scanning for git repos...");
|
|
59029
59433
|
const repos = discoverRepos(workspaceRoot);
|
|
@@ -59080,7 +59484,7 @@ async function initWorkspace(args, workspaceRootOverride, opts = {}) {
|
|
|
59080
59484
|
for (const warning of pluginDriftWarnings()) {
|
|
59081
59485
|
warn(warning);
|
|
59082
59486
|
}
|
|
59083
|
-
const envPath =
|
|
59487
|
+
const envPath = path63.join(workspaceRoot, ".env");
|
|
59084
59488
|
seedWorkspaceMetadataEnv(envPath, meta);
|
|
59085
59489
|
success(`Wrote ${envPath}`);
|
|
59086
59490
|
const feature2 = (meta["FEATURE"] ?? "").trim();
|
|
@@ -59139,8 +59543,8 @@ async function provisionWorkspace(resolved, opts = {}) {
|
|
|
59139
59543
|
fail2(`Workspace name '${workspaceName}' is not a valid slug (expected lowercase letters, digits, and single hyphens).`);
|
|
59140
59544
|
}
|
|
59141
59545
|
const branch = buildBranchName(os19.userInfo().username, workspaceName);
|
|
59142
|
-
const workspaceDir =
|
|
59143
|
-
if (
|
|
59546
|
+
const workspaceDir = path63.join(resolveWorkspacesRoot(), workspaceName);
|
|
59547
|
+
if (fs55.existsSync(workspaceDir)) {
|
|
59144
59548
|
info(`Workspace already exists: ${workspaceDir} \u2014 re-running init-workspace.`);
|
|
59145
59549
|
} else {
|
|
59146
59550
|
const repos = await selectRepos(resolved.repos, resolved.services);
|
|
@@ -59297,21 +59701,21 @@ __export(sandbox_exports, {
|
|
|
59297
59701
|
import { execFile as execFile2, spawn as spawn8 } from "node:child_process";
|
|
59298
59702
|
import { EventEmitter } from "node:events";
|
|
59299
59703
|
import { createReadStream } from "node:fs";
|
|
59300
|
-
import
|
|
59704
|
+
import fs56 from "node:fs/promises";
|
|
59301
59705
|
import os20 from "node:os";
|
|
59302
|
-
import
|
|
59706
|
+
import path64 from "node:path";
|
|
59303
59707
|
import readline5 from "node:readline";
|
|
59304
59708
|
import { promisify } from "node:util";
|
|
59305
59709
|
async function isDir5(p) {
|
|
59306
59710
|
try {
|
|
59307
|
-
return (await
|
|
59711
|
+
return (await fs56.stat(p)).isDirectory();
|
|
59308
59712
|
} catch {
|
|
59309
59713
|
return false;
|
|
59310
59714
|
}
|
|
59311
59715
|
}
|
|
59312
59716
|
async function isFile3(p) {
|
|
59313
59717
|
try {
|
|
59314
|
-
return (await
|
|
59718
|
+
return (await fs56.stat(p)).isFile();
|
|
59315
59719
|
} catch {
|
|
59316
59720
|
return false;
|
|
59317
59721
|
}
|
|
@@ -59322,23 +59726,23 @@ async function resolveSandboxBin(opts = {}) {
|
|
|
59322
59726
|
return explicit;
|
|
59323
59727
|
const root = envVar("ROOT");
|
|
59324
59728
|
if (root) {
|
|
59325
|
-
const cand =
|
|
59729
|
+
const cand = path64.join(root, "sandbox");
|
|
59326
59730
|
if (await isExecutable(cand))
|
|
59327
59731
|
return cand;
|
|
59328
59732
|
}
|
|
59329
59733
|
let dir = opts.from ?? import.meta.dirname;
|
|
59330
59734
|
for (let i = 0; i < 8; i++) {
|
|
59331
|
-
const cand =
|
|
59332
|
-
const hasCli = await isDir5(
|
|
59735
|
+
const cand = path64.join(dir, "sandbox");
|
|
59736
|
+
const hasCli = await isDir5(path64.join(dir, "apps", "cli"));
|
|
59333
59737
|
if (await isExecutable(cand) && hasCli) {
|
|
59334
59738
|
return cand;
|
|
59335
59739
|
}
|
|
59336
|
-
const parent =
|
|
59740
|
+
const parent = path64.dirname(dir);
|
|
59337
59741
|
if (parent === dir)
|
|
59338
59742
|
break;
|
|
59339
59743
|
dir = parent;
|
|
59340
59744
|
}
|
|
59341
|
-
const local =
|
|
59745
|
+
const local = path64.join(os20.homedir(), ".local", "bin", "sandbox");
|
|
59342
59746
|
if (await isExecutable(local))
|
|
59343
59747
|
return local;
|
|
59344
59748
|
throw new WorkspaceError("Could not locate the `sandbox` CLI. Set SANDBOX_ROOT to your sandbox checkout (or SANDBOX_BIN to the wrapper itself), or ensure the repo with its ./sandbox wrapper is reachable.");
|
|
@@ -59435,7 +59839,7 @@ async function readServicesRegistry() {
|
|
|
59435
59839
|
if (!p)
|
|
59436
59840
|
return null;
|
|
59437
59841
|
try {
|
|
59438
|
-
return await
|
|
59842
|
+
return await fs56.readFile(p, "utf-8");
|
|
59439
59843
|
} catch {
|
|
59440
59844
|
return null;
|
|
59441
59845
|
}
|
|
@@ -59592,7 +59996,7 @@ function pickValidServices(raw2, known) {
|
|
|
59592
59996
|
}
|
|
59593
59997
|
async function readWorkspaceConfig(workspaceDir) {
|
|
59594
59998
|
try {
|
|
59595
|
-
const text = await
|
|
59999
|
+
const text = await fs56.readFile(path64.join(dotDir(workspaceDir), "workspace.yml"), "utf-8");
|
|
59596
60000
|
const data = (0, import_yaml15.parse)(text);
|
|
59597
60001
|
const issueId = issueIdOf(data) || null;
|
|
59598
60002
|
const labels = Array.isArray(data?.labels) ? data.labels.filter((l) => typeof l === "string") : [];
|
|
@@ -59606,19 +60010,19 @@ async function listWorkspaces() {
|
|
|
59606
60010
|
const root = workspacesRoot();
|
|
59607
60011
|
let entries;
|
|
59608
60012
|
try {
|
|
59609
|
-
entries = await
|
|
60013
|
+
entries = await fs56.readdir(root, { withFileTypes: true });
|
|
59610
60014
|
} catch (e) {
|
|
59611
60015
|
throw new WorkspaceError(`Could not read workspaces root ${root}: ${e.message}`);
|
|
59612
60016
|
}
|
|
59613
60017
|
const names = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name).sort((a, b) => a.localeCompare(b));
|
|
59614
60018
|
const workspaces = await Promise.all(names.map(async (name) => {
|
|
59615
|
-
const p =
|
|
60019
|
+
const p = path64.join(root, name);
|
|
59616
60020
|
const { issueId, labels, model } = await readWorkspaceConfig(p);
|
|
59617
60021
|
return {
|
|
59618
60022
|
name,
|
|
59619
60023
|
path: p,
|
|
59620
60024
|
// vscode://file + an absolute path (already leading-slash) → single slash.
|
|
59621
|
-
vscodeUri: `vscode://file${p.split(
|
|
60025
|
+
vscodeUri: `vscode://file${p.split(path64.sep).map(encodeURIComponent).join("/")}`,
|
|
59622
60026
|
issueId,
|
|
59623
60027
|
issueUrl: issueId ? issueTracker().url(issueId) : null,
|
|
59624
60028
|
labels,
|
|
@@ -59795,7 +60199,7 @@ async function resolveWorkspaceDir(name) {
|
|
|
59795
60199
|
if (!clean || clean.includes("/") || clean.includes("\\") || clean.includes("..")) {
|
|
59796
60200
|
throw new WorkspaceError(`Invalid workspace name: ${JSON.stringify(name)}`);
|
|
59797
60201
|
}
|
|
59798
|
-
const target =
|
|
60202
|
+
const target = path64.join(workspacesRoot(), clean);
|
|
59799
60203
|
if (!await isDir5(target)) {
|
|
59800
60204
|
throw new WorkspaceError(`No such workspace: ${clean}`);
|
|
59801
60205
|
}
|
|
@@ -59806,7 +60210,7 @@ function reconcileDirForKey(name) {
|
|
|
59806
60210
|
if (!slug || slug.includes("/") || slug.includes("\\") || slug.includes("..")) {
|
|
59807
60211
|
throw new WorkspaceError(`Invalid reconcile session: ${JSON.stringify(name)}`);
|
|
59808
60212
|
}
|
|
59809
|
-
return
|
|
60213
|
+
return path64.join(reconcileRoot(), slug);
|
|
59810
60214
|
}
|
|
59811
60215
|
async function resolveSessionDir(name) {
|
|
59812
60216
|
if ((name ?? "").startsWith(RECONCILE_PREFIX)) {
|
|
@@ -59826,7 +60230,7 @@ async function openWorkspace(name) {
|
|
|
59826
60230
|
}
|
|
59827
60231
|
async function readEnvValue(dir, key) {
|
|
59828
60232
|
try {
|
|
59829
|
-
const text = await
|
|
60233
|
+
const text = await fs56.readFile(path64.join(dir, ".env"), "utf-8");
|
|
59830
60234
|
for (const line of text.split(/\r?\n/)) {
|
|
59831
60235
|
const eq = line.indexOf("=");
|
|
59832
60236
|
if (eq > 0 && line.slice(0, eq).trim() === key)
|
|
@@ -59838,7 +60242,7 @@ async function readEnvValue(dir, key) {
|
|
|
59838
60242
|
}
|
|
59839
60243
|
async function readWorkspaceFeature(dir) {
|
|
59840
60244
|
try {
|
|
59841
|
-
const text = await
|
|
60245
|
+
const text = await fs56.readFile(path64.join(dotDir(dir), "workspace.yml"), "utf-8");
|
|
59842
60246
|
const m = /^feature: *["']?([^"'\s#]+)/m.exec(text);
|
|
59843
60247
|
return m ? m[1] : null;
|
|
59844
60248
|
} catch {
|
|
@@ -59846,7 +60250,7 @@ async function readWorkspaceFeature(dir) {
|
|
|
59846
60250
|
}
|
|
59847
60251
|
}
|
|
59848
60252
|
async function readFileTail(file, maxBytes = 131072) {
|
|
59849
|
-
const fh = await
|
|
60253
|
+
const fh = await fs56.open(file, "r");
|
|
59850
60254
|
try {
|
|
59851
60255
|
const { size } = await fh.stat();
|
|
59852
60256
|
const start2 = Math.max(0, size - maxBytes);
|
|
@@ -59942,7 +60346,7 @@ async function loadPortsDoc() {
|
|
|
59942
60346
|
if (!file)
|
|
59943
60347
|
return null;
|
|
59944
60348
|
try {
|
|
59945
|
-
return (0, import_yaml15.parse)(await
|
|
60349
|
+
return (0, import_yaml15.parse)(await fs56.readFile(file, "utf8")) ?? null;
|
|
59946
60350
|
} catch {
|
|
59947
60351
|
return null;
|
|
59948
60352
|
}
|
|
@@ -59953,12 +60357,12 @@ function slotPortLabel(slot, ports) {
|
|
|
59953
60357
|
}
|
|
59954
60358
|
async function getWorkspaceStatuses() {
|
|
59955
60359
|
const root = workspacesRoot();
|
|
59956
|
-
const entries = await
|
|
60360
|
+
const entries = await fs56.readdir(root, { withFileTypes: true }).catch(() => []);
|
|
59957
60361
|
const names = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name).sort((a, b) => a.localeCompare(b));
|
|
59958
60362
|
const projects = await dockerComposeProjects();
|
|
59959
60363
|
const portsDoc = await loadPortsDoc();
|
|
59960
60364
|
const statuses = await Promise.all(names.map(async (name) => {
|
|
59961
|
-
const dir =
|
|
60365
|
+
const dir = path64.join(root, name);
|
|
59962
60366
|
let project = await readEnvValue(dir, "COMPOSE_PROJECT_NAME");
|
|
59963
60367
|
if (!project) {
|
|
59964
60368
|
const feature2 = await readWorkspaceFeature(dir);
|
|
@@ -60037,19 +60441,19 @@ async function getIntercepts() {
|
|
|
60037
60441
|
}
|
|
60038
60442
|
async function hasGit(dir) {
|
|
60039
60443
|
try {
|
|
60040
|
-
await
|
|
60444
|
+
await fs56.access(path64.join(dir, ".git"));
|
|
60041
60445
|
return true;
|
|
60042
60446
|
} catch {
|
|
60043
60447
|
return false;
|
|
60044
60448
|
}
|
|
60045
60449
|
}
|
|
60046
60450
|
async function listRepoWorktrees(workspaceDir) {
|
|
60047
|
-
const entries = await
|
|
60451
|
+
const entries = await fs56.readdir(workspaceDir, { withFileTypes: true }).catch(() => []);
|
|
60048
60452
|
const out = [];
|
|
60049
60453
|
for (const e of entries) {
|
|
60050
60454
|
if (!e.isDirectory() || e.name.startsWith("."))
|
|
60051
60455
|
continue;
|
|
60052
|
-
const dir =
|
|
60456
|
+
const dir = path64.join(workspaceDir, e.name);
|
|
60053
60457
|
if (await hasGit(dir))
|
|
60054
60458
|
out.push({ repo: e.name, dir });
|
|
60055
60459
|
}
|
|
@@ -60091,10 +60495,10 @@ async function ghPrForWorktree(repoDir) {
|
|
|
60091
60495
|
}
|
|
60092
60496
|
async function getWorkspacePRs() {
|
|
60093
60497
|
const root = workspacesRoot();
|
|
60094
|
-
const entries = await
|
|
60498
|
+
const entries = await fs56.readdir(root, { withFileTypes: true }).catch(() => []);
|
|
60095
60499
|
const names = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name).sort((a, b) => a.localeCompare(b));
|
|
60096
60500
|
return Promise.all(names.map(async (name) => {
|
|
60097
|
-
const repos = await listRepoWorktrees(
|
|
60501
|
+
const repos = await listRepoWorktrees(path64.join(root, name));
|
|
60098
60502
|
const repoPrs = await Promise.all(repos.map(async ({ repo, dir }) => ({ repo, ...await ghPrForWorktree(dir) })));
|
|
60099
60503
|
return { name, repos: repoPrs };
|
|
60100
60504
|
}));
|
|
@@ -60302,7 +60706,7 @@ async function worktreeSources(worktrees) {
|
|
|
60302
60706
|
if (res && res.code === 0) {
|
|
60303
60707
|
const common2 = stripAnsi(res.stdout).trim();
|
|
60304
60708
|
if (common2)
|
|
60305
|
-
srcs.add(
|
|
60709
|
+
srcs.add(path64.dirname(common2));
|
|
60306
60710
|
}
|
|
60307
60711
|
}
|
|
60308
60712
|
return [...srcs];
|
|
@@ -60311,8 +60715,8 @@ function gitErrorLine(text) {
|
|
|
60311
60715
|
const lines = stripAnsi(text).trim().split("\n");
|
|
60312
60716
|
return (lines.find((l) => /^\s*!\s|^(error|fatal):/.test(l)) ?? lines[0]).trim().replace(/\s+/g, " ");
|
|
60313
60717
|
}
|
|
60314
|
-
async function commitAndPush(dir, message = `chore: handoff ${
|
|
60315
|
-
const label =
|
|
60718
|
+
async function commitAndPush(dir, message = `chore: handoff ${path64.basename(dir)} before workspace teardown`, paths = []) {
|
|
60719
|
+
const label = path64.basename(dir);
|
|
60316
60720
|
const br = await runCapture("git", ["-C", dir, "rev-parse", "--abbrev-ref", "HEAD"]).catch(() => null);
|
|
60317
60721
|
const branch = br && br.code === 0 ? stripAnsi(br.stdout).trim() : null;
|
|
60318
60722
|
if (!branch || branch === "HEAD")
|
|
@@ -60394,7 +60798,7 @@ ${parts.join("\n")}`);
|
|
|
60394
60798
|
parts.push(detail);
|
|
60395
60799
|
}
|
|
60396
60800
|
}
|
|
60397
|
-
await
|
|
60801
|
+
await fs56.rm(target, { recursive: true, force: true });
|
|
60398
60802
|
for (const src of sources) {
|
|
60399
60803
|
await runCapture("git", ["-C", src, "worktree", "prune"]).catch(() => {
|
|
60400
60804
|
});
|
|
@@ -60559,17 +60963,17 @@ function encodeClaudeProjectDir(p) {
|
|
|
60559
60963
|
return p.replace(/[^a-zA-Z0-9]/g, "-");
|
|
60560
60964
|
}
|
|
60561
60965
|
async function findClaudeProjectDir(workspacePath) {
|
|
60562
|
-
const root =
|
|
60966
|
+
const root = path64.join(os20.homedir(), ".claude", "projects");
|
|
60563
60967
|
const want = encodeClaudeProjectDir(workspacePath);
|
|
60564
60968
|
let entries;
|
|
60565
60969
|
try {
|
|
60566
|
-
entries = await
|
|
60970
|
+
entries = await fs56.readdir(root, { withFileTypes: true });
|
|
60567
60971
|
} catch {
|
|
60568
60972
|
return null;
|
|
60569
60973
|
}
|
|
60570
60974
|
for (const e of entries) {
|
|
60571
60975
|
if (e.isDirectory() && encodeClaudeProjectDir(e.name) === want) {
|
|
60572
|
-
return
|
|
60976
|
+
return path64.join(root, e.name);
|
|
60573
60977
|
}
|
|
60574
60978
|
}
|
|
60575
60979
|
return null;
|
|
@@ -60577,7 +60981,7 @@ async function findClaudeProjectDir(workspacePath) {
|
|
|
60577
60981
|
async function listSessionFiles(projectDir) {
|
|
60578
60982
|
let names;
|
|
60579
60983
|
try {
|
|
60580
|
-
names = await
|
|
60984
|
+
names = await fs56.readdir(projectDir);
|
|
60581
60985
|
} catch {
|
|
60582
60986
|
return [];
|
|
60583
60987
|
}
|
|
@@ -60586,8 +60990,8 @@ async function listSessionFiles(projectDir) {
|
|
|
60586
60990
|
if (!n.endsWith(".jsonl"))
|
|
60587
60991
|
continue;
|
|
60588
60992
|
try {
|
|
60589
|
-
const st = await
|
|
60590
|
-
out.push({ file:
|
|
60993
|
+
const st = await fs56.stat(path64.join(projectDir, n));
|
|
60994
|
+
out.push({ file: path64.join(projectDir, n), mtime: st.mtime });
|
|
60591
60995
|
} catch {
|
|
60592
60996
|
}
|
|
60593
60997
|
}
|
|
@@ -60597,7 +61001,7 @@ function pickSession(sessions, sessionId) {
|
|
|
60597
61001
|
if (!sessions.length)
|
|
60598
61002
|
return null;
|
|
60599
61003
|
if (sessionId) {
|
|
60600
|
-
const match = sessions.find((s) =>
|
|
61004
|
+
const match = sessions.find((s) => path64.basename(s.file, ".jsonl") === sessionId);
|
|
60601
61005
|
if (match)
|
|
60602
61006
|
return match;
|
|
60603
61007
|
}
|
|
@@ -60640,7 +61044,7 @@ async function listSessions(name) {
|
|
|
60640
61044
|
return [];
|
|
60641
61045
|
const files = await listSessionFiles(projectDir);
|
|
60642
61046
|
return Promise.all(files.map(async (f, i) => {
|
|
60643
|
-
const sessionId =
|
|
61047
|
+
const sessionId = path64.basename(f.file, ".jsonl");
|
|
60644
61048
|
return {
|
|
60645
61049
|
sessionId,
|
|
60646
61050
|
title: await readSessionTitle(f.file, sessionId),
|
|
@@ -60802,7 +61206,7 @@ async function loadPriceTable() {
|
|
|
60802
61206
|
if (!p)
|
|
60803
61207
|
return DEFAULT_PRICES;
|
|
60804
61208
|
try {
|
|
60805
|
-
const doc = (0, import_yaml15.parse)(await
|
|
61209
|
+
const doc = (0, import_yaml15.parse)(await fs56.readFile(p, "utf-8"));
|
|
60806
61210
|
return doc?.models && Object.keys(doc.models).length ? doc.models : DEFAULT_PRICES;
|
|
60807
61211
|
} catch {
|
|
60808
61212
|
return DEFAULT_PRICES;
|
|
@@ -60827,7 +61231,7 @@ async function getWorkspaceUsage(name) {
|
|
|
60827
61231
|
for (const f of files) {
|
|
60828
61232
|
let text;
|
|
60829
61233
|
try {
|
|
60830
|
-
text = await
|
|
61234
|
+
text = await fs56.readFile(f.file, "utf-8");
|
|
60831
61235
|
} catch {
|
|
60832
61236
|
continue;
|
|
60833
61237
|
}
|
|
@@ -60848,7 +61252,7 @@ async function getWorkspaceUsage(name) {
|
|
|
60848
61252
|
if (usd != null)
|
|
60849
61253
|
sessionUsd = (sessionUsd ?? 0) + usd;
|
|
60850
61254
|
}
|
|
60851
|
-
const sessionId =
|
|
61255
|
+
const sessionId = path64.basename(f.file, ".jsonl");
|
|
60852
61256
|
sessions.push({
|
|
60853
61257
|
sessionId,
|
|
60854
61258
|
title: await readSessionTitle(f.file, sessionId),
|
|
@@ -60893,7 +61297,7 @@ async function resolveSessionSource(name, sessionId) {
|
|
|
60893
61297
|
workspacePath: target,
|
|
60894
61298
|
projectDir,
|
|
60895
61299
|
sessionFile: chosen.file,
|
|
60896
|
-
sessionId:
|
|
61300
|
+
sessionId: path64.basename(chosen.file, ".jsonl"),
|
|
60897
61301
|
sessionCount: sessions.length,
|
|
60898
61302
|
updatedAt: chosen.mtime.toISOString()
|
|
60899
61303
|
};
|
|
@@ -60914,10 +61318,10 @@ async function getClaudeTranscript(name, limit = 20, sessionId) {
|
|
|
60914
61318
|
const chosen = pickSession(sessions, sessionId);
|
|
60915
61319
|
if (!chosen)
|
|
60916
61320
|
return empty;
|
|
60917
|
-
const resolvedId =
|
|
61321
|
+
const resolvedId = path64.basename(chosen.file, ".jsonl");
|
|
60918
61322
|
let text;
|
|
60919
61323
|
try {
|
|
60920
|
-
text = await
|
|
61324
|
+
text = await fs56.readFile(chosen.file, "utf-8");
|
|
60921
61325
|
} catch (e) {
|
|
60922
61326
|
throw new WorkspaceError(`Could not read session file: ${e.message}`);
|
|
60923
61327
|
}
|
|
@@ -61019,7 +61423,7 @@ async function getK8sForwardsHealth() {
|
|
|
61019
61423
|
if (!file) {
|
|
61020
61424
|
throw new WorkspaceError("Could not locate the service registry (~/.sandbox/services.yaml) \u2014 run `sandbox setup`.");
|
|
61021
61425
|
}
|
|
61022
|
-
const doc = (0, import_yaml15.parse)(await
|
|
61426
|
+
const doc = (0, import_yaml15.parse)(await fs56.readFile(file, "utf-8")) ?? {};
|
|
61023
61427
|
const forwards = await Promise.all(forwardSpecs(doc.services, doc.infrastructure).map(async (d) => {
|
|
61024
61428
|
const r = await checkForwardHealth(d.listen, d.health);
|
|
61025
61429
|
return { name: d.name, port: d.listen, healthy: r.ok, detail: r.detail };
|
|
@@ -61111,16 +61515,16 @@ async function launchPrReconcile(url, issueRef) {
|
|
|
61111
61515
|
}
|
|
61112
61516
|
const { repo, number } = parseRepoAndNumber(url);
|
|
61113
61517
|
const slug = `${repo}-${number}`;
|
|
61114
|
-
const dir =
|
|
61115
|
-
await
|
|
61518
|
+
const dir = path64.join(reconcileRoot(), slug);
|
|
61519
|
+
await fs56.mkdir(dir, { recursive: true });
|
|
61116
61520
|
const [issue, pr, diffRes] = await Promise.all([
|
|
61117
61521
|
fetchIssueCard(issueRef || url),
|
|
61118
61522
|
ghPrView(url),
|
|
61119
61523
|
runCapture("gh", ["pr", "diff", url]).catch(() => null)
|
|
61120
61524
|
]);
|
|
61121
61525
|
const diff = diffRes && diffRes.code === 0 ? stripAnsi(diffRes.stdout) : "";
|
|
61122
|
-
const ctxFile =
|
|
61123
|
-
await
|
|
61526
|
+
const ctxFile = path64.join(dir, "reconcile-context.md");
|
|
61527
|
+
await fs56.writeFile(ctxFile, buildReconcileContext(issue, pr, diff), "utf-8");
|
|
61124
61528
|
await openTerminalRunning(`cd ${shellQuote(dir)} && claude -p --disallowedTools Bash Edit Write --append-system-prompt-file ${shellQuote(ctxFile)} ${shellQuote(RECONCILE_PROMPT)}`, false);
|
|
61125
61529
|
return { sessionKey: `${RECONCILE_PREFIX}${slug}`, slug, issue };
|
|
61126
61530
|
}
|
|
@@ -61174,7 +61578,7 @@ var init_sandbox = __esm({
|
|
|
61174
61578
|
"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code"
|
|
61175
61579
|
].filter((p) => Boolean(p));
|
|
61176
61580
|
DISTILLED = ["feature", "task", "description", "starting_prompt", "services"];
|
|
61177
|
-
reconcileRoot = () =>
|
|
61581
|
+
reconcileRoot = () => path64.join(dotDir(os20.homedir()), "reconcile");
|
|
61178
61582
|
RECONCILE_PREFIX = "reconcile:";
|
|
61179
61583
|
APPROVAL_LABEL = "ready-for-human-approval";
|
|
61180
61584
|
FAILING_CONCLUSIONS = /* @__PURE__ */ new Set([
|
|
@@ -61464,8 +61868,8 @@ __export(detect_exports, {
|
|
|
61464
61868
|
run: () => run10,
|
|
61465
61869
|
runDetect: () => runDetect
|
|
61466
61870
|
});
|
|
61467
|
-
import * as
|
|
61468
|
-
import * as
|
|
61871
|
+
import * as fs57 from "node:fs";
|
|
61872
|
+
import * as path65 from "node:path";
|
|
61469
61873
|
function mark(fv) {
|
|
61470
61874
|
return fv.confidence === "stated" ? "" : ` ${Colors.YELLOW}[${fv.confidence}]${Colors.RESET}`;
|
|
61471
61875
|
}
|
|
@@ -61518,8 +61922,8 @@ function printPlan(plan) {
|
|
|
61518
61922
|
async function runDetect(args, deps) {
|
|
61519
61923
|
let root;
|
|
61520
61924
|
if (args.repo) {
|
|
61521
|
-
root =
|
|
61522
|
-
if (!
|
|
61925
|
+
root = path65.resolve(deps.cwd, args.repo);
|
|
61926
|
+
if (!fs57.statSync(root, { throwIfNoEntry: false })?.isDirectory()) {
|
|
61523
61927
|
warn(`${root} is not a directory \u2014 pass the repo path: sandbox detect <repo>`);
|
|
61524
61928
|
return 2;
|
|
61525
61929
|
}
|
|
@@ -61828,18 +62232,18 @@ var open_exports = {};
|
|
|
61828
62232
|
__export(open_exports, {
|
|
61829
62233
|
run: () => run15
|
|
61830
62234
|
});
|
|
61831
|
-
import * as
|
|
61832
|
-
import * as
|
|
62235
|
+
import * as fs58 from "node:fs";
|
|
62236
|
+
import * as path66 from "node:path";
|
|
61833
62237
|
function loadWorkspaceYaml(start2) {
|
|
61834
|
-
let dir =
|
|
62238
|
+
let dir = path66.resolve(start2);
|
|
61835
62239
|
for (; ; ) {
|
|
61836
62240
|
for (const name of ["workspace.yml", "workspace.yaml"]) {
|
|
61837
|
-
const p =
|
|
61838
|
-
if (
|
|
62241
|
+
const p = path66.join(dotDir(dir), name);
|
|
62242
|
+
if (fs58.existsSync(p) && fs58.statSync(p).isFile()) {
|
|
61839
62243
|
return readWorkspaceYaml(dir);
|
|
61840
62244
|
}
|
|
61841
62245
|
}
|
|
61842
|
-
const parent =
|
|
62246
|
+
const parent = path66.dirname(dir);
|
|
61843
62247
|
if (parent === dir) return {};
|
|
61844
62248
|
dir = parent;
|
|
61845
62249
|
}
|
|
@@ -61969,8 +62373,8 @@ var init_clean = __esm({
|
|
|
61969
62373
|
});
|
|
61970
62374
|
|
|
61971
62375
|
// ../../packages/sandbox-core/dist/stats.js
|
|
61972
|
-
import * as
|
|
61973
|
-
import * as
|
|
62376
|
+
import * as fs59 from "node:fs";
|
|
62377
|
+
import * as path67 from "node:path";
|
|
61974
62378
|
function sleep4(seconds, signal) {
|
|
61975
62379
|
return new Promise((resolve13) => {
|
|
61976
62380
|
if (signal?.aborted)
|
|
@@ -62068,12 +62472,12 @@ function pad2(n) {
|
|
|
62068
62472
|
return String(n).padStart(2, "0");
|
|
62069
62473
|
}
|
|
62070
62474
|
function saveReport(stats, recommendations) {
|
|
62071
|
-
const statsDir =
|
|
62072
|
-
if (!
|
|
62073
|
-
|
|
62475
|
+
const statsDir = path67.join(sandboxRoot(), "stats");
|
|
62476
|
+
if (!fs59.existsSync(statsDir))
|
|
62477
|
+
fs59.mkdirSync(statsDir);
|
|
62074
62478
|
const now = /* @__PURE__ */ new Date();
|
|
62075
62479
|
const timestamp = `${now.getFullYear()}${pad2(now.getMonth() + 1)}${pad2(now.getDate())}-${pad2(now.getHours())}${pad2(now.getMinutes())}${pad2(now.getSeconds())}`;
|
|
62076
|
-
const csvPath =
|
|
62480
|
+
const csvPath = path67.join(statsDir, `docker-stats-${timestamp}.csv`);
|
|
62077
62481
|
const csvRows = [
|
|
62078
62482
|
["container", "avg_cpu_pct", "max_cpu_pct", "avg_mem_mib", "max_mem_mib", "samples"].join(",")
|
|
62079
62483
|
];
|
|
@@ -62088,8 +62492,8 @@ function saveReport(stats, recommendations) {
|
|
|
62088
62492
|
String(s.samples)
|
|
62089
62493
|
].join(","));
|
|
62090
62494
|
}
|
|
62091
|
-
|
|
62092
|
-
const summaryPath =
|
|
62495
|
+
fs59.writeFileSync(csvPath, csvRows.join("\r\n") + "\r\n");
|
|
62496
|
+
const summaryPath = path67.join(statsDir, `resource-summary-${timestamp}.txt`);
|
|
62093
62497
|
const generated = `${now.getFullYear()}-${pad2(now.getMonth() + 1)}-${pad2(now.getDate())} ${pad2(now.getHours())}:${pad2(now.getMinutes())}:${pad2(now.getSeconds())}`;
|
|
62094
62498
|
let out = "";
|
|
62095
62499
|
out += "Docker Resource Usage Summary\n";
|
|
@@ -62107,7 +62511,7 @@ function saveReport(stats, recommendations) {
|
|
|
62107
62511
|
out += ` ${r.name.padEnd(35)} cpus: ${r.recommended_cpus.padStart(5)} memory: ${r.recommended_memory.padStart(5)}
|
|
62108
62512
|
`;
|
|
62109
62513
|
}
|
|
62110
|
-
|
|
62514
|
+
fs59.writeFileSync(summaryPath, out);
|
|
62111
62515
|
return summaryPath;
|
|
62112
62516
|
}
|
|
62113
62517
|
async function runCollection(durationMinutes, intervalSeconds, save2, signal) {
|
|
@@ -62559,14 +62963,14 @@ var init_standards2 = __esm({
|
|
|
62559
62963
|
});
|
|
62560
62964
|
|
|
62561
62965
|
// ../../packages/sandbox-core/dist/pr_guard.js
|
|
62562
|
-
import * as
|
|
62563
|
-
import * as
|
|
62966
|
+
import * as fs60 from "node:fs";
|
|
62967
|
+
import * as path68 from "node:path";
|
|
62564
62968
|
function findRecordRoot(start2) {
|
|
62565
|
-
let dir =
|
|
62969
|
+
let dir = path68.resolve(start2);
|
|
62566
62970
|
for (; ; ) {
|
|
62567
|
-
if (
|
|
62971
|
+
if (fs60.existsSync(path68.join(dotDir(dir), "workspace.yml")))
|
|
62568
62972
|
return dir;
|
|
62569
|
-
const parent =
|
|
62973
|
+
const parent = path68.dirname(dir);
|
|
62570
62974
|
if (parent === dir)
|
|
62571
62975
|
return null;
|
|
62572
62976
|
dir = parent;
|
|
@@ -62574,11 +62978,11 @@ function findRecordRoot(start2) {
|
|
|
62574
62978
|
}
|
|
62575
62979
|
function prFileDirName(root) {
|
|
62576
62980
|
try {
|
|
62577
|
-
const names =
|
|
62981
|
+
const names = fs60.readdirSync(path68.join(root, "stages")).sort();
|
|
62578
62982
|
const dir = names.find((n) => /^\d+_propose$/.test(n)) ?? names.find((n) => /^\d+_ship$/.test(n));
|
|
62579
62983
|
return dir ? `stages/${dir}/output/` : "the propose stage's output/";
|
|
62580
62984
|
} catch {
|
|
62581
|
-
return `${
|
|
62985
|
+
return `${path68.basename(dotDir(root))}/notes/`;
|
|
62582
62986
|
}
|
|
62583
62987
|
}
|
|
62584
62988
|
function decide2(input2) {
|
|
@@ -62590,13 +62994,13 @@ function decide2(input2) {
|
|
|
62590
62994
|
const opensPr = tool === "Bash" && GH_PR_CREATE.test(command) || MCP_CREATE_PR.test(tool);
|
|
62591
62995
|
if (!opensPr)
|
|
62592
62996
|
return null;
|
|
62593
|
-
const rules = ["_shared/pr-rules.md", ...DOT_DIRS.map((d) => `${d}/pr-rules.md`)].find((p) =>
|
|
62594
|
-
return `Inside sandbox workspace \`${
|
|
62997
|
+
const rules = ["_shared/pr-rules.md", ...DOT_DIRS.map((d) => `${d}/pr-rules.md`)].find((p) => fs60.existsSync(path68.join(root, p))) ?? "docs/workspace-lifecycle.md in the sandbox repo";
|
|
62998
|
+
return `Inside sandbox workspace \`${path68.basename(root)}\`, PRs open only through \`sandbox pr open\`: it runs the pre-PR gate and records the URL. Write the PR file in \`${prFileDirName(root)}\`, have the person set \`status: approved\`, then run \`sandbox pr open\`. Rules: \`${rules}\`.`;
|
|
62595
62999
|
}
|
|
62596
63000
|
function main2() {
|
|
62597
63001
|
let reason2 = null;
|
|
62598
63002
|
try {
|
|
62599
|
-
const raw2 = process.stdin.isTTY ? "" :
|
|
63003
|
+
const raw2 = process.stdin.isTTY ? "" : fs60.readFileSync(0, "utf8");
|
|
62600
63004
|
const parsed = raw2.trim() ? JSON.parse(raw2) : {};
|
|
62601
63005
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed))
|
|
62602
63006
|
reason2 = decide2(parsed);
|
|
@@ -62661,13 +63065,13 @@ __export(init_workspace_exports, {
|
|
|
62661
63065
|
readStdinTask: () => readStdinTask,
|
|
62662
63066
|
run: () => run23
|
|
62663
63067
|
});
|
|
62664
|
-
import * as
|
|
63068
|
+
import * as fs61 from "node:fs";
|
|
62665
63069
|
function readStdinTask() {
|
|
62666
63070
|
if (process.stdin.isTTY) {
|
|
62667
63071
|
return "";
|
|
62668
63072
|
}
|
|
62669
63073
|
try {
|
|
62670
|
-
return
|
|
63074
|
+
return fs61.readFileSync(0, "utf8");
|
|
62671
63075
|
} catch {
|
|
62672
63076
|
return "";
|
|
62673
63077
|
}
|
|
@@ -62700,8 +63104,8 @@ __export(workspace_exports, {
|
|
|
62700
63104
|
browserFormCommand: () => browserFormCommand,
|
|
62701
63105
|
run: () => run24
|
|
62702
63106
|
});
|
|
62703
|
-
import * as
|
|
62704
|
-
import * as
|
|
63107
|
+
import * as fs62 from "node:fs";
|
|
63108
|
+
import * as path69 from "node:path";
|
|
62705
63109
|
async function resolveWorkspaceInputs(args) {
|
|
62706
63110
|
const base = process.cwd();
|
|
62707
63111
|
let resolved = resolveInputs(args, base);
|
|
@@ -62737,12 +63141,12 @@ async function resolveWorkspaceInputs(args) {
|
|
|
62737
63141
|
return { resolved, issue };
|
|
62738
63142
|
}
|
|
62739
63143
|
function which2(cmd) {
|
|
62740
|
-
const dirs = (process.env.PATH || "").split(
|
|
63144
|
+
const dirs = (process.env.PATH || "").split(path69.delimiter);
|
|
62741
63145
|
for (const dir of dirs) {
|
|
62742
63146
|
if (!dir) continue;
|
|
62743
|
-
const full =
|
|
63147
|
+
const full = path69.join(dir, cmd);
|
|
62744
63148
|
try {
|
|
62745
|
-
|
|
63149
|
+
fs62.accessSync(full, fs62.constants.X_OK);
|
|
62746
63150
|
return full;
|
|
62747
63151
|
} catch {
|
|
62748
63152
|
}
|
|
@@ -62763,8 +63167,8 @@ function browserFormCommand(node, cliJs, args) {
|
|
|
62763
63167
|
return cmd;
|
|
62764
63168
|
}
|
|
62765
63169
|
function runBrowserForm(args) {
|
|
62766
|
-
const cliJs =
|
|
62767
|
-
if (!(
|
|
63170
|
+
const cliJs = path69.join(getMonorepoRoot(), "apps", "sandbox-dashboard", "dist", "form-cli.js");
|
|
63171
|
+
if (!(fs62.existsSync(cliJs) && fs62.statSync(cliJs).isFile())) {
|
|
62768
63172
|
error(
|
|
62769
63173
|
`Browser form not built: ${cliJs} is missing. Run \`npm --prefix apps/sandbox-dashboard run build\` (or \`sandbox dashboard\` once) first.`
|
|
62770
63174
|
);
|
|
@@ -62887,7 +63291,7 @@ __export(issue_exports, {
|
|
|
62887
63291
|
run: () => run26,
|
|
62888
63292
|
updateFromArgs: () => updateFromArgs
|
|
62889
63293
|
});
|
|
62890
|
-
import * as
|
|
63294
|
+
import * as fs63 from "node:fs";
|
|
62891
63295
|
function updateFromArgs(args) {
|
|
62892
63296
|
const status2 = args.status || void 0;
|
|
62893
63297
|
const comment = args.comment || void 0;
|
|
@@ -62895,7 +63299,7 @@ function updateFromArgs(args) {
|
|
|
62895
63299
|
return "Nothing to update: pass --status <name> and/or --comment <text>.";
|
|
62896
63300
|
return { status: status2, comment };
|
|
62897
63301
|
}
|
|
62898
|
-
function newIssueFromArgs(args, read = (p) =>
|
|
63302
|
+
function newIssueFromArgs(args, read = (p) => fs63.readFileSync(p, "utf8")) {
|
|
62899
63303
|
const project = (args.project ?? "").trim().toUpperCase();
|
|
62900
63304
|
const type = args.type ?? "";
|
|
62901
63305
|
const title = (args.title ?? "").trim();
|
|
@@ -62935,6 +63339,14 @@ async function run26(args) {
|
|
|
62935
63339
|
}
|
|
62936
63340
|
return;
|
|
62937
63341
|
}
|
|
63342
|
+
if (args.action === "start") {
|
|
63343
|
+
const workspace = findWorkspaceRoot(process.cwd());
|
|
63344
|
+
if (workspace === null)
|
|
63345
|
+
fail3("Not inside a sandbox workspace (no .sandbox/ directory found above CWD).");
|
|
63346
|
+
const line = await startIssue(workspace) || "left as is (no in_progress status configured)";
|
|
63347
|
+
(line.includes("not moved") ? warn : success)(`Issue: ${line}`);
|
|
63348
|
+
return;
|
|
63349
|
+
}
|
|
62938
63350
|
const update = args.action === "update" ? updateFromArgs(args) : null;
|
|
62939
63351
|
if (typeof update === "string") fail3(update);
|
|
62940
63352
|
let text = args.ref ?? "";
|
|
@@ -62974,6 +63386,7 @@ var init_issue = __esm({
|
|
|
62974
63386
|
"use strict";
|
|
62975
63387
|
init_define_SANDBOX_BUILD();
|
|
62976
63388
|
init_gate();
|
|
63389
|
+
init_issue_status();
|
|
62977
63390
|
init_issue_tracker();
|
|
62978
63391
|
init_workspace_prompts();
|
|
62979
63392
|
init_config2();
|
|
@@ -63063,10 +63476,10 @@ var close_out_exports = {};
|
|
|
63063
63476
|
__export(close_out_exports, {
|
|
63064
63477
|
run: () => run29
|
|
63065
63478
|
});
|
|
63066
|
-
import
|
|
63479
|
+
import path70 from "node:path";
|
|
63067
63480
|
async function run29(args) {
|
|
63068
63481
|
const root = findRecordRoot(process.cwd());
|
|
63069
|
-
const name = args.name ?? (root ?
|
|
63482
|
+
const name = args.name ?? (root ? path70.basename(root) : null);
|
|
63070
63483
|
if (!name) {
|
|
63071
63484
|
error("No workspace given and not inside one.");
|
|
63072
63485
|
process.exit(1);
|
|
@@ -63118,9 +63531,9 @@ async function run30(args) {
|
|
|
63118
63531
|
error("Feature name required: pass it as an argument or set FEATURE in a .env file.");
|
|
63119
63532
|
process.exit(1);
|
|
63120
63533
|
}
|
|
63121
|
-
const { path:
|
|
63534
|
+
const { path: path74 } = await openWorkspace(name);
|
|
63122
63535
|
console.log(
|
|
63123
|
-
`${Colors.GREEN}Opened '${name}' in VS Code${Colors.RESET} ${Colors.DIM}${
|
|
63536
|
+
`${Colors.GREEN}Opened '${name}' in VS Code${Colors.RESET} ${Colors.DIM}${path74}${Colors.RESET}`
|
|
63124
63537
|
);
|
|
63125
63538
|
}
|
|
63126
63539
|
var init_code = __esm({
|
|
@@ -63138,8 +63551,8 @@ var setup_exports = {};
|
|
|
63138
63551
|
__export(setup_exports, {
|
|
63139
63552
|
run: () => run31
|
|
63140
63553
|
});
|
|
63141
|
-
import * as
|
|
63142
|
-
import * as
|
|
63554
|
+
import * as fs64 from "node:fs";
|
|
63555
|
+
import * as path71 from "node:path";
|
|
63143
63556
|
function heading(text) {
|
|
63144
63557
|
console.log(`
|
|
63145
63558
|
${Colors.BOLD}${text}${Colors.RESET}`);
|
|
@@ -63245,7 +63658,7 @@ async function configureMachine(interactive) {
|
|
|
63245
63658
|
interactive
|
|
63246
63659
|
);
|
|
63247
63660
|
}
|
|
63248
|
-
if (!
|
|
63661
|
+
if (!fs64.existsSync(servicesLocalYamlPath()) && interactive) {
|
|
63249
63662
|
k8sContext = await prompt(
|
|
63250
63663
|
"Kube context for `sandbox intercept` (services.local.yaml k8s_context)",
|
|
63251
63664
|
k8sContext
|
|
@@ -63407,9 +63820,9 @@ async function configureMachine(interactive) {
|
|
|
63407
63820
|
info(
|
|
63408
63821
|
`${zshrcPath()}: ${completionChanged ? "installed tab completion" : "tab completion already installed"}`
|
|
63409
63822
|
);
|
|
63410
|
-
if (
|
|
63823
|
+
if (fs64.existsSync(desktopConfigPath())) {
|
|
63411
63824
|
info(
|
|
63412
|
-
`${
|
|
63825
|
+
`${path71.basename(desktopConfigPath())}: ${desktopChanged ? "dropped stale env block" : "no env block to remove"}`
|
|
63413
63826
|
);
|
|
63414
63827
|
}
|
|
63415
63828
|
return {
|
|
@@ -63520,8 +63933,8 @@ var config_exports = {};
|
|
|
63520
63933
|
__export(config_exports, {
|
|
63521
63934
|
run: () => run32
|
|
63522
63935
|
});
|
|
63523
|
-
import * as
|
|
63524
|
-
import * as
|
|
63936
|
+
import * as fs65 from "node:fs";
|
|
63937
|
+
import * as path72 from "node:path";
|
|
63525
63938
|
function show(cfg) {
|
|
63526
63939
|
const paths = cfg.paths || {};
|
|
63527
63940
|
console.log(`${Colors.BOLD}config file:${Colors.RESET} ${configPath()}`);
|
|
@@ -63616,9 +64029,9 @@ async function run32(args) {
|
|
|
63616
64029
|
info(`Regenerated ${envSh}`);
|
|
63617
64030
|
info(`${zshenvPath()}: ${zshChanged ? "updated" : "already sources env.sh"}`);
|
|
63618
64031
|
const desktopPath = desktopConfigPath();
|
|
63619
|
-
if (
|
|
64032
|
+
if (fs65.existsSync(desktopPath)) {
|
|
63620
64033
|
info(
|
|
63621
|
-
`${
|
|
64034
|
+
`${path72.basename(desktopPath)}: ${desktopChanged ? "dropped stale env block" : "no env block to remove"}`
|
|
63622
64035
|
);
|
|
63623
64036
|
}
|
|
63624
64037
|
info("Open a new shell (or `source ~/.zshenv`) for the exports to take effect.");
|
|
@@ -63669,7 +64082,7 @@ var init_registry = __esm({
|
|
|
63669
64082
|
|
|
63670
64083
|
// src/main.ts
|
|
63671
64084
|
init_define_SANDBOX_BUILD();
|
|
63672
|
-
import
|
|
64085
|
+
import path73 from "node:path";
|
|
63673
64086
|
import { fileURLToPath } from "node:url";
|
|
63674
64087
|
|
|
63675
64088
|
// ../../node_modules/commander/esm.mjs
|
|
@@ -63723,7 +64136,7 @@ function staleBuildWarning(build, repoRoot) {
|
|
|
63723
64136
|
|
|
63724
64137
|
// src/main.ts
|
|
63725
64138
|
init_interrupt();
|
|
63726
|
-
var _REPO_ROOT =
|
|
64139
|
+
var _REPO_ROOT = path73.resolve(path73.dirname(fileURLToPath(import.meta.url)), "..");
|
|
63727
64140
|
if (!envVar("ROOT")) {
|
|
63728
64141
|
process.env.SANDBOX_ROOT = _REPO_ROOT;
|
|
63729
64142
|
}
|
|
@@ -63744,11 +64157,15 @@ var SPECS = [
|
|
|
63744
64157
|
).option(
|
|
63745
64158
|
"-w, --workspace <path>",
|
|
63746
64159
|
"Path to directory containing repo checkouts. Default: ~/workspace (override with WORKSPACE env var)."
|
|
64160
|
+
).option(
|
|
64161
|
+
"--dev",
|
|
64162
|
+
"Run the file watchers in cli/library/extension containers, which otherwise idle."
|
|
63747
64163
|
);
|
|
63748
64164
|
},
|
|
63749
64165
|
namespace: ([feature2], o) => ({
|
|
63750
64166
|
feature: feature2 ?? null,
|
|
63751
|
-
workspace: o.workspace ?? null
|
|
64167
|
+
workspace: o.workspace ?? null,
|
|
64168
|
+
dev: o.dev ?? false
|
|
63752
64169
|
})
|
|
63753
64170
|
},
|
|
63754
64171
|
{
|
|
@@ -63819,9 +64236,16 @@ var SPECS = [
|
|
|
63819
64236
|
).option(
|
|
63820
64237
|
"--if-idle",
|
|
63821
64238
|
"SessionEnd hook mode: stop the workspace's own feature unless another Claude session is open there; silent, always exits 0, never stops infra"
|
|
64239
|
+
).option(
|
|
64240
|
+
"--idle <minutes>",
|
|
64241
|
+
"Stop every workspace's feature containers nothing has used for <minutes> (a busy or recently active session, a gate exec or a fresh start all count as use); with [feature], only that one"
|
|
63822
64242
|
);
|
|
63823
64243
|
},
|
|
63824
|
-
namespace: ([feature2], o) => ({
|
|
64244
|
+
namespace: ([feature2], o) => ({
|
|
64245
|
+
feature: feature2 ?? null,
|
|
64246
|
+
if_idle: Boolean(o.ifIdle),
|
|
64247
|
+
idle: o.idle ?? null
|
|
64248
|
+
})
|
|
63825
64249
|
},
|
|
63826
64250
|
{
|
|
63827
64251
|
name: "status",
|
|
@@ -64157,8 +64581,8 @@ var SPECS = [
|
|
|
64157
64581
|
).addArgument(
|
|
64158
64582
|
new Argument(
|
|
64159
64583
|
"<action>",
|
|
64160
|
-
"view: print the issue; update: change its status and/or add a comment; create: file a new issue and print its URL (Jira)"
|
|
64161
|
-
).choices(["view", "update", "create"])
|
|
64584
|
+
"view: print the issue; update: change its status and/or add a comment; create: file a new issue and print its URL (Jira); start: move the workspace's issue to in progress (forward only)"
|
|
64585
|
+
).choices(["view", "update", "create", "start"])
|
|
64162
64586
|
).argument(
|
|
64163
64587
|
"[ref]",
|
|
64164
64588
|
"Issue ID; omit inside a workspace to use its .sandbox/workspace.yml issue"
|
|
@@ -64456,7 +64880,7 @@ ${_RED}Interrupted${_RESET}`));
|
|
|
64456
64880
|
return completeLine(program2);
|
|
64457
64881
|
}
|
|
64458
64882
|
if (process.argv[2] !== "hooks" && !process.argv.includes("--if-idle")) {
|
|
64459
|
-
const warning = staleBuildWarning(BUILD_STAMP,
|
|
64883
|
+
const warning = staleBuildWarning(BUILD_STAMP, path73.resolve(_REPO_ROOT, "../.."));
|
|
64460
64884
|
if (warning) console.error(warning);
|
|
64461
64885
|
}
|
|
64462
64886
|
if (process.argv.length <= 2) {
|