@brainervirus/workit-opencode 1.1.1 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -2
- package/dist/plugin.js +1869 -488
- package/package.json +4 -3
package/dist/plugin.js
CHANGED
|
@@ -13,10 +13,9 @@ var __export = (target, all) => {
|
|
|
13
13
|
});
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
// packages/workit-opencode/src/
|
|
17
|
-
import { existsSync as
|
|
18
|
-
import
|
|
19
|
-
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
16
|
+
// packages/workit-opencode/src/v1/server.ts
|
|
17
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
18
|
+
import path27 from "node:path";
|
|
20
19
|
|
|
21
20
|
// packages/workit-core/src/core.ts
|
|
22
21
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
@@ -5970,10 +5969,35 @@ var numberProcessor = (schema, ctx, _json, params) => {
|
|
|
5970
5969
|
var booleanProcessor = (_schema, _ctx, json, _params) => {
|
|
5971
5970
|
json.type = "boolean";
|
|
5972
5971
|
};
|
|
5972
|
+
var bigintProcessor = (schema, ctx, json, params) => {
|
|
5973
|
+
handleUnrepresentable(schema, ctx, json, params, "BigInt cannot be represented in JSON Schema");
|
|
5974
|
+
};
|
|
5975
|
+
var symbolProcessor = (schema, ctx, json, params) => {
|
|
5976
|
+
handleUnrepresentable(schema, ctx, json, params, "Symbols cannot be represented in JSON Schema");
|
|
5977
|
+
};
|
|
5978
|
+
var nullProcessor = (_schema, ctx, json, _params) => {
|
|
5979
|
+
if (ctx.target === "openapi-3.0") {
|
|
5980
|
+
json.type = "string";
|
|
5981
|
+
json.nullable = true;
|
|
5982
|
+
json.enum = [null];
|
|
5983
|
+
} else {
|
|
5984
|
+
json.type = "null";
|
|
5985
|
+
}
|
|
5986
|
+
};
|
|
5987
|
+
var undefinedProcessor = (schema, ctx, json, params) => {
|
|
5988
|
+
handleUnrepresentable(schema, ctx, json, params, "Undefined cannot be represented in JSON Schema");
|
|
5989
|
+
};
|
|
5990
|
+
var voidProcessor = (schema, ctx, json, params) => {
|
|
5991
|
+
handleUnrepresentable(schema, ctx, json, params, "Void cannot be represented in JSON Schema");
|
|
5992
|
+
};
|
|
5973
5993
|
var neverProcessor = (_schema, _ctx, json, _params) => {
|
|
5974
5994
|
json.not = {};
|
|
5975
5995
|
};
|
|
5996
|
+
var anyProcessor = (_schema, _ctx, _json, _params) => {};
|
|
5976
5997
|
var unknownProcessor = (_schema, _ctx, _json, _params) => {};
|
|
5998
|
+
var dateProcessor = (schema, ctx, json, params) => {
|
|
5999
|
+
handleUnrepresentable(schema, ctx, json, params, "Date cannot be represented in JSON Schema");
|
|
6000
|
+
};
|
|
5977
6001
|
var enumProcessor = (schema, _ctx, json, _params) => {
|
|
5978
6002
|
const def = schema._zod.def;
|
|
5979
6003
|
const values = getEnumValues(def.entries);
|
|
@@ -6026,12 +6050,59 @@ var literalProcessor = (schema, ctx, json, params) => {
|
|
|
6026
6050
|
json.enum = vals;
|
|
6027
6051
|
}
|
|
6028
6052
|
};
|
|
6053
|
+
var nanProcessor = (schema, ctx, json, params) => {
|
|
6054
|
+
handleUnrepresentable(schema, ctx, json, params, "NaN cannot be represented in JSON Schema");
|
|
6055
|
+
};
|
|
6056
|
+
var templateLiteralProcessor = (schema, _ctx, json, _params) => {
|
|
6057
|
+
const _json = json;
|
|
6058
|
+
const pattern = schema._zod.pattern;
|
|
6059
|
+
if (!pattern)
|
|
6060
|
+
throw new Error("Pattern not found in template literal");
|
|
6061
|
+
_json.type = "string";
|
|
6062
|
+
_json.pattern = pattern.source;
|
|
6063
|
+
};
|
|
6064
|
+
var fileProcessor = (schema, _ctx, json, _params) => {
|
|
6065
|
+
const _json = json;
|
|
6066
|
+
const file = {
|
|
6067
|
+
type: "string",
|
|
6068
|
+
format: "binary",
|
|
6069
|
+
contentEncoding: "binary"
|
|
6070
|
+
};
|
|
6071
|
+
const { minimum, maximum, mime } = schema._zod.bag;
|
|
6072
|
+
if (minimum !== undefined)
|
|
6073
|
+
file.minLength = minimum;
|
|
6074
|
+
if (maximum !== undefined)
|
|
6075
|
+
file.maxLength = maximum;
|
|
6076
|
+
if (mime) {
|
|
6077
|
+
if (mime.length === 1) {
|
|
6078
|
+
file.contentMediaType = mime[0];
|
|
6079
|
+
Object.assign(_json, file);
|
|
6080
|
+
} else {
|
|
6081
|
+
Object.assign(_json, file);
|
|
6082
|
+
_json.anyOf = mime.map((m) => ({ contentMediaType: m }));
|
|
6083
|
+
}
|
|
6084
|
+
} else {
|
|
6085
|
+
Object.assign(_json, file);
|
|
6086
|
+
}
|
|
6087
|
+
};
|
|
6088
|
+
var successProcessor = (_schema, _ctx, json, _params) => {
|
|
6089
|
+
json.type = "boolean";
|
|
6090
|
+
};
|
|
6029
6091
|
var customProcessor = (schema, ctx, json, params) => {
|
|
6030
6092
|
handleUnrepresentable(schema, ctx, json, params, "Custom types cannot be represented in JSON Schema");
|
|
6031
6093
|
};
|
|
6094
|
+
var functionProcessor = (schema, ctx, json, params) => {
|
|
6095
|
+
handleUnrepresentable(schema, ctx, json, params, "Function types cannot be represented in JSON Schema");
|
|
6096
|
+
};
|
|
6032
6097
|
var transformProcessor = (schema, ctx, json, params) => {
|
|
6033
6098
|
handleUnrepresentable(schema, ctx, json, params, "Transforms cannot be represented in JSON Schema");
|
|
6034
6099
|
};
|
|
6100
|
+
var mapProcessor = (schema, ctx, json, params) => {
|
|
6101
|
+
handleUnrepresentable(schema, ctx, json, params, "Map cannot be represented in JSON Schema");
|
|
6102
|
+
};
|
|
6103
|
+
var setProcessor = (schema, ctx, json, params) => {
|
|
6104
|
+
handleUnrepresentable(schema, ctx, json, params, "Set cannot be represented in JSON Schema");
|
|
6105
|
+
};
|
|
6035
6106
|
var arrayProcessor = (schema, ctx, _json, params) => {
|
|
6036
6107
|
const json = _json;
|
|
6037
6108
|
const def = schema._zod.def;
|
|
@@ -6127,6 +6198,70 @@ var intersectionProcessor = (schema, ctx, json, params) => {
|
|
|
6127
6198
|
json.allOf = allOf;
|
|
6128
6199
|
ctx.intersections.push(allOf);
|
|
6129
6200
|
};
|
|
6201
|
+
var tupleProcessor = (schema, ctx, _json, params) => {
|
|
6202
|
+
const json = _json;
|
|
6203
|
+
const def = schema._zod.def;
|
|
6204
|
+
json.type = "array";
|
|
6205
|
+
const prefixPath = ctx.target === "draft-2020-12" ? "prefixItems" : "items";
|
|
6206
|
+
const restPath = ctx.target === "draft-2020-12" ? "items" : ctx.target === "openapi-3.0" ? "items" : "additionalItems";
|
|
6207
|
+
const prefixItems = def.items.map((x, i) => process2(x, ctx, {
|
|
6208
|
+
...params,
|
|
6209
|
+
path: [...params.path, prefixPath, i]
|
|
6210
|
+
}));
|
|
6211
|
+
const rest = def.rest ? process2(def.rest, ctx, {
|
|
6212
|
+
...params,
|
|
6213
|
+
path: [...params.path, restPath, ...ctx.target === "openapi-3.0" ? [def.items.length] : []]
|
|
6214
|
+
}) : null;
|
|
6215
|
+
let minItems = def.items.length;
|
|
6216
|
+
while (minItems > 0) {
|
|
6217
|
+
const item = def.items[minItems - 1];
|
|
6218
|
+
const optional = ctx.io === "input" ? inputOptin(item) !== undefined : item._zod.optout === "optional";
|
|
6219
|
+
if (!optional)
|
|
6220
|
+
break;
|
|
6221
|
+
minItems--;
|
|
6222
|
+
}
|
|
6223
|
+
const maxItems = def.items.length;
|
|
6224
|
+
const isClosed = !def.rest;
|
|
6225
|
+
if (ctx.target === "draft-2020-12") {
|
|
6226
|
+
json.prefixItems = prefixItems;
|
|
6227
|
+
if (isClosed) {
|
|
6228
|
+
json.items = false;
|
|
6229
|
+
} else if (rest) {
|
|
6230
|
+
json.items = rest;
|
|
6231
|
+
}
|
|
6232
|
+
if (minItems > 0)
|
|
6233
|
+
json.minItems = minItems;
|
|
6234
|
+
if (isClosed)
|
|
6235
|
+
json.maxItems = maxItems;
|
|
6236
|
+
} else if (ctx.target === "openapi-3.0") {
|
|
6237
|
+
json.items = {
|
|
6238
|
+
anyOf: prefixItems
|
|
6239
|
+
};
|
|
6240
|
+
if (rest) {
|
|
6241
|
+
json.items.anyOf.push(rest);
|
|
6242
|
+
}
|
|
6243
|
+
if (minItems > 0)
|
|
6244
|
+
json.minItems = minItems;
|
|
6245
|
+
if (isClosed)
|
|
6246
|
+
json.maxItems = maxItems;
|
|
6247
|
+
} else {
|
|
6248
|
+
json.items = prefixItems;
|
|
6249
|
+
if (isClosed) {
|
|
6250
|
+
json.additionalItems = false;
|
|
6251
|
+
} else if (rest) {
|
|
6252
|
+
json.additionalItems = rest;
|
|
6253
|
+
}
|
|
6254
|
+
if (minItems > 0)
|
|
6255
|
+
json.minItems = minItems;
|
|
6256
|
+
if (isClosed)
|
|
6257
|
+
json.maxItems = maxItems;
|
|
6258
|
+
}
|
|
6259
|
+
const { minimum, maximum } = schema._zod.bag;
|
|
6260
|
+
if (typeof minimum === "number")
|
|
6261
|
+
json.minItems = minimum;
|
|
6262
|
+
if (typeof maximum === "number")
|
|
6263
|
+
json.maxItems = maximum;
|
|
6264
|
+
};
|
|
6130
6265
|
function stringifyKeyNames(bySchema, json, visited) {
|
|
6131
6266
|
if (json.$ref) {
|
|
6132
6267
|
if (visited.has(json))
|
|
@@ -6314,12 +6449,99 @@ var readonlyProcessor = (schema, ctx, json, params) => {
|
|
|
6314
6449
|
seen.ref = def.innerType;
|
|
6315
6450
|
json.readOnly = true;
|
|
6316
6451
|
};
|
|
6452
|
+
var promiseProcessor = (schema, ctx, _json, params) => {
|
|
6453
|
+
const def = schema._zod.def;
|
|
6454
|
+
process2(def.innerType, ctx, params);
|
|
6455
|
+
const seen = ctx.seen.get(schema);
|
|
6456
|
+
seen.ref = def.innerType;
|
|
6457
|
+
};
|
|
6317
6458
|
var optionalProcessor = (schema, ctx, _json, params) => {
|
|
6318
6459
|
const def = schema._zod.def;
|
|
6319
6460
|
process2(def.innerType, ctx, params);
|
|
6320
6461
|
const seen = ctx.seen.get(schema);
|
|
6321
6462
|
seen.ref = def.innerType;
|
|
6322
6463
|
};
|
|
6464
|
+
var lazyProcessor = (schema, ctx, _json, params) => {
|
|
6465
|
+
const innerType = schema._zod.innerType;
|
|
6466
|
+
process2(innerType, ctx, params);
|
|
6467
|
+
const seen = ctx.seen.get(schema);
|
|
6468
|
+
seen.ref = innerType;
|
|
6469
|
+
};
|
|
6470
|
+
var allProcessors = {
|
|
6471
|
+
string: stringProcessor,
|
|
6472
|
+
number: numberProcessor,
|
|
6473
|
+
boolean: booleanProcessor,
|
|
6474
|
+
bigint: bigintProcessor,
|
|
6475
|
+
symbol: symbolProcessor,
|
|
6476
|
+
null: nullProcessor,
|
|
6477
|
+
undefined: undefinedProcessor,
|
|
6478
|
+
void: voidProcessor,
|
|
6479
|
+
never: neverProcessor,
|
|
6480
|
+
any: anyProcessor,
|
|
6481
|
+
unknown: unknownProcessor,
|
|
6482
|
+
date: dateProcessor,
|
|
6483
|
+
enum: enumProcessor,
|
|
6484
|
+
literal: literalProcessor,
|
|
6485
|
+
nan: nanProcessor,
|
|
6486
|
+
template_literal: templateLiteralProcessor,
|
|
6487
|
+
file: fileProcessor,
|
|
6488
|
+
success: successProcessor,
|
|
6489
|
+
custom: customProcessor,
|
|
6490
|
+
function: functionProcessor,
|
|
6491
|
+
transform: transformProcessor,
|
|
6492
|
+
map: mapProcessor,
|
|
6493
|
+
set: setProcessor,
|
|
6494
|
+
array: arrayProcessor,
|
|
6495
|
+
object: objectProcessor,
|
|
6496
|
+
union: unionProcessor,
|
|
6497
|
+
intersection: intersectionProcessor,
|
|
6498
|
+
tuple: tupleProcessor,
|
|
6499
|
+
record: recordProcessor,
|
|
6500
|
+
nullable: nullableProcessor,
|
|
6501
|
+
nonoptional: nonoptionalProcessor,
|
|
6502
|
+
default: defaultProcessor,
|
|
6503
|
+
prefault: prefaultProcessor,
|
|
6504
|
+
catch: catchProcessor,
|
|
6505
|
+
pipe: pipeProcessor,
|
|
6506
|
+
readonly: readonlyProcessor,
|
|
6507
|
+
promise: promiseProcessor,
|
|
6508
|
+
optional: optionalProcessor,
|
|
6509
|
+
lazy: lazyProcessor
|
|
6510
|
+
};
|
|
6511
|
+
function toJSONSchema(input, params) {
|
|
6512
|
+
if ("_idmap" in input) {
|
|
6513
|
+
const registry = input;
|
|
6514
|
+
const ctx = initializeContext({ ...params, processors: allProcessors });
|
|
6515
|
+
const defs = {};
|
|
6516
|
+
for (const entry of registry._idmap.entries()) {
|
|
6517
|
+
const [_, schema] = entry;
|
|
6518
|
+
process2(schema, ctx);
|
|
6519
|
+
}
|
|
6520
|
+
const schemas = {};
|
|
6521
|
+
const external = {
|
|
6522
|
+
registry,
|
|
6523
|
+
uri: params?.uri,
|
|
6524
|
+
defs
|
|
6525
|
+
};
|
|
6526
|
+
ctx.external = external;
|
|
6527
|
+
for (const entry of registry._idmap.entries()) {
|
|
6528
|
+
const [key, schema] = entry;
|
|
6529
|
+
extractDefs(ctx, schema);
|
|
6530
|
+
assignProp(schemas, key, finalize(ctx, schema));
|
|
6531
|
+
}
|
|
6532
|
+
if (Object.keys(defs).length > 0) {
|
|
6533
|
+
const defsSegment = ctx.target === "draft-2020-12" ? "$defs" : "definitions";
|
|
6534
|
+
schemas.__shared = {
|
|
6535
|
+
[defsSegment]: defs
|
|
6536
|
+
};
|
|
6537
|
+
}
|
|
6538
|
+
return { schemas };
|
|
6539
|
+
}
|
|
6540
|
+
const ctx = initializeContext({ ...params, processors: allProcessors });
|
|
6541
|
+
process2(input, ctx);
|
|
6542
|
+
extractDefs(ctx, input);
|
|
6543
|
+
return finalize(ctx, input);
|
|
6544
|
+
}
|
|
6323
6545
|
// node_modules/zod/v4/classic/errors.js
|
|
6324
6546
|
var _installedErrorProtos = /* @__PURE__ */ new WeakSet([Object.prototype, Error.prototype]);
|
|
6325
6547
|
function _lazyMethod(proto, key, make) {
|
|
@@ -7743,6 +7965,7 @@ var workerSchema = object({
|
|
|
7743
7965
|
assignment: assignmentSchema,
|
|
7744
7966
|
state: _enum(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
|
|
7745
7967
|
session: refSchema.nullable(),
|
|
7968
|
+
coordinator: refSchema.optional(),
|
|
7746
7969
|
report: workerReportSchema.nullable()
|
|
7747
7970
|
}).strict();
|
|
7748
7971
|
var ownerSchema = object({
|
|
@@ -7854,6 +8077,7 @@ var taskSummarySchema = object({
|
|
|
7854
8077
|
requirements: array(requirementEvaluationSchema),
|
|
7855
8078
|
writer: workspaceRecordSchema.shape.writer
|
|
7856
8079
|
}).strict();
|
|
8080
|
+
var taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true });
|
|
7857
8081
|
var exportBundleSchema = object({
|
|
7858
8082
|
schemaVersion: literal(1),
|
|
7859
8083
|
exportedAt: utc,
|
|
@@ -7875,11 +8099,15 @@ var taskOperations = {
|
|
|
7875
8099
|
expectedWorkspaceRevision: revision.nullable().optional(),
|
|
7876
8100
|
intent: intentSchema
|
|
7877
8101
|
}),
|
|
7878
|
-
list: operation({
|
|
8102
|
+
list: operation({
|
|
8103
|
+
action: literal("list"),
|
|
8104
|
+
status: _enum(["open", "closed", "all"]).optional(),
|
|
8105
|
+
limit: number2().int().min(1).max(50).optional()
|
|
8106
|
+
}),
|
|
7879
8107
|
inspect: operation({
|
|
7880
8108
|
action: literal("inspect"),
|
|
7881
8109
|
...taskId,
|
|
7882
|
-
view: _enum(["summary", "full"])
|
|
8110
|
+
view: _enum(["summary", "full"]).optional()
|
|
7883
8111
|
}),
|
|
7884
8112
|
revise: operation({
|
|
7885
8113
|
action: literal("revise"),
|
|
@@ -7907,7 +8135,7 @@ var taskOperations = {
|
|
|
7907
8135
|
...taskId,
|
|
7908
8136
|
...revisions,
|
|
7909
8137
|
expectedWorkspaceRevision: revision.optional(),
|
|
7910
|
-
authorityRefs: array(refSchema)
|
|
8138
|
+
authorityRefs: array(refSchema).optional()
|
|
7911
8139
|
}),
|
|
7912
8140
|
close: operation({
|
|
7913
8141
|
action: literal("close"),
|
|
@@ -8070,8 +8298,62 @@ function parseOperation(family, input) {
|
|
|
8070
8298
|
}));
|
|
8071
8299
|
return failure("invalid_input", "operation input is invalid", { fields });
|
|
8072
8300
|
}
|
|
8301
|
+
function operationJsonSchema(family) {
|
|
8302
|
+
return toJSONSchema(operationSchemas[family], { target: "draft-2020-12" });
|
|
8303
|
+
}
|
|
8073
8304
|
var OPERATION_SCHEMA_DEPTH = 1;
|
|
8074
8305
|
var canonicalFieldsDescription = (fields) => `Canonical object fields: ${fields.join(", ")}. Workit validates the complete nested value.`;
|
|
8306
|
+
function boundedOperationJsonSchema(family, maxDepth = OPERATION_SCHEMA_DEPTH, stringifiedObjects = false) {
|
|
8307
|
+
const tolerate = (projected) => {
|
|
8308
|
+
if (!stringifiedObjects)
|
|
8309
|
+
return projected;
|
|
8310
|
+
if (projected.properties && typeof projected.properties === "object")
|
|
8311
|
+
return { ...projected, type: ["object", "string"] };
|
|
8312
|
+
if (projected.items)
|
|
8313
|
+
return { ...projected, type: ["array", "string"] };
|
|
8314
|
+
if ("const" in projected && (typeof projected.const === "number" || typeof projected.const === "boolean"))
|
|
8315
|
+
return { anyOf: [projected, { const: String(projected.const) }] };
|
|
8316
|
+
if (projected.type === "object")
|
|
8317
|
+
return { ...projected, type: ["object", "string"] };
|
|
8318
|
+
if (projected.type === "array")
|
|
8319
|
+
return { ...projected, type: ["array", "string"] };
|
|
8320
|
+
return projected;
|
|
8321
|
+
};
|
|
8322
|
+
const collapse = (node, currentDepth) => {
|
|
8323
|
+
if (Array.isArray(node))
|
|
8324
|
+
return node.map((item) => collapse(item, currentDepth));
|
|
8325
|
+
if (typeof node !== "object" || node === null)
|
|
8326
|
+
return node;
|
|
8327
|
+
const record = node;
|
|
8328
|
+
if (record.properties && typeof record.properties === "object") {
|
|
8329
|
+
const fields = Object.keys(record.properties);
|
|
8330
|
+
if (currentDepth >= maxDepth || fields.length === 0) {
|
|
8331
|
+
const description = stringifiedObjects ? `${canonicalFieldsDescription(fields)} A JSON-encoded string is also accepted.` : canonicalFieldsDescription(fields);
|
|
8332
|
+
return tolerate({ type: "object", description });
|
|
8333
|
+
}
|
|
8334
|
+
return tolerate({
|
|
8335
|
+
...record,
|
|
8336
|
+
properties: Object.fromEntries(Object.entries(record.properties).map(([key, child]) => [
|
|
8337
|
+
key,
|
|
8338
|
+
collapse(child, currentDepth + 1)
|
|
8339
|
+
]))
|
|
8340
|
+
});
|
|
8341
|
+
}
|
|
8342
|
+
if (record.items)
|
|
8343
|
+
return tolerate({ ...record, items: collapse(record.items, currentDepth) });
|
|
8344
|
+
const composed = ["anyOf", "oneOf", "allOf"].filter((key) => Array.isArray(record[key]));
|
|
8345
|
+
if (composed.length > 0) {
|
|
8346
|
+
const projected = { ...record };
|
|
8347
|
+
for (const key of composed)
|
|
8348
|
+
projected[key] = record[key].map((item) => collapse(item, currentDepth));
|
|
8349
|
+
return projected;
|
|
8350
|
+
}
|
|
8351
|
+
if ("const" in record)
|
|
8352
|
+
return tolerate({ ...record });
|
|
8353
|
+
return node;
|
|
8354
|
+
};
|
|
8355
|
+
return collapse(operationJsonSchema(family), 0);
|
|
8356
|
+
}
|
|
8075
8357
|
function canonical(value, seen) {
|
|
8076
8358
|
if (value === null || typeof value === "boolean" || typeof value === "string") {
|
|
8077
8359
|
if (typeof value === "string" && invalidUnicode(value))
|
|
@@ -9686,6 +9968,10 @@ var validPath = (value) => typeof value === "string" && value.length > 0 && ![..
|
|
|
9686
9968
|
}) && !value.split(/[\\/]/).includes("..");
|
|
9687
9969
|
var callerValue = (value) => ({ host: value.host, actor: value.actor });
|
|
9688
9970
|
var workerIdOf = (value) => value.workerId ?? null;
|
|
9971
|
+
var currentWriterOwnsTask = (task, workspace, caller) => {
|
|
9972
|
+
const owner = workspace.writer?.owner;
|
|
9973
|
+
return task.status === "active" && task.workspaceId === workspace.id && owner?.taskId === task.id && owner.session.host === caller.host && owner.session.handle === caller.actor;
|
|
9974
|
+
};
|
|
9689
9975
|
function assertProductWriteAllowed(input) {
|
|
9690
9976
|
if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
|
|
9691
9977
|
return failure("invalid_input", "invalid product write path");
|
|
@@ -10868,9 +11154,11 @@ var standingReceiptFor = (root, workspace, cls, dir) => {
|
|
|
10868
11154
|
var verifyStandingApproval = (root, task, caller, binding) => {
|
|
10869
11155
|
if (task.status !== "active")
|
|
10870
11156
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
10871
|
-
const
|
|
10872
|
-
if (!
|
|
10873
|
-
return
|
|
11157
|
+
const workspace = new TaskStore(root).readWorkspace();
|
|
11158
|
+
if (!workspace.ok)
|
|
11159
|
+
return workspace;
|
|
11160
|
+
if (!workspace.data || !currentWriterOwnsTask(task, workspace.data, caller))
|
|
11161
|
+
return failure("permission_denied", "standing approval requires the current writer session");
|
|
10874
11162
|
const standing = binding.standing;
|
|
10875
11163
|
if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
|
|
10876
11164
|
return failure("invalid_input", "standing approval needs a workspace and class");
|
|
@@ -10918,14 +11206,14 @@ var standingAutoApplies = (store, host, actor, operation) => {
|
|
|
10918
11206
|
const workspace = store.readWorkspace();
|
|
10919
11207
|
if (!listed.ok || !workspace.ok || !workspace.data)
|
|
10920
11208
|
return null;
|
|
10921
|
-
const
|
|
10922
|
-
if (
|
|
11209
|
+
const task = listed.data.find((task) => currentWriterOwnsTask(task, workspace.data, { host, actor }));
|
|
11210
|
+
if (!task)
|
|
10923
11211
|
return null;
|
|
10924
11212
|
const resolved = resolveAutoApproval(store.root);
|
|
10925
11213
|
if (resolved.status !== "on")
|
|
10926
11214
|
return null;
|
|
10927
11215
|
return {
|
|
10928
|
-
task
|
|
11216
|
+
task,
|
|
10929
11217
|
workspaceId: workspace.data.id,
|
|
10930
11218
|
workspaceRevision: workspace.data.revision,
|
|
10931
11219
|
cls,
|
|
@@ -11184,6 +11472,20 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
11184
11472
|
};
|
|
11185
11473
|
var verifyContentRefs = (store, binding) => verifyDecisionContentAtRoot(store.root, binding);
|
|
11186
11474
|
var storedDecisionApplicable = (store, task, purpose, binding) => task.decisions.filter((entry) => entry.provenance.kind !== "imported" && decisionMatches(entry, purpose, binding) && verifyDecisionContentAtRoot(store.root, entry.data.binding).ok);
|
|
11475
|
+
var approvalAuthorityMatches = (store, task, approval, current) => {
|
|
11476
|
+
if (approval.kind !== "host_observed" || approval.receipts.length === 0)
|
|
11477
|
+
return false;
|
|
11478
|
+
if (provenanceMatches(approval, current))
|
|
11479
|
+
return true;
|
|
11480
|
+
const session = current.kind === "host_observed" ? current.session : null;
|
|
11481
|
+
if (!session || session.kind !== "host")
|
|
11482
|
+
return false;
|
|
11483
|
+
const workspace = store.readWorkspace();
|
|
11484
|
+
return workspace.ok && workspace.data !== null && currentWriterOwnsTask(task, workspace.data, {
|
|
11485
|
+
host: session.host,
|
|
11486
|
+
actor: session.handle
|
|
11487
|
+
});
|
|
11488
|
+
};
|
|
11187
11489
|
var validateAction = (store, task, workspaceId, input, authority) => {
|
|
11188
11490
|
if (task.status !== "active")
|
|
11189
11491
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
@@ -11209,7 +11511,7 @@ var validateAction = (store, task, workspaceId, input, authority) => {
|
|
|
11209
11511
|
return failure("permission_denied", "native reservation authority does not match decision");
|
|
11210
11512
|
if (decision.response !== "approved")
|
|
11211
11513
|
return failure("permission_denied", "decision was rejected");
|
|
11212
|
-
if (
|
|
11514
|
+
if (!approvalAuthorityMatches(store, task, entry.provenance, authority.provenance))
|
|
11213
11515
|
return failure("permission_denied", "action approval lacks native receipt assurance");
|
|
11214
11516
|
const receipts = entry.provenance.receipts;
|
|
11215
11517
|
const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
|
|
@@ -11376,7 +11678,7 @@ function settleAction(input) {
|
|
|
11376
11678
|
return failure("not_found", "decision not found");
|
|
11377
11679
|
if (!authorityDecisionMatches(authority, entry.data))
|
|
11378
11680
|
return failure("permission_denied", "native settlement authority does not match decision");
|
|
11379
|
-
if (!
|
|
11681
|
+
if (!approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
11380
11682
|
return failure("permission_denied", "native settlement caller does not match approval");
|
|
11381
11683
|
if (entry.data.purpose !== "action" || entry.data.digest !== decisionDigest(entry.data))
|
|
11382
11684
|
return failure("permission_denied", "decision binding is invalid");
|
|
@@ -11475,7 +11777,7 @@ function reconcileAction(input) {
|
|
|
11475
11777
|
const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
11476
11778
|
if (!entry || entry.data.purpose !== "action")
|
|
11477
11779
|
return failure("not_found", "decision not found");
|
|
11478
|
-
if (!authorityDecisionMatches(authority, entry.data) || !
|
|
11780
|
+
if (!authorityDecisionMatches(authority, entry.data) || !approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
11479
11781
|
return failure("permission_denied", "native reconciliation caller does not match approval");
|
|
11480
11782
|
if (entry.data.response !== "approved" || entry.data.revoked !== null || entry.data.digest !== decisionDigest(entry.data) || entry.data.binding.taskId !== current.id || entry.data.binding.workspaceId !== current.workspaceId || entry.data.binding.workspaceId !== workspace.data.id)
|
|
11481
11783
|
return failure("permission_denied", "reconciliation decision binding is invalid");
|
|
@@ -11812,7 +12114,7 @@ var resolveBinding = (evidence, pin, currentCandidateId) => {
|
|
|
11812
12114
|
candidateId: evidence.candidateId ?? fallback
|
|
11813
12115
|
};
|
|
11814
12116
|
};
|
|
11815
|
-
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId
|
|
12117
|
+
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId != null);
|
|
11816
12118
|
var applicableDecision2 = (task, workspace, requirement, checkoutRoot) => task.decisions.filter((entry) => entry.provenance.kind !== "imported").map((entry) => entry.data).filter((decision) => decision.purpose === "limitation" && decision.response === "approved" && decision.revoked === null && decision.binding.taskId === task.id && decision.binding.workspaceId === workspace.id && decision.requirementIds.includes(requirement.id) && decision.binding.scope && scopeCovers2(decision.binding.scope, requirement.scope) && decision.digest === decisionDigest(decision) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, decision.binding).ok : decision.binding.contentRefs.every((reference) => reference.kind !== "file")));
|
|
11817
12119
|
var applicableRequirementDecision = (task, workspace, requirement, checkoutRoot) => task.decisions.filter(({ data, provenance }) => provenance.kind !== "imported" && data.purpose !== "limitation" && (data.response === "approved" || data.response === "stated") && data.revoked === null && data.binding.taskId === task.id && data.binding.workspaceId === workspace.id && data.requirementIds.includes(requirement.id) && scopeCovers2(data.binding.scope, requirement.scope) && data.digest === decisionDigest(data) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, data.binding).ok : data.binding.contentRefs.every((reference) => reference.kind !== "file"))).map(({ id }) => ({ id }));
|
|
11818
12120
|
var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
|
|
@@ -12282,6 +12584,11 @@ function shouldDenyShellRoute(dir, command) {
|
|
|
12282
12584
|
}
|
|
12283
12585
|
// packages/workit-core/src/core/external-action.ts
|
|
12284
12586
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
12587
|
+
var chainStepSchema = union([
|
|
12588
|
+
string2().min(1),
|
|
12589
|
+
object({ branch: string2().min(1) }).strict(),
|
|
12590
|
+
object({ pr: literal(true) }).strict()
|
|
12591
|
+
]);
|
|
12285
12592
|
var externalActionSchema = discriminatedUnion("operation", [
|
|
12286
12593
|
object({
|
|
12287
12594
|
operation: literal("git.branch_setup"),
|
|
@@ -12296,7 +12603,7 @@ var externalActionSchema = discriminatedUnion("operation", [
|
|
|
12296
12603
|
operation: literal("git.commit"),
|
|
12297
12604
|
payload: object({
|
|
12298
12605
|
message: string2().min(1).optional(),
|
|
12299
|
-
plan_steps: array(
|
|
12606
|
+
plan_steps: array(chainStepSchema).min(1).max(32).optional(),
|
|
12300
12607
|
plan_branch: string2().min(1).optional()
|
|
12301
12608
|
}).strict()
|
|
12302
12609
|
}).strict(),
|
|
@@ -12394,7 +12701,13 @@ var externalActionDescriptor = (operation, payload) => canonicalJson({
|
|
|
12394
12701
|
payload,
|
|
12395
12702
|
...payload && typeof payload === "object" && !Array.isArray(payload) && "resolved" in payload && payload.resolved && typeof payload.resolved === "object" && Array.isArray(payload.resolved.steps) ? { steps: payload.resolved.steps } : {}
|
|
12396
12703
|
});
|
|
12397
|
-
var
|
|
12704
|
+
var planReservationLength = (operation, descriptorPayload) => {
|
|
12705
|
+
if (operation !== "git.commit")
|
|
12706
|
+
return null;
|
|
12707
|
+
const payload = descriptorPayload ?? {};
|
|
12708
|
+
return Array.isArray(payload.plan_steps) ? payload.plan_steps.length : null;
|
|
12709
|
+
};
|
|
12710
|
+
var externalActionHelp = "Fixed actions: git.branch_setup {action?,sdd_dir?,target_branch(required unless reapply_stash; the working branch to create or switch to),stash?}; git.commit {message?; plan_steps?: (string | {branch:string} | {pr:true})[]; plan_branch?}; git.push {branch?}; hosting.pull_request {title,body?,draft?,target_branch?,babysit?}; hosting.merge {target_branch?,source_branch?}; youtrack.update {issueId,markdown,minutes?}; youtrack.time {issueId,minutes,text?,dateMs?}; youtrack.meeting {issueId,minutes,text}; changelog.apply {entries?,path?,normalize_only?}; context.read {kind: git|pr|youtrack|github_issue|gitlab_issue|changelog|release|affected,range?,issueId?,issueUrl?,issueRef?,mode?,specPath?,planPath?}. context.read is read-only and needs no approval; all other operations require native approval (CLI uses a TTY; caller-unattested MCP cannot mutate).";
|
|
12398
12711
|
var externalActionRef = (host, actor, descriptor) => ({
|
|
12399
12712
|
kind: "host",
|
|
12400
12713
|
host,
|
|
@@ -12408,12 +12721,12 @@ var externalActionState = (store, host, actor, operation) => {
|
|
|
12408
12721
|
if (!workspace.data)
|
|
12409
12722
|
return failure("not_found", "workspace not found");
|
|
12410
12723
|
const candidates = listed.data.flatMap((task) => {
|
|
12411
|
-
if (task.status !== "active" || task.workspaceId !== workspace.data.id
|
|
12724
|
+
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
12412
12725
|
return [];
|
|
12413
12726
|
return task.decisions.filter((entry) => entry.data.purpose === "action" && entry.data.response === "approved" && entry.data.revoked === null && entry.data.binding.approvedContent === operation).map((entry) => ({ task, entry }));
|
|
12414
12727
|
});
|
|
12415
12728
|
if (candidates.length !== 1)
|
|
12416
|
-
return failure("permission_denied", candidates.length === 0 ? "no approved action is bound to
|
|
12729
|
+
return failure("permission_denied", candidates.length === 0 ? "no approved action is bound to the active task" : "multiple approved actions match the active task");
|
|
12417
12730
|
return success(workspace.data.revision, null, { ...candidates[0], workspace: workspace.data });
|
|
12418
12731
|
};
|
|
12419
12732
|
var priorExternalAction = (store, host, actor, operation, payload) => {
|
|
@@ -12493,8 +12806,6 @@ var approvedPlanCommit = (store, host, actor, message) => {
|
|
|
12493
12806
|
const matches = listed.data.flatMap((task) => {
|
|
12494
12807
|
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
12495
12808
|
return [];
|
|
12496
|
-
if (task.intent.provenance.session?.kind !== "host" || task.intent.provenance.session.host !== host || task.intent.provenance.session.handle !== actor)
|
|
12497
|
-
return [];
|
|
12498
12809
|
return task.decisions.flatMap((entry) => {
|
|
12499
12810
|
const decision = entry.data;
|
|
12500
12811
|
if (decision.purpose !== "action" || decision.response !== "approved" || decision.revoked !== null)
|
|
@@ -12536,8 +12847,6 @@ var approvedChainStep = (store, host, actor, query) => {
|
|
|
12536
12847
|
const matches = listed.data.flatMap((task) => {
|
|
12537
12848
|
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
12538
12849
|
return [];
|
|
12539
|
-
if (task.intent.provenance.session?.kind !== "host" || task.intent.provenance.session.host !== host || task.intent.provenance.session.handle !== actor)
|
|
12540
|
-
return [];
|
|
12541
12850
|
return task.decisions.flatMap((entry) => {
|
|
12542
12851
|
const decision = entry.data;
|
|
12543
12852
|
if (decision.purpose !== "action" || decision.response !== "approved" || decision.revoked !== null)
|
|
@@ -12694,7 +13003,7 @@ var branchSetupIntent = (descriptor) => {
|
|
|
12694
13003
|
var approvedBranchSetupIntent = (store, host, actor, operation) => {
|
|
12695
13004
|
const current = branchSetupIntent(operation);
|
|
12696
13005
|
if (!current)
|
|
12697
|
-
return failure("permission_denied", "no approved action is bound to
|
|
13006
|
+
return failure("permission_denied", "no approved action is bound to the active task");
|
|
12698
13007
|
const listed = store.listTasks();
|
|
12699
13008
|
const workspace = store.readWorkspace();
|
|
12700
13009
|
if (!listed.ok || !workspace.ok)
|
|
@@ -12702,7 +13011,7 @@ var approvedBranchSetupIntent = (store, host, actor, operation) => {
|
|
|
12702
13011
|
if (!workspace.data)
|
|
12703
13012
|
return failure("not_found", "workspace not found");
|
|
12704
13013
|
const candidates = listed.data.flatMap((task) => {
|
|
12705
|
-
if (task.status !== "active" || task.workspaceId !== workspace.data.id
|
|
13014
|
+
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
12706
13015
|
return [];
|
|
12707
13016
|
return task.decisions.filter((entry) => entry.data.purpose === "action" && entry.data.response === "approved" && entry.data.revoked === null).map((entry) => ({ task, entry }));
|
|
12708
13017
|
});
|
|
@@ -12711,7 +13020,7 @@ var approvedBranchSetupIntent = (store, host, actor, operation) => {
|
|
|
12711
13020
|
return intent !== null && intent.target === current.target && intent.base === current.base && intent.remoteBase === current.remoteBase && intent.targetExists === current.targetExists;
|
|
12712
13021
|
});
|
|
12713
13022
|
if (matches.length !== 1)
|
|
12714
|
-
return failure("permission_denied", matches.length === 0 ? "no approved action is bound to
|
|
13023
|
+
return failure("permission_denied", matches.length === 0 ? "no approved action is bound to the active task" : "multiple approved actions match the active task");
|
|
12715
13024
|
return success(workspace.data.revision, null, { ...matches[0], workspace: workspace.data });
|
|
12716
13025
|
};
|
|
12717
13026
|
var priorResolvedDrift = (store, host, actor, operation, payload, currentResolved) => {
|
|
@@ -13268,6 +13577,7 @@ var verifyNativeWorker = (verifier, input, binding) => {
|
|
|
13268
13577
|
expectedWorkspaceRevision: expected.expectedWorkspaceRevision,
|
|
13269
13578
|
state: expected.state,
|
|
13270
13579
|
session: expected.session,
|
|
13580
|
+
report: expected.report ?? null,
|
|
13271
13581
|
provenance: result.data,
|
|
13272
13582
|
...binding
|
|
13273
13583
|
});
|
|
@@ -13278,7 +13588,7 @@ var takeWorker = (token, binding, expected) => {
|
|
|
13278
13588
|
verifiedWorkers.delete(token);
|
|
13279
13589
|
if (!value)
|
|
13280
13590
|
return null;
|
|
13281
|
-
if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session))
|
|
13591
|
+
if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session) || !sameValue(value.report, expected.report ?? null))
|
|
13282
13592
|
return null;
|
|
13283
13593
|
return value;
|
|
13284
13594
|
};
|
|
@@ -13319,6 +13629,8 @@ var applyWorkerLifecycle = (input) => {
|
|
|
13319
13629
|
return failure("invalid_transition", "worker already has an observed session");
|
|
13320
13630
|
if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
|
|
13321
13631
|
return failure("invalid_transition", "worker launch was not claimed");
|
|
13632
|
+
if (input.report && input.state !== "stopped")
|
|
13633
|
+
return failure("invalid_input", "worker reports require a stopped worker");
|
|
13322
13634
|
if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
|
|
13323
13635
|
return failure("invalid_input", "running worker observations require a session");
|
|
13324
13636
|
if (entry.data.session && !sameValue(entry.data.session, input.session))
|
|
@@ -13335,12 +13647,13 @@ var applyWorkerLifecycle = (input) => {
|
|
|
13335
13647
|
data: {
|
|
13336
13648
|
...entry.data,
|
|
13337
13649
|
state: terminalCancel ? "cancelling" : input.state,
|
|
13338
|
-
session: input.session
|
|
13650
|
+
session: input.session,
|
|
13651
|
+
report: entry.data.report ?? input.report ?? null
|
|
13339
13652
|
}
|
|
13340
13653
|
};
|
|
13341
13654
|
const shouldClear = input.state === "stopped" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
13342
13655
|
const shouldUncertain = input.state === "unknown" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
13343
|
-
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
|
|
13656
|
+
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && sameValue(entry.data.report, nextEntry.data.report) && !shouldClear && !shouldUncertain)
|
|
13344
13657
|
return success(task.data.revision, workspace.data.revision, entry);
|
|
13345
13658
|
const changed = input.store.mutateTaskAndWorkspace({
|
|
13346
13659
|
taskId: task.data.id,
|
|
@@ -13758,14 +14071,30 @@ class WorkitCore {
|
|
|
13758
14071
|
const listed = this.store.listTasks();
|
|
13759
14072
|
if (!listed.ok)
|
|
13760
14073
|
return listed;
|
|
13761
|
-
const
|
|
13762
|
-
|
|
13763
|
-
|
|
13764
|
-
|
|
13765
|
-
|
|
13766
|
-
|
|
13767
|
-
|
|
13768
|
-
|
|
14074
|
+
const status = input.status ?? "open";
|
|
14075
|
+
const limit = input.limit ?? 20;
|
|
14076
|
+
const selected = listed.data.filter((task) => status === "all" ? true : status === "closed" ? task.status === "closed" : task.status !== "closed").sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, limit);
|
|
14077
|
+
if (selected.length === 0)
|
|
14078
|
+
return success(null, null, []);
|
|
14079
|
+
const workspace = this.store.readWorkspace();
|
|
14080
|
+
if (!workspace.ok)
|
|
14081
|
+
return workspace;
|
|
14082
|
+
if (!workspace.data)
|
|
14083
|
+
return failure("not_found", "workspace not found");
|
|
14084
|
+
const tasks = selected.map((task) => ({
|
|
14085
|
+
id: task.id,
|
|
14086
|
+
revision: task.revision,
|
|
14087
|
+
workspaceRevision: workspace.data.revision,
|
|
14088
|
+
createdAt: task.createdAt,
|
|
14089
|
+
updatedAt: task.updatedAt,
|
|
14090
|
+
runtime: task.runtime ?? null,
|
|
14091
|
+
objective: task.intent.data.objective,
|
|
14092
|
+
status: task.status,
|
|
14093
|
+
closure: task.closure,
|
|
14094
|
+
progress: task.progress,
|
|
14095
|
+
writer: task.status === "closed" ? null : workspace.data.writer
|
|
14096
|
+
}));
|
|
14097
|
+
return success(null, null, tasks);
|
|
13769
14098
|
}
|
|
13770
14099
|
case "inspect": {
|
|
13771
14100
|
const task = this.store.readTask(input.taskId);
|
|
@@ -13774,7 +14103,7 @@ class WorkitCore {
|
|
|
13774
14103
|
const helper = this.helperTaskGuard(task.data, true);
|
|
13775
14104
|
if (!helper.ok)
|
|
13776
14105
|
return helper;
|
|
13777
|
-
if (input.view === "summary")
|
|
14106
|
+
if ((input.view ?? "summary") === "summary")
|
|
13778
14107
|
return this.summary(task.data);
|
|
13779
14108
|
return this.view(task.data);
|
|
13780
14109
|
}
|
|
@@ -14118,7 +14447,7 @@ class WorkitCore {
|
|
|
14118
14447
|
const evaluations = evaluateEvidence(task.data, current.data);
|
|
14119
14448
|
const verified = evidence.some((entry) => {
|
|
14120
14449
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
14121
|
-
return findingVerificationPasses(finding.data.candidateId, {
|
|
14450
|
+
return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
|
|
14122
14451
|
kind: entry.data.kind,
|
|
14123
14452
|
candidateId: entry.data.candidateId,
|
|
14124
14453
|
status: evaluation?.status ?? "missing"
|
|
@@ -14198,7 +14527,18 @@ class WorkitCore {
|
|
|
14198
14527
|
if (task.data.status !== "active")
|
|
14199
14528
|
return failure("invalid_transition", "paused or closed tasks cannot assign workers");
|
|
14200
14529
|
if (!bindingCovers(task.data.intent.data.scope, input.assignment.scope))
|
|
14201
|
-
return failure("permission_denied", "worker assignment is outside task scope"
|
|
14530
|
+
return failure("permission_denied", "worker assignment is outside task scope", {
|
|
14531
|
+
fields: [
|
|
14532
|
+
{
|
|
14533
|
+
path: "assignment.scope",
|
|
14534
|
+
reason: `paths ${JSON.stringify(input.assignment.scope.paths)} must be contained by lead paths ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
14535
|
+
},
|
|
14536
|
+
{
|
|
14537
|
+
path: "task.intent.scope",
|
|
14538
|
+
reason: `lead paths are ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
14539
|
+
}
|
|
14540
|
+
]
|
|
14541
|
+
});
|
|
14202
14542
|
if (input.assignment.decisionIds.some((id) => !task.data.decisions.some((entry) => entry.id === id)))
|
|
14203
14543
|
return failure("invalid_input", "worker assignment references an unknown decision");
|
|
14204
14544
|
if (input.assignment.requirementIds.some((id) => !task.data.policy?.requirements.some((requirement) => requirement.id === id)))
|
|
@@ -14405,7 +14745,15 @@ class WorkitCore {
|
|
|
14405
14745
|
...item,
|
|
14406
14746
|
recordedAt: mutation.now,
|
|
14407
14747
|
provenance: attested.data,
|
|
14408
|
-
data: {
|
|
14748
|
+
data: {
|
|
14749
|
+
...item.data,
|
|
14750
|
+
state: "dispatching",
|
|
14751
|
+
coordinator: {
|
|
14752
|
+
kind: "host",
|
|
14753
|
+
host: this.context.caller.host,
|
|
14754
|
+
handle: this.context.caller.actor
|
|
14755
|
+
}
|
|
14756
|
+
}
|
|
14409
14757
|
} : item)
|
|
14410
14758
|
});
|
|
14411
14759
|
}
|
|
@@ -14481,6 +14829,7 @@ class WorkitCore {
|
|
|
14481
14829
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
14482
14830
|
state: "stopped",
|
|
14483
14831
|
session: null,
|
|
14832
|
+
report: null,
|
|
14484
14833
|
provenance: attested.data,
|
|
14485
14834
|
owner: this.authorityOwner,
|
|
14486
14835
|
store: this.store,
|
|
@@ -14788,10 +15137,12 @@ class WorkitCore {
|
|
|
14788
15137
|
return workspace;
|
|
14789
15138
|
if (!workspace.data)
|
|
14790
15139
|
return failure("not_found", "workspace not found");
|
|
14791
|
-
const
|
|
15140
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
15141
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
14792
15142
|
if (!current.ok)
|
|
14793
15143
|
return current;
|
|
14794
|
-
const
|
|
15144
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
15145
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
14795
15146
|
return success(task.revision, workspace.data.revision, {
|
|
14796
15147
|
id: task.id,
|
|
14797
15148
|
revision: task.revision,
|
|
@@ -14805,7 +15156,7 @@ class WorkitCore {
|
|
|
14805
15156
|
progress: task.progress,
|
|
14806
15157
|
policy: task.policy,
|
|
14807
15158
|
requirements,
|
|
14808
|
-
writer:
|
|
15159
|
+
writer: evaluationWorkspace.writer
|
|
14809
15160
|
});
|
|
14810
15161
|
}
|
|
14811
15162
|
view(task) {
|
|
@@ -14814,13 +15165,15 @@ class WorkitCore {
|
|
|
14814
15165
|
return workspace;
|
|
14815
15166
|
if (!workspace.data)
|
|
14816
15167
|
return failure("not_found", "workspace not found");
|
|
14817
|
-
const
|
|
15168
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
15169
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
14818
15170
|
if (!current.ok)
|
|
14819
15171
|
return current;
|
|
14820
|
-
const
|
|
15172
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
15173
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
14821
15174
|
return success(task.revision, workspace.data.revision, {
|
|
14822
15175
|
task,
|
|
14823
|
-
workspace:
|
|
15176
|
+
workspace: evaluationWorkspace,
|
|
14824
15177
|
capabilities: this.context.capabilities,
|
|
14825
15178
|
requirements,
|
|
14826
15179
|
evidence: evaluateEvidence(task, current.data)
|
|
@@ -14911,8 +15264,6 @@ class WorkitCore {
|
|
|
14911
15264
|
return helper;
|
|
14912
15265
|
if (status === "paused" && task.data.status !== "active" || status === "active" && task.data.status !== "paused")
|
|
14913
15266
|
return failure("invalid_transition", `cannot transition ${task.data.status} to ${status}`);
|
|
14914
|
-
if (status === "active" && input.authorityRefs.length === 0)
|
|
14915
|
-
return failure("permission_denied", "resuming a task requires authority references");
|
|
14916
15267
|
const workspace = this.store.readWorkspace();
|
|
14917
15268
|
if (!workspace.ok)
|
|
14918
15269
|
return workspace;
|
|
@@ -14942,7 +15293,7 @@ class WorkitCore {
|
|
|
14942
15293
|
return failure("needs_input", "imported task requires fresh destination evidence");
|
|
14943
15294
|
if (current.data.blockers.some((blocker) => blocker.dependentAction === "resume" || blocker.reason === "worker state requires reconciliation"))
|
|
14944
15295
|
return failure("recovery_required", "imported task requires resume reconciliation");
|
|
14945
|
-
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs, this.context))
|
|
15296
|
+
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs ?? [], this.context))
|
|
14946
15297
|
return failure("permission_denied", "imported resume requires native destination approval");
|
|
14947
15298
|
resumeCandidate = current.data.candidate;
|
|
14948
15299
|
}
|
|
@@ -15072,7 +15423,7 @@ class WorkitCore {
|
|
|
15072
15423
|
task: (value, context) => success(context.revision, null, {
|
|
15073
15424
|
...value,
|
|
15074
15425
|
status: "closed",
|
|
15075
|
-
candidates:
|
|
15426
|
+
candidates: taskForView.candidates,
|
|
15076
15427
|
closure: {
|
|
15077
15428
|
outcome: closure.data.outcome,
|
|
15078
15429
|
at: context.now,
|
|
@@ -15151,6 +15502,22 @@ function run2(root, executable, args, env = {}) {
|
|
|
15151
15502
|
}
|
|
15152
15503
|
|
|
15153
15504
|
// packages/workit-core/src/core/skill-manifests.ts
|
|
15505
|
+
var WORKIT_METHOD_SKILLS = [
|
|
15506
|
+
"workit-challenge",
|
|
15507
|
+
"workit-behavioral-tdd",
|
|
15508
|
+
"workit-review",
|
|
15509
|
+
"workit-plan",
|
|
15510
|
+
"workit-implement",
|
|
15511
|
+
"workit-debug",
|
|
15512
|
+
"workit-handoff",
|
|
15513
|
+
"workit-babysit",
|
|
15514
|
+
"workit-blast-radius",
|
|
15515
|
+
"workit-deslop",
|
|
15516
|
+
"workit-diagram",
|
|
15517
|
+
"workit-mockup",
|
|
15518
|
+
"workit-green-run",
|
|
15519
|
+
"workit-steer"
|
|
15520
|
+
];
|
|
15154
15521
|
var WORKIT_SKILL_ALIASES = {
|
|
15155
15522
|
"wk-challenge": "workit-challenge",
|
|
15156
15523
|
"wk-babysit": "workit-babysit",
|
|
@@ -15407,27 +15774,54 @@ OpenCode Workit decisions use the native question header "Workit decision: <purp
|
|
|
15407
15774
|
return cached2;
|
|
15408
15775
|
};
|
|
15409
15776
|
|
|
15410
|
-
// packages/workit-opencode/src/
|
|
15411
|
-
import { existsSync as existsSync7 } from "node:fs";
|
|
15777
|
+
// packages/workit-opencode/src/shared/assets.ts
|
|
15778
|
+
import { existsSync as existsSync7, realpathSync as realpathSync7 } from "node:fs";
|
|
15779
|
+
import path13 from "node:path";
|
|
15412
15780
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
15781
|
+
var moduleDir = () => {
|
|
15782
|
+
try {
|
|
15783
|
+
return realpathSync7(path13.dirname(fileURLToPath2(import.meta.url)));
|
|
15784
|
+
} catch {
|
|
15785
|
+
return path13.dirname(fileURLToPath2(import.meta.url));
|
|
15786
|
+
}
|
|
15787
|
+
};
|
|
15788
|
+
var packageRoot2 = () => {
|
|
15789
|
+
const dir = moduleDir();
|
|
15790
|
+
for (const candidate of [
|
|
15791
|
+
path13.resolve(dir, ".."),
|
|
15792
|
+
path13.resolve(dir, "..", ".."),
|
|
15793
|
+
path13.resolve(dir, "..", "..", "..")
|
|
15794
|
+
]) {
|
|
15795
|
+
if (existsSync7(path13.join(candidate, "package.json")))
|
|
15796
|
+
return candidate;
|
|
15797
|
+
}
|
|
15798
|
+
return path13.resolve(dir, "..");
|
|
15799
|
+
};
|
|
15800
|
+
var assetsRoot = () => path13.join(packageRoot2(), "assets");
|
|
15801
|
+
|
|
15802
|
+
// packages/workit-opencode/src/stale-sources.ts
|
|
15803
|
+
import { existsSync as existsSync8, readdirSync as readdirSync5 } from "node:fs";
|
|
15804
|
+
import path14 from "node:path";
|
|
15805
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
15806
|
+
var typescriptFiles = (directory) => {
|
|
15807
|
+
if (!existsSync8(directory))
|
|
15808
|
+
return [];
|
|
15809
|
+
return readdirSync5(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
15810
|
+
const file = path14.join(directory, entry.name);
|
|
15811
|
+
return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
|
|
15812
|
+
});
|
|
15813
|
+
};
|
|
15413
15814
|
var pluginSourceFiles = [
|
|
15414
|
-
|
|
15415
|
-
|
|
15416
|
-
|
|
15417
|
-
|
|
15418
|
-
"
|
|
15419
|
-
|
|
15420
|
-
|
|
15421
|
-
"task-store.ts",
|
|
15422
|
-
"workers.ts",
|
|
15423
|
-
"authority.ts",
|
|
15424
|
-
"methods.ts"
|
|
15425
|
-
].map((file) => fileURLToPath2(new URL(`../../workit-core/src/core/${file}`, import.meta.url)))
|
|
15426
|
-
].filter((file) => existsSync7(file));
|
|
15815
|
+
...new Set([
|
|
15816
|
+
fileURLToPath3(import.meta.url),
|
|
15817
|
+
...typescriptFiles(path14.join(packageRoot2(), "src")),
|
|
15818
|
+
path14.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
|
|
15819
|
+
...typescriptFiles(path14.join(packageRoot2(), "..", "workit-core", "src", "core"))
|
|
15820
|
+
])
|
|
15821
|
+
].filter((file) => existsSync8(file)).sort();
|
|
15427
15822
|
|
|
15428
15823
|
// packages/workit-opencode/src/tools/repo.ts
|
|
15429
|
-
import
|
|
15430
|
-
import { realpathSync as realpathSync8 } from "node:fs";
|
|
15824
|
+
import { realpathSync as realpathSync9 } from "node:fs";
|
|
15431
15825
|
|
|
15432
15826
|
// node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/external.js
|
|
15433
15827
|
var exports_external = {};
|
|
@@ -27751,7 +28145,7 @@ function tool(input) {
|
|
|
27751
28145
|
tool.schema = exports_external;
|
|
27752
28146
|
// packages/workit-core/src/core/changelog.ts
|
|
27753
28147
|
import fs8 from "node:fs";
|
|
27754
|
-
import
|
|
28148
|
+
import path15 from "node:path";
|
|
27755
28149
|
import { createHash as createHash6 } from "node:crypto";
|
|
27756
28150
|
|
|
27757
28151
|
// packages/workit-core/src/core/scripts.ts
|
|
@@ -27948,7 +28342,7 @@ function applyChangelog(target, entries, normalizeOnly) {
|
|
|
27948
28342
|
const rendered = renderChangelog(source.text, entries, normalizeOnly);
|
|
27949
28343
|
if ("error" in rendered)
|
|
27950
28344
|
return { error: rendered.error };
|
|
27951
|
-
fs8.mkdirSync(
|
|
28345
|
+
fs8.mkdirSync(path15.dirname(target), { recursive: true });
|
|
27952
28346
|
fs8.writeFileSync(target, rendered.text, "utf8");
|
|
27953
28347
|
return { ...rendered.result, path: target };
|
|
27954
28348
|
}
|
|
@@ -27983,14 +28377,14 @@ function renderChangelog(source, entries, normalizeOnly) {
|
|
|
27983
28377
|
var changelogTarget = (workspaceRoot, changelogPath) => {
|
|
27984
28378
|
try {
|
|
27985
28379
|
const cwd = fs8.realpathSync(resolveWorkspaceRoot(workspaceRoot));
|
|
27986
|
-
const target =
|
|
27987
|
-
if (target !== cwd && !target.startsWith(cwd +
|
|
28380
|
+
const target = path15.resolve(cwd, changelogPath || "CHANGELOG.md");
|
|
28381
|
+
if (target !== cwd && !target.startsWith(cwd + path15.sep))
|
|
27988
28382
|
return { error: "changelog path must be inside workspace_root" };
|
|
27989
28383
|
let ancestor = target;
|
|
27990
28384
|
while (!fs8.existsSync(ancestor))
|
|
27991
|
-
ancestor =
|
|
28385
|
+
ancestor = path15.dirname(ancestor);
|
|
27992
28386
|
const canonicalAncestor = fs8.realpathSync(ancestor);
|
|
27993
|
-
if (canonicalAncestor !== cwd && !canonicalAncestor.startsWith(cwd +
|
|
28387
|
+
if (canonicalAncestor !== cwd && !canonicalAncestor.startsWith(cwd + path15.sep))
|
|
27994
28388
|
return { error: "changelog path must be inside workspace_root" };
|
|
27995
28389
|
return { cwd, target: fs8.existsSync(target) ? fs8.realpathSync(target) : target };
|
|
27996
28390
|
} catch {
|
|
@@ -28138,121 +28532,9 @@ function parseVerifyOutput(stdout) {
|
|
|
28138
28532
|
return { passed, failed, skipped, commands };
|
|
28139
28533
|
}
|
|
28140
28534
|
|
|
28141
|
-
// packages/workit-core/src/core/gitignore.ts
|
|
28142
|
-
import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "node:fs";
|
|
28143
|
-
import path14 from "node:path";
|
|
28144
|
-
var GITIGNORE_ENTRIES = [
|
|
28145
|
-
"# workit: SDD working state (never commit)",
|
|
28146
|
-
"docs/*/sdd/",
|
|
28147
|
-
"",
|
|
28148
|
-
"# OS / editor cruft",
|
|
28149
|
-
".DS_Store",
|
|
28150
|
-
"Thumbs.db",
|
|
28151
|
-
"*.swp",
|
|
28152
|
-
".idea/",
|
|
28153
|
-
".vscode/",
|
|
28154
|
-
".env",
|
|
28155
|
-
"node_modules/",
|
|
28156
|
-
"dist/",
|
|
28157
|
-
"*.log",
|
|
28158
|
-
".cache/"
|
|
28159
|
-
];
|
|
28160
|
-
var ensureProjectGitignore = (workspaceRoot, confirmed) => {
|
|
28161
|
-
if (!confirmed)
|
|
28162
|
-
return { ok: false, error: "confirmed: true required" };
|
|
28163
|
-
const file = path14.join(workspaceRoot, ".gitignore");
|
|
28164
|
-
const existing = existsSync8(file) ? readFileSync8(file, "utf8") : "";
|
|
28165
|
-
const existingLines = new Set(existing.split(`
|
|
28166
|
-
`).map((l) => l.trim()).filter(Boolean));
|
|
28167
|
-
const added = [];
|
|
28168
|
-
const append = [];
|
|
28169
|
-
for (const entry of GITIGNORE_ENTRIES) {
|
|
28170
|
-
if (entry.trim() === "" || existingLines.has(entry.trim()))
|
|
28171
|
-
continue;
|
|
28172
|
-
append.push(entry);
|
|
28173
|
-
added.push(entry);
|
|
28174
|
-
}
|
|
28175
|
-
if (append.length) {
|
|
28176
|
-
const separator = existing && !existing.endsWith(`
|
|
28177
|
-
`) ? `
|
|
28178
|
-
` : "";
|
|
28179
|
-
writeFileSync4(file, existing + separator + (existing ? `
|
|
28180
|
-
` : "") + append.join(`
|
|
28181
|
-
`) + `
|
|
28182
|
-
`, "utf8");
|
|
28183
|
-
} else if (!existsSync8(file)) {
|
|
28184
|
-
writeFileSync4(file, "", "utf8");
|
|
28185
|
-
}
|
|
28186
|
-
return { ok: true, path: file, added };
|
|
28187
|
-
};
|
|
28188
|
-
|
|
28189
|
-
// packages/workit-core/src/core/hygiene.ts
|
|
28190
|
-
import { existsSync as existsSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "node:fs";
|
|
28191
|
-
import path15 from "node:path";
|
|
28192
|
-
var repoRoot = assetRoot();
|
|
28193
|
-
var templatesDir = () => path15.join(repoRoot, "templates", "hygiene");
|
|
28194
|
-
var packageJson = (root) => {
|
|
28195
|
-
if (!existsSync9(path15.join(root, "package.json")))
|
|
28196
|
-
return null;
|
|
28197
|
-
try {
|
|
28198
|
-
return JSON.parse(readFileSync9(path15.join(root, "package.json"), "utf8"));
|
|
28199
|
-
} catch {
|
|
28200
|
-
return null;
|
|
28201
|
-
}
|
|
28202
|
-
};
|
|
28203
|
-
var isOpenSource = (root) => {
|
|
28204
|
-
if (existsSync9(path15.join(root, "LICENSE")))
|
|
28205
|
-
return true;
|
|
28206
|
-
const pkg = packageJson(root);
|
|
28207
|
-
if (pkg && !pkg.private)
|
|
28208
|
-
return true;
|
|
28209
|
-
return path15.basename(root) === "workflow-toolkit";
|
|
28210
|
-
};
|
|
28211
|
-
var licenseHolder = (root) => {
|
|
28212
|
-
const pkg = packageJson(root);
|
|
28213
|
-
if (pkg) {
|
|
28214
|
-
const author = pkg.author;
|
|
28215
|
-
if (typeof author === "string" && author.trim())
|
|
28216
|
-
return author.trim();
|
|
28217
|
-
if (author && typeof author === "object") {
|
|
28218
|
-
const name = author.name;
|
|
28219
|
-
if (typeof name === "string" && name.trim())
|
|
28220
|
-
return name.trim();
|
|
28221
|
-
}
|
|
28222
|
-
}
|
|
28223
|
-
return "";
|
|
28224
|
-
};
|
|
28225
|
-
var planHygieneFiles = (root, opts = {}) => {
|
|
28226
|
-
const openSource = opts.includeOpenSource ?? isOpenSource(root);
|
|
28227
|
-
const files = ["CHANGELOG.md", "README.md", ".editorconfig", ".gitattributes"];
|
|
28228
|
-
if (openSource)
|
|
28229
|
-
files.push("LICENSE", "CONTRIBUTING.md");
|
|
28230
|
-
const planned = [];
|
|
28231
|
-
for (const file of files) {
|
|
28232
|
-
if (existsSync9(path15.join(root, file)))
|
|
28233
|
-
continue;
|
|
28234
|
-
const tpl = path15.join(templatesDir(), file);
|
|
28235
|
-
if (!existsSync9(tpl))
|
|
28236
|
-
continue;
|
|
28237
|
-
const content = readFileSync9(tpl, "utf8").replace(/<PROJECT>/g, path15.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
|
|
28238
|
-
planned.push({ path: path15.join(root, file), content });
|
|
28239
|
-
}
|
|
28240
|
-
return planned;
|
|
28241
|
-
};
|
|
28242
|
-
var ensureHygieneFiles = (root, opts) => {
|
|
28243
|
-
if (!opts.confirmed)
|
|
28244
|
-
return { ok: false, error: "confirmed: true required" };
|
|
28245
|
-
const created = [];
|
|
28246
|
-
for (const planned of planHygieneFiles(root, opts)) {
|
|
28247
|
-
writeFileSync5(planned.path, planned.content, "utf8");
|
|
28248
|
-
created.push(path15.basename(planned.path));
|
|
28249
|
-
}
|
|
28250
|
-
return { ok: true, created };
|
|
28251
|
-
};
|
|
28252
|
-
|
|
28253
28535
|
// packages/workit-core/src/core/repo-context.ts
|
|
28254
28536
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
28255
|
-
import { existsSync as
|
|
28537
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, readdirSync as readdirSync6 } from "node:fs";
|
|
28256
28538
|
import path16 from "node:path";
|
|
28257
28539
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
28258
28540
|
try {
|
|
@@ -28287,7 +28569,7 @@ var runGit = (cwd, args) => {
|
|
|
28287
28569
|
exitCode: result.status ?? 1
|
|
28288
28570
|
};
|
|
28289
28571
|
};
|
|
28290
|
-
var
|
|
28572
|
+
var repoRoot = (cwd) => {
|
|
28291
28573
|
const r = runGit(cwd, ["rev-parse", "--show-toplevel"]);
|
|
28292
28574
|
return r.exitCode === 0 && r.stdout ? r.stdout : cwd;
|
|
28293
28575
|
};
|
|
@@ -28324,7 +28606,7 @@ function resolvePrBranchContext(cwd) {
|
|
|
28324
28606
|
if (branch === "unknown") {
|
|
28325
28607
|
return {
|
|
28326
28608
|
ok: false,
|
|
28327
|
-
error: `not in a git repository at ${
|
|
28609
|
+
error: `not in a git repository at ${repoRoot(cwd)} — open the target repository as the OpenCode session directory`
|
|
28328
28610
|
};
|
|
28329
28611
|
}
|
|
28330
28612
|
return {
|
|
@@ -28367,7 +28649,7 @@ var printSection = (title) => `
|
|
|
28367
28649
|
`;
|
|
28368
28650
|
var probeCaseInsensitive = (dir, wanted) => {
|
|
28369
28651
|
try {
|
|
28370
|
-
for (const entry of
|
|
28652
|
+
for (const entry of readdirSync6(dir)) {
|
|
28371
28653
|
if (entry.toLowerCase() === wanted.toLowerCase())
|
|
28372
28654
|
return entry;
|
|
28373
28655
|
}
|
|
@@ -28419,10 +28701,10 @@ var diffStatForRange = (cwd, range) => {
|
|
|
28419
28701
|
};
|
|
28420
28702
|
var packageScripts = (cwd, keys) => {
|
|
28421
28703
|
const pkgPath = path16.join(cwd, "package.json");
|
|
28422
|
-
if (!
|
|
28704
|
+
if (!existsSync9(pkgPath))
|
|
28423
28705
|
return [];
|
|
28424
28706
|
try {
|
|
28425
|
-
const pkg = JSON.parse(
|
|
28707
|
+
const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
28426
28708
|
return keys.filter((k) => pkg.scripts && typeof pkg.scripts[k] === "string").map((k) => `${k}: ${pkg.scripts?.[k]}`);
|
|
28427
28709
|
} catch {
|
|
28428
28710
|
return [];
|
|
@@ -28436,7 +28718,7 @@ var documentationFiles = (cwd) => {
|
|
|
28436
28718
|
return;
|
|
28437
28719
|
let entries;
|
|
28438
28720
|
try {
|
|
28439
|
-
entries =
|
|
28721
|
+
entries = readdirSync6(dir, { withFileTypes: true });
|
|
28440
28722
|
} catch {
|
|
28441
28723
|
return;
|
|
28442
28724
|
}
|
|
@@ -28454,15 +28736,15 @@ var documentationFiles = (cwd) => {
|
|
|
28454
28736
|
return matches.sort().slice(0, 200);
|
|
28455
28737
|
};
|
|
28456
28738
|
var readTrimmed = (file, maxLines) => {
|
|
28457
|
-
if (!
|
|
28739
|
+
if (!existsSync9(file))
|
|
28458
28740
|
return "";
|
|
28459
|
-
const lines =
|
|
28741
|
+
const lines = readFileSync8(file, "utf8").split(`
|
|
28460
28742
|
`);
|
|
28461
28743
|
return lines.slice(0, maxLines).join(`
|
|
28462
28744
|
`);
|
|
28463
28745
|
};
|
|
28464
28746
|
function prReadyContext(root, range) {
|
|
28465
|
-
const cwd = path16.resolve(
|
|
28747
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
28466
28748
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
28467
28749
|
return invalidRange(cwd);
|
|
28468
28750
|
let stdout = "";
|
|
@@ -28530,7 +28812,7 @@ ${status.stderr}` : "");
|
|
|
28530
28812
|
stdout += `
|
|
28531
28813
|
`;
|
|
28532
28814
|
stdout += printSection("Recent Validation Signals");
|
|
28533
|
-
if (
|
|
28815
|
+
if (existsSync9(path16.join(cwd, "package.json"))) {
|
|
28534
28816
|
stdout += `package.json detected. Common scripts:
|
|
28535
28817
|
`;
|
|
28536
28818
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -28539,7 +28821,7 @@ ${status.stderr}` : "");
|
|
|
28539
28821
|
`) + `
|
|
28540
28822
|
`;
|
|
28541
28823
|
}
|
|
28542
|
-
if (
|
|
28824
|
+
if (existsSync9(path16.join(cwd, "Cargo.toml")) || existsSync9(path16.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
28543
28825
|
stdout += `Rust project detected.
|
|
28544
28826
|
`;
|
|
28545
28827
|
}
|
|
@@ -28572,7 +28854,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
28572
28854
|
- Apply with the native workit_changelog_apply tool only (not hand-edits under Unreleased).
|
|
28573
28855
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
28574
28856
|
function changelogContext(root, range) {
|
|
28575
|
-
const cwd = path16.resolve(
|
|
28857
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
28576
28858
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
28577
28859
|
return invalidRange(cwd);
|
|
28578
28860
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -28591,7 +28873,7 @@ function changelogContext(root, range) {
|
|
|
28591
28873
|
`;
|
|
28592
28874
|
stdout += printSection("Existing CHANGELOG.md");
|
|
28593
28875
|
const changelogPath = path16.join(cwd, "CHANGELOG.md");
|
|
28594
|
-
if (
|
|
28876
|
+
if (existsSync9(changelogPath)) {
|
|
28595
28877
|
stdout += readTrimmed(changelogPath, 260) + `
|
|
28596
28878
|
`;
|
|
28597
28879
|
} else {
|
|
@@ -28610,7 +28892,7 @@ function changelogContext(root, range) {
|
|
|
28610
28892
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
28611
28893
|
}
|
|
28612
28894
|
function docsRefreshContext(root, range) {
|
|
28613
|
-
const cwd = path16.resolve(
|
|
28895
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
28614
28896
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
28615
28897
|
return invalidRange(cwd);
|
|
28616
28898
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -28633,7 +28915,7 @@ function docsRefreshContext(root, range) {
|
|
|
28633
28915
|
`;
|
|
28634
28916
|
stdout += printSection("README Preview");
|
|
28635
28917
|
const readme = path16.join(cwd, "README.md");
|
|
28636
|
-
if (
|
|
28918
|
+
if (existsSync9(readme)) {
|
|
28637
28919
|
stdout += readTrimmed(readme, 220) + `
|
|
28638
28920
|
`;
|
|
28639
28921
|
} else {
|
|
@@ -28642,9 +28924,9 @@ function docsRefreshContext(root, range) {
|
|
|
28642
28924
|
}
|
|
28643
28925
|
stdout += printSection("Package Scripts");
|
|
28644
28926
|
const pkgPath = path16.join(cwd, "package.json");
|
|
28645
|
-
if (
|
|
28927
|
+
if (existsSync9(pkgPath)) {
|
|
28646
28928
|
try {
|
|
28647
|
-
const pkg = JSON.parse(
|
|
28929
|
+
const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
28648
28930
|
stdout += JSON.stringify({ name: pkg.name, version: pkg.version, scripts: pkg.scripts }, null, 2) + `
|
|
28649
28931
|
`;
|
|
28650
28932
|
} catch {}
|
|
@@ -28655,7 +28937,7 @@ function docsRefreshContext(root, range) {
|
|
|
28655
28937
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
28656
28938
|
}
|
|
28657
28939
|
function releaseNotesContext(root, rangeOrTag) {
|
|
28658
|
-
const cwd = path16.resolve(
|
|
28940
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
28659
28941
|
if (!rangeOrTag) {
|
|
28660
28942
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
28661
28943
|
`, exitCode: 1, cwd };
|
|
@@ -28693,7 +28975,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
28693
28975
|
`;
|
|
28694
28976
|
stdout += printSection("Existing Release Files");
|
|
28695
28977
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
28696
|
-
if (
|
|
28978
|
+
if (existsSync9(path16.join(cwd, rel)))
|
|
28697
28979
|
stdout += rel + `
|
|
28698
28980
|
`;
|
|
28699
28981
|
}
|
|
@@ -28722,7 +29004,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
28722
29004
|
|
|
28723
29005
|
// packages/workit-core/src/core/verify-project.ts
|
|
28724
29006
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
28725
|
-
import { existsSync as
|
|
29007
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9, statSync as statSync2 } from "node:fs";
|
|
28726
29008
|
import path17 from "node:path";
|
|
28727
29009
|
var commandOnPath = (name) => {
|
|
28728
29010
|
const dirs = (process.env.PATH ?? "").split(path17.delimiter);
|
|
@@ -28747,33 +29029,33 @@ var commandOnPath = (name) => {
|
|
|
28747
29029
|
};
|
|
28748
29030
|
var hasScript = (cwd, name) => {
|
|
28749
29031
|
const pkgPath = path17.join(cwd, "package.json");
|
|
28750
|
-
if (!
|
|
29032
|
+
if (!existsSync10(pkgPath))
|
|
28751
29033
|
return false;
|
|
28752
29034
|
try {
|
|
28753
|
-
const pkg = JSON.parse(
|
|
29035
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf8"));
|
|
28754
29036
|
return Boolean(pkg.scripts && typeof pkg.scripts[name] === "string");
|
|
28755
29037
|
} catch {
|
|
28756
29038
|
return false;
|
|
28757
29039
|
}
|
|
28758
29040
|
};
|
|
28759
29041
|
var packageRunner = (cwd) => {
|
|
28760
|
-
if (
|
|
29042
|
+
if (existsSync10(path17.join(cwd, "pnpm-lock.yaml")))
|
|
28761
29043
|
return "pnpm";
|
|
28762
29044
|
const pkgPath = path17.join(cwd, "package.json");
|
|
28763
|
-
if (
|
|
29045
|
+
if (existsSync10(pkgPath)) {
|
|
28764
29046
|
try {
|
|
28765
|
-
const pkg = JSON.parse(
|
|
29047
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf8"));
|
|
28766
29048
|
if (typeof pkg.packageManager === "string" && pkg.packageManager.startsWith("pnpm")) {
|
|
28767
29049
|
return "pnpm";
|
|
28768
29050
|
}
|
|
28769
29051
|
} catch {}
|
|
28770
29052
|
}
|
|
28771
|
-
if (
|
|
29053
|
+
if (existsSync10(path17.join(cwd, "yarn.lock")))
|
|
28772
29054
|
return "yarn";
|
|
28773
29055
|
return "npm";
|
|
28774
29056
|
};
|
|
28775
29057
|
function runVerifyProject(root, dryRun = false) {
|
|
28776
|
-
const cwd =
|
|
29058
|
+
const cwd = repoRoot(root);
|
|
28777
29059
|
let passed = 0;
|
|
28778
29060
|
let failed = 0;
|
|
28779
29061
|
let skipped = 0;
|
|
@@ -28829,7 +29111,7 @@ root: ${cwd}
|
|
|
28829
29111
|
lines.push(`dry_run: ${String(dryRun)}
|
|
28830
29112
|
`);
|
|
28831
29113
|
const pkgPath = path17.join(cwd, "package.json");
|
|
28832
|
-
if (
|
|
29114
|
+
if (existsSync10(pkgPath)) {
|
|
28833
29115
|
const runner = packageRunner(cwd);
|
|
28834
29116
|
for (const script of ["lint", "format:check", "test", "build"]) {
|
|
28835
29117
|
if (hasScript(cwd, script)) {
|
|
@@ -28844,7 +29126,7 @@ root: ${cwd}
|
|
|
28844
29126
|
} else {
|
|
28845
29127
|
skipCheck("javascript", "package.json not found");
|
|
28846
29128
|
}
|
|
28847
|
-
const cargoManifest =
|
|
29129
|
+
const cargoManifest = existsSync10(path17.join(cwd, "Cargo.toml")) ? "Cargo.toml" : existsSync10(path17.join(cwd, "src-tauri/Cargo.toml")) ? "src-tauri/Cargo.toml" : "";
|
|
28848
29130
|
if (cargoManifest) {
|
|
28849
29131
|
runCheck("cargo fmt", "cargo", ["fmt", "--manifest-path", cargoManifest, "--", "--check"]);
|
|
28850
29132
|
runCheck("cargo clippy", "cargo", [
|
|
@@ -28860,7 +29142,7 @@ root: ${cwd}
|
|
|
28860
29142
|
} else {
|
|
28861
29143
|
skipCheck("rust", "Cargo.toml not found");
|
|
28862
29144
|
}
|
|
28863
|
-
if (
|
|
29145
|
+
if (existsSync10(path17.join(cwd, "pyproject.toml")) || existsSync10(path17.join(cwd, "pytest.ini")) || existsSync10(path17.join(cwd, "tests"))) {
|
|
28864
29146
|
if (commandOnPath("pytest"))
|
|
28865
29147
|
runCheck("pytest", "pytest", []);
|
|
28866
29148
|
else
|
|
@@ -28882,8 +29164,8 @@ root: ${cwd}
|
|
|
28882
29164
|
lines.push(`status: skipped (dry run)
|
|
28883
29165
|
`);
|
|
28884
29166
|
skipped += 1;
|
|
28885
|
-
} else if (
|
|
28886
|
-
if (/## \[Unreleased\]/i.test(
|
|
29167
|
+
} else if (existsSync10(changelogPath)) {
|
|
29168
|
+
if (/## \[Unreleased\]/i.test(readFileSync9(changelogPath, "utf8"))) {
|
|
28887
29169
|
lines.push(`status: pass
|
|
28888
29170
|
`);
|
|
28889
29171
|
passed += 1;
|
|
@@ -28997,12 +29279,12 @@ function whichOnPath(tool) {
|
|
|
28997
29279
|
return null;
|
|
28998
29280
|
}
|
|
28999
29281
|
var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
|
|
29000
|
-
function
|
|
29282
|
+
function repoRoot2(cwd) {
|
|
29001
29283
|
const result = spawnSync6("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf8" });
|
|
29002
29284
|
return result.status === 0 ? (result.stdout ?? "").trim() : cwd;
|
|
29003
29285
|
}
|
|
29004
29286
|
function prCreate(env, cwd) {
|
|
29005
|
-
const root = cwd ?
|
|
29287
|
+
const root = cwd ? repoRoot2(cwd) : process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
29006
29288
|
const policy = resolveBranchPolicyFor(root);
|
|
29007
29289
|
const cfg = vcsConfig("load", root);
|
|
29008
29290
|
if (!cfg.ok)
|
|
@@ -29195,7 +29477,7 @@ function prCreate(env, cwd) {
|
|
|
29195
29477
|
};
|
|
29196
29478
|
}
|
|
29197
29479
|
function mergePr(cwd, opts) {
|
|
29198
|
-
const root =
|
|
29480
|
+
const root = repoRoot2(cwd);
|
|
29199
29481
|
const cfg = vcsConfig("load", root);
|
|
29200
29482
|
if (!cfg.ok)
|
|
29201
29483
|
return { error: cfg.error ?? "vcs config missing" };
|
|
@@ -29244,14 +29526,14 @@ function mergePr(cwd, opts) {
|
|
|
29244
29526
|
|
|
29245
29527
|
// packages/workit-core/src/core/init.ts
|
|
29246
29528
|
import fs11 from "node:fs";
|
|
29247
|
-
import
|
|
29529
|
+
import path23 from "node:path";
|
|
29248
29530
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
29249
29531
|
|
|
29250
29532
|
// packages/workit-core/src/core/safe-write.ts
|
|
29251
|
-
import { writeFileSync as
|
|
29533
|
+
import { writeFileSync as writeFileSync4 } from "node:fs";
|
|
29252
29534
|
function writeFileExclusive(file, content, mode) {
|
|
29253
29535
|
try {
|
|
29254
|
-
|
|
29536
|
+
writeFileSync4(file, content, { encoding: "utf8", flag: "wx", mode });
|
|
29255
29537
|
return "created";
|
|
29256
29538
|
} catch (err) {
|
|
29257
29539
|
if (err.code === "EEXIST")
|
|
@@ -29264,19 +29546,19 @@ function writeFileExclusive(file, content, mode) {
|
|
|
29264
29546
|
import {
|
|
29265
29547
|
copyFileSync as copyFileSync2,
|
|
29266
29548
|
cpSync as cpSync2,
|
|
29267
|
-
existsSync as
|
|
29549
|
+
existsSync as existsSync13,
|
|
29268
29550
|
lstatSync as lstatSync4,
|
|
29269
29551
|
mkdirSync as mkdirSync5,
|
|
29270
29552
|
mkdtempSync,
|
|
29271
29553
|
readFileSync as readFileSync12,
|
|
29272
|
-
readdirSync as
|
|
29273
|
-
realpathSync as
|
|
29554
|
+
readdirSync as readdirSync7,
|
|
29555
|
+
realpathSync as realpathSync8,
|
|
29274
29556
|
renameSync as renameSync2,
|
|
29275
29557
|
rmSync,
|
|
29276
29558
|
statSync as statSync3,
|
|
29277
29559
|
writeFileSync as writeFileSync7
|
|
29278
29560
|
} from "node:fs";
|
|
29279
|
-
import
|
|
29561
|
+
import path21 from "node:path";
|
|
29280
29562
|
import { isDeepStrictEqual } from "node:util";
|
|
29281
29563
|
|
|
29282
29564
|
// packages/workit-core/src/core/branch-policy.ts
|
|
@@ -29339,6 +29621,118 @@ var detectBranchPolicy = (workspaceRoot) => {
|
|
|
29339
29621
|
};
|
|
29340
29622
|
};
|
|
29341
29623
|
|
|
29624
|
+
// packages/workit-core/src/core/gitignore.ts
|
|
29625
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "node:fs";
|
|
29626
|
+
import path19 from "node:path";
|
|
29627
|
+
var GITIGNORE_ENTRIES = [
|
|
29628
|
+
"# workit: SDD working state (never commit)",
|
|
29629
|
+
"docs/*/sdd/",
|
|
29630
|
+
"",
|
|
29631
|
+
"# OS / editor cruft",
|
|
29632
|
+
".DS_Store",
|
|
29633
|
+
"Thumbs.db",
|
|
29634
|
+
"*.swp",
|
|
29635
|
+
".idea/",
|
|
29636
|
+
".vscode/",
|
|
29637
|
+
".env",
|
|
29638
|
+
"node_modules/",
|
|
29639
|
+
"dist/",
|
|
29640
|
+
"*.log",
|
|
29641
|
+
".cache/"
|
|
29642
|
+
];
|
|
29643
|
+
var ensureProjectGitignore = (workspaceRoot, confirmed) => {
|
|
29644
|
+
if (!confirmed)
|
|
29645
|
+
return { ok: false, error: "confirmed: true required" };
|
|
29646
|
+
const file = path19.join(workspaceRoot, ".gitignore");
|
|
29647
|
+
const existing = existsSync11(file) ? readFileSync10(file, "utf8") : "";
|
|
29648
|
+
const existingLines = new Set(existing.split(`
|
|
29649
|
+
`).map((l) => l.trim()).filter(Boolean));
|
|
29650
|
+
const added = [];
|
|
29651
|
+
const append = [];
|
|
29652
|
+
for (const entry of GITIGNORE_ENTRIES) {
|
|
29653
|
+
if (entry.trim() === "" || existingLines.has(entry.trim()))
|
|
29654
|
+
continue;
|
|
29655
|
+
append.push(entry);
|
|
29656
|
+
added.push(entry);
|
|
29657
|
+
}
|
|
29658
|
+
if (append.length) {
|
|
29659
|
+
const separator = existing && !existing.endsWith(`
|
|
29660
|
+
`) ? `
|
|
29661
|
+
` : "";
|
|
29662
|
+
writeFileSync5(file, existing + separator + (existing ? `
|
|
29663
|
+
` : "") + append.join(`
|
|
29664
|
+
`) + `
|
|
29665
|
+
`, "utf8");
|
|
29666
|
+
} else if (!existsSync11(file)) {
|
|
29667
|
+
writeFileSync5(file, "", "utf8");
|
|
29668
|
+
}
|
|
29669
|
+
return { ok: true, path: file, added };
|
|
29670
|
+
};
|
|
29671
|
+
|
|
29672
|
+
// packages/workit-core/src/core/hygiene.ts
|
|
29673
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "node:fs";
|
|
29674
|
+
import path20 from "node:path";
|
|
29675
|
+
var repoRoot3 = assetRoot();
|
|
29676
|
+
var templatesDir = () => path20.join(repoRoot3, "templates", "hygiene");
|
|
29677
|
+
var packageJson = (root) => {
|
|
29678
|
+
if (!existsSync12(path20.join(root, "package.json")))
|
|
29679
|
+
return null;
|
|
29680
|
+
try {
|
|
29681
|
+
return JSON.parse(readFileSync11(path20.join(root, "package.json"), "utf8"));
|
|
29682
|
+
} catch {
|
|
29683
|
+
return null;
|
|
29684
|
+
}
|
|
29685
|
+
};
|
|
29686
|
+
var isOpenSource = (root) => {
|
|
29687
|
+
if (existsSync12(path20.join(root, "LICENSE")))
|
|
29688
|
+
return true;
|
|
29689
|
+
const pkg = packageJson(root);
|
|
29690
|
+
if (pkg && !pkg.private)
|
|
29691
|
+
return true;
|
|
29692
|
+
return path20.basename(root) === "workflow-toolkit";
|
|
29693
|
+
};
|
|
29694
|
+
var licenseHolder = (root) => {
|
|
29695
|
+
const pkg = packageJson(root);
|
|
29696
|
+
if (pkg) {
|
|
29697
|
+
const author = pkg.author;
|
|
29698
|
+
if (typeof author === "string" && author.trim())
|
|
29699
|
+
return author.trim();
|
|
29700
|
+
if (author && typeof author === "object") {
|
|
29701
|
+
const name = author.name;
|
|
29702
|
+
if (typeof name === "string" && name.trim())
|
|
29703
|
+
return name.trim();
|
|
29704
|
+
}
|
|
29705
|
+
}
|
|
29706
|
+
return "";
|
|
29707
|
+
};
|
|
29708
|
+
var planHygieneFiles = (root, opts = {}) => {
|
|
29709
|
+
const openSource = opts.includeOpenSource ?? isOpenSource(root);
|
|
29710
|
+
const files = ["CHANGELOG.md", "README.md", ".editorconfig", ".gitattributes"];
|
|
29711
|
+
if (openSource)
|
|
29712
|
+
files.push("LICENSE", "CONTRIBUTING.md");
|
|
29713
|
+
const planned = [];
|
|
29714
|
+
for (const file of files) {
|
|
29715
|
+
if (existsSync12(path20.join(root, file)))
|
|
29716
|
+
continue;
|
|
29717
|
+
const tpl = path20.join(templatesDir(), file);
|
|
29718
|
+
if (!existsSync12(tpl))
|
|
29719
|
+
continue;
|
|
29720
|
+
const content = readFileSync11(tpl, "utf8").replace(/<PROJECT>/g, path20.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
|
|
29721
|
+
planned.push({ path: path20.join(root, file), content });
|
|
29722
|
+
}
|
|
29723
|
+
return planned;
|
|
29724
|
+
};
|
|
29725
|
+
var ensureHygieneFiles = (root, opts) => {
|
|
29726
|
+
if (!opts.confirmed)
|
|
29727
|
+
return { ok: false, error: "confirmed: true required" };
|
|
29728
|
+
const created = [];
|
|
29729
|
+
for (const planned of planHygieneFiles(root, opts)) {
|
|
29730
|
+
writeFileSync6(planned.path, planned.content, "utf8");
|
|
29731
|
+
created.push(path20.basename(planned.path));
|
|
29732
|
+
}
|
|
29733
|
+
return { ok: true, created };
|
|
29734
|
+
};
|
|
29735
|
+
|
|
29342
29736
|
// packages/workit-core/src/core/registration.ts
|
|
29343
29737
|
var CURSOR_RUNTIME_PACKAGE = "@brainervirus/workit-cursor@latest";
|
|
29344
29738
|
var CURSOR_HOOK_RUN_COMMAND = `npx -y --prefer-online --min-release-age=0 --package=${CURSOR_RUNTIME_PACKAGE} workit-cursor-hook`;
|
|
@@ -29380,12 +29774,12 @@ var VCS_OVERRIDES = [
|
|
|
29380
29774
|
var SETUP_OVERRIDES = [...YT_OVERRIDES, ...VCS_OVERRIDES];
|
|
29381
29775
|
function applyWorkspaceBranchPolicy(opts) {
|
|
29382
29776
|
const { workspace_root, env = process.env } = opts;
|
|
29383
|
-
const dir =
|
|
29777
|
+
const dir = path21.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
|
|
29384
29778
|
const { status, path: wsPath, entries } = readWorkspacesResult(dir);
|
|
29385
29779
|
if (status === "malformed")
|
|
29386
29780
|
return { ok: false, error: `malformed workspaces.json: ${wsPath}` };
|
|
29387
29781
|
const detection = detectBranchPolicy(workspace_root);
|
|
29388
|
-
const name = String(env.WORKFLOW_BP_NAME ??
|
|
29782
|
+
const name = String(env.WORKFLOW_BP_NAME ?? path21.basename(workspace_root));
|
|
29389
29783
|
const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
|
|
29390
29784
|
const policy = {
|
|
29391
29785
|
preset: detection.preset,
|
|
@@ -29410,7 +29804,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
29410
29804
|
};
|
|
29411
29805
|
}
|
|
29412
29806
|
const next = existing ? entries.map((w, i) => i === idx ? { ...w, branchPolicy: policy } : w) : [...entries, { name, glob, branchPolicy: policy }];
|
|
29413
|
-
mkdirSync5(
|
|
29807
|
+
mkdirSync5(path21.dirname(wsPath), { recursive: true });
|
|
29414
29808
|
writeFileSync7(wsPath, JSON.stringify({ workspaces: next }, null, 2) + `
|
|
29415
29809
|
`, "utf8");
|
|
29416
29810
|
return {
|
|
@@ -29424,7 +29818,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
29424
29818
|
|
|
29425
29819
|
// packages/workit-core/src/core/youtrack.ts
|
|
29426
29820
|
import fs10 from "node:fs";
|
|
29427
|
-
import
|
|
29821
|
+
import path22 from "node:path";
|
|
29428
29822
|
|
|
29429
29823
|
// packages/workit-core/src/core/templates.ts
|
|
29430
29824
|
var repoRoot4 = assetRoot();
|
|
@@ -29432,8 +29826,8 @@ var repoRoot4 = assetRoot();
|
|
|
29432
29826
|
// packages/workit-core/src/core/youtrack.ts
|
|
29433
29827
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
29434
29828
|
var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE";
|
|
29435
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
29436
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
29829
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path22.join(configDir(), "youtrack.json");
|
|
29830
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path22.join(resolveConfigDir(), "youtrack.json");
|
|
29437
29831
|
var youTrackTokenModeOk = (p) => {
|
|
29438
29832
|
if (process.platform === "win32")
|
|
29439
29833
|
return true;
|
|
@@ -29477,7 +29871,7 @@ function youTrackConfigLoad() {
|
|
|
29477
29871
|
}
|
|
29478
29872
|
const cfgPath = loaded.path;
|
|
29479
29873
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
29480
|
-
const tokenPath = tokenFile ?
|
|
29874
|
+
const tokenPath = tokenFile ? path22.isAbsolute(tokenFile) ? path22.resolve(tokenFile) : path22.resolve(process.cwd(), tokenFile) : "";
|
|
29481
29875
|
if (!tokenPath || !fs10.existsSync(tokenPath)) {
|
|
29482
29876
|
return { error: "missing youtrack.token" };
|
|
29483
29877
|
}
|
|
@@ -29491,8 +29885,8 @@ function youTrackConfigLoad() {
|
|
|
29491
29885
|
const redacted = { ...loaded.config };
|
|
29492
29886
|
delete redacted.tokenFile;
|
|
29493
29887
|
redacted.tokenPresent = true;
|
|
29494
|
-
redacted.configPath =
|
|
29495
|
-
redacted.tokenPath =
|
|
29888
|
+
redacted.configPath = path22.resolve(cfgPath);
|
|
29889
|
+
redacted.tokenPath = path22.resolve(tokenPath);
|
|
29496
29890
|
return { data: redacted };
|
|
29497
29891
|
}
|
|
29498
29892
|
function tzParts(date, tz) {
|
|
@@ -29605,7 +29999,7 @@ var youTrackToken = () => {
|
|
|
29605
29999
|
if ("error" in loaded)
|
|
29606
30000
|
return loaded;
|
|
29607
30001
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
29608
|
-
const tokenPath = tokenFile ?
|
|
30002
|
+
const tokenPath = tokenFile ? path22.isAbsolute(tokenFile) ? path22.resolve(tokenFile) : path22.resolve(process.cwd(), tokenFile) : "";
|
|
29609
30003
|
if (!tokenPath || !fs10.existsSync(tokenPath))
|
|
29610
30004
|
return { error: "missing youtrack.token" };
|
|
29611
30005
|
if (!youTrackTokenModeOk(tokenPath))
|
|
@@ -29760,7 +30154,7 @@ function youTrackTokenCreateUrl() {
|
|
|
29760
30154
|
const desc = String(defaults.description ?? "OpenCode workit — /wk-issue-update and /wk-meetings");
|
|
29761
30155
|
const scopes = Array.isArray(defaults.scopes) ? defaults.scopes : ["YouTrack"];
|
|
29762
30156
|
const base = String(config.baseUrl ?? "https://enghouseamg.youtrack.cloud").replace(/\/+$/, "");
|
|
29763
|
-
const tokenFile = String(config.tokenFile ??
|
|
30157
|
+
const tokenFile = String(config.tokenFile ?? path22.join(path22.dirname(loaded.path), "youtrack.token"));
|
|
29764
30158
|
const tab = String(defaults.profileTab ?? "account-security");
|
|
29765
30159
|
const createUrl = `${base}/users/me?${new URLSearchParams({ tab })}`;
|
|
29766
30160
|
const docsUrl = "https://www.jetbrains.com/help/youtrack/cloud/manage-permanent-token.html";
|
|
@@ -29769,7 +30163,7 @@ function youTrackTokenCreateUrl() {
|
|
|
29769
30163
|
tokenName: name,
|
|
29770
30164
|
tokenDescription: desc,
|
|
29771
30165
|
scopes,
|
|
29772
|
-
tokenFile:
|
|
30166
|
+
tokenFile: path22.resolve(tokenFile),
|
|
29773
30167
|
createUrl,
|
|
29774
30168
|
docsUrl,
|
|
29775
30169
|
prefillSupported: false,
|
|
@@ -30087,12 +30481,12 @@ var resolvePath = (p) => {
|
|
|
30087
30481
|
try {
|
|
30088
30482
|
return fs11.realpathSync(p);
|
|
30089
30483
|
} catch {
|
|
30090
|
-
return
|
|
30484
|
+
return path23.resolve(p);
|
|
30091
30485
|
}
|
|
30092
30486
|
};
|
|
30093
30487
|
function initStatusData(configDirPath = configDir()) {
|
|
30094
|
-
const ytJson =
|
|
30095
|
-
const vcsJson =
|
|
30488
|
+
const ytJson = path23.join(configDirPath, "youtrack.json");
|
|
30489
|
+
const vcsJson = path23.join(configDirPath, "vcs.json");
|
|
30096
30490
|
const items = [];
|
|
30097
30491
|
let youtrackConfig = null;
|
|
30098
30492
|
let youtrackTokenCreate = null;
|
|
@@ -30115,8 +30509,8 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30115
30509
|
});
|
|
30116
30510
|
}
|
|
30117
30511
|
}
|
|
30118
|
-
const expanded = tokenFile ?
|
|
30119
|
-
const resolvedTokenFile = expanded && fs11.existsSync(expanded) ? resolvePath(expanded) : expanded ?
|
|
30512
|
+
const expanded = tokenFile ? path23.resolve(tokenFile) : null;
|
|
30513
|
+
const resolvedTokenFile = expanded && fs11.existsSync(expanded) ? resolvePath(expanded) : expanded ? path23.isAbsolute(expanded) ? expanded : path23.resolve(configDirPath, expanded) : null;
|
|
30120
30514
|
youtrackConfig = {
|
|
30121
30515
|
config_edit_path: resolvePath(ytJson),
|
|
30122
30516
|
baseUrl: base,
|
|
@@ -30163,11 +30557,11 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30163
30557
|
id: "youtrack_json",
|
|
30164
30558
|
label: "YouTrack config",
|
|
30165
30559
|
ok: fs11.existsSync(ytJson) && youtrackConfig !== null && !("error" in youtrackConfig),
|
|
30166
|
-
path: fs11.existsSync(ytJson) ? resolvePath(ytJson) :
|
|
30560
|
+
path: fs11.existsSync(ytJson) ? resolvePath(ytJson) : path23.resolve(ytJson),
|
|
30167
30561
|
config_edit_path: resolvePath(ytJson),
|
|
30168
30562
|
fix: "workit_init_apply action=youtrack_scaffold"
|
|
30169
30563
|
});
|
|
30170
|
-
const ytTokenPath = youtrackConfig?.tokenFile ??
|
|
30564
|
+
const ytTokenPath = youtrackConfig?.tokenFile ?? path23.join(configDirPath, "youtrack.token");
|
|
30171
30565
|
const tokenText = fs11.existsSync(ytTokenPath) ? fs11.readFileSync(ytTokenPath, "utf8").trim() : "";
|
|
30172
30566
|
const placeholder = fs11.existsSync(ytTokenPath) && isPlaceholder(tokenText);
|
|
30173
30567
|
const tokenOk = fs11.existsSync(ytTokenPath) && modeOk(ytTokenPath) && Boolean(tokenText) && !isPlaceholder(tokenText);
|
|
@@ -30175,8 +30569,8 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30175
30569
|
id: "youtrack_token",
|
|
30176
30570
|
label: "YouTrack API token (mode 600, not placeholder)",
|
|
30177
30571
|
ok: tokenOk,
|
|
30178
|
-
path: fs11.existsSync(ytTokenPath) ? resolvePath(ytTokenPath) :
|
|
30179
|
-
token_edit_path: fs11.existsSync(ytTokenPath) ? resolvePath(ytTokenPath) :
|
|
30572
|
+
path: fs11.existsSync(ytTokenPath) ? resolvePath(ytTokenPath) : path23.resolve(ytTokenPath),
|
|
30573
|
+
token_edit_path: fs11.existsSync(ytTokenPath) ? resolvePath(ytTokenPath) : path23.resolve(ytTokenPath),
|
|
30180
30574
|
placeholder,
|
|
30181
30575
|
fix: `Open ${resolvePath(ytTokenPath)} — replace ${TOKEN_PLACEHOLDER3} with your permanent token, save, then /wk-status`
|
|
30182
30576
|
};
|
|
@@ -30202,7 +30596,7 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30202
30596
|
const provider = typeof rawProvider === "string" && rawProvider.trim() ? rawProvider.toLowerCase() : null;
|
|
30203
30597
|
const tokenFiles = {};
|
|
30204
30598
|
for (const k of ["gitlab", "github"]) {
|
|
30205
|
-
tokenFiles[k] = String(vcsParsed[k]?.tokenFile ??
|
|
30599
|
+
tokenFiles[k] = String(vcsParsed[k]?.tokenFile ?? path23.join(configDirPath, `${k}.token`));
|
|
30206
30600
|
}
|
|
30207
30601
|
vcsCfg = {
|
|
30208
30602
|
config_edit_path: resolvePath(vcsJson),
|
|
@@ -30227,14 +30621,14 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30227
30621
|
id: "vcs_json",
|
|
30228
30622
|
label: "VCS config (GitLab / GitHub)",
|
|
30229
30623
|
ok: fs11.existsSync(vcsJson) && vcsCfg !== null && !("error" in vcsCfg),
|
|
30230
|
-
path: fs11.existsSync(vcsJson) ? resolvePath(vcsJson) :
|
|
30624
|
+
path: fs11.existsSync(vcsJson) ? resolvePath(vcsJson) : path23.resolve(vcsJson),
|
|
30231
30625
|
config_edit_path: resolvePath(vcsJson),
|
|
30232
30626
|
fix: "workit_init_apply action=vcs_scaffold"
|
|
30233
30627
|
});
|
|
30234
30628
|
const provActive = vcsCfg && !("error" in vcsCfg) ? vcsCfg.provider : null;
|
|
30235
30629
|
const tokenItem = (tid, label, rawPath, providerKey) => {
|
|
30236
|
-
const t =
|
|
30237
|
-
const abs = fs11.existsSync(t) ? resolvePath(t) :
|
|
30630
|
+
const t = path23.isAbsolute(rawPath) ? rawPath : path23.resolve(configDirPath, rawPath);
|
|
30631
|
+
const abs = fs11.existsSync(t) ? resolvePath(t) : path23.resolve(t);
|
|
30238
30632
|
const text = fs11.existsSync(t) ? fs11.readFileSync(t, "utf8").trim() : "";
|
|
30239
30633
|
const ph = isPlaceholder(text);
|
|
30240
30634
|
const ok = fs11.existsSync(t) && modeOk(t) && Boolean(text) && !ph;
|
|
@@ -30265,7 +30659,7 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30265
30659
|
};
|
|
30266
30660
|
const vcsTokenFiles = {};
|
|
30267
30661
|
for (const k of ["gitlab", "github"]) {
|
|
30268
|
-
vcsTokenFiles[k] = String(vcsParsed?.[k]?.tokenFile ??
|
|
30662
|
+
vcsTokenFiles[k] = String(vcsParsed?.[k]?.tokenFile ?? path23.join(configDirPath, `${k}.token`));
|
|
30269
30663
|
}
|
|
30270
30664
|
items.push(tokenItem("gitlab_token", "GitLab token (mode 600, not placeholder)", vcsTokenFiles.gitlab, "gitlab"));
|
|
30271
30665
|
items.push(tokenItem("github_token", "GitHub token (mode 600, not placeholder)", vcsTokenFiles.github, "github"));
|
|
@@ -30322,7 +30716,7 @@ async function toolkitStatusData(configDirPath = configDir()) {
|
|
|
30322
30716
|
}
|
|
30323
30717
|
var youtrackJsonContent = (dir) => ({
|
|
30324
30718
|
baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://enghouseamg.youtrack.cloud",
|
|
30325
|
-
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ??
|
|
30719
|
+
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path23.join(dir, "youtrack.token"),
|
|
30326
30720
|
timezone: process.env.WORKFLOW_YT_TIMEZONE ?? "America/Santiago",
|
|
30327
30721
|
locale: "es-CL",
|
|
30328
30722
|
defaultMention: process.env.WORKFLOW_YT_MENTION ?? "Alejandra.Flores",
|
|
@@ -30361,11 +30755,11 @@ var vcsJsonContent = (dir, cwd) => {
|
|
|
30361
30755
|
gitlab: {
|
|
30362
30756
|
host: process.env.WORKFLOW_GITLAB_HOST ?? "gitlab.com",
|
|
30363
30757
|
apiUrl: process.env.WORKFLOW_GITLAB_API_URL ?? "https://gitlab.com/api/v4",
|
|
30364
|
-
tokenFile:
|
|
30758
|
+
tokenFile: path23.join(dir, "gitlab.token")
|
|
30365
30759
|
},
|
|
30366
30760
|
github: {
|
|
30367
30761
|
host: process.env.WORKFLOW_GITHUB_HOST ?? "github.com",
|
|
30368
|
-
tokenFile:
|
|
30762
|
+
tokenFile: path23.join(dir, "github.token")
|
|
30369
30763
|
},
|
|
30370
30764
|
pr: { squashOnMerge: true, removeSourceBranch: true, pushBranch: true, confirmSkip: true },
|
|
30371
30765
|
tokenDefaults: {
|
|
@@ -30382,16 +30776,16 @@ function initApplyData(action, env = process.env) {
|
|
|
30382
30776
|
fs11.mkdirSync(dir, { recursive: true });
|
|
30383
30777
|
switch (action) {
|
|
30384
30778
|
case "youtrack_json": {
|
|
30385
|
-
const out =
|
|
30779
|
+
const out = path23.join(dir, "youtrack.json");
|
|
30386
30780
|
fs11.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
30387
30781
|
`, "utf8");
|
|
30388
30782
|
return { action, ok: true, path: out };
|
|
30389
30783
|
}
|
|
30390
30784
|
case "youtrack_token_placeholder": {
|
|
30391
|
-
const p =
|
|
30785
|
+
const p = path23.join(dir, "youtrack.token");
|
|
30392
30786
|
const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER3 + `
|
|
30393
30787
|
`, 384) === "preserved";
|
|
30394
|
-
const abs =
|
|
30788
|
+
const abs = path23.resolve(p);
|
|
30395
30789
|
return {
|
|
30396
30790
|
action,
|
|
30397
30791
|
ok: true,
|
|
@@ -30403,14 +30797,14 @@ function initApplyData(action, env = process.env) {
|
|
|
30403
30797
|
};
|
|
30404
30798
|
}
|
|
30405
30799
|
case "youtrack_scaffold": {
|
|
30406
|
-
const jsonOut =
|
|
30407
|
-
const tokenOut =
|
|
30800
|
+
const jsonOut = path23.join(dir, "youtrack.json");
|
|
30801
|
+
const tokenOut = path23.join(dir, "youtrack.token");
|
|
30408
30802
|
fs11.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
30409
30803
|
`, "utf8");
|
|
30410
30804
|
const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER3 + `
|
|
30411
30805
|
`, 384) === "preserved";
|
|
30412
|
-
const configPath =
|
|
30413
|
-
const tokenPath =
|
|
30806
|
+
const configPath = path23.resolve(jsonOut);
|
|
30807
|
+
const tokenPath = path23.resolve(tokenOut);
|
|
30414
30808
|
const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
|
|
30415
30809
|
process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
|
|
30416
30810
|
let tokenCreate = {};
|
|
@@ -30464,19 +30858,19 @@ function initApplyData(action, env = process.env) {
|
|
|
30464
30858
|
}
|
|
30465
30859
|
}
|
|
30466
30860
|
case "vcs_scaffold": {
|
|
30467
|
-
const jsonOut =
|
|
30861
|
+
const jsonOut = path23.join(dir, "vcs.json");
|
|
30468
30862
|
fs11.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(dir), null, 2) + `
|
|
30469
30863
|
`, "utf8");
|
|
30470
|
-
const glPath =
|
|
30471
|
-
const ghPath =
|
|
30864
|
+
const glPath = path23.join(dir, "gitlab.token");
|
|
30865
|
+
const ghPath = path23.join(dir, "github.token");
|
|
30472
30866
|
const preservedTokens = [];
|
|
30473
30867
|
for (const p of [glPath, ghPath]) {
|
|
30474
30868
|
if (writeFileExclusive(p, TOKEN_PLACEHOLDER3 + `
|
|
30475
30869
|
`, 384) === "preserved") {
|
|
30476
|
-
preservedTokens.push(
|
|
30870
|
+
preservedTokens.push(path23.resolve(p));
|
|
30477
30871
|
}
|
|
30478
30872
|
}
|
|
30479
|
-
const configPath =
|
|
30873
|
+
const configPath = path23.resolve(jsonOut);
|
|
30480
30874
|
const prev = process.env.WORKFLOW_VCS_CONFIG;
|
|
30481
30875
|
process.env.WORKFLOW_VCS_CONFIG = configPath;
|
|
30482
30876
|
try {
|
|
@@ -30485,14 +30879,14 @@ function initApplyData(action, env = process.env) {
|
|
|
30485
30879
|
const provider = typeof rawProvider === "string" && rawProvider.trim() ? rawProvider : null;
|
|
30486
30880
|
const tokenUrls = vcsTokenCreateUrls();
|
|
30487
30881
|
const active = tokenUrls.active ?? {};
|
|
30488
|
-
const activePath = provider === "gitlab" ?
|
|
30882
|
+
const activePath = provider === "gitlab" ? path23.resolve(glPath) : provider === "github" ? path23.resolve(ghPath) : null;
|
|
30489
30883
|
return {
|
|
30490
30884
|
action,
|
|
30491
30885
|
ok: true,
|
|
30492
30886
|
vcs_json: configPath,
|
|
30493
30887
|
config_edit_path: configPath,
|
|
30494
|
-
gitlab_token:
|
|
30495
|
-
github_token:
|
|
30888
|
+
gitlab_token: path23.resolve(glPath),
|
|
30889
|
+
github_token: path23.resolve(ghPath),
|
|
30496
30890
|
token_edit_path: activePath,
|
|
30497
30891
|
token_create_url: active.createUrl,
|
|
30498
30892
|
token_create_urls: tokenUrls,
|
|
@@ -30547,6 +30941,109 @@ var normalizeLegacyResult = (value) => {
|
|
|
30547
30941
|
return ok(data);
|
|
30548
30942
|
};
|
|
30549
30943
|
|
|
30944
|
+
// packages/workit-opencode/src/shared/init-apply.ts
|
|
30945
|
+
import path24 from "node:path";
|
|
30946
|
+
|
|
30947
|
+
// packages/workit-opencode/src/shared/repo-result.ts
|
|
30948
|
+
var output = (value) => JSON.stringify(value, null, 2);
|
|
30949
|
+
var diagnostics = ({ stdout, stderr, exitCode }) => ({
|
|
30950
|
+
stdout,
|
|
30951
|
+
stderr,
|
|
30952
|
+
exitCode
|
|
30953
|
+
});
|
|
30954
|
+
var requireConfirmed = (confirmed) => {
|
|
30955
|
+
if (confirmed === true)
|
|
30956
|
+
return null;
|
|
30957
|
+
return output(fail("confirmed: true required"));
|
|
30958
|
+
};
|
|
30959
|
+
function scriptResult(result, parse) {
|
|
30960
|
+
if (result.exitCode !== 0) {
|
|
30961
|
+
return fail(result.stderr.trim() || result.stdout.trim() || "workflow script failed", diagnostics(result));
|
|
30962
|
+
}
|
|
30963
|
+
try {
|
|
30964
|
+
return ok({
|
|
30965
|
+
...parse(result.stdout),
|
|
30966
|
+
exitCode: 0,
|
|
30967
|
+
...result.stderr ? { stderr: result.stderr } : {}
|
|
30968
|
+
});
|
|
30969
|
+
} catch (error) {
|
|
30970
|
+
return fail(error instanceof Error ? error.message : "workflow output parse failed", diagnostics(result));
|
|
30971
|
+
}
|
|
30972
|
+
}
|
|
30973
|
+
var legacyScriptResult = (result) => {
|
|
30974
|
+
let parsed = null;
|
|
30975
|
+
try {
|
|
30976
|
+
parsed = JSON.parse(result.stdout.trim());
|
|
30977
|
+
} catch {}
|
|
30978
|
+
if (result.exitCode !== 0 || parsed?.error || parsed?.ok === false) {
|
|
30979
|
+
const error = parsed?.error ? String(parsed.error) : parsed?.ok === false ? "legacy operation reported failure" : result.stderr.trim() || result.stdout.trim() || "workflow script failed";
|
|
30980
|
+
return fail(error, diagnostics(result));
|
|
30981
|
+
}
|
|
30982
|
+
if (!parsed)
|
|
30983
|
+
return fail("workflow output parse failed", diagnostics(result));
|
|
30984
|
+
const { ok: _legacyOk, ...data } = parsed;
|
|
30985
|
+
return ok({ ...data, exitCode: 0, ...result.stderr ? { stderr: result.stderr } : {} });
|
|
30986
|
+
};
|
|
30987
|
+
|
|
30988
|
+
// packages/workit-opencode/src/shared/init-apply.ts
|
|
30989
|
+
var initApplyRuntime = {
|
|
30990
|
+
initApply: (root, action, env) => {
|
|
30991
|
+
const out = initApply({ action, confirmed: true, env });
|
|
30992
|
+
return {
|
|
30993
|
+
exitCode: out.error ? 1 : 0,
|
|
30994
|
+
stdout: JSON.stringify(out.data ?? out),
|
|
30995
|
+
stderr: "",
|
|
30996
|
+
cwd: root
|
|
30997
|
+
};
|
|
30998
|
+
}
|
|
30999
|
+
};
|
|
31000
|
+
var executeInitApply = (args, directory, runtime) => {
|
|
31001
|
+
const rejected = requireConfirmed(args.confirmed);
|
|
31002
|
+
if (rejected)
|
|
31003
|
+
return rejected;
|
|
31004
|
+
const action = args.action;
|
|
31005
|
+
if (action === "hygiene") {
|
|
31006
|
+
const result = ensureHygieneFiles(directory, {
|
|
31007
|
+
confirmed: args.confirmed,
|
|
31008
|
+
includeOpenSource: args.include_open_source
|
|
31009
|
+
});
|
|
31010
|
+
return output(result.ok ? ok(result) : fail(result.error));
|
|
31011
|
+
}
|
|
31012
|
+
if (action === "gitignore") {
|
|
31013
|
+
const result = ensureProjectGitignore(directory, args.confirmed);
|
|
31014
|
+
return output(result.ok ? ok(result) : fail(result.error));
|
|
31015
|
+
}
|
|
31016
|
+
if (action === "config") {
|
|
31017
|
+
const LOCALE_RE = /^[a-z]{2,3}(-(?:[A-Z]{2}|[0-9]{3}))?$/;
|
|
31018
|
+
if (args.locale !== undefined && !LOCALE_RE.test(args.locale)) {
|
|
31019
|
+
return output(fail(`invalid locale: ${JSON.stringify(args.locale)} — expected BCP-47 like en or es-CL`));
|
|
31020
|
+
}
|
|
31021
|
+
const current = readConfig();
|
|
31022
|
+
const next = mergeConfigValues({
|
|
31023
|
+
locale: args.locale,
|
|
31024
|
+
localeOptions: args.locale_options,
|
|
31025
|
+
timezone: args.timezone,
|
|
31026
|
+
preset: args.branch_policy_preset,
|
|
31027
|
+
allowed: args.branch_policy_allowed,
|
|
31028
|
+
protectedNames: args.branch_policy_protected
|
|
31029
|
+
}, current);
|
|
31030
|
+
writeConfig(next);
|
|
31031
|
+
return output(ok({ action: "config", path: path24.join(configDir(), "config.json"), ...next }));
|
|
31032
|
+
}
|
|
31033
|
+
const env = Object.fromEntries(Object.entries({
|
|
31034
|
+
WORKFLOW_YT_BASE_URL: args.base_url,
|
|
31035
|
+
WORKFLOW_YT_MENTION: args.default_mention,
|
|
31036
|
+
WORKFLOW_YT_MEETING_ISSUE: args.meeting_issue,
|
|
31037
|
+
WORKFLOW_VCS_PROVIDER: args.vcs_provider,
|
|
31038
|
+
WORKFLOW_VCS_TARGET_BRANCH: args.vcs_target_branch,
|
|
31039
|
+
WORKFLOW_WORKSPACE_ROOT: directory,
|
|
31040
|
+
WORKFLOW_BP_NAME: args.name,
|
|
31041
|
+
WORKFLOW_BP_DEVELOP: args.develop_branch,
|
|
31042
|
+
WORKFLOW_BP_INTEGRATION: args.integration
|
|
31043
|
+
}).filter((entry) => entry[1] !== undefined));
|
|
31044
|
+
return output(legacyScriptResult(runtime.initApply(directory, action, env)));
|
|
31045
|
+
};
|
|
31046
|
+
|
|
30550
31047
|
// packages/workit-opencode/src/tools/repo.ts
|
|
30551
31048
|
var defaultRuntime = {
|
|
30552
31049
|
git: (root, args) => run2(root, "git", args),
|
|
@@ -30564,15 +31061,7 @@ var defaultRuntime = {
|
|
|
30564
31061
|
cwd: root
|
|
30565
31062
|
};
|
|
30566
31063
|
},
|
|
30567
|
-
|
|
30568
|
-
const out = initApply({ action, confirmed: true, env });
|
|
30569
|
-
return {
|
|
30570
|
-
exitCode: out.error ? 1 : 0,
|
|
30571
|
-
stdout: JSON.stringify(out.data ?? out),
|
|
30572
|
-
stderr: "",
|
|
30573
|
-
cwd: root
|
|
30574
|
-
};
|
|
30575
|
-
},
|
|
31064
|
+
...initApplyRuntime,
|
|
30576
31065
|
initStatus: (root) => ({
|
|
30577
31066
|
exitCode: 0,
|
|
30578
31067
|
stdout: JSON.stringify(initStatusData()),
|
|
@@ -30586,36 +31075,7 @@ var defaultRuntime = {
|
|
|
30586
31075
|
cwd: root
|
|
30587
31076
|
})
|
|
30588
31077
|
};
|
|
30589
|
-
var output = (value) => JSON.stringify(value, null, 2);
|
|
30590
|
-
var diagnostics = ({ stdout, stderr, exitCode }) => ({ stdout, stderr, exitCode });
|
|
30591
|
-
var requireConfirmed = (confirmed) => confirmed === true ? null : output(fail("confirmed: true required"));
|
|
30592
|
-
function scriptResult(result, parse) {
|
|
30593
|
-
if (result.exitCode !== 0) {
|
|
30594
|
-
return fail(result.stderr.trim() || result.stdout.trim() || "workflow script failed", diagnostics(result));
|
|
30595
|
-
}
|
|
30596
|
-
try {
|
|
30597
|
-
return ok({
|
|
30598
|
-
...parse(result.stdout),
|
|
30599
|
-
exitCode: 0,
|
|
30600
|
-
...result.stderr ? { stderr: result.stderr } : {}
|
|
30601
|
-
});
|
|
30602
|
-
} catch (error) {
|
|
30603
|
-
return fail(error instanceof Error ? error.message : "workflow output parse failed", diagnostics(result));
|
|
30604
|
-
}
|
|
30605
|
-
}
|
|
30606
31078
|
var json2 = (stdout) => JSON.parse(stdout.trim());
|
|
30607
|
-
var legacyScriptResult = (result) => {
|
|
30608
|
-
let parsed = null;
|
|
30609
|
-
try {
|
|
30610
|
-
parsed = json2(result.stdout);
|
|
30611
|
-
} catch {}
|
|
30612
|
-
if (result.exitCode !== 0 || parsed?.error || parsed?.ok === false)
|
|
30613
|
-
return fail(parsed?.error ? String(parsed.error) : parsed?.ok === false ? "legacy operation reported failure" : result.stderr.trim() || result.stdout.trim() || "workflow script failed", diagnostics(result));
|
|
30614
|
-
if (!parsed)
|
|
30615
|
-
return fail("workflow output parse failed", diagnostics(result));
|
|
30616
|
-
const { ok: _legacyOk, ...data } = parsed;
|
|
30617
|
-
return ok({ ...data, exitCode: 0, ...result.stderr ? { stderr: result.stderr } : {} });
|
|
30618
|
-
};
|
|
30619
31079
|
var optionalJson = (value) => {
|
|
30620
31080
|
if (!value?.trim())
|
|
30621
31081
|
return null;
|
|
@@ -30770,7 +31230,7 @@ function createRepoTools(runtime = defaultRuntime) {
|
|
|
30770
31230
|
entries,
|
|
30771
31231
|
path: changelogPath,
|
|
30772
31232
|
normalize_only,
|
|
30773
|
-
workspace_root:
|
|
31233
|
+
workspace_root: realpathSync9(context.directory)
|
|
30774
31234
|
})));
|
|
30775
31235
|
}
|
|
30776
31236
|
}),
|
|
@@ -30891,77 +31351,11 @@ function createRepoTools(runtime = defaultRuntime) {
|
|
|
30891
31351
|
branch_policy_protected: tool.schema.array(tool.schema.string()).optional(),
|
|
30892
31352
|
include_open_source: tool.schema.boolean().optional()
|
|
30893
31353
|
},
|
|
30894
|
-
execute: async (
|
|
30895
|
-
confirmed,
|
|
30896
|
-
action,
|
|
30897
|
-
base_url,
|
|
30898
|
-
default_mention,
|
|
30899
|
-
meeting_issue,
|
|
30900
|
-
vcs_provider,
|
|
30901
|
-
vcs_target_branch,
|
|
30902
|
-
name,
|
|
30903
|
-
develop_branch,
|
|
30904
|
-
integration,
|
|
30905
|
-
locale,
|
|
30906
|
-
locale_options,
|
|
30907
|
-
timezone,
|
|
30908
|
-
branch_policy_preset,
|
|
30909
|
-
branch_policy_allowed,
|
|
30910
|
-
branch_policy_protected,
|
|
30911
|
-
include_open_source
|
|
30912
|
-
}, context) => {
|
|
30913
|
-
const rejected = requireConfirmed(confirmed);
|
|
30914
|
-
if (rejected)
|
|
30915
|
-
return rejected;
|
|
30916
|
-
if (action === "hygiene") {
|
|
30917
|
-
const result = ensureHygieneFiles(context.directory, {
|
|
30918
|
-
confirmed,
|
|
30919
|
-
includeOpenSource: include_open_source
|
|
30920
|
-
});
|
|
30921
|
-
return output(result.ok ? ok(result) : fail(result.error));
|
|
30922
|
-
}
|
|
30923
|
-
if (action === "gitignore") {
|
|
30924
|
-
const result = ensureProjectGitignore(context.directory, confirmed);
|
|
30925
|
-
return output(result.ok ? ok(result) : fail(result.error));
|
|
30926
|
-
}
|
|
30927
|
-
if (action === "config") {
|
|
30928
|
-
const LOCALE_RE = /^[a-z]{2,3}(-(?:[A-Z]{2}|[0-9]{3}))?$/;
|
|
30929
|
-
if (locale !== undefined && !LOCALE_RE.test(locale)) {
|
|
30930
|
-
return output(fail(`invalid locale: ${JSON.stringify(locale)} — expected BCP-47 like en or es-CL`));
|
|
30931
|
-
}
|
|
30932
|
-
const current = readConfig();
|
|
30933
|
-
const next = mergeConfigValues({
|
|
30934
|
-
locale,
|
|
30935
|
-
localeOptions: locale_options,
|
|
30936
|
-
timezone,
|
|
30937
|
-
preset: branch_policy_preset,
|
|
30938
|
-
allowed: branch_policy_allowed,
|
|
30939
|
-
protectedNames: branch_policy_protected
|
|
30940
|
-
}, current);
|
|
30941
|
-
writeConfig(next);
|
|
30942
|
-
return output(ok({ action: "config", path: path22.join(configDir(), "config.json"), ...next }));
|
|
30943
|
-
}
|
|
30944
|
-
const env = Object.fromEntries(Object.entries({
|
|
30945
|
-
WORKFLOW_YT_BASE_URL: base_url,
|
|
30946
|
-
WORKFLOW_YT_MENTION: default_mention,
|
|
30947
|
-
WORKFLOW_YT_MEETING_ISSUE: meeting_issue,
|
|
30948
|
-
WORKFLOW_VCS_PROVIDER: vcs_provider,
|
|
30949
|
-
WORKFLOW_VCS_TARGET_BRANCH: vcs_target_branch,
|
|
30950
|
-
WORKFLOW_WORKSPACE_ROOT: context.directory,
|
|
30951
|
-
WORKFLOW_BP_NAME: name,
|
|
30952
|
-
WORKFLOW_BP_DEVELOP: develop_branch,
|
|
30953
|
-
WORKFLOW_BP_INTEGRATION: integration
|
|
30954
|
-
}).filter((entry) => entry[1] !== undefined));
|
|
30955
|
-
return output(legacyScriptResult(runtime.initApply(context.directory, action, env)));
|
|
30956
|
-
}
|
|
31354
|
+
execute: async (args, context) => executeInitApply(args, context.directory, runtime)
|
|
30957
31355
|
})
|
|
30958
31356
|
};
|
|
30959
31357
|
}
|
|
30960
31358
|
|
|
30961
|
-
// packages/workit-opencode/src/tools/workit.ts
|
|
30962
|
-
import { realpathSync as realpathSync9 } from "node:fs";
|
|
30963
|
-
import path24 from "node:path";
|
|
30964
|
-
|
|
30965
31359
|
// packages/workit-core/src/core/tracker-issues.ts
|
|
30966
31360
|
import fs12 from "node:fs";
|
|
30967
31361
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
@@ -31233,7 +31627,7 @@ async function logTimeUpdate(input, operation = defaultOperations) {
|
|
|
31233
31627
|
|
|
31234
31628
|
// packages/workit-core/src/core/external-action-effects.ts
|
|
31235
31629
|
import fs13 from "node:fs";
|
|
31236
|
-
import
|
|
31630
|
+
import path25 from "node:path";
|
|
31237
31631
|
|
|
31238
31632
|
// packages/workit-core/src/core/commit-flavors.ts
|
|
31239
31633
|
var CONVENTIONAL_RE = /^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?(!)?: .+/;
|
|
@@ -31423,7 +31817,7 @@ var readExternalContext = async (root, payload) => {
|
|
|
31423
31817
|
}
|
|
31424
31818
|
}
|
|
31425
31819
|
};
|
|
31426
|
-
var localAction = (operation) => operation === "git.branch_setup" || operation === "git.commit" || operation === "git.push" || operation === "hosting.pull_request" || operation === "changelog.apply";
|
|
31820
|
+
var localAction = (operation) => operation === "git.branch_setup" || operation === "git.commit" || operation === "git.push" || operation === "hosting.pull_request" || operation === "hosting.merge" || operation === "changelog.apply";
|
|
31427
31821
|
var assertLocalExternalActionWriter = (root, caller, paths = ["."]) => {
|
|
31428
31822
|
const store = new TaskStore(root);
|
|
31429
31823
|
const listed = store.listTasks();
|
|
@@ -31434,7 +31828,7 @@ var assertLocalExternalActionWriter = (root, caller, paths = ["."]) => {
|
|
|
31434
31828
|
return workspace;
|
|
31435
31829
|
if (!workspace.data)
|
|
31436
31830
|
return failure("not_found", "workspace not found");
|
|
31437
|
-
const candidates = listed.data.filter((task) => task
|
|
31831
|
+
const candidates = listed.data.filter((task) => currentWriterOwnsTask(task, workspace.data, caller));
|
|
31438
31832
|
if (candidates.length !== 1)
|
|
31439
31833
|
return failure("permission_denied", "local external action requires exactly one active task");
|
|
31440
31834
|
const allowed = assertProductWriteAllowed({
|
|
@@ -31444,7 +31838,7 @@ var assertLocalExternalActionWriter = (root, caller, paths = ["."]) => {
|
|
|
31444
31838
|
paths,
|
|
31445
31839
|
store
|
|
31446
31840
|
});
|
|
31447
|
-
return allowed
|
|
31841
|
+
return allowed;
|
|
31448
31842
|
};
|
|
31449
31843
|
var safePath = (root, value) => {
|
|
31450
31844
|
try {
|
|
@@ -31456,13 +31850,20 @@ var safePath = (root, value) => {
|
|
|
31456
31850
|
var branchCreationBaseline = (root, target, base) => {
|
|
31457
31851
|
const exists = gitValue(root, ["rev-parse", "--verify", "--quiet", `refs/heads/${target}`]) !== null;
|
|
31458
31852
|
if (exists)
|
|
31459
|
-
return {
|
|
31853
|
+
return {
|
|
31854
|
+
base_branch: base,
|
|
31855
|
+
target_exists: true,
|
|
31856
|
+
remote_base: null,
|
|
31857
|
+
target_head: gitValue(root, ["rev-parse", `refs/heads/${target}`])
|
|
31858
|
+
};
|
|
31460
31859
|
const remote = run4(root, ["ls-remote", "origin", `refs/heads/${base}`]);
|
|
31461
31860
|
const sha = remote.exitCode === 0 ? remote.stdout.trim().split(/\s+/)[0] : "";
|
|
31861
|
+
const remoteBase = /^[0-9a-f]{40}$/.test(sha ?? "") ? sha : null;
|
|
31462
31862
|
return {
|
|
31463
31863
|
base_branch: base,
|
|
31464
31864
|
target_exists: false,
|
|
31465
|
-
remote_base:
|
|
31865
|
+
remote_base: remoteBase,
|
|
31866
|
+
target_head: remoteBase
|
|
31466
31867
|
};
|
|
31467
31868
|
};
|
|
31468
31869
|
var shortSha = (value) => typeof value === "string" && value ? value.slice(0, 8) : "unknown";
|
|
@@ -31573,7 +31974,7 @@ var actionProposalQuestion = (request, descriptorPayload) => {
|
|
|
31573
31974
|
var approvedResolvedExternalAction = (content) => {
|
|
31574
31975
|
try {
|
|
31575
31976
|
const value = JSON.parse(content);
|
|
31576
|
-
if (value.operation !== "hosting.pull_request" && value.operation !== "hosting.merge" && value.operation !== "changelog.apply" && value.operation !== "youtrack.update" && value.operation !== "youtrack.time" && value.operation !== "youtrack.meeting")
|
|
31977
|
+
if (value.operation !== "git.branch_setup" && value.operation !== "git.commit" && value.operation !== "git.push" && value.operation !== "hosting.pull_request" && value.operation !== "hosting.merge" && value.operation !== "changelog.apply" && value.operation !== "youtrack.update" && value.operation !== "youtrack.time" && value.operation !== "youtrack.meeting")
|
|
31577
31978
|
return failure("invalid_input", "approved action is not a reconciliable external action");
|
|
31578
31979
|
if (!value.payload || typeof value.payload !== "object")
|
|
31579
31980
|
return failure("invalid_input", "approved action payload is incomplete");
|
|
@@ -31584,18 +31985,24 @@ var approvedResolvedExternalAction = (content) => {
|
|
|
31584
31985
|
if (!request.ok || !resolved || typeof resolved !== "object")
|
|
31585
31986
|
return failure("invalid_input", "approved hosting target is incomplete");
|
|
31586
31987
|
const target = resolved;
|
|
31988
|
+
if (value.operation === "git.commit" && (typeof target.head !== "string" || typeof target.staged !== "string" || typeof payload.message !== "string"))
|
|
31989
|
+
return failure("invalid_input", "approved Git commit target is incomplete");
|
|
31990
|
+
if (value.operation === "git.push" && (typeof target.branch !== "string" || typeof target.commit !== "string" || typeof target.remote !== "string"))
|
|
31991
|
+
return failure("invalid_input", "approved Git push target is incomplete");
|
|
31992
|
+
if (value.operation === "git.branch_setup" && (typeof target.head !== "string" || (payload.action === "reapply_stash" ? typeof target.stash_commit !== "string" : typeof payload.target_branch !== "string" || typeof target.target_head !== "string")))
|
|
31993
|
+
return failure("invalid_input", "approved Git branch target is incomplete");
|
|
31587
31994
|
if (value.operation === "hosting.pull_request" && (typeof target.marker !== "string" || typeof target.source_branch !== "string" || typeof target.source_commit !== "string" || typeof target.remote !== "string" || typeof payload.target_branch !== "string"))
|
|
31588
31995
|
return failure("invalid_input", "approved hosting target is incomplete");
|
|
31589
31996
|
if (value.operation === "hosting.merge" && (typeof target.marker !== "string" || typeof target.source_branch !== "string" || typeof target.source_commit !== "string" || typeof target.remote !== "string" || typeof payload.target_branch !== "string"))
|
|
31590
31997
|
return failure("invalid_input", "approved hosting target is incomplete");
|
|
31591
31998
|
if (value.operation === "changelog.apply" && (typeof target.target !== "string" || typeof target.beforeDigest !== "string" || typeof target.beforeExists !== "boolean" || typeof target.afterDigest !== "string"))
|
|
31592
31999
|
return failure("invalid_input", "approved changelog target is incomplete");
|
|
31593
|
-
if (value.operation !== "hosting.pull_request" && value.operation !== "changelog.apply" && (typeof target.marker !== "string" || typeof target.baseUrl !== "string" || typeof target.issueId !== "string" || !Array.isArray(target.steps)))
|
|
32000
|
+
if (value.operation !== "git.branch_setup" && value.operation !== "git.commit" && value.operation !== "git.push" && value.operation !== "hosting.pull_request" && value.operation !== "hosting.merge" && value.operation !== "changelog.apply" && (typeof target.marker !== "string" || typeof target.baseUrl !== "string" || typeof target.issueId !== "string" || !Array.isArray(target.steps)))
|
|
31594
32001
|
return failure("invalid_input", "approved YouTrack target is incomplete");
|
|
31595
32002
|
return success(null, null, {
|
|
31596
32003
|
request: request.data,
|
|
31597
32004
|
descriptorPayload: value.payload,
|
|
31598
|
-
marker:
|
|
32005
|
+
...typeof target.marker === "string" ? { marker: target.marker } : {}
|
|
31599
32006
|
});
|
|
31600
32007
|
} catch {
|
|
31601
32008
|
return failure("invalid_input", "approved action descriptor is invalid");
|
|
@@ -31621,12 +32028,13 @@ var unknownHostingEvidence = (operation) => failure("external_outcome_unknown",
|
|
|
31621
32028
|
outcome: "unknown"
|
|
31622
32029
|
});
|
|
31623
32030
|
var readHostingAction = async (root, resolved, actionRef) => {
|
|
31624
|
-
|
|
31625
|
-
|
|
32031
|
+
const operation = resolved.request.operation;
|
|
32032
|
+
if (operation !== "hosting.pull_request" && operation !== "hosting.merge" || !resolved.marker)
|
|
32033
|
+
return unknownHostingEvidence(operation);
|
|
31626
32034
|
try {
|
|
31627
32035
|
const cfg = vcsConfig("load", root);
|
|
31628
32036
|
if (!cfg.ok || !cfg.tokenPath || !cfg.provider || cfg.provider !== "github" && cfg.provider !== "gitlab")
|
|
31629
|
-
return unknownHostingEvidence(
|
|
32037
|
+
return unknownHostingEvidence(operation);
|
|
31630
32038
|
const remote = pushRemote(root);
|
|
31631
32039
|
const repo = remote ? remoteRepo(remote) : null;
|
|
31632
32040
|
const approved = resolved.descriptorPayload;
|
|
@@ -31645,7 +32053,7 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
31645
32053
|
}
|
|
31646
32054
|
})();
|
|
31647
32055
|
if (!repo || typeof branch !== "string" || typeof target !== "string" || typeof sourceCommit !== "string" || typeof approvedRemote !== "string" || !currentIdentity || currentIdentity !== approvedIdentity || repo !== remoteRepo(approvedRemote) || remoteProvider2(approvedRemote) !== null && remoteProvider2(approvedRemote) !== cfg.provider || remoteParts(approvedRemote)?.host !== apiHost)
|
|
31648
|
-
return unknownHostingEvidence(
|
|
32056
|
+
return unknownHostingEvidence(operation);
|
|
31649
32057
|
const token = fs13.readFileSync(cfg.tokenPath, "utf8").trim();
|
|
31650
32058
|
const headers = { Authorization: `Bearer ${token}`, Accept: "application/vnd.github+json" };
|
|
31651
32059
|
const url = cfg.provider === "github" ? `https://api.github.com/repos/${repo}/pulls?state=all&head=${encodeURIComponent(`${repo.split("/")[0]}:${branch}`)}&base=${encodeURIComponent(target)}&per_page=100&page=1` : `${cfg.gitlab?.apiUrl ?? "https://gitlab.com/api/v4"}/projects/${encodeURIComponent(repo)}/merge_requests?state=all&source_branch=${encodeURIComponent(branch)}&target_branch=${encodeURIComponent(target)}&per_page=100&page=1`;
|
|
@@ -31653,14 +32061,14 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
31653
32061
|
headers: cfg.provider === "gitlab" ? { "PRIVATE-TOKEN": token } : headers
|
|
31654
32062
|
});
|
|
31655
32063
|
if (!response.ok)
|
|
31656
|
-
return unknownHostingEvidence(
|
|
32064
|
+
return unknownHostingEvidence(operation);
|
|
31657
32065
|
const values = await response.json();
|
|
31658
32066
|
if (!Array.isArray(values) || values.length >= 100)
|
|
31659
|
-
return unknownHostingEvidence(
|
|
32067
|
+
return unknownHostingEvidence(operation);
|
|
31660
32068
|
const records = [];
|
|
31661
32069
|
for (const item of values) {
|
|
31662
32070
|
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
31663
|
-
return unknownHostingEvidence(
|
|
32071
|
+
return unknownHostingEvidence(operation);
|
|
31664
32072
|
const record = item;
|
|
31665
32073
|
const body = record.body ?? record.description;
|
|
31666
32074
|
const base = record.base;
|
|
@@ -31672,7 +32080,7 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
31672
32080
|
const shaValue = cfg.provider === "github" && githubShape ? head.sha : record.sha;
|
|
31673
32081
|
const idValue = record.number ?? record.iid ?? record.id;
|
|
31674
32082
|
if (body !== undefined && body !== null && typeof body !== "string" || (cfg.provider === "github" ? !githubShape : !gitlabShape) || typeof targetValue !== "string" || typeof sourceValue !== "string" || typeof shaValue !== "string" || typeof idValue !== "string" && typeof idValue !== "number")
|
|
31675
|
-
return unknownHostingEvidence(
|
|
32083
|
+
return unknownHostingEvidence(operation);
|
|
31676
32084
|
records.push(record);
|
|
31677
32085
|
}
|
|
31678
32086
|
const matches = records.filter((item) => {
|
|
@@ -31682,15 +32090,23 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
31682
32090
|
const itemTarget = cfg.provider === "github" ? base?.ref : item.target_branch;
|
|
31683
32091
|
const itemSource = cfg.provider === "github" ? head?.ref : item.source_branch;
|
|
31684
32092
|
const itemSha = cfg.provider === "github" ? head?.sha : item.sha;
|
|
31685
|
-
|
|
32093
|
+
const merged = cfg.provider === "github" ? typeof item.merged_at === "string" : item.state === "merged" || typeof item.merged_at === "string";
|
|
32094
|
+
return (operation === "hosting.merge" ? merged : body.includes(resolved.marker)) && itemTarget === target && itemSource === branch && itemSha === sourceCommit;
|
|
31686
32095
|
});
|
|
32096
|
+
if (operation === "hosting.merge" && records.some((item) => {
|
|
32097
|
+
const base = item.base;
|
|
32098
|
+
const head = item.head;
|
|
32099
|
+
return (cfg.provider === "github" ? base?.ref : item.target_branch) === target && (cfg.provider === "github" ? head?.ref : item.source_branch) === branch && (cfg.provider === "github" ? typeof item.merged_at !== "string" : item.state !== "merged" && typeof item.merged_at !== "string");
|
|
32100
|
+
}))
|
|
32101
|
+
return unknownHostingEvidence(operation);
|
|
31687
32102
|
if (matches.length !== 1)
|
|
31688
|
-
return unknownHostingEvidence(
|
|
32103
|
+
return unknownHostingEvidence(operation);
|
|
31689
32104
|
const item = matches[0];
|
|
31690
32105
|
const id = item.number ?? item.iid ?? item.id;
|
|
31691
32106
|
if (typeof id !== "string" && typeof id !== "number")
|
|
31692
|
-
return unknownHostingEvidence(
|
|
32107
|
+
return unknownHostingEvidence(operation);
|
|
31693
32108
|
const evidenceDigest = sha256({
|
|
32109
|
+
operation,
|
|
31694
32110
|
provider: cfg.provider,
|
|
31695
32111
|
id,
|
|
31696
32112
|
marker: resolved.marker,
|
|
@@ -31711,7 +32127,7 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
31711
32127
|
data: { provider: cfg.provider, id }
|
|
31712
32128
|
});
|
|
31713
32129
|
} catch {
|
|
31714
|
-
return unknownHostingEvidence(
|
|
32130
|
+
return unknownHostingEvidence(operation);
|
|
31715
32131
|
}
|
|
31716
32132
|
};
|
|
31717
32133
|
var appendMarker = (text, marker) => `${text}${text ? `
|
|
@@ -31801,7 +32217,70 @@ var readYouTrackAction = async (root, resolved, actionRef) => {
|
|
|
31801
32217
|
return unknownHostingEvidence(operation);
|
|
31802
32218
|
}
|
|
31803
32219
|
};
|
|
31804
|
-
var
|
|
32220
|
+
var readLocalAction = async (root, resolved, actionRef) => {
|
|
32221
|
+
const operation = resolved.request.operation;
|
|
32222
|
+
const target = resolved.descriptorPayload.resolved;
|
|
32223
|
+
if (!target)
|
|
32224
|
+
return unknownHostingEvidence(operation);
|
|
32225
|
+
let observed = null;
|
|
32226
|
+
if (operation === "git.commit") {
|
|
32227
|
+
const commit = gitValue(root, ["rev-parse", "HEAD"]);
|
|
32228
|
+
const parent = commit ? gitValue(root, ["rev-parse", `${commit}^`]) : null;
|
|
32229
|
+
const message = commit ? gitValue(root, ["show", "-s", "--format=%B", commit]) : null;
|
|
32230
|
+
const diff = commit && parent ? gitRaw(root, ["diff", "--raw", "-z", "--no-ext-diff", "--no-textconv", parent, commit]) : null;
|
|
32231
|
+
if (commit && parent === target.head && message === resolved.request.payload.message && diff !== null && sha256(diff) === target.staged)
|
|
32232
|
+
observed = { commit, parent, message, staged: target.staged };
|
|
32233
|
+
} else if (operation === "git.push") {
|
|
32234
|
+
const branch = target.branch;
|
|
32235
|
+
const commit = target.commit;
|
|
32236
|
+
const approvedRemote = target.remote;
|
|
32237
|
+
const currentRemote = pushRemote(root);
|
|
32238
|
+
const currentIdentity = currentRemote ? credentialFreeRemote(currentRemote) : null;
|
|
32239
|
+
if (typeof branch === "string" && typeof commit === "string" && typeof approvedRemote === "string" && currentIdentity === approvedRemote) {
|
|
32240
|
+
const remote = run4(root, ["ls-remote", currentRemote, `refs/heads/${branch}`]);
|
|
32241
|
+
const remoteCommit = remote.exitCode === 0 ? remote.stdout.trim().split(/\s+/)[0] : null;
|
|
32242
|
+
if (remoteCommit === commit)
|
|
32243
|
+
observed = { branch, commit, remote: approvedRemote };
|
|
32244
|
+
}
|
|
32245
|
+
} else if (operation === "git.branch_setup") {
|
|
32246
|
+
const targetBranch = resolved.request.payload.target_branch;
|
|
32247
|
+
const sdd = resolved.request.payload.sdd_dir;
|
|
32248
|
+
if (resolved.request.payload.action === "reapply_stash" && typeof sdd === "string") {
|
|
32249
|
+
try {
|
|
32250
|
+
const manifest = JSON.parse(fs13.readFileSync(path25.join(sdd, "manifest.json"), "utf8"));
|
|
32251
|
+
const stashes = gitRaw(root, ["stash", "list", "--format=%H"]);
|
|
32252
|
+
if (typeof target.stash_commit === "string" && manifest.stash_ref === undefined && stashes !== null && !stashes.split(`
|
|
32253
|
+
`).includes(target.stash_commit))
|
|
32254
|
+
observed = { stash_commit: target.stash_commit };
|
|
32255
|
+
} catch {}
|
|
32256
|
+
} else if (typeof targetBranch === "string" && typeof sdd === "string" && gitValue(root, ["branch", "--show-current"]) === targetBranch && typeof target.target_head === "string" && gitValue(root, ["rev-parse", "HEAD"]) === target.target_head) {
|
|
32257
|
+
try {
|
|
32258
|
+
const manifest = JSON.parse(fs13.readFileSync(path25.join(sdd, "manifest.json"), "utf8"));
|
|
32259
|
+
if (manifest.branch === targetBranch)
|
|
32260
|
+
observed = { branch: targetBranch };
|
|
32261
|
+
} catch {}
|
|
32262
|
+
}
|
|
32263
|
+
} else if (operation === "changelog.apply") {
|
|
32264
|
+
const afterDigest = target.afterDigest;
|
|
32265
|
+
const preview = changelogApplyPreview({ ...resolved.request.payload, workspace_root: root });
|
|
32266
|
+
if (!preview.error && typeof afterDigest === "string" && preview.beforeDigest === afterDigest)
|
|
32267
|
+
observed = { target: target.target, afterDigest };
|
|
32268
|
+
}
|
|
32269
|
+
if (!observed)
|
|
32270
|
+
return unknownHostingEvidence(operation);
|
|
32271
|
+
const evidenceDigest = sha256({ operation, observed });
|
|
32272
|
+
return success(null, null, {
|
|
32273
|
+
outcome: "succeeded",
|
|
32274
|
+
evidenceDigest,
|
|
32275
|
+
observation: {
|
|
32276
|
+
kind: "provider_read",
|
|
32277
|
+
actionRef,
|
|
32278
|
+
outcome: "succeeded",
|
|
32279
|
+
evidenceDigest
|
|
32280
|
+
}
|
|
32281
|
+
});
|
|
32282
|
+
};
|
|
32283
|
+
var readExternalAction = async (root, resolved, actionRef) => resolved.request.operation === "git.branch_setup" || resolved.request.operation === "git.commit" || resolved.request.operation === "git.push" || resolved.request.operation === "changelog.apply" ? readLocalAction(root, resolved, actionRef) : resolved.request.operation === "hosting.pull_request" || resolved.request.operation === "hosting.merge" ? readHostingAction(root, resolved, actionRef) : readYouTrackAction(root, resolved, actionRef);
|
|
31805
32284
|
var youTrackBase = () => {
|
|
31806
32285
|
try {
|
|
31807
32286
|
const config = JSON.parse(fs13.readFileSync(youTrackConfigPath(), "utf8"));
|
|
@@ -31859,6 +32338,16 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
31859
32338
|
return failure("storage_error", "current Git commit could not be resolved");
|
|
31860
32339
|
const dirty = Boolean((gitRaw(root, ["status", "--short"]) ?? "").trim());
|
|
31861
32340
|
const creation = action === "reapply_stash" || !target_branch ? { base_branch: policy.defaultTargetBranch } : branchCreationBaseline(root, target_branch, policy.defaultTargetBranch);
|
|
32341
|
+
let stashTarget;
|
|
32342
|
+
if (action === "reapply_stash") {
|
|
32343
|
+
try {
|
|
32344
|
+
const manifest = JSON.parse(fs13.readFileSync(path25.join(sdd_dir, "manifest.json"), "utf8"));
|
|
32345
|
+
const stash_ref = typeof manifest.stash_ref === "string" ? manifest.stash_ref : "";
|
|
32346
|
+
const stash_commit = stash_ref ? gitValue(root, ["rev-parse", stash_ref]) : null;
|
|
32347
|
+
if (stash_ref && stash_commit)
|
|
32348
|
+
stashTarget = { stash_ref, stash_commit };
|
|
32349
|
+
} catch {}
|
|
32350
|
+
}
|
|
31862
32351
|
const normalized = {
|
|
31863
32352
|
operation: request.operation,
|
|
31864
32353
|
payload: {
|
|
@@ -31873,7 +32362,7 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
31873
32362
|
request: normalized,
|
|
31874
32363
|
descriptorPayload: {
|
|
31875
32364
|
...normalized.payload,
|
|
31876
|
-
resolved: { head, dirty, ...creation }
|
|
32365
|
+
resolved: { head, dirty, ...creation, ...stashTarget }
|
|
31877
32366
|
}
|
|
31878
32367
|
});
|
|
31879
32368
|
}
|
|
@@ -32025,7 +32514,7 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
32025
32514
|
outcome: "not_started",
|
|
32026
32515
|
fields: [{ path: "source_branch", reason: "protected branch" }]
|
|
32027
32516
|
});
|
|
32028
|
-
const source_commit = gitValue(root, ["rev-parse", "
|
|
32517
|
+
const source_commit = source_branch ? gitValue(root, ["rev-parse", "--verify", `refs/heads/${source_branch}`]) : null;
|
|
32029
32518
|
const remote = pushRemote(root);
|
|
32030
32519
|
const identity = remote ? credentialFreeRemote(remote) : null;
|
|
32031
32520
|
if (!source_branch || !source_commit || !identity || remoteProvider2(identity) !== null && remoteProvider2(identity) !== String(vcsConfig("load", root).provider).toLowerCase())
|
|
@@ -32060,7 +32549,7 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
32060
32549
|
if (preview.error)
|
|
32061
32550
|
return failure("invalid_input", preview.error);
|
|
32062
32551
|
const target = String(preview.path);
|
|
32063
|
-
const relative =
|
|
32552
|
+
const relative = path25.relative(fs13.realpathSync(root), target).replaceAll(path25.sep, "/") || "CHANGELOG.md";
|
|
32064
32553
|
const normalized = {
|
|
32065
32554
|
operation: request.operation,
|
|
32066
32555
|
payload: {
|
|
@@ -32313,27 +32802,34 @@ var executeResolvedExternalAction = async (resolved, root, step, caller) => {
|
|
|
32313
32802
|
const workText = fresh.data.descriptorPayload.resolved?.workText;
|
|
32314
32803
|
const beforeDigest = typeof resolvedDetails.resolved?.beforeDigest === "string" ? resolvedDetails.resolved.beforeDigest : undefined;
|
|
32315
32804
|
const beforeExists = typeof resolvedDetails.resolved?.beforeExists === "boolean" ? resolvedDetails.resolved.beforeExists : undefined;
|
|
32316
|
-
|
|
32805
|
+
const result = await executeConcreteExternalAction(fresh.data.request, root, fresh.data.marker, resolvedDate, step, typeof workText === "string" ? workText : undefined, caller, beforeDigest, beforeExists);
|
|
32806
|
+
if (!result.ok && result.code === "external_outcome_unknown" && caller) {
|
|
32807
|
+
const observed = await readLocalAction(root, fresh.data, {
|
|
32808
|
+
kind: "host",
|
|
32809
|
+
host: caller.host,
|
|
32810
|
+
handle: `local-recovery:${fresh.data.request.operation}`
|
|
32811
|
+
});
|
|
32812
|
+
if (observed.ok)
|
|
32813
|
+
return success(null, null, { recovered: true });
|
|
32814
|
+
}
|
|
32815
|
+
return result;
|
|
32317
32816
|
};
|
|
32318
32817
|
|
|
32319
|
-
// packages/workit-opencode/src/
|
|
32320
|
-
|
|
32321
|
-
|
|
32322
|
-
|
|
32323
|
-
if (
|
|
32324
|
-
return;
|
|
32325
|
-
const parentID = session.parentID;
|
|
32326
|
-
return typeof parentID === "string" && parentID.length > 0 ? parentID : null;
|
|
32327
|
-
};
|
|
32328
|
-
var rejectedDescription = "Reject this decision";
|
|
32329
|
-
var isSelfAuthorizingActionContent = (content) => {
|
|
32818
|
+
// packages/workit-opencode/src/shared/session.ts
|
|
32819
|
+
import { realpathSync as realpathSync10 } from "node:fs";
|
|
32820
|
+
import path26 from "node:path";
|
|
32821
|
+
var sameWorkspace = (expected, observed) => {
|
|
32822
|
+
if (typeof observed !== "string" || !observed)
|
|
32823
|
+
return false;
|
|
32330
32824
|
try {
|
|
32331
|
-
|
|
32332
|
-
return typeof value.operation === "string" && value.operation.length > 0;
|
|
32825
|
+
return realpathSync10(expected) === realpathSync10(observed);
|
|
32333
32826
|
} catch {
|
|
32334
|
-
return
|
|
32827
|
+
return path26.resolve(expected) === path26.resolve(observed);
|
|
32335
32828
|
}
|
|
32336
32829
|
};
|
|
32830
|
+
|
|
32831
|
+
// packages/workit-opencode/src/shared/decision-content.ts
|
|
32832
|
+
var rejectedDescription = "Reject this decision";
|
|
32337
32833
|
var decisionContent = (purpose, question, approvedContent) => ({
|
|
32338
32834
|
header: `Workit decision: ${purpose}`,
|
|
32339
32835
|
question,
|
|
@@ -32342,6 +32838,24 @@ var decisionContent = (purpose, question, approvedContent) => ({
|
|
|
32342
32838
|
{ label: "rejected", description: rejectedDescription }
|
|
32343
32839
|
]
|
|
32344
32840
|
});
|
|
32841
|
+
var isSelfAuthorizingActionContent = (content) => {
|
|
32842
|
+
try {
|
|
32843
|
+
const value = JSON.parse(content);
|
|
32844
|
+
return typeof value.operation === "string" && value.operation.length > 0;
|
|
32845
|
+
} catch {
|
|
32846
|
+
return /^[a-z][a-z_]*\.[a-z_]+$/.test(content);
|
|
32847
|
+
}
|
|
32848
|
+
};
|
|
32849
|
+
|
|
32850
|
+
// packages/workit-opencode/src/tools/workit.ts
|
|
32851
|
+
var sessionParent = (session) => {
|
|
32852
|
+
if (typeof session !== "object" || session === null)
|
|
32853
|
+
return null;
|
|
32854
|
+
if (!Object.prototype.hasOwnProperty.call(session, "parentID"))
|
|
32855
|
+
return;
|
|
32856
|
+
const parentID = session.parentID;
|
|
32857
|
+
return typeof parentID === "string" && parentID.length > 0 ? parentID : null;
|
|
32858
|
+
};
|
|
32345
32859
|
var decisionOptions = (options) => Array.isArray(options) && options.length === 2 && options.every((option) => typeof option === "object" && option !== null && typeof option.label === "string" && typeof option.description === "string" && Object.keys(option).length === 2 && Object.keys(option).every((key) => key === "label" || key === "description")) ? options : null;
|
|
32346
32860
|
var normalizeReceiptLabel = (label) => label.trim().replace(/(?:\s*[([]\s*[^()[\]]*\s*[)\]]\s*)+$/u, "").trim().toLowerCase();
|
|
32347
32861
|
var purposeForQuestion = (question) => {
|
|
@@ -32356,6 +32870,7 @@ var purposeForQuestion = (question) => {
|
|
|
32356
32870
|
class NativeReceiptStore {
|
|
32357
32871
|
#receipts = new Map;
|
|
32358
32872
|
#observations = new WeakSet;
|
|
32873
|
+
#reservations = new WeakMap;
|
|
32359
32874
|
#pending = new Map;
|
|
32360
32875
|
#now;
|
|
32361
32876
|
constructor(options = {}) {
|
|
@@ -32428,7 +32943,7 @@ class NativeReceiptStore {
|
|
|
32428
32943
|
const args = input.args;
|
|
32429
32944
|
this.mint(input.sessionID, input.callID, args?.questions, answers);
|
|
32430
32945
|
}
|
|
32431
|
-
|
|
32946
|
+
reserve(sessionID, purpose, expected = {}) {
|
|
32432
32947
|
const queue = this.#receipts.get(sessionID) ?? [];
|
|
32433
32948
|
for (let i = queue.length - 1;i >= 0; i -= 1) {
|
|
32434
32949
|
const receipt = queue[i];
|
|
@@ -32436,11 +32951,9 @@ class NativeReceiptStore {
|
|
|
32436
32951
|
continue;
|
|
32437
32952
|
if (expected.callID !== undefined && receipt.callID !== expected.callID || expected.selectedLabel !== undefined && normalizeReceiptLabel(receipt.selectedLabel) !== normalizeReceiptLabel(expected.selectedLabel) || expected.selectedDescription !== undefined && receipt.selectedDescription !== expected.selectedDescription || expected.decisionPurpose !== undefined && receipt.decisionPurpose !== expected.decisionPurpose || expected.contentDigest !== undefined && receipt.contentDigest !== expected.contentDigest || expected.question !== undefined && receipt.question !== expected.question)
|
|
32438
32953
|
continue;
|
|
32439
|
-
queue.splice(i, 1);
|
|
32440
|
-
if (!queue.length)
|
|
32441
|
-
this.#receipts.delete(sessionID);
|
|
32442
32954
|
const observation = { receipt };
|
|
32443
32955
|
this.#observations.add(observation);
|
|
32956
|
+
this.#reservations.set(observation, receipt);
|
|
32444
32957
|
return { ok: true, receipt, observation };
|
|
32445
32958
|
}
|
|
32446
32959
|
return {
|
|
@@ -32466,6 +32979,26 @@ class NativeReceiptStore {
|
|
|
32466
32979
|
})()
|
|
32467
32980
|
};
|
|
32468
32981
|
}
|
|
32982
|
+
commit(observation) {
|
|
32983
|
+
const receipt = this.#reservations.get(observation);
|
|
32984
|
+
this.#reservations.delete(observation);
|
|
32985
|
+
if (!receipt)
|
|
32986
|
+
return false;
|
|
32987
|
+
const queue = this.#receipts.get(receipt.sessionID) ?? [];
|
|
32988
|
+
const index = queue.indexOf(receipt);
|
|
32989
|
+
if (index < 0)
|
|
32990
|
+
return false;
|
|
32991
|
+
queue.splice(index, 1);
|
|
32992
|
+
if (!queue.length)
|
|
32993
|
+
this.#receipts.delete(receipt.sessionID);
|
|
32994
|
+
return true;
|
|
32995
|
+
}
|
|
32996
|
+
consume(sessionID, purpose, expected = {}) {
|
|
32997
|
+
const reserved = this.reserve(sessionID, purpose, expected);
|
|
32998
|
+
if (reserved.ok)
|
|
32999
|
+
this.commit(reserved.observation);
|
|
33000
|
+
return reserved;
|
|
33001
|
+
}
|
|
32469
33002
|
verify(observation, sessionID, purpose) {
|
|
32470
33003
|
if (typeof observation !== "object" || observation === null || !this.#observations.has(observation))
|
|
32471
33004
|
return null;
|
|
@@ -32540,15 +33073,6 @@ var sessionData = async (client, sessionID) => {
|
|
|
32540
33073
|
return null;
|
|
32541
33074
|
}
|
|
32542
33075
|
};
|
|
32543
|
-
var sameWorkspace = (expected, observed) => {
|
|
32544
|
-
if (typeof observed !== "string" || !observed)
|
|
32545
|
-
return false;
|
|
32546
|
-
try {
|
|
32547
|
-
return realpathSync9(expected) === realpathSync9(observed);
|
|
32548
|
-
} catch {
|
|
32549
|
-
return path24.resolve(expected) === path24.resolve(observed);
|
|
32550
|
-
}
|
|
32551
|
-
};
|
|
32552
33076
|
var hostRef = (handle) => ({ kind: "host", host: "opencode", handle });
|
|
32553
33077
|
var opencodeCapabilities = () => [
|
|
32554
33078
|
{
|
|
@@ -32717,6 +33241,10 @@ var nativeExternalActionRunner = (root, actor, core, step) => createAuthorizedEx
|
|
|
32717
33241
|
settleObservation: (outcome, revisions) => nativeExternalActionObservation(actor, actionRef, outcome, actionRef.kind === "host" ? actionRef.handle : "external-action", revisions)
|
|
32718
33242
|
};
|
|
32719
33243
|
});
|
|
33244
|
+
var workerCoordinatorFor = (task, worker) => {
|
|
33245
|
+
const coordinator = worker.data.coordinator ?? task.intent.provenance.session;
|
|
33246
|
+
return coordinator?.kind === "host" && coordinator.host === "opencode" ? coordinator.handle : null;
|
|
33247
|
+
};
|
|
32720
33248
|
var workerIdFor = (store, actor, current, directChildren) => {
|
|
32721
33249
|
const parentID = sessionParent(current);
|
|
32722
33250
|
if (!parentID)
|
|
@@ -32727,10 +33255,9 @@ var workerIdFor = (store, actor, current, directChildren) => {
|
|
|
32727
33255
|
const matches = tasks.data.flatMap((task) => {
|
|
32728
33256
|
if (task.status !== "active")
|
|
32729
33257
|
return [];
|
|
32730
|
-
|
|
32731
|
-
if (coordinator?.kind !== "host" || coordinator.host !== "opencode" || coordinator.handle !== parentID || directChildren.get(actor) !== parentID)
|
|
33258
|
+
if (directChildren.get(actor) !== parentID)
|
|
32732
33259
|
return [];
|
|
32733
|
-
return task.workers.filter((worker) => worker.data.state === "running" && worker.data.session?.kind === "host" && worker.data.session.host === "opencode" && worker.data.session.handle === actor);
|
|
33260
|
+
return task.workers.filter((worker) => worker.data.state === "running" && worker.data.session?.kind === "host" && worker.data.session.host === "opencode" && worker.data.session.handle === actor && workerCoordinatorFor(task, worker) === parentID);
|
|
32734
33261
|
});
|
|
32735
33262
|
return matches.length === 1 ? matches[0].id : null;
|
|
32736
33263
|
};
|
|
@@ -32790,10 +33317,11 @@ var createWorkitTools = ({
|
|
|
32790
33317
|
};
|
|
32791
33318
|
if (decision.response === "approved")
|
|
32792
33319
|
expectation.selectedDescription = decision.binding.approvedContent;
|
|
32793
|
-
const observed = receipts.
|
|
33320
|
+
const observed = receipts.reserve(context.sessionID, "decision", expectation);
|
|
32794
33321
|
if (!observed.ok)
|
|
32795
33322
|
return output2(failure("permission_denied", observed.error));
|
|
32796
33323
|
let recordInput = parsed.data;
|
|
33324
|
+
let proposalToCommit;
|
|
32797
33325
|
if (decision.purpose === "action" && decision.response === "approved") {
|
|
32798
33326
|
const queue = actionProposals.get(context.sessionID) ?? [];
|
|
32799
33327
|
const textMatches = queue.filter((pending) => pending.presented === decision.binding.presented && pending.approvedText === decision.binding.approvedContent);
|
|
@@ -32805,7 +33333,7 @@ var createWorkitTools = ({
|
|
|
32805
33333
|
const fresh = resolveExternalActionRequest(context.directory, pending.request);
|
|
32806
33334
|
const freshDescriptor = fresh.ok ? externalActionDescriptor(pending.request.operation, fresh.data.descriptorPayload) : null;
|
|
32807
33335
|
if (freshDescriptor === pending.descriptor) {
|
|
32808
|
-
|
|
33336
|
+
proposalToCommit = pending;
|
|
32809
33337
|
recordInput = {
|
|
32810
33338
|
...recordInput,
|
|
32811
33339
|
binding: {
|
|
@@ -32824,6 +33352,13 @@ var createWorkitTools = ({
|
|
|
32824
33352
|
}
|
|
32825
33353
|
}
|
|
32826
33354
|
result = core.observeDecision(recordInput, observed.observation);
|
|
33355
|
+
if (result.ok) {
|
|
33356
|
+
receipts.commit(observed.observation);
|
|
33357
|
+
if (proposalToCommit) {
|
|
33358
|
+
const queue = actionProposals.get(context.sessionID) ?? [];
|
|
33359
|
+
actionProposals.set(context.sessionID, queue.filter((candidate) => candidate !== proposalToCommit));
|
|
33360
|
+
}
|
|
33361
|
+
}
|
|
32827
33362
|
} else {
|
|
32828
33363
|
const run = core[family];
|
|
32829
33364
|
result = run.call(core, parsed.data);
|
|
@@ -32879,7 +33414,7 @@ var createWorkitTools = ({
|
|
|
32879
33414
|
return output2(failure("permission_denied", "child sessions cannot run external actions"));
|
|
32880
33415
|
const store = new TaskStore(context.directory);
|
|
32881
33416
|
const prior = priorExternalAction(store, "opencode", context.sessionID, resolved.data.request.operation, resolved.data.request.payload);
|
|
32882
|
-
if (prior.ok && prior.data.entry.data.consumption !== null && prior.data.entry.data.consumption.state !== "uncertain")
|
|
33417
|
+
if (prior.ok && prior.data.entry.data.consumption !== null && prior.data.entry.data.consumption.state !== "uncertain" && resolved.data.request.operation !== "git.commit" && resolved.data.request.operation !== "git.push")
|
|
32883
33418
|
return output2(failure("permission_denied", "external action was already settled"));
|
|
32884
33419
|
const reconciliationTokens = new WeakSet;
|
|
32885
33420
|
const core = new WorkitCore(store, {
|
|
@@ -32962,6 +33497,16 @@ var createWorkitTools = ({
|
|
|
32962
33497
|
}
|
|
32963
33498
|
}
|
|
32964
33499
|
const autoApplies = !selected.ok && !planAuthorized && standingAutoApplies(store, "opencode", context.sessionID, descriptor);
|
|
33500
|
+
const planCount = planReservationLength(resolved.data.request.operation, resolved.data.descriptorPayload);
|
|
33501
|
+
if (planCount !== null) {
|
|
33502
|
+
if (selected.ok)
|
|
33503
|
+
return output2(success(null, null, { plan_commits: planCount }));
|
|
33504
|
+
if (autoApplies) {
|
|
33505
|
+
const bound = standingAutoBinding(core, store, "opencode", context.sessionID, descriptor);
|
|
33506
|
+
if (bound)
|
|
33507
|
+
return output2(success(null, null, { plan_commits: planCount }));
|
|
33508
|
+
}
|
|
33509
|
+
}
|
|
32965
33510
|
if (!selected.ok && !planAuthorized && !autoApplies) {
|
|
32966
33511
|
if (selected.error.startsWith("no approved action")) {
|
|
32967
33512
|
const proposal = actionProposalQuestion(resolved.data.request, resolved.data.descriptorPayload);
|
|
@@ -33090,9 +33635,9 @@ var loadProvenance = (logger, pkgUrl) => {
|
|
|
33090
33635
|
}
|
|
33091
33636
|
};
|
|
33092
33637
|
|
|
33093
|
-
// packages/workit-opencode/src/
|
|
33094
|
-
var root =
|
|
33095
|
-
var skillsPath =
|
|
33638
|
+
// packages/workit-opencode/src/v1/server.ts
|
|
33639
|
+
var root = assetsRoot();
|
|
33640
|
+
var skillsPath = path27.join(root, "skills");
|
|
33096
33641
|
var logger = createLogger({
|
|
33097
33642
|
appLog: () => {
|
|
33098
33643
|
return;
|
|
@@ -33111,7 +33656,7 @@ var warnStaleSources = () => {
|
|
|
33111
33656
|
logger.warn(EVENT.hooks, {
|
|
33112
33657
|
boundary: "stale_sources",
|
|
33113
33658
|
reason: "workit sources changed after plugin load; restart the session for latest behavior",
|
|
33114
|
-
files: changed.map((file) =>
|
|
33659
|
+
files: changed.map((file) => path27.basename(file))
|
|
33115
33660
|
});
|
|
33116
33661
|
} catch {}
|
|
33117
33662
|
};
|
|
@@ -33140,7 +33685,7 @@ var plugin = async ({ client, directory }) => {
|
|
|
33140
33685
|
const dispatches = new Map;
|
|
33141
33686
|
try {
|
|
33142
33687
|
logger.info(EVENT.initialization, { host: "opencode", plugin_root: root });
|
|
33143
|
-
logger.info(EVENT.provenance, loadProvenance(logger,
|
|
33688
|
+
logger.info(EVENT.provenance, loadProvenance(logger, path27.join(packageRoot2(), "package.json")));
|
|
33144
33689
|
} catch (error) {
|
|
33145
33690
|
logger.warn(EVENT.hooks, { boundary: "initialization", ...errorDetail(error) });
|
|
33146
33691
|
}
|
|
@@ -33172,10 +33717,10 @@ var plugin = async ({ client, directory }) => {
|
|
|
33172
33717
|
const listed = store.listTasks();
|
|
33173
33718
|
if (!workspace.ok || !workspace.data || !listed.ok)
|
|
33174
33719
|
return "unmanaged";
|
|
33175
|
-
const
|
|
33176
|
-
if (
|
|
33720
|
+
const managed = listed.data.filter((task) => task.status === "active" && task.workspaceId === workspace.data?.id && (task.intent.provenance.session?.kind === "host" && task.intent.provenance.session.host === "opencode" && task.intent.provenance.session.handle === coordinator || task.workers.some((entry) => entry.provenance.session?.kind === "host" && entry.provenance.session.host === "opencode" && entry.provenance.session.handle === coordinator) || workspace.data.writer?.owner.taskId === task.id && workspace.data.writer.owner.session.host === "opencode" && workspace.data.writer.owner.session.handle === coordinator));
|
|
33721
|
+
if (managed.length === 0)
|
|
33177
33722
|
return "unmanaged";
|
|
33178
|
-
const eligible =
|
|
33723
|
+
const eligible = managed.flatMap((task) => task.workers.filter((entry) => entry.data.state === "assigned" && entry.data.session === null && entry.provenance.session?.kind === "host" && entry.provenance.session.host === "opencode" && entry.provenance.session.handle === coordinator).map((entry) => ({ task, entry })));
|
|
33179
33724
|
const next = oldestOfSingleTask(eligible);
|
|
33180
33725
|
if (!next)
|
|
33181
33726
|
return "unbound";
|
|
@@ -33259,7 +33804,7 @@ var plugin = async ({ client, directory }) => {
|
|
|
33259
33804
|
dispatches.delete(coordinator);
|
|
33260
33805
|
return committed.ok;
|
|
33261
33806
|
};
|
|
33262
|
-
const observeLifecycle = async (sessionID, parentID, state, binding,
|
|
33807
|
+
const observeLifecycle = async (sessionID, parentID, state, binding, eventInfo, report) => {
|
|
33263
33808
|
const store = new TaskStore(directory);
|
|
33264
33809
|
const listed = store.listTasks();
|
|
33265
33810
|
const workspace = store.readWorkspace();
|
|
@@ -33269,7 +33814,7 @@ var plugin = async ({ client, directory }) => {
|
|
|
33269
33814
|
}
|
|
33270
33815
|
const persisted = listed.data.flatMap((task) => task.status === "active" ? task.workers.filter((entry) => entry.id === binding?.workerId || entry.data.session?.kind === "host" && entry.data.session.handle === sessionID).map((entry) => ({ task, entry })) : []);
|
|
33271
33816
|
const matches = persisted.filter(({ entry }) => entry.data.session?.kind === "host");
|
|
33272
|
-
if (binding &&
|
|
33817
|
+
if (binding && matches.length !== 1) {
|
|
33273
33818
|
droppedLifecycle(sessionID, "bound child worker handle is ambiguous");
|
|
33274
33819
|
return;
|
|
33275
33820
|
}
|
|
@@ -33278,7 +33823,7 @@ var plugin = async ({ client, directory }) => {
|
|
|
33278
33823
|
droppedLifecycle(sessionID, binding ? "bound worker is missing from persisted state" : "worker session is ambiguous");
|
|
33279
33824
|
return;
|
|
33280
33825
|
}
|
|
33281
|
-
if (
|
|
33826
|
+
if (selected.entry.data.session?.kind !== "host") {
|
|
33282
33827
|
droppedLifecycle(sessionID, "selected worker has no bound host session");
|
|
33283
33828
|
return;
|
|
33284
33829
|
}
|
|
@@ -33286,25 +33831,26 @@ var plugin = async ({ client, directory }) => {
|
|
|
33286
33831
|
droppedLifecycle(sessionID, "selected worker provenance is not host-observed");
|
|
33287
33832
|
return;
|
|
33288
33833
|
}
|
|
33289
|
-
|
|
33290
|
-
|
|
33291
|
-
if (
|
|
33292
|
-
droppedLifecycle(sessionID, "
|
|
33834
|
+
const observed = eventInfo ?? await sessionData2(client, sessionID);
|
|
33835
|
+
if (binding && eventInfo) {
|
|
33836
|
+
if (!deletedSessionEnd(directory, sessionID, parentID, observed)) {
|
|
33837
|
+
droppedLifecycle(sessionID, "deleted session payload contradicts the binding");
|
|
33293
33838
|
return;
|
|
33294
33839
|
}
|
|
33295
|
-
|
|
33840
|
+
} else if (!trustedSession(directory, sessionID, observed)) {
|
|
33841
|
+
droppedLifecycle(sessionID, "live session observation is not trusted");
|
|
33842
|
+
return;
|
|
33296
33843
|
}
|
|
33297
|
-
if (!
|
|
33298
|
-
const
|
|
33299
|
-
if (
|
|
33300
|
-
|
|
33301
|
-
droppedLifecycle(sessionID, "deleted session payload contradicts the binding");
|
|
33302
|
-
return;
|
|
33303
|
-
}
|
|
33304
|
-
} else if (!trustedSession(directory, sessionID, observed) || observed.parentID !== parentID) {
|
|
33305
|
-
droppedLifecycle(sessionID, "live session observation is not trusted");
|
|
33844
|
+
if (!binding) {
|
|
33845
|
+
const coordinator = workerCoordinatorFor(selected.task, selected.entry);
|
|
33846
|
+
if (!coordinator || observed.parentID !== coordinator) {
|
|
33847
|
+
droppedLifecycle(sessionID, "live session parent contradicts persisted coordinator");
|
|
33306
33848
|
return;
|
|
33307
33849
|
}
|
|
33850
|
+
parentID = coordinator;
|
|
33851
|
+
} else if (!eventInfo && observed.parentID !== parentID) {
|
|
33852
|
+
droppedLifecycle(sessionID, "live session parent contradicts the binding");
|
|
33853
|
+
return;
|
|
33308
33854
|
}
|
|
33309
33855
|
directChildren.set(sessionID, parentID);
|
|
33310
33856
|
const core = new WorkitCore(store, {
|
|
@@ -33322,6 +33868,7 @@ var plugin = async ({ client, directory }) => {
|
|
|
33322
33868
|
expectedWorkspaceRevision: workspace.data.revision,
|
|
33323
33869
|
state,
|
|
33324
33870
|
session: { kind: "host", host: "opencode", handle: sessionID },
|
|
33871
|
+
...report ? { report } : {},
|
|
33325
33872
|
observation: { event: state, sessionID }
|
|
33326
33873
|
});
|
|
33327
33874
|
if (!result.ok) {
|
|
@@ -33337,25 +33884,9 @@ var plugin = async ({ client, directory }) => {
|
|
|
33337
33884
|
const bindCreatedSession = async (info) => {
|
|
33338
33885
|
if (typeof info.id !== "string" || typeof info.parentID !== "string" || !sameWorkspace(directory, info.directory))
|
|
33339
33886
|
return;
|
|
33340
|
-
const live = dispatches.get(info.parentID);
|
|
33341
|
-
if (live)
|
|
33342
|
-
live.generation.childCreated = true;
|
|
33343
|
-
const store = new TaskStore(directory);
|
|
33344
|
-
const workspace = store.readWorkspace();
|
|
33345
|
-
const listed = store.listTasks();
|
|
33346
|
-
if (!workspace.ok || !workspace.data || !listed.ok)
|
|
33347
|
-
return;
|
|
33348
|
-
const candidates = listed.data.flatMap((task) => task.status === "active" && task.workspaceId === workspace.data?.id ? task.workers.filter((entry) => (entry.data.state === "assigned" || entry.data.state === "dispatching") && entry.data.session === null && entry.provenance.session?.kind === "host" && entry.provenance.session.host === "opencode" && entry.provenance.session.handle === info.parentID).map((entry) => ({ task, entry })) : []);
|
|
33349
|
-
const next = oldestOfSingleTask(candidates);
|
|
33350
|
-
if (!next)
|
|
33351
|
-
return;
|
|
33352
33887
|
directChildren.set(info.id, info.parentID);
|
|
33353
|
-
if (
|
|
33354
|
-
|
|
33355
|
-
await observeLifecycle(info.id, info.parentID, "running", {
|
|
33356
|
-
taskId: next.task.id,
|
|
33357
|
-
workerId: next.entry.id
|
|
33358
|
-
}, true);
|
|
33888
|
+
if (dispatches.has(info.parentID))
|
|
33889
|
+
commitDispatchStart(info.parentID, info.id);
|
|
33359
33890
|
};
|
|
33360
33891
|
return {
|
|
33361
33892
|
tool: tools,
|
|
@@ -33377,7 +33908,7 @@ var plugin = async ({ client, directory }) => {
|
|
|
33377
33908
|
return;
|
|
33378
33909
|
const binding = lifecycleBindings.get(sessionID);
|
|
33379
33910
|
const state = event.type === "session.status" ? event.properties.status.type === "busy" ? "running" : event.properties.status.type === "idle" ? "stopped" : "unknown" : event.type === "session.idle" ? "stopped" : event.type === "session.deleted" ? "stopped" : "unknown";
|
|
33380
|
-
await observeLifecycle(sessionID, binding?.parentID ?? "", state, binding,
|
|
33911
|
+
await observeLifecycle(sessionID, binding?.parentID ?? "", state, binding, event.type === "session.deleted" ? properties.info : undefined);
|
|
33381
33912
|
},
|
|
33382
33913
|
"tool.execute.after": async (input, output) => {
|
|
33383
33914
|
observeQuestion(receipts, input, output);
|
|
@@ -33403,7 +33934,12 @@ var plugin = async ({ client, directory }) => {
|
|
|
33403
33934
|
if (completedChild) {
|
|
33404
33935
|
const completionBinding = lifecycleBindings.get(completedChild);
|
|
33405
33936
|
if (completionBinding)
|
|
33406
|
-
await observeLifecycle(completedChild, input.sessionID, "stopped", completionBinding,
|
|
33937
|
+
await observeLifecycle(completedChild, input.sessionID, "stopped", completionBinding, undefined, {
|
|
33938
|
+
outcome: "completed",
|
|
33939
|
+
summary: outputText.match(/<task_result>([\s\S]*?)<\/task_result>/)?.[1]?.trim() || outputText.trim() || "Native worker completed.",
|
|
33940
|
+
evidenceIds: [],
|
|
33941
|
+
findingIds: []
|
|
33942
|
+
});
|
|
33407
33943
|
} else if (!resolved && /(?:cancel|interrupt|unknown|uncertain)/.test(state))
|
|
33408
33944
|
unresolvedTaskLaunches.add(input.sessionID);
|
|
33409
33945
|
}
|
|
@@ -33442,7 +33978,7 @@ var plugin = async ({ client, directory }) => {
|
|
|
33442
33978
|
config: async (config) => {
|
|
33443
33979
|
const mutable = config;
|
|
33444
33980
|
const paths = [...mutable.skills?.paths ?? []];
|
|
33445
|
-
if (
|
|
33981
|
+
if (existsSync14(skillsPath) && !paths.includes(skillsPath))
|
|
33446
33982
|
paths.push(skillsPath);
|
|
33447
33983
|
mutable.skills ??= {};
|
|
33448
33984
|
mutable.skills.paths = paths;
|
|
@@ -33512,7 +34048,852 @@ var plugin = async ({ client, directory }) => {
|
|
|
33512
34048
|
}
|
|
33513
34049
|
};
|
|
33514
34050
|
};
|
|
33515
|
-
var
|
|
34051
|
+
var server_default = plugin;
|
|
34052
|
+
|
|
34053
|
+
// node_modules/@opencode/plugin/dist/promise/plugin.js
|
|
34054
|
+
function define(plugin) {
|
|
34055
|
+
return plugin;
|
|
34056
|
+
}
|
|
34057
|
+
|
|
34058
|
+
// packages/workit-opencode/src/shared/tools.ts
|
|
34059
|
+
var objectSchema = (properties, required) => ({
|
|
34060
|
+
type: "object",
|
|
34061
|
+
properties,
|
|
34062
|
+
required,
|
|
34063
|
+
additionalProperties: false
|
|
34064
|
+
});
|
|
34065
|
+
var WORKIT_EXTERNAL_ACTION_OPERATIONS = [
|
|
34066
|
+
"git.branch_setup",
|
|
34067
|
+
"git.commit",
|
|
34068
|
+
"git.push",
|
|
34069
|
+
"hosting.pull_request",
|
|
34070
|
+
"hosting.merge",
|
|
34071
|
+
"youtrack.update",
|
|
34072
|
+
"youtrack.time",
|
|
34073
|
+
"youtrack.meeting",
|
|
34074
|
+
"changelog.apply",
|
|
34075
|
+
"context.read"
|
|
34076
|
+
];
|
|
34077
|
+
var familyTools = OPERATION_FAMILIES.map((family) => ({
|
|
34078
|
+
name: `workit_${family}`,
|
|
34079
|
+
description: `Workit ${family} operations backed by the shared task contract.`,
|
|
34080
|
+
input: { type: "object", ...boundedOperationJsonSchema(family, OPERATION_SCHEMA_DEPTH) }
|
|
34081
|
+
}));
|
|
34082
|
+
var WORKIT_TOOL_CATALOG = [
|
|
34083
|
+
...familyTools,
|
|
34084
|
+
{
|
|
34085
|
+
name: "workit_external_action",
|
|
34086
|
+
description: `Run one fixed optional action. ${externalActionHelp}`,
|
|
34087
|
+
input: objectSchema({
|
|
34088
|
+
operation: {
|
|
34089
|
+
type: "string",
|
|
34090
|
+
enum: [...WORKIT_EXTERNAL_ACTION_OPERATIONS],
|
|
34091
|
+
description: "Fixed optional action to run."
|
|
34092
|
+
},
|
|
34093
|
+
payload: {
|
|
34094
|
+
type: "object",
|
|
34095
|
+
description: "Operation-specific payload; Workit validates it.",
|
|
34096
|
+
additionalProperties: true
|
|
34097
|
+
}
|
|
34098
|
+
}, ["operation", "payload"])
|
|
34099
|
+
},
|
|
34100
|
+
{
|
|
34101
|
+
name: "workit_init_apply",
|
|
34102
|
+
description: "Apply a confirmed toolkit initialization action",
|
|
34103
|
+
input: objectSchema({
|
|
34104
|
+
confirmed: { type: "boolean" },
|
|
34105
|
+
action: {
|
|
34106
|
+
type: "string",
|
|
34107
|
+
enum: [
|
|
34108
|
+
"youtrack_scaffold",
|
|
34109
|
+
"youtrack_json",
|
|
34110
|
+
"youtrack_token_placeholder",
|
|
34111
|
+
"vcs_scaffold",
|
|
34112
|
+
"config",
|
|
34113
|
+
"gitignore",
|
|
34114
|
+
"hygiene",
|
|
34115
|
+
"branch_policy"
|
|
34116
|
+
]
|
|
34117
|
+
},
|
|
34118
|
+
base_url: { type: "string" },
|
|
34119
|
+
default_mention: { type: "string" },
|
|
34120
|
+
meeting_issue: { type: "string" },
|
|
34121
|
+
vcs_provider: { type: "string", enum: ["gitlab", "github"] },
|
|
34122
|
+
vcs_target_branch: { type: "string" },
|
|
34123
|
+
name: { type: "string" },
|
|
34124
|
+
develop_branch: { type: "string" },
|
|
34125
|
+
integration: { type: "string", enum: ["pr", "merge"] },
|
|
34126
|
+
locale: { type: "string" },
|
|
34127
|
+
locale_options: { type: "array", items: { type: "string" } },
|
|
34128
|
+
timezone: { type: "string" },
|
|
34129
|
+
branch_policy_preset: {
|
|
34130
|
+
type: "string",
|
|
34131
|
+
enum: ["gitflow", "github-flow", "trunk-based", "custom"]
|
|
34132
|
+
},
|
|
34133
|
+
branch_policy_allowed: { type: "array", items: { type: "string" } },
|
|
34134
|
+
branch_policy_protected: { type: "array", items: { type: "string" } },
|
|
34135
|
+
include_open_source: { type: "boolean" }
|
|
34136
|
+
}, ["confirmed", "action"])
|
|
34137
|
+
}
|
|
34138
|
+
];
|
|
34139
|
+
var workitFamilyOf = (toolName) => {
|
|
34140
|
+
for (const family of OPERATION_FAMILIES)
|
|
34141
|
+
if (toolName === `workit_${family}`)
|
|
34142
|
+
return family;
|
|
34143
|
+
return null;
|
|
34144
|
+
};
|
|
34145
|
+
|
|
34146
|
+
// packages/workit-opencode/src/v2/lifecycle.ts
|
|
34147
|
+
var record3 = (value) => typeof value === "object" && value !== null ? value : {};
|
|
34148
|
+
var sessionIDOf = (value) => typeof value === "string" && value.length > 0 ? value : null;
|
|
34149
|
+
var parentOf = (session) => typeof session?.parentID === "string" && session.parentID ? session.parentID : undefined;
|
|
34150
|
+
var contentText = (content) => {
|
|
34151
|
+
if (typeof content === "string")
|
|
34152
|
+
return content;
|
|
34153
|
+
if (!Array.isArray(content))
|
|
34154
|
+
return "";
|
|
34155
|
+
return content.map((part) => record3(part).type === "text" ? String(part.text ?? "") : "").join(`
|
|
34156
|
+
`);
|
|
34157
|
+
};
|
|
34158
|
+
var createV2Lifecycle = (deps) => {
|
|
34159
|
+
const { root } = deps;
|
|
34160
|
+
const directChildren = new Map;
|
|
34161
|
+
const dispatches = new Map;
|
|
34162
|
+
const inflight = new Map;
|
|
34163
|
+
const lifecycleBindings = new Map;
|
|
34164
|
+
const unresolvedTaskLaunches = new Set;
|
|
34165
|
+
const now = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
34166
|
+
const readSession = async (sessionID) => {
|
|
34167
|
+
try {
|
|
34168
|
+
const info = await deps.getSession(sessionID);
|
|
34169
|
+
if (!info || typeof info !== "object" || info.id !== sessionID)
|
|
34170
|
+
return null;
|
|
34171
|
+
return info;
|
|
34172
|
+
} catch {
|
|
34173
|
+
return null;
|
|
34174
|
+
}
|
|
34175
|
+
};
|
|
34176
|
+
const trustedLead = async (coordinator) => {
|
|
34177
|
+
const session = await readSession(coordinator);
|
|
34178
|
+
if (!session || parentOf(session) !== undefined)
|
|
34179
|
+
return null;
|
|
34180
|
+
if (!sameWorkspace(root, session.directory))
|
|
34181
|
+
return null;
|
|
34182
|
+
return session;
|
|
34183
|
+
};
|
|
34184
|
+
const managedByCoordinator = (task, workspace, coordinator) => task.status === "active" && task.workspaceId === workspace.id && (task.intent.provenance.session?.kind === "host" && task.intent.provenance.session.host === "opencode" && task.intent.provenance.session.handle === coordinator || task.workers.some((entry) => entry.provenance.session?.kind === "host" && entry.provenance.session.host === "opencode" && entry.provenance.session.handle === coordinator) || workspace.writer?.owner.taskId === task.id && workspace.writer.owner.session.host === "opencode" && workspace.writer.owner.session.handle === coordinator);
|
|
34185
|
+
const ownsActiveTask = (coordinator) => {
|
|
34186
|
+
try {
|
|
34187
|
+
const store = new TaskStore(root);
|
|
34188
|
+
const workspace = store.readWorkspace();
|
|
34189
|
+
const listed = store.listTasks();
|
|
34190
|
+
if (!workspace.ok || !workspace.data || !listed.ok)
|
|
34191
|
+
return false;
|
|
34192
|
+
return listed.data.some((task) => managedByCoordinator(task, workspace.data, coordinator));
|
|
34193
|
+
} catch {
|
|
34194
|
+
return false;
|
|
34195
|
+
}
|
|
34196
|
+
};
|
|
34197
|
+
const revisions = (taskId) => {
|
|
34198
|
+
const store = new TaskStore(root);
|
|
34199
|
+
const task = store.readTask(taskId);
|
|
34200
|
+
const workspace = store.readWorkspace();
|
|
34201
|
+
return task.ok && workspace.ok && workspace.data ? {
|
|
34202
|
+
expectedRevision: task.data.revision,
|
|
34203
|
+
expectedWorkspaceRevision: workspace.data.revision
|
|
34204
|
+
} : null;
|
|
34205
|
+
};
|
|
34206
|
+
const oldestOfSingleTask = (items) => {
|
|
34207
|
+
const tasks = new Set(items.map((item) => item.task.id));
|
|
34208
|
+
if (tasks.size !== 1 || items.length === 0)
|
|
34209
|
+
return null;
|
|
34210
|
+
return [...items].sort((a, b) => a.entry.recordedAt < b.entry.recordedAt ? -1 : a.entry.recordedAt > b.entry.recordedAt ? 1 : a.entry.id < b.entry.id ? -1 : 1)[0];
|
|
34211
|
+
};
|
|
34212
|
+
const prepareDispatch = (coordinator, callID) => {
|
|
34213
|
+
if (dispatches.has(coordinator))
|
|
34214
|
+
return "unsettled";
|
|
34215
|
+
const store = new TaskStore(root);
|
|
34216
|
+
const workspace = store.readWorkspace();
|
|
34217
|
+
const listed = store.listTasks();
|
|
34218
|
+
if (!workspace.ok || !workspace.data || !listed.ok)
|
|
34219
|
+
return "unmanaged";
|
|
34220
|
+
const managed = listed.data.filter((task) => managedByCoordinator(task, workspace.data, coordinator));
|
|
34221
|
+
if (managed.length === 0)
|
|
34222
|
+
return "unmanaged";
|
|
34223
|
+
const eligible = managed.flatMap((task) => task.workers.filter((entry) => entry.data.state === "assigned" && entry.data.session === null && entry.provenance.session?.kind === "host" && entry.provenance.session.host === "opencode" && entry.provenance.session.handle === coordinator).map((entry) => ({ task, entry })));
|
|
34224
|
+
const next = oldestOfSingleTask(eligible);
|
|
34225
|
+
if (!next)
|
|
34226
|
+
return "unbound";
|
|
34227
|
+
const generation = {
|
|
34228
|
+
coordinator,
|
|
34229
|
+
callID,
|
|
34230
|
+
childCreated: false,
|
|
34231
|
+
noChild: false
|
|
34232
|
+
};
|
|
34233
|
+
const core = new WorkitCore(store, {
|
|
34234
|
+
root,
|
|
34235
|
+
caller: { host: "opencode", actor: coordinator },
|
|
34236
|
+
capabilities: [],
|
|
34237
|
+
constraints: [],
|
|
34238
|
+
now,
|
|
34239
|
+
nativeWorker: nativeDispatchFor(directChildren, coordinator, generation)
|
|
34240
|
+
});
|
|
34241
|
+
const prepared = core.prepareWorkerDispatch({
|
|
34242
|
+
taskId: next.task.id,
|
|
34243
|
+
workerId: next.entry.id,
|
|
34244
|
+
expectedRevision: next.task.revision,
|
|
34245
|
+
expectedWorkspaceRevision: workspace.data.revision,
|
|
34246
|
+
observation: { stage: "prepare", sessionID: coordinator, callID }
|
|
34247
|
+
});
|
|
34248
|
+
if (!prepared.ok)
|
|
34249
|
+
return "unbound";
|
|
34250
|
+
dispatches.set(coordinator, {
|
|
34251
|
+
generation,
|
|
34252
|
+
dispatch: prepared.data,
|
|
34253
|
+
core,
|
|
34254
|
+
taskId: next.task.id,
|
|
34255
|
+
workerId: next.entry.id,
|
|
34256
|
+
callID,
|
|
34257
|
+
childID: null
|
|
34258
|
+
});
|
|
34259
|
+
return "prepared";
|
|
34260
|
+
};
|
|
34261
|
+
const commitDispatchStart = (coordinator, childID) => {
|
|
34262
|
+
const pending = dispatches.get(coordinator);
|
|
34263
|
+
if (!pending)
|
|
34264
|
+
return false;
|
|
34265
|
+
pending.generation.childCreated = true;
|
|
34266
|
+
pending.childID = childID;
|
|
34267
|
+
const current = revisions(pending.taskId);
|
|
34268
|
+
if (!current)
|
|
34269
|
+
return false;
|
|
34270
|
+
const committed = pending.core.commitWorkerDispatch({
|
|
34271
|
+
...current,
|
|
34272
|
+
dispatch: pending.dispatch,
|
|
34273
|
+
taskId: pending.taskId,
|
|
34274
|
+
workerId: pending.workerId,
|
|
34275
|
+
outcome: "started",
|
|
34276
|
+
session: { kind: "host", host: "opencode", handle: childID },
|
|
34277
|
+
observation: { event: "running", sessionID: childID }
|
|
34278
|
+
});
|
|
34279
|
+
if (!committed.ok)
|
|
34280
|
+
return false;
|
|
34281
|
+
dispatches.delete(coordinator);
|
|
34282
|
+
if (inflight.get(coordinator) === pending.callID)
|
|
34283
|
+
inflight.delete(coordinator);
|
|
34284
|
+
lifecycleBindings.set(childID, {
|
|
34285
|
+
parentID: coordinator,
|
|
34286
|
+
taskId: pending.taskId,
|
|
34287
|
+
workerId: pending.workerId
|
|
34288
|
+
});
|
|
34289
|
+
return true;
|
|
34290
|
+
};
|
|
34291
|
+
const observeLifecycle = async (sessionID, parentID, state, binding, deleted = false, report) => {
|
|
34292
|
+
const store = new TaskStore(root);
|
|
34293
|
+
const listed = store.listTasks();
|
|
34294
|
+
const workspace = store.readWorkspace();
|
|
34295
|
+
if (!listed.ok || !workspace.ok || !workspace.data)
|
|
34296
|
+
return;
|
|
34297
|
+
const persisted = listed.data.flatMap((task) => task.status === "active" ? task.workers.filter((entry) => entry.id === binding?.workerId || entry.data.session?.kind === "host" && entry.data.session.handle === sessionID).map((entry) => ({ task, entry })) : []);
|
|
34298
|
+
const matches = persisted.filter(({ entry }) => entry.data.session?.kind === "host");
|
|
34299
|
+
if (binding && matches.length !== 1)
|
|
34300
|
+
return;
|
|
34301
|
+
const selected = binding ? persisted.find(({ task, entry }) => task.id === binding.taskId && entry.id === binding.workerId) : matches.length === 1 ? matches[0] : undefined;
|
|
34302
|
+
if (!selected)
|
|
34303
|
+
return;
|
|
34304
|
+
if (selected.entry.data.session?.kind !== "host")
|
|
34305
|
+
return;
|
|
34306
|
+
if (selected.entry.provenance.session?.kind !== "host")
|
|
34307
|
+
return;
|
|
34308
|
+
if (!binding && deleted)
|
|
34309
|
+
return;
|
|
34310
|
+
if (!deleted) {
|
|
34311
|
+
const observed = await readSession(sessionID);
|
|
34312
|
+
const observedParent = parentOf(observed);
|
|
34313
|
+
if (!observed || !sameWorkspace(root, observed.directory) || !observedParent)
|
|
34314
|
+
return;
|
|
34315
|
+
if (!binding) {
|
|
34316
|
+
const coordinator = workerCoordinatorFor(selected.task, selected.entry);
|
|
34317
|
+
if (!coordinator || observedParent !== coordinator)
|
|
34318
|
+
return;
|
|
34319
|
+
parentID = coordinator;
|
|
34320
|
+
} else if (observedParent !== parentID)
|
|
34321
|
+
return;
|
|
34322
|
+
}
|
|
34323
|
+
directChildren.set(sessionID, parentID);
|
|
34324
|
+
const core = new WorkitCore(store, {
|
|
34325
|
+
root,
|
|
34326
|
+
caller: { host: "opencode", actor: parentID },
|
|
34327
|
+
capabilities: [],
|
|
34328
|
+
constraints: [],
|
|
34329
|
+
now,
|
|
34330
|
+
nativeWorker: nativeWorkerFor(directChildren, parentID)
|
|
34331
|
+
});
|
|
34332
|
+
const result = core.observeWorkerLifecycle({
|
|
34333
|
+
taskId: selected.task.id,
|
|
34334
|
+
workerId: selected.entry.id,
|
|
34335
|
+
expectedRevision: selected.task.revision,
|
|
34336
|
+
expectedWorkspaceRevision: workspace.data.revision,
|
|
34337
|
+
state,
|
|
34338
|
+
session: { kind: "host", host: "opencode", handle: sessionID },
|
|
34339
|
+
...report ? { report } : {},
|
|
34340
|
+
observation: { event: state, sessionID }
|
|
34341
|
+
});
|
|
34342
|
+
if (!result.ok)
|
|
34343
|
+
return;
|
|
34344
|
+
lifecycleBindings.set(sessionID, {
|
|
34345
|
+
parentID,
|
|
34346
|
+
taskId: selected.task.id,
|
|
34347
|
+
workerId: selected.entry.id
|
|
34348
|
+
});
|
|
34349
|
+
};
|
|
34350
|
+
const reconcilePending = async (coordinator) => {
|
|
34351
|
+
const pending = dispatches.get(coordinator);
|
|
34352
|
+
if (!pending?.childID)
|
|
34353
|
+
return;
|
|
34354
|
+
const info = await readSession(pending.childID);
|
|
34355
|
+
if (!info || info.id !== pending.childID || !info.outcome)
|
|
34356
|
+
return;
|
|
34357
|
+
await observeLifecycle(pending.childID, coordinator, "stopped", {
|
|
34358
|
+
taskId: pending.taskId,
|
|
34359
|
+
workerId: pending.workerId
|
|
34360
|
+
});
|
|
34361
|
+
dispatches.delete(coordinator);
|
|
34362
|
+
if (inflight.get(coordinator) === pending.callID)
|
|
34363
|
+
inflight.delete(coordinator);
|
|
34364
|
+
};
|
|
34365
|
+
const coordinatorVeto = (coordinator) => {
|
|
34366
|
+
const listed = new TaskStore(root).listTasks();
|
|
34367
|
+
if (listed.ok && listed.data.some((task) => task.workers.some((worker) => (worker.data.state === "cancelling" || worker.data.state === "unknown") && worker.provenance.session?.kind === "host" && worker.provenance.session.handle === coordinator)))
|
|
34368
|
+
return "recovery_required: a cancelled worker remains uncertain; repeat worker.cancel on the ended worker to confirm its stop, then retry";
|
|
34369
|
+
if (unresolvedTaskLaunches.delete(coordinator))
|
|
34370
|
+
return "recovery_required: a cancelled worker remains uncertain";
|
|
34371
|
+
return null;
|
|
34372
|
+
};
|
|
34373
|
+
const executeBefore = async (event) => {
|
|
34374
|
+
if (event.tool !== "subagent")
|
|
34375
|
+
return;
|
|
34376
|
+
const coordinator = event.sessionID;
|
|
34377
|
+
const continuation = record3(event.input).sessionID;
|
|
34378
|
+
if (continuation !== undefined) {
|
|
34379
|
+
const lead = await trustedLead(coordinator);
|
|
34380
|
+
if (!lead)
|
|
34381
|
+
throw new Error("delegation_lineage_denied: native subagent workers must be direct children of the coordinator");
|
|
34382
|
+
const childID = sessionIDOf(continuation);
|
|
34383
|
+
const child = childID ? await readSession(childID) : null;
|
|
34384
|
+
if (!child || parentOf(child) !== coordinator)
|
|
34385
|
+
throw new Error("delegation_lineage_denied: a continuation sessionID must name an existing direct child of the coordinator");
|
|
34386
|
+
const listed = new TaskStore(root).listTasks();
|
|
34387
|
+
const bound = listed.ok ? listed.data.filter((task) => task.workers.some((worker) => worker.data.state === "running" && worker.data.session?.kind === "host" && worker.data.session.handle === childID && workerCoordinatorFor(task, worker) === coordinator)).length : 0;
|
|
34388
|
+
if (bound !== 1 && ownsActiveTask(coordinator))
|
|
34389
|
+
throw new Error("delegation_lineage_denied: a continuation sessionID must name a running Workit worker");
|
|
34390
|
+
return;
|
|
34391
|
+
}
|
|
34392
|
+
if (!ownsActiveTask(coordinator)) {
|
|
34393
|
+
const lead = await trustedLead(coordinator);
|
|
34394
|
+
if (!lead)
|
|
34395
|
+
throw new Error("delegation_lineage_denied: native subagent workers must be direct children of the coordinator");
|
|
34396
|
+
return;
|
|
34397
|
+
}
|
|
34398
|
+
if (inflight.has(coordinator)) {
|
|
34399
|
+
await reconcilePending(coordinator);
|
|
34400
|
+
if (inflight.has(coordinator))
|
|
34401
|
+
throw new Error("recovery_required: a previous fresh subagent launch is unsettled; wait for it to settle or reconcile its worker before launching again");
|
|
34402
|
+
}
|
|
34403
|
+
inflight.set(coordinator, event.id);
|
|
34404
|
+
try {
|
|
34405
|
+
const veto = coordinatorVeto(coordinator);
|
|
34406
|
+
if (veto)
|
|
34407
|
+
throw new Error(veto);
|
|
34408
|
+
const lead = await trustedLead(coordinator);
|
|
34409
|
+
if (!lead)
|
|
34410
|
+
throw new Error("delegation_lineage_denied: native subagent workers must be direct children of the coordinator");
|
|
34411
|
+
const prepared = prepareDispatch(coordinator, event.id);
|
|
34412
|
+
if (prepared === "unmanaged") {
|
|
34413
|
+
inflight.delete(coordinator);
|
|
34414
|
+
return;
|
|
34415
|
+
}
|
|
34416
|
+
if (prepared === "unsettled") {
|
|
34417
|
+
throw new Error("recovery_required: a previous native task launch is unsettled; wait for it to settle or reconcile its worker before launching again");
|
|
34418
|
+
}
|
|
34419
|
+
if (prepared === "unbound") {
|
|
34420
|
+
throw new Error("recovery_required: no assigned Workit worker is attributable to this coordinator; assign one with workit_worker before launching a native task");
|
|
34421
|
+
}
|
|
34422
|
+
} catch (error) {
|
|
34423
|
+
if (inflight.get(coordinator) === event.id)
|
|
34424
|
+
inflight.delete(coordinator);
|
|
34425
|
+
throw error;
|
|
34426
|
+
}
|
|
34427
|
+
};
|
|
34428
|
+
const executeAfter = async (event) => {
|
|
34429
|
+
if (event.tool !== "subagent")
|
|
34430
|
+
return;
|
|
34431
|
+
const coordinator = event.sessionID;
|
|
34432
|
+
const pending = dispatches.get(coordinator);
|
|
34433
|
+
const generation = pending?.callID === event.id ? pending : undefined;
|
|
34434
|
+
if (event.status === "completed") {
|
|
34435
|
+
const metadata = record3(event.result.metadata);
|
|
34436
|
+
const child = sessionIDOf(metadata.sessionID ?? metadata.sessionId);
|
|
34437
|
+
if (child) {
|
|
34438
|
+
const childSession = await readSession(child);
|
|
34439
|
+
if (parentOf(childSession) === coordinator) {
|
|
34440
|
+
directChildren.set(child, coordinator);
|
|
34441
|
+
if (generation)
|
|
34442
|
+
commitDispatchStart(coordinator, child);
|
|
34443
|
+
}
|
|
34444
|
+
}
|
|
34445
|
+
const output = contentText(event.result.content);
|
|
34446
|
+
const completedChild = child && /<subagent\b[^>]*\bstate="completed"/.test(output) && lifecycleBindings.has(child) ? child : null;
|
|
34447
|
+
if (completedChild) {
|
|
34448
|
+
const binding = lifecycleBindings.get(completedChild);
|
|
34449
|
+
if (binding)
|
|
34450
|
+
await observeLifecycle(completedChild, coordinator, "stopped", binding, false, {
|
|
34451
|
+
outcome: "completed",
|
|
34452
|
+
summary: output.trim() || "Native worker completed.",
|
|
34453
|
+
evidenceIds: [],
|
|
34454
|
+
findingIds: []
|
|
34455
|
+
});
|
|
34456
|
+
}
|
|
34457
|
+
} else {
|
|
34458
|
+
const message = String(event.error?.message ?? event.error ?? "").toLowerCase();
|
|
34459
|
+
if (generation && /(?:cancel|interrupt|unknown|uncertain)/.test(message))
|
|
34460
|
+
unresolvedTaskLaunches.add(coordinator);
|
|
34461
|
+
}
|
|
34462
|
+
if (generation) {
|
|
34463
|
+
dispatches.delete(coordinator);
|
|
34464
|
+
if (inflight.get(coordinator) === event.id)
|
|
34465
|
+
inflight.delete(coordinator);
|
|
34466
|
+
} else if (inflight.get(coordinator) === event.id) {
|
|
34467
|
+
inflight.delete(coordinator);
|
|
34468
|
+
}
|
|
34469
|
+
};
|
|
34470
|
+
const bindCreatedSession = async (data) => {
|
|
34471
|
+
if (!sameWorkspace(root, data.directory))
|
|
34472
|
+
return;
|
|
34473
|
+
directChildren.set(data.sessionID, data.parentID);
|
|
34474
|
+
const live = dispatches.get(data.parentID);
|
|
34475
|
+
if (live) {
|
|
34476
|
+
live.childID = data.sessionID;
|
|
34477
|
+
commitDispatchStart(data.parentID, data.sessionID);
|
|
34478
|
+
}
|
|
34479
|
+
};
|
|
34480
|
+
const handleEvent = async (event) => {
|
|
34481
|
+
const type = event.type;
|
|
34482
|
+
if (typeof type !== "string")
|
|
34483
|
+
return;
|
|
34484
|
+
if (type === "session.created") {
|
|
34485
|
+
const data = record3(event.data);
|
|
34486
|
+
const sessionID = sessionIDOf(data.sessionID);
|
|
34487
|
+
const parentID = sessionIDOf(data.parentID);
|
|
34488
|
+
const directory = record3(data.location).directory;
|
|
34489
|
+
if (!sessionID || !parentID || typeof directory !== "string")
|
|
34490
|
+
return;
|
|
34491
|
+
await bindCreatedSession({ sessionID, parentID, directory });
|
|
34492
|
+
return;
|
|
34493
|
+
}
|
|
34494
|
+
const terminal = type === "session.execution.succeeded" || type === "session.execution.failed" || type === "session.execution.interrupted";
|
|
34495
|
+
if (type !== "session.execution.started" && !terminal && type !== "session.deleted")
|
|
34496
|
+
return;
|
|
34497
|
+
const sessionID = sessionIDOf(record3(event.data).sessionID);
|
|
34498
|
+
if (!sessionID)
|
|
34499
|
+
return;
|
|
34500
|
+
const binding = lifecycleBindings.get(sessionID);
|
|
34501
|
+
if (type === "session.deleted") {
|
|
34502
|
+
const directory = event.location?.directory;
|
|
34503
|
+
if (typeof directory === "string" && !sameWorkspace(root, directory))
|
|
34504
|
+
return;
|
|
34505
|
+
await observeLifecycle(sessionID, binding?.parentID ?? "", "stopped", binding, true);
|
|
34506
|
+
return;
|
|
34507
|
+
}
|
|
34508
|
+
const state = type === "session.execution.started" ? "running" : "stopped";
|
|
34509
|
+
await observeLifecycle(sessionID, binding?.parentID ?? "", state, binding);
|
|
34510
|
+
};
|
|
34511
|
+
return {
|
|
34512
|
+
directChildren,
|
|
34513
|
+
executeBefore,
|
|
34514
|
+
executeAfter,
|
|
34515
|
+
handleEvent,
|
|
34516
|
+
pendingLaunch: (coordinator) => inflight.has(coordinator)
|
|
34517
|
+
};
|
|
34518
|
+
};
|
|
34519
|
+
|
|
34520
|
+
// packages/workit-opencode/src/v2/injection.ts
|
|
34521
|
+
var textPart = (text) => ({ type: "text", text });
|
|
34522
|
+
var hasMarker = (system, marker) => system.some((part) => part.type === "text" && typeof part.text === "string" && part.text.includes(marker));
|
|
34523
|
+
var injectAgentContext = (root, session, directChildren, system) => {
|
|
34524
|
+
if (!session || !sameWorkspace(root, session.directory))
|
|
34525
|
+
return;
|
|
34526
|
+
const bootstrap = session.parentID === undefined ? getWorkitBootstrap() : null;
|
|
34527
|
+
const taskContext = compactContextFor(root, session.id);
|
|
34528
|
+
const workerContext = session.parentID ? workerContextFor(root, session.id, session.parentID, directChildren) : null;
|
|
34529
|
+
if (bootstrap && !hasMarker(system, "<workit-contract>"))
|
|
34530
|
+
system.unshift(textPart(bootstrap));
|
|
34531
|
+
if (taskContext && !hasMarker(system, "<workit-task-context>"))
|
|
34532
|
+
system.unshift(textPart(`<workit-task-context>${taskContext}</workit-task-context>`));
|
|
34533
|
+
if (workerContext && !hasMarker(system, "<workit-worker-context>"))
|
|
34534
|
+
system.unshift(textPart(`<workit-worker-context>${workerContext}</workit-worker-context>`));
|
|
34535
|
+
};
|
|
34536
|
+
var injectCompactionContext = (root, sessionID, system) => {
|
|
34537
|
+
const context = compactContextFor(root, sessionID);
|
|
34538
|
+
if (!context || hasMarker(system, "<workit-task-context>"))
|
|
34539
|
+
return;
|
|
34540
|
+
system.push(textPart(`<workit-task-context>${context}</workit-task-context>`));
|
|
34541
|
+
};
|
|
34542
|
+
|
|
34543
|
+
// packages/workit-opencode/src/v2/permissions.ts
|
|
34544
|
+
var GIT_WORKTREE = /git [\s\S]*worktree/u;
|
|
34545
|
+
var WORKTREE_MESSAGE = "Workit does not use git worktrees; use the guarded in-place git.branch_setup route instead";
|
|
34546
|
+
var evaluateShellPermission = (root, event) => {
|
|
34547
|
+
if (event.action !== "shell" || event.effect === "deny")
|
|
34548
|
+
return;
|
|
34549
|
+
for (const resource of event.resources) {
|
|
34550
|
+
if (typeof resource !== "string")
|
|
34551
|
+
continue;
|
|
34552
|
+
const route = shouldDenyShellRoute(root, resource);
|
|
34553
|
+
if (route) {
|
|
34554
|
+
event.effect = "deny";
|
|
34555
|
+
event.message = `direct branch or PR creation bypasses the Workit route; ${route.guidance}`;
|
|
34556
|
+
return;
|
|
34557
|
+
}
|
|
34558
|
+
if (GIT_WORKTREE.test(resource)) {
|
|
34559
|
+
event.effect = "deny";
|
|
34560
|
+
event.message = WORKTREE_MESSAGE;
|
|
34561
|
+
return;
|
|
34562
|
+
}
|
|
34563
|
+
}
|
|
34564
|
+
};
|
|
34565
|
+
|
|
34566
|
+
// packages/workit-opencode/src/v2/receipts.ts
|
|
34567
|
+
var normalizeQuestionAnswers = (answers) => {
|
|
34568
|
+
const singleValue = (value) => typeof value === "string" && value.trim() ? value : null;
|
|
34569
|
+
if (answers === null || typeof answers !== "object")
|
|
34570
|
+
return answers;
|
|
34571
|
+
if (Array.isArray(answers)) {
|
|
34572
|
+
if (answers.length === 0)
|
|
34573
|
+
return answers;
|
|
34574
|
+
const first = answers[0];
|
|
34575
|
+
const direct = singleValue(first);
|
|
34576
|
+
if (direct)
|
|
34577
|
+
return [[direct]];
|
|
34578
|
+
if (Array.isArray(first)) {
|
|
34579
|
+
if (first.length !== 1)
|
|
34580
|
+
return answers;
|
|
34581
|
+
const only = singleValue(first[0]);
|
|
34582
|
+
return only ? [[only]] : answers;
|
|
34583
|
+
}
|
|
34584
|
+
if (first && typeof first === "object") {
|
|
34585
|
+
const values = Object.values(first);
|
|
34586
|
+
if (values.length === 1 && singleValue(values[0]) !== null)
|
|
34587
|
+
return [[singleValue(values[0])]];
|
|
34588
|
+
}
|
|
34589
|
+
return answers;
|
|
34590
|
+
}
|
|
34591
|
+
const values = Object.values(answers);
|
|
34592
|
+
if (values.length === 1 && singleValue(values[0]) !== null)
|
|
34593
|
+
return [[singleValue(values[0])]];
|
|
34594
|
+
return answers;
|
|
34595
|
+
};
|
|
34596
|
+
|
|
34597
|
+
// packages/workit-opencode/src/v2/registry.ts
|
|
34598
|
+
import { existsSync as existsSync15, readFileSync as readFileSync14 } from "node:fs";
|
|
34599
|
+
import path28 from "node:path";
|
|
34600
|
+
var parseFrontmatter = (markdown) => {
|
|
34601
|
+
const match = markdown.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
34602
|
+
if (!match)
|
|
34603
|
+
return { name: "", body: markdown };
|
|
34604
|
+
const meta = {};
|
|
34605
|
+
for (const line of match[1].split(`
|
|
34606
|
+
`)) {
|
|
34607
|
+
const index = line.indexOf(":");
|
|
34608
|
+
if (index > 0)
|
|
34609
|
+
meta[line.slice(0, index).trim()] = line.slice(index + 1).trim();
|
|
34610
|
+
}
|
|
34611
|
+
return {
|
|
34612
|
+
name: (meta.name ?? "").replace(/^["']|["']$/g, "").trim(),
|
|
34613
|
+
description: (meta.description ?? "").replace(/^["']|["']$/g, "").trim() || undefined,
|
|
34614
|
+
body: `${match[2].trim()}
|
|
34615
|
+
`
|
|
34616
|
+
};
|
|
34617
|
+
};
|
|
34618
|
+
var registerSkills = async (ctx) => {
|
|
34619
|
+
const skillsDir = path28.join(assetsRoot(), "skills");
|
|
34620
|
+
if (!existsSync15(skillsDir))
|
|
34621
|
+
return;
|
|
34622
|
+
await ctx.skill.transform((editor) => {
|
|
34623
|
+
const existing = new Set(editor.list().map((skill) => skill.id));
|
|
34624
|
+
for (const id of WORKIT_METHOD_SKILLS) {
|
|
34625
|
+
if (existing.has(id))
|
|
34626
|
+
continue;
|
|
34627
|
+
const file = path28.join(skillsDir, id, "SKILL.md");
|
|
34628
|
+
if (!existsSync15(file))
|
|
34629
|
+
continue;
|
|
34630
|
+
const parsed = parseFrontmatter(readFileSync14(file, "utf8"));
|
|
34631
|
+
editor.add({
|
|
34632
|
+
id,
|
|
34633
|
+
name: parsed.name || id,
|
|
34634
|
+
...parsed.description ? { description: parsed.description } : {},
|
|
34635
|
+
location: file,
|
|
34636
|
+
content: parsed.body
|
|
34637
|
+
});
|
|
34638
|
+
}
|
|
34639
|
+
});
|
|
34640
|
+
};
|
|
34641
|
+
var registerCommands = async (ctx) => {
|
|
34642
|
+
const listed = await ctx.command.list();
|
|
34643
|
+
const existing = new Set((listed?.data ?? []).map((command) => command.name));
|
|
34644
|
+
await ctx.command.transform((editor) => {
|
|
34645
|
+
for (const [alias, skill] of Object.entries(WORKIT_SKILL_ALIASES)) {
|
|
34646
|
+
if (existing.has(alias))
|
|
34647
|
+
continue;
|
|
34648
|
+
editor.add({
|
|
34649
|
+
name: alias,
|
|
34650
|
+
description: `Apply the ${skill} method skill to the current task.`,
|
|
34651
|
+
execute: async ({ sessionID, prompt, delivery }) => {
|
|
34652
|
+
await ctx.session.prompt({
|
|
34653
|
+
sessionID,
|
|
34654
|
+
text: `Load the ${skill} skill with the skill tool and apply it to the current task. Arguments: ${prompt.text}`,
|
|
34655
|
+
...prompt.files ? { files: prompt.files } : {},
|
|
34656
|
+
...prompt.agents ? { agents: prompt.agents } : {},
|
|
34657
|
+
...prompt.skills ? { skills: prompt.skills } : {},
|
|
34658
|
+
delivery
|
|
34659
|
+
});
|
|
34660
|
+
}
|
|
34661
|
+
});
|
|
34662
|
+
}
|
|
34663
|
+
});
|
|
34664
|
+
};
|
|
34665
|
+
|
|
34666
|
+
// packages/workit-opencode/src/v2/plugin.ts
|
|
34667
|
+
var sessionFacts = async (ctx, sessionID) => {
|
|
34668
|
+
try {
|
|
34669
|
+
const session = await ctx.session.get({ sessionID });
|
|
34670
|
+
const id = session?.id;
|
|
34671
|
+
const directory = session?.location?.directory;
|
|
34672
|
+
if (typeof id !== "string" || id !== sessionID || typeof directory !== "string")
|
|
34673
|
+
return null;
|
|
34674
|
+
const parentID = typeof session.parentID === "string" ? session.parentID : undefined;
|
|
34675
|
+
const outcome = session.outcome === "succeeded" || session.outcome === "failed" || session.outcome === "interrupted" ? session.outcome : undefined;
|
|
34676
|
+
return { id, parentID, directory, outcome };
|
|
34677
|
+
} catch {
|
|
34678
|
+
return null;
|
|
34679
|
+
}
|
|
34680
|
+
};
|
|
34681
|
+
var resultContent = (value) => ({
|
|
34682
|
+
content: JSON.stringify(value, null, 2)
|
|
34683
|
+
});
|
|
34684
|
+
var v2Capabilities = () => [
|
|
34685
|
+
{
|
|
34686
|
+
name: "interactive_decision",
|
|
34687
|
+
surface: "question",
|
|
34688
|
+
assurance: "enforced",
|
|
34689
|
+
reason: "native question answers are observed by tool.execute.after and consumed once",
|
|
34690
|
+
refs: [{ kind: "host", host: "opencode", handle: "question" }]
|
|
34691
|
+
},
|
|
34692
|
+
{
|
|
34693
|
+
name: "known_product_writes",
|
|
34694
|
+
surface: "edit/shell",
|
|
34695
|
+
assurance: "unavailable",
|
|
34696
|
+
reason: "file writes are host-policy; OpenCode native permissions govern them, workit no longer gates write tools",
|
|
34697
|
+
refs: [{ kind: "host", host: "opencode", handle: "permission.evaluate" }]
|
|
34698
|
+
},
|
|
34699
|
+
{
|
|
34700
|
+
name: "direct_child_workers",
|
|
34701
|
+
surface: "subagent",
|
|
34702
|
+
assurance: "enforced",
|
|
34703
|
+
reason: "nested subagent launches are denied and observed child sessions are parent-bound before they may own a worker",
|
|
34704
|
+
refs: [{ kind: "host", host: "opencode", handle: "subagent" }]
|
|
34705
|
+
},
|
|
34706
|
+
{
|
|
34707
|
+
name: "fresh-context-review",
|
|
34708
|
+
surface: "subagent",
|
|
34709
|
+
assurance: "agent_guided",
|
|
34710
|
+
reason: "independent review runs as a native child session; evidence evaluation enforces creator and duplicate-reviewer exclusion",
|
|
34711
|
+
refs: [{ kind: "host", host: "opencode", handle: "subagent" }]
|
|
34712
|
+
}
|
|
34713
|
+
];
|
|
34714
|
+
var workerIdFor2 = (store, actor, parentID, directChildren) => {
|
|
34715
|
+
if (parentID === undefined)
|
|
34716
|
+
return null;
|
|
34717
|
+
const tasks = store.listTasks();
|
|
34718
|
+
if (!tasks.ok)
|
|
34719
|
+
return null;
|
|
34720
|
+
const matches = tasks.data.flatMap((task) => {
|
|
34721
|
+
if (task.status !== "active")
|
|
34722
|
+
return [];
|
|
34723
|
+
if (directChildren.get(actor) !== parentID)
|
|
34724
|
+
return [];
|
|
34725
|
+
return task.workers.filter((worker) => worker.data.state === "running" && worker.data.session?.kind === "host" && worker.data.session.host === "opencode" && worker.data.session.handle === actor && workerCoordinatorFor(task, worker) === parentID);
|
|
34726
|
+
});
|
|
34727
|
+
return matches.length === 1 ? matches[0].id : null;
|
|
34728
|
+
};
|
|
34729
|
+
var recordDecision = (core, receipts, request, sessionID) => {
|
|
34730
|
+
const decision = request;
|
|
34731
|
+
if (decision.response === "stated")
|
|
34732
|
+
return core.observeDecision(request, undefined);
|
|
34733
|
+
const content = decisionContent(decision.purpose, decision.binding.presented, decision.binding.approvedContent);
|
|
34734
|
+
const budgetIssue = workitBindingQuestionIssue([content]);
|
|
34735
|
+
if (budgetIssue)
|
|
34736
|
+
return failure("invalid_input", budgetIssue);
|
|
34737
|
+
const observed = receipts.reserve(sessionID, "decision", {
|
|
34738
|
+
selectedLabel: decision.response,
|
|
34739
|
+
decisionPurpose: decision.purpose,
|
|
34740
|
+
contentDigest: sha256(canonicalJson(content)),
|
|
34741
|
+
question: decision.binding.presented,
|
|
34742
|
+
...decision.response === "approved" ? { selectedDescription: decision.binding.approvedContent } : {}
|
|
34743
|
+
});
|
|
34744
|
+
if (!observed.ok)
|
|
34745
|
+
return failure("permission_denied", observed.error);
|
|
34746
|
+
if (decision.purpose === "action" && decision.response === "approved" && !isSelfAuthorizingActionContent(decision.binding.approvedContent))
|
|
34747
|
+
return failure("invalid_input", "no matching action proposal; resolve the action through the action tool first");
|
|
34748
|
+
const result = core.observeDecision(request, observed.observation);
|
|
34749
|
+
if (result.ok)
|
|
34750
|
+
receipts.commit(observed.observation);
|
|
34751
|
+
return result;
|
|
34752
|
+
};
|
|
34753
|
+
var setup = async (ctx) => {
|
|
34754
|
+
const root = ctx.location.directory;
|
|
34755
|
+
const sourceMarker = markSourcesLoaded(pluginSourceFiles);
|
|
34756
|
+
let staleSourcesWarned = false;
|
|
34757
|
+
const receipts = new NativeReceiptStore;
|
|
34758
|
+
const lifecycle = createV2Lifecycle({
|
|
34759
|
+
root,
|
|
34760
|
+
getSession: async (sessionID) => {
|
|
34761
|
+
const session = await sessionFacts(ctx, sessionID);
|
|
34762
|
+
return session ? {
|
|
34763
|
+
id: session.id,
|
|
34764
|
+
parentID: session.parentID,
|
|
34765
|
+
directory: session.directory,
|
|
34766
|
+
outcome: session.outcome
|
|
34767
|
+
} : null;
|
|
34768
|
+
}
|
|
34769
|
+
});
|
|
34770
|
+
const runFamily = (family, input, session, store, workerId) => {
|
|
34771
|
+
const parsed = parseOperation(family, input);
|
|
34772
|
+
if (!parsed.ok)
|
|
34773
|
+
return parsed;
|
|
34774
|
+
const core = new WorkitCore(store, {
|
|
34775
|
+
root,
|
|
34776
|
+
caller: { host: "opencode", actor: session.id },
|
|
34777
|
+
capabilities: v2Capabilities(),
|
|
34778
|
+
constraints: [],
|
|
34779
|
+
now: () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
34780
|
+
workerId,
|
|
34781
|
+
nativeAuthority: nativeAuthority(receipts, session.id),
|
|
34782
|
+
nativeWorker: nativeWorkerFor(lifecycle.directChildren, session.id)
|
|
34783
|
+
});
|
|
34784
|
+
if (family === "decision" && parsed.data.action === "record")
|
|
34785
|
+
return recordDecision(core, receipts, parsed.data, session.id);
|
|
34786
|
+
const run = core[family];
|
|
34787
|
+
return run.call(core, parsed.data);
|
|
34788
|
+
};
|
|
34789
|
+
await ctx.tool.transform((editor) => {
|
|
34790
|
+
for (const spec of WORKIT_TOOL_CATALOG) {
|
|
34791
|
+
editor.add({
|
|
34792
|
+
name: spec.name,
|
|
34793
|
+
description: spec.description,
|
|
34794
|
+
input: spec.input,
|
|
34795
|
+
options: { codemode: false },
|
|
34796
|
+
execute: async (input, tool) => {
|
|
34797
|
+
const session = await sessionFacts(ctx, tool.sessionID);
|
|
34798
|
+
if (session === null || !sameWorkspace(root, session.directory))
|
|
34799
|
+
return resultContent(failure("permission_denied", "OpenCode session location does not match the plugin checkout"));
|
|
34800
|
+
const store = new TaskStore(root);
|
|
34801
|
+
const workerId = workerIdFor2(store, session.id, session.parentID, lifecycle.directChildren);
|
|
34802
|
+
if (session.parentID !== undefined && workerId === null)
|
|
34803
|
+
return resultContent(failure("permission_denied", "OpenCode child session has no validated Workit worker"));
|
|
34804
|
+
const family = workitFamilyOf(spec.name);
|
|
34805
|
+
if (family !== null)
|
|
34806
|
+
return resultContent(runFamily(family, input, session, store, workerId));
|
|
34807
|
+
if (spec.name === "workit_external_action") {
|
|
34808
|
+
const parsed = externalActionRequest(input);
|
|
34809
|
+
if (!parsed.ok)
|
|
34810
|
+
return resultContent(parsed);
|
|
34811
|
+
if (parsed.data.operation !== "context.read")
|
|
34812
|
+
return resultContent(failure("capability_unavailable", "OpenCode V2 external action approvals are not available yet; only context.read is", { capability: "external_action", outcome: "not_started" }));
|
|
34813
|
+
const resolved = resolveExternalActionRequest(root, parsed.data);
|
|
34814
|
+
if (!resolved.ok)
|
|
34815
|
+
return resultContent(resolved);
|
|
34816
|
+
return resultContent(await executeResolvedExternalAction(resolved.data, root));
|
|
34817
|
+
}
|
|
34818
|
+
if (spec.name === "workit_init_apply") {
|
|
34819
|
+
return { content: executeInitApply(input, root, initApplyRuntime) };
|
|
34820
|
+
}
|
|
34821
|
+
return resultContent(failure("not_found", `unknown Workit tool ${spec.name}`));
|
|
34822
|
+
}
|
|
34823
|
+
});
|
|
34824
|
+
}
|
|
34825
|
+
});
|
|
34826
|
+
await ctx.tool.hook("execute.before", async (event) => {
|
|
34827
|
+
if (event.tool !== "subagent")
|
|
34828
|
+
return;
|
|
34829
|
+
await lifecycle.executeBefore({
|
|
34830
|
+
tool: event.tool,
|
|
34831
|
+
sessionID: String(event.sessionID),
|
|
34832
|
+
id: String(event.id),
|
|
34833
|
+
input: event.input
|
|
34834
|
+
});
|
|
34835
|
+
});
|
|
34836
|
+
await ctx.tool.hook("execute.after", async (event) => {
|
|
34837
|
+
if (event.tool === "question") {
|
|
34838
|
+
if (event.status !== "completed")
|
|
34839
|
+
return;
|
|
34840
|
+
const metadata = event.result.metadata;
|
|
34841
|
+
const answers = metadata?.answers;
|
|
34842
|
+
receipts.record({ sessionID: String(event.sessionID), callID: String(event.id), args: event.input }, {
|
|
34843
|
+
metadata: metadata === undefined ? undefined : { answers: normalizeQuestionAnswers(answers) }
|
|
34844
|
+
});
|
|
34845
|
+
return;
|
|
34846
|
+
}
|
|
34847
|
+
if (event.tool !== "subagent")
|
|
34848
|
+
return;
|
|
34849
|
+
await lifecycle.executeAfter({
|
|
34850
|
+
tool: event.tool,
|
|
34851
|
+
sessionID: String(event.sessionID),
|
|
34852
|
+
id: String(event.id),
|
|
34853
|
+
input: event.input,
|
|
34854
|
+
...event.status === "completed" ? { status: "completed", result: event.result } : { status: "error", error: event.error }
|
|
34855
|
+
});
|
|
34856
|
+
});
|
|
34857
|
+
await ctx.permission.hook("evaluate", (event) => {
|
|
34858
|
+
evaluateShellPermission(root, event);
|
|
34859
|
+
});
|
|
34860
|
+
await ctx.session.hook("context", async (event) => {
|
|
34861
|
+
const session = await sessionFacts(ctx, String(event.sessionID));
|
|
34862
|
+
injectAgentContext(root, session, lifecycle.directChildren, event.system);
|
|
34863
|
+
if (!staleSourcesWarned) {
|
|
34864
|
+
const changed = changedSourcesSinceLoad(sourceMarker);
|
|
34865
|
+
if (changed.length > 0) {
|
|
34866
|
+
staleSourcesWarned = true;
|
|
34867
|
+
event.system.push({
|
|
34868
|
+
type: "text",
|
|
34869
|
+
text: `<workit-warning>Workit sources changed after plugin load (${changed.map((file) => file.split(/[\\/]/).at(-1)).join(", ")}); restart the session for latest behavior.</workit-warning>`
|
|
34870
|
+
});
|
|
34871
|
+
}
|
|
34872
|
+
}
|
|
34873
|
+
});
|
|
34874
|
+
await ctx.session.hook("compaction", (event) => {
|
|
34875
|
+
injectCompactionContext(root, String(event.sessionID), event.system);
|
|
34876
|
+
});
|
|
34877
|
+
await registerSkills(ctx);
|
|
34878
|
+
await registerCommands(ctx);
|
|
34879
|
+
const subscription = new AbortController;
|
|
34880
|
+
(async () => {
|
|
34881
|
+
try {
|
|
34882
|
+
for await (const event of ctx.event.subscribe({ signal: subscription.signal })) {
|
|
34883
|
+
await lifecycle.handleEvent(event);
|
|
34884
|
+
}
|
|
34885
|
+
} catch {}
|
|
34886
|
+
})();
|
|
34887
|
+
return () => {
|
|
34888
|
+
subscription.abort();
|
|
34889
|
+
};
|
|
34890
|
+
};
|
|
34891
|
+
var definition = define({ id: "workit", setup });
|
|
34892
|
+
var plugin_default = definition;
|
|
34893
|
+
|
|
34894
|
+
// packages/workit-opencode/src/index.ts
|
|
34895
|
+
var src_default = { ...plugin_default, server: server_default };
|
|
33516
34896
|
export {
|
|
33517
|
-
|
|
34897
|
+
src_default as default,
|
|
34898
|
+
server_default as server
|
|
33518
34899
|
};
|