werkmeister 0.14.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 +1099 -603
- package/dist/mcp.js +624 -281
- package/dist/sandbox-workspaces.vsix +0 -0
- package/infra/compose/scripts/sandbox-install-startup.sh +53 -4
- package/infra/schemas/workspace.schema.json +1 -1
- package/infra/skills/sb-pr/SKILL.md +2 -2
- package/infra/skills/sb-research/SKILL.md +3 -1
- package/infra/skills/sb-test/SKILL.md +1 -1
- package/infra/templates/docker-compose.feature.yml.njk +19 -3
- package/package.json +2 -2
- package/werkmeister +3 -3
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
|
{
|
|
@@ -13276,7 +13298,7 @@ function writeWorkspaceSettings(workspaceRoot) {
|
|
|
13276
13298
|
writeWorkspaceModel(workspaceRoot, readModel(workspaceRoot));
|
|
13277
13299
|
writeAutoStopHook(workspaceRoot, readAutoStop(workspaceRoot));
|
|
13278
13300
|
}
|
|
13279
|
-
var _HOOK_MARKER, _GATE_MARKER, _PR_GUARD_MARKER, _AUTO_STOP_MARKER, _SKILLS_SELF_HEAL, SESSION_START_COMMAND, PRE_TOOL_USE_GATE_COMMAND, PR_GUARD_COMMAND, SESSION_END_COMMAND, _SESSION_END_TIMEOUT_S, READ_ONLY_GIT_RULES, AUTONOMOUS_RULES, AUTONOMOUS_DENY, permissionsRecord, _strings;
|
|
13301
|
+
var _HOOK_MARKER, _GATE_MARKER, _PR_GUARD_MARKER, _AUTO_STOP_MARKER, _SKILLS_SELF_HEAL, SESSION_START_COMMAND, PRE_TOOL_USE_GATE_COMMAND, PR_GUARD_COMMAND, SESSION_END_COMMAND, _SESSION_END_TIMEOUT_S, READ_ONLY_GIT_RULES, PLUGIN_MCP_PREFIXES, pluginTool, AUTONOMOUS_RULES, AUTONOMOUS_DENY, permissionsRecord, _strings;
|
|
13280
13302
|
var init_hooks_gen = __esm({
|
|
13281
13303
|
"../../packages/sandbox-core/dist/hooks_gen.js"() {
|
|
13282
13304
|
"use strict";
|
|
@@ -13299,6 +13321,11 @@ var init_hooks_gen = __esm({
|
|
|
13299
13321
|
"Bash(git status:*)",
|
|
13300
13322
|
"Bash(git ls-files:*)"
|
|
13301
13323
|
];
|
|
13324
|
+
PLUGIN_MCP_PREFIXES = [
|
|
13325
|
+
"mcp__plugin_werkmeister_workspaces__",
|
|
13326
|
+
"mcp__plugin_sandbox-tools_sandbox-workspace__"
|
|
13327
|
+
];
|
|
13328
|
+
pluginTool = (tool) => PLUGIN_MCP_PREFIXES.map((prefix) => `${prefix}${tool}`);
|
|
13302
13329
|
AUTONOMOUS_RULES = [
|
|
13303
13330
|
"Bash(git:*)",
|
|
13304
13331
|
// `sandbox pr open` does the push and the PR; gh is read-only here.
|
|
@@ -13306,7 +13333,16 @@ var init_hooks_gen = __esm({
|
|
|
13306
13333
|
// No-arg forms act on the cwd's FEATURE; `autonomousRules` adds the named ones.
|
|
13307
13334
|
"Bash(sandbox up)",
|
|
13308
13335
|
"Bash(sandbox restart)",
|
|
13309
|
-
...[
|
|
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}:*)`),
|
|
13310
13346
|
"Bash(docker ps:*)",
|
|
13311
13347
|
// Local build and test; no `npm install`, so a new dependency waits for a person.
|
|
13312
13348
|
...["npm ci", "npm run", "npm test", "npx", "node", "python3"].map((cmd) => `Bash(${cmd}:*)`),
|
|
@@ -13322,7 +13358,7 @@ var init_hooks_gen = __esm({
|
|
|
13322
13358
|
"workspace-prs",
|
|
13323
13359
|
"kube-context",
|
|
13324
13360
|
"k8s-forwards-status"
|
|
13325
|
-
].
|
|
13361
|
+
].flatMap(pluginTool)
|
|
13326
13362
|
];
|
|
13327
13363
|
AUTONOMOUS_DENY = [
|
|
13328
13364
|
...[
|
|
@@ -13365,7 +13401,7 @@ var init_hooks_gen = __esm({
|
|
|
13365
13401
|
"claude-send",
|
|
13366
13402
|
"claude-resume",
|
|
13367
13403
|
"sandbox-connect"
|
|
13368
|
-
].
|
|
13404
|
+
].flatMap(pluginTool)
|
|
13369
13405
|
];
|
|
13370
13406
|
permissionsRecord = (workspaceRoot) => path7.join(dotDir(workspaceRoot), "claude-permissions.json");
|
|
13371
13407
|
_strings = (v) => Array.isArray(v) ? v.filter((r) => typeof r === "string") : [];
|
|
@@ -13555,8 +13591,8 @@ Lifecycle: \`working \u2192 ready \u2192 pr-open \u2192 merged \u2192 shipped \u
|
|
|
13555
13591
|
| \`sandbox pr status\` | record merged / shipped / closed from GitHub and release tags |
|
|
13556
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 |
|
|
13557
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 |
|
|
13558
|
-
| \`sandbox close-out\` | after ship:
|
|
13559
|
-
| \`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 |
|
|
13560
13596
|
`;
|
|
13561
13597
|
}
|
|
13562
13598
|
var init_pr_rules = __esm({
|
|
@@ -14179,7 +14215,7 @@ var init_icm_templates = __esm({
|
|
|
14179
14215
|
"One commit per code repo touched, on that worktree's branch",
|
|
14180
14216
|
"`output/pr-<repo>.md` per repo"
|
|
14181
14217
|
],
|
|
14182
|
-
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`.",
|
|
14183
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."
|
|
14184
14220
|
})
|
|
14185
14221
|
};
|
|
@@ -14457,6 +14493,7 @@ function renderIssueRef(issue) {
|
|
|
14457
14493
|
"",
|
|
14458
14494
|
`${name}${title}. ${copy}`,
|
|
14459
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.",
|
|
14460
14497
|
""
|
|
14461
14498
|
].join("\n");
|
|
14462
14499
|
}
|
|
@@ -14490,6 +14527,17 @@ function proposeDir(stages) {
|
|
|
14490
14527
|
const i = stages.findIndex((s) => s.name === "propose");
|
|
14491
14528
|
return i < 0 ? null : stageDirs(stages)[i];
|
|
14492
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
|
+
}
|
|
14493
14541
|
function vars(ctx, stages) {
|
|
14494
14542
|
const dirs = stageDirs(stages);
|
|
14495
14543
|
const feature2 = (ctx.meta["FEATURE"] ?? "").trim();
|
|
@@ -14498,8 +14546,7 @@ function vars(ctx, stages) {
|
|
|
14498
14546
|
return {
|
|
14499
14547
|
at: (name) => `../${dirs[stages.findIndex((s) => s.name === name)]}`,
|
|
14500
14548
|
repos: repos.length ? repos.map((r) => `\`../../${r}/\``).join(", ") : "the code worktrees in this workspace",
|
|
14501
|
-
|
|
14502
|
-
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",
|
|
14503
14550
|
standards: "",
|
|
14504
14551
|
autonomous: isTrue(ctx.inputs["autonomous"])
|
|
14505
14552
|
};
|
|
@@ -14550,7 +14597,7 @@ function standardsRef(tier, r) {
|
|
|
14550
14597
|
const rows = tier === "all" ? r.active : r.active.filter((d) => universal.includes(d));
|
|
14551
14598
|
return `- Reference (every run): \`../../_shared/standards.md\` \u2014 rows ${[...rows, "ponytail"].map((d) => `\`${d}\``).join(", ")}`;
|
|
14552
14599
|
}
|
|
14553
|
-
function renderContract(dir, stage, v) {
|
|
14600
|
+
function renderContract(dir, stage, v, startsIssue = false) {
|
|
14554
14601
|
const c = stage.contract(v);
|
|
14555
14602
|
return [
|
|
14556
14603
|
`# ${dir} \u2014 ${stage.job}`,
|
|
@@ -14564,6 +14611,8 @@ function renderContract(dir, stage, v) {
|
|
|
14564
14611
|
`Do NOT load: ${c.doNotLoad}`,
|
|
14565
14612
|
"",
|
|
14566
14613
|
"## Process",
|
|
14614
|
+
// Step 0, so the contracts' own "steps 1–3" references keep their numbers.
|
|
14615
|
+
...startsIssue ? [`0. ${START_ISSUE}`] : [],
|
|
14567
14616
|
...c.process.map((p, i) => `${i + 1}. ${p}`),
|
|
14568
14617
|
"",
|
|
14569
14618
|
"## Outputs",
|
|
@@ -14610,7 +14659,7 @@ function renderClaudeMd(ctx, stages) {
|
|
|
14610
14659
|
...proposeDir(stages) ? [
|
|
14611
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 |"
|
|
14612
14661
|
] : [],
|
|
14613
|
-
"| 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 |",
|
|
14614
14663
|
"| asked for status | `ls stages/*/output/` | report which stages hold artifacts |",
|
|
14615
14664
|
"| containers / ports / routing broken | `.sandbox/sandbox-context.md` | \u2014 |",
|
|
14616
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 |",
|
|
@@ -14690,10 +14739,7 @@ function render(ctx) {
|
|
|
14690
14739
|
if (propose2)
|
|
14691
14740
|
files["_shared/pr-rules.md"] = buildPrRulesMd(`stages/${propose2}/output/`, base.autonomous);
|
|
14692
14741
|
stages.forEach((stage, i) => {
|
|
14693
|
-
files[`stages/${dirs[i]}/CONTEXT.md`] = renderContract(dirs[i], stage, {
|
|
14694
|
-
...base,
|
|
14695
|
-
standards: standardsRef(stage.standards, r)
|
|
14696
|
-
});
|
|
14742
|
+
files[`stages/${dirs[i]}/CONTEXT.md`] = renderContract(dirs[i], stage, { ...base, standards: standardsRef(stage.standards, r) }, i === 0 && Boolean(issueIdOfMeta(ctx.meta)));
|
|
14697
14743
|
});
|
|
14698
14744
|
return files;
|
|
14699
14745
|
}
|
|
@@ -14763,7 +14809,7 @@ function init(ctx) {
|
|
|
14763
14809
|
}
|
|
14764
14810
|
icm.refresh(root);
|
|
14765
14811
|
}
|
|
14766
|
-
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;
|
|
14767
14813
|
var init_icm = __esm({
|
|
14768
14814
|
"../../packages/sandbox-core/dist/strategies/icm.js"() {
|
|
14769
14815
|
"use strict";
|
|
@@ -14781,6 +14827,7 @@ var init_icm = __esm({
|
|
|
14781
14827
|
init_icm_templates();
|
|
14782
14828
|
init_issue_section();
|
|
14783
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.";
|
|
14784
14831
|
ONE_RULE = [
|
|
14785
14832
|
"## The one rule",
|
|
14786
14833
|
"",
|
|
@@ -15090,69 +15137,69 @@ var require_chownr = __commonJS({
|
|
|
15090
15137
|
"../../node_modules/chownr/chownr.js"(exports, module) {
|
|
15091
15138
|
"use strict";
|
|
15092
15139
|
init_define_SANDBOX_BUILD();
|
|
15093
|
-
var
|
|
15094
|
-
var
|
|
15095
|
-
var LCHOWN =
|
|
15096
|
-
var LCHOWNSYNC =
|
|
15097
|
-
var needEISDIRHandled =
|
|
15098
|
-
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) => {
|
|
15099
15146
|
try {
|
|
15100
|
-
return
|
|
15147
|
+
return fs66[LCHOWNSYNC](path75, uid, gid);
|
|
15101
15148
|
} catch (er) {
|
|
15102
15149
|
if (er.code !== "ENOENT")
|
|
15103
15150
|
throw er;
|
|
15104
15151
|
}
|
|
15105
15152
|
};
|
|
15106
|
-
var chownSync = (
|
|
15153
|
+
var chownSync = (path75, uid, gid) => {
|
|
15107
15154
|
try {
|
|
15108
|
-
return
|
|
15155
|
+
return fs66.chownSync(path75, uid, gid);
|
|
15109
15156
|
} catch (er) {
|
|
15110
15157
|
if (er.code !== "ENOENT")
|
|
15111
15158
|
throw er;
|
|
15112
15159
|
}
|
|
15113
15160
|
};
|
|
15114
|
-
var handleEISDIR = needEISDIRHandled ? (
|
|
15161
|
+
var handleEISDIR = needEISDIRHandled ? (path75, uid, gid, cb) => (er) => {
|
|
15115
15162
|
if (!er || er.code !== "EISDIR")
|
|
15116
15163
|
cb(er);
|
|
15117
15164
|
else
|
|
15118
|
-
|
|
15165
|
+
fs66.chown(path75, uid, gid, cb);
|
|
15119
15166
|
} : (_, __, ___, cb) => cb;
|
|
15120
|
-
var handleEISDirSync = needEISDIRHandled ? (
|
|
15167
|
+
var handleEISDirSync = needEISDIRHandled ? (path75, uid, gid) => {
|
|
15121
15168
|
try {
|
|
15122
|
-
return lchownSync(
|
|
15169
|
+
return lchownSync(path75, uid, gid);
|
|
15123
15170
|
} catch (er) {
|
|
15124
15171
|
if (er.code !== "EISDIR")
|
|
15125
15172
|
throw er;
|
|
15126
|
-
chownSync(
|
|
15173
|
+
chownSync(path75, uid, gid);
|
|
15127
15174
|
}
|
|
15128
|
-
} : (
|
|
15175
|
+
} : (path75, uid, gid) => lchownSync(path75, uid, gid);
|
|
15129
15176
|
var nodeVersion = process.version;
|
|
15130
|
-
var readdir = (
|
|
15131
|
-
var
|
|
15177
|
+
var readdir = (path75, options, cb) => fs66.readdir(path75, options, cb);
|
|
15178
|
+
var readdirSync20 = (path75, options) => fs66.readdirSync(path75, options);
|
|
15132
15179
|
if (/^v4\./.test(nodeVersion))
|
|
15133
|
-
readdir = (
|
|
15180
|
+
readdir = (path75, options, cb) => fs66.readdir(path75, cb);
|
|
15134
15181
|
var chown = (cpath, uid, gid, cb) => {
|
|
15135
|
-
|
|
15182
|
+
fs66[LCHOWN](cpath, uid, gid, handleEISDIR(cpath, uid, gid, (er) => {
|
|
15136
15183
|
cb(er && er.code !== "ENOENT" ? er : null);
|
|
15137
15184
|
}));
|
|
15138
15185
|
};
|
|
15139
15186
|
var chownrKid = (p, child, uid, gid, cb) => {
|
|
15140
15187
|
if (typeof child === "string")
|
|
15141
|
-
return
|
|
15188
|
+
return fs66.lstat(path74.resolve(p, child), (er, stats) => {
|
|
15142
15189
|
if (er)
|
|
15143
15190
|
return cb(er.code !== "ENOENT" ? er : null);
|
|
15144
15191
|
stats.name = child;
|
|
15145
15192
|
chownrKid(p, stats, uid, gid, cb);
|
|
15146
15193
|
});
|
|
15147
15194
|
if (child.isDirectory()) {
|
|
15148
|
-
chownr(
|
|
15195
|
+
chownr(path74.resolve(p, child.name), uid, gid, (er) => {
|
|
15149
15196
|
if (er)
|
|
15150
15197
|
return cb(er);
|
|
15151
|
-
const cpath =
|
|
15198
|
+
const cpath = path74.resolve(p, child.name);
|
|
15152
15199
|
chown(cpath, uid, gid, cb);
|
|
15153
15200
|
});
|
|
15154
15201
|
} else {
|
|
15155
|
-
const cpath =
|
|
15202
|
+
const cpath = path74.resolve(p, child.name);
|
|
15156
15203
|
chown(cpath, uid, gid, cb);
|
|
15157
15204
|
}
|
|
15158
15205
|
};
|
|
@@ -15182,7 +15229,7 @@ var require_chownr = __commonJS({
|
|
|
15182
15229
|
var chownrKidSync = (p, child, uid, gid) => {
|
|
15183
15230
|
if (typeof child === "string") {
|
|
15184
15231
|
try {
|
|
15185
|
-
const stats =
|
|
15232
|
+
const stats = fs66.lstatSync(path74.resolve(p, child));
|
|
15186
15233
|
stats.name = child;
|
|
15187
15234
|
child = stats;
|
|
15188
15235
|
} catch (er) {
|
|
@@ -15193,13 +15240,13 @@ var require_chownr = __commonJS({
|
|
|
15193
15240
|
}
|
|
15194
15241
|
}
|
|
15195
15242
|
if (child.isDirectory())
|
|
15196
|
-
chownrSync(
|
|
15197
|
-
handleEISDirSync(
|
|
15243
|
+
chownrSync(path74.resolve(p, child.name), uid, gid);
|
|
15244
|
+
handleEISDirSync(path74.resolve(p, child.name), uid, gid);
|
|
15198
15245
|
};
|
|
15199
15246
|
var chownrSync = (p, uid, gid) => {
|
|
15200
15247
|
let children;
|
|
15201
15248
|
try {
|
|
15202
|
-
children =
|
|
15249
|
+
children = readdirSync20(p, { withFileTypes: true });
|
|
15203
15250
|
} catch (er) {
|
|
15204
15251
|
if (er.code === "ENOENT")
|
|
15205
15252
|
return;
|
|
@@ -15350,7 +15397,7 @@ var require_buffer_list = __commonJS({
|
|
|
15350
15397
|
}
|
|
15351
15398
|
}, {
|
|
15352
15399
|
key: "join",
|
|
15353
|
-
value: function
|
|
15400
|
+
value: function join53(s) {
|
|
15354
15401
|
if (this.length === 0) return "";
|
|
15355
15402
|
var p = this.head;
|
|
15356
15403
|
var ret = "" + p.data;
|
|
@@ -19195,9 +19242,9 @@ var require_pump = __commonJS({
|
|
|
19195
19242
|
init_define_SANDBOX_BUILD();
|
|
19196
19243
|
var once = require_once();
|
|
19197
19244
|
var eos = require_end_of_stream2();
|
|
19198
|
-
var
|
|
19245
|
+
var fs66;
|
|
19199
19246
|
try {
|
|
19200
|
-
|
|
19247
|
+
fs66 = __require("fs");
|
|
19201
19248
|
} catch (e) {
|
|
19202
19249
|
}
|
|
19203
19250
|
var noop = function() {
|
|
@@ -19208,8 +19255,8 @@ var require_pump = __commonJS({
|
|
|
19208
19255
|
};
|
|
19209
19256
|
var isFS = function(stream) {
|
|
19210
19257
|
if (!ancient) return false;
|
|
19211
|
-
if (!
|
|
19212
|
-
return (stream instanceof (
|
|
19258
|
+
if (!fs66) return false;
|
|
19259
|
+
return (stream instanceof (fs66.ReadStream || noop) || stream instanceof (fs66.WriteStream || noop)) && isFn(stream.close);
|
|
19213
19260
|
};
|
|
19214
19261
|
var isRequest = function(stream) {
|
|
19215
19262
|
return stream.setHeader && isFn(stream.abort);
|
|
@@ -19269,8 +19316,8 @@ var require_pump = __commonJS({
|
|
|
19269
19316
|
var require_mkdirp_classic = __commonJS({
|
|
19270
19317
|
"../../node_modules/mkdirp-classic/index.js"(exports, module) {
|
|
19271
19318
|
init_define_SANDBOX_BUILD();
|
|
19272
|
-
var
|
|
19273
|
-
var
|
|
19319
|
+
var path74 = __require("path");
|
|
19320
|
+
var fs66 = __require("fs");
|
|
19274
19321
|
var _0777 = parseInt("0777", 8);
|
|
19275
19322
|
module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP;
|
|
19276
19323
|
function mkdirP(p, opts, f, made) {
|
|
@@ -19281,14 +19328,14 @@ var require_mkdirp_classic = __commonJS({
|
|
|
19281
19328
|
opts = { mode: opts };
|
|
19282
19329
|
}
|
|
19283
19330
|
var mode = opts.mode;
|
|
19284
|
-
var xfs = opts.fs ||
|
|
19331
|
+
var xfs = opts.fs || fs66;
|
|
19285
19332
|
if (mode === void 0) {
|
|
19286
19333
|
mode = _0777 & ~process.umask();
|
|
19287
19334
|
}
|
|
19288
19335
|
if (!made) made = null;
|
|
19289
19336
|
var cb = f || function() {
|
|
19290
19337
|
};
|
|
19291
|
-
p =
|
|
19338
|
+
p = path74.resolve(p);
|
|
19292
19339
|
xfs.mkdir(p, mode, function(er) {
|
|
19293
19340
|
if (!er) {
|
|
19294
19341
|
made = made || p;
|
|
@@ -19296,7 +19343,7 @@ var require_mkdirp_classic = __commonJS({
|
|
|
19296
19343
|
}
|
|
19297
19344
|
switch (er.code) {
|
|
19298
19345
|
case "ENOENT":
|
|
19299
|
-
mkdirP(
|
|
19346
|
+
mkdirP(path74.dirname(p), opts, function(er2, made2) {
|
|
19300
19347
|
if (er2) cb(er2, made2);
|
|
19301
19348
|
else mkdirP(p, opts, cb, made2);
|
|
19302
19349
|
});
|
|
@@ -19318,19 +19365,19 @@ var require_mkdirp_classic = __commonJS({
|
|
|
19318
19365
|
opts = { mode: opts };
|
|
19319
19366
|
}
|
|
19320
19367
|
var mode = opts.mode;
|
|
19321
|
-
var xfs = opts.fs ||
|
|
19368
|
+
var xfs = opts.fs || fs66;
|
|
19322
19369
|
if (mode === void 0) {
|
|
19323
19370
|
mode = _0777 & ~process.umask();
|
|
19324
19371
|
}
|
|
19325
19372
|
if (!made) made = null;
|
|
19326
|
-
p =
|
|
19373
|
+
p = path74.resolve(p);
|
|
19327
19374
|
try {
|
|
19328
19375
|
xfs.mkdirSync(p, mode);
|
|
19329
19376
|
made = made || p;
|
|
19330
19377
|
} catch (err0) {
|
|
19331
19378
|
switch (err0.code) {
|
|
19332
19379
|
case "ENOENT":
|
|
19333
|
-
made = sync(
|
|
19380
|
+
made = sync(path74.dirname(p), opts, made);
|
|
19334
19381
|
sync(p, opts, made);
|
|
19335
19382
|
break;
|
|
19336
19383
|
// In the case of any other error, just see if there's a dir
|
|
@@ -19360,8 +19407,8 @@ var require_tar_fs = __commonJS({
|
|
|
19360
19407
|
var tar2 = require_tar_stream();
|
|
19361
19408
|
var pump = require_pump();
|
|
19362
19409
|
var mkdirp = require_mkdirp_classic();
|
|
19363
|
-
var
|
|
19364
|
-
var
|
|
19410
|
+
var fs66 = __require("fs");
|
|
19411
|
+
var path74 = __require("path");
|
|
19365
19412
|
var os21 = __require("os");
|
|
19366
19413
|
var win32 = os21.platform() === "win32";
|
|
19367
19414
|
var noop = function() {
|
|
@@ -19372,23 +19419,23 @@ var require_tar_fs = __commonJS({
|
|
|
19372
19419
|
var normalize = !win32 ? echo : function(name) {
|
|
19373
19420
|
return name.replace(/\\/g, "/").replace(/[:?<>|]/g, "_");
|
|
19374
19421
|
};
|
|
19375
|
-
var statAll = function(
|
|
19422
|
+
var statAll = function(fs67, stat, cwd, ignore, entries, sort) {
|
|
19376
19423
|
var queue = entries || ["."];
|
|
19377
19424
|
for (var i = 0; i < queue.length; i++) {
|
|
19378
|
-
if (!inCwd(
|
|
19425
|
+
if (!inCwd(path74.resolve(cwd, queue[i]), cwd)) throw new Error(queue[i] + " is not a valid path");
|
|
19379
19426
|
}
|
|
19380
19427
|
return function loop(callback) {
|
|
19381
19428
|
if (!queue.length) return callback();
|
|
19382
19429
|
var next = queue.shift();
|
|
19383
|
-
var nextAbs =
|
|
19384
|
-
stat.call(
|
|
19430
|
+
var nextAbs = path74.join(cwd, next);
|
|
19431
|
+
stat.call(fs67, nextAbs, function(err, stat2) {
|
|
19385
19432
|
if (err) return callback(err);
|
|
19386
19433
|
if (!stat2.isDirectory()) return callback(null, next, stat2);
|
|
19387
|
-
|
|
19434
|
+
fs67.readdir(nextAbs, function(err2, files) {
|
|
19388
19435
|
if (err2) return callback(err2);
|
|
19389
19436
|
if (sort) files.sort();
|
|
19390
19437
|
for (var i2 = 0; i2 < files.length; i2++) {
|
|
19391
|
-
if (!ignore(
|
|
19438
|
+
if (!ignore(path74.join(cwd, next, files[i2]))) queue.push(path74.join(next, files[i2]));
|
|
19392
19439
|
}
|
|
19393
19440
|
callback(null, next, stat2);
|
|
19394
19441
|
});
|
|
@@ -19399,7 +19446,7 @@ var require_tar_fs = __commonJS({
|
|
|
19399
19446
|
return function(header) {
|
|
19400
19447
|
header.name = header.name.split("/").slice(level).join("/");
|
|
19401
19448
|
var linkname = header.linkname;
|
|
19402
|
-
if (linkname && (header.type === "link" ||
|
|
19449
|
+
if (linkname && (header.type === "link" || path74.isAbsolute(linkname))) {
|
|
19403
19450
|
header.linkname = linkname.split("/").slice(level).join("/");
|
|
19404
19451
|
}
|
|
19405
19452
|
return map(header);
|
|
@@ -19408,7 +19455,7 @@ var require_tar_fs = __commonJS({
|
|
|
19408
19455
|
exports.pack = function(cwd, opts) {
|
|
19409
19456
|
if (!cwd) cwd = ".";
|
|
19410
19457
|
if (!opts) opts = {};
|
|
19411
|
-
var xfs = opts.fs ||
|
|
19458
|
+
var xfs = opts.fs || fs66;
|
|
19412
19459
|
var ignore = opts.ignore || opts.filter || noop;
|
|
19413
19460
|
var map = opts.map || noop;
|
|
19414
19461
|
var mapStream = opts.mapStream || echo;
|
|
@@ -19429,7 +19476,7 @@ var require_tar_fs = __commonJS({
|
|
|
19429
19476
|
fmode |= parseInt(222, 8);
|
|
19430
19477
|
}
|
|
19431
19478
|
var onsymlink = function(filename, header) {
|
|
19432
|
-
xfs.readlink(
|
|
19479
|
+
xfs.readlink(path74.join(cwd, filename), function(err, linkname) {
|
|
19433
19480
|
if (err) return pack2.destroy(err);
|
|
19434
19481
|
header.linkname = normalize(linkname);
|
|
19435
19482
|
pack2.entry(header, onnextentry);
|
|
@@ -19470,7 +19517,7 @@ var require_tar_fs = __commonJS({
|
|
|
19470
19517
|
}
|
|
19471
19518
|
var entry = pack2.entry(header, onnextentry);
|
|
19472
19519
|
if (!entry) return;
|
|
19473
|
-
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);
|
|
19474
19521
|
rs.on("error", function(err2) {
|
|
19475
19522
|
entry.destroy(err2);
|
|
19476
19523
|
});
|
|
@@ -19495,7 +19542,7 @@ var require_tar_fs = __commonJS({
|
|
|
19495
19542
|
exports.extract = function(cwd, opts) {
|
|
19496
19543
|
if (!cwd) cwd = ".";
|
|
19497
19544
|
if (!opts) opts = {};
|
|
19498
|
-
var xfs = opts.fs ||
|
|
19545
|
+
var xfs = opts.fs || fs66;
|
|
19499
19546
|
var ignore = opts.ignore || opts.filter || noop;
|
|
19500
19547
|
var map = opts.map || noop;
|
|
19501
19548
|
var mapStream = opts.mapStream || echo;
|
|
@@ -19548,14 +19595,14 @@ var require_tar_fs = __commonJS({
|
|
|
19548
19595
|
extract.on("entry", function(header, stream, next) {
|
|
19549
19596
|
header = map(header) || header;
|
|
19550
19597
|
header.name = normalize(header.name);
|
|
19551
|
-
var name =
|
|
19598
|
+
var name = path74.join(cwd, path74.join("/", header.name));
|
|
19552
19599
|
if (ignore(name, header)) {
|
|
19553
19600
|
stream.resume();
|
|
19554
19601
|
return next();
|
|
19555
19602
|
}
|
|
19556
19603
|
var stat = function(err) {
|
|
19557
19604
|
if (err) return next(err);
|
|
19558
|
-
if (
|
|
19605
|
+
if (path74.join(name, ".") === path74.join(cwd, ".")) return next();
|
|
19559
19606
|
utimes(name, header, function(err2) {
|
|
19560
19607
|
if (err2) return next(err2);
|
|
19561
19608
|
if (win32) return next();
|
|
@@ -19565,9 +19612,9 @@ var require_tar_fs = __commonJS({
|
|
|
19565
19612
|
var onsymlink = function() {
|
|
19566
19613
|
if (win32) return next();
|
|
19567
19614
|
xfs.unlink(name, function() {
|
|
19568
|
-
var dst =
|
|
19615
|
+
var dst = path74.resolve(path74.dirname(name), header.linkname);
|
|
19569
19616
|
if (!inCwd(dst, cwd)) return next(new Error(name + " is not a valid symlink"));
|
|
19570
|
-
validateNotSymlink(xfs, dst,
|
|
19617
|
+
validateNotSymlink(xfs, dst, path74.resolve(cwd), function(err, valid) {
|
|
19571
19618
|
if (err) return next(err);
|
|
19572
19619
|
if (!valid) return next(new Error(name + " is not a valid symlink"));
|
|
19573
19620
|
xfs.symlink(header.linkname, name, stat);
|
|
@@ -19577,7 +19624,7 @@ var require_tar_fs = __commonJS({
|
|
|
19577
19624
|
var onlink = function() {
|
|
19578
19625
|
if (win32) return next();
|
|
19579
19626
|
xfs.unlink(name, function() {
|
|
19580
|
-
var srcpath =
|
|
19627
|
+
var srcpath = path74.join(cwd, path74.join("/", header.linkname));
|
|
19581
19628
|
xfs.realpath(srcpath, function(err, dst) {
|
|
19582
19629
|
if (err || !inCwd(dst, cwd)) return next(new Error(name + " is not a valid hardlink"));
|
|
19583
19630
|
xfs.link(dst, name, function(err2) {
|
|
@@ -19617,8 +19664,8 @@ var require_tar_fs = __commonJS({
|
|
|
19617
19664
|
gid: header.gid
|
|
19618
19665
|
}, stat);
|
|
19619
19666
|
}
|
|
19620
|
-
var dir =
|
|
19621
|
-
validate(xfs, dir,
|
|
19667
|
+
var dir = path74.dirname(name);
|
|
19668
|
+
validate(xfs, dir, path74.join(cwd, "."), function(err, valid) {
|
|
19622
19669
|
if (err) return next(err);
|
|
19623
19670
|
if (!valid) return next(new Error(dir + " is not a valid path"));
|
|
19624
19671
|
mkdirfix(dir, {
|
|
@@ -19645,20 +19692,20 @@ var require_tar_fs = __commonJS({
|
|
|
19645
19692
|
if (opts.finish) extract.on("finish", opts.finish);
|
|
19646
19693
|
return extract;
|
|
19647
19694
|
};
|
|
19648
|
-
function validate(
|
|
19695
|
+
function validate(fs67, name, root, cb) {
|
|
19649
19696
|
if (name === root) return cb(null, true);
|
|
19650
|
-
|
|
19697
|
+
fs67.lstat(name, function(err, st) {
|
|
19651
19698
|
if (err && err.code !== "ENOENT") return cb(err);
|
|
19652
|
-
if (err || st.isDirectory()) return validate(
|
|
19699
|
+
if (err || st.isDirectory()) return validate(fs67, path74.join(name, ".."), root, cb);
|
|
19653
19700
|
cb(null, false);
|
|
19654
19701
|
});
|
|
19655
19702
|
}
|
|
19656
|
-
function validateNotSymlink(
|
|
19703
|
+
function validateNotSymlink(fs67, name, root, cb) {
|
|
19657
19704
|
if (name === root) return cb(null, true);
|
|
19658
|
-
if (!name.startsWith(root +
|
|
19659
|
-
|
|
19705
|
+
if (!name.startsWith(root + path74.sep)) return cb(null, false);
|
|
19706
|
+
fs67.lstat(name, function(err, st) {
|
|
19660
19707
|
if (err && err.code !== "ENOENT" && err.code !== "EPERM") return cb(err);
|
|
19661
|
-
if (err || !st.isSymbolicLink()) return validateNotSymlink(
|
|
19708
|
+
if (err || !st.isSymbolicLink()) return validateNotSymlink(fs67, path74.join(name, ".."), root, cb);
|
|
19662
19709
|
cb(null, false);
|
|
19663
19710
|
});
|
|
19664
19711
|
}
|
|
@@ -19672,8 +19719,8 @@ var require_tar_fs = __commonJS({
|
|
|
19672
19719
|
});
|
|
19673
19720
|
}
|
|
19674
19721
|
function inCwd(dst, cwd) {
|
|
19675
|
-
cwd =
|
|
19676
|
-
return cwd === dst || dst.startsWith(cwd +
|
|
19722
|
+
cwd = path74.resolve(cwd);
|
|
19723
|
+
return cwd === dst || dst.startsWith(cwd + path74.sep);
|
|
19677
19724
|
}
|
|
19678
19725
|
}
|
|
19679
19726
|
});
|
|
@@ -22543,11 +22590,11 @@ var require_lib = __commonJS({
|
|
|
22543
22590
|
function lookupEscape(ch) {
|
|
22544
22591
|
return escapeMap[ch];
|
|
22545
22592
|
}
|
|
22546
|
-
function _prettifyError(
|
|
22593
|
+
function _prettifyError(path74, withInternals, err) {
|
|
22547
22594
|
if (!err.Update) {
|
|
22548
22595
|
err = new _exports.TemplateError(err);
|
|
22549
22596
|
}
|
|
22550
|
-
err.Update(
|
|
22597
|
+
err.Update(path74);
|
|
22551
22598
|
if (!withInternals) {
|
|
22552
22599
|
var old = err;
|
|
22553
22600
|
err = new Error(old.message);
|
|
@@ -22608,8 +22655,8 @@ var require_lib = __commonJS({
|
|
|
22608
22655
|
err.lineno = lineno;
|
|
22609
22656
|
err.colno = colno;
|
|
22610
22657
|
err.firstUpdate = true;
|
|
22611
|
-
err.Update = function Update(
|
|
22612
|
-
var msg = "(" + (
|
|
22658
|
+
err.Update = function Update(path74) {
|
|
22659
|
+
var msg = "(" + (path74 || "unknown path") + ")";
|
|
22613
22660
|
if (this.firstUpdate) {
|
|
22614
22661
|
if (this.lineno && this.colno) {
|
|
22615
22662
|
msg += " [Line " + this.lineno + ", Column " + this.colno + "]";
|
|
@@ -26459,7 +26506,7 @@ var require_filters = __commonJS({
|
|
|
26459
26506
|
return r.copySafeness(str3, res);
|
|
26460
26507
|
}
|
|
26461
26508
|
_exports.indent = indent;
|
|
26462
|
-
function
|
|
26509
|
+
function join53(arr, del, attr) {
|
|
26463
26510
|
del = del || "";
|
|
26464
26511
|
if (attr) {
|
|
26465
26512
|
arr = lib.map(arr, function(v) {
|
|
@@ -26468,7 +26515,7 @@ var require_filters = __commonJS({
|
|
|
26468
26515
|
}
|
|
26469
26516
|
return arr.join(del);
|
|
26470
26517
|
}
|
|
26471
|
-
_exports.join =
|
|
26518
|
+
_exports.join = join53;
|
|
26472
26519
|
function last(arr) {
|
|
26473
26520
|
return arr[arr.length - 1];
|
|
26474
26521
|
}
|
|
@@ -26821,7 +26868,7 @@ var require_loader = __commonJS({
|
|
|
26821
26868
|
};
|
|
26822
26869
|
return _setPrototypeOf(o, p);
|
|
26823
26870
|
}
|
|
26824
|
-
var
|
|
26871
|
+
var path74 = __require("path");
|
|
26825
26872
|
var _require = require_object();
|
|
26826
26873
|
var EmitterObj = _require.EmitterObj;
|
|
26827
26874
|
module.exports = /* @__PURE__ */ function(_EmitterObj) {
|
|
@@ -26831,7 +26878,7 @@ var require_loader = __commonJS({
|
|
|
26831
26878
|
}
|
|
26832
26879
|
var _proto = Loader.prototype;
|
|
26833
26880
|
_proto.resolve = function resolve13(from, to) {
|
|
26834
|
-
return
|
|
26881
|
+
return path74.resolve(path74.dirname(from), to);
|
|
26835
26882
|
};
|
|
26836
26883
|
_proto.isRelative = function isRelative(filename) {
|
|
26837
26884
|
return filename.indexOf("./") === 0 || filename.indexOf("../") === 0;
|
|
@@ -26905,8 +26952,8 @@ var require_node_loaders = __commonJS({
|
|
|
26905
26952
|
};
|
|
26906
26953
|
return _setPrototypeOf(o, p);
|
|
26907
26954
|
}
|
|
26908
|
-
var
|
|
26909
|
-
var
|
|
26955
|
+
var fs66 = __require("fs");
|
|
26956
|
+
var path74 = __require("path");
|
|
26910
26957
|
var Loader = require_loader();
|
|
26911
26958
|
var _require = require_precompiled_loader();
|
|
26912
26959
|
var PrecompiledLoader = _require.PrecompiledLoader;
|
|
@@ -26924,7 +26971,7 @@ var require_node_loaders = __commonJS({
|
|
|
26924
26971
|
_this.noCache = !!opts.noCache;
|
|
26925
26972
|
if (searchPaths) {
|
|
26926
26973
|
searchPaths = Array.isArray(searchPaths) ? searchPaths : [searchPaths];
|
|
26927
|
-
_this.searchPaths = searchPaths.map(
|
|
26974
|
+
_this.searchPaths = searchPaths.map(path74.normalize);
|
|
26928
26975
|
} else {
|
|
26929
26976
|
_this.searchPaths = ["."];
|
|
26930
26977
|
}
|
|
@@ -26934,10 +26981,10 @@ var require_node_loaders = __commonJS({
|
|
|
26934
26981
|
} catch (e) {
|
|
26935
26982
|
throw new Error("watch requires chokidar to be installed");
|
|
26936
26983
|
}
|
|
26937
|
-
var paths = _this.searchPaths.filter(
|
|
26984
|
+
var paths = _this.searchPaths.filter(fs66.existsSync);
|
|
26938
26985
|
var watcher = chokidar.watch(paths);
|
|
26939
26986
|
watcher.on("all", function(event, fullname) {
|
|
26940
|
-
fullname =
|
|
26987
|
+
fullname = path74.resolve(fullname);
|
|
26941
26988
|
if (event === "change" && fullname in _this.pathsToNames) {
|
|
26942
26989
|
_this.emit("update", _this.pathsToNames[fullname], fullname);
|
|
26943
26990
|
}
|
|
@@ -26953,9 +27000,9 @@ var require_node_loaders = __commonJS({
|
|
|
26953
27000
|
var fullpath = null;
|
|
26954
27001
|
var paths = this.searchPaths;
|
|
26955
27002
|
for (var i = 0; i < paths.length; i++) {
|
|
26956
|
-
var basePath =
|
|
26957
|
-
var p =
|
|
26958
|
-
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)) {
|
|
26959
27006
|
fullpath = p;
|
|
26960
27007
|
break;
|
|
26961
27008
|
}
|
|
@@ -26965,7 +27012,7 @@ var require_node_loaders = __commonJS({
|
|
|
26965
27012
|
}
|
|
26966
27013
|
this.pathsToNames[fullpath] = name;
|
|
26967
27014
|
var source = {
|
|
26968
|
-
src:
|
|
27015
|
+
src: fs66.readFileSync(fullpath, "utf-8"),
|
|
26969
27016
|
path: fullpath,
|
|
26970
27017
|
noCache: this.noCache
|
|
26971
27018
|
};
|
|
@@ -27017,7 +27064,7 @@ var require_node_loaders = __commonJS({
|
|
|
27017
27064
|
}
|
|
27018
27065
|
this.pathsToNames[fullpath] = name;
|
|
27019
27066
|
var source = {
|
|
27020
|
-
src:
|
|
27067
|
+
src: fs66.readFileSync(fullpath, "utf-8"),
|
|
27021
27068
|
path: fullpath,
|
|
27022
27069
|
noCache: this.noCache
|
|
27023
27070
|
};
|
|
@@ -27224,13 +27271,13 @@ var require_express_app = __commonJS({
|
|
|
27224
27271
|
"../../node_modules/nunjucks/src/express-app.js"(exports, module) {
|
|
27225
27272
|
"use strict";
|
|
27226
27273
|
init_define_SANDBOX_BUILD();
|
|
27227
|
-
var
|
|
27274
|
+
var path74 = __require("path");
|
|
27228
27275
|
module.exports = function express(env, app) {
|
|
27229
27276
|
function NunjucksView(name, opts) {
|
|
27230
27277
|
this.name = name;
|
|
27231
27278
|
this.path = name;
|
|
27232
27279
|
this.defaultEngine = opts.defaultEngine;
|
|
27233
|
-
this.ext =
|
|
27280
|
+
this.ext = path74.extname(name);
|
|
27234
27281
|
if (!this.ext && !this.defaultEngine) {
|
|
27235
27282
|
throw new Error("No default engine was specified and no extension was provided.");
|
|
27236
27283
|
}
|
|
@@ -27611,7 +27658,7 @@ var require_environment = __commonJS({
|
|
|
27611
27658
|
return _Obj2.apply(this, arguments) || this;
|
|
27612
27659
|
}
|
|
27613
27660
|
var _proto3 = Template2.prototype;
|
|
27614
|
-
_proto3.init = function init3(src, env,
|
|
27661
|
+
_proto3.init = function init3(src, env, path74, eagerCompile) {
|
|
27615
27662
|
this.env = env || new Environment();
|
|
27616
27663
|
if (lib.isObject(src)) {
|
|
27617
27664
|
switch (src.type) {
|
|
@@ -27629,7 +27676,7 @@ var require_environment = __commonJS({
|
|
|
27629
27676
|
} else {
|
|
27630
27677
|
throw new Error("src must be a string or an object describing the source");
|
|
27631
27678
|
}
|
|
27632
|
-
this.path =
|
|
27679
|
+
this.path = path74;
|
|
27633
27680
|
if (eagerCompile) {
|
|
27634
27681
|
try {
|
|
27635
27682
|
this._compile();
|
|
@@ -27781,8 +27828,8 @@ var require_precompile = __commonJS({
|
|
|
27781
27828
|
"../../node_modules/nunjucks/src/precompile.js"(exports, module) {
|
|
27782
27829
|
"use strict";
|
|
27783
27830
|
init_define_SANDBOX_BUILD();
|
|
27784
|
-
var
|
|
27785
|
-
var
|
|
27831
|
+
var fs66 = __require("fs");
|
|
27832
|
+
var path74 = __require("path");
|
|
27786
27833
|
var _require = require_lib();
|
|
27787
27834
|
var _prettifyError = _require._prettifyError;
|
|
27788
27835
|
var compiler = require_compiler();
|
|
@@ -27814,14 +27861,14 @@ var require_precompile = __commonJS({
|
|
|
27814
27861
|
if (opts.isString) {
|
|
27815
27862
|
return precompileString(input2, opts);
|
|
27816
27863
|
}
|
|
27817
|
-
var pathStats =
|
|
27864
|
+
var pathStats = fs66.existsSync(input2) && fs66.statSync(input2);
|
|
27818
27865
|
var precompiled = [];
|
|
27819
27866
|
var templates = [];
|
|
27820
27867
|
function addTemplates(dir) {
|
|
27821
|
-
|
|
27822
|
-
var filepath =
|
|
27823
|
-
var subpath = filepath.substr(
|
|
27824
|
-
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);
|
|
27825
27872
|
if (stat && stat.isDirectory()) {
|
|
27826
27873
|
subpath += "/";
|
|
27827
27874
|
if (!match(subpath, opts.exclude)) {
|
|
@@ -27833,13 +27880,13 @@ var require_precompile = __commonJS({
|
|
|
27833
27880
|
});
|
|
27834
27881
|
}
|
|
27835
27882
|
if (pathStats.isFile()) {
|
|
27836
|
-
precompiled.push(_precompile(
|
|
27883
|
+
precompiled.push(_precompile(fs66.readFileSync(input2, "utf-8"), opts.name || input2, env));
|
|
27837
27884
|
} else if (pathStats.isDirectory()) {
|
|
27838
27885
|
addTemplates(input2);
|
|
27839
27886
|
for (var i = 0; i < templates.length; i++) {
|
|
27840
|
-
var name = templates[i].replace(
|
|
27887
|
+
var name = templates[i].replace(path74.join(input2, "/"), "");
|
|
27841
27888
|
try {
|
|
27842
|
-
precompiled.push(_precompile(
|
|
27889
|
+
precompiled.push(_precompile(fs66.readFileSync(templates[i], "utf-8"), name, env));
|
|
27843
27890
|
} catch (e) {
|
|
27844
27891
|
if (opts.force) {
|
|
27845
27892
|
console.error(e);
|
|
@@ -28218,11 +28265,11 @@ var require_nunjucks = __commonJS({
|
|
|
28218
28265
|
reset: function reset() {
|
|
28219
28266
|
e = void 0;
|
|
28220
28267
|
},
|
|
28221
|
-
compile: function compile(src, env,
|
|
28268
|
+
compile: function compile(src, env, path74, eagerCompile) {
|
|
28222
28269
|
if (!e) {
|
|
28223
28270
|
configure();
|
|
28224
28271
|
}
|
|
28225
|
-
return new Template(src, env,
|
|
28272
|
+
return new Template(src, env, path74, eagerCompile);
|
|
28226
28273
|
},
|
|
28227
28274
|
render: function render2(name, ctx, cb) {
|
|
28228
28275
|
if (!e) {
|
|
@@ -28274,12 +28321,12 @@ function resolveSecrets(all, servicesConfig, serviceNames, overrides = {}) {
|
|
|
28274
28321
|
function getSecretsPath() {
|
|
28275
28322
|
return secretsPath();
|
|
28276
28323
|
}
|
|
28277
|
-
function loadSecrets(
|
|
28278
|
-
if (!(fs26.existsSync(
|
|
28324
|
+
function loadSecrets(path74) {
|
|
28325
|
+
if (!(fs26.existsSync(path74) && fs26.statSync(path74).isFile())) {
|
|
28279
28326
|
return {};
|
|
28280
28327
|
}
|
|
28281
28328
|
const result = {};
|
|
28282
|
-
for (const rawLine of fs26.readFileSync(
|
|
28329
|
+
for (const rawLine of fs26.readFileSync(path74, "utf8").split("\n")) {
|
|
28283
28330
|
const line = rawLine.trim();
|
|
28284
28331
|
if (!line || line.startsWith("#") || !line.includes("=")) {
|
|
28285
28332
|
continue;
|
|
@@ -28570,6 +28617,7 @@ function getTemplateOrNull(env, name) {
|
|
|
28570
28617
|
function composeHeader(feature2, source) {
|
|
28571
28618
|
return `# Sandbox docker-compose for feature: ${feature2}
|
|
28572
28619
|
# Generated by: ${source}
|
|
28620
|
+
# Regenerated by \`sandbox up\` whenever stale; do not hand-edit.
|
|
28573
28621
|
#
|
|
28574
28622
|
# Run from workspace root:
|
|
28575
28623
|
# docker compose up -d --build (start)
|
|
@@ -28737,6 +28785,7 @@ function renderFeatureCompose(feature2, slot, localServices, config, workspaceOv
|
|
|
28737
28785
|
base_port: portStart,
|
|
28738
28786
|
local_services: svcObjects,
|
|
28739
28787
|
services_config: servicesConfig,
|
|
28788
|
+
idle_types: IDLE_SERVICE_TYPES,
|
|
28740
28789
|
setup_configs: setupConfigs,
|
|
28741
28790
|
ports_config: portsConfig,
|
|
28742
28791
|
workspace_path: workspaceRoot,
|
|
@@ -29293,9 +29342,9 @@ async function generateWorkspaceCompose(feature2, servicesStr, workspaceRoot, te
|
|
|
29293
29342
|
continue;
|
|
29294
29343
|
}
|
|
29295
29344
|
const stripped = (svcDef["path"] ?? "").replace(/\/+$/, "");
|
|
29296
|
-
const
|
|
29297
|
-
if (
|
|
29298
|
-
pathAliases[
|
|
29345
|
+
const basename27 = stripped.includes("/") ? stripped.slice(stripped.lastIndexOf("/") + 1) : stripped;
|
|
29346
|
+
if (basename27 !== svcName) {
|
|
29347
|
+
pathAliases[basename27] = svcName;
|
|
29299
29348
|
}
|
|
29300
29349
|
}
|
|
29301
29350
|
const resolve13 = (name) => {
|
|
@@ -29477,6 +29526,18 @@ function refreshKubeconfig() {
|
|
|
29477
29526
|
}
|
|
29478
29527
|
return fs29.existsSync(kubeconfig) && fs29.statSync(kubeconfig).isFile();
|
|
29479
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
|
+
}
|
|
29480
29541
|
function ensurePortForwardsHealthy() {
|
|
29481
29542
|
const health = checkPortForwardHealth();
|
|
29482
29543
|
if (health["k8s-forwards"] ?? false) {
|
|
@@ -29488,6 +29549,10 @@ function ensurePortForwardsHealthy() {
|
|
|
29488
29549
|
error("Not restarting k8s-forwards without a kubeconfig file to mount");
|
|
29489
29550
|
return "failed";
|
|
29490
29551
|
}
|
|
29552
|
+
if (!clusterReachable()) {
|
|
29553
|
+
warn("No reachable k8s cluster \u2014 skipping port-forwards; services not run locally are unavailable");
|
|
29554
|
+
return "failed";
|
|
29555
|
+
}
|
|
29491
29556
|
const { argv, env } = composeInvocation();
|
|
29492
29557
|
try {
|
|
29493
29558
|
const result = run([...argv, "up", "-d", "--force-recreate", "k8s-forwards"], {
|
|
@@ -30065,7 +30130,8 @@ async function up(feature2, workspace, { signal } = {}) {
|
|
|
30065
30130
|
generateFeatureEnv(feature2, slot, localServices, config, workspace);
|
|
30066
30131
|
} else if (isFeatureComposeStale(feature2, slot, localServices, config, workspace)) {
|
|
30067
30132
|
staleCompose = true;
|
|
30068
|
-
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");
|
|
30069
30135
|
}
|
|
30070
30136
|
generateSupergraphConfig(feature2, slot, localServices, config, workspace);
|
|
30071
30137
|
raw(`
|
|
@@ -30164,27 +30230,119 @@ function refuseInsideOwnContainer(feature2, verb) {
|
|
|
30164
30230
|
Run it from the host: cd to the workspace and run 'sandbox ${verb}'.`, "INSIDE_OWN_CONTAINER");
|
|
30165
30231
|
}
|
|
30166
30232
|
}
|
|
30167
|
-
function
|
|
30233
|
+
function liveSessions(workspaceRoot, sessionsDir = path30.join(os8.homedir(), ".claude", "sessions")) {
|
|
30168
30234
|
let names;
|
|
30169
30235
|
try {
|
|
30170
30236
|
names = fs31.readdirSync(sessionsDir).filter((n) => n.endsWith(".json"));
|
|
30171
30237
|
} catch {
|
|
30172
30238
|
return [];
|
|
30173
30239
|
}
|
|
30174
|
-
const
|
|
30240
|
+
const sessions = [];
|
|
30175
30241
|
for (const name of names) {
|
|
30176
30242
|
try {
|
|
30177
30243
|
const s = JSON.parse(fs31.readFileSync(path30.join(sessionsDir, name), "utf8"));
|
|
30178
30244
|
const cwd = String(s.cwd ?? "");
|
|
30179
30245
|
const inWorkspace = cwd === workspaceRoot || cwd.startsWith(workspaceRoot + path30.sep);
|
|
30180
|
-
if (!inWorkspace ||
|
|
30246
|
+
if (!inWorkspace || !Number.isInteger(s.pid))
|
|
30181
30247
|
continue;
|
|
30182
30248
|
process.kill(s.pid, 0);
|
|
30183
|
-
|
|
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
|
+
});
|
|
30184
30255
|
} catch {
|
|
30185
30256
|
}
|
|
30186
30257
|
}
|
|
30187
|
-
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 };
|
|
30188
30346
|
}
|
|
30189
30347
|
function idleStopFeature(input2) {
|
|
30190
30348
|
const { feature: feature2, workspaceRoot, sessionId, reason: reason2, entrypoint, sessionsDir } = input2;
|
|
@@ -30509,7 +30667,7 @@ async function teardownNotes(feature2) {
|
|
|
30509
30667
|
graduated
|
|
30510
30668
|
};
|
|
30511
30669
|
}
|
|
30512
|
-
var _CONTINUING_REASONS, REAL_PROXY_OPS;
|
|
30670
|
+
var _CONTINUING_REASONS, LABEL_FEATURE2, LABEL_WORKDIR, REAL_PROXY_OPS;
|
|
30513
30671
|
var init_lifecycle = __esm({
|
|
30514
30672
|
"../../packages/sandbox-core/dist/lifecycle.js"() {
|
|
30515
30673
|
"use strict";
|
|
@@ -30520,17 +30678,22 @@ var init_lifecycle = __esm({
|
|
|
30520
30678
|
init_compose_file();
|
|
30521
30679
|
init_config();
|
|
30522
30680
|
init_docker();
|
|
30681
|
+
init_dot_dir();
|
|
30523
30682
|
init_errors();
|
|
30524
30683
|
init_feature_routing();
|
|
30525
30684
|
init_generate();
|
|
30685
|
+
init_global_config();
|
|
30526
30686
|
init_graduate();
|
|
30527
30687
|
init_k8s();
|
|
30528
30688
|
init_notes();
|
|
30689
|
+
init_proc();
|
|
30529
30690
|
init_reporter();
|
|
30530
30691
|
init_secrets();
|
|
30531
30692
|
init_slots();
|
|
30532
30693
|
init_strategies();
|
|
30533
30694
|
_CONTINUING_REASONS = /* @__PURE__ */ new Set(["clear", "resume"]);
|
|
30695
|
+
LABEL_FEATURE2 = "com.sandbox.local.feature";
|
|
30696
|
+
LABEL_WORKDIR = "com.docker.compose.project.working_dir";
|
|
30534
30697
|
REAL_PROXY_OPS = {
|
|
30535
30698
|
generate: () => generateCaddyfile(),
|
|
30536
30699
|
slots: () => getAllSlots(),
|
|
@@ -31429,6 +31592,7 @@ async function run3(args) {
|
|
|
31429
31592
|
console.log(`
|
|
31430
31593
|
${Colors.BOLD}=== sandbox up: ${feature2} ===${Colors.RESET}
|
|
31431
31594
|
`);
|
|
31595
|
+
if (args.dev) process.env.SANDBOX_DEV = "1";
|
|
31432
31596
|
printSummary(await up(feature2, workspace, { signal: interruptSignal() }));
|
|
31433
31597
|
}
|
|
31434
31598
|
var init_up = __esm({
|
|
@@ -31949,6 +32113,17 @@ async function run8(args) {
|
|
|
31949
32113
|
stopIfIdle();
|
|
31950
32114
|
return;
|
|
31951
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
|
+
}
|
|
31952
32127
|
const dotenv = loadDotenv(process.cwd());
|
|
31953
32128
|
const feature2 = args.feature || dotenv["FEATURE"];
|
|
31954
32129
|
if (!feature2) {
|
|
@@ -32507,7 +32682,7 @@ models:
|
|
|
32507
32682
|
claude-sonnet-5: { input: 3, output: 15, cacheWrite: 3.75, cacheRead: 0.30 }
|
|
32508
32683
|
claude-haiku-4-5: { input: 1, output: 5, cacheWrite: 1.25, cacheRead: 0.10 }
|
|
32509
32684
|
`;
|
|
32510
|
-
SANDBOX_MCP_SERVER = "
|
|
32685
|
+
SANDBOX_MCP_SERVER = "werkmeister";
|
|
32511
32686
|
REGISTRY_DEFAULTS = {
|
|
32512
32687
|
archetypes: {
|
|
32513
32688
|
"node-web": {
|
|
@@ -32560,16 +32735,19 @@ function discoverFragments(reposRoot) {
|
|
|
32560
32735
|
continue;
|
|
32561
32736
|
if (fs40.statSync(path39.join(reposRoot, repo, ".git"), { throwIfNoEntry: false })?.isFile())
|
|
32562
32737
|
continue;
|
|
32563
|
-
|
|
32564
|
-
try {
|
|
32565
|
-
doc = (0, import_yaml2.parse)(fs40.readFileSync(file, "utf8"));
|
|
32566
|
-
} catch (e) {
|
|
32567
|
-
throw new RegistryError(`${file}: ${e instanceof Error ? e.message : String(e)}`);
|
|
32568
|
-
}
|
|
32569
|
-
out.push({ repo, path: file, doc: isDict7(doc) ? doc : {} });
|
|
32738
|
+
out.push(readFragment(repo, file));
|
|
32570
32739
|
}
|
|
32571
32740
|
return out;
|
|
32572
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
|
+
}
|
|
32573
32751
|
function expandPlaceholders(value, paths) {
|
|
32574
32752
|
if (typeof value === "string") {
|
|
32575
32753
|
return value.replace(PLACEHOLDER, (_m, key) => paths[key]);
|
|
@@ -32731,6 +32909,18 @@ function readLocalOverlay(localPath) {
|
|
|
32731
32909
|
throw new RegistryError(`${localPath}: must be a map of registry keys`);
|
|
32732
32910
|
return doc;
|
|
32733
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
|
+
}
|
|
32734
32924
|
function syncRegistry(opts = {}) {
|
|
32735
32925
|
const label = "config services.yaml";
|
|
32736
32926
|
const reposRoot = opts.reposRoot ?? getReposRoot();
|
|
@@ -37628,8 +37818,8 @@ var require_utils = __commonJS({
|
|
|
37628
37818
|
}
|
|
37629
37819
|
return ind;
|
|
37630
37820
|
}
|
|
37631
|
-
function removeDotSegments(
|
|
37632
|
-
let input2 =
|
|
37821
|
+
function removeDotSegments(path74) {
|
|
37822
|
+
let input2 = path74;
|
|
37633
37823
|
const output = [];
|
|
37634
37824
|
let nextSlash = -1;
|
|
37635
37825
|
let len = 0;
|
|
@@ -38039,8 +38229,8 @@ var require_schemes = __commonJS({
|
|
|
38039
38229
|
}
|
|
38040
38230
|
if (wsComponent.resourceName) {
|
|
38041
38231
|
const queryIndex = wsComponent.resourceName.indexOf("?");
|
|
38042
|
-
const
|
|
38043
|
-
wsComponent.path =
|
|
38232
|
+
const path74 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
|
|
38233
|
+
wsComponent.path = path74 && path74 !== "/" ? path74 : void 0;
|
|
38044
38234
|
wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
|
|
38045
38235
|
wsComponent.resourceName = void 0;
|
|
38046
38236
|
}
|
|
@@ -49512,10 +49702,10 @@ var require_lib3 = __commonJS({
|
|
|
49512
49702
|
exports.analyse = analyse;
|
|
49513
49703
|
var detectFile = (filepath, opts = {}) => new Promise((resolve13, reject) => {
|
|
49514
49704
|
let fd;
|
|
49515
|
-
const
|
|
49705
|
+
const fs66 = (0, node_1.default)();
|
|
49516
49706
|
const handler = (err, buffer) => {
|
|
49517
49707
|
if (fd) {
|
|
49518
|
-
|
|
49708
|
+
fs66.closeSync(fd);
|
|
49519
49709
|
}
|
|
49520
49710
|
if (err) {
|
|
49521
49711
|
reject(err);
|
|
@@ -49527,9 +49717,9 @@ var require_lib3 = __commonJS({
|
|
|
49527
49717
|
};
|
|
49528
49718
|
const sampleSize = (opts === null || opts === void 0 ? void 0 : opts.sampleSize) || 0;
|
|
49529
49719
|
if (sampleSize > 0) {
|
|
49530
|
-
fd =
|
|
49720
|
+
fd = fs66.openSync(filepath, "r");
|
|
49531
49721
|
let sample = Buffer.allocUnsafe(sampleSize);
|
|
49532
|
-
|
|
49722
|
+
fs66.read(fd, sample, 0, sampleSize, opts.offset, (err, bytesRead) => {
|
|
49533
49723
|
if (err) {
|
|
49534
49724
|
handler(err, null);
|
|
49535
49725
|
} else {
|
|
@@ -49541,22 +49731,22 @@ var require_lib3 = __commonJS({
|
|
|
49541
49731
|
});
|
|
49542
49732
|
return;
|
|
49543
49733
|
}
|
|
49544
|
-
|
|
49734
|
+
fs66.readFile(filepath, handler);
|
|
49545
49735
|
});
|
|
49546
49736
|
exports.detectFile = detectFile;
|
|
49547
49737
|
var detectFileSync = (filepath, opts = {}) => {
|
|
49548
|
-
const
|
|
49738
|
+
const fs66 = (0, node_1.default)();
|
|
49549
49739
|
if (opts && opts.sampleSize) {
|
|
49550
|
-
const fd =
|
|
49740
|
+
const fd = fs66.openSync(filepath, "r");
|
|
49551
49741
|
let sample = Buffer.allocUnsafe(opts.sampleSize);
|
|
49552
|
-
const bytesRead =
|
|
49742
|
+
const bytesRead = fs66.readSync(fd, sample, 0, opts.sampleSize, opts.offset);
|
|
49553
49743
|
if (bytesRead < opts.sampleSize) {
|
|
49554
49744
|
sample = sample.subarray(0, bytesRead);
|
|
49555
49745
|
}
|
|
49556
|
-
|
|
49746
|
+
fs66.closeSync(fd);
|
|
49557
49747
|
return (0, exports.detect)(sample);
|
|
49558
49748
|
}
|
|
49559
|
-
return (0, exports.detect)(
|
|
49749
|
+
return (0, exports.detect)(fs66.readFileSync(filepath));
|
|
49560
49750
|
};
|
|
49561
49751
|
exports.detectFileSync = detectFileSync;
|
|
49562
49752
|
exports.default = {
|
|
@@ -54253,7 +54443,7 @@ function cacheSet(key, v) {
|
|
|
54253
54443
|
await fs45.writeFile(tmp, JSON.stringify(store), "utf-8");
|
|
54254
54444
|
await fs45.rename(tmp, file);
|
|
54255
54445
|
} catch (e) {
|
|
54256
|
-
console.error(`[
|
|
54446
|
+
console.error(`[werkmeister] cache write failed for ${key}:`, e.message);
|
|
54257
54447
|
}
|
|
54258
54448
|
});
|
|
54259
54449
|
writeChain = run34.catch(() => {
|
|
@@ -54266,7 +54456,7 @@ function runBatchOnce(keys, job) {
|
|
|
54266
54456
|
return;
|
|
54267
54457
|
for (const k of fresh)
|
|
54268
54458
|
inFlight.add(k);
|
|
54269
|
-
void Promise.resolve().then(() => job(fresh)).catch((e) => console.error("[
|
|
54459
|
+
void Promise.resolve().then(() => job(fresh)).catch((e) => console.error("[werkmeister] background cache refresh failed:", e.message)).finally(() => {
|
|
54270
54460
|
for (const k of fresh)
|
|
54271
54461
|
inFlight.delete(k);
|
|
54272
54462
|
});
|
|
@@ -54323,8 +54513,8 @@ function parseRect(size, position) {
|
|
|
54323
54513
|
const [x, y] = (position ?? "60,60").split(",").map(Number);
|
|
54324
54514
|
return { width, height: height2, x, y };
|
|
54325
54515
|
}
|
|
54326
|
-
async function wdFetch(port,
|
|
54327
|
-
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}`, {
|
|
54328
54518
|
method,
|
|
54329
54519
|
headers: body !== void 0 ? { "Content-Type": "application/json" } : void 0,
|
|
54330
54520
|
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
@@ -54332,7 +54522,7 @@ async function wdFetch(port, path73, method, body) {
|
|
|
54332
54522
|
const data = await r.json().catch(() => ({}));
|
|
54333
54523
|
if (!r.ok || data?.value?.error) {
|
|
54334
54524
|
const msg = data?.value?.message || data?.value?.error || `HTTP ${r.status}`;
|
|
54335
|
-
throw new Error(`safaridriver ${method} ${
|
|
54525
|
+
throw new Error(`safaridriver ${method} ${path74} failed: ${msg}`);
|
|
54336
54526
|
}
|
|
54337
54527
|
return data.value;
|
|
54338
54528
|
}
|
|
@@ -55170,11 +55360,131 @@ var init_issue_snapshot = __esm({
|
|
|
55170
55360
|
}
|
|
55171
55361
|
});
|
|
55172
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
|
+
|
|
55173
55483
|
// ../../packages/sandbox-core/dist/pr_lifecycle.js
|
|
55174
55484
|
import { execFile } from "node:child_process";
|
|
55175
|
-
import * as
|
|
55485
|
+
import * as fs50 from "node:fs";
|
|
55176
55486
|
import * as os16 from "node:os";
|
|
55177
|
-
import * as
|
|
55487
|
+
import * as path58 from "node:path";
|
|
55178
55488
|
async function httpStatus(url) {
|
|
55179
55489
|
try {
|
|
55180
55490
|
return (await fetch(url, { redirect: "manual", signal: AbortSignal.timeout(5e3) })).status;
|
|
@@ -55191,27 +55501,51 @@ function defaultDeps() {
|
|
|
55191
55501
|
archetypes: loadArchetypes(),
|
|
55192
55502
|
repos: loadReposConfig(),
|
|
55193
55503
|
tracker: issueTracker(),
|
|
55194
|
-
|
|
55195
|
-
doneStatus: String(overrides["issue_done_status"] ?? (loadIssueTracker() === "github" ? "closed" : "Done")),
|
|
55504
|
+
statuses: resolveIssueStatuses(overrides, loadIssueTracker()),
|
|
55196
55505
|
commitRecord: commitAndPush,
|
|
55197
55506
|
remove: (name) => removeWorkspace(name, { closedOut: true }),
|
|
55198
55507
|
terminals: (ws) => openTerminalSessions(ws),
|
|
55199
55508
|
sourceOf: async (dir) => (await worktreeSources([dir]))[0] ?? null,
|
|
55200
|
-
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
|
+
}
|
|
55201
55516
|
};
|
|
55202
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
|
+
}
|
|
55203
55537
|
function prFileDir(ws) {
|
|
55204
55538
|
let names;
|
|
55205
55539
|
try {
|
|
55206
|
-
names =
|
|
55540
|
+
names = fs50.readdirSync(path58.join(ws, "stages")).sort();
|
|
55207
55541
|
} catch {
|
|
55208
|
-
return
|
|
55542
|
+
return path58.join(dotDir(ws), "notes");
|
|
55209
55543
|
}
|
|
55210
55544
|
const dir = names.find((n) => /^\d+_propose$/.test(n)) ?? names.find((n) => /^\d+_ship$/.test(n));
|
|
55211
|
-
return dir ?
|
|
55545
|
+
return dir ? path58.join(ws, "stages", dir, "output") : null;
|
|
55212
55546
|
}
|
|
55213
55547
|
function parsePrFile(p) {
|
|
55214
|
-
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"));
|
|
55215
55549
|
let fm = null;
|
|
55216
55550
|
let yamlError = "";
|
|
55217
55551
|
try {
|
|
@@ -55225,7 +55559,7 @@ function parsePrFile(p) {
|
|
|
55225
55559
|
const heading2 = /^#\s+(.+)$/m.exec(m[2]);
|
|
55226
55560
|
return {
|
|
55227
55561
|
path: p,
|
|
55228
|
-
dir:
|
|
55562
|
+
dir: path58.basename(p).replace(/^pr-/, "").replace(/\.md$/, ""),
|
|
55229
55563
|
fm,
|
|
55230
55564
|
title: heading2 ? heading2[1].trim() : "",
|
|
55231
55565
|
body: heading2 ? m[2].slice(heading2.index + heading2[0].length).replace(/^\s*\n/, "") : m[2]
|
|
@@ -55237,10 +55571,10 @@ function readPrFiles(ws) {
|
|
|
55237
55571
|
return [];
|
|
55238
55572
|
let names = [];
|
|
55239
55573
|
try {
|
|
55240
|
-
names =
|
|
55574
|
+
names = fs50.readdirSync(dir).filter((n) => /^pr-.+\.md$/.test(n)).sort();
|
|
55241
55575
|
} catch {
|
|
55242
55576
|
}
|
|
55243
|
-
return names.map((n) => parsePrFile(
|
|
55577
|
+
return names.map((n) => parsePrFile(path58.join(dir, n)));
|
|
55244
55578
|
}
|
|
55245
55579
|
function writePrFields(file, fields) {
|
|
55246
55580
|
updateNoteStatus(file.path, fields);
|
|
@@ -55255,18 +55589,18 @@ function passTable(text) {
|
|
|
55255
55589
|
}
|
|
55256
55590
|
function stagesCheck(ws) {
|
|
55257
55591
|
const prDir = prFileDir(ws);
|
|
55258
|
-
if (!
|
|
55259
|
-
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));
|
|
55260
55594
|
return {
|
|
55261
55595
|
name: "stages",
|
|
55262
55596
|
ok: open.length === 0,
|
|
55263
55597
|
detail: open.length ? `open decision notes: ${open.join(", ")}` : "no open decisions"
|
|
55264
55598
|
};
|
|
55265
55599
|
}
|
|
55266
|
-
const own = prDir ?
|
|
55267
|
-
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) => {
|
|
55268
55602
|
try {
|
|
55269
|
-
return !
|
|
55603
|
+
return !fs50.readdirSync(path58.join(ws, "stages", d, "output")).some((f) => !f.startsWith("."));
|
|
55270
55604
|
} catch {
|
|
55271
55605
|
return true;
|
|
55272
55606
|
}
|
|
@@ -55278,14 +55612,14 @@ function stagesCheck(ws) {
|
|
|
55278
55612
|
};
|
|
55279
55613
|
}
|
|
55280
55614
|
function exerciseFile(ws) {
|
|
55281
|
-
if (!
|
|
55282
|
-
const p =
|
|
55283
|
-
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;
|
|
55284
55618
|
}
|
|
55285
55619
|
for (const name of ["exercise.md", "verify.md"]) {
|
|
55286
|
-
for (const d of
|
|
55287
|
-
const p =
|
|
55288
|
-
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))
|
|
55289
55623
|
return p;
|
|
55290
55624
|
}
|
|
55291
55625
|
}
|
|
@@ -55313,7 +55647,7 @@ function mapTouched(files, repo) {
|
|
|
55313
55647
|
}
|
|
55314
55648
|
function readEnv(ws, key) {
|
|
55315
55649
|
try {
|
|
55316
|
-
for (const line of
|
|
55650
|
+
for (const line of fs50.readFileSync(path58.join(ws, ".env"), "utf8").split(/\r?\n/)) {
|
|
55317
55651
|
const eq = line.indexOf("=");
|
|
55318
55652
|
if (eq > 0 && line.slice(0, eq).trim() === key)
|
|
55319
55653
|
return line.slice(eq + 1).trim();
|
|
@@ -55327,14 +55661,24 @@ async function runGate(ws, file, deps = defaultDeps()) {
|
|
|
55327
55661
|
const add = (name, ok, detail) => checks.push({ name, ok, detail });
|
|
55328
55662
|
const table = passTable(file.body);
|
|
55329
55663
|
add("acceptance", table === null, table ? `PR file ${table}` : "all PASS");
|
|
55330
|
-
const dir =
|
|
55664
|
+
const dir = path58.join(ws, file.dir);
|
|
55331
55665
|
const status2 = await deps.run("git", ["-C", dir, "status", "--porcelain"]);
|
|
55332
55666
|
add("clean", status2.code === 0 && status2.stdout.trim() === "", status2.code ? status2.stderr.trim() : status2.stdout.trim() ? `uncommitted:
|
|
55333
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
|
+
}
|
|
55334
55679
|
const cfg = loadConfigFile(ws, null);
|
|
55335
55680
|
const feature2 = String(cfg["feature"] ?? readEnv(ws, "FEATURE")).trim();
|
|
55336
55681
|
const local = new Set(String(cfg["services"] ?? "").split(",").map((s) => s.trim()).filter(Boolean));
|
|
55337
|
-
const repo = deps.repos[file.dir] ?? {};
|
|
55338
55682
|
const ps = await deps.run("docker", ["ps", "--format", "{{.Names}}"]);
|
|
55339
55683
|
const running = new Set(ps.stdout.split("\n").map((s) => s.trim()).filter(Boolean));
|
|
55340
55684
|
const containerFor = (svc) => {
|
|
@@ -55352,7 +55696,7 @@ ${status2.stdout.trimEnd()}` : "clean");
|
|
|
55352
55696
|
const touched = diff.code === 0 ? mapTouched(diff.stdout.split("\n").filter(Boolean), repo) : [];
|
|
55353
55697
|
const skips = file.fm["skip_tests"] ?? {};
|
|
55354
55698
|
for (const svc of touched) {
|
|
55355
|
-
const cmd = serviceTestCommand(svc,
|
|
55699
|
+
const cmd = serviceTestCommand(svc, services[svc], deps.archetypes);
|
|
55356
55700
|
const why = reason(skips[svc]);
|
|
55357
55701
|
if (!cmd) {
|
|
55358
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> }`");
|
|
@@ -55363,16 +55707,17 @@ ${status2.stdout.trimEnd()}` : "clean");
|
|
|
55363
55707
|
add(`test ${svc}`, Boolean(why), why ? `skipped: ${why}` : "no running container for this repo \u2014 run `sandbox up`");
|
|
55364
55708
|
continue;
|
|
55365
55709
|
}
|
|
55710
|
+
touchExecMarker(ws);
|
|
55366
55711
|
const res = await deps.run("docker", ["exec", container, "sh", "-c", cmd]);
|
|
55367
55712
|
add(`test ${svc}`, res.code === 0, `${cmd} \u2192 exit ${res.code}${res.code ? `
|
|
55368
55713
|
${(res.stdout + res.stderr).trimEnd().split("\n").slice(-15).join("\n")}` : ""}`);
|
|
55369
55714
|
}
|
|
55370
55715
|
const evidence = exerciseFile(ws);
|
|
55371
|
-
const evidenceProblem = evidence ? passTable(
|
|
55372
|
-
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");
|
|
55373
55718
|
const slot = Number(readEnv(ws, "SANDBOX_SLOT"));
|
|
55374
55719
|
for (const svc of touched.filter((s) => local.has(s))) {
|
|
55375
|
-
const def =
|
|
55720
|
+
const def = services[svc] ?? {};
|
|
55376
55721
|
if (!running.has(`${feature2}-${svc}`)) {
|
|
55377
55722
|
add(`baseline ${svc}`, false, "container not running \u2014 run `sandbox up`");
|
|
55378
55723
|
continue;
|
|
@@ -55393,6 +55738,7 @@ ${(res.stdout + res.stderr).trimEnd().split("\n").slice(-15).join("\n")}` : ""}`
|
|
|
55393
55738
|
else if (!container)
|
|
55394
55739
|
add("release", false, "no running container for this repo \u2014 run `sandbox up`");
|
|
55395
55740
|
else {
|
|
55741
|
+
touchExecMarker(ws);
|
|
55396
55742
|
const res = await deps.run("docker", ["exec", container, "sh", "-c", repo["release_check"]]);
|
|
55397
55743
|
add("release", res.code === 0, `${repo["release_check"]} \u2192 exit ${res.code}${res.code ? `
|
|
55398
55744
|
${(res.stdout + res.stderr).trimEnd().split("\n").slice(-5).join("\n")}` : ""}`);
|
|
@@ -55439,7 +55785,7 @@ async function openPrs(ws, only, deps = defaultDeps()) {
|
|
|
55439
55785
|
lines.push(...formatGate(f.dir, g));
|
|
55440
55786
|
continue;
|
|
55441
55787
|
}
|
|
55442
|
-
const dir =
|
|
55788
|
+
const dir = path58.join(ws, f.dir);
|
|
55443
55789
|
const branch = String(f.fm["branch"] ?? "");
|
|
55444
55790
|
const push = await deps.run("git", ["-C", dir, "push", "-u", "origin", branch]);
|
|
55445
55791
|
if (push.code !== 0) {
|
|
@@ -55463,8 +55809,8 @@ async function openPrs(ws, only, deps = defaultDeps()) {
|
|
|
55463
55809
|
], dir);
|
|
55464
55810
|
let url = existing2.code === 0 ? existing2.stdout.trim() : "";
|
|
55465
55811
|
if (!url) {
|
|
55466
|
-
const bodyFile =
|
|
55467
|
-
|
|
55812
|
+
const bodyFile = path58.join(fs50.mkdtempSync(path58.join(os16.tmpdir(), "sandbox-pr-")), "body.md");
|
|
55813
|
+
fs50.writeFileSync(bodyFile, f.body);
|
|
55468
55814
|
const created = await deps.run("gh", [
|
|
55469
55815
|
"pr",
|
|
55470
55816
|
"create",
|
|
@@ -55479,7 +55825,7 @@ async function openPrs(ws, only, deps = defaultDeps()) {
|
|
|
55479
55825
|
"--body-file",
|
|
55480
55826
|
bodyFile
|
|
55481
55827
|
], dir);
|
|
55482
|
-
|
|
55828
|
+
fs50.rmSync(path58.dirname(bodyFile), { recursive: true, force: true });
|
|
55483
55829
|
url = created.stdout.trim().split("\n").pop() ?? "";
|
|
55484
55830
|
if (created.code !== 0 || !url.startsWith("http")) {
|
|
55485
55831
|
ok = false;
|
|
@@ -55488,53 +55834,76 @@ async function openPrs(ws, only, deps = defaultDeps()) {
|
|
|
55488
55834
|
}
|
|
55489
55835
|
}
|
|
55490
55836
|
writePrFields(f, { status: "open", url, ...await deps.session(ws) });
|
|
55491
|
-
opened.push(
|
|
55837
|
+
opened.push(path58.relative(ws, f.path));
|
|
55492
55838
|
lines.push(`${f.dir}: open ${url}`);
|
|
55493
55839
|
const ref = issueIdOf(loadConfigFile(ws, null));
|
|
55494
55840
|
if (ref) {
|
|
55495
|
-
|
|
55496
|
-
|
|
55497
|
-
}
|
|
55498
|
-
|
|
55499
|
-
|
|
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}`);
|
|
55500
55846
|
}
|
|
55501
55847
|
}
|
|
55502
55848
|
if (opened.length)
|
|
55503
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));
|
|
55504
55852
|
return { ok, lines };
|
|
55505
55853
|
}
|
|
55506
55854
|
async function refreshPrStatus(ws, deps = defaultDeps()) {
|
|
55507
55855
|
const lines = [];
|
|
55508
55856
|
const changed = [];
|
|
55857
|
+
const seen = {
|
|
55858
|
+
shipped: [],
|
|
55859
|
+
merged: [],
|
|
55860
|
+
closed: [],
|
|
55861
|
+
changesRequested: [],
|
|
55862
|
+
reReview: []
|
|
55863
|
+
};
|
|
55509
55864
|
for (const f of readPrFiles(ws)) {
|
|
55510
55865
|
const before = statusOf(f);
|
|
55866
|
+
const reviewBefore = f.fm["review"];
|
|
55511
55867
|
if (before === "open" && f.fm["url"]) {
|
|
55512
55868
|
const v = await deps.run("gh", [
|
|
55513
55869
|
"pr",
|
|
55514
55870
|
"view",
|
|
55515
55871
|
String(f.fm["url"]),
|
|
55516
55872
|
"--json",
|
|
55517
|
-
"state,mergeCommit"
|
|
55873
|
+
"state,mergeCommit,reviewDecision,latestReviews"
|
|
55518
55874
|
]);
|
|
55519
55875
|
if (v.code !== 0) {
|
|
55520
55876
|
lines.push(`${f.dir}: gh pr view failed \u2014 ${v.stderr.trim()}`);
|
|
55521
55877
|
continue;
|
|
55522
55878
|
}
|
|
55523
55879
|
const view2 = JSON.parse(v.stdout);
|
|
55524
|
-
|
|
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") {
|
|
55525
55883
|
writePrFields(f, { status: "merged", merge_sha: view2.mergeCommit?.oid ?? "" });
|
|
55526
|
-
|
|
55884
|
+
seen.merged.push(url);
|
|
55885
|
+
} else if (view2.state === "CLOSED") {
|
|
55527
55886
|
writePrFields(f, {
|
|
55528
55887
|
status: "closed",
|
|
55529
55888
|
reason: reason(f.fm["reason"]) || "closed on GitHub"
|
|
55530
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
|
+
}
|
|
55531
55900
|
}
|
|
55532
55901
|
if (statusOf(f) === "merged") {
|
|
55533
55902
|
const repo = deps.repos[f.dir] ?? {};
|
|
55534
55903
|
if (repo["ship"] !== "tag") {
|
|
55535
55904
|
writePrFields(f, { status: "shipped" });
|
|
55536
55905
|
} else {
|
|
55537
|
-
const dir =
|
|
55906
|
+
const dir = path58.join(ws, f.dir);
|
|
55538
55907
|
await deps.run("git", ["-C", dir, "fetch", "-q", "--tags", "origin"]);
|
|
55539
55908
|
const tags = await deps.run("git", [
|
|
55540
55909
|
"-C",
|
|
@@ -55552,16 +55921,43 @@ async function refreshPrStatus(ws, deps = defaultDeps()) {
|
|
|
55552
55921
|
}
|
|
55553
55922
|
}
|
|
55554
55923
|
const after = statusOf(f);
|
|
55555
|
-
if (after !==
|
|
55556
|
-
|
|
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));
|
|
55557
55928
|
lines.push(`${f.dir}: ${after}${f.fm["release"] ? ` (${f.fm["release"]})` : ""}${f.fm["url"] ? ` ${f.fm["url"]}` : ""}`);
|
|
55558
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));
|
|
55559
55935
|
if (changed.length)
|
|
55560
55936
|
lines.push((await deps.commitRecord(ws, "chore: record PR status", changed)).message);
|
|
55561
55937
|
return { lines, changed: changed.length > 0 };
|
|
55562
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
|
+
}
|
|
55563
55959
|
async function callbackFields(ws, env) {
|
|
55564
|
-
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;
|
|
55565
55961
|
if (!session)
|
|
55566
55962
|
return {};
|
|
55567
55963
|
const host = env["CLAUDE_CODE_HOST_SESSION_ID"];
|
|
@@ -55579,23 +55975,23 @@ function callbackMessage(status2, url, release) {
|
|
|
55579
55975
|
function takeLock(file) {
|
|
55580
55976
|
for (let i = 0; i < 2; i++) {
|
|
55581
55977
|
try {
|
|
55582
|
-
|
|
55978
|
+
fs50.closeSync(fs50.openSync(file, "wx"));
|
|
55583
55979
|
return true;
|
|
55584
55980
|
} catch (e) {
|
|
55585
55981
|
if (e.code !== "EEXIST")
|
|
55586
55982
|
throw e;
|
|
55587
55983
|
try {
|
|
55588
|
-
if (Date.now() -
|
|
55984
|
+
if (Date.now() - fs50.statSync(file).mtimeMs < LOCK_STALE_MS)
|
|
55589
55985
|
return false;
|
|
55590
|
-
|
|
55986
|
+
fs50.rmSync(file, { force: true });
|
|
55591
55987
|
} catch {
|
|
55592
55988
|
}
|
|
55593
55989
|
}
|
|
55594
55990
|
}
|
|
55595
55991
|
return false;
|
|
55596
55992
|
}
|
|
55597
|
-
async function claimCallbacks(workspaces, opts, deps = defaultDeps(), lockFile =
|
|
55598
|
-
|
|
55993
|
+
async function claimCallbacks(workspaces, opts, deps = defaultDeps(), lockFile = path58.join(sandboxHome(), "pr-callbacks.lock")) {
|
|
55994
|
+
fs50.mkdirSync(path58.dirname(lockFile), { recursive: true });
|
|
55599
55995
|
if (!takeLock(lockFile))
|
|
55600
55996
|
return { claimed: [], lines: ["another PR watch pass is running"] };
|
|
55601
55997
|
const claimed2 = [];
|
|
@@ -55614,7 +56010,7 @@ async function claimCallbacks(workspaces, opts, deps = defaultDeps(), lockFile =
|
|
|
55614
56010
|
continue;
|
|
55615
56011
|
if (before.some((f) => statusOf(f) === "open" || statusOf(f) === "merged"))
|
|
55616
56012
|
for (const l of (await refreshPrStatus(ws, deps)).lines)
|
|
55617
|
-
lines.push(`${
|
|
56013
|
+
lines.push(`${path58.basename(ws)}/${l}`);
|
|
55618
56014
|
const changed = [];
|
|
55619
56015
|
for (const f of callbacks()) {
|
|
55620
56016
|
const status2 = statusOf(f);
|
|
@@ -55632,23 +56028,23 @@ async function claimCallbacks(workspaces, opts, deps = defaultDeps(), lockFile =
|
|
|
55632
56028
|
message
|
|
55633
56029
|
});
|
|
55634
56030
|
writePrFields(f, SETTLED.has(status2) ? { session: void 0, host_session: void 0, notified: void 0 } : { notified: status2 });
|
|
55635
|
-
changed.push(
|
|
56031
|
+
changed.push(path58.relative(ws, f.path));
|
|
55636
56032
|
}
|
|
55637
56033
|
if (changed.length)
|
|
55638
56034
|
lines.push((await deps.commitRecord(ws, "chore: record PR callback", changed)).message);
|
|
55639
56035
|
}
|
|
55640
56036
|
} finally {
|
|
55641
|
-
|
|
56037
|
+
fs50.rmSync(lockFile, { force: true });
|
|
55642
56038
|
}
|
|
55643
56039
|
return { claimed: claimed2, lines };
|
|
55644
56040
|
}
|
|
55645
56041
|
async function deliverHeadless(cb) {
|
|
55646
|
-
const name =
|
|
56042
|
+
const name = path58.basename(cb.ws);
|
|
55647
56043
|
if ((await resolveSessionSource(name, cb.session)).sessionId !== cb.session)
|
|
55648
56044
|
throw new WorkspaceError(`session ${cb.session} is gone`);
|
|
55649
56045
|
await sendToClaudeSession(name, cb.message, cb.session);
|
|
55650
56046
|
}
|
|
55651
|
-
async function watchPrs(opts, deps = defaultDeps(), send = deliverHeadless) {
|
|
56047
|
+
async function watchPrs(opts, deps = defaultDeps(), send = deliverHeadless, idleLock = path58.join(sandboxHome(), "idle-stop.lock")) {
|
|
55652
56048
|
const { workspaces } = await listWorkspaces();
|
|
55653
56049
|
const { claimed: claimed2, lines } = await claimCallbacks(workspaces.map((w) => w.path), opts, deps);
|
|
55654
56050
|
const desktop = [];
|
|
@@ -55664,6 +56060,15 @@ async function watchPrs(opts, deps = defaultDeps(), send = deliverHeadless) {
|
|
|
55664
56060
|
lines.push(`${cb.dir}: could not resume session ${cb.session}: ${e.message}`);
|
|
55665
56061
|
}
|
|
55666
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");
|
|
55667
56072
|
return { ok: true, lines, desktop };
|
|
55668
56073
|
}
|
|
55669
56074
|
async function adoptPr(ws, opts, deps = defaultDeps()) {
|
|
@@ -55677,8 +56082,8 @@ async function adoptPr(ws, opts, deps = defaultDeps()) {
|
|
|
55677
56082
|
throw new WorkspaceError(`Name the repo \u2014 this workspace has ${wts.map((w) => w.repo).join(", ") || "no code worktree"}.`);
|
|
55678
56083
|
repo = wts[0].repo;
|
|
55679
56084
|
}
|
|
55680
|
-
const file =
|
|
55681
|
-
if (
|
|
56085
|
+
const file = path58.join(dir, `pr-${repo}.md`);
|
|
56086
|
+
if (fs50.existsSync(file))
|
|
55682
56087
|
throw new WorkspaceError(`${file} already exists \u2014 remove it first to adopt.`);
|
|
55683
56088
|
let fm;
|
|
55684
56089
|
let title;
|
|
@@ -55690,7 +56095,7 @@ async function adoptPr(ws, opts, deps = defaultDeps()) {
|
|
|
55690
56095
|
} else {
|
|
55691
56096
|
if (!opts.pr)
|
|
55692
56097
|
throw new WorkspaceError("Give a PR number or URL, or --no-pr <reason>.");
|
|
55693
|
-
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));
|
|
55694
56099
|
if (v.code !== 0)
|
|
55695
56100
|
throw new WorkspaceError(`gh pr view ${opts.pr} failed \u2014 ${v.stderr.trim()}`);
|
|
55696
56101
|
const pr = JSON.parse(v.stdout);
|
|
@@ -55708,7 +56113,7 @@ async function adoptPr(ws, opts, deps = defaultDeps()) {
|
|
|
55708
56113
|
};
|
|
55709
56114
|
title = pr.title;
|
|
55710
56115
|
}
|
|
55711
|
-
|
|
56116
|
+
fs50.writeFileSync(file, `---
|
|
55712
56117
|
${import_yaml14.default.stringify(fm)}---
|
|
55713
56118
|
# ${title}
|
|
55714
56119
|
|
|
@@ -55717,7 +56122,7 @@ Adopted with \`sandbox pr adopt\`: the work landed outside this workspace.
|
|
|
55717
56122
|
const lines = [`${repo}: adopted as ${fm["status"]}`];
|
|
55718
56123
|
if (fm["status"] !== "closed")
|
|
55719
56124
|
lines.push(...(await refreshPrStatus(ws, deps)).lines);
|
|
55720
|
-
lines.push((await deps.commitRecord(ws, "chore: adopt PR", [
|
|
56125
|
+
lines.push((await deps.commitRecord(ws, "chore: adopt PR", [path58.relative(ws, file)])).message);
|
|
55721
56126
|
return { ok: true, lines };
|
|
55722
56127
|
}
|
|
55723
56128
|
function lifecycleOf(ws) {
|
|
@@ -55733,7 +56138,7 @@ function lifecycleOf(ws) {
|
|
|
55733
56138
|
}));
|
|
55734
56139
|
const all = (set) => prs.every((p) => set.has(p.status));
|
|
55735
56140
|
let state;
|
|
55736
|
-
if (
|
|
56141
|
+
if (fs50.existsSync(path58.join(ws, "closeout.md")))
|
|
55737
56142
|
state = "closed-out";
|
|
55738
56143
|
else if (!prs.length)
|
|
55739
56144
|
state = "working";
|
|
@@ -55752,7 +56157,7 @@ function lifecycleOf(ws) {
|
|
|
55752
56157
|
return { state, prs };
|
|
55753
56158
|
}
|
|
55754
56159
|
async function removalBlockers(ws, worktrees, run34 = execRun) {
|
|
55755
|
-
if (
|
|
56160
|
+
if (fs50.existsSync(path58.join(ws, "closeout.md")))
|
|
55756
56161
|
return [];
|
|
55757
56162
|
const files = readPrFiles(ws);
|
|
55758
56163
|
const out = [];
|
|
@@ -55797,7 +56202,7 @@ function abandonWorkspace(ws, why) {
|
|
|
55797
56202
|
for (const f of files)
|
|
55798
56203
|
if (!SETTLED.has(statusOf(f)))
|
|
55799
56204
|
writePrFields(f, { status: "abandoned", reason: why });
|
|
55800
|
-
|
|
56205
|
+
fs50.writeFileSync(path58.join(ws, "closeout.md"), renderCloseout(files, "not closed (abandoned)", `Abandoned: ${why}`));
|
|
55801
56206
|
}
|
|
55802
56207
|
async function openTerminalSessions(ws, run34 = execRun) {
|
|
55803
56208
|
const ps = await run34("ps", ["-axo", "pid=,tty=,comm="]);
|
|
@@ -55806,7 +56211,7 @@ async function openTerminalSessions(ws, run34 = execRun) {
|
|
|
55806
56211
|
const ttys = /* @__PURE__ */ new Map();
|
|
55807
56212
|
for (const line of ps.stdout.split("\n")) {
|
|
55808
56213
|
const m = /^\s*(\d+)\s+(\S+)\s+(.+?)\s*$/.exec(line);
|
|
55809
|
-
if (m &&
|
|
56214
|
+
if (m && path58.basename(m[3]) === "claude" && m[2] !== "??" && m[2] !== "?")
|
|
55810
56215
|
ttys.set(m[1], m[2]);
|
|
55811
56216
|
}
|
|
55812
56217
|
if (!ttys.size)
|
|
@@ -55819,7 +56224,7 @@ async function openTerminalSessions(ws, run34 = execRun) {
|
|
|
55819
56224
|
pid = line.slice(1);
|
|
55820
56225
|
else if (line.startsWith("n") && ttys.has(pid)) {
|
|
55821
56226
|
const cwd = line.slice(1);
|
|
55822
|
-
if (cwd === ws || cwd.startsWith(ws +
|
|
56227
|
+
if (cwd === ws || cwd.startsWith(ws + path58.sep))
|
|
55823
56228
|
out.push(`terminal session: pid ${pid} on ${ttys.get(pid)} in ${cwd} \u2014 /exit it`);
|
|
55824
56229
|
}
|
|
55825
56230
|
}
|
|
@@ -55845,28 +56250,26 @@ async function closeOut(name, opts = {}, deps = defaultDeps()) {
|
|
|
55845
56250
|
const ref = issueIdOf(cfg);
|
|
55846
56251
|
let issueLine = "none";
|
|
55847
56252
|
if (ref) {
|
|
55848
|
-
|
|
55849
|
-
|
|
55850
|
-
|
|
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 ?? "";
|
|
55851
56260
|
issueLine = `${ref} left ${current || "as is"} (abandoned)`;
|
|
55852
|
-
} else if ([done, "closed", "done"].includes(current.toLowerCase())) {
|
|
55853
|
-
issueLine = `${ref} already ${current}`;
|
|
55854
56261
|
} else {
|
|
55855
|
-
|
|
55856
|
-
|
|
55857
|
-
|
|
55858
|
-
status: deps.doneStatus,
|
|
55859
|
-
comment: `Shipped: ${shipped.join("; ") || "no PRs"}`
|
|
55860
|
-
});
|
|
55861
|
-
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)`;
|
|
55862
56265
|
}
|
|
55863
56266
|
lines.push(`Issue: ${issueLine}`);
|
|
55864
56267
|
}
|
|
55865
56268
|
const files = readPrFiles(ws);
|
|
55866
|
-
|
|
56269
|
+
fs50.writeFileSync(path58.join(ws, "closeout.md"), renderCloseout(files, issueLine, why ? `Abandoned: ${why}` : void 0));
|
|
55867
56270
|
const branches = [];
|
|
55868
56271
|
for (const f of abandoned ? [] : files.filter((x) => (statusOf(x) === "merged" || statusOf(x) === "shipped") && !x.fm["adopted"])) {
|
|
55869
|
-
const src = await deps.sourceOf(
|
|
56272
|
+
const src = await deps.sourceOf(path58.join(ws, f.dir));
|
|
55870
56273
|
if (src && f.fm["branch"])
|
|
55871
56274
|
branches.push({ src, branch: String(f.fm["branch"]) });
|
|
55872
56275
|
}
|
|
@@ -55895,7 +56298,7 @@ async function closeOut(name, opts = {}, deps = defaultDeps()) {
|
|
|
55895
56298
|
const command = `sandbox close-out ${name}${why ? " --abandon" : ""}`;
|
|
55896
56299
|
return { command, output: lines.filter(Boolean).join("\n"), path: ws, terminals };
|
|
55897
56300
|
}
|
|
55898
|
-
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;
|
|
55899
56302
|
var init_pr_lifecycle = __esm({
|
|
55900
56303
|
"../../packages/sandbox-core/dist/pr_lifecycle.js"() {
|
|
55901
56304
|
"use strict";
|
|
@@ -55906,8 +56309,11 @@ var init_pr_lifecycle = __esm({
|
|
|
55906
56309
|
init_config();
|
|
55907
56310
|
init_global_config();
|
|
55908
56311
|
init_graduate();
|
|
56312
|
+
init_issue_status();
|
|
55909
56313
|
init_issue_tracker();
|
|
56314
|
+
init_lifecycle();
|
|
55910
56315
|
init_notes();
|
|
56316
|
+
init_registry_sync();
|
|
55911
56317
|
init_sandbox();
|
|
55912
56318
|
init_slots();
|
|
55913
56319
|
init_workspace_prompts();
|
|
@@ -55927,6 +56333,7 @@ var init_pr_lifecycle = __esm({
|
|
|
55927
56333
|
reason = (v) => typeof v === "string" ? v.trim() : "";
|
|
55928
56334
|
PASS_CELL = /\|\s*PASS\s*\|/;
|
|
55929
56335
|
FAIL_CELL = /\|\s*FAIL\s*\|/;
|
|
56336
|
+
shippedComment = (files) => `Shipped: ${files.map((f) => `${f.fm["release"] ?? "merge"} \u2014 ${f.fm["url"]}`).join("; ") || "no PRs"}`;
|
|
55930
56337
|
LOCK_STALE_MS = 15 * 6e4;
|
|
55931
56338
|
}
|
|
55932
56339
|
});
|
|
@@ -56741,12 +57148,12 @@ function parseTree(text, errors = [], options = ParseOptions.DEFAULT) {
|
|
|
56741
57148
|
}
|
|
56742
57149
|
return result;
|
|
56743
57150
|
}
|
|
56744
|
-
function findNodeAtLocation(root,
|
|
57151
|
+
function findNodeAtLocation(root, path74) {
|
|
56745
57152
|
if (!root) {
|
|
56746
57153
|
return void 0;
|
|
56747
57154
|
}
|
|
56748
57155
|
let node = root;
|
|
56749
|
-
for (let segment of
|
|
57156
|
+
for (let segment of path74) {
|
|
56750
57157
|
if (typeof segment === "string") {
|
|
56751
57158
|
if (node.type !== "object" || !Array.isArray(node.children)) {
|
|
56752
57159
|
return void 0;
|
|
@@ -57113,14 +57520,14 @@ var init_parser = __esm({
|
|
|
57113
57520
|
|
|
57114
57521
|
// ../../node_modules/jsonc-parser/lib/esm/impl/edit.js
|
|
57115
57522
|
function setProperty(text, originalPath, value, options) {
|
|
57116
|
-
const
|
|
57523
|
+
const path74 = originalPath.slice();
|
|
57117
57524
|
const errors = [];
|
|
57118
57525
|
const root = parseTree(text, errors);
|
|
57119
57526
|
let parent = void 0;
|
|
57120
57527
|
let lastSegment = void 0;
|
|
57121
|
-
while (
|
|
57122
|
-
lastSegment =
|
|
57123
|
-
parent = findNodeAtLocation(root,
|
|
57528
|
+
while (path74.length > 0) {
|
|
57529
|
+
lastSegment = path74.pop();
|
|
57530
|
+
parent = findNodeAtLocation(root, path74);
|
|
57124
57531
|
if (parent === void 0 && value !== void 0) {
|
|
57125
57532
|
if (typeof lastSegment === "string") {
|
|
57126
57533
|
value = { [lastSegment]: value };
|
|
@@ -57264,8 +57671,8 @@ var init_edit = __esm({
|
|
|
57264
57671
|
});
|
|
57265
57672
|
|
|
57266
57673
|
// ../../node_modules/jsonc-parser/lib/esm/main.js
|
|
57267
|
-
function modify(text,
|
|
57268
|
-
return setProperty(text,
|
|
57674
|
+
function modify(text, path74, value, options) {
|
|
57675
|
+
return setProperty(text, path74, value, options);
|
|
57269
57676
|
}
|
|
57270
57677
|
function applyEdits(text, edits) {
|
|
57271
57678
|
let sortedEdits = edits.slice(0).sort((a, b) => {
|
|
@@ -57347,20 +57754,20 @@ var init_main2 = __esm({
|
|
|
57347
57754
|
});
|
|
57348
57755
|
|
|
57349
57756
|
// ../../packages/sandbox-core/dist/plugin_check.js
|
|
57350
|
-
import * as
|
|
57757
|
+
import * as fs51 from "node:fs";
|
|
57351
57758
|
import * as os17 from "node:os";
|
|
57352
|
-
import * as
|
|
57759
|
+
import * as path59 from "node:path";
|
|
57353
57760
|
function defaultRepoPluginDir() {
|
|
57354
|
-
return
|
|
57761
|
+
return path59.join(sandboxRoot(), "plugins", PLUGIN_NAME);
|
|
57355
57762
|
}
|
|
57356
57763
|
function defaultClaudePluginsDir() {
|
|
57357
|
-
return
|
|
57764
|
+
return path59.join(os17.homedir(), ".claude", "plugins");
|
|
57358
57765
|
}
|
|
57359
57766
|
function repoPluginVersion(repoPluginDir) {
|
|
57360
|
-
const manifest =
|
|
57767
|
+
const manifest = path59.join(repoPluginDir, ".claude-plugin", "plugin.json");
|
|
57361
57768
|
let data;
|
|
57362
57769
|
try {
|
|
57363
|
-
data = JSON.parse(
|
|
57770
|
+
data = JSON.parse(fs51.readFileSync(manifest, "utf8"));
|
|
57364
57771
|
} catch {
|
|
57365
57772
|
return null;
|
|
57366
57773
|
}
|
|
@@ -57368,23 +57775,23 @@ function repoPluginVersion(repoPluginDir) {
|
|
|
57368
57775
|
return version ? String(version) : null;
|
|
57369
57776
|
}
|
|
57370
57777
|
function loadRegistry(claudePluginsDir) {
|
|
57371
|
-
const registry =
|
|
57778
|
+
const registry = path59.join(claudePluginsDir, "installed_plugins.json");
|
|
57372
57779
|
let data;
|
|
57373
57780
|
try {
|
|
57374
|
-
data = JSON.parse(
|
|
57781
|
+
data = JSON.parse(fs51.readFileSync(registry, "utf8"));
|
|
57375
57782
|
} catch {
|
|
57376
57783
|
return null;
|
|
57377
57784
|
}
|
|
57378
57785
|
const plugins = typeof data === "object" && data !== null && !Array.isArray(data) ? data["plugins"] : void 0;
|
|
57379
57786
|
return typeof plugins === "object" && plugins !== null && !Array.isArray(plugins) ? plugins : null;
|
|
57380
57787
|
}
|
|
57381
|
-
function installedPlugin(claudePluginsDir) {
|
|
57788
|
+
function installedPlugin(claudePluginsDir, name = PLUGIN_NAME) {
|
|
57382
57789
|
const plugins = loadRegistry(claudePluginsDir);
|
|
57383
57790
|
if (plugins === null) {
|
|
57384
57791
|
return [null, null];
|
|
57385
57792
|
}
|
|
57386
57793
|
for (const [key, entries] of Object.entries(plugins)) {
|
|
57387
|
-
if (!key.startsWith(`${
|
|
57794
|
+
if (!key.startsWith(`${name}@`) || !Array.isArray(entries)) {
|
|
57388
57795
|
continue;
|
|
57389
57796
|
}
|
|
57390
57797
|
for (const entry of entries) {
|
|
@@ -57404,7 +57811,7 @@ function skillBodies(skillsDir) {
|
|
|
57404
57811
|
const bodies = /* @__PURE__ */ new Map();
|
|
57405
57812
|
let isDir6 = false;
|
|
57406
57813
|
try {
|
|
57407
|
-
isDir6 =
|
|
57814
|
+
isDir6 = fs51.statSync(skillsDir).isDirectory();
|
|
57408
57815
|
} catch {
|
|
57409
57816
|
isDir6 = false;
|
|
57410
57817
|
}
|
|
@@ -57413,14 +57820,14 @@ function skillBodies(skillsDir) {
|
|
|
57413
57820
|
}
|
|
57414
57821
|
let entries;
|
|
57415
57822
|
try {
|
|
57416
|
-
entries =
|
|
57823
|
+
entries = fs51.readdirSync(skillsDir, { withFileTypes: true });
|
|
57417
57824
|
} catch {
|
|
57418
57825
|
return bodies;
|
|
57419
57826
|
}
|
|
57420
57827
|
const names = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
57421
57828
|
for (const name of names) {
|
|
57422
57829
|
try {
|
|
57423
|
-
bodies.set(name,
|
|
57830
|
+
bodies.set(name, fs51.readFileSync(path59.join(skillsDir, name, "SKILL.md"), "utf8"));
|
|
57424
57831
|
} catch {
|
|
57425
57832
|
continue;
|
|
57426
57833
|
}
|
|
@@ -57454,7 +57861,7 @@ function pluginDriftWarnings(repoPluginDir, claudePluginsDir) {
|
|
|
57454
57861
|
}
|
|
57455
57862
|
let repoSkillsIsDir = false;
|
|
57456
57863
|
try {
|
|
57457
|
-
repoSkillsIsDir =
|
|
57864
|
+
repoSkillsIsDir = fs51.statSync(path59.join(repoPluginDir, "skills")).isDirectory();
|
|
57458
57865
|
} catch {
|
|
57459
57866
|
repoSkillsIsDir = false;
|
|
57460
57867
|
}
|
|
@@ -57466,11 +57873,16 @@ function pluginDriftWarnings(repoPluginDir, claudePluginsDir) {
|
|
|
57466
57873
|
}
|
|
57467
57874
|
const [installedVersion2, installPath] = installedPlugin(claudePluginsDir);
|
|
57468
57875
|
if (installPath === null) {
|
|
57876
|
+
if (installedPlugin(claudePluginsDir, LEGACY_PLUGIN_NAME)[1] !== null) {
|
|
57877
|
+
return [
|
|
57878
|
+
`the ${LEGACY_PLUGIN_NAME} plugin was renamed ${PLUGIN_NAME}, and only the old one is installed \u2014 its skills are now ${PLUGIN_NAME}:diagnose, ${PLUGIN_NAME}:status, ${PLUGIN_NAME}:workspace, \u2026 Run \`w9r setup\` to switch over.`
|
|
57879
|
+
];
|
|
57880
|
+
}
|
|
57469
57881
|
return [
|
|
57470
|
-
`${PLUGIN_NAME} plugin is not installed \u2014 the
|
|
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.`
|
|
57471
57883
|
];
|
|
57472
57884
|
}
|
|
57473
|
-
const drift = skillDrift(
|
|
57885
|
+
const drift = skillDrift(path59.join(repoPluginDir, "skills"), path59.join(installPath, "skills"));
|
|
57474
57886
|
if (!drift.length) {
|
|
57475
57887
|
return [];
|
|
57476
57888
|
}
|
|
@@ -57483,27 +57895,28 @@ function pluginDriftWarnings(repoPluginDir, claudePluginsDir) {
|
|
|
57483
57895
|
}
|
|
57484
57896
|
return [`${lag}: ${drift.join("; ")} \u2014 ${REFRESH_HINT}.`];
|
|
57485
57897
|
}
|
|
57486
|
-
var PLUGIN_NAME, REFRESH_HINT;
|
|
57898
|
+
var PLUGIN_NAME, LEGACY_PLUGIN_NAME, REFRESH_HINT;
|
|
57487
57899
|
var init_plugin_check = __esm({
|
|
57488
57900
|
"../../packages/sandbox-core/dist/plugin_check.js"() {
|
|
57489
57901
|
"use strict";
|
|
57490
57902
|
init_define_SANDBOX_BUILD();
|
|
57491
57903
|
init_config();
|
|
57492
|
-
PLUGIN_NAME = "
|
|
57493
|
-
|
|
57904
|
+
PLUGIN_NAME = "werkmeister";
|
|
57905
|
+
LEGACY_PLUGIN_NAME = "sandbox-tools";
|
|
57906
|
+
REFRESH_HINT = "run `w9r setup` to refresh it so sessions pick up the current werkmeister ops skills";
|
|
57494
57907
|
}
|
|
57495
57908
|
});
|
|
57496
57909
|
|
|
57497
57910
|
// ../../packages/sandbox-core/dist/test_run.js
|
|
57498
|
-
import * as
|
|
57911
|
+
import * as fs52 from "node:fs";
|
|
57499
57912
|
import { createRequire } from "node:module";
|
|
57500
|
-
import * as
|
|
57913
|
+
import * as path60 from "node:path";
|
|
57501
57914
|
function playwrightSearchDirs(workspace) {
|
|
57502
57915
|
const dirs = [workspace];
|
|
57503
57916
|
try {
|
|
57504
|
-
for (const entry of
|
|
57505
|
-
if (entry.isDirectory() &&
|
|
57506
|
-
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));
|
|
57507
57920
|
}
|
|
57508
57921
|
}
|
|
57509
57922
|
} catch {
|
|
@@ -57512,20 +57925,20 @@ function playwrightSearchDirs(workspace) {
|
|
|
57512
57925
|
}
|
|
57513
57926
|
function playwrightCwd(dirs) {
|
|
57514
57927
|
for (const dir of dirs) {
|
|
57515
|
-
if (!(
|
|
57928
|
+
if (!(fs52.existsSync(dir) && fs52.statSync(dir).isDirectory())) {
|
|
57516
57929
|
continue;
|
|
57517
57930
|
}
|
|
57518
|
-
if (CONFIG_NAMES.some((name) =>
|
|
57931
|
+
if (CONFIG_NAMES.some((name) => fs52.existsSync(path60.join(dir, name)))) {
|
|
57519
57932
|
return dir;
|
|
57520
57933
|
}
|
|
57521
57934
|
}
|
|
57522
57935
|
return null;
|
|
57523
57936
|
}
|
|
57524
57937
|
function noSuiteMessage(workspace) {
|
|
57525
|
-
const repoNames = playwrightSearchDirs(workspace).slice(1).map((d) =>
|
|
57938
|
+
const repoNames = playwrightSearchDirs(workspace).slice(1).map((d) => path60.basename(d));
|
|
57526
57939
|
let services = "";
|
|
57527
57940
|
try {
|
|
57528
|
-
services = /^SERVICES=(.*)$/m.exec(
|
|
57941
|
+
services = /^SERVICES=(.*)$/m.exec(fs52.readFileSync(path60.join(workspace, ".env"), "utf8"))?.[1] ?? "";
|
|
57529
57942
|
} catch {
|
|
57530
57943
|
}
|
|
57531
57944
|
const selected = services.split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -57539,8 +57952,8 @@ ${lines.join("\n")}` : "");
|
|
|
57539
57952
|
}
|
|
57540
57953
|
function installedPlaywrightVersion(cwd) {
|
|
57541
57954
|
try {
|
|
57542
|
-
const pkg = createRequire(
|
|
57543
|
-
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;
|
|
57544
57957
|
} catch {
|
|
57545
57958
|
return null;
|
|
57546
57959
|
}
|
|
@@ -57620,15 +58033,62 @@ var init_test_run = __esm({
|
|
|
57620
58033
|
});
|
|
57621
58034
|
|
|
57622
58035
|
// ../../packages/sandbox-core/dist/machine_setup.js
|
|
57623
|
-
import * as
|
|
58036
|
+
import * as fs53 from "node:fs";
|
|
57624
58037
|
import * as os18 from "node:os";
|
|
57625
|
-
import * as
|
|
58038
|
+
import * as path61 from "node:path";
|
|
57626
58039
|
function briefOutput(out, maxLines = 4) {
|
|
57627
58040
|
const lines = out.split("\n").map((l) => l.trimEnd()).filter((l) => l.trim() && !/^\s*[✔✓⣾⣽⣻⢿⡿⣟⣯⣷]/.test(l));
|
|
57628
58041
|
if (!lines.length)
|
|
57629
58042
|
return out.trim().slice(0, 200);
|
|
57630
58043
|
return lines.slice(0, maxLines).join("; ");
|
|
57631
58044
|
}
|
|
58045
|
+
function retireLegacyPlugin(claudePluginsDir, runner, checkOnly) {
|
|
58046
|
+
const label = `plugin ${LEGACY_PLUGIN.name} (renamed ${PLUGIN_NAME})`;
|
|
58047
|
+
const installed = installedVersion(claudePluginsDir, LEGACY_PLUGIN) !== null;
|
|
58048
|
+
const marketplaceKnown = LEGACY_PLUGIN.marketplace in knownMarketplaces(claudePluginsDir);
|
|
58049
|
+
if (!installed && !marketplaceKnown)
|
|
58050
|
+
return [];
|
|
58051
|
+
if (checkOnly) {
|
|
58052
|
+
return [
|
|
58053
|
+
{
|
|
58054
|
+
label,
|
|
58055
|
+
ok: false,
|
|
58056
|
+
detail: `old name still set up; run \`w9r setup\` to switch to ${PLUGIN_NAME}`
|
|
58057
|
+
}
|
|
58058
|
+
];
|
|
58059
|
+
}
|
|
58060
|
+
const steps = [];
|
|
58061
|
+
if (installed) {
|
|
58062
|
+
const [rc, out] = runner([
|
|
58063
|
+
"claude",
|
|
58064
|
+
"plugin",
|
|
58065
|
+
"uninstall",
|
|
58066
|
+
`${LEGACY_PLUGIN.name}@${LEGACY_PLUGIN.marketplace}`
|
|
58067
|
+
]);
|
|
58068
|
+
steps.push({
|
|
58069
|
+
label,
|
|
58070
|
+
ok: rc === 0,
|
|
58071
|
+
detail: rc === 0 ? "uninstalled" : `uninstall failed: ${out}`
|
|
58072
|
+
});
|
|
58073
|
+
if (rc !== 0)
|
|
58074
|
+
return steps;
|
|
58075
|
+
}
|
|
58076
|
+
if (marketplaceKnown) {
|
|
58077
|
+
const [rc, out] = runner([
|
|
58078
|
+
"claude",
|
|
58079
|
+
"plugin",
|
|
58080
|
+
"marketplace",
|
|
58081
|
+
"remove",
|
|
58082
|
+
LEGACY_PLUGIN.marketplace
|
|
58083
|
+
]);
|
|
58084
|
+
steps.push({
|
|
58085
|
+
label,
|
|
58086
|
+
ok: rc === 0,
|
|
58087
|
+
detail: rc === 0 ? `removed marketplace ${LEGACY_PLUGIN.marketplace}` : `marketplace remove failed: ${out}`
|
|
58088
|
+
});
|
|
58089
|
+
}
|
|
58090
|
+
return steps;
|
|
58091
|
+
}
|
|
57632
58092
|
function workspaceRepoUrl(slug) {
|
|
57633
58093
|
return `https://github.com/${slug}.git`;
|
|
57634
58094
|
}
|
|
@@ -57679,17 +58139,17 @@ function defaultInteractiveRunner(cmd) {
|
|
|
57679
58139
|
function readJson2(p) {
|
|
57680
58140
|
let data;
|
|
57681
58141
|
try {
|
|
57682
|
-
data = JSON.parse(
|
|
58142
|
+
data = JSON.parse(fs53.readFileSync(p, "utf8"));
|
|
57683
58143
|
} catch {
|
|
57684
58144
|
return null;
|
|
57685
58145
|
}
|
|
57686
58146
|
return typeof data === "object" && data !== null && !Array.isArray(data) ? data : null;
|
|
57687
58147
|
}
|
|
57688
58148
|
function knownMarketplaces(claudePluginsDir) {
|
|
57689
|
-
return readJson2(
|
|
58149
|
+
return readJson2(path61.join(claudePluginsDir, "known_marketplaces.json")) || {};
|
|
57690
58150
|
}
|
|
57691
58151
|
function installedVersion(claudePluginsDir, spec) {
|
|
57692
|
-
const registry = readJson2(
|
|
58152
|
+
const registry = readJson2(path61.join(claudePluginsDir, "installed_plugins.json")) || {};
|
|
57693
58153
|
const plugins = registry["plugins"];
|
|
57694
58154
|
if (typeof plugins !== "object" || plugins === null || Array.isArray(plugins)) {
|
|
57695
58155
|
return null;
|
|
@@ -57782,9 +58242,9 @@ function syncOptionalPlugin(spec, claudePluginsDir, runner, checkOnly, install)
|
|
|
57782
58242
|
}));
|
|
57783
58243
|
}
|
|
57784
58244
|
function syncRepo(reposRoot, spec, runner, checkOnly) {
|
|
57785
|
-
const dest =
|
|
58245
|
+
const dest = path61.join(reposRoot, spec.name);
|
|
57786
58246
|
const label = `repo ${spec.name}`;
|
|
57787
|
-
if (!
|
|
58247
|
+
if (!fs53.existsSync(dest)) {
|
|
57788
58248
|
if (checkOnly) {
|
|
57789
58249
|
return [
|
|
57790
58250
|
{
|
|
@@ -57803,7 +58263,7 @@ function syncRepo(reposRoot, spec, runner, checkOnly) {
|
|
|
57803
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)}`;
|
|
57804
58264
|
return [{ label, ok: false, optional: true, detail: detail2 }];
|
|
57805
58265
|
}
|
|
57806
|
-
if (!
|
|
58266
|
+
if (!fs53.existsSync(path61.join(dest, ".git"))) {
|
|
57807
58267
|
return [
|
|
57808
58268
|
{
|
|
57809
58269
|
label,
|
|
@@ -57836,8 +58296,8 @@ function syncPathDirs(checkOnly, dirs) {
|
|
|
57836
58296
|
continue;
|
|
57837
58297
|
const target = expandUser(value);
|
|
57838
58298
|
const label = `paths.${key}`;
|
|
57839
|
-
if (
|
|
57840
|
-
if (!
|
|
58299
|
+
if (fs53.existsSync(target)) {
|
|
58300
|
+
if (!fs53.statSync(target).isDirectory()) {
|
|
57841
58301
|
steps.push({ label, ok: false, detail: `${target} exists but is not a directory` });
|
|
57842
58302
|
} else {
|
|
57843
58303
|
steps.push({ label, ok: true, detail: target });
|
|
@@ -57849,7 +58309,7 @@ function syncPathDirs(checkOnly, dirs) {
|
|
|
57849
58309
|
continue;
|
|
57850
58310
|
}
|
|
57851
58311
|
try {
|
|
57852
|
-
|
|
58312
|
+
fs53.mkdirSync(target, { recursive: true });
|
|
57853
58313
|
steps.push({ label, ok: true, detail: `created ${target}` });
|
|
57854
58314
|
} catch (e) {
|
|
57855
58315
|
steps.push({ label, ok: false, detail: `could not create ${target}: ${e}` });
|
|
@@ -57858,7 +58318,7 @@ function syncPathDirs(checkOnly, dirs) {
|
|
|
57858
58318
|
return steps;
|
|
57859
58319
|
}
|
|
57860
58320
|
function syncRegistryConfigs(checkOnly, options = {}) {
|
|
57861
|
-
const legacyDir = options.legacyDir === void 0 ?
|
|
58321
|
+
const legacyDir = options.legacyDir === void 0 ? path61.join(sandboxRoot(), LEGACY_CONFIG_DIR) : options.legacyDir;
|
|
57862
58322
|
const files = [
|
|
57863
58323
|
[
|
|
57864
58324
|
"services.local.yaml",
|
|
@@ -57877,12 +58337,12 @@ function syncRegistryConfigs(checkOnly, options = {}) {
|
|
|
57877
58337
|
const steps = [];
|
|
57878
58338
|
for (const [name, dest, legacyName, content] of files) {
|
|
57879
58339
|
const label2 = `config ${name}`;
|
|
57880
|
-
if (
|
|
58340
|
+
if (fs53.existsSync(dest)) {
|
|
57881
58341
|
steps.push({ label: label2, ok: true, detail: `present at ${dest}` });
|
|
57882
58342
|
continue;
|
|
57883
58343
|
}
|
|
57884
|
-
const legacy = legacyDir && legacyName ?
|
|
57885
|
-
const migratable = legacy !== null &&
|
|
58344
|
+
const legacy = legacyDir && legacyName ? path61.join(legacyDir, legacyName) : null;
|
|
58345
|
+
const migratable = legacy !== null && fs53.existsSync(legacy);
|
|
57886
58346
|
if (checkOnly) {
|
|
57887
58347
|
steps.push({
|
|
57888
58348
|
label: label2,
|
|
@@ -57893,8 +58353,8 @@ function syncRegistryConfigs(checkOnly, options = {}) {
|
|
|
57893
58353
|
}
|
|
57894
58354
|
try {
|
|
57895
58355
|
if (migratable) {
|
|
57896
|
-
|
|
57897
|
-
|
|
58356
|
+
fs53.mkdirSync(path61.dirname(dest), { recursive: true });
|
|
58357
|
+
fs53.copyFileSync(legacy, dest);
|
|
57898
58358
|
steps.push({ label: label2, ok: true, detail: `migrated from ${legacy}` });
|
|
57899
58359
|
} else {
|
|
57900
58360
|
const result = seedFile(dest, content());
|
|
@@ -57906,7 +58366,7 @@ function syncRegistryConfigs(checkOnly, options = {}) {
|
|
|
57906
58366
|
}
|
|
57907
58367
|
const label = "config secrets.env";
|
|
57908
58368
|
const secrets = secretsPath();
|
|
57909
|
-
if (
|
|
58369
|
+
if (fs53.existsSync(secrets)) {
|
|
57910
58370
|
steps.push({ label, ok: true, detail: `present at ${secrets}` });
|
|
57911
58371
|
} else if (checkOnly) {
|
|
57912
58372
|
steps.push({ label, ok: true, detail: `absent \u2014 would write a template to ${secrets}` });
|
|
@@ -57927,11 +58387,11 @@ function syncRegistryConfigs(checkOnly, options = {}) {
|
|
|
57927
58387
|
function resolveCertScript(startDir) {
|
|
57928
58388
|
let dir = startDir ?? sandboxRoot();
|
|
57929
58389
|
for (let i = 0; i < 5; i++) {
|
|
57930
|
-
const candidate =
|
|
57931
|
-
if (
|
|
58390
|
+
const candidate = path61.join(dir, "bin", "generate-certs.sh");
|
|
58391
|
+
if (fs53.existsSync(candidate)) {
|
|
57932
58392
|
return candidate;
|
|
57933
58393
|
}
|
|
57934
|
-
const parent =
|
|
58394
|
+
const parent = path61.dirname(dir);
|
|
57935
58395
|
if (parent === dir)
|
|
57936
58396
|
break;
|
|
57937
58397
|
dir = parent;
|
|
@@ -58044,15 +58504,15 @@ ${JIRA_CONFIG_SNIPPET}`
|
|
|
58044
58504
|
];
|
|
58045
58505
|
}
|
|
58046
58506
|
function vscodeSettingsPath() {
|
|
58047
|
-
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");
|
|
58048
58508
|
}
|
|
58049
|
-
function editorSchemaState(settingsPath = vscodeSettingsPath(), schemasDir =
|
|
58509
|
+
function editorSchemaState(settingsPath = vscodeSettingsPath(), schemasDir = path61.join(sandboxRoot(), "infra", "schemas")) {
|
|
58050
58510
|
const state = { present: false, set: {}, drop: [], text: "{}" };
|
|
58051
|
-
if (!
|
|
58511
|
+
if (!fs53.existsSync(path61.dirname(settingsPath)))
|
|
58052
58512
|
return state;
|
|
58053
58513
|
state.present = true;
|
|
58054
|
-
if (
|
|
58055
|
-
state.text =
|
|
58514
|
+
if (fs53.existsSync(settingsPath))
|
|
58515
|
+
state.text = fs53.readFileSync(settingsPath, "utf8");
|
|
58056
58516
|
const errors = [];
|
|
58057
58517
|
const data = parse7(state.text, errors, { allowTrailingComma: true });
|
|
58058
58518
|
if (errors.length) {
|
|
@@ -58062,7 +58522,7 @@ function editorSchemaState(settingsPath = vscodeSettingsPath(), schemasDir = pat
|
|
|
58062
58522
|
const mapped = data?.["yaml.schemas"] ?? {};
|
|
58063
58523
|
const covers = (v, glob) => v === glob || Array.isArray(v) && v.includes(glob);
|
|
58064
58524
|
for (const [file, glob] of Object.entries(EDITOR_SCHEMAS)) {
|
|
58065
|
-
const want =
|
|
58525
|
+
const want = path61.join(schemasDir, file);
|
|
58066
58526
|
if (!covers(mapped[want], glob))
|
|
58067
58527
|
state.set[want] = glob;
|
|
58068
58528
|
for (const [key, v] of Object.entries(mapped)) {
|
|
@@ -58107,11 +58567,11 @@ function syncEditorSchemas(checkOnly, install, settingsPath = vscodeSettingsPath
|
|
|
58107
58567
|
for (const [key, glob] of Object.entries(state.set)) {
|
|
58108
58568
|
text = applyEdits(text, modify(text, ["yaml.schemas", key], glob, fmt));
|
|
58109
58569
|
}
|
|
58110
|
-
|
|
58570
|
+
fs53.writeFileSync(settingsPath, text);
|
|
58111
58571
|
return [{ label, ok: true, optional: true, detail: `mapped in ${settingsPath}` }];
|
|
58112
58572
|
}
|
|
58113
58573
|
function claudeUserSettingsPath() {
|
|
58114
|
-
return
|
|
58574
|
+
return path61.join(os18.homedir(), ".claude", "settings.json");
|
|
58115
58575
|
}
|
|
58116
58576
|
function claudeStandardsDirState(settingsPath = claudeUserSettingsPath(), dir = standardsDirPath()) {
|
|
58117
58577
|
const state = {
|
|
@@ -58120,12 +58580,12 @@ function claudeStandardsDirState(settingsPath = claudeUserSettingsPath(), dir =
|
|
|
58120
58580
|
granted: false,
|
|
58121
58581
|
text: "{}"
|
|
58122
58582
|
};
|
|
58123
|
-
state.present =
|
|
58124
|
-
state.dirMissing = !(
|
|
58583
|
+
state.present = fs53.existsSync(path61.dirname(settingsPath));
|
|
58584
|
+
state.dirMissing = !(fs53.existsSync(dir) && fs53.statSync(dir).isDirectory());
|
|
58125
58585
|
if (!state.present || state.dirMissing)
|
|
58126
58586
|
return state;
|
|
58127
|
-
if (
|
|
58128
|
-
state.text =
|
|
58587
|
+
if (fs53.existsSync(settingsPath))
|
|
58588
|
+
state.text = fs53.readFileSync(settingsPath, "utf8");
|
|
58129
58589
|
const errors = [];
|
|
58130
58590
|
const data = parse7(state.text, errors, { allowTrailingComma: true });
|
|
58131
58591
|
if (errors.length) {
|
|
@@ -58137,8 +58597,8 @@ function claudeStandardsDirState(settingsPath = claudeUserSettingsPath(), dir =
|
|
|
58137
58597
|
state.error = "permissions.additionalDirectories is not an array";
|
|
58138
58598
|
return state;
|
|
58139
58599
|
}
|
|
58140
|
-
const want =
|
|
58141
|
-
state.granted = (dirs ?? []).some((d) =>
|
|
58600
|
+
const want = path61.resolve(dir);
|
|
58601
|
+
state.granted = (dirs ?? []).some((d) => path61.resolve(expandUser(String(d))) === want);
|
|
58142
58602
|
return state;
|
|
58143
58603
|
}
|
|
58144
58604
|
function syncClaudeStandardsDir(checkOnly, install, settingsPath = claudeUserSettingsPath(), dir = standardsDirPath()) {
|
|
@@ -58168,18 +58628,18 @@ function syncClaudeStandardsDir(checkOnly, install, settingsPath = claudeUserSet
|
|
|
58168
58628
|
...fmt,
|
|
58169
58629
|
isArrayInsertion: true
|
|
58170
58630
|
}) : modify(state.text, ["permissions", "additionalDirectories"], [dir], fmt);
|
|
58171
|
-
|
|
58631
|
+
fs53.writeFileSync(settingsPath, applyEdits(state.text, edits));
|
|
58172
58632
|
return [{ label, ok: true, optional: true, detail: `granted in ${settingsPath}` }];
|
|
58173
58633
|
}
|
|
58174
58634
|
function vscodeExtensionState(runner = defaultRunner, root = sandboxRoot()) {
|
|
58175
58635
|
const [rc, out] = runner(["code", "--list-extensions", "--show-versions"]);
|
|
58176
58636
|
const prefix = `${VSCODE_EXTENSION_ID}@`;
|
|
58177
58637
|
const line = rc === 0 ? out.split("\n").find((l) => l.trim().toLowerCase().startsWith(prefix)) : void 0;
|
|
58178
|
-
const vsix =
|
|
58638
|
+
const vsix = path61.join(root, BUNDLED_VSIX_REL);
|
|
58179
58639
|
return {
|
|
58180
58640
|
cli: rc === 0,
|
|
58181
58641
|
installed: line ? line.trim().slice(prefix.length) : null,
|
|
58182
|
-
bundled:
|
|
58642
|
+
bundled: fs53.existsSync(vsix) ? { path: vsix, version: readJson2(path61.join(root, "package.json"))?.version } : null
|
|
58183
58643
|
};
|
|
58184
58644
|
}
|
|
58185
58645
|
function syncVscodeExtension(checkOnly, install, state = vscodeExtensionState(), runner = defaultRunner) {
|
|
@@ -58333,7 +58793,7 @@ function syncPlaywright(runner, checkOnly, interactive) {
|
|
|
58333
58793
|
const dirs = [...out.matchAll(/Install location:\s*(\S.*)$/gm)].map((m) => m[1].trim());
|
|
58334
58794
|
if (rc2 !== 0 || dirs.length === 0)
|
|
58335
58795
|
return null;
|
|
58336
|
-
return dirs.filter((d) => !
|
|
58796
|
+
return dirs.filter((d) => !fs53.existsSync(path61.join(d, "INSTALLATION_COMPLETE")));
|
|
58337
58797
|
};
|
|
58338
58798
|
const before = missingDirs();
|
|
58339
58799
|
if (before === null) {
|
|
@@ -58377,8 +58837,8 @@ function syncPlaywright(runner, checkOnly, interactive) {
|
|
|
58377
58837
|
async function runSetup(options = {}) {
|
|
58378
58838
|
const checkOnly = options.checkOnly ?? false;
|
|
58379
58839
|
const reposRoot = options.reposRoot ?? getReposRoot();
|
|
58380
|
-
const claudePluginsDir = options.claudePluginsDir ??
|
|
58381
|
-
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);
|
|
58382
58842
|
const runner = options.runner ?? defaultRunner;
|
|
58383
58843
|
const jira2 = options.jira ?? loadJira();
|
|
58384
58844
|
const steps = [];
|
|
@@ -58393,15 +58853,19 @@ async function runSetup(options = {}) {
|
|
|
58393
58853
|
steps.push({
|
|
58394
58854
|
label: "claude CLI",
|
|
58395
58855
|
ok: false,
|
|
58396
|
-
detail:
|
|
58856
|
+
detail: `not found on PATH \u2014 the ${PLUGIN_NAME} and ponytail plugins cannot be installed without it. Install Claude Code from https://claude.com/claude-code (the VS Code extension alone does not put \`claude\` on PATH), then re-run \`sandbox setup\`.`
|
|
58397
58857
|
});
|
|
58398
58858
|
} else {
|
|
58399
58859
|
for (const spec of REQUIRED_PLUGINS) {
|
|
58400
58860
|
let driftProbe = null;
|
|
58401
|
-
if (spec.name ===
|
|
58861
|
+
if (spec.name === PLUGIN_NAME) {
|
|
58402
58862
|
driftProbe = () => pluginDriftWarnings(repoPluginDir, claudePluginsDir);
|
|
58403
58863
|
}
|
|
58404
|
-
|
|
58864
|
+
const synced = syncPlugin(spec, claudePluginsDir, runner, checkOnly, driftProbe);
|
|
58865
|
+
steps.push(...synced);
|
|
58866
|
+
if (spec.name === PLUGIN_NAME && (checkOnly || synced.every((step) => step.ok))) {
|
|
58867
|
+
steps.push(...retireLegacyPlugin(claudePluginsDir, runner, checkOnly));
|
|
58868
|
+
}
|
|
58405
58869
|
}
|
|
58406
58870
|
for (const spec of OPTIONAL_PLUGINS) {
|
|
58407
58871
|
steps.push(...syncOptionalPlugin(spec, claudePluginsDir, runner, checkOnly, options.installOptionalPlugins ?? false));
|
|
@@ -58418,7 +58882,7 @@ async function runSetup(options = {}) {
|
|
|
58418
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."
|
|
58419
58883
|
});
|
|
58420
58884
|
}
|
|
58421
|
-
if (options.demo ||
|
|
58885
|
+
if (options.demo || fs53.existsSync(path61.join(reposRoot, DEMO_SERVICES_REPO))) {
|
|
58422
58886
|
steps.push(...syncRepo(reposRoot, DEMO_SERVICES_SPEC, runner, checkOnly));
|
|
58423
58887
|
}
|
|
58424
58888
|
steps.push(...syncRegistry({ reposRoot, checkOnly }));
|
|
@@ -58432,7 +58896,7 @@ async function runSetup(options = {}) {
|
|
|
58432
58896
|
steps.push(...syncVscodeExtension(checkOnly, options.installVscodeExtension ?? false, vscodeExtensionState(runner), runner));
|
|
58433
58897
|
return steps;
|
|
58434
58898
|
}
|
|
58435
|
-
var REQUIRED_PLUGINS, OPTIONAL_PLUGINS, WORKSPACE_ROOT_REPO, isLogin, DEMO_SERVICES_REPO, DEMO_SERVICES_REPO_URL, workspaceRootSpec, DEMO_SERVICES_SPEC, LEGACY_CONFIG_DIR, JIRA_TOKEN_URL, JIRA_REQUIRED_KEYS, JIRA_CONFIG_SNIPPET, EDITOR_SCHEMAS, VSCODE_EXTENSION_ID, BUNDLED_VSIX_REL;
|
|
58899
|
+
var REQUIRED_PLUGINS, LEGACY_PLUGIN, OPTIONAL_PLUGINS, WORKSPACE_ROOT_REPO, isLogin, DEMO_SERVICES_REPO, DEMO_SERVICES_REPO_URL, workspaceRootSpec, DEMO_SERVICES_SPEC, LEGACY_CONFIG_DIR, JIRA_TOKEN_URL, JIRA_REQUIRED_KEYS, JIRA_CONFIG_SNIPPET, EDITOR_SCHEMAS, VSCODE_EXTENSION_ID, BUNDLED_VSIX_REL;
|
|
58436
58900
|
var init_machine_setup = __esm({
|
|
58437
58901
|
"../../packages/sandbox-core/dist/machine_setup.js"() {
|
|
58438
58902
|
"use strict";
|
|
@@ -58449,11 +58913,11 @@ var init_machine_setup = __esm({
|
|
|
58449
58913
|
init_test_run();
|
|
58450
58914
|
REQUIRED_PLUGINS = [
|
|
58451
58915
|
// This repo doubles as the marketplace: `.claude-plugin/marketplace.json` at
|
|
58452
|
-
// the root serves `plugins/
|
|
58916
|
+
// the root serves `plugins/werkmeister`. `marketplace` must match the
|
|
58453
58917
|
// `name` field in that manifest, or install resolves nothing.
|
|
58454
58918
|
{
|
|
58455
|
-
name: "
|
|
58456
|
-
marketplace: "
|
|
58919
|
+
name: "werkmeister",
|
|
58920
|
+
marketplace: "werkmeister",
|
|
58457
58921
|
source: "jbbz-sandbox/werkmeister@main"
|
|
58458
58922
|
},
|
|
58459
58923
|
{
|
|
@@ -58462,6 +58926,11 @@ var init_machine_setup = __esm({
|
|
|
58462
58926
|
source: "DietrichGebert/ponytail"
|
|
58463
58927
|
}
|
|
58464
58928
|
];
|
|
58929
|
+
LEGACY_PLUGIN = {
|
|
58930
|
+
name: LEGACY_PLUGIN_NAME,
|
|
58931
|
+
marketplace: "sandbox",
|
|
58932
|
+
source: "jbbz-sandbox/werkmeister@main"
|
|
58933
|
+
};
|
|
58465
58934
|
OPTIONAL_PLUGINS = [
|
|
58466
58935
|
{
|
|
58467
58936
|
// Not obra/superpowers directly: that repo's manifest names itself
|
|
@@ -58487,7 +58956,7 @@ var init_machine_setup = __esm({
|
|
|
58487
58956
|
url: DEMO_SERVICES_REPO_URL,
|
|
58488
58957
|
missingConsequence: "The demo backend/frontend services will not start until it is cloned there."
|
|
58489
58958
|
};
|
|
58490
|
-
LEGACY_CONFIG_DIR =
|
|
58959
|
+
LEGACY_CONFIG_DIR = path61.join("infra", "config");
|
|
58491
58960
|
JIRA_TOKEN_URL = "https://id.atlassian.com/manage-profile/security/api-tokens";
|
|
58492
58961
|
JIRA_REQUIRED_KEYS = ["base_url", "email", "api_token"];
|
|
58493
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";
|
|
@@ -58495,14 +58964,14 @@ var init_machine_setup = __esm({
|
|
|
58495
58964
|
"services-fragment.schema.json": "**/.sandbox/services.yaml",
|
|
58496
58965
|
"workspace.schema.json": "**/.sandbox/workspace.yml"
|
|
58497
58966
|
};
|
|
58498
|
-
VSCODE_EXTENSION_ID = "jbbz.vscode
|
|
58499
|
-
BUNDLED_VSIX_REL =
|
|
58967
|
+
VSCODE_EXTENSION_ID = "jbbz.werkmeister-vscode";
|
|
58968
|
+
BUNDLED_VSIX_REL = path61.join("dist", "sandbox-workspaces.vsix");
|
|
58500
58969
|
}
|
|
58501
58970
|
});
|
|
58502
58971
|
|
|
58503
58972
|
// ../../packages/sandbox-core/dist/mcp_gen.js
|
|
58504
|
-
import * as
|
|
58505
|
-
import * as
|
|
58973
|
+
import * as fs54 from "node:fs";
|
|
58974
|
+
import * as path62 from "node:path";
|
|
58506
58975
|
function resolveWorkspaceMcpServers(presentArchetypes2, archetypes) {
|
|
58507
58976
|
const names = /* @__PURE__ */ new Set();
|
|
58508
58977
|
for (const archName of Object.keys(presentArchetypes2)) {
|
|
@@ -58543,12 +59012,12 @@ function substituteVars(value, vars2) {
|
|
|
58543
59012
|
return value;
|
|
58544
59013
|
}
|
|
58545
59014
|
function writeWorkspaceMcp(workspaceRoot, serverNames, definitions, vars2 = {}) {
|
|
58546
|
-
const mcpPath =
|
|
58547
|
-
const isFile4 =
|
|
59015
|
+
const mcpPath = path62.join(workspaceRoot, ".mcp.json");
|
|
59016
|
+
const isFile4 = fs54.existsSync(mcpPath) && fs54.statSync(mcpPath).isFile();
|
|
58548
59017
|
let config = {};
|
|
58549
59018
|
if (isFile4) {
|
|
58550
59019
|
try {
|
|
58551
|
-
const existing2 = JSON.parse(
|
|
59020
|
+
const existing2 = JSON.parse(fs54.readFileSync(mcpPath, "utf8"));
|
|
58552
59021
|
if (typeof existing2 === "object" && existing2 !== null && !Array.isArray(existing2)) {
|
|
58553
59022
|
config = existing2;
|
|
58554
59023
|
} else {
|
|
@@ -58575,21 +59044,26 @@ function writeWorkspaceMcp(workspaceRoot, serverNames, definitions, vars2 = {})
|
|
|
58575
59044
|
delete servers[name];
|
|
58576
59045
|
}
|
|
58577
59046
|
}
|
|
59047
|
+
const legacy = servers[LEGACY_SERVER];
|
|
59048
|
+
if (legacy?.headers?.["X-Sandbox-Feature"] !== void 0)
|
|
59049
|
+
delete servers[LEGACY_SERVER];
|
|
58578
59050
|
Object.assign(servers, wanted);
|
|
58579
|
-
|
|
59051
|
+
fs54.writeFileSync(mcpPath, JSON.stringify(config, null, 2) + "\n");
|
|
58580
59052
|
return mcpPath;
|
|
58581
59053
|
}
|
|
59054
|
+
var LEGACY_SERVER;
|
|
58582
59055
|
var init_mcp_gen = __esm({
|
|
58583
59056
|
"../../packages/sandbox-core/dist/mcp_gen.js"() {
|
|
58584
59057
|
"use strict";
|
|
58585
59058
|
init_define_SANDBOX_BUILD();
|
|
59059
|
+
LEGACY_SERVER = "sandbox";
|
|
58586
59060
|
}
|
|
58587
59061
|
});
|
|
58588
59062
|
|
|
58589
59063
|
// ../../packages/sandbox-core/dist/workspace_init.js
|
|
58590
|
-
import * as
|
|
59064
|
+
import * as fs55 from "node:fs";
|
|
58591
59065
|
import * as os19 from "node:os";
|
|
58592
|
-
import * as
|
|
59066
|
+
import * as path63 from "node:path";
|
|
58593
59067
|
function fail2(message) {
|
|
58594
59068
|
error(message);
|
|
58595
59069
|
throw new WorkspaceInitError(message);
|
|
@@ -58612,7 +59086,7 @@ async function resolveIssueCopy(root, issueId, opts) {
|
|
|
58612
59086
|
}
|
|
58613
59087
|
function isDir4(p) {
|
|
58614
59088
|
try {
|
|
58615
|
-
return
|
|
59089
|
+
return fs55.statSync(p).isDirectory();
|
|
58616
59090
|
} catch {
|
|
58617
59091
|
return false;
|
|
58618
59092
|
}
|
|
@@ -58629,26 +59103,26 @@ function splitMultiValue(value) {
|
|
|
58629
59103
|
return value.split(",").map((s) => s.trim()).filter((s) => s);
|
|
58630
59104
|
}
|
|
58631
59105
|
function hasWorkspaceConfig(workspaceRoot, override) {
|
|
58632
|
-
if (override &&
|
|
59106
|
+
if (override && fs55.existsSync(expandUserPath(override))) {
|
|
58633
59107
|
return true;
|
|
58634
59108
|
}
|
|
58635
59109
|
const envOverride = envVar("WORKSPACE_CONFIG");
|
|
58636
|
-
if (envOverride &&
|
|
59110
|
+
if (envOverride && fs55.existsSync(expandUserPath(envOverride))) {
|
|
58637
59111
|
return true;
|
|
58638
59112
|
}
|
|
58639
59113
|
const sandbox = dotDir(workspaceRoot);
|
|
58640
|
-
return _WORKSPACE_CONFIG_NAMES.some((name) =>
|
|
59114
|
+
return _WORKSPACE_CONFIG_NAMES.some((name) => fs55.existsSync(path63.join(sandbox, name)));
|
|
58641
59115
|
}
|
|
58642
59116
|
function writeWorkspaceConfig(workspaceRoot, resolved) {
|
|
58643
59117
|
if (Object.keys(resolved).length === 0) {
|
|
58644
59118
|
return;
|
|
58645
59119
|
}
|
|
58646
59120
|
const sandboxDir = dotDir(workspaceRoot);
|
|
58647
|
-
const target =
|
|
58648
|
-
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));
|
|
58649
59123
|
let existing2 = {};
|
|
58650
59124
|
for (const source of [target, ...legacies]) {
|
|
58651
|
-
if (
|
|
59125
|
+
if (fs55.existsSync(source) && fs55.statSync(source).isFile()) {
|
|
58652
59126
|
try {
|
|
58653
59127
|
const data = loadWorkspaceConfig(source);
|
|
58654
59128
|
if (typeof data === "object" && data !== null && !Array.isArray(data)) {
|
|
@@ -58670,21 +59144,23 @@ function writeWorkspaceConfig(workspaceRoot, resolved) {
|
|
|
58670
59144
|
}
|
|
58671
59145
|
if (field === "autonomous" || field === "auto_stop")
|
|
58672
59146
|
value = value === "true";
|
|
59147
|
+
if (field === "auto_stop" && value === false && !(field in existing2))
|
|
59148
|
+
continue;
|
|
58673
59149
|
merged[field] = value;
|
|
58674
59150
|
}
|
|
58675
59151
|
}
|
|
58676
59152
|
const body = YAML8.stringify(merged, { sortMapEntries: true, blockQuote: "literal" });
|
|
58677
59153
|
const serialized = _WORKSPACE_CONFIG_HEADER + body;
|
|
58678
|
-
if (
|
|
59154
|
+
if (fs55.existsSync(target) && fs55.readFileSync(target, "utf8") === serialized) {
|
|
58679
59155
|
return;
|
|
58680
59156
|
}
|
|
58681
|
-
|
|
58682
|
-
|
|
59157
|
+
fs55.mkdirSync(sandboxDir, { recursive: true });
|
|
59158
|
+
fs55.writeFileSync(target, serialized);
|
|
58683
59159
|
success(`Persisted inputs to ${target}`);
|
|
58684
59160
|
for (const legacy of legacies) {
|
|
58685
|
-
if (
|
|
58686
|
-
|
|
58687
|
-
info(`Removed legacy ${
|
|
59161
|
+
if (fs55.existsSync(legacy)) {
|
|
59162
|
+
fs55.unlinkSync(legacy);
|
|
59163
|
+
info(`Removed legacy ${path63.basename(legacy)} (superseded by workspace.yml)`);
|
|
58688
59164
|
}
|
|
58689
59165
|
}
|
|
58690
59166
|
}
|
|
@@ -58702,16 +59178,16 @@ function resolveReposRoot() {
|
|
|
58702
59178
|
function resolveWorkspacesRoot() {
|
|
58703
59179
|
const configured = loadPaths()["workspaces_root"];
|
|
58704
59180
|
if (configured) {
|
|
58705
|
-
return
|
|
59181
|
+
return path63.resolve(expandUser(configured));
|
|
58706
59182
|
}
|
|
58707
|
-
return
|
|
59183
|
+
return path63.resolve(path63.join(os19.homedir(), "workspace"));
|
|
58708
59184
|
}
|
|
58709
59185
|
function createWorkspaceRoot(workspaceDir, branch, force) {
|
|
58710
59186
|
const reposRoot = resolveReposRoot();
|
|
58711
|
-
const rootRepoSrc =
|
|
59187
|
+
const rootRepoSrc = path63.join(reposRoot, WORKSPACE_ROOT_REPO);
|
|
58712
59188
|
info(`Workspace: ${workspaceDir}`);
|
|
58713
59189
|
info(`Branch: ${branch}`);
|
|
58714
|
-
if (
|
|
59190
|
+
if (fs55.existsSync(path63.join(rootRepoSrc, ".git"))) {
|
|
58715
59191
|
try {
|
|
58716
59192
|
createWorktree(rootRepoSrc, workspaceDir, branch, { force });
|
|
58717
59193
|
} catch (exc) {
|
|
@@ -58720,11 +59196,11 @@ function createWorkspaceRoot(workspaceDir, branch, force) {
|
|
|
58720
59196
|
success(`Created workspace root (worktree of ${WORKSPACE_ROOT_REPO}) at ${workspaceDir}`);
|
|
58721
59197
|
} else {
|
|
58722
59198
|
warn(`No '${WORKSPACE_ROOT_REPO}' repo at ${rootRepoSrc} \u2014 workspace root will be a plain directory (no git history for notes/config).`);
|
|
58723
|
-
|
|
59199
|
+
fs55.mkdirSync(workspaceDir, { recursive: true });
|
|
58724
59200
|
}
|
|
58725
59201
|
}
|
|
58726
59202
|
function commitWorkspaceState(workspaceDir, workspaceName) {
|
|
58727
|
-
if (!
|
|
59203
|
+
if (!fs55.existsSync(path63.join(workspaceDir, ".git"))) {
|
|
58728
59204
|
return;
|
|
58729
59205
|
}
|
|
58730
59206
|
try {
|
|
@@ -58764,9 +59240,9 @@ function candidateWorkspaceRoots() {
|
|
|
58764
59240
|
const roots = [];
|
|
58765
59241
|
const configured = loadPaths()["workspaces_root"];
|
|
58766
59242
|
if (configured) {
|
|
58767
|
-
roots.push(
|
|
59243
|
+
roots.push(path63.resolve(expandUserPath(configured)));
|
|
58768
59244
|
}
|
|
58769
|
-
roots.push(
|
|
59245
|
+
roots.push(path63.join(os19.homedir(), "workspace"));
|
|
58770
59246
|
const seen = /* @__PURE__ */ new Set();
|
|
58771
59247
|
const unique = [];
|
|
58772
59248
|
for (const r of roots) {
|
|
@@ -58778,20 +59254,20 @@ function candidateWorkspaceRoots() {
|
|
|
58778
59254
|
return unique;
|
|
58779
59255
|
}
|
|
58780
59256
|
function findWorkspaceRoot2() {
|
|
58781
|
-
const cwd =
|
|
59257
|
+
const cwd = path63.resolve(process.cwd());
|
|
58782
59258
|
for (const root of candidateWorkspaceRoots()) {
|
|
58783
|
-
const rel =
|
|
58784
|
-
if (rel === ".." || rel.startsWith(".." +
|
|
59259
|
+
const rel = path63.relative(root, cwd);
|
|
59260
|
+
if (rel === ".." || rel.startsWith(".." + path63.sep) || path63.isAbsolute(rel)) {
|
|
58785
59261
|
continue;
|
|
58786
59262
|
}
|
|
58787
59263
|
if (rel === "") {
|
|
58788
59264
|
continue;
|
|
58789
59265
|
}
|
|
58790
|
-
return
|
|
59266
|
+
return path63.join(root, rel.split(path63.sep)[0]);
|
|
58791
59267
|
}
|
|
58792
59268
|
for (const directory of [cwd, ...ancestors(cwd)]) {
|
|
58793
|
-
if (
|
|
58794
|
-
return
|
|
59269
|
+
if (fs55.existsSync(path63.join(directory, ".git"))) {
|
|
59270
|
+
return path63.dirname(directory);
|
|
58795
59271
|
}
|
|
58796
59272
|
}
|
|
58797
59273
|
const rootsDisplay = candidateWorkspaceRoots().join(", ");
|
|
@@ -58802,7 +59278,7 @@ function ancestors(p) {
|
|
|
58802
59278
|
const result = [];
|
|
58803
59279
|
let cur = p;
|
|
58804
59280
|
while (true) {
|
|
58805
|
-
const parent =
|
|
59281
|
+
const parent = path63.dirname(cur);
|
|
58806
59282
|
if (parent === cur) {
|
|
58807
59283
|
break;
|
|
58808
59284
|
}
|
|
@@ -58821,16 +59297,16 @@ function getBranch(p) {
|
|
|
58821
59297
|
return result2.stdout.trim() || "unknown";
|
|
58822
59298
|
}
|
|
58823
59299
|
function discoverRepos(workspaceRoot) {
|
|
58824
|
-
return repoDirs(workspaceRoot).map((d) => [
|
|
59300
|
+
return repoDirs(workspaceRoot).map((d) => [path63.basename(d), getBranch(d)]);
|
|
58825
59301
|
}
|
|
58826
59302
|
function workspaceBranch(workspaceRoot) {
|
|
58827
|
-
if (
|
|
59303
|
+
if (fs55.existsSync(path63.join(workspaceRoot, ".git"))) {
|
|
58828
59304
|
const branch = getBranch(workspaceRoot);
|
|
58829
59305
|
if (branch && branch !== "unknown") {
|
|
58830
59306
|
return branch;
|
|
58831
59307
|
}
|
|
58832
59308
|
}
|
|
58833
|
-
return `${getuser()}/${
|
|
59309
|
+
return `${getuser()}/${path63.basename(workspaceRoot)}`;
|
|
58834
59310
|
}
|
|
58835
59311
|
function reconcileWorktrees(workspaceRoot, desiredRepos, force) {
|
|
58836
59312
|
const reposRoot = getReposRoot();
|
|
@@ -58839,12 +59315,12 @@ function reconcileWorktrees(workspaceRoot, desiredRepos, force) {
|
|
|
58839
59315
|
const present = new Set(discoverRepos(workspaceRoot).map(([name]) => name));
|
|
58840
59316
|
const toCreate = desired.filter((r) => !present.has(r));
|
|
58841
59317
|
const toRemove = [...present].filter((r) => !desiredSet.has(r)).sort();
|
|
58842
|
-
const missingSrc = toCreate.filter((r) => !isDir4(
|
|
59318
|
+
const missingSrc = toCreate.filter((r) => !isDir4(path63.join(reposRoot, r)));
|
|
58843
59319
|
if (missingSrc.length) {
|
|
58844
59320
|
fail2(`Source repo(s) not found under ${reposRoot}: ${missingSrc.join(", ")}. Clone them there (or fix \`repos\` in .sandbox/workspace.yml) and rerun.`);
|
|
58845
59321
|
}
|
|
58846
59322
|
if (toRemove.length && !force) {
|
|
58847
|
-
const dirty = toRemove.filter((r) => worktreeIsDirty(
|
|
59323
|
+
const dirty = toRemove.filter((r) => worktreeIsDirty(path63.join(workspaceRoot, r)));
|
|
58848
59324
|
if (dirty.length) {
|
|
58849
59325
|
fail2(`Refusing to remove worktree(s) with uncommitted changes: ${dirty.join(", ")}. Commit or stash them, or rerun with --force to discard them.`);
|
|
58850
59326
|
}
|
|
@@ -58854,7 +59330,7 @@ function reconcileWorktrees(workspaceRoot, desiredRepos, force) {
|
|
|
58854
59330
|
}
|
|
58855
59331
|
const branch = workspaceBranch(workspaceRoot);
|
|
58856
59332
|
for (const repo of toRemove) {
|
|
58857
|
-
const [ok, message] = removeWorktree(
|
|
59333
|
+
const [ok, message] = removeWorktree(path63.join(reposRoot, repo), path63.join(workspaceRoot, repo), branch);
|
|
58858
59334
|
if (ok) {
|
|
58859
59335
|
success(`Removed worktree '${repo}' (no longer in workspace.yml).`);
|
|
58860
59336
|
if (message) {
|
|
@@ -58867,8 +59343,8 @@ function reconcileWorktrees(workspaceRoot, desiredRepos, force) {
|
|
|
58867
59343
|
const created = [];
|
|
58868
59344
|
try {
|
|
58869
59345
|
for (const repo of toCreate) {
|
|
58870
|
-
const src =
|
|
58871
|
-
const dest =
|
|
59346
|
+
const src = path63.join(reposRoot, repo);
|
|
59347
|
+
const dest = path63.join(workspaceRoot, repo);
|
|
58872
59348
|
createWorktree(src, dest, branch, { force });
|
|
58873
59349
|
created.push([dest, src]);
|
|
58874
59350
|
}
|
|
@@ -58896,10 +59372,10 @@ function resolveTask(argsTask, workspaceName, readStdinTask2) {
|
|
|
58896
59372
|
return [slugToTask(workspaceName), {}];
|
|
58897
59373
|
}
|
|
58898
59374
|
function warnIfExisting(claudeMdPath) {
|
|
58899
|
-
if (!
|
|
59375
|
+
if (!fs55.existsSync(claudeMdPath)) {
|
|
58900
59376
|
return;
|
|
58901
59377
|
}
|
|
58902
|
-
const existing2 =
|
|
59378
|
+
const existing2 = fs55.readFileSync(claudeMdPath, "utf8");
|
|
58903
59379
|
if (existing2.includes("## Layout")) {
|
|
58904
59380
|
warn("CLAUDE.md already initialised \u2014 overwriting with fresh content.");
|
|
58905
59381
|
} else {
|
|
@@ -58922,7 +59398,7 @@ function logMeta(task, meta) {
|
|
|
58922
59398
|
}
|
|
58923
59399
|
async function initWorkspace(args, workspaceRootOverride, opts = {}) {
|
|
58924
59400
|
const workspaceRoot = workspaceRootOverride ?? findWorkspaceRoot2();
|
|
58925
|
-
const workspaceName =
|
|
59401
|
+
const workspaceName = path63.basename(workspaceRoot);
|
|
58926
59402
|
info(`Workspace root: ${workspaceRoot}`);
|
|
58927
59403
|
const inputs = resolveInputs(args, workspaceRoot);
|
|
58928
59404
|
const strategy = strategyById(parseStrategyId(inputs["context_strategy"]));
|
|
@@ -58951,7 +59427,7 @@ async function initWorkspace(args, workspaceRootOverride, opts = {}) {
|
|
|
58951
59427
|
}
|
|
58952
59428
|
reconcileWorktrees(workspaceRoot, desiredRepos, Boolean(args["force"]));
|
|
58953
59429
|
}
|
|
58954
|
-
const claudeMdPath =
|
|
59430
|
+
const claudeMdPath = path63.join(workspaceRoot, "CLAUDE.md");
|
|
58955
59431
|
warnIfExisting(claudeMdPath);
|
|
58956
59432
|
info("Scanning for git repos...");
|
|
58957
59433
|
const repos = discoverRepos(workspaceRoot);
|
|
@@ -59008,7 +59484,7 @@ async function initWorkspace(args, workspaceRootOverride, opts = {}) {
|
|
|
59008
59484
|
for (const warning of pluginDriftWarnings()) {
|
|
59009
59485
|
warn(warning);
|
|
59010
59486
|
}
|
|
59011
|
-
const envPath =
|
|
59487
|
+
const envPath = path63.join(workspaceRoot, ".env");
|
|
59012
59488
|
seedWorkspaceMetadataEnv(envPath, meta);
|
|
59013
59489
|
success(`Wrote ${envPath}`);
|
|
59014
59490
|
const feature2 = (meta["FEATURE"] ?? "").trim();
|
|
@@ -59067,8 +59543,8 @@ async function provisionWorkspace(resolved, opts = {}) {
|
|
|
59067
59543
|
fail2(`Workspace name '${workspaceName}' is not a valid slug (expected lowercase letters, digits, and single hyphens).`);
|
|
59068
59544
|
}
|
|
59069
59545
|
const branch = buildBranchName(os19.userInfo().username, workspaceName);
|
|
59070
|
-
const workspaceDir =
|
|
59071
|
-
if (
|
|
59546
|
+
const workspaceDir = path63.join(resolveWorkspacesRoot(), workspaceName);
|
|
59547
|
+
if (fs55.existsSync(workspaceDir)) {
|
|
59072
59548
|
info(`Workspace already exists: ${workspaceDir} \u2014 re-running init-workspace.`);
|
|
59073
59549
|
} else {
|
|
59074
59550
|
const repos = await selectRepos(resolved.repos, resolved.services);
|
|
@@ -59225,21 +59701,21 @@ __export(sandbox_exports, {
|
|
|
59225
59701
|
import { execFile as execFile2, spawn as spawn8 } from "node:child_process";
|
|
59226
59702
|
import { EventEmitter } from "node:events";
|
|
59227
59703
|
import { createReadStream } from "node:fs";
|
|
59228
|
-
import
|
|
59704
|
+
import fs56 from "node:fs/promises";
|
|
59229
59705
|
import os20 from "node:os";
|
|
59230
|
-
import
|
|
59706
|
+
import path64 from "node:path";
|
|
59231
59707
|
import readline5 from "node:readline";
|
|
59232
59708
|
import { promisify } from "node:util";
|
|
59233
59709
|
async function isDir5(p) {
|
|
59234
59710
|
try {
|
|
59235
|
-
return (await
|
|
59711
|
+
return (await fs56.stat(p)).isDirectory();
|
|
59236
59712
|
} catch {
|
|
59237
59713
|
return false;
|
|
59238
59714
|
}
|
|
59239
59715
|
}
|
|
59240
59716
|
async function isFile3(p) {
|
|
59241
59717
|
try {
|
|
59242
|
-
return (await
|
|
59718
|
+
return (await fs56.stat(p)).isFile();
|
|
59243
59719
|
} catch {
|
|
59244
59720
|
return false;
|
|
59245
59721
|
}
|
|
@@ -59250,23 +59726,23 @@ async function resolveSandboxBin(opts = {}) {
|
|
|
59250
59726
|
return explicit;
|
|
59251
59727
|
const root = envVar("ROOT");
|
|
59252
59728
|
if (root) {
|
|
59253
|
-
const cand =
|
|
59729
|
+
const cand = path64.join(root, "sandbox");
|
|
59254
59730
|
if (await isExecutable(cand))
|
|
59255
59731
|
return cand;
|
|
59256
59732
|
}
|
|
59257
59733
|
let dir = opts.from ?? import.meta.dirname;
|
|
59258
59734
|
for (let i = 0; i < 8; i++) {
|
|
59259
|
-
const cand =
|
|
59260
|
-
const hasCli = await isDir5(
|
|
59735
|
+
const cand = path64.join(dir, "sandbox");
|
|
59736
|
+
const hasCli = await isDir5(path64.join(dir, "apps", "cli"));
|
|
59261
59737
|
if (await isExecutable(cand) && hasCli) {
|
|
59262
59738
|
return cand;
|
|
59263
59739
|
}
|
|
59264
|
-
const parent =
|
|
59740
|
+
const parent = path64.dirname(dir);
|
|
59265
59741
|
if (parent === dir)
|
|
59266
59742
|
break;
|
|
59267
59743
|
dir = parent;
|
|
59268
59744
|
}
|
|
59269
|
-
const local =
|
|
59745
|
+
const local = path64.join(os20.homedir(), ".local", "bin", "sandbox");
|
|
59270
59746
|
if (await isExecutable(local))
|
|
59271
59747
|
return local;
|
|
59272
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.");
|
|
@@ -59363,7 +59839,7 @@ async function readServicesRegistry() {
|
|
|
59363
59839
|
if (!p)
|
|
59364
59840
|
return null;
|
|
59365
59841
|
try {
|
|
59366
|
-
return await
|
|
59842
|
+
return await fs56.readFile(p, "utf-8");
|
|
59367
59843
|
} catch {
|
|
59368
59844
|
return null;
|
|
59369
59845
|
}
|
|
@@ -59520,7 +59996,7 @@ function pickValidServices(raw2, known) {
|
|
|
59520
59996
|
}
|
|
59521
59997
|
async function readWorkspaceConfig(workspaceDir) {
|
|
59522
59998
|
try {
|
|
59523
|
-
const text = await
|
|
59999
|
+
const text = await fs56.readFile(path64.join(dotDir(workspaceDir), "workspace.yml"), "utf-8");
|
|
59524
60000
|
const data = (0, import_yaml15.parse)(text);
|
|
59525
60001
|
const issueId = issueIdOf(data) || null;
|
|
59526
60002
|
const labels = Array.isArray(data?.labels) ? data.labels.filter((l) => typeof l === "string") : [];
|
|
@@ -59534,19 +60010,19 @@ async function listWorkspaces() {
|
|
|
59534
60010
|
const root = workspacesRoot();
|
|
59535
60011
|
let entries;
|
|
59536
60012
|
try {
|
|
59537
|
-
entries = await
|
|
60013
|
+
entries = await fs56.readdir(root, { withFileTypes: true });
|
|
59538
60014
|
} catch (e) {
|
|
59539
60015
|
throw new WorkspaceError(`Could not read workspaces root ${root}: ${e.message}`);
|
|
59540
60016
|
}
|
|
59541
60017
|
const names = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name).sort((a, b) => a.localeCompare(b));
|
|
59542
60018
|
const workspaces = await Promise.all(names.map(async (name) => {
|
|
59543
|
-
const p =
|
|
60019
|
+
const p = path64.join(root, name);
|
|
59544
60020
|
const { issueId, labels, model } = await readWorkspaceConfig(p);
|
|
59545
60021
|
return {
|
|
59546
60022
|
name,
|
|
59547
60023
|
path: p,
|
|
59548
60024
|
// vscode://file + an absolute path (already leading-slash) → single slash.
|
|
59549
|
-
vscodeUri: `vscode://file${p.split(
|
|
60025
|
+
vscodeUri: `vscode://file${p.split(path64.sep).map(encodeURIComponent).join("/")}`,
|
|
59550
60026
|
issueId,
|
|
59551
60027
|
issueUrl: issueId ? issueTracker().url(issueId) : null,
|
|
59552
60028
|
labels,
|
|
@@ -59723,7 +60199,7 @@ async function resolveWorkspaceDir(name) {
|
|
|
59723
60199
|
if (!clean || clean.includes("/") || clean.includes("\\") || clean.includes("..")) {
|
|
59724
60200
|
throw new WorkspaceError(`Invalid workspace name: ${JSON.stringify(name)}`);
|
|
59725
60201
|
}
|
|
59726
|
-
const target =
|
|
60202
|
+
const target = path64.join(workspacesRoot(), clean);
|
|
59727
60203
|
if (!await isDir5(target)) {
|
|
59728
60204
|
throw new WorkspaceError(`No such workspace: ${clean}`);
|
|
59729
60205
|
}
|
|
@@ -59734,7 +60210,7 @@ function reconcileDirForKey(name) {
|
|
|
59734
60210
|
if (!slug || slug.includes("/") || slug.includes("\\") || slug.includes("..")) {
|
|
59735
60211
|
throw new WorkspaceError(`Invalid reconcile session: ${JSON.stringify(name)}`);
|
|
59736
60212
|
}
|
|
59737
|
-
return
|
|
60213
|
+
return path64.join(reconcileRoot(), slug);
|
|
59738
60214
|
}
|
|
59739
60215
|
async function resolveSessionDir(name) {
|
|
59740
60216
|
if ((name ?? "").startsWith(RECONCILE_PREFIX)) {
|
|
@@ -59754,7 +60230,7 @@ async function openWorkspace(name) {
|
|
|
59754
60230
|
}
|
|
59755
60231
|
async function readEnvValue(dir, key) {
|
|
59756
60232
|
try {
|
|
59757
|
-
const text = await
|
|
60233
|
+
const text = await fs56.readFile(path64.join(dir, ".env"), "utf-8");
|
|
59758
60234
|
for (const line of text.split(/\r?\n/)) {
|
|
59759
60235
|
const eq = line.indexOf("=");
|
|
59760
60236
|
if (eq > 0 && line.slice(0, eq).trim() === key)
|
|
@@ -59766,7 +60242,7 @@ async function readEnvValue(dir, key) {
|
|
|
59766
60242
|
}
|
|
59767
60243
|
async function readWorkspaceFeature(dir) {
|
|
59768
60244
|
try {
|
|
59769
|
-
const text = await
|
|
60245
|
+
const text = await fs56.readFile(path64.join(dotDir(dir), "workspace.yml"), "utf-8");
|
|
59770
60246
|
const m = /^feature: *["']?([^"'\s#]+)/m.exec(text);
|
|
59771
60247
|
return m ? m[1] : null;
|
|
59772
60248
|
} catch {
|
|
@@ -59774,7 +60250,7 @@ async function readWorkspaceFeature(dir) {
|
|
|
59774
60250
|
}
|
|
59775
60251
|
}
|
|
59776
60252
|
async function readFileTail(file, maxBytes = 131072) {
|
|
59777
|
-
const fh = await
|
|
60253
|
+
const fh = await fs56.open(file, "r");
|
|
59778
60254
|
try {
|
|
59779
60255
|
const { size } = await fh.stat();
|
|
59780
60256
|
const start2 = Math.max(0, size - maxBytes);
|
|
@@ -59870,7 +60346,7 @@ async function loadPortsDoc() {
|
|
|
59870
60346
|
if (!file)
|
|
59871
60347
|
return null;
|
|
59872
60348
|
try {
|
|
59873
|
-
return (0, import_yaml15.parse)(await
|
|
60349
|
+
return (0, import_yaml15.parse)(await fs56.readFile(file, "utf8")) ?? null;
|
|
59874
60350
|
} catch {
|
|
59875
60351
|
return null;
|
|
59876
60352
|
}
|
|
@@ -59881,12 +60357,12 @@ function slotPortLabel(slot, ports) {
|
|
|
59881
60357
|
}
|
|
59882
60358
|
async function getWorkspaceStatuses() {
|
|
59883
60359
|
const root = workspacesRoot();
|
|
59884
|
-
const entries = await
|
|
60360
|
+
const entries = await fs56.readdir(root, { withFileTypes: true }).catch(() => []);
|
|
59885
60361
|
const names = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name).sort((a, b) => a.localeCompare(b));
|
|
59886
60362
|
const projects = await dockerComposeProjects();
|
|
59887
60363
|
const portsDoc = await loadPortsDoc();
|
|
59888
60364
|
const statuses = await Promise.all(names.map(async (name) => {
|
|
59889
|
-
const dir =
|
|
60365
|
+
const dir = path64.join(root, name);
|
|
59890
60366
|
let project = await readEnvValue(dir, "COMPOSE_PROJECT_NAME");
|
|
59891
60367
|
if (!project) {
|
|
59892
60368
|
const feature2 = await readWorkspaceFeature(dir);
|
|
@@ -59965,19 +60441,19 @@ async function getIntercepts() {
|
|
|
59965
60441
|
}
|
|
59966
60442
|
async function hasGit(dir) {
|
|
59967
60443
|
try {
|
|
59968
|
-
await
|
|
60444
|
+
await fs56.access(path64.join(dir, ".git"));
|
|
59969
60445
|
return true;
|
|
59970
60446
|
} catch {
|
|
59971
60447
|
return false;
|
|
59972
60448
|
}
|
|
59973
60449
|
}
|
|
59974
60450
|
async function listRepoWorktrees(workspaceDir) {
|
|
59975
|
-
const entries = await
|
|
60451
|
+
const entries = await fs56.readdir(workspaceDir, { withFileTypes: true }).catch(() => []);
|
|
59976
60452
|
const out = [];
|
|
59977
60453
|
for (const e of entries) {
|
|
59978
60454
|
if (!e.isDirectory() || e.name.startsWith("."))
|
|
59979
60455
|
continue;
|
|
59980
|
-
const dir =
|
|
60456
|
+
const dir = path64.join(workspaceDir, e.name);
|
|
59981
60457
|
if (await hasGit(dir))
|
|
59982
60458
|
out.push({ repo: e.name, dir });
|
|
59983
60459
|
}
|
|
@@ -60019,10 +60495,10 @@ async function ghPrForWorktree(repoDir) {
|
|
|
60019
60495
|
}
|
|
60020
60496
|
async function getWorkspacePRs() {
|
|
60021
60497
|
const root = workspacesRoot();
|
|
60022
|
-
const entries = await
|
|
60498
|
+
const entries = await fs56.readdir(root, { withFileTypes: true }).catch(() => []);
|
|
60023
60499
|
const names = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name).sort((a, b) => a.localeCompare(b));
|
|
60024
60500
|
return Promise.all(names.map(async (name) => {
|
|
60025
|
-
const repos = await listRepoWorktrees(
|
|
60501
|
+
const repos = await listRepoWorktrees(path64.join(root, name));
|
|
60026
60502
|
const repoPrs = await Promise.all(repos.map(async ({ repo, dir }) => ({ repo, ...await ghPrForWorktree(dir) })));
|
|
60027
60503
|
return { name, repos: repoPrs };
|
|
60028
60504
|
}));
|
|
@@ -60230,7 +60706,7 @@ async function worktreeSources(worktrees) {
|
|
|
60230
60706
|
if (res && res.code === 0) {
|
|
60231
60707
|
const common2 = stripAnsi(res.stdout).trim();
|
|
60232
60708
|
if (common2)
|
|
60233
|
-
srcs.add(
|
|
60709
|
+
srcs.add(path64.dirname(common2));
|
|
60234
60710
|
}
|
|
60235
60711
|
}
|
|
60236
60712
|
return [...srcs];
|
|
@@ -60239,8 +60715,8 @@ function gitErrorLine(text) {
|
|
|
60239
60715
|
const lines = stripAnsi(text).trim().split("\n");
|
|
60240
60716
|
return (lines.find((l) => /^\s*!\s|^(error|fatal):/.test(l)) ?? lines[0]).trim().replace(/\s+/g, " ");
|
|
60241
60717
|
}
|
|
60242
|
-
async function commitAndPush(dir, message = `chore: handoff ${
|
|
60243
|
-
const label =
|
|
60718
|
+
async function commitAndPush(dir, message = `chore: handoff ${path64.basename(dir)} before workspace teardown`, paths = []) {
|
|
60719
|
+
const label = path64.basename(dir);
|
|
60244
60720
|
const br = await runCapture("git", ["-C", dir, "rev-parse", "--abbrev-ref", "HEAD"]).catch(() => null);
|
|
60245
60721
|
const branch = br && br.code === 0 ? stripAnsi(br.stdout).trim() : null;
|
|
60246
60722
|
if (!branch || branch === "HEAD")
|
|
@@ -60322,7 +60798,7 @@ ${parts.join("\n")}`);
|
|
|
60322
60798
|
parts.push(detail);
|
|
60323
60799
|
}
|
|
60324
60800
|
}
|
|
60325
|
-
await
|
|
60801
|
+
await fs56.rm(target, { recursive: true, force: true });
|
|
60326
60802
|
for (const src of sources) {
|
|
60327
60803
|
await runCapture("git", ["-C", src, "worktree", "prune"]).catch(() => {
|
|
60328
60804
|
});
|
|
@@ -60487,17 +60963,17 @@ function encodeClaudeProjectDir(p) {
|
|
|
60487
60963
|
return p.replace(/[^a-zA-Z0-9]/g, "-");
|
|
60488
60964
|
}
|
|
60489
60965
|
async function findClaudeProjectDir(workspacePath) {
|
|
60490
|
-
const root =
|
|
60966
|
+
const root = path64.join(os20.homedir(), ".claude", "projects");
|
|
60491
60967
|
const want = encodeClaudeProjectDir(workspacePath);
|
|
60492
60968
|
let entries;
|
|
60493
60969
|
try {
|
|
60494
|
-
entries = await
|
|
60970
|
+
entries = await fs56.readdir(root, { withFileTypes: true });
|
|
60495
60971
|
} catch {
|
|
60496
60972
|
return null;
|
|
60497
60973
|
}
|
|
60498
60974
|
for (const e of entries) {
|
|
60499
60975
|
if (e.isDirectory() && encodeClaudeProjectDir(e.name) === want) {
|
|
60500
|
-
return
|
|
60976
|
+
return path64.join(root, e.name);
|
|
60501
60977
|
}
|
|
60502
60978
|
}
|
|
60503
60979
|
return null;
|
|
@@ -60505,7 +60981,7 @@ async function findClaudeProjectDir(workspacePath) {
|
|
|
60505
60981
|
async function listSessionFiles(projectDir) {
|
|
60506
60982
|
let names;
|
|
60507
60983
|
try {
|
|
60508
|
-
names = await
|
|
60984
|
+
names = await fs56.readdir(projectDir);
|
|
60509
60985
|
} catch {
|
|
60510
60986
|
return [];
|
|
60511
60987
|
}
|
|
@@ -60514,8 +60990,8 @@ async function listSessionFiles(projectDir) {
|
|
|
60514
60990
|
if (!n.endsWith(".jsonl"))
|
|
60515
60991
|
continue;
|
|
60516
60992
|
try {
|
|
60517
|
-
const st = await
|
|
60518
|
-
out.push({ file:
|
|
60993
|
+
const st = await fs56.stat(path64.join(projectDir, n));
|
|
60994
|
+
out.push({ file: path64.join(projectDir, n), mtime: st.mtime });
|
|
60519
60995
|
} catch {
|
|
60520
60996
|
}
|
|
60521
60997
|
}
|
|
@@ -60525,7 +61001,7 @@ function pickSession(sessions, sessionId) {
|
|
|
60525
61001
|
if (!sessions.length)
|
|
60526
61002
|
return null;
|
|
60527
61003
|
if (sessionId) {
|
|
60528
|
-
const match = sessions.find((s) =>
|
|
61004
|
+
const match = sessions.find((s) => path64.basename(s.file, ".jsonl") === sessionId);
|
|
60529
61005
|
if (match)
|
|
60530
61006
|
return match;
|
|
60531
61007
|
}
|
|
@@ -60568,7 +61044,7 @@ async function listSessions(name) {
|
|
|
60568
61044
|
return [];
|
|
60569
61045
|
const files = await listSessionFiles(projectDir);
|
|
60570
61046
|
return Promise.all(files.map(async (f, i) => {
|
|
60571
|
-
const sessionId =
|
|
61047
|
+
const sessionId = path64.basename(f.file, ".jsonl");
|
|
60572
61048
|
return {
|
|
60573
61049
|
sessionId,
|
|
60574
61050
|
title: await readSessionTitle(f.file, sessionId),
|
|
@@ -60730,7 +61206,7 @@ async function loadPriceTable() {
|
|
|
60730
61206
|
if (!p)
|
|
60731
61207
|
return DEFAULT_PRICES;
|
|
60732
61208
|
try {
|
|
60733
|
-
const doc = (0, import_yaml15.parse)(await
|
|
61209
|
+
const doc = (0, import_yaml15.parse)(await fs56.readFile(p, "utf-8"));
|
|
60734
61210
|
return doc?.models && Object.keys(doc.models).length ? doc.models : DEFAULT_PRICES;
|
|
60735
61211
|
} catch {
|
|
60736
61212
|
return DEFAULT_PRICES;
|
|
@@ -60755,7 +61231,7 @@ async function getWorkspaceUsage(name) {
|
|
|
60755
61231
|
for (const f of files) {
|
|
60756
61232
|
let text;
|
|
60757
61233
|
try {
|
|
60758
|
-
text = await
|
|
61234
|
+
text = await fs56.readFile(f.file, "utf-8");
|
|
60759
61235
|
} catch {
|
|
60760
61236
|
continue;
|
|
60761
61237
|
}
|
|
@@ -60776,7 +61252,7 @@ async function getWorkspaceUsage(name) {
|
|
|
60776
61252
|
if (usd != null)
|
|
60777
61253
|
sessionUsd = (sessionUsd ?? 0) + usd;
|
|
60778
61254
|
}
|
|
60779
|
-
const sessionId =
|
|
61255
|
+
const sessionId = path64.basename(f.file, ".jsonl");
|
|
60780
61256
|
sessions.push({
|
|
60781
61257
|
sessionId,
|
|
60782
61258
|
title: await readSessionTitle(f.file, sessionId),
|
|
@@ -60821,7 +61297,7 @@ async function resolveSessionSource(name, sessionId) {
|
|
|
60821
61297
|
workspacePath: target,
|
|
60822
61298
|
projectDir,
|
|
60823
61299
|
sessionFile: chosen.file,
|
|
60824
|
-
sessionId:
|
|
61300
|
+
sessionId: path64.basename(chosen.file, ".jsonl"),
|
|
60825
61301
|
sessionCount: sessions.length,
|
|
60826
61302
|
updatedAt: chosen.mtime.toISOString()
|
|
60827
61303
|
};
|
|
@@ -60842,10 +61318,10 @@ async function getClaudeTranscript(name, limit = 20, sessionId) {
|
|
|
60842
61318
|
const chosen = pickSession(sessions, sessionId);
|
|
60843
61319
|
if (!chosen)
|
|
60844
61320
|
return empty;
|
|
60845
|
-
const resolvedId =
|
|
61321
|
+
const resolvedId = path64.basename(chosen.file, ".jsonl");
|
|
60846
61322
|
let text;
|
|
60847
61323
|
try {
|
|
60848
|
-
text = await
|
|
61324
|
+
text = await fs56.readFile(chosen.file, "utf-8");
|
|
60849
61325
|
} catch (e) {
|
|
60850
61326
|
throw new WorkspaceError(`Could not read session file: ${e.message}`);
|
|
60851
61327
|
}
|
|
@@ -60947,7 +61423,7 @@ async function getK8sForwardsHealth() {
|
|
|
60947
61423
|
if (!file) {
|
|
60948
61424
|
throw new WorkspaceError("Could not locate the service registry (~/.sandbox/services.yaml) \u2014 run `sandbox setup`.");
|
|
60949
61425
|
}
|
|
60950
|
-
const doc = (0, import_yaml15.parse)(await
|
|
61426
|
+
const doc = (0, import_yaml15.parse)(await fs56.readFile(file, "utf-8")) ?? {};
|
|
60951
61427
|
const forwards = await Promise.all(forwardSpecs(doc.services, doc.infrastructure).map(async (d) => {
|
|
60952
61428
|
const r = await checkForwardHealth(d.listen, d.health);
|
|
60953
61429
|
return { name: d.name, port: d.listen, healthy: r.ok, detail: r.detail };
|
|
@@ -61039,16 +61515,16 @@ async function launchPrReconcile(url, issueRef) {
|
|
|
61039
61515
|
}
|
|
61040
61516
|
const { repo, number } = parseRepoAndNumber(url);
|
|
61041
61517
|
const slug = `${repo}-${number}`;
|
|
61042
|
-
const dir =
|
|
61043
|
-
await
|
|
61518
|
+
const dir = path64.join(reconcileRoot(), slug);
|
|
61519
|
+
await fs56.mkdir(dir, { recursive: true });
|
|
61044
61520
|
const [issue, pr, diffRes] = await Promise.all([
|
|
61045
61521
|
fetchIssueCard(issueRef || url),
|
|
61046
61522
|
ghPrView(url),
|
|
61047
61523
|
runCapture("gh", ["pr", "diff", url]).catch(() => null)
|
|
61048
61524
|
]);
|
|
61049
61525
|
const diff = diffRes && diffRes.code === 0 ? stripAnsi(diffRes.stdout) : "";
|
|
61050
|
-
const ctxFile =
|
|
61051
|
-
await
|
|
61526
|
+
const ctxFile = path64.join(dir, "reconcile-context.md");
|
|
61527
|
+
await fs56.writeFile(ctxFile, buildReconcileContext(issue, pr, diff), "utf-8");
|
|
61052
61528
|
await openTerminalRunning(`cd ${shellQuote(dir)} && claude -p --disallowedTools Bash Edit Write --append-system-prompt-file ${shellQuote(ctxFile)} ${shellQuote(RECONCILE_PROMPT)}`, false);
|
|
61053
61529
|
return { sessionKey: `${RECONCILE_PREFIX}${slug}`, slug, issue };
|
|
61054
61530
|
}
|
|
@@ -61102,7 +61578,7 @@ var init_sandbox = __esm({
|
|
|
61102
61578
|
"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code"
|
|
61103
61579
|
].filter((p) => Boolean(p));
|
|
61104
61580
|
DISTILLED = ["feature", "task", "description", "starting_prompt", "services"];
|
|
61105
|
-
reconcileRoot = () =>
|
|
61581
|
+
reconcileRoot = () => path64.join(dotDir(os20.homedir()), "reconcile");
|
|
61106
61582
|
RECONCILE_PREFIX = "reconcile:";
|
|
61107
61583
|
APPROVAL_LABEL = "ready-for-human-approval";
|
|
61108
61584
|
FAILING_CONCLUSIONS = /* @__PURE__ */ new Set([
|
|
@@ -61392,8 +61868,8 @@ __export(detect_exports, {
|
|
|
61392
61868
|
run: () => run10,
|
|
61393
61869
|
runDetect: () => runDetect
|
|
61394
61870
|
});
|
|
61395
|
-
import * as
|
|
61396
|
-
import * as
|
|
61871
|
+
import * as fs57 from "node:fs";
|
|
61872
|
+
import * as path65 from "node:path";
|
|
61397
61873
|
function mark(fv) {
|
|
61398
61874
|
return fv.confidence === "stated" ? "" : ` ${Colors.YELLOW}[${fv.confidence}]${Colors.RESET}`;
|
|
61399
61875
|
}
|
|
@@ -61446,8 +61922,8 @@ function printPlan(plan) {
|
|
|
61446
61922
|
async function runDetect(args, deps) {
|
|
61447
61923
|
let root;
|
|
61448
61924
|
if (args.repo) {
|
|
61449
|
-
root =
|
|
61450
|
-
if (!
|
|
61925
|
+
root = path65.resolve(deps.cwd, args.repo);
|
|
61926
|
+
if (!fs57.statSync(root, { throwIfNoEntry: false })?.isDirectory()) {
|
|
61451
61927
|
warn(`${root} is not a directory \u2014 pass the repo path: sandbox detect <repo>`);
|
|
61452
61928
|
return 2;
|
|
61453
61929
|
}
|
|
@@ -61756,18 +62232,18 @@ var open_exports = {};
|
|
|
61756
62232
|
__export(open_exports, {
|
|
61757
62233
|
run: () => run15
|
|
61758
62234
|
});
|
|
61759
|
-
import * as
|
|
61760
|
-
import * as
|
|
62235
|
+
import * as fs58 from "node:fs";
|
|
62236
|
+
import * as path66 from "node:path";
|
|
61761
62237
|
function loadWorkspaceYaml(start2) {
|
|
61762
|
-
let dir =
|
|
62238
|
+
let dir = path66.resolve(start2);
|
|
61763
62239
|
for (; ; ) {
|
|
61764
62240
|
for (const name of ["workspace.yml", "workspace.yaml"]) {
|
|
61765
|
-
const p =
|
|
61766
|
-
if (
|
|
62241
|
+
const p = path66.join(dotDir(dir), name);
|
|
62242
|
+
if (fs58.existsSync(p) && fs58.statSync(p).isFile()) {
|
|
61767
62243
|
return readWorkspaceYaml(dir);
|
|
61768
62244
|
}
|
|
61769
62245
|
}
|
|
61770
|
-
const parent =
|
|
62246
|
+
const parent = path66.dirname(dir);
|
|
61771
62247
|
if (parent === dir) return {};
|
|
61772
62248
|
dir = parent;
|
|
61773
62249
|
}
|
|
@@ -61897,8 +62373,8 @@ var init_clean = __esm({
|
|
|
61897
62373
|
});
|
|
61898
62374
|
|
|
61899
62375
|
// ../../packages/sandbox-core/dist/stats.js
|
|
61900
|
-
import * as
|
|
61901
|
-
import * as
|
|
62376
|
+
import * as fs59 from "node:fs";
|
|
62377
|
+
import * as path67 from "node:path";
|
|
61902
62378
|
function sleep4(seconds, signal) {
|
|
61903
62379
|
return new Promise((resolve13) => {
|
|
61904
62380
|
if (signal?.aborted)
|
|
@@ -61996,12 +62472,12 @@ function pad2(n) {
|
|
|
61996
62472
|
return String(n).padStart(2, "0");
|
|
61997
62473
|
}
|
|
61998
62474
|
function saveReport(stats, recommendations) {
|
|
61999
|
-
const statsDir =
|
|
62000
|
-
if (!
|
|
62001
|
-
|
|
62475
|
+
const statsDir = path67.join(sandboxRoot(), "stats");
|
|
62476
|
+
if (!fs59.existsSync(statsDir))
|
|
62477
|
+
fs59.mkdirSync(statsDir);
|
|
62002
62478
|
const now = /* @__PURE__ */ new Date();
|
|
62003
62479
|
const timestamp = `${now.getFullYear()}${pad2(now.getMonth() + 1)}${pad2(now.getDate())}-${pad2(now.getHours())}${pad2(now.getMinutes())}${pad2(now.getSeconds())}`;
|
|
62004
|
-
const csvPath =
|
|
62480
|
+
const csvPath = path67.join(statsDir, `docker-stats-${timestamp}.csv`);
|
|
62005
62481
|
const csvRows = [
|
|
62006
62482
|
["container", "avg_cpu_pct", "max_cpu_pct", "avg_mem_mib", "max_mem_mib", "samples"].join(",")
|
|
62007
62483
|
];
|
|
@@ -62016,8 +62492,8 @@ function saveReport(stats, recommendations) {
|
|
|
62016
62492
|
String(s.samples)
|
|
62017
62493
|
].join(","));
|
|
62018
62494
|
}
|
|
62019
|
-
|
|
62020
|
-
const summaryPath =
|
|
62495
|
+
fs59.writeFileSync(csvPath, csvRows.join("\r\n") + "\r\n");
|
|
62496
|
+
const summaryPath = path67.join(statsDir, `resource-summary-${timestamp}.txt`);
|
|
62021
62497
|
const generated = `${now.getFullYear()}-${pad2(now.getMonth() + 1)}-${pad2(now.getDate())} ${pad2(now.getHours())}:${pad2(now.getMinutes())}:${pad2(now.getSeconds())}`;
|
|
62022
62498
|
let out = "";
|
|
62023
62499
|
out += "Docker Resource Usage Summary\n";
|
|
@@ -62035,7 +62511,7 @@ function saveReport(stats, recommendations) {
|
|
|
62035
62511
|
out += ` ${r.name.padEnd(35)} cpus: ${r.recommended_cpus.padStart(5)} memory: ${r.recommended_memory.padStart(5)}
|
|
62036
62512
|
`;
|
|
62037
62513
|
}
|
|
62038
|
-
|
|
62514
|
+
fs59.writeFileSync(summaryPath, out);
|
|
62039
62515
|
return summaryPath;
|
|
62040
62516
|
}
|
|
62041
62517
|
async function runCollection(durationMinutes, intervalSeconds, save2, signal) {
|
|
@@ -62487,14 +62963,14 @@ var init_standards2 = __esm({
|
|
|
62487
62963
|
});
|
|
62488
62964
|
|
|
62489
62965
|
// ../../packages/sandbox-core/dist/pr_guard.js
|
|
62490
|
-
import * as
|
|
62491
|
-
import * as
|
|
62966
|
+
import * as fs60 from "node:fs";
|
|
62967
|
+
import * as path68 from "node:path";
|
|
62492
62968
|
function findRecordRoot(start2) {
|
|
62493
|
-
let dir =
|
|
62969
|
+
let dir = path68.resolve(start2);
|
|
62494
62970
|
for (; ; ) {
|
|
62495
|
-
if (
|
|
62971
|
+
if (fs60.existsSync(path68.join(dotDir(dir), "workspace.yml")))
|
|
62496
62972
|
return dir;
|
|
62497
|
-
const parent =
|
|
62973
|
+
const parent = path68.dirname(dir);
|
|
62498
62974
|
if (parent === dir)
|
|
62499
62975
|
return null;
|
|
62500
62976
|
dir = parent;
|
|
@@ -62502,11 +62978,11 @@ function findRecordRoot(start2) {
|
|
|
62502
62978
|
}
|
|
62503
62979
|
function prFileDirName(root) {
|
|
62504
62980
|
try {
|
|
62505
|
-
const names =
|
|
62981
|
+
const names = fs60.readdirSync(path68.join(root, "stages")).sort();
|
|
62506
62982
|
const dir = names.find((n) => /^\d+_propose$/.test(n)) ?? names.find((n) => /^\d+_ship$/.test(n));
|
|
62507
62983
|
return dir ? `stages/${dir}/output/` : "the propose stage's output/";
|
|
62508
62984
|
} catch {
|
|
62509
|
-
return `${
|
|
62985
|
+
return `${path68.basename(dotDir(root))}/notes/`;
|
|
62510
62986
|
}
|
|
62511
62987
|
}
|
|
62512
62988
|
function decide2(input2) {
|
|
@@ -62518,13 +62994,13 @@ function decide2(input2) {
|
|
|
62518
62994
|
const opensPr = tool === "Bash" && GH_PR_CREATE.test(command) || MCP_CREATE_PR.test(tool);
|
|
62519
62995
|
if (!opensPr)
|
|
62520
62996
|
return null;
|
|
62521
|
-
const rules = ["_shared/pr-rules.md", ...DOT_DIRS.map((d) => `${d}/pr-rules.md`)].find((p) =>
|
|
62522
|
-
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}\`.`;
|
|
62523
62999
|
}
|
|
62524
63000
|
function main2() {
|
|
62525
63001
|
let reason2 = null;
|
|
62526
63002
|
try {
|
|
62527
|
-
const raw2 = process.stdin.isTTY ? "" :
|
|
63003
|
+
const raw2 = process.stdin.isTTY ? "" : fs60.readFileSync(0, "utf8");
|
|
62528
63004
|
const parsed = raw2.trim() ? JSON.parse(raw2) : {};
|
|
62529
63005
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed))
|
|
62530
63006
|
reason2 = decide2(parsed);
|
|
@@ -62589,13 +63065,13 @@ __export(init_workspace_exports, {
|
|
|
62589
63065
|
readStdinTask: () => readStdinTask,
|
|
62590
63066
|
run: () => run23
|
|
62591
63067
|
});
|
|
62592
|
-
import * as
|
|
63068
|
+
import * as fs61 from "node:fs";
|
|
62593
63069
|
function readStdinTask() {
|
|
62594
63070
|
if (process.stdin.isTTY) {
|
|
62595
63071
|
return "";
|
|
62596
63072
|
}
|
|
62597
63073
|
try {
|
|
62598
|
-
return
|
|
63074
|
+
return fs61.readFileSync(0, "utf8");
|
|
62599
63075
|
} catch {
|
|
62600
63076
|
return "";
|
|
62601
63077
|
}
|
|
@@ -62628,8 +63104,8 @@ __export(workspace_exports, {
|
|
|
62628
63104
|
browserFormCommand: () => browserFormCommand,
|
|
62629
63105
|
run: () => run24
|
|
62630
63106
|
});
|
|
62631
|
-
import * as
|
|
62632
|
-
import * as
|
|
63107
|
+
import * as fs62 from "node:fs";
|
|
63108
|
+
import * as path69 from "node:path";
|
|
62633
63109
|
async function resolveWorkspaceInputs(args) {
|
|
62634
63110
|
const base = process.cwd();
|
|
62635
63111
|
let resolved = resolveInputs(args, base);
|
|
@@ -62665,12 +63141,12 @@ async function resolveWorkspaceInputs(args) {
|
|
|
62665
63141
|
return { resolved, issue };
|
|
62666
63142
|
}
|
|
62667
63143
|
function which2(cmd) {
|
|
62668
|
-
const dirs = (process.env.PATH || "").split(
|
|
63144
|
+
const dirs = (process.env.PATH || "").split(path69.delimiter);
|
|
62669
63145
|
for (const dir of dirs) {
|
|
62670
63146
|
if (!dir) continue;
|
|
62671
|
-
const full =
|
|
63147
|
+
const full = path69.join(dir, cmd);
|
|
62672
63148
|
try {
|
|
62673
|
-
|
|
63149
|
+
fs62.accessSync(full, fs62.constants.X_OK);
|
|
62674
63150
|
return full;
|
|
62675
63151
|
} catch {
|
|
62676
63152
|
}
|
|
@@ -62691,8 +63167,8 @@ function browserFormCommand(node, cliJs, args) {
|
|
|
62691
63167
|
return cmd;
|
|
62692
63168
|
}
|
|
62693
63169
|
function runBrowserForm(args) {
|
|
62694
|
-
const cliJs =
|
|
62695
|
-
if (!(
|
|
63170
|
+
const cliJs = path69.join(getMonorepoRoot(), "apps", "sandbox-dashboard", "dist", "form-cli.js");
|
|
63171
|
+
if (!(fs62.existsSync(cliJs) && fs62.statSync(cliJs).isFile())) {
|
|
62696
63172
|
error(
|
|
62697
63173
|
`Browser form not built: ${cliJs} is missing. Run \`npm --prefix apps/sandbox-dashboard run build\` (or \`sandbox dashboard\` once) first.`
|
|
62698
63174
|
);
|
|
@@ -62815,7 +63291,7 @@ __export(issue_exports, {
|
|
|
62815
63291
|
run: () => run26,
|
|
62816
63292
|
updateFromArgs: () => updateFromArgs
|
|
62817
63293
|
});
|
|
62818
|
-
import * as
|
|
63294
|
+
import * as fs63 from "node:fs";
|
|
62819
63295
|
function updateFromArgs(args) {
|
|
62820
63296
|
const status2 = args.status || void 0;
|
|
62821
63297
|
const comment = args.comment || void 0;
|
|
@@ -62823,7 +63299,7 @@ function updateFromArgs(args) {
|
|
|
62823
63299
|
return "Nothing to update: pass --status <name> and/or --comment <text>.";
|
|
62824
63300
|
return { status: status2, comment };
|
|
62825
63301
|
}
|
|
62826
|
-
function newIssueFromArgs(args, read = (p) =>
|
|
63302
|
+
function newIssueFromArgs(args, read = (p) => fs63.readFileSync(p, "utf8")) {
|
|
62827
63303
|
const project = (args.project ?? "").trim().toUpperCase();
|
|
62828
63304
|
const type = args.type ?? "";
|
|
62829
63305
|
const title = (args.title ?? "").trim();
|
|
@@ -62863,6 +63339,14 @@ async function run26(args) {
|
|
|
62863
63339
|
}
|
|
62864
63340
|
return;
|
|
62865
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
|
+
}
|
|
62866
63350
|
const update = args.action === "update" ? updateFromArgs(args) : null;
|
|
62867
63351
|
if (typeof update === "string") fail3(update);
|
|
62868
63352
|
let text = args.ref ?? "";
|
|
@@ -62902,6 +63386,7 @@ var init_issue = __esm({
|
|
|
62902
63386
|
"use strict";
|
|
62903
63387
|
init_define_SANDBOX_BUILD();
|
|
62904
63388
|
init_gate();
|
|
63389
|
+
init_issue_status();
|
|
62905
63390
|
init_issue_tracker();
|
|
62906
63391
|
init_workspace_prompts();
|
|
62907
63392
|
init_config2();
|
|
@@ -62991,10 +63476,10 @@ var close_out_exports = {};
|
|
|
62991
63476
|
__export(close_out_exports, {
|
|
62992
63477
|
run: () => run29
|
|
62993
63478
|
});
|
|
62994
|
-
import
|
|
63479
|
+
import path70 from "node:path";
|
|
62995
63480
|
async function run29(args) {
|
|
62996
63481
|
const root = findRecordRoot(process.cwd());
|
|
62997
|
-
const name = args.name ?? (root ?
|
|
63482
|
+
const name = args.name ?? (root ? path70.basename(root) : null);
|
|
62998
63483
|
if (!name) {
|
|
62999
63484
|
error("No workspace given and not inside one.");
|
|
63000
63485
|
process.exit(1);
|
|
@@ -63046,9 +63531,9 @@ async function run30(args) {
|
|
|
63046
63531
|
error("Feature name required: pass it as an argument or set FEATURE in a .env file.");
|
|
63047
63532
|
process.exit(1);
|
|
63048
63533
|
}
|
|
63049
|
-
const { path:
|
|
63534
|
+
const { path: path74 } = await openWorkspace(name);
|
|
63050
63535
|
console.log(
|
|
63051
|
-
`${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}`
|
|
63052
63537
|
);
|
|
63053
63538
|
}
|
|
63054
63539
|
var init_code = __esm({
|
|
@@ -63066,8 +63551,8 @@ var setup_exports = {};
|
|
|
63066
63551
|
__export(setup_exports, {
|
|
63067
63552
|
run: () => run31
|
|
63068
63553
|
});
|
|
63069
|
-
import * as
|
|
63070
|
-
import * as
|
|
63554
|
+
import * as fs64 from "node:fs";
|
|
63555
|
+
import * as path71 from "node:path";
|
|
63071
63556
|
function heading(text) {
|
|
63072
63557
|
console.log(`
|
|
63073
63558
|
${Colors.BOLD}${text}${Colors.RESET}`);
|
|
@@ -63173,7 +63658,7 @@ async function configureMachine(interactive) {
|
|
|
63173
63658
|
interactive
|
|
63174
63659
|
);
|
|
63175
63660
|
}
|
|
63176
|
-
if (!
|
|
63661
|
+
if (!fs64.existsSync(servicesLocalYamlPath()) && interactive) {
|
|
63177
63662
|
k8sContext = await prompt(
|
|
63178
63663
|
"Kube context for `sandbox intercept` (services.local.yaml k8s_context)",
|
|
63179
63664
|
k8sContext
|
|
@@ -63335,9 +63820,9 @@ async function configureMachine(interactive) {
|
|
|
63335
63820
|
info(
|
|
63336
63821
|
`${zshrcPath()}: ${completionChanged ? "installed tab completion" : "tab completion already installed"}`
|
|
63337
63822
|
);
|
|
63338
|
-
if (
|
|
63823
|
+
if (fs64.existsSync(desktopConfigPath())) {
|
|
63339
63824
|
info(
|
|
63340
|
-
`${
|
|
63825
|
+
`${path71.basename(desktopConfigPath())}: ${desktopChanged ? "dropped stale env block" : "no env block to remove"}`
|
|
63341
63826
|
);
|
|
63342
63827
|
}
|
|
63343
63828
|
return {
|
|
@@ -63448,8 +63933,8 @@ var config_exports = {};
|
|
|
63448
63933
|
__export(config_exports, {
|
|
63449
63934
|
run: () => run32
|
|
63450
63935
|
});
|
|
63451
|
-
import * as
|
|
63452
|
-
import * as
|
|
63936
|
+
import * as fs65 from "node:fs";
|
|
63937
|
+
import * as path72 from "node:path";
|
|
63453
63938
|
function show(cfg) {
|
|
63454
63939
|
const paths = cfg.paths || {};
|
|
63455
63940
|
console.log(`${Colors.BOLD}config file:${Colors.RESET} ${configPath()}`);
|
|
@@ -63544,9 +64029,9 @@ async function run32(args) {
|
|
|
63544
64029
|
info(`Regenerated ${envSh}`);
|
|
63545
64030
|
info(`${zshenvPath()}: ${zshChanged ? "updated" : "already sources env.sh"}`);
|
|
63546
64031
|
const desktopPath = desktopConfigPath();
|
|
63547
|
-
if (
|
|
64032
|
+
if (fs65.existsSync(desktopPath)) {
|
|
63548
64033
|
info(
|
|
63549
|
-
`${
|
|
64034
|
+
`${path72.basename(desktopPath)}: ${desktopChanged ? "dropped stale env block" : "no env block to remove"}`
|
|
63550
64035
|
);
|
|
63551
64036
|
}
|
|
63552
64037
|
info("Open a new shell (or `source ~/.zshenv`) for the exports to take effect.");
|
|
@@ -63597,7 +64082,7 @@ var init_registry = __esm({
|
|
|
63597
64082
|
|
|
63598
64083
|
// src/main.ts
|
|
63599
64084
|
init_define_SANDBOX_BUILD();
|
|
63600
|
-
import
|
|
64085
|
+
import path73 from "node:path";
|
|
63601
64086
|
import { fileURLToPath } from "node:url";
|
|
63602
64087
|
|
|
63603
64088
|
// ../../node_modules/commander/esm.mjs
|
|
@@ -63631,7 +64116,7 @@ import { execFileSync } from "node:child_process";
|
|
|
63631
64116
|
import * as fs21 from "node:fs";
|
|
63632
64117
|
import * as path22 from "node:path";
|
|
63633
64118
|
var BUILD_STAMP = typeof define_SANDBOX_BUILD_default === "undefined" ? void 0 : define_SANDBOX_BUILD_default;
|
|
63634
|
-
var REBUILD_COMMAND = "npm run build --workspace
|
|
64119
|
+
var REBUILD_COMMAND = "npm run build --workspace werkmeister-core && npm run build --workspace apps/cli";
|
|
63635
64120
|
function staleBuildWarning(build, repoRoot) {
|
|
63636
64121
|
if (!build?.sha || !fs21.existsSync(path22.join(repoRoot, ".git"))) return null;
|
|
63637
64122
|
let head;
|
|
@@ -63651,7 +64136,7 @@ function staleBuildWarning(build, repoRoot) {
|
|
|
63651
64136
|
|
|
63652
64137
|
// src/main.ts
|
|
63653
64138
|
init_interrupt();
|
|
63654
|
-
var _REPO_ROOT =
|
|
64139
|
+
var _REPO_ROOT = path73.resolve(path73.dirname(fileURLToPath(import.meta.url)), "..");
|
|
63655
64140
|
if (!envVar("ROOT")) {
|
|
63656
64141
|
process.env.SANDBOX_ROOT = _REPO_ROOT;
|
|
63657
64142
|
}
|
|
@@ -63672,11 +64157,15 @@ var SPECS = [
|
|
|
63672
64157
|
).option(
|
|
63673
64158
|
"-w, --workspace <path>",
|
|
63674
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."
|
|
63675
64163
|
);
|
|
63676
64164
|
},
|
|
63677
64165
|
namespace: ([feature2], o) => ({
|
|
63678
64166
|
feature: feature2 ?? null,
|
|
63679
|
-
workspace: o.workspace ?? null
|
|
64167
|
+
workspace: o.workspace ?? null,
|
|
64168
|
+
dev: o.dev ?? false
|
|
63680
64169
|
})
|
|
63681
64170
|
},
|
|
63682
64171
|
{
|
|
@@ -63747,9 +64236,16 @@ var SPECS = [
|
|
|
63747
64236
|
).option(
|
|
63748
64237
|
"--if-idle",
|
|
63749
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"
|
|
63750
64242
|
);
|
|
63751
64243
|
},
|
|
63752
|
-
namespace: ([feature2], o) => ({
|
|
64244
|
+
namespace: ([feature2], o) => ({
|
|
64245
|
+
feature: feature2 ?? null,
|
|
64246
|
+
if_idle: Boolean(o.ifIdle),
|
|
64247
|
+
idle: o.idle ?? null
|
|
64248
|
+
})
|
|
63753
64249
|
},
|
|
63754
64250
|
{
|
|
63755
64251
|
name: "status",
|
|
@@ -64085,8 +64581,8 @@ var SPECS = [
|
|
|
64085
64581
|
).addArgument(
|
|
64086
64582
|
new Argument(
|
|
64087
64583
|
"<action>",
|
|
64088
|
-
"view: print the issue; update: change its status and/or add a comment; create: file a new issue and print its URL (Jira)"
|
|
64089
|
-
).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"])
|
|
64090
64586
|
).argument(
|
|
64091
64587
|
"[ref]",
|
|
64092
64588
|
"Issue ID; omit inside a workspace to use its .sandbox/workspace.yml issue"
|
|
@@ -64366,7 +64862,7 @@ ${_RED}Interrupted${_RESET}`));
|
|
|
64366
64862
|
for (const [name, description] of [
|
|
64367
64863
|
[
|
|
64368
64864
|
"dashboard",
|
|
64369
|
-
"Launch the workspace dashboard's MCP server over stdio (Claude Desktop, the
|
|
64865
|
+
"Launch the workspace dashboard's MCP server over stdio (Claude Desktop, the werkmeister plugin)"
|
|
64370
64866
|
],
|
|
64371
64867
|
[
|
|
64372
64868
|
"mcp",
|
|
@@ -64384,7 +64880,7 @@ ${_RED}Interrupted${_RESET}`));
|
|
|
64384
64880
|
return completeLine(program2);
|
|
64385
64881
|
}
|
|
64386
64882
|
if (process.argv[2] !== "hooks" && !process.argv.includes("--if-idle")) {
|
|
64387
|
-
const warning = staleBuildWarning(BUILD_STAMP,
|
|
64883
|
+
const warning = staleBuildWarning(BUILD_STAMP, path73.resolve(_REPO_ROOT, "../.."));
|
|
64388
64884
|
if (warning) console.error(warning);
|
|
64389
64885
|
}
|
|
64390
64886
|
if (process.argv.length <= 2) {
|