@brainervirus/workit-opencode 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -2
- package/dist/plugin.js +1529 -354
- 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) {
|
|
@@ -8070,8 +8292,62 @@ function parseOperation(family, input) {
|
|
|
8070
8292
|
}));
|
|
8071
8293
|
return failure("invalid_input", "operation input is invalid", { fields });
|
|
8072
8294
|
}
|
|
8295
|
+
function operationJsonSchema(family) {
|
|
8296
|
+
return toJSONSchema(operationSchemas[family], { target: "draft-2020-12" });
|
|
8297
|
+
}
|
|
8073
8298
|
var OPERATION_SCHEMA_DEPTH = 1;
|
|
8074
8299
|
var canonicalFieldsDescription = (fields) => `Canonical object fields: ${fields.join(", ")}. Workit validates the complete nested value.`;
|
|
8300
|
+
function boundedOperationJsonSchema(family, maxDepth = OPERATION_SCHEMA_DEPTH, stringifiedObjects = false) {
|
|
8301
|
+
const tolerate = (projected) => {
|
|
8302
|
+
if (!stringifiedObjects)
|
|
8303
|
+
return projected;
|
|
8304
|
+
if (projected.properties && typeof projected.properties === "object")
|
|
8305
|
+
return { ...projected, type: ["object", "string"] };
|
|
8306
|
+
if (projected.items)
|
|
8307
|
+
return { ...projected, type: ["array", "string"] };
|
|
8308
|
+
if ("const" in projected && (typeof projected.const === "number" || typeof projected.const === "boolean"))
|
|
8309
|
+
return { anyOf: [projected, { const: String(projected.const) }] };
|
|
8310
|
+
if (projected.type === "object")
|
|
8311
|
+
return { ...projected, type: ["object", "string"] };
|
|
8312
|
+
if (projected.type === "array")
|
|
8313
|
+
return { ...projected, type: ["array", "string"] };
|
|
8314
|
+
return projected;
|
|
8315
|
+
};
|
|
8316
|
+
const collapse = (node, currentDepth) => {
|
|
8317
|
+
if (Array.isArray(node))
|
|
8318
|
+
return node.map((item) => collapse(item, currentDepth));
|
|
8319
|
+
if (typeof node !== "object" || node === null)
|
|
8320
|
+
return node;
|
|
8321
|
+
const record = node;
|
|
8322
|
+
if (record.properties && typeof record.properties === "object") {
|
|
8323
|
+
const fields = Object.keys(record.properties);
|
|
8324
|
+
if (currentDepth >= maxDepth || fields.length === 0) {
|
|
8325
|
+
const description = stringifiedObjects ? `${canonicalFieldsDescription(fields)} A JSON-encoded string is also accepted.` : canonicalFieldsDescription(fields);
|
|
8326
|
+
return tolerate({ type: "object", description });
|
|
8327
|
+
}
|
|
8328
|
+
return tolerate({
|
|
8329
|
+
...record,
|
|
8330
|
+
properties: Object.fromEntries(Object.entries(record.properties).map(([key, child]) => [
|
|
8331
|
+
key,
|
|
8332
|
+
collapse(child, currentDepth + 1)
|
|
8333
|
+
]))
|
|
8334
|
+
});
|
|
8335
|
+
}
|
|
8336
|
+
if (record.items)
|
|
8337
|
+
return tolerate({ ...record, items: collapse(record.items, currentDepth) });
|
|
8338
|
+
const composed = ["anyOf", "oneOf", "allOf"].filter((key) => Array.isArray(record[key]));
|
|
8339
|
+
if (composed.length > 0) {
|
|
8340
|
+
const projected = { ...record };
|
|
8341
|
+
for (const key of composed)
|
|
8342
|
+
projected[key] = record[key].map((item) => collapse(item, currentDepth));
|
|
8343
|
+
return projected;
|
|
8344
|
+
}
|
|
8345
|
+
if ("const" in record)
|
|
8346
|
+
return tolerate({ ...record });
|
|
8347
|
+
return node;
|
|
8348
|
+
};
|
|
8349
|
+
return collapse(operationJsonSchema(family), 0);
|
|
8350
|
+
}
|
|
8075
8351
|
function canonical(value, seen) {
|
|
8076
8352
|
if (value === null || typeof value === "boolean" || typeof value === "string") {
|
|
8077
8353
|
if (typeof value === "string" && invalidUnicode(value))
|
|
@@ -12394,6 +12670,12 @@ var externalActionDescriptor = (operation, payload) => canonicalJson({
|
|
|
12394
12670
|
payload,
|
|
12395
12671
|
...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
12672
|
});
|
|
12673
|
+
var planReservationLength = (operation, descriptorPayload) => {
|
|
12674
|
+
if (operation !== "git.commit")
|
|
12675
|
+
return null;
|
|
12676
|
+
const payload = descriptorPayload ?? {};
|
|
12677
|
+
return Array.isArray(payload.plan_steps) ? payload.plan_steps.length : null;
|
|
12678
|
+
};
|
|
12397
12679
|
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}; 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,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
12680
|
var externalActionRef = (host, actor, descriptor) => ({
|
|
12399
12681
|
kind: "host",
|
|
@@ -15151,6 +15433,22 @@ function run2(root, executable, args, env = {}) {
|
|
|
15151
15433
|
}
|
|
15152
15434
|
|
|
15153
15435
|
// packages/workit-core/src/core/skill-manifests.ts
|
|
15436
|
+
var WORKIT_METHOD_SKILLS = [
|
|
15437
|
+
"workit-challenge",
|
|
15438
|
+
"workit-behavioral-tdd",
|
|
15439
|
+
"workit-review",
|
|
15440
|
+
"workit-plan",
|
|
15441
|
+
"workit-implement",
|
|
15442
|
+
"workit-debug",
|
|
15443
|
+
"workit-handoff",
|
|
15444
|
+
"workit-babysit",
|
|
15445
|
+
"workit-blast-radius",
|
|
15446
|
+
"workit-deslop",
|
|
15447
|
+
"workit-diagram",
|
|
15448
|
+
"workit-mockup",
|
|
15449
|
+
"workit-green-run",
|
|
15450
|
+
"workit-steer"
|
|
15451
|
+
];
|
|
15154
15452
|
var WORKIT_SKILL_ALIASES = {
|
|
15155
15453
|
"wk-challenge": "workit-challenge",
|
|
15156
15454
|
"wk-babysit": "workit-babysit",
|
|
@@ -15407,13 +15705,47 @@ OpenCode Workit decisions use the native question header "Workit decision: <purp
|
|
|
15407
15705
|
return cached2;
|
|
15408
15706
|
};
|
|
15409
15707
|
|
|
15410
|
-
// packages/workit-opencode/src/
|
|
15411
|
-
import { existsSync as existsSync7 } from "node:fs";
|
|
15708
|
+
// packages/workit-opencode/src/shared/assets.ts
|
|
15709
|
+
import { existsSync as existsSync7, realpathSync as realpathSync7 } from "node:fs";
|
|
15710
|
+
import path13 from "node:path";
|
|
15412
15711
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
15712
|
+
var moduleDir = () => {
|
|
15713
|
+
try {
|
|
15714
|
+
return realpathSync7(path13.dirname(fileURLToPath2(import.meta.url)));
|
|
15715
|
+
} catch {
|
|
15716
|
+
return path13.dirname(fileURLToPath2(import.meta.url));
|
|
15717
|
+
}
|
|
15718
|
+
};
|
|
15719
|
+
var packageRoot2 = () => {
|
|
15720
|
+
const dir = moduleDir();
|
|
15721
|
+
for (const candidate of [
|
|
15722
|
+
path13.resolve(dir, ".."),
|
|
15723
|
+
path13.resolve(dir, "..", ".."),
|
|
15724
|
+
path13.resolve(dir, "..", "..", "..")
|
|
15725
|
+
]) {
|
|
15726
|
+
if (existsSync7(path13.join(candidate, "package.json")))
|
|
15727
|
+
return candidate;
|
|
15728
|
+
}
|
|
15729
|
+
return path13.resolve(dir, "..");
|
|
15730
|
+
};
|
|
15731
|
+
var assetsRoot = () => path13.join(packageRoot2(), "assets");
|
|
15732
|
+
|
|
15733
|
+
// packages/workit-opencode/src/stale-sources.ts
|
|
15734
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
15735
|
+
import path14 from "node:path";
|
|
15736
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
15737
|
+
var packageFiles = [
|
|
15738
|
+
"src/index.ts",
|
|
15739
|
+
"src/plugin.ts",
|
|
15740
|
+
"src/v1/server.ts",
|
|
15741
|
+
"src/v2/plugin.ts",
|
|
15742
|
+
"src/shared/tools.ts",
|
|
15743
|
+
"src/tools/workit.ts"
|
|
15744
|
+
].map((file) => path14.join(packageRoot2(), file));
|
|
15745
|
+
var coreDir = path14.join(packageRoot2(), "..", "workit-core", "src", "core");
|
|
15413
15746
|
var pluginSourceFiles = [
|
|
15414
|
-
|
|
15415
|
-
|
|
15416
|
-
fileURLToPath2(new URL("./tools/workit.ts", import.meta.url)),
|
|
15747
|
+
fileURLToPath3(import.meta.url),
|
|
15748
|
+
...packageFiles,
|
|
15417
15749
|
...[
|
|
15418
15750
|
"task-contract.ts",
|
|
15419
15751
|
"task-engine.ts",
|
|
@@ -15422,12 +15754,11 @@ var pluginSourceFiles = [
|
|
|
15422
15754
|
"workers.ts",
|
|
15423
15755
|
"authority.ts",
|
|
15424
15756
|
"methods.ts"
|
|
15425
|
-
].map((file) =>
|
|
15426
|
-
].filter((file) =>
|
|
15757
|
+
].map((file) => path14.join(coreDir, file))
|
|
15758
|
+
].filter((file) => existsSync8(file));
|
|
15427
15759
|
|
|
15428
15760
|
// packages/workit-opencode/src/tools/repo.ts
|
|
15429
|
-
import
|
|
15430
|
-
import { realpathSync as realpathSync8 } from "node:fs";
|
|
15761
|
+
import { realpathSync as realpathSync9 } from "node:fs";
|
|
15431
15762
|
|
|
15432
15763
|
// node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/external.js
|
|
15433
15764
|
var exports_external = {};
|
|
@@ -27751,7 +28082,7 @@ function tool(input) {
|
|
|
27751
28082
|
tool.schema = exports_external;
|
|
27752
28083
|
// packages/workit-core/src/core/changelog.ts
|
|
27753
28084
|
import fs8 from "node:fs";
|
|
27754
|
-
import
|
|
28085
|
+
import path15 from "node:path";
|
|
27755
28086
|
import { createHash as createHash6 } from "node:crypto";
|
|
27756
28087
|
|
|
27757
28088
|
// packages/workit-core/src/core/scripts.ts
|
|
@@ -27948,7 +28279,7 @@ function applyChangelog(target, entries, normalizeOnly) {
|
|
|
27948
28279
|
const rendered = renderChangelog(source.text, entries, normalizeOnly);
|
|
27949
28280
|
if ("error" in rendered)
|
|
27950
28281
|
return { error: rendered.error };
|
|
27951
|
-
fs8.mkdirSync(
|
|
28282
|
+
fs8.mkdirSync(path15.dirname(target), { recursive: true });
|
|
27952
28283
|
fs8.writeFileSync(target, rendered.text, "utf8");
|
|
27953
28284
|
return { ...rendered.result, path: target };
|
|
27954
28285
|
}
|
|
@@ -27983,14 +28314,14 @@ function renderChangelog(source, entries, normalizeOnly) {
|
|
|
27983
28314
|
var changelogTarget = (workspaceRoot, changelogPath) => {
|
|
27984
28315
|
try {
|
|
27985
28316
|
const cwd = fs8.realpathSync(resolveWorkspaceRoot(workspaceRoot));
|
|
27986
|
-
const target =
|
|
27987
|
-
if (target !== cwd && !target.startsWith(cwd +
|
|
28317
|
+
const target = path15.resolve(cwd, changelogPath || "CHANGELOG.md");
|
|
28318
|
+
if (target !== cwd && !target.startsWith(cwd + path15.sep))
|
|
27988
28319
|
return { error: "changelog path must be inside workspace_root" };
|
|
27989
28320
|
let ancestor = target;
|
|
27990
28321
|
while (!fs8.existsSync(ancestor))
|
|
27991
|
-
ancestor =
|
|
28322
|
+
ancestor = path15.dirname(ancestor);
|
|
27992
28323
|
const canonicalAncestor = fs8.realpathSync(ancestor);
|
|
27993
|
-
if (canonicalAncestor !== cwd && !canonicalAncestor.startsWith(cwd +
|
|
28324
|
+
if (canonicalAncestor !== cwd && !canonicalAncestor.startsWith(cwd + path15.sep))
|
|
27994
28325
|
return { error: "changelog path must be inside workspace_root" };
|
|
27995
28326
|
return { cwd, target: fs8.existsSync(target) ? fs8.realpathSync(target) : target };
|
|
27996
28327
|
} catch {
|
|
@@ -28138,121 +28469,9 @@ function parseVerifyOutput(stdout) {
|
|
|
28138
28469
|
return { passed, failed, skipped, commands };
|
|
28139
28470
|
}
|
|
28140
28471
|
|
|
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
28472
|
// packages/workit-core/src/core/repo-context.ts
|
|
28254
28473
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
28255
|
-
import { existsSync as
|
|
28474
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, readdirSync as readdirSync5 } from "node:fs";
|
|
28256
28475
|
import path16 from "node:path";
|
|
28257
28476
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
28258
28477
|
try {
|
|
@@ -28287,7 +28506,7 @@ var runGit = (cwd, args) => {
|
|
|
28287
28506
|
exitCode: result.status ?? 1
|
|
28288
28507
|
};
|
|
28289
28508
|
};
|
|
28290
|
-
var
|
|
28509
|
+
var repoRoot = (cwd) => {
|
|
28291
28510
|
const r = runGit(cwd, ["rev-parse", "--show-toplevel"]);
|
|
28292
28511
|
return r.exitCode === 0 && r.stdout ? r.stdout : cwd;
|
|
28293
28512
|
};
|
|
@@ -28324,7 +28543,7 @@ function resolvePrBranchContext(cwd) {
|
|
|
28324
28543
|
if (branch === "unknown") {
|
|
28325
28544
|
return {
|
|
28326
28545
|
ok: false,
|
|
28327
|
-
error: `not in a git repository at ${
|
|
28546
|
+
error: `not in a git repository at ${repoRoot(cwd)} — open the target repository as the OpenCode session directory`
|
|
28328
28547
|
};
|
|
28329
28548
|
}
|
|
28330
28549
|
return {
|
|
@@ -28419,10 +28638,10 @@ var diffStatForRange = (cwd, range) => {
|
|
|
28419
28638
|
};
|
|
28420
28639
|
var packageScripts = (cwd, keys) => {
|
|
28421
28640
|
const pkgPath = path16.join(cwd, "package.json");
|
|
28422
|
-
if (!
|
|
28641
|
+
if (!existsSync9(pkgPath))
|
|
28423
28642
|
return [];
|
|
28424
28643
|
try {
|
|
28425
|
-
const pkg = JSON.parse(
|
|
28644
|
+
const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
28426
28645
|
return keys.filter((k) => pkg.scripts && typeof pkg.scripts[k] === "string").map((k) => `${k}: ${pkg.scripts?.[k]}`);
|
|
28427
28646
|
} catch {
|
|
28428
28647
|
return [];
|
|
@@ -28454,15 +28673,15 @@ var documentationFiles = (cwd) => {
|
|
|
28454
28673
|
return matches.sort().slice(0, 200);
|
|
28455
28674
|
};
|
|
28456
28675
|
var readTrimmed = (file, maxLines) => {
|
|
28457
|
-
if (!
|
|
28676
|
+
if (!existsSync9(file))
|
|
28458
28677
|
return "";
|
|
28459
|
-
const lines =
|
|
28678
|
+
const lines = readFileSync8(file, "utf8").split(`
|
|
28460
28679
|
`);
|
|
28461
28680
|
return lines.slice(0, maxLines).join(`
|
|
28462
28681
|
`);
|
|
28463
28682
|
};
|
|
28464
28683
|
function prReadyContext(root, range) {
|
|
28465
|
-
const cwd = path16.resolve(
|
|
28684
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
28466
28685
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
28467
28686
|
return invalidRange(cwd);
|
|
28468
28687
|
let stdout = "";
|
|
@@ -28530,7 +28749,7 @@ ${status.stderr}` : "");
|
|
|
28530
28749
|
stdout += `
|
|
28531
28750
|
`;
|
|
28532
28751
|
stdout += printSection("Recent Validation Signals");
|
|
28533
|
-
if (
|
|
28752
|
+
if (existsSync9(path16.join(cwd, "package.json"))) {
|
|
28534
28753
|
stdout += `package.json detected. Common scripts:
|
|
28535
28754
|
`;
|
|
28536
28755
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -28539,7 +28758,7 @@ ${status.stderr}` : "");
|
|
|
28539
28758
|
`) + `
|
|
28540
28759
|
`;
|
|
28541
28760
|
}
|
|
28542
|
-
if (
|
|
28761
|
+
if (existsSync9(path16.join(cwd, "Cargo.toml")) || existsSync9(path16.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
28543
28762
|
stdout += `Rust project detected.
|
|
28544
28763
|
`;
|
|
28545
28764
|
}
|
|
@@ -28572,7 +28791,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
28572
28791
|
- Apply with the native workit_changelog_apply tool only (not hand-edits under Unreleased).
|
|
28573
28792
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
28574
28793
|
function changelogContext(root, range) {
|
|
28575
|
-
const cwd = path16.resolve(
|
|
28794
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
28576
28795
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
28577
28796
|
return invalidRange(cwd);
|
|
28578
28797
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -28591,7 +28810,7 @@ function changelogContext(root, range) {
|
|
|
28591
28810
|
`;
|
|
28592
28811
|
stdout += printSection("Existing CHANGELOG.md");
|
|
28593
28812
|
const changelogPath = path16.join(cwd, "CHANGELOG.md");
|
|
28594
|
-
if (
|
|
28813
|
+
if (existsSync9(changelogPath)) {
|
|
28595
28814
|
stdout += readTrimmed(changelogPath, 260) + `
|
|
28596
28815
|
`;
|
|
28597
28816
|
} else {
|
|
@@ -28610,7 +28829,7 @@ function changelogContext(root, range) {
|
|
|
28610
28829
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
28611
28830
|
}
|
|
28612
28831
|
function docsRefreshContext(root, range) {
|
|
28613
|
-
const cwd = path16.resolve(
|
|
28832
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
28614
28833
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
28615
28834
|
return invalidRange(cwd);
|
|
28616
28835
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -28633,7 +28852,7 @@ function docsRefreshContext(root, range) {
|
|
|
28633
28852
|
`;
|
|
28634
28853
|
stdout += printSection("README Preview");
|
|
28635
28854
|
const readme = path16.join(cwd, "README.md");
|
|
28636
|
-
if (
|
|
28855
|
+
if (existsSync9(readme)) {
|
|
28637
28856
|
stdout += readTrimmed(readme, 220) + `
|
|
28638
28857
|
`;
|
|
28639
28858
|
} else {
|
|
@@ -28642,9 +28861,9 @@ function docsRefreshContext(root, range) {
|
|
|
28642
28861
|
}
|
|
28643
28862
|
stdout += printSection("Package Scripts");
|
|
28644
28863
|
const pkgPath = path16.join(cwd, "package.json");
|
|
28645
|
-
if (
|
|
28864
|
+
if (existsSync9(pkgPath)) {
|
|
28646
28865
|
try {
|
|
28647
|
-
const pkg = JSON.parse(
|
|
28866
|
+
const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
28648
28867
|
stdout += JSON.stringify({ name: pkg.name, version: pkg.version, scripts: pkg.scripts }, null, 2) + `
|
|
28649
28868
|
`;
|
|
28650
28869
|
} catch {}
|
|
@@ -28655,7 +28874,7 @@ function docsRefreshContext(root, range) {
|
|
|
28655
28874
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
28656
28875
|
}
|
|
28657
28876
|
function releaseNotesContext(root, rangeOrTag) {
|
|
28658
|
-
const cwd = path16.resolve(
|
|
28877
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
28659
28878
|
if (!rangeOrTag) {
|
|
28660
28879
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
28661
28880
|
`, exitCode: 1, cwd };
|
|
@@ -28693,7 +28912,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
28693
28912
|
`;
|
|
28694
28913
|
stdout += printSection("Existing Release Files");
|
|
28695
28914
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
28696
|
-
if (
|
|
28915
|
+
if (existsSync9(path16.join(cwd, rel)))
|
|
28697
28916
|
stdout += rel + `
|
|
28698
28917
|
`;
|
|
28699
28918
|
}
|
|
@@ -28722,7 +28941,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
28722
28941
|
|
|
28723
28942
|
// packages/workit-core/src/core/verify-project.ts
|
|
28724
28943
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
28725
|
-
import { existsSync as
|
|
28944
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9, statSync as statSync2 } from "node:fs";
|
|
28726
28945
|
import path17 from "node:path";
|
|
28727
28946
|
var commandOnPath = (name) => {
|
|
28728
28947
|
const dirs = (process.env.PATH ?? "").split(path17.delimiter);
|
|
@@ -28747,33 +28966,33 @@ var commandOnPath = (name) => {
|
|
|
28747
28966
|
};
|
|
28748
28967
|
var hasScript = (cwd, name) => {
|
|
28749
28968
|
const pkgPath = path17.join(cwd, "package.json");
|
|
28750
|
-
if (!
|
|
28969
|
+
if (!existsSync10(pkgPath))
|
|
28751
28970
|
return false;
|
|
28752
28971
|
try {
|
|
28753
|
-
const pkg = JSON.parse(
|
|
28972
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf8"));
|
|
28754
28973
|
return Boolean(pkg.scripts && typeof pkg.scripts[name] === "string");
|
|
28755
28974
|
} catch {
|
|
28756
28975
|
return false;
|
|
28757
28976
|
}
|
|
28758
28977
|
};
|
|
28759
28978
|
var packageRunner = (cwd) => {
|
|
28760
|
-
if (
|
|
28979
|
+
if (existsSync10(path17.join(cwd, "pnpm-lock.yaml")))
|
|
28761
28980
|
return "pnpm";
|
|
28762
28981
|
const pkgPath = path17.join(cwd, "package.json");
|
|
28763
|
-
if (
|
|
28982
|
+
if (existsSync10(pkgPath)) {
|
|
28764
28983
|
try {
|
|
28765
|
-
const pkg = JSON.parse(
|
|
28984
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf8"));
|
|
28766
28985
|
if (typeof pkg.packageManager === "string" && pkg.packageManager.startsWith("pnpm")) {
|
|
28767
28986
|
return "pnpm";
|
|
28768
28987
|
}
|
|
28769
28988
|
} catch {}
|
|
28770
28989
|
}
|
|
28771
|
-
if (
|
|
28990
|
+
if (existsSync10(path17.join(cwd, "yarn.lock")))
|
|
28772
28991
|
return "yarn";
|
|
28773
28992
|
return "npm";
|
|
28774
28993
|
};
|
|
28775
28994
|
function runVerifyProject(root, dryRun = false) {
|
|
28776
|
-
const cwd =
|
|
28995
|
+
const cwd = repoRoot(root);
|
|
28777
28996
|
let passed = 0;
|
|
28778
28997
|
let failed = 0;
|
|
28779
28998
|
let skipped = 0;
|
|
@@ -28829,7 +29048,7 @@ root: ${cwd}
|
|
|
28829
29048
|
lines.push(`dry_run: ${String(dryRun)}
|
|
28830
29049
|
`);
|
|
28831
29050
|
const pkgPath = path17.join(cwd, "package.json");
|
|
28832
|
-
if (
|
|
29051
|
+
if (existsSync10(pkgPath)) {
|
|
28833
29052
|
const runner = packageRunner(cwd);
|
|
28834
29053
|
for (const script of ["lint", "format:check", "test", "build"]) {
|
|
28835
29054
|
if (hasScript(cwd, script)) {
|
|
@@ -28844,7 +29063,7 @@ root: ${cwd}
|
|
|
28844
29063
|
} else {
|
|
28845
29064
|
skipCheck("javascript", "package.json not found");
|
|
28846
29065
|
}
|
|
28847
|
-
const cargoManifest =
|
|
29066
|
+
const cargoManifest = existsSync10(path17.join(cwd, "Cargo.toml")) ? "Cargo.toml" : existsSync10(path17.join(cwd, "src-tauri/Cargo.toml")) ? "src-tauri/Cargo.toml" : "";
|
|
28848
29067
|
if (cargoManifest) {
|
|
28849
29068
|
runCheck("cargo fmt", "cargo", ["fmt", "--manifest-path", cargoManifest, "--", "--check"]);
|
|
28850
29069
|
runCheck("cargo clippy", "cargo", [
|
|
@@ -28860,7 +29079,7 @@ root: ${cwd}
|
|
|
28860
29079
|
} else {
|
|
28861
29080
|
skipCheck("rust", "Cargo.toml not found");
|
|
28862
29081
|
}
|
|
28863
|
-
if (
|
|
29082
|
+
if (existsSync10(path17.join(cwd, "pyproject.toml")) || existsSync10(path17.join(cwd, "pytest.ini")) || existsSync10(path17.join(cwd, "tests"))) {
|
|
28864
29083
|
if (commandOnPath("pytest"))
|
|
28865
29084
|
runCheck("pytest", "pytest", []);
|
|
28866
29085
|
else
|
|
@@ -28882,8 +29101,8 @@ root: ${cwd}
|
|
|
28882
29101
|
lines.push(`status: skipped (dry run)
|
|
28883
29102
|
`);
|
|
28884
29103
|
skipped += 1;
|
|
28885
|
-
} else if (
|
|
28886
|
-
if (/## \[Unreleased\]/i.test(
|
|
29104
|
+
} else if (existsSync10(changelogPath)) {
|
|
29105
|
+
if (/## \[Unreleased\]/i.test(readFileSync9(changelogPath, "utf8"))) {
|
|
28887
29106
|
lines.push(`status: pass
|
|
28888
29107
|
`);
|
|
28889
29108
|
passed += 1;
|
|
@@ -28997,12 +29216,12 @@ function whichOnPath(tool) {
|
|
|
28997
29216
|
return null;
|
|
28998
29217
|
}
|
|
28999
29218
|
var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
|
|
29000
|
-
function
|
|
29219
|
+
function repoRoot2(cwd) {
|
|
29001
29220
|
const result = spawnSync6("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf8" });
|
|
29002
29221
|
return result.status === 0 ? (result.stdout ?? "").trim() : cwd;
|
|
29003
29222
|
}
|
|
29004
29223
|
function prCreate(env, cwd) {
|
|
29005
|
-
const root = cwd ?
|
|
29224
|
+
const root = cwd ? repoRoot2(cwd) : process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
29006
29225
|
const policy = resolveBranchPolicyFor(root);
|
|
29007
29226
|
const cfg = vcsConfig("load", root);
|
|
29008
29227
|
if (!cfg.ok)
|
|
@@ -29195,7 +29414,7 @@ function prCreate(env, cwd) {
|
|
|
29195
29414
|
};
|
|
29196
29415
|
}
|
|
29197
29416
|
function mergePr(cwd, opts) {
|
|
29198
|
-
const root =
|
|
29417
|
+
const root = repoRoot2(cwd);
|
|
29199
29418
|
const cfg = vcsConfig("load", root);
|
|
29200
29419
|
if (!cfg.ok)
|
|
29201
29420
|
return { error: cfg.error ?? "vcs config missing" };
|
|
@@ -29244,14 +29463,14 @@ function mergePr(cwd, opts) {
|
|
|
29244
29463
|
|
|
29245
29464
|
// packages/workit-core/src/core/init.ts
|
|
29246
29465
|
import fs11 from "node:fs";
|
|
29247
|
-
import
|
|
29466
|
+
import path23 from "node:path";
|
|
29248
29467
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
29249
29468
|
|
|
29250
29469
|
// packages/workit-core/src/core/safe-write.ts
|
|
29251
|
-
import { writeFileSync as
|
|
29470
|
+
import { writeFileSync as writeFileSync4 } from "node:fs";
|
|
29252
29471
|
function writeFileExclusive(file, content, mode) {
|
|
29253
29472
|
try {
|
|
29254
|
-
|
|
29473
|
+
writeFileSync4(file, content, { encoding: "utf8", flag: "wx", mode });
|
|
29255
29474
|
return "created";
|
|
29256
29475
|
} catch (err) {
|
|
29257
29476
|
if (err.code === "EEXIST")
|
|
@@ -29264,19 +29483,19 @@ function writeFileExclusive(file, content, mode) {
|
|
|
29264
29483
|
import {
|
|
29265
29484
|
copyFileSync as copyFileSync2,
|
|
29266
29485
|
cpSync as cpSync2,
|
|
29267
|
-
existsSync as
|
|
29486
|
+
existsSync as existsSync13,
|
|
29268
29487
|
lstatSync as lstatSync4,
|
|
29269
29488
|
mkdirSync as mkdirSync5,
|
|
29270
29489
|
mkdtempSync,
|
|
29271
29490
|
readFileSync as readFileSync12,
|
|
29272
29491
|
readdirSync as readdirSync6,
|
|
29273
|
-
realpathSync as
|
|
29492
|
+
realpathSync as realpathSync8,
|
|
29274
29493
|
renameSync as renameSync2,
|
|
29275
29494
|
rmSync,
|
|
29276
29495
|
statSync as statSync3,
|
|
29277
29496
|
writeFileSync as writeFileSync7
|
|
29278
29497
|
} from "node:fs";
|
|
29279
|
-
import
|
|
29498
|
+
import path21 from "node:path";
|
|
29280
29499
|
import { isDeepStrictEqual } from "node:util";
|
|
29281
29500
|
|
|
29282
29501
|
// packages/workit-core/src/core/branch-policy.ts
|
|
@@ -29339,6 +29558,118 @@ var detectBranchPolicy = (workspaceRoot) => {
|
|
|
29339
29558
|
};
|
|
29340
29559
|
};
|
|
29341
29560
|
|
|
29561
|
+
// packages/workit-core/src/core/gitignore.ts
|
|
29562
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "node:fs";
|
|
29563
|
+
import path19 from "node:path";
|
|
29564
|
+
var GITIGNORE_ENTRIES = [
|
|
29565
|
+
"# workit: SDD working state (never commit)",
|
|
29566
|
+
"docs/*/sdd/",
|
|
29567
|
+
"",
|
|
29568
|
+
"# OS / editor cruft",
|
|
29569
|
+
".DS_Store",
|
|
29570
|
+
"Thumbs.db",
|
|
29571
|
+
"*.swp",
|
|
29572
|
+
".idea/",
|
|
29573
|
+
".vscode/",
|
|
29574
|
+
".env",
|
|
29575
|
+
"node_modules/",
|
|
29576
|
+
"dist/",
|
|
29577
|
+
"*.log",
|
|
29578
|
+
".cache/"
|
|
29579
|
+
];
|
|
29580
|
+
var ensureProjectGitignore = (workspaceRoot, confirmed) => {
|
|
29581
|
+
if (!confirmed)
|
|
29582
|
+
return { ok: false, error: "confirmed: true required" };
|
|
29583
|
+
const file = path19.join(workspaceRoot, ".gitignore");
|
|
29584
|
+
const existing = existsSync11(file) ? readFileSync10(file, "utf8") : "";
|
|
29585
|
+
const existingLines = new Set(existing.split(`
|
|
29586
|
+
`).map((l) => l.trim()).filter(Boolean));
|
|
29587
|
+
const added = [];
|
|
29588
|
+
const append = [];
|
|
29589
|
+
for (const entry of GITIGNORE_ENTRIES) {
|
|
29590
|
+
if (entry.trim() === "" || existingLines.has(entry.trim()))
|
|
29591
|
+
continue;
|
|
29592
|
+
append.push(entry);
|
|
29593
|
+
added.push(entry);
|
|
29594
|
+
}
|
|
29595
|
+
if (append.length) {
|
|
29596
|
+
const separator = existing && !existing.endsWith(`
|
|
29597
|
+
`) ? `
|
|
29598
|
+
` : "";
|
|
29599
|
+
writeFileSync5(file, existing + separator + (existing ? `
|
|
29600
|
+
` : "") + append.join(`
|
|
29601
|
+
`) + `
|
|
29602
|
+
`, "utf8");
|
|
29603
|
+
} else if (!existsSync11(file)) {
|
|
29604
|
+
writeFileSync5(file, "", "utf8");
|
|
29605
|
+
}
|
|
29606
|
+
return { ok: true, path: file, added };
|
|
29607
|
+
};
|
|
29608
|
+
|
|
29609
|
+
// packages/workit-core/src/core/hygiene.ts
|
|
29610
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "node:fs";
|
|
29611
|
+
import path20 from "node:path";
|
|
29612
|
+
var repoRoot3 = assetRoot();
|
|
29613
|
+
var templatesDir = () => path20.join(repoRoot3, "templates", "hygiene");
|
|
29614
|
+
var packageJson = (root) => {
|
|
29615
|
+
if (!existsSync12(path20.join(root, "package.json")))
|
|
29616
|
+
return null;
|
|
29617
|
+
try {
|
|
29618
|
+
return JSON.parse(readFileSync11(path20.join(root, "package.json"), "utf8"));
|
|
29619
|
+
} catch {
|
|
29620
|
+
return null;
|
|
29621
|
+
}
|
|
29622
|
+
};
|
|
29623
|
+
var isOpenSource = (root) => {
|
|
29624
|
+
if (existsSync12(path20.join(root, "LICENSE")))
|
|
29625
|
+
return true;
|
|
29626
|
+
const pkg = packageJson(root);
|
|
29627
|
+
if (pkg && !pkg.private)
|
|
29628
|
+
return true;
|
|
29629
|
+
return path20.basename(root) === "workflow-toolkit";
|
|
29630
|
+
};
|
|
29631
|
+
var licenseHolder = (root) => {
|
|
29632
|
+
const pkg = packageJson(root);
|
|
29633
|
+
if (pkg) {
|
|
29634
|
+
const author = pkg.author;
|
|
29635
|
+
if (typeof author === "string" && author.trim())
|
|
29636
|
+
return author.trim();
|
|
29637
|
+
if (author && typeof author === "object") {
|
|
29638
|
+
const name = author.name;
|
|
29639
|
+
if (typeof name === "string" && name.trim())
|
|
29640
|
+
return name.trim();
|
|
29641
|
+
}
|
|
29642
|
+
}
|
|
29643
|
+
return "";
|
|
29644
|
+
};
|
|
29645
|
+
var planHygieneFiles = (root, opts = {}) => {
|
|
29646
|
+
const openSource = opts.includeOpenSource ?? isOpenSource(root);
|
|
29647
|
+
const files = ["CHANGELOG.md", "README.md", ".editorconfig", ".gitattributes"];
|
|
29648
|
+
if (openSource)
|
|
29649
|
+
files.push("LICENSE", "CONTRIBUTING.md");
|
|
29650
|
+
const planned = [];
|
|
29651
|
+
for (const file of files) {
|
|
29652
|
+
if (existsSync12(path20.join(root, file)))
|
|
29653
|
+
continue;
|
|
29654
|
+
const tpl = path20.join(templatesDir(), file);
|
|
29655
|
+
if (!existsSync12(tpl))
|
|
29656
|
+
continue;
|
|
29657
|
+
const content = readFileSync11(tpl, "utf8").replace(/<PROJECT>/g, path20.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
|
|
29658
|
+
planned.push({ path: path20.join(root, file), content });
|
|
29659
|
+
}
|
|
29660
|
+
return planned;
|
|
29661
|
+
};
|
|
29662
|
+
var ensureHygieneFiles = (root, opts) => {
|
|
29663
|
+
if (!opts.confirmed)
|
|
29664
|
+
return { ok: false, error: "confirmed: true required" };
|
|
29665
|
+
const created = [];
|
|
29666
|
+
for (const planned of planHygieneFiles(root, opts)) {
|
|
29667
|
+
writeFileSync6(planned.path, planned.content, "utf8");
|
|
29668
|
+
created.push(path20.basename(planned.path));
|
|
29669
|
+
}
|
|
29670
|
+
return { ok: true, created };
|
|
29671
|
+
};
|
|
29672
|
+
|
|
29342
29673
|
// packages/workit-core/src/core/registration.ts
|
|
29343
29674
|
var CURSOR_RUNTIME_PACKAGE = "@brainervirus/workit-cursor@latest";
|
|
29344
29675
|
var CURSOR_HOOK_RUN_COMMAND = `npx -y --prefer-online --min-release-age=0 --package=${CURSOR_RUNTIME_PACKAGE} workit-cursor-hook`;
|
|
@@ -29380,12 +29711,12 @@ var VCS_OVERRIDES = [
|
|
|
29380
29711
|
var SETUP_OVERRIDES = [...YT_OVERRIDES, ...VCS_OVERRIDES];
|
|
29381
29712
|
function applyWorkspaceBranchPolicy(opts) {
|
|
29382
29713
|
const { workspace_root, env = process.env } = opts;
|
|
29383
|
-
const dir =
|
|
29714
|
+
const dir = path21.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
|
|
29384
29715
|
const { status, path: wsPath, entries } = readWorkspacesResult(dir);
|
|
29385
29716
|
if (status === "malformed")
|
|
29386
29717
|
return { ok: false, error: `malformed workspaces.json: ${wsPath}` };
|
|
29387
29718
|
const detection = detectBranchPolicy(workspace_root);
|
|
29388
|
-
const name = String(env.WORKFLOW_BP_NAME ??
|
|
29719
|
+
const name = String(env.WORKFLOW_BP_NAME ?? path21.basename(workspace_root));
|
|
29389
29720
|
const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
|
|
29390
29721
|
const policy = {
|
|
29391
29722
|
preset: detection.preset,
|
|
@@ -29410,7 +29741,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
29410
29741
|
};
|
|
29411
29742
|
}
|
|
29412
29743
|
const next = existing ? entries.map((w, i) => i === idx ? { ...w, branchPolicy: policy } : w) : [...entries, { name, glob, branchPolicy: policy }];
|
|
29413
|
-
mkdirSync5(
|
|
29744
|
+
mkdirSync5(path21.dirname(wsPath), { recursive: true });
|
|
29414
29745
|
writeFileSync7(wsPath, JSON.stringify({ workspaces: next }, null, 2) + `
|
|
29415
29746
|
`, "utf8");
|
|
29416
29747
|
return {
|
|
@@ -29424,7 +29755,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
29424
29755
|
|
|
29425
29756
|
// packages/workit-core/src/core/youtrack.ts
|
|
29426
29757
|
import fs10 from "node:fs";
|
|
29427
|
-
import
|
|
29758
|
+
import path22 from "node:path";
|
|
29428
29759
|
|
|
29429
29760
|
// packages/workit-core/src/core/templates.ts
|
|
29430
29761
|
var repoRoot4 = assetRoot();
|
|
@@ -29432,8 +29763,8 @@ var repoRoot4 = assetRoot();
|
|
|
29432
29763
|
// packages/workit-core/src/core/youtrack.ts
|
|
29433
29764
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
29434
29765
|
var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE";
|
|
29435
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
29436
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
29766
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path22.join(configDir(), "youtrack.json");
|
|
29767
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path22.join(resolveConfigDir(), "youtrack.json");
|
|
29437
29768
|
var youTrackTokenModeOk = (p) => {
|
|
29438
29769
|
if (process.platform === "win32")
|
|
29439
29770
|
return true;
|
|
@@ -29477,7 +29808,7 @@ function youTrackConfigLoad() {
|
|
|
29477
29808
|
}
|
|
29478
29809
|
const cfgPath = loaded.path;
|
|
29479
29810
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
29480
|
-
const tokenPath = tokenFile ?
|
|
29811
|
+
const tokenPath = tokenFile ? path22.isAbsolute(tokenFile) ? path22.resolve(tokenFile) : path22.resolve(process.cwd(), tokenFile) : "";
|
|
29481
29812
|
if (!tokenPath || !fs10.existsSync(tokenPath)) {
|
|
29482
29813
|
return { error: "missing youtrack.token" };
|
|
29483
29814
|
}
|
|
@@ -29491,8 +29822,8 @@ function youTrackConfigLoad() {
|
|
|
29491
29822
|
const redacted = { ...loaded.config };
|
|
29492
29823
|
delete redacted.tokenFile;
|
|
29493
29824
|
redacted.tokenPresent = true;
|
|
29494
|
-
redacted.configPath =
|
|
29495
|
-
redacted.tokenPath =
|
|
29825
|
+
redacted.configPath = path22.resolve(cfgPath);
|
|
29826
|
+
redacted.tokenPath = path22.resolve(tokenPath);
|
|
29496
29827
|
return { data: redacted };
|
|
29497
29828
|
}
|
|
29498
29829
|
function tzParts(date, tz) {
|
|
@@ -29605,7 +29936,7 @@ var youTrackToken = () => {
|
|
|
29605
29936
|
if ("error" in loaded)
|
|
29606
29937
|
return loaded;
|
|
29607
29938
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
29608
|
-
const tokenPath = tokenFile ?
|
|
29939
|
+
const tokenPath = tokenFile ? path22.isAbsolute(tokenFile) ? path22.resolve(tokenFile) : path22.resolve(process.cwd(), tokenFile) : "";
|
|
29609
29940
|
if (!tokenPath || !fs10.existsSync(tokenPath))
|
|
29610
29941
|
return { error: "missing youtrack.token" };
|
|
29611
29942
|
if (!youTrackTokenModeOk(tokenPath))
|
|
@@ -29760,7 +30091,7 @@ function youTrackTokenCreateUrl() {
|
|
|
29760
30091
|
const desc = String(defaults.description ?? "OpenCode workit — /wk-issue-update and /wk-meetings");
|
|
29761
30092
|
const scopes = Array.isArray(defaults.scopes) ? defaults.scopes : ["YouTrack"];
|
|
29762
30093
|
const base = String(config.baseUrl ?? "https://enghouseamg.youtrack.cloud").replace(/\/+$/, "");
|
|
29763
|
-
const tokenFile = String(config.tokenFile ??
|
|
30094
|
+
const tokenFile = String(config.tokenFile ?? path22.join(path22.dirname(loaded.path), "youtrack.token"));
|
|
29764
30095
|
const tab = String(defaults.profileTab ?? "account-security");
|
|
29765
30096
|
const createUrl = `${base}/users/me?${new URLSearchParams({ tab })}`;
|
|
29766
30097
|
const docsUrl = "https://www.jetbrains.com/help/youtrack/cloud/manage-permanent-token.html";
|
|
@@ -29769,7 +30100,7 @@ function youTrackTokenCreateUrl() {
|
|
|
29769
30100
|
tokenName: name,
|
|
29770
30101
|
tokenDescription: desc,
|
|
29771
30102
|
scopes,
|
|
29772
|
-
tokenFile:
|
|
30103
|
+
tokenFile: path22.resolve(tokenFile),
|
|
29773
30104
|
createUrl,
|
|
29774
30105
|
docsUrl,
|
|
29775
30106
|
prefillSupported: false,
|
|
@@ -30087,12 +30418,12 @@ var resolvePath = (p) => {
|
|
|
30087
30418
|
try {
|
|
30088
30419
|
return fs11.realpathSync(p);
|
|
30089
30420
|
} catch {
|
|
30090
|
-
return
|
|
30421
|
+
return path23.resolve(p);
|
|
30091
30422
|
}
|
|
30092
30423
|
};
|
|
30093
30424
|
function initStatusData(configDirPath = configDir()) {
|
|
30094
|
-
const ytJson =
|
|
30095
|
-
const vcsJson =
|
|
30425
|
+
const ytJson = path23.join(configDirPath, "youtrack.json");
|
|
30426
|
+
const vcsJson = path23.join(configDirPath, "vcs.json");
|
|
30096
30427
|
const items = [];
|
|
30097
30428
|
let youtrackConfig = null;
|
|
30098
30429
|
let youtrackTokenCreate = null;
|
|
@@ -30115,8 +30446,8 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30115
30446
|
});
|
|
30116
30447
|
}
|
|
30117
30448
|
}
|
|
30118
|
-
const expanded = tokenFile ?
|
|
30119
|
-
const resolvedTokenFile = expanded && fs11.existsSync(expanded) ? resolvePath(expanded) : expanded ?
|
|
30449
|
+
const expanded = tokenFile ? path23.resolve(tokenFile) : null;
|
|
30450
|
+
const resolvedTokenFile = expanded && fs11.existsSync(expanded) ? resolvePath(expanded) : expanded ? path23.isAbsolute(expanded) ? expanded : path23.resolve(configDirPath, expanded) : null;
|
|
30120
30451
|
youtrackConfig = {
|
|
30121
30452
|
config_edit_path: resolvePath(ytJson),
|
|
30122
30453
|
baseUrl: base,
|
|
@@ -30163,11 +30494,11 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30163
30494
|
id: "youtrack_json",
|
|
30164
30495
|
label: "YouTrack config",
|
|
30165
30496
|
ok: fs11.existsSync(ytJson) && youtrackConfig !== null && !("error" in youtrackConfig),
|
|
30166
|
-
path: fs11.existsSync(ytJson) ? resolvePath(ytJson) :
|
|
30497
|
+
path: fs11.existsSync(ytJson) ? resolvePath(ytJson) : path23.resolve(ytJson),
|
|
30167
30498
|
config_edit_path: resolvePath(ytJson),
|
|
30168
30499
|
fix: "workit_init_apply action=youtrack_scaffold"
|
|
30169
30500
|
});
|
|
30170
|
-
const ytTokenPath = youtrackConfig?.tokenFile ??
|
|
30501
|
+
const ytTokenPath = youtrackConfig?.tokenFile ?? path23.join(configDirPath, "youtrack.token");
|
|
30171
30502
|
const tokenText = fs11.existsSync(ytTokenPath) ? fs11.readFileSync(ytTokenPath, "utf8").trim() : "";
|
|
30172
30503
|
const placeholder = fs11.existsSync(ytTokenPath) && isPlaceholder(tokenText);
|
|
30173
30504
|
const tokenOk = fs11.existsSync(ytTokenPath) && modeOk(ytTokenPath) && Boolean(tokenText) && !isPlaceholder(tokenText);
|
|
@@ -30175,8 +30506,8 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30175
30506
|
id: "youtrack_token",
|
|
30176
30507
|
label: "YouTrack API token (mode 600, not placeholder)",
|
|
30177
30508
|
ok: tokenOk,
|
|
30178
|
-
path: fs11.existsSync(ytTokenPath) ? resolvePath(ytTokenPath) :
|
|
30179
|
-
token_edit_path: fs11.existsSync(ytTokenPath) ? resolvePath(ytTokenPath) :
|
|
30509
|
+
path: fs11.existsSync(ytTokenPath) ? resolvePath(ytTokenPath) : path23.resolve(ytTokenPath),
|
|
30510
|
+
token_edit_path: fs11.existsSync(ytTokenPath) ? resolvePath(ytTokenPath) : path23.resolve(ytTokenPath),
|
|
30180
30511
|
placeholder,
|
|
30181
30512
|
fix: `Open ${resolvePath(ytTokenPath)} — replace ${TOKEN_PLACEHOLDER3} with your permanent token, save, then /wk-status`
|
|
30182
30513
|
};
|
|
@@ -30202,7 +30533,7 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30202
30533
|
const provider = typeof rawProvider === "string" && rawProvider.trim() ? rawProvider.toLowerCase() : null;
|
|
30203
30534
|
const tokenFiles = {};
|
|
30204
30535
|
for (const k of ["gitlab", "github"]) {
|
|
30205
|
-
tokenFiles[k] = String(vcsParsed[k]?.tokenFile ??
|
|
30536
|
+
tokenFiles[k] = String(vcsParsed[k]?.tokenFile ?? path23.join(configDirPath, `${k}.token`));
|
|
30206
30537
|
}
|
|
30207
30538
|
vcsCfg = {
|
|
30208
30539
|
config_edit_path: resolvePath(vcsJson),
|
|
@@ -30227,14 +30558,14 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30227
30558
|
id: "vcs_json",
|
|
30228
30559
|
label: "VCS config (GitLab / GitHub)",
|
|
30229
30560
|
ok: fs11.existsSync(vcsJson) && vcsCfg !== null && !("error" in vcsCfg),
|
|
30230
|
-
path: fs11.existsSync(vcsJson) ? resolvePath(vcsJson) :
|
|
30561
|
+
path: fs11.existsSync(vcsJson) ? resolvePath(vcsJson) : path23.resolve(vcsJson),
|
|
30231
30562
|
config_edit_path: resolvePath(vcsJson),
|
|
30232
30563
|
fix: "workit_init_apply action=vcs_scaffold"
|
|
30233
30564
|
});
|
|
30234
30565
|
const provActive = vcsCfg && !("error" in vcsCfg) ? vcsCfg.provider : null;
|
|
30235
30566
|
const tokenItem = (tid, label, rawPath, providerKey) => {
|
|
30236
|
-
const t =
|
|
30237
|
-
const abs = fs11.existsSync(t) ? resolvePath(t) :
|
|
30567
|
+
const t = path23.isAbsolute(rawPath) ? rawPath : path23.resolve(configDirPath, rawPath);
|
|
30568
|
+
const abs = fs11.existsSync(t) ? resolvePath(t) : path23.resolve(t);
|
|
30238
30569
|
const text = fs11.existsSync(t) ? fs11.readFileSync(t, "utf8").trim() : "";
|
|
30239
30570
|
const ph = isPlaceholder(text);
|
|
30240
30571
|
const ok = fs11.existsSync(t) && modeOk(t) && Boolean(text) && !ph;
|
|
@@ -30265,7 +30596,7 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30265
30596
|
};
|
|
30266
30597
|
const vcsTokenFiles = {};
|
|
30267
30598
|
for (const k of ["gitlab", "github"]) {
|
|
30268
|
-
vcsTokenFiles[k] = String(vcsParsed?.[k]?.tokenFile ??
|
|
30599
|
+
vcsTokenFiles[k] = String(vcsParsed?.[k]?.tokenFile ?? path23.join(configDirPath, `${k}.token`));
|
|
30269
30600
|
}
|
|
30270
30601
|
items.push(tokenItem("gitlab_token", "GitLab token (mode 600, not placeholder)", vcsTokenFiles.gitlab, "gitlab"));
|
|
30271
30602
|
items.push(tokenItem("github_token", "GitHub token (mode 600, not placeholder)", vcsTokenFiles.github, "github"));
|
|
@@ -30322,7 +30653,7 @@ async function toolkitStatusData(configDirPath = configDir()) {
|
|
|
30322
30653
|
}
|
|
30323
30654
|
var youtrackJsonContent = (dir) => ({
|
|
30324
30655
|
baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://enghouseamg.youtrack.cloud",
|
|
30325
|
-
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ??
|
|
30656
|
+
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path23.join(dir, "youtrack.token"),
|
|
30326
30657
|
timezone: process.env.WORKFLOW_YT_TIMEZONE ?? "America/Santiago",
|
|
30327
30658
|
locale: "es-CL",
|
|
30328
30659
|
defaultMention: process.env.WORKFLOW_YT_MENTION ?? "Alejandra.Flores",
|
|
@@ -30361,11 +30692,11 @@ var vcsJsonContent = (dir, cwd) => {
|
|
|
30361
30692
|
gitlab: {
|
|
30362
30693
|
host: process.env.WORKFLOW_GITLAB_HOST ?? "gitlab.com",
|
|
30363
30694
|
apiUrl: process.env.WORKFLOW_GITLAB_API_URL ?? "https://gitlab.com/api/v4",
|
|
30364
|
-
tokenFile:
|
|
30695
|
+
tokenFile: path23.join(dir, "gitlab.token")
|
|
30365
30696
|
},
|
|
30366
30697
|
github: {
|
|
30367
30698
|
host: process.env.WORKFLOW_GITHUB_HOST ?? "github.com",
|
|
30368
|
-
tokenFile:
|
|
30699
|
+
tokenFile: path23.join(dir, "github.token")
|
|
30369
30700
|
},
|
|
30370
30701
|
pr: { squashOnMerge: true, removeSourceBranch: true, pushBranch: true, confirmSkip: true },
|
|
30371
30702
|
tokenDefaults: {
|
|
@@ -30382,16 +30713,16 @@ function initApplyData(action, env = process.env) {
|
|
|
30382
30713
|
fs11.mkdirSync(dir, { recursive: true });
|
|
30383
30714
|
switch (action) {
|
|
30384
30715
|
case "youtrack_json": {
|
|
30385
|
-
const out =
|
|
30716
|
+
const out = path23.join(dir, "youtrack.json");
|
|
30386
30717
|
fs11.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
30387
30718
|
`, "utf8");
|
|
30388
30719
|
return { action, ok: true, path: out };
|
|
30389
30720
|
}
|
|
30390
30721
|
case "youtrack_token_placeholder": {
|
|
30391
|
-
const p =
|
|
30722
|
+
const p = path23.join(dir, "youtrack.token");
|
|
30392
30723
|
const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER3 + `
|
|
30393
30724
|
`, 384) === "preserved";
|
|
30394
|
-
const abs =
|
|
30725
|
+
const abs = path23.resolve(p);
|
|
30395
30726
|
return {
|
|
30396
30727
|
action,
|
|
30397
30728
|
ok: true,
|
|
@@ -30403,14 +30734,14 @@ function initApplyData(action, env = process.env) {
|
|
|
30403
30734
|
};
|
|
30404
30735
|
}
|
|
30405
30736
|
case "youtrack_scaffold": {
|
|
30406
|
-
const jsonOut =
|
|
30407
|
-
const tokenOut =
|
|
30737
|
+
const jsonOut = path23.join(dir, "youtrack.json");
|
|
30738
|
+
const tokenOut = path23.join(dir, "youtrack.token");
|
|
30408
30739
|
fs11.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
30409
30740
|
`, "utf8");
|
|
30410
30741
|
const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER3 + `
|
|
30411
30742
|
`, 384) === "preserved";
|
|
30412
|
-
const configPath =
|
|
30413
|
-
const tokenPath =
|
|
30743
|
+
const configPath = path23.resolve(jsonOut);
|
|
30744
|
+
const tokenPath = path23.resolve(tokenOut);
|
|
30414
30745
|
const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
|
|
30415
30746
|
process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
|
|
30416
30747
|
let tokenCreate = {};
|
|
@@ -30464,19 +30795,19 @@ function initApplyData(action, env = process.env) {
|
|
|
30464
30795
|
}
|
|
30465
30796
|
}
|
|
30466
30797
|
case "vcs_scaffold": {
|
|
30467
|
-
const jsonOut =
|
|
30798
|
+
const jsonOut = path23.join(dir, "vcs.json");
|
|
30468
30799
|
fs11.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(dir), null, 2) + `
|
|
30469
30800
|
`, "utf8");
|
|
30470
|
-
const glPath =
|
|
30471
|
-
const ghPath =
|
|
30801
|
+
const glPath = path23.join(dir, "gitlab.token");
|
|
30802
|
+
const ghPath = path23.join(dir, "github.token");
|
|
30472
30803
|
const preservedTokens = [];
|
|
30473
30804
|
for (const p of [glPath, ghPath]) {
|
|
30474
30805
|
if (writeFileExclusive(p, TOKEN_PLACEHOLDER3 + `
|
|
30475
30806
|
`, 384) === "preserved") {
|
|
30476
|
-
preservedTokens.push(
|
|
30807
|
+
preservedTokens.push(path23.resolve(p));
|
|
30477
30808
|
}
|
|
30478
30809
|
}
|
|
30479
|
-
const configPath =
|
|
30810
|
+
const configPath = path23.resolve(jsonOut);
|
|
30480
30811
|
const prev = process.env.WORKFLOW_VCS_CONFIG;
|
|
30481
30812
|
process.env.WORKFLOW_VCS_CONFIG = configPath;
|
|
30482
30813
|
try {
|
|
@@ -30485,14 +30816,14 @@ function initApplyData(action, env = process.env) {
|
|
|
30485
30816
|
const provider = typeof rawProvider === "string" && rawProvider.trim() ? rawProvider : null;
|
|
30486
30817
|
const tokenUrls = vcsTokenCreateUrls();
|
|
30487
30818
|
const active = tokenUrls.active ?? {};
|
|
30488
|
-
const activePath = provider === "gitlab" ?
|
|
30819
|
+
const activePath = provider === "gitlab" ? path23.resolve(glPath) : provider === "github" ? path23.resolve(ghPath) : null;
|
|
30489
30820
|
return {
|
|
30490
30821
|
action,
|
|
30491
30822
|
ok: true,
|
|
30492
30823
|
vcs_json: configPath,
|
|
30493
30824
|
config_edit_path: configPath,
|
|
30494
|
-
gitlab_token:
|
|
30495
|
-
github_token:
|
|
30825
|
+
gitlab_token: path23.resolve(glPath),
|
|
30826
|
+
github_token: path23.resolve(ghPath),
|
|
30496
30827
|
token_edit_path: activePath,
|
|
30497
30828
|
token_create_url: active.createUrl,
|
|
30498
30829
|
token_create_urls: tokenUrls,
|
|
@@ -30547,6 +30878,109 @@ var normalizeLegacyResult = (value) => {
|
|
|
30547
30878
|
return ok(data);
|
|
30548
30879
|
};
|
|
30549
30880
|
|
|
30881
|
+
// packages/workit-opencode/src/shared/init-apply.ts
|
|
30882
|
+
import path24 from "node:path";
|
|
30883
|
+
|
|
30884
|
+
// packages/workit-opencode/src/shared/repo-result.ts
|
|
30885
|
+
var output = (value) => JSON.stringify(value, null, 2);
|
|
30886
|
+
var diagnostics = ({ stdout, stderr, exitCode }) => ({
|
|
30887
|
+
stdout,
|
|
30888
|
+
stderr,
|
|
30889
|
+
exitCode
|
|
30890
|
+
});
|
|
30891
|
+
var requireConfirmed = (confirmed) => {
|
|
30892
|
+
if (confirmed === true)
|
|
30893
|
+
return null;
|
|
30894
|
+
return output(fail("confirmed: true required"));
|
|
30895
|
+
};
|
|
30896
|
+
function scriptResult(result, parse) {
|
|
30897
|
+
if (result.exitCode !== 0) {
|
|
30898
|
+
return fail(result.stderr.trim() || result.stdout.trim() || "workflow script failed", diagnostics(result));
|
|
30899
|
+
}
|
|
30900
|
+
try {
|
|
30901
|
+
return ok({
|
|
30902
|
+
...parse(result.stdout),
|
|
30903
|
+
exitCode: 0,
|
|
30904
|
+
...result.stderr ? { stderr: result.stderr } : {}
|
|
30905
|
+
});
|
|
30906
|
+
} catch (error) {
|
|
30907
|
+
return fail(error instanceof Error ? error.message : "workflow output parse failed", diagnostics(result));
|
|
30908
|
+
}
|
|
30909
|
+
}
|
|
30910
|
+
var legacyScriptResult = (result) => {
|
|
30911
|
+
let parsed = null;
|
|
30912
|
+
try {
|
|
30913
|
+
parsed = JSON.parse(result.stdout.trim());
|
|
30914
|
+
} catch {}
|
|
30915
|
+
if (result.exitCode !== 0 || parsed?.error || parsed?.ok === false) {
|
|
30916
|
+
const error = parsed?.error ? String(parsed.error) : parsed?.ok === false ? "legacy operation reported failure" : result.stderr.trim() || result.stdout.trim() || "workflow script failed";
|
|
30917
|
+
return fail(error, diagnostics(result));
|
|
30918
|
+
}
|
|
30919
|
+
if (!parsed)
|
|
30920
|
+
return fail("workflow output parse failed", diagnostics(result));
|
|
30921
|
+
const { ok: _legacyOk, ...data } = parsed;
|
|
30922
|
+
return ok({ ...data, exitCode: 0, ...result.stderr ? { stderr: result.stderr } : {} });
|
|
30923
|
+
};
|
|
30924
|
+
|
|
30925
|
+
// packages/workit-opencode/src/shared/init-apply.ts
|
|
30926
|
+
var initApplyRuntime = {
|
|
30927
|
+
initApply: (root, action, env) => {
|
|
30928
|
+
const out = initApply({ action, confirmed: true, env });
|
|
30929
|
+
return {
|
|
30930
|
+
exitCode: out.error ? 1 : 0,
|
|
30931
|
+
stdout: JSON.stringify(out.data ?? out),
|
|
30932
|
+
stderr: "",
|
|
30933
|
+
cwd: root
|
|
30934
|
+
};
|
|
30935
|
+
}
|
|
30936
|
+
};
|
|
30937
|
+
var executeInitApply = (args, directory, runtime) => {
|
|
30938
|
+
const rejected = requireConfirmed(args.confirmed);
|
|
30939
|
+
if (rejected)
|
|
30940
|
+
return rejected;
|
|
30941
|
+
const action = args.action;
|
|
30942
|
+
if (action === "hygiene") {
|
|
30943
|
+
const result = ensureHygieneFiles(directory, {
|
|
30944
|
+
confirmed: args.confirmed,
|
|
30945
|
+
includeOpenSource: args.include_open_source
|
|
30946
|
+
});
|
|
30947
|
+
return output(result.ok ? ok(result) : fail(result.error));
|
|
30948
|
+
}
|
|
30949
|
+
if (action === "gitignore") {
|
|
30950
|
+
const result = ensureProjectGitignore(directory, args.confirmed);
|
|
30951
|
+
return output(result.ok ? ok(result) : fail(result.error));
|
|
30952
|
+
}
|
|
30953
|
+
if (action === "config") {
|
|
30954
|
+
const LOCALE_RE = /^[a-z]{2,3}(-(?:[A-Z]{2}|[0-9]{3}))?$/;
|
|
30955
|
+
if (args.locale !== undefined && !LOCALE_RE.test(args.locale)) {
|
|
30956
|
+
return output(fail(`invalid locale: ${JSON.stringify(args.locale)} — expected BCP-47 like en or es-CL`));
|
|
30957
|
+
}
|
|
30958
|
+
const current = readConfig();
|
|
30959
|
+
const next = mergeConfigValues({
|
|
30960
|
+
locale: args.locale,
|
|
30961
|
+
localeOptions: args.locale_options,
|
|
30962
|
+
timezone: args.timezone,
|
|
30963
|
+
preset: args.branch_policy_preset,
|
|
30964
|
+
allowed: args.branch_policy_allowed,
|
|
30965
|
+
protectedNames: args.branch_policy_protected
|
|
30966
|
+
}, current);
|
|
30967
|
+
writeConfig(next);
|
|
30968
|
+
return output(ok({ action: "config", path: path24.join(configDir(), "config.json"), ...next }));
|
|
30969
|
+
}
|
|
30970
|
+
const env = Object.fromEntries(Object.entries({
|
|
30971
|
+
WORKFLOW_YT_BASE_URL: args.base_url,
|
|
30972
|
+
WORKFLOW_YT_MENTION: args.default_mention,
|
|
30973
|
+
WORKFLOW_YT_MEETING_ISSUE: args.meeting_issue,
|
|
30974
|
+
WORKFLOW_VCS_PROVIDER: args.vcs_provider,
|
|
30975
|
+
WORKFLOW_VCS_TARGET_BRANCH: args.vcs_target_branch,
|
|
30976
|
+
WORKFLOW_WORKSPACE_ROOT: directory,
|
|
30977
|
+
WORKFLOW_BP_NAME: args.name,
|
|
30978
|
+
WORKFLOW_BP_DEVELOP: args.develop_branch,
|
|
30979
|
+
WORKFLOW_BP_INTEGRATION: args.integration
|
|
30980
|
+
}).filter((entry) => entry[1] !== undefined));
|
|
30981
|
+
return output(legacyScriptResult(runtime.initApply(directory, action, env)));
|
|
30982
|
+
};
|
|
30983
|
+
|
|
30550
30984
|
// packages/workit-opencode/src/tools/repo.ts
|
|
30551
30985
|
var defaultRuntime = {
|
|
30552
30986
|
git: (root, args) => run2(root, "git", args),
|
|
@@ -30564,15 +30998,7 @@ var defaultRuntime = {
|
|
|
30564
30998
|
cwd: root
|
|
30565
30999
|
};
|
|
30566
31000
|
},
|
|
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
|
-
},
|
|
31001
|
+
...initApplyRuntime,
|
|
30576
31002
|
initStatus: (root) => ({
|
|
30577
31003
|
exitCode: 0,
|
|
30578
31004
|
stdout: JSON.stringify(initStatusData()),
|
|
@@ -30586,36 +31012,7 @@ var defaultRuntime = {
|
|
|
30586
31012
|
cwd: root
|
|
30587
31013
|
})
|
|
30588
31014
|
};
|
|
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
31015
|
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
31016
|
var optionalJson = (value) => {
|
|
30620
31017
|
if (!value?.trim())
|
|
30621
31018
|
return null;
|
|
@@ -30770,7 +31167,7 @@ function createRepoTools(runtime = defaultRuntime) {
|
|
|
30770
31167
|
entries,
|
|
30771
31168
|
path: changelogPath,
|
|
30772
31169
|
normalize_only,
|
|
30773
|
-
workspace_root:
|
|
31170
|
+
workspace_root: realpathSync9(context.directory)
|
|
30774
31171
|
})));
|
|
30775
31172
|
}
|
|
30776
31173
|
}),
|
|
@@ -30891,77 +31288,11 @@ function createRepoTools(runtime = defaultRuntime) {
|
|
|
30891
31288
|
branch_policy_protected: tool.schema.array(tool.schema.string()).optional(),
|
|
30892
31289
|
include_open_source: tool.schema.boolean().optional()
|
|
30893
31290
|
},
|
|
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
|
-
}
|
|
31291
|
+
execute: async (args, context) => executeInitApply(args, context.directory, runtime)
|
|
30957
31292
|
})
|
|
30958
31293
|
};
|
|
30959
31294
|
}
|
|
30960
31295
|
|
|
30961
|
-
// packages/workit-opencode/src/tools/workit.ts
|
|
30962
|
-
import { realpathSync as realpathSync9 } from "node:fs";
|
|
30963
|
-
import path24 from "node:path";
|
|
30964
|
-
|
|
30965
31296
|
// packages/workit-core/src/core/tracker-issues.ts
|
|
30966
31297
|
import fs12 from "node:fs";
|
|
30967
31298
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
@@ -31233,7 +31564,7 @@ async function logTimeUpdate(input, operation = defaultOperations) {
|
|
|
31233
31564
|
|
|
31234
31565
|
// packages/workit-core/src/core/external-action-effects.ts
|
|
31235
31566
|
import fs13 from "node:fs";
|
|
31236
|
-
import
|
|
31567
|
+
import path25 from "node:path";
|
|
31237
31568
|
|
|
31238
31569
|
// packages/workit-core/src/core/commit-flavors.ts
|
|
31239
31570
|
var CONVENTIONAL_RE = /^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?(!)?: .+/;
|
|
@@ -32060,7 +32391,7 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
32060
32391
|
if (preview.error)
|
|
32061
32392
|
return failure("invalid_input", preview.error);
|
|
32062
32393
|
const target = String(preview.path);
|
|
32063
|
-
const relative =
|
|
32394
|
+
const relative = path25.relative(fs13.realpathSync(root), target).replaceAll(path25.sep, "/") || "CHANGELOG.md";
|
|
32064
32395
|
const normalized = {
|
|
32065
32396
|
operation: request.operation,
|
|
32066
32397
|
payload: {
|
|
@@ -32316,24 +32647,21 @@ var executeResolvedExternalAction = async (resolved, root, step, caller) => {
|
|
|
32316
32647
|
return executeConcreteExternalAction(fresh.data.request, root, fresh.data.marker, resolvedDate, step, typeof workText === "string" ? workText : undefined, caller, beforeDigest, beforeExists);
|
|
32317
32648
|
};
|
|
32318
32649
|
|
|
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) => {
|
|
32650
|
+
// packages/workit-opencode/src/shared/session.ts
|
|
32651
|
+
import { realpathSync as realpathSync10 } from "node:fs";
|
|
32652
|
+
import path26 from "node:path";
|
|
32653
|
+
var sameWorkspace = (expected, observed) => {
|
|
32654
|
+
if (typeof observed !== "string" || !observed)
|
|
32655
|
+
return false;
|
|
32330
32656
|
try {
|
|
32331
|
-
|
|
32332
|
-
return typeof value.operation === "string" && value.operation.length > 0;
|
|
32657
|
+
return realpathSync10(expected) === realpathSync10(observed);
|
|
32333
32658
|
} catch {
|
|
32334
|
-
return
|
|
32659
|
+
return path26.resolve(expected) === path26.resolve(observed);
|
|
32335
32660
|
}
|
|
32336
32661
|
};
|
|
32662
|
+
|
|
32663
|
+
// packages/workit-opencode/src/shared/decision-content.ts
|
|
32664
|
+
var rejectedDescription = "Reject this decision";
|
|
32337
32665
|
var decisionContent = (purpose, question, approvedContent) => ({
|
|
32338
32666
|
header: `Workit decision: ${purpose}`,
|
|
32339
32667
|
question,
|
|
@@ -32342,6 +32670,24 @@ var decisionContent = (purpose, question, approvedContent) => ({
|
|
|
32342
32670
|
{ label: "rejected", description: rejectedDescription }
|
|
32343
32671
|
]
|
|
32344
32672
|
});
|
|
32673
|
+
var isSelfAuthorizingActionContent = (content) => {
|
|
32674
|
+
try {
|
|
32675
|
+
const value = JSON.parse(content);
|
|
32676
|
+
return typeof value.operation === "string" && value.operation.length > 0;
|
|
32677
|
+
} catch {
|
|
32678
|
+
return /^[a-z][a-z_]*\.[a-z_]+$/.test(content);
|
|
32679
|
+
}
|
|
32680
|
+
};
|
|
32681
|
+
|
|
32682
|
+
// packages/workit-opencode/src/tools/workit.ts
|
|
32683
|
+
var sessionParent = (session) => {
|
|
32684
|
+
if (typeof session !== "object" || session === null)
|
|
32685
|
+
return null;
|
|
32686
|
+
if (!Object.prototype.hasOwnProperty.call(session, "parentID"))
|
|
32687
|
+
return;
|
|
32688
|
+
const parentID = session.parentID;
|
|
32689
|
+
return typeof parentID === "string" && parentID.length > 0 ? parentID : null;
|
|
32690
|
+
};
|
|
32345
32691
|
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
32692
|
var normalizeReceiptLabel = (label) => label.trim().replace(/(?:\s*[([]\s*[^()[\]]*\s*[)\]]\s*)+$/u, "").trim().toLowerCase();
|
|
32347
32693
|
var purposeForQuestion = (question) => {
|
|
@@ -32540,15 +32886,6 @@ var sessionData = async (client, sessionID) => {
|
|
|
32540
32886
|
return null;
|
|
32541
32887
|
}
|
|
32542
32888
|
};
|
|
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
32889
|
var hostRef = (handle) => ({ kind: "host", host: "opencode", handle });
|
|
32553
32890
|
var opencodeCapabilities = () => [
|
|
32554
32891
|
{
|
|
@@ -32962,6 +33299,16 @@ var createWorkitTools = ({
|
|
|
32962
33299
|
}
|
|
32963
33300
|
}
|
|
32964
33301
|
const autoApplies = !selected.ok && !planAuthorized && standingAutoApplies(store, "opencode", context.sessionID, descriptor);
|
|
33302
|
+
const planCount = planReservationLength(resolved.data.request.operation, resolved.data.descriptorPayload);
|
|
33303
|
+
if (planCount !== null) {
|
|
33304
|
+
if (selected.ok)
|
|
33305
|
+
return output2(success(null, null, { plan_commits: planCount }));
|
|
33306
|
+
if (autoApplies) {
|
|
33307
|
+
const bound = standingAutoBinding(core, store, "opencode", context.sessionID, descriptor);
|
|
33308
|
+
if (bound)
|
|
33309
|
+
return output2(success(null, null, { plan_commits: planCount }));
|
|
33310
|
+
}
|
|
33311
|
+
}
|
|
32965
33312
|
if (!selected.ok && !planAuthorized && !autoApplies) {
|
|
32966
33313
|
if (selected.error.startsWith("no approved action")) {
|
|
32967
33314
|
const proposal = actionProposalQuestion(resolved.data.request, resolved.data.descriptorPayload);
|
|
@@ -33090,9 +33437,9 @@ var loadProvenance = (logger, pkgUrl) => {
|
|
|
33090
33437
|
}
|
|
33091
33438
|
};
|
|
33092
33439
|
|
|
33093
|
-
// packages/workit-opencode/src/
|
|
33094
|
-
var root =
|
|
33095
|
-
var skillsPath =
|
|
33440
|
+
// packages/workit-opencode/src/v1/server.ts
|
|
33441
|
+
var root = assetsRoot();
|
|
33442
|
+
var skillsPath = path27.join(root, "skills");
|
|
33096
33443
|
var logger = createLogger({
|
|
33097
33444
|
appLog: () => {
|
|
33098
33445
|
return;
|
|
@@ -33111,7 +33458,7 @@ var warnStaleSources = () => {
|
|
|
33111
33458
|
logger.warn(EVENT.hooks, {
|
|
33112
33459
|
boundary: "stale_sources",
|
|
33113
33460
|
reason: "workit sources changed after plugin load; restart the session for latest behavior",
|
|
33114
|
-
files: changed.map((file) =>
|
|
33461
|
+
files: changed.map((file) => path27.basename(file))
|
|
33115
33462
|
});
|
|
33116
33463
|
} catch {}
|
|
33117
33464
|
};
|
|
@@ -33140,7 +33487,7 @@ var plugin = async ({ client, directory }) => {
|
|
|
33140
33487
|
const dispatches = new Map;
|
|
33141
33488
|
try {
|
|
33142
33489
|
logger.info(EVENT.initialization, { host: "opencode", plugin_root: root });
|
|
33143
|
-
logger.info(EVENT.provenance, loadProvenance(logger,
|
|
33490
|
+
logger.info(EVENT.provenance, loadProvenance(logger, path27.join(packageRoot2(), "package.json")));
|
|
33144
33491
|
} catch (error) {
|
|
33145
33492
|
logger.warn(EVENT.hooks, { boundary: "initialization", ...errorDetail(error) });
|
|
33146
33493
|
}
|
|
@@ -33442,7 +33789,7 @@ var plugin = async ({ client, directory }) => {
|
|
|
33442
33789
|
config: async (config) => {
|
|
33443
33790
|
const mutable = config;
|
|
33444
33791
|
const paths = [...mutable.skills?.paths ?? []];
|
|
33445
|
-
if (
|
|
33792
|
+
if (existsSync14(skillsPath) && !paths.includes(skillsPath))
|
|
33446
33793
|
paths.push(skillsPath);
|
|
33447
33794
|
mutable.skills ??= {};
|
|
33448
33795
|
mutable.skills.paths = paths;
|
|
@@ -33512,7 +33859,835 @@ var plugin = async ({ client, directory }) => {
|
|
|
33512
33859
|
}
|
|
33513
33860
|
};
|
|
33514
33861
|
};
|
|
33515
|
-
var
|
|
33862
|
+
var server_default = plugin;
|
|
33863
|
+
|
|
33864
|
+
// node_modules/@opencode/plugin/dist/promise/plugin.js
|
|
33865
|
+
function define(plugin) {
|
|
33866
|
+
return plugin;
|
|
33867
|
+
}
|
|
33868
|
+
|
|
33869
|
+
// packages/workit-opencode/src/shared/tools.ts
|
|
33870
|
+
var objectSchema = (properties, required) => ({
|
|
33871
|
+
type: "object",
|
|
33872
|
+
properties,
|
|
33873
|
+
required,
|
|
33874
|
+
additionalProperties: false
|
|
33875
|
+
});
|
|
33876
|
+
var WORKIT_EXTERNAL_ACTION_OPERATIONS = [
|
|
33877
|
+
"git.branch_setup",
|
|
33878
|
+
"git.commit",
|
|
33879
|
+
"git.push",
|
|
33880
|
+
"hosting.pull_request",
|
|
33881
|
+
"hosting.merge",
|
|
33882
|
+
"youtrack.update",
|
|
33883
|
+
"youtrack.time",
|
|
33884
|
+
"youtrack.meeting",
|
|
33885
|
+
"changelog.apply",
|
|
33886
|
+
"context.read"
|
|
33887
|
+
];
|
|
33888
|
+
var familyTools = OPERATION_FAMILIES.map((family) => ({
|
|
33889
|
+
name: `workit_${family}`,
|
|
33890
|
+
description: `Workit ${family} operations backed by the shared task contract.`,
|
|
33891
|
+
input: { type: "object", ...boundedOperationJsonSchema(family, OPERATION_SCHEMA_DEPTH) }
|
|
33892
|
+
}));
|
|
33893
|
+
var WORKIT_TOOL_CATALOG = [
|
|
33894
|
+
...familyTools,
|
|
33895
|
+
{
|
|
33896
|
+
name: "workit_external_action",
|
|
33897
|
+
description: `Run one fixed optional action. ${externalActionHelp}`,
|
|
33898
|
+
input: objectSchema({
|
|
33899
|
+
operation: {
|
|
33900
|
+
type: "string",
|
|
33901
|
+
enum: [...WORKIT_EXTERNAL_ACTION_OPERATIONS],
|
|
33902
|
+
description: "Fixed optional action to run."
|
|
33903
|
+
},
|
|
33904
|
+
payload: {
|
|
33905
|
+
type: "object",
|
|
33906
|
+
description: "Operation-specific payload; Workit validates it.",
|
|
33907
|
+
additionalProperties: true
|
|
33908
|
+
}
|
|
33909
|
+
}, ["operation", "payload"])
|
|
33910
|
+
},
|
|
33911
|
+
{
|
|
33912
|
+
name: "workit_init_apply",
|
|
33913
|
+
description: "Apply a confirmed toolkit initialization action",
|
|
33914
|
+
input: objectSchema({
|
|
33915
|
+
confirmed: { type: "boolean" },
|
|
33916
|
+
action: {
|
|
33917
|
+
type: "string",
|
|
33918
|
+
enum: [
|
|
33919
|
+
"youtrack_scaffold",
|
|
33920
|
+
"youtrack_json",
|
|
33921
|
+
"youtrack_token_placeholder",
|
|
33922
|
+
"vcs_scaffold",
|
|
33923
|
+
"config",
|
|
33924
|
+
"gitignore",
|
|
33925
|
+
"hygiene",
|
|
33926
|
+
"branch_policy"
|
|
33927
|
+
]
|
|
33928
|
+
},
|
|
33929
|
+
base_url: { type: "string" },
|
|
33930
|
+
default_mention: { type: "string" },
|
|
33931
|
+
meeting_issue: { type: "string" },
|
|
33932
|
+
vcs_provider: { type: "string", enum: ["gitlab", "github"] },
|
|
33933
|
+
vcs_target_branch: { type: "string" },
|
|
33934
|
+
name: { type: "string" },
|
|
33935
|
+
develop_branch: { type: "string" },
|
|
33936
|
+
integration: { type: "string", enum: ["pr", "merge"] },
|
|
33937
|
+
locale: { type: "string" },
|
|
33938
|
+
locale_options: { type: "array", items: { type: "string" } },
|
|
33939
|
+
timezone: { type: "string" },
|
|
33940
|
+
branch_policy_preset: {
|
|
33941
|
+
type: "string",
|
|
33942
|
+
enum: ["gitflow", "github-flow", "trunk-based", "custom"]
|
|
33943
|
+
},
|
|
33944
|
+
branch_policy_allowed: { type: "array", items: { type: "string" } },
|
|
33945
|
+
branch_policy_protected: { type: "array", items: { type: "string" } },
|
|
33946
|
+
include_open_source: { type: "boolean" }
|
|
33947
|
+
}, ["confirmed", "action"])
|
|
33948
|
+
}
|
|
33949
|
+
];
|
|
33950
|
+
var workitFamilyOf = (toolName) => {
|
|
33951
|
+
for (const family of OPERATION_FAMILIES)
|
|
33952
|
+
if (toolName === `workit_${family}`)
|
|
33953
|
+
return family;
|
|
33954
|
+
return null;
|
|
33955
|
+
};
|
|
33956
|
+
|
|
33957
|
+
// packages/workit-opencode/src/v2/lifecycle.ts
|
|
33958
|
+
var record3 = (value) => typeof value === "object" && value !== null ? value : {};
|
|
33959
|
+
var sessionIDOf = (value) => typeof value === "string" && value.length > 0 ? value : null;
|
|
33960
|
+
var parentOf = (session) => typeof session?.parentID === "string" && session.parentID ? session.parentID : undefined;
|
|
33961
|
+
var contentText = (content) => {
|
|
33962
|
+
if (typeof content === "string")
|
|
33963
|
+
return content;
|
|
33964
|
+
if (!Array.isArray(content))
|
|
33965
|
+
return "";
|
|
33966
|
+
return content.map((part) => record3(part).type === "text" ? String(part.text ?? "") : "").join(`
|
|
33967
|
+
`);
|
|
33968
|
+
};
|
|
33969
|
+
var createV2Lifecycle = (deps) => {
|
|
33970
|
+
const { root } = deps;
|
|
33971
|
+
const directChildren = new Map;
|
|
33972
|
+
const dispatches = new Map;
|
|
33973
|
+
const inflight = new Map;
|
|
33974
|
+
const lifecycleBindings = new Map;
|
|
33975
|
+
const unresolvedTaskLaunches = new Set;
|
|
33976
|
+
const now = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
33977
|
+
const readSession = async (sessionID) => {
|
|
33978
|
+
try {
|
|
33979
|
+
const info = await deps.getSession(sessionID);
|
|
33980
|
+
if (!info || typeof info !== "object" || info.id !== sessionID)
|
|
33981
|
+
return null;
|
|
33982
|
+
return info;
|
|
33983
|
+
} catch {
|
|
33984
|
+
return null;
|
|
33985
|
+
}
|
|
33986
|
+
};
|
|
33987
|
+
const trustedLead = async (coordinator) => {
|
|
33988
|
+
const session = await readSession(coordinator);
|
|
33989
|
+
if (!session || parentOf(session) !== undefined)
|
|
33990
|
+
return null;
|
|
33991
|
+
if (!sameWorkspace(root, session.directory))
|
|
33992
|
+
return null;
|
|
33993
|
+
return session;
|
|
33994
|
+
};
|
|
33995
|
+
const ownsActiveTask = (coordinator) => {
|
|
33996
|
+
try {
|
|
33997
|
+
const store = new TaskStore(root);
|
|
33998
|
+
const workspace = store.readWorkspace();
|
|
33999
|
+
const listed = store.listTasks();
|
|
34000
|
+
if (!workspace.ok || !workspace.data || !listed.ok)
|
|
34001
|
+
return false;
|
|
34002
|
+
return listed.data.some((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);
|
|
34003
|
+
} catch {
|
|
34004
|
+
return false;
|
|
34005
|
+
}
|
|
34006
|
+
};
|
|
34007
|
+
const revisions = (taskId) => {
|
|
34008
|
+
const store = new TaskStore(root);
|
|
34009
|
+
const task = store.readTask(taskId);
|
|
34010
|
+
const workspace = store.readWorkspace();
|
|
34011
|
+
return task.ok && workspace.ok && workspace.data ? {
|
|
34012
|
+
expectedRevision: task.data.revision,
|
|
34013
|
+
expectedWorkspaceRevision: workspace.data.revision
|
|
34014
|
+
} : null;
|
|
34015
|
+
};
|
|
34016
|
+
const oldestOfSingleTask = (items) => {
|
|
34017
|
+
const tasks = new Set(items.map((item) => item.task.id));
|
|
34018
|
+
if (tasks.size !== 1 || items.length === 0)
|
|
34019
|
+
return null;
|
|
34020
|
+
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];
|
|
34021
|
+
};
|
|
34022
|
+
const prepareDispatch = (coordinator, callID) => {
|
|
34023
|
+
if (dispatches.has(coordinator))
|
|
34024
|
+
return "unsettled";
|
|
34025
|
+
const store = new TaskStore(root);
|
|
34026
|
+
const workspace = store.readWorkspace();
|
|
34027
|
+
const listed = store.listTasks();
|
|
34028
|
+
if (!workspace.ok || !workspace.data || !listed.ok)
|
|
34029
|
+
return "unmanaged";
|
|
34030
|
+
const owned = 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);
|
|
34031
|
+
if (owned.length === 0)
|
|
34032
|
+
return "unmanaged";
|
|
34033
|
+
const eligible = owned.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 })));
|
|
34034
|
+
const next = oldestOfSingleTask(eligible);
|
|
34035
|
+
if (!next)
|
|
34036
|
+
return "unbound";
|
|
34037
|
+
const generation = {
|
|
34038
|
+
coordinator,
|
|
34039
|
+
callID,
|
|
34040
|
+
childCreated: false,
|
|
34041
|
+
noChild: false
|
|
34042
|
+
};
|
|
34043
|
+
const core = new WorkitCore(store, {
|
|
34044
|
+
root,
|
|
34045
|
+
caller: { host: "opencode", actor: coordinator },
|
|
34046
|
+
capabilities: [],
|
|
34047
|
+
constraints: [],
|
|
34048
|
+
now,
|
|
34049
|
+
nativeWorker: nativeDispatchFor(directChildren, coordinator, generation)
|
|
34050
|
+
});
|
|
34051
|
+
const prepared = core.prepareWorkerDispatch({
|
|
34052
|
+
taskId: next.task.id,
|
|
34053
|
+
workerId: next.entry.id,
|
|
34054
|
+
expectedRevision: next.task.revision,
|
|
34055
|
+
expectedWorkspaceRevision: workspace.data.revision,
|
|
34056
|
+
observation: { stage: "prepare", sessionID: coordinator, callID }
|
|
34057
|
+
});
|
|
34058
|
+
if (!prepared.ok)
|
|
34059
|
+
return "unbound";
|
|
34060
|
+
dispatches.set(coordinator, {
|
|
34061
|
+
generation,
|
|
34062
|
+
dispatch: prepared.data,
|
|
34063
|
+
core,
|
|
34064
|
+
taskId: next.task.id,
|
|
34065
|
+
workerId: next.entry.id,
|
|
34066
|
+
callID,
|
|
34067
|
+
childID: null
|
|
34068
|
+
});
|
|
34069
|
+
return "prepared";
|
|
34070
|
+
};
|
|
34071
|
+
const commitDispatchStart = (coordinator, childID) => {
|
|
34072
|
+
const pending = dispatches.get(coordinator);
|
|
34073
|
+
if (!pending)
|
|
34074
|
+
return false;
|
|
34075
|
+
pending.generation.childCreated = true;
|
|
34076
|
+
pending.childID = childID;
|
|
34077
|
+
const current = revisions(pending.taskId);
|
|
34078
|
+
if (!current)
|
|
34079
|
+
return false;
|
|
34080
|
+
const committed = pending.core.commitWorkerDispatch({
|
|
34081
|
+
...current,
|
|
34082
|
+
dispatch: pending.dispatch,
|
|
34083
|
+
taskId: pending.taskId,
|
|
34084
|
+
workerId: pending.workerId,
|
|
34085
|
+
outcome: "started",
|
|
34086
|
+
session: { kind: "host", host: "opencode", handle: childID },
|
|
34087
|
+
observation: { event: "running", sessionID: childID }
|
|
34088
|
+
});
|
|
34089
|
+
if (!committed.ok)
|
|
34090
|
+
return false;
|
|
34091
|
+
dispatches.delete(coordinator);
|
|
34092
|
+
if (inflight.get(coordinator) === pending.callID)
|
|
34093
|
+
inflight.delete(coordinator);
|
|
34094
|
+
lifecycleBindings.set(childID, {
|
|
34095
|
+
parentID: coordinator,
|
|
34096
|
+
taskId: pending.taskId,
|
|
34097
|
+
workerId: pending.workerId
|
|
34098
|
+
});
|
|
34099
|
+
return true;
|
|
34100
|
+
};
|
|
34101
|
+
const observeLifecycle = async (sessionID, parentID, state, binding, initial = false, deleted = false) => {
|
|
34102
|
+
const store = new TaskStore(root);
|
|
34103
|
+
const listed = store.listTasks();
|
|
34104
|
+
const workspace = store.readWorkspace();
|
|
34105
|
+
if (!listed.ok || !workspace.ok || !workspace.data)
|
|
34106
|
+
return;
|
|
34107
|
+
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 })) : []);
|
|
34108
|
+
const matches = persisted.filter(({ entry }) => entry.data.session?.kind === "host");
|
|
34109
|
+
if (binding && !initial && matches.length !== 1)
|
|
34110
|
+
return;
|
|
34111
|
+
const selected = binding ? persisted.find(({ task, entry }) => task.id === binding.taskId && entry.id === binding.workerId) : matches.length === 1 ? matches[0] : undefined;
|
|
34112
|
+
if (!selected)
|
|
34113
|
+
return;
|
|
34114
|
+
if (!initial && selected.entry.data.session?.kind !== "host")
|
|
34115
|
+
return;
|
|
34116
|
+
if (selected.entry.provenance.session?.kind !== "host")
|
|
34117
|
+
return;
|
|
34118
|
+
if (!binding) {
|
|
34119
|
+
const coordinator = selected.task.intent.provenance.session;
|
|
34120
|
+
if (coordinator?.kind !== "host" || coordinator.host !== "opencode")
|
|
34121
|
+
return;
|
|
34122
|
+
parentID = coordinator.handle;
|
|
34123
|
+
}
|
|
34124
|
+
if (!initial && !deleted) {
|
|
34125
|
+
const observed = await readSession(sessionID);
|
|
34126
|
+
if (!observed || !sameWorkspace(root, observed.directory) || parentOf(observed) !== parentID)
|
|
34127
|
+
return;
|
|
34128
|
+
}
|
|
34129
|
+
directChildren.set(sessionID, parentID);
|
|
34130
|
+
const core = new WorkitCore(store, {
|
|
34131
|
+
root,
|
|
34132
|
+
caller: { host: "opencode", actor: parentID },
|
|
34133
|
+
capabilities: [],
|
|
34134
|
+
constraints: [],
|
|
34135
|
+
now,
|
|
34136
|
+
nativeWorker: nativeWorkerFor(directChildren, parentID)
|
|
34137
|
+
});
|
|
34138
|
+
const result = core.observeWorkerLifecycle({
|
|
34139
|
+
taskId: selected.task.id,
|
|
34140
|
+
workerId: selected.entry.id,
|
|
34141
|
+
expectedRevision: selected.task.revision,
|
|
34142
|
+
expectedWorkspaceRevision: workspace.data.revision,
|
|
34143
|
+
state,
|
|
34144
|
+
session: { kind: "host", host: "opencode", handle: sessionID },
|
|
34145
|
+
observation: { event: state, sessionID }
|
|
34146
|
+
});
|
|
34147
|
+
if (!result.ok)
|
|
34148
|
+
return;
|
|
34149
|
+
lifecycleBindings.set(sessionID, {
|
|
34150
|
+
parentID,
|
|
34151
|
+
taskId: selected.task.id,
|
|
34152
|
+
workerId: selected.entry.id
|
|
34153
|
+
});
|
|
34154
|
+
};
|
|
34155
|
+
const reconcilePending = async (coordinator) => {
|
|
34156
|
+
const pending = dispatches.get(coordinator);
|
|
34157
|
+
if (!pending?.childID)
|
|
34158
|
+
return;
|
|
34159
|
+
const info = await readSession(pending.childID);
|
|
34160
|
+
if (!info || info.id !== pending.childID || !info.outcome)
|
|
34161
|
+
return;
|
|
34162
|
+
await observeLifecycle(pending.childID, coordinator, "stopped", {
|
|
34163
|
+
taskId: pending.taskId,
|
|
34164
|
+
workerId: pending.workerId
|
|
34165
|
+
});
|
|
34166
|
+
dispatches.delete(coordinator);
|
|
34167
|
+
if (inflight.get(coordinator) === pending.callID)
|
|
34168
|
+
inflight.delete(coordinator);
|
|
34169
|
+
};
|
|
34170
|
+
const coordinatorVeto = (coordinator) => {
|
|
34171
|
+
const listed = new TaskStore(root).listTasks();
|
|
34172
|
+
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)))
|
|
34173
|
+
return "recovery_required: a cancelled worker remains uncertain; repeat worker.cancel on the ended worker to confirm its stop, then retry";
|
|
34174
|
+
if (unresolvedTaskLaunches.delete(coordinator))
|
|
34175
|
+
return "recovery_required: a cancelled worker remains uncertain";
|
|
34176
|
+
return null;
|
|
34177
|
+
};
|
|
34178
|
+
const executeBefore = async (event) => {
|
|
34179
|
+
if (event.tool !== "subagent")
|
|
34180
|
+
return;
|
|
34181
|
+
const coordinator = event.sessionID;
|
|
34182
|
+
const continuation = record3(event.input).sessionID;
|
|
34183
|
+
if (continuation !== undefined) {
|
|
34184
|
+
const lead = await trustedLead(coordinator);
|
|
34185
|
+
if (!lead)
|
|
34186
|
+
throw new Error("delegation_lineage_denied: native subagent workers must be direct children of the coordinator");
|
|
34187
|
+
const childID = sessionIDOf(continuation);
|
|
34188
|
+
const child = childID ? await readSession(childID) : null;
|
|
34189
|
+
if (!child || parentOf(child) !== coordinator)
|
|
34190
|
+
throw new Error("delegation_lineage_denied: a continuation sessionID must name an existing direct child of the coordinator");
|
|
34191
|
+
return;
|
|
34192
|
+
}
|
|
34193
|
+
if (!ownsActiveTask(coordinator)) {
|
|
34194
|
+
const lead = await trustedLead(coordinator);
|
|
34195
|
+
if (!lead)
|
|
34196
|
+
throw new Error("delegation_lineage_denied: native subagent workers must be direct children of the coordinator");
|
|
34197
|
+
return;
|
|
34198
|
+
}
|
|
34199
|
+
if (inflight.has(coordinator)) {
|
|
34200
|
+
await reconcilePending(coordinator);
|
|
34201
|
+
if (inflight.has(coordinator))
|
|
34202
|
+
throw new Error("recovery_required: a previous fresh subagent launch is unsettled; wait for it to settle or reconcile its worker before launching again");
|
|
34203
|
+
}
|
|
34204
|
+
inflight.set(coordinator, event.id);
|
|
34205
|
+
try {
|
|
34206
|
+
const veto = coordinatorVeto(coordinator);
|
|
34207
|
+
if (veto)
|
|
34208
|
+
throw new Error(veto);
|
|
34209
|
+
const lead = await trustedLead(coordinator);
|
|
34210
|
+
if (!lead)
|
|
34211
|
+
throw new Error("delegation_lineage_denied: native subagent workers must be direct children of the coordinator");
|
|
34212
|
+
const prepared = prepareDispatch(coordinator, event.id);
|
|
34213
|
+
if (prepared === "unmanaged") {
|
|
34214
|
+
inflight.delete(coordinator);
|
|
34215
|
+
return;
|
|
34216
|
+
}
|
|
34217
|
+
if (prepared === "unsettled") {
|
|
34218
|
+
throw new Error("recovery_required: a previous native task launch is unsettled; wait for it to settle or reconcile its worker before launching again");
|
|
34219
|
+
}
|
|
34220
|
+
if (prepared === "unbound") {
|
|
34221
|
+
throw new Error("recovery_required: no assigned Workit worker is attributable to this coordinator; assign one with workit_worker before launching a native task");
|
|
34222
|
+
}
|
|
34223
|
+
} catch (error) {
|
|
34224
|
+
if (inflight.get(coordinator) === event.id)
|
|
34225
|
+
inflight.delete(coordinator);
|
|
34226
|
+
throw error;
|
|
34227
|
+
}
|
|
34228
|
+
};
|
|
34229
|
+
const executeAfter = async (event) => {
|
|
34230
|
+
if (event.tool !== "subagent")
|
|
34231
|
+
return;
|
|
34232
|
+
const coordinator = event.sessionID;
|
|
34233
|
+
const pending = dispatches.get(coordinator);
|
|
34234
|
+
const generation = pending?.callID === event.id ? pending : undefined;
|
|
34235
|
+
if (event.status === "completed") {
|
|
34236
|
+
const metadata = record3(event.result.metadata);
|
|
34237
|
+
const child = sessionIDOf(metadata.sessionID ?? metadata.sessionId);
|
|
34238
|
+
if (child) {
|
|
34239
|
+
const childSession = await readSession(child);
|
|
34240
|
+
if (parentOf(childSession) === coordinator) {
|
|
34241
|
+
directChildren.set(child, coordinator);
|
|
34242
|
+
if (generation)
|
|
34243
|
+
commitDispatchStart(coordinator, child);
|
|
34244
|
+
}
|
|
34245
|
+
}
|
|
34246
|
+
const output = contentText(event.result.content);
|
|
34247
|
+
const completedChild = child && /<subagent\b[^>]*\bstate="completed"/.test(output) && lifecycleBindings.has(child) ? child : null;
|
|
34248
|
+
if (completedChild) {
|
|
34249
|
+
const binding = lifecycleBindings.get(completedChild);
|
|
34250
|
+
if (binding)
|
|
34251
|
+
await observeLifecycle(completedChild, coordinator, "stopped", binding);
|
|
34252
|
+
}
|
|
34253
|
+
} else {
|
|
34254
|
+
const message = String(event.error?.message ?? event.error ?? "").toLowerCase();
|
|
34255
|
+
if (generation && /(?:cancel|interrupt|unknown|uncertain)/.test(message))
|
|
34256
|
+
unresolvedTaskLaunches.add(coordinator);
|
|
34257
|
+
}
|
|
34258
|
+
if (generation) {
|
|
34259
|
+
dispatches.delete(coordinator);
|
|
34260
|
+
if (inflight.get(coordinator) === event.id)
|
|
34261
|
+
inflight.delete(coordinator);
|
|
34262
|
+
} else if (inflight.get(coordinator) === event.id) {
|
|
34263
|
+
inflight.delete(coordinator);
|
|
34264
|
+
}
|
|
34265
|
+
};
|
|
34266
|
+
const bindCreatedSession = async (data) => {
|
|
34267
|
+
if (!sameWorkspace(root, data.directory))
|
|
34268
|
+
return;
|
|
34269
|
+
const live = dispatches.get(data.parentID);
|
|
34270
|
+
if (live) {
|
|
34271
|
+
live.generation.childCreated = true;
|
|
34272
|
+
live.childID = data.sessionID;
|
|
34273
|
+
}
|
|
34274
|
+
const store = new TaskStore(root);
|
|
34275
|
+
const workspace = store.readWorkspace();
|
|
34276
|
+
const listed = store.listTasks();
|
|
34277
|
+
if (!workspace.ok || !workspace.data || !listed.ok)
|
|
34278
|
+
return;
|
|
34279
|
+
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 === data.parentID).map((entry) => ({ task, entry })) : []);
|
|
34280
|
+
const next = oldestOfSingleTask(candidates);
|
|
34281
|
+
if (!next)
|
|
34282
|
+
return;
|
|
34283
|
+
directChildren.set(data.sessionID, data.parentID);
|
|
34284
|
+
if (live && live.taskId === next.task.id && live.workerId === next.entry.id && commitDispatchStart(data.parentID, data.sessionID))
|
|
34285
|
+
return;
|
|
34286
|
+
await observeLifecycle(data.sessionID, data.parentID, "running", { taskId: next.task.id, workerId: next.entry.id }, true);
|
|
34287
|
+
};
|
|
34288
|
+
const handleEvent = async (event) => {
|
|
34289
|
+
const type = event.type;
|
|
34290
|
+
if (typeof type !== "string")
|
|
34291
|
+
return;
|
|
34292
|
+
if (type === "session.created") {
|
|
34293
|
+
const data = record3(event.data);
|
|
34294
|
+
const sessionID = sessionIDOf(data.sessionID);
|
|
34295
|
+
const parentID = sessionIDOf(data.parentID);
|
|
34296
|
+
const directory = record3(data.location).directory;
|
|
34297
|
+
if (!sessionID || !parentID || typeof directory !== "string")
|
|
34298
|
+
return;
|
|
34299
|
+
await bindCreatedSession({ sessionID, parentID, directory });
|
|
34300
|
+
return;
|
|
34301
|
+
}
|
|
34302
|
+
const terminal = type === "session.execution.succeeded" || type === "session.execution.failed" || type === "session.execution.interrupted";
|
|
34303
|
+
if (type !== "session.execution.started" && !terminal && type !== "session.deleted")
|
|
34304
|
+
return;
|
|
34305
|
+
const sessionID = sessionIDOf(record3(event.data).sessionID);
|
|
34306
|
+
if (!sessionID)
|
|
34307
|
+
return;
|
|
34308
|
+
const binding = lifecycleBindings.get(sessionID);
|
|
34309
|
+
if (type === "session.deleted") {
|
|
34310
|
+
const directory = event.location?.directory;
|
|
34311
|
+
if (typeof directory === "string" && !sameWorkspace(root, directory))
|
|
34312
|
+
return;
|
|
34313
|
+
await observeLifecycle(sessionID, binding?.parentID ?? "", "stopped", binding, false, true);
|
|
34314
|
+
return;
|
|
34315
|
+
}
|
|
34316
|
+
const state = type === "session.execution.started" ? "running" : "stopped";
|
|
34317
|
+
await observeLifecycle(sessionID, binding?.parentID ?? "", state, binding);
|
|
34318
|
+
};
|
|
34319
|
+
return {
|
|
34320
|
+
directChildren,
|
|
34321
|
+
executeBefore,
|
|
34322
|
+
executeAfter,
|
|
34323
|
+
handleEvent,
|
|
34324
|
+
pendingLaunch: (coordinator) => inflight.has(coordinator)
|
|
34325
|
+
};
|
|
34326
|
+
};
|
|
34327
|
+
|
|
34328
|
+
// packages/workit-opencode/src/v2/injection.ts
|
|
34329
|
+
var textPart = (text) => ({ type: "text", text });
|
|
34330
|
+
var hasMarker = (system, marker) => system.some((part) => part.type === "text" && typeof part.text === "string" && part.text.includes(marker));
|
|
34331
|
+
var injectAgentContext = (root, session, directChildren, system) => {
|
|
34332
|
+
if (!session || !sameWorkspace(root, session.directory))
|
|
34333
|
+
return;
|
|
34334
|
+
const bootstrap = session.parentID === undefined ? getWorkitBootstrap() : null;
|
|
34335
|
+
const taskContext = compactContextFor(root, session.id);
|
|
34336
|
+
const workerContext = session.parentID ? workerContextFor(root, session.id, session.parentID, directChildren) : null;
|
|
34337
|
+
if (bootstrap && !hasMarker(system, "<workit-contract>"))
|
|
34338
|
+
system.unshift(textPart(bootstrap));
|
|
34339
|
+
if (taskContext && !hasMarker(system, "<workit-task-context>"))
|
|
34340
|
+
system.unshift(textPart(`<workit-task-context>${taskContext}</workit-task-context>`));
|
|
34341
|
+
if (workerContext && !hasMarker(system, "<workit-worker-context>"))
|
|
34342
|
+
system.unshift(textPart(`<workit-worker-context>${workerContext}</workit-worker-context>`));
|
|
34343
|
+
};
|
|
34344
|
+
var injectCompactionContext = (root, sessionID, system) => {
|
|
34345
|
+
const context = compactContextFor(root, sessionID);
|
|
34346
|
+
if (!context || hasMarker(system, "<workit-task-context>"))
|
|
34347
|
+
return;
|
|
34348
|
+
system.push(textPart(`<workit-task-context>${context}</workit-task-context>`));
|
|
34349
|
+
};
|
|
34350
|
+
|
|
34351
|
+
// packages/workit-opencode/src/v2/permissions.ts
|
|
34352
|
+
var GIT_WORKTREE = /git [\s\S]*worktree/u;
|
|
34353
|
+
var WORKTREE_MESSAGE = "Workit does not use git worktrees; use the guarded in-place git.branch_setup route instead";
|
|
34354
|
+
var evaluateShellPermission = (root, event) => {
|
|
34355
|
+
if (event.action !== "shell" || event.effect === "deny")
|
|
34356
|
+
return;
|
|
34357
|
+
for (const resource of event.resources) {
|
|
34358
|
+
if (typeof resource !== "string")
|
|
34359
|
+
continue;
|
|
34360
|
+
const route = shouldDenyShellRoute(root, resource);
|
|
34361
|
+
if (route) {
|
|
34362
|
+
event.effect = "deny";
|
|
34363
|
+
event.message = `direct branch or PR creation bypasses the Workit route; ${route.guidance}`;
|
|
34364
|
+
return;
|
|
34365
|
+
}
|
|
34366
|
+
if (GIT_WORKTREE.test(resource)) {
|
|
34367
|
+
event.effect = "deny";
|
|
34368
|
+
event.message = WORKTREE_MESSAGE;
|
|
34369
|
+
return;
|
|
34370
|
+
}
|
|
34371
|
+
}
|
|
34372
|
+
};
|
|
34373
|
+
|
|
34374
|
+
// packages/workit-opencode/src/v2/receipts.ts
|
|
34375
|
+
var normalizeQuestionAnswers = (answers) => {
|
|
34376
|
+
const singleValue = (value) => typeof value === "string" && value.trim() ? value : null;
|
|
34377
|
+
if (answers === null || typeof answers !== "object")
|
|
34378
|
+
return answers;
|
|
34379
|
+
if (Array.isArray(answers)) {
|
|
34380
|
+
if (answers.length === 0)
|
|
34381
|
+
return answers;
|
|
34382
|
+
const first = answers[0];
|
|
34383
|
+
const direct = singleValue(first);
|
|
34384
|
+
if (direct)
|
|
34385
|
+
return [[direct]];
|
|
34386
|
+
if (Array.isArray(first)) {
|
|
34387
|
+
if (first.length !== 1)
|
|
34388
|
+
return answers;
|
|
34389
|
+
const only = singleValue(first[0]);
|
|
34390
|
+
return only ? [[only]] : answers;
|
|
34391
|
+
}
|
|
34392
|
+
if (first && typeof first === "object") {
|
|
34393
|
+
const values = Object.values(first);
|
|
34394
|
+
if (values.length > 0 && values.every((entry) => singleValue(entry) !== null))
|
|
34395
|
+
return [[singleValue(values[0])]];
|
|
34396
|
+
}
|
|
34397
|
+
return answers;
|
|
34398
|
+
}
|
|
34399
|
+
const values = Object.values(answers);
|
|
34400
|
+
if (values.length > 0 && values.every((entry) => singleValue(entry) !== null))
|
|
34401
|
+
return [[singleValue(values[0])]];
|
|
34402
|
+
return answers;
|
|
34403
|
+
};
|
|
34404
|
+
|
|
34405
|
+
// packages/workit-opencode/src/v2/registry.ts
|
|
34406
|
+
import { existsSync as existsSync15, readFileSync as readFileSync14 } from "node:fs";
|
|
34407
|
+
import path28 from "node:path";
|
|
34408
|
+
var parseFrontmatter = (markdown) => {
|
|
34409
|
+
const match = markdown.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
34410
|
+
if (!match)
|
|
34411
|
+
return { name: "", body: markdown };
|
|
34412
|
+
const meta = {};
|
|
34413
|
+
for (const line of match[1].split(`
|
|
34414
|
+
`)) {
|
|
34415
|
+
const index = line.indexOf(":");
|
|
34416
|
+
if (index > 0)
|
|
34417
|
+
meta[line.slice(0, index).trim()] = line.slice(index + 1).trim();
|
|
34418
|
+
}
|
|
34419
|
+
return {
|
|
34420
|
+
name: (meta.name ?? "").replace(/^["']|["']$/g, "").trim(),
|
|
34421
|
+
description: (meta.description ?? "").replace(/^["']|["']$/g, "").trim() || undefined,
|
|
34422
|
+
body: `${match[2].trim()}
|
|
34423
|
+
`
|
|
34424
|
+
};
|
|
34425
|
+
};
|
|
34426
|
+
var registerSkills = async (ctx) => {
|
|
34427
|
+
const skillsDir = path28.join(assetsRoot(), "skills");
|
|
34428
|
+
if (!existsSync15(skillsDir))
|
|
34429
|
+
return;
|
|
34430
|
+
await ctx.skill.transform((editor) => {
|
|
34431
|
+
const existing = new Set(editor.list().map((skill) => skill.id));
|
|
34432
|
+
for (const id of WORKIT_METHOD_SKILLS) {
|
|
34433
|
+
if (existing.has(id))
|
|
34434
|
+
continue;
|
|
34435
|
+
const file = path28.join(skillsDir, id, "SKILL.md");
|
|
34436
|
+
if (!existsSync15(file))
|
|
34437
|
+
continue;
|
|
34438
|
+
const parsed = parseFrontmatter(readFileSync14(file, "utf8"));
|
|
34439
|
+
editor.add({
|
|
34440
|
+
id,
|
|
34441
|
+
name: parsed.name || id,
|
|
34442
|
+
...parsed.description ? { description: parsed.description } : {},
|
|
34443
|
+
location: file,
|
|
34444
|
+
content: parsed.body
|
|
34445
|
+
});
|
|
34446
|
+
}
|
|
34447
|
+
});
|
|
34448
|
+
};
|
|
34449
|
+
var registerCommands = async (ctx) => {
|
|
34450
|
+
const listed = await ctx.command.list();
|
|
34451
|
+
const existing = new Set((listed?.data ?? []).map((command) => command.name));
|
|
34452
|
+
await ctx.command.transform((editor) => {
|
|
34453
|
+
for (const [alias, skill] of Object.entries(WORKIT_SKILL_ALIASES)) {
|
|
34454
|
+
if (existing.has(alias))
|
|
34455
|
+
continue;
|
|
34456
|
+
editor.add({
|
|
34457
|
+
name: alias,
|
|
34458
|
+
description: `Apply the ${skill} method skill to the current task.`,
|
|
34459
|
+
execute: async ({ sessionID, prompt, delivery }) => {
|
|
34460
|
+
await ctx.session.prompt({
|
|
34461
|
+
sessionID,
|
|
34462
|
+
text: `Load the ${skill} skill with the skill tool and apply it to the current task. Arguments: ${prompt.text}`,
|
|
34463
|
+
...prompt.files ? { files: prompt.files } : {},
|
|
34464
|
+
...prompt.agents ? { agents: prompt.agents } : {},
|
|
34465
|
+
...prompt.skills ? { skills: prompt.skills } : {},
|
|
34466
|
+
delivery
|
|
34467
|
+
});
|
|
34468
|
+
}
|
|
34469
|
+
});
|
|
34470
|
+
}
|
|
34471
|
+
});
|
|
34472
|
+
};
|
|
34473
|
+
|
|
34474
|
+
// packages/workit-opencode/src/v2/plugin.ts
|
|
34475
|
+
var sessionFacts = async (ctx, sessionID) => {
|
|
34476
|
+
try {
|
|
34477
|
+
const session = await ctx.session.get({ sessionID });
|
|
34478
|
+
const id = session?.id;
|
|
34479
|
+
const directory = session?.location?.directory;
|
|
34480
|
+
if (typeof id !== "string" || id !== sessionID || typeof directory !== "string")
|
|
34481
|
+
return null;
|
|
34482
|
+
const parentID = typeof session.parentID === "string" ? session.parentID : undefined;
|
|
34483
|
+
const outcome = session.outcome === "succeeded" || session.outcome === "failed" || session.outcome === "interrupted" ? session.outcome : undefined;
|
|
34484
|
+
return { id, parentID, directory, outcome };
|
|
34485
|
+
} catch {
|
|
34486
|
+
return null;
|
|
34487
|
+
}
|
|
34488
|
+
};
|
|
34489
|
+
var resultContent = (value) => ({
|
|
34490
|
+
content: JSON.stringify(value, null, 2)
|
|
34491
|
+
});
|
|
34492
|
+
var v2Capabilities = () => [
|
|
34493
|
+
{
|
|
34494
|
+
name: "interactive_decision",
|
|
34495
|
+
surface: "question",
|
|
34496
|
+
assurance: "enforced",
|
|
34497
|
+
reason: "native question answers are observed by tool.execute.after and consumed once",
|
|
34498
|
+
refs: [{ kind: "host", host: "opencode", handle: "question" }]
|
|
34499
|
+
},
|
|
34500
|
+
{
|
|
34501
|
+
name: "known_product_writes",
|
|
34502
|
+
surface: "edit/shell",
|
|
34503
|
+
assurance: "unavailable",
|
|
34504
|
+
reason: "file writes are host-policy; OpenCode native permissions govern them, workit no longer gates write tools",
|
|
34505
|
+
refs: [{ kind: "host", host: "opencode", handle: "permission.evaluate" }]
|
|
34506
|
+
},
|
|
34507
|
+
{
|
|
34508
|
+
name: "direct_child_workers",
|
|
34509
|
+
surface: "subagent",
|
|
34510
|
+
assurance: "enforced",
|
|
34511
|
+
reason: "nested subagent launches are denied and observed child sessions are parent-bound before they may own a worker",
|
|
34512
|
+
refs: [{ kind: "host", host: "opencode", handle: "subagent" }]
|
|
34513
|
+
},
|
|
34514
|
+
{
|
|
34515
|
+
name: "fresh-context-review",
|
|
34516
|
+
surface: "subagent",
|
|
34517
|
+
assurance: "agent_guided",
|
|
34518
|
+
reason: "independent review runs as a native child session; evidence evaluation enforces creator and duplicate-reviewer exclusion",
|
|
34519
|
+
refs: [{ kind: "host", host: "opencode", handle: "subagent" }]
|
|
34520
|
+
}
|
|
34521
|
+
];
|
|
34522
|
+
var workerIdFor2 = (store, actor, parentID, directChildren) => {
|
|
34523
|
+
if (parentID === undefined)
|
|
34524
|
+
return null;
|
|
34525
|
+
const tasks = store.listTasks();
|
|
34526
|
+
if (!tasks.ok)
|
|
34527
|
+
return null;
|
|
34528
|
+
const matches = tasks.data.flatMap((task) => {
|
|
34529
|
+
if (task.status !== "active")
|
|
34530
|
+
return [];
|
|
34531
|
+
const coordinator = task.intent.provenance.session;
|
|
34532
|
+
if (coordinator?.kind !== "host" || coordinator.host !== "opencode" || coordinator.handle !== parentID || directChildren.get(actor) !== parentID)
|
|
34533
|
+
return [];
|
|
34534
|
+
return task.workers.filter((worker) => worker.data.state === "running" && worker.data.session?.kind === "host" && worker.data.session.host === "opencode" && worker.data.session.handle === actor);
|
|
34535
|
+
});
|
|
34536
|
+
return matches.length === 1 ? matches[0].id : null;
|
|
34537
|
+
};
|
|
34538
|
+
var recordDecision = (core, receipts, request, sessionID) => {
|
|
34539
|
+
const decision = request;
|
|
34540
|
+
if (decision.response === "stated")
|
|
34541
|
+
return core.observeDecision(request, undefined);
|
|
34542
|
+
const content = decisionContent(decision.purpose, decision.binding.presented, decision.binding.approvedContent);
|
|
34543
|
+
const budgetIssue = workitBindingQuestionIssue([content]);
|
|
34544
|
+
if (budgetIssue)
|
|
34545
|
+
return failure("invalid_input", budgetIssue);
|
|
34546
|
+
const observed = receipts.consume(sessionID, "decision", {
|
|
34547
|
+
selectedLabel: decision.response,
|
|
34548
|
+
decisionPurpose: decision.purpose,
|
|
34549
|
+
contentDigest: sha256(canonicalJson(content)),
|
|
34550
|
+
question: decision.binding.presented,
|
|
34551
|
+
...decision.response === "approved" ? { selectedDescription: decision.binding.approvedContent } : {}
|
|
34552
|
+
});
|
|
34553
|
+
if (!observed.ok)
|
|
34554
|
+
return failure("permission_denied", observed.error);
|
|
34555
|
+
if (decision.purpose === "action" && decision.response === "approved" && !isSelfAuthorizingActionContent(decision.binding.approvedContent))
|
|
34556
|
+
return failure("invalid_input", "no matching action proposal; resolve the action through the action tool first");
|
|
34557
|
+
return core.observeDecision(request, observed.observation);
|
|
34558
|
+
};
|
|
34559
|
+
var setup = async (ctx) => {
|
|
34560
|
+
const root = ctx.location.directory;
|
|
34561
|
+
const receipts = new NativeReceiptStore;
|
|
34562
|
+
const lifecycle = createV2Lifecycle({
|
|
34563
|
+
root,
|
|
34564
|
+
getSession: async (sessionID) => {
|
|
34565
|
+
const session = await sessionFacts(ctx, sessionID);
|
|
34566
|
+
return session ? {
|
|
34567
|
+
id: session.id,
|
|
34568
|
+
parentID: session.parentID,
|
|
34569
|
+
directory: session.directory,
|
|
34570
|
+
outcome: session.outcome
|
|
34571
|
+
} : null;
|
|
34572
|
+
}
|
|
34573
|
+
});
|
|
34574
|
+
const runFamily = (family, input, session, store, workerId) => {
|
|
34575
|
+
const parsed = parseOperation(family, input);
|
|
34576
|
+
if (!parsed.ok)
|
|
34577
|
+
return parsed;
|
|
34578
|
+
const core = new WorkitCore(store, {
|
|
34579
|
+
root,
|
|
34580
|
+
caller: { host: "opencode", actor: session.id },
|
|
34581
|
+
capabilities: v2Capabilities(),
|
|
34582
|
+
constraints: [],
|
|
34583
|
+
now: () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
34584
|
+
workerId,
|
|
34585
|
+
nativeAuthority: nativeAuthority(receipts, session.id),
|
|
34586
|
+
nativeWorker: nativeWorkerFor(lifecycle.directChildren, session.id)
|
|
34587
|
+
});
|
|
34588
|
+
if (family === "decision" && parsed.data.action === "record")
|
|
34589
|
+
return recordDecision(core, receipts, parsed.data, session.id);
|
|
34590
|
+
const run = core[family];
|
|
34591
|
+
return run.call(core, parsed.data);
|
|
34592
|
+
};
|
|
34593
|
+
await ctx.tool.transform((editor) => {
|
|
34594
|
+
for (const spec of WORKIT_TOOL_CATALOG) {
|
|
34595
|
+
editor.add({
|
|
34596
|
+
name: spec.name,
|
|
34597
|
+
description: spec.description,
|
|
34598
|
+
input: spec.input,
|
|
34599
|
+
options: { codemode: false },
|
|
34600
|
+
execute: async (input, tool) => {
|
|
34601
|
+
const session = await sessionFacts(ctx, tool.sessionID);
|
|
34602
|
+
if (session === null || !sameWorkspace(root, session.directory))
|
|
34603
|
+
return resultContent(failure("permission_denied", "OpenCode session location does not match the plugin checkout"));
|
|
34604
|
+
const store = new TaskStore(root);
|
|
34605
|
+
const workerId = workerIdFor2(store, session.id, session.parentID, lifecycle.directChildren);
|
|
34606
|
+
if (session.parentID !== undefined && workerId === null)
|
|
34607
|
+
return resultContent(failure("permission_denied", "OpenCode child session has no validated Workit worker"));
|
|
34608
|
+
const family = workitFamilyOf(spec.name);
|
|
34609
|
+
if (family !== null)
|
|
34610
|
+
return resultContent(runFamily(family, input, session, store, workerId));
|
|
34611
|
+
if (spec.name === "workit_external_action") {
|
|
34612
|
+
const parsed = externalActionRequest(input);
|
|
34613
|
+
if (!parsed.ok)
|
|
34614
|
+
return resultContent(parsed);
|
|
34615
|
+
if (parsed.data.operation !== "context.read")
|
|
34616
|
+
return resultContent(failure("capability_unavailable", "OpenCode V2 external action approvals are not available yet; only context.read is", { capability: "external_action", outcome: "not_started" }));
|
|
34617
|
+
const resolved = resolveExternalActionRequest(root, parsed.data);
|
|
34618
|
+
if (!resolved.ok)
|
|
34619
|
+
return resultContent(resolved);
|
|
34620
|
+
return resultContent(await executeResolvedExternalAction(resolved.data, root));
|
|
34621
|
+
}
|
|
34622
|
+
if (spec.name === "workit_init_apply") {
|
|
34623
|
+
return { content: executeInitApply(input, root, initApplyRuntime) };
|
|
34624
|
+
}
|
|
34625
|
+
return resultContent(failure("not_found", `unknown Workit tool ${spec.name}`));
|
|
34626
|
+
}
|
|
34627
|
+
});
|
|
34628
|
+
}
|
|
34629
|
+
});
|
|
34630
|
+
await ctx.tool.hook("execute.before", async (event) => {
|
|
34631
|
+
if (event.tool !== "subagent")
|
|
34632
|
+
return;
|
|
34633
|
+
await lifecycle.executeBefore({
|
|
34634
|
+
tool: event.tool,
|
|
34635
|
+
sessionID: String(event.sessionID),
|
|
34636
|
+
id: String(event.id),
|
|
34637
|
+
input: event.input
|
|
34638
|
+
});
|
|
34639
|
+
});
|
|
34640
|
+
await ctx.tool.hook("execute.after", async (event) => {
|
|
34641
|
+
if (event.tool === "question") {
|
|
34642
|
+
if (event.status !== "completed")
|
|
34643
|
+
return;
|
|
34644
|
+
const metadata = event.result.metadata;
|
|
34645
|
+
const answers = metadata?.answers;
|
|
34646
|
+
receipts.record({ sessionID: String(event.sessionID), callID: String(event.id), args: event.input }, {
|
|
34647
|
+
metadata: metadata === undefined ? undefined : { answers: normalizeQuestionAnswers(answers) }
|
|
34648
|
+
});
|
|
34649
|
+
return;
|
|
34650
|
+
}
|
|
34651
|
+
if (event.tool !== "subagent")
|
|
34652
|
+
return;
|
|
34653
|
+
await lifecycle.executeAfter({
|
|
34654
|
+
tool: event.tool,
|
|
34655
|
+
sessionID: String(event.sessionID),
|
|
34656
|
+
id: String(event.id),
|
|
34657
|
+
input: event.input,
|
|
34658
|
+
...event.status === "completed" ? { status: "completed", result: event.result } : { status: "error", error: event.error }
|
|
34659
|
+
});
|
|
34660
|
+
});
|
|
34661
|
+
await ctx.permission.hook("evaluate", (event) => {
|
|
34662
|
+
evaluateShellPermission(root, event);
|
|
34663
|
+
});
|
|
34664
|
+
await ctx.session.hook("context", async (event) => {
|
|
34665
|
+
const session = await sessionFacts(ctx, String(event.sessionID));
|
|
34666
|
+
injectAgentContext(root, session, lifecycle.directChildren, event.system);
|
|
34667
|
+
});
|
|
34668
|
+
await ctx.session.hook("compaction", (event) => {
|
|
34669
|
+
injectCompactionContext(root, String(event.sessionID), event.system);
|
|
34670
|
+
});
|
|
34671
|
+
await registerSkills(ctx);
|
|
34672
|
+
await registerCommands(ctx);
|
|
34673
|
+
const subscription = new AbortController;
|
|
34674
|
+
(async () => {
|
|
34675
|
+
try {
|
|
34676
|
+
for await (const event of ctx.event.subscribe({ signal: subscription.signal })) {
|
|
34677
|
+
await lifecycle.handleEvent(event);
|
|
34678
|
+
}
|
|
34679
|
+
} catch {}
|
|
34680
|
+
})();
|
|
34681
|
+
return () => {
|
|
34682
|
+
subscription.abort();
|
|
34683
|
+
};
|
|
34684
|
+
};
|
|
34685
|
+
var definition = define({ id: "workit", setup });
|
|
34686
|
+
var plugin_default = definition;
|
|
34687
|
+
|
|
34688
|
+
// packages/workit-opencode/src/index.ts
|
|
34689
|
+
var src_default = { ...plugin_default, server: server_default };
|
|
33516
34690
|
export {
|
|
33517
|
-
|
|
34691
|
+
src_default as default,
|
|
34692
|
+
server_default as server
|
|
33518
34693
|
};
|