@brainervirus/workit-opencode 1.1.0 → 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 -355
- 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" };
|
|
@@ -29224,7 +29443,6 @@ function mergePr(cwd, opts) {
|
|
|
29224
29443
|
cmd.push(squash ? "--squash" : "--merge");
|
|
29225
29444
|
if (removeBranch)
|
|
29226
29445
|
cmd.push("--delete-branch");
|
|
29227
|
-
cmd.push("--yes");
|
|
29228
29446
|
cmdEnv = { ...process.env, PATH: process.env.PATH ?? "", GH_TOKEN: token };
|
|
29229
29447
|
}
|
|
29230
29448
|
const result = spawnSync6(cmd[0], cmd.slice(1), { cwd: root, encoding: "utf8", env: cmdEnv });
|
|
@@ -29245,14 +29463,14 @@ function mergePr(cwd, opts) {
|
|
|
29245
29463
|
|
|
29246
29464
|
// packages/workit-core/src/core/init.ts
|
|
29247
29465
|
import fs11 from "node:fs";
|
|
29248
|
-
import
|
|
29466
|
+
import path23 from "node:path";
|
|
29249
29467
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
29250
29468
|
|
|
29251
29469
|
// packages/workit-core/src/core/safe-write.ts
|
|
29252
|
-
import { writeFileSync as
|
|
29470
|
+
import { writeFileSync as writeFileSync4 } from "node:fs";
|
|
29253
29471
|
function writeFileExclusive(file, content, mode) {
|
|
29254
29472
|
try {
|
|
29255
|
-
|
|
29473
|
+
writeFileSync4(file, content, { encoding: "utf8", flag: "wx", mode });
|
|
29256
29474
|
return "created";
|
|
29257
29475
|
} catch (err) {
|
|
29258
29476
|
if (err.code === "EEXIST")
|
|
@@ -29265,19 +29483,19 @@ function writeFileExclusive(file, content, mode) {
|
|
|
29265
29483
|
import {
|
|
29266
29484
|
copyFileSync as copyFileSync2,
|
|
29267
29485
|
cpSync as cpSync2,
|
|
29268
|
-
existsSync as
|
|
29486
|
+
existsSync as existsSync13,
|
|
29269
29487
|
lstatSync as lstatSync4,
|
|
29270
29488
|
mkdirSync as mkdirSync5,
|
|
29271
29489
|
mkdtempSync,
|
|
29272
29490
|
readFileSync as readFileSync12,
|
|
29273
29491
|
readdirSync as readdirSync6,
|
|
29274
|
-
realpathSync as
|
|
29492
|
+
realpathSync as realpathSync8,
|
|
29275
29493
|
renameSync as renameSync2,
|
|
29276
29494
|
rmSync,
|
|
29277
29495
|
statSync as statSync3,
|
|
29278
29496
|
writeFileSync as writeFileSync7
|
|
29279
29497
|
} from "node:fs";
|
|
29280
|
-
import
|
|
29498
|
+
import path21 from "node:path";
|
|
29281
29499
|
import { isDeepStrictEqual } from "node:util";
|
|
29282
29500
|
|
|
29283
29501
|
// packages/workit-core/src/core/branch-policy.ts
|
|
@@ -29340,6 +29558,118 @@ var detectBranchPolicy = (workspaceRoot) => {
|
|
|
29340
29558
|
};
|
|
29341
29559
|
};
|
|
29342
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
|
+
|
|
29343
29673
|
// packages/workit-core/src/core/registration.ts
|
|
29344
29674
|
var CURSOR_RUNTIME_PACKAGE = "@brainervirus/workit-cursor@latest";
|
|
29345
29675
|
var CURSOR_HOOK_RUN_COMMAND = `npx -y --prefer-online --min-release-age=0 --package=${CURSOR_RUNTIME_PACKAGE} workit-cursor-hook`;
|
|
@@ -29381,12 +29711,12 @@ var VCS_OVERRIDES = [
|
|
|
29381
29711
|
var SETUP_OVERRIDES = [...YT_OVERRIDES, ...VCS_OVERRIDES];
|
|
29382
29712
|
function applyWorkspaceBranchPolicy(opts) {
|
|
29383
29713
|
const { workspace_root, env = process.env } = opts;
|
|
29384
|
-
const dir =
|
|
29714
|
+
const dir = path21.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
|
|
29385
29715
|
const { status, path: wsPath, entries } = readWorkspacesResult(dir);
|
|
29386
29716
|
if (status === "malformed")
|
|
29387
29717
|
return { ok: false, error: `malformed workspaces.json: ${wsPath}` };
|
|
29388
29718
|
const detection = detectBranchPolicy(workspace_root);
|
|
29389
|
-
const name = String(env.WORKFLOW_BP_NAME ??
|
|
29719
|
+
const name = String(env.WORKFLOW_BP_NAME ?? path21.basename(workspace_root));
|
|
29390
29720
|
const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
|
|
29391
29721
|
const policy = {
|
|
29392
29722
|
preset: detection.preset,
|
|
@@ -29411,7 +29741,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
29411
29741
|
};
|
|
29412
29742
|
}
|
|
29413
29743
|
const next = existing ? entries.map((w, i) => i === idx ? { ...w, branchPolicy: policy } : w) : [...entries, { name, glob, branchPolicy: policy }];
|
|
29414
|
-
mkdirSync5(
|
|
29744
|
+
mkdirSync5(path21.dirname(wsPath), { recursive: true });
|
|
29415
29745
|
writeFileSync7(wsPath, JSON.stringify({ workspaces: next }, null, 2) + `
|
|
29416
29746
|
`, "utf8");
|
|
29417
29747
|
return {
|
|
@@ -29425,7 +29755,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
29425
29755
|
|
|
29426
29756
|
// packages/workit-core/src/core/youtrack.ts
|
|
29427
29757
|
import fs10 from "node:fs";
|
|
29428
|
-
import
|
|
29758
|
+
import path22 from "node:path";
|
|
29429
29759
|
|
|
29430
29760
|
// packages/workit-core/src/core/templates.ts
|
|
29431
29761
|
var repoRoot4 = assetRoot();
|
|
@@ -29433,8 +29763,8 @@ var repoRoot4 = assetRoot();
|
|
|
29433
29763
|
// packages/workit-core/src/core/youtrack.ts
|
|
29434
29764
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
29435
29765
|
var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE";
|
|
29436
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
29437
|
-
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");
|
|
29438
29768
|
var youTrackTokenModeOk = (p) => {
|
|
29439
29769
|
if (process.platform === "win32")
|
|
29440
29770
|
return true;
|
|
@@ -29478,7 +29808,7 @@ function youTrackConfigLoad() {
|
|
|
29478
29808
|
}
|
|
29479
29809
|
const cfgPath = loaded.path;
|
|
29480
29810
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
29481
|
-
const tokenPath = tokenFile ?
|
|
29811
|
+
const tokenPath = tokenFile ? path22.isAbsolute(tokenFile) ? path22.resolve(tokenFile) : path22.resolve(process.cwd(), tokenFile) : "";
|
|
29482
29812
|
if (!tokenPath || !fs10.existsSync(tokenPath)) {
|
|
29483
29813
|
return { error: "missing youtrack.token" };
|
|
29484
29814
|
}
|
|
@@ -29492,8 +29822,8 @@ function youTrackConfigLoad() {
|
|
|
29492
29822
|
const redacted = { ...loaded.config };
|
|
29493
29823
|
delete redacted.tokenFile;
|
|
29494
29824
|
redacted.tokenPresent = true;
|
|
29495
|
-
redacted.configPath =
|
|
29496
|
-
redacted.tokenPath =
|
|
29825
|
+
redacted.configPath = path22.resolve(cfgPath);
|
|
29826
|
+
redacted.tokenPath = path22.resolve(tokenPath);
|
|
29497
29827
|
return { data: redacted };
|
|
29498
29828
|
}
|
|
29499
29829
|
function tzParts(date, tz) {
|
|
@@ -29606,7 +29936,7 @@ var youTrackToken = () => {
|
|
|
29606
29936
|
if ("error" in loaded)
|
|
29607
29937
|
return loaded;
|
|
29608
29938
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
29609
|
-
const tokenPath = tokenFile ?
|
|
29939
|
+
const tokenPath = tokenFile ? path22.isAbsolute(tokenFile) ? path22.resolve(tokenFile) : path22.resolve(process.cwd(), tokenFile) : "";
|
|
29610
29940
|
if (!tokenPath || !fs10.existsSync(tokenPath))
|
|
29611
29941
|
return { error: "missing youtrack.token" };
|
|
29612
29942
|
if (!youTrackTokenModeOk(tokenPath))
|
|
@@ -29761,7 +30091,7 @@ function youTrackTokenCreateUrl() {
|
|
|
29761
30091
|
const desc = String(defaults.description ?? "OpenCode workit — /wk-issue-update and /wk-meetings");
|
|
29762
30092
|
const scopes = Array.isArray(defaults.scopes) ? defaults.scopes : ["YouTrack"];
|
|
29763
30093
|
const base = String(config.baseUrl ?? "https://enghouseamg.youtrack.cloud").replace(/\/+$/, "");
|
|
29764
|
-
const tokenFile = String(config.tokenFile ??
|
|
30094
|
+
const tokenFile = String(config.tokenFile ?? path22.join(path22.dirname(loaded.path), "youtrack.token"));
|
|
29765
30095
|
const tab = String(defaults.profileTab ?? "account-security");
|
|
29766
30096
|
const createUrl = `${base}/users/me?${new URLSearchParams({ tab })}`;
|
|
29767
30097
|
const docsUrl = "https://www.jetbrains.com/help/youtrack/cloud/manage-permanent-token.html";
|
|
@@ -29770,7 +30100,7 @@ function youTrackTokenCreateUrl() {
|
|
|
29770
30100
|
tokenName: name,
|
|
29771
30101
|
tokenDescription: desc,
|
|
29772
30102
|
scopes,
|
|
29773
|
-
tokenFile:
|
|
30103
|
+
tokenFile: path22.resolve(tokenFile),
|
|
29774
30104
|
createUrl,
|
|
29775
30105
|
docsUrl,
|
|
29776
30106
|
prefillSupported: false,
|
|
@@ -30088,12 +30418,12 @@ var resolvePath = (p) => {
|
|
|
30088
30418
|
try {
|
|
30089
30419
|
return fs11.realpathSync(p);
|
|
30090
30420
|
} catch {
|
|
30091
|
-
return
|
|
30421
|
+
return path23.resolve(p);
|
|
30092
30422
|
}
|
|
30093
30423
|
};
|
|
30094
30424
|
function initStatusData(configDirPath = configDir()) {
|
|
30095
|
-
const ytJson =
|
|
30096
|
-
const vcsJson =
|
|
30425
|
+
const ytJson = path23.join(configDirPath, "youtrack.json");
|
|
30426
|
+
const vcsJson = path23.join(configDirPath, "vcs.json");
|
|
30097
30427
|
const items = [];
|
|
30098
30428
|
let youtrackConfig = null;
|
|
30099
30429
|
let youtrackTokenCreate = null;
|
|
@@ -30116,8 +30446,8 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30116
30446
|
});
|
|
30117
30447
|
}
|
|
30118
30448
|
}
|
|
30119
|
-
const expanded = tokenFile ?
|
|
30120
|
-
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;
|
|
30121
30451
|
youtrackConfig = {
|
|
30122
30452
|
config_edit_path: resolvePath(ytJson),
|
|
30123
30453
|
baseUrl: base,
|
|
@@ -30164,11 +30494,11 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30164
30494
|
id: "youtrack_json",
|
|
30165
30495
|
label: "YouTrack config",
|
|
30166
30496
|
ok: fs11.existsSync(ytJson) && youtrackConfig !== null && !("error" in youtrackConfig),
|
|
30167
|
-
path: fs11.existsSync(ytJson) ? resolvePath(ytJson) :
|
|
30497
|
+
path: fs11.existsSync(ytJson) ? resolvePath(ytJson) : path23.resolve(ytJson),
|
|
30168
30498
|
config_edit_path: resolvePath(ytJson),
|
|
30169
30499
|
fix: "workit_init_apply action=youtrack_scaffold"
|
|
30170
30500
|
});
|
|
30171
|
-
const ytTokenPath = youtrackConfig?.tokenFile ??
|
|
30501
|
+
const ytTokenPath = youtrackConfig?.tokenFile ?? path23.join(configDirPath, "youtrack.token");
|
|
30172
30502
|
const tokenText = fs11.existsSync(ytTokenPath) ? fs11.readFileSync(ytTokenPath, "utf8").trim() : "";
|
|
30173
30503
|
const placeholder = fs11.existsSync(ytTokenPath) && isPlaceholder(tokenText);
|
|
30174
30504
|
const tokenOk = fs11.existsSync(ytTokenPath) && modeOk(ytTokenPath) && Boolean(tokenText) && !isPlaceholder(tokenText);
|
|
@@ -30176,8 +30506,8 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30176
30506
|
id: "youtrack_token",
|
|
30177
30507
|
label: "YouTrack API token (mode 600, not placeholder)",
|
|
30178
30508
|
ok: tokenOk,
|
|
30179
|
-
path: fs11.existsSync(ytTokenPath) ? resolvePath(ytTokenPath) :
|
|
30180
|
-
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),
|
|
30181
30511
|
placeholder,
|
|
30182
30512
|
fix: `Open ${resolvePath(ytTokenPath)} — replace ${TOKEN_PLACEHOLDER3} with your permanent token, save, then /wk-status`
|
|
30183
30513
|
};
|
|
@@ -30203,7 +30533,7 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30203
30533
|
const provider = typeof rawProvider === "string" && rawProvider.trim() ? rawProvider.toLowerCase() : null;
|
|
30204
30534
|
const tokenFiles = {};
|
|
30205
30535
|
for (const k of ["gitlab", "github"]) {
|
|
30206
|
-
tokenFiles[k] = String(vcsParsed[k]?.tokenFile ??
|
|
30536
|
+
tokenFiles[k] = String(vcsParsed[k]?.tokenFile ?? path23.join(configDirPath, `${k}.token`));
|
|
30207
30537
|
}
|
|
30208
30538
|
vcsCfg = {
|
|
30209
30539
|
config_edit_path: resolvePath(vcsJson),
|
|
@@ -30228,14 +30558,14 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30228
30558
|
id: "vcs_json",
|
|
30229
30559
|
label: "VCS config (GitLab / GitHub)",
|
|
30230
30560
|
ok: fs11.existsSync(vcsJson) && vcsCfg !== null && !("error" in vcsCfg),
|
|
30231
|
-
path: fs11.existsSync(vcsJson) ? resolvePath(vcsJson) :
|
|
30561
|
+
path: fs11.existsSync(vcsJson) ? resolvePath(vcsJson) : path23.resolve(vcsJson),
|
|
30232
30562
|
config_edit_path: resolvePath(vcsJson),
|
|
30233
30563
|
fix: "workit_init_apply action=vcs_scaffold"
|
|
30234
30564
|
});
|
|
30235
30565
|
const provActive = vcsCfg && !("error" in vcsCfg) ? vcsCfg.provider : null;
|
|
30236
30566
|
const tokenItem = (tid, label, rawPath, providerKey) => {
|
|
30237
|
-
const t =
|
|
30238
|
-
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);
|
|
30239
30569
|
const text = fs11.existsSync(t) ? fs11.readFileSync(t, "utf8").trim() : "";
|
|
30240
30570
|
const ph = isPlaceholder(text);
|
|
30241
30571
|
const ok = fs11.existsSync(t) && modeOk(t) && Boolean(text) && !ph;
|
|
@@ -30266,7 +30596,7 @@ function initStatusData(configDirPath = configDir()) {
|
|
|
30266
30596
|
};
|
|
30267
30597
|
const vcsTokenFiles = {};
|
|
30268
30598
|
for (const k of ["gitlab", "github"]) {
|
|
30269
|
-
vcsTokenFiles[k] = String(vcsParsed?.[k]?.tokenFile ??
|
|
30599
|
+
vcsTokenFiles[k] = String(vcsParsed?.[k]?.tokenFile ?? path23.join(configDirPath, `${k}.token`));
|
|
30270
30600
|
}
|
|
30271
30601
|
items.push(tokenItem("gitlab_token", "GitLab token (mode 600, not placeholder)", vcsTokenFiles.gitlab, "gitlab"));
|
|
30272
30602
|
items.push(tokenItem("github_token", "GitHub token (mode 600, not placeholder)", vcsTokenFiles.github, "github"));
|
|
@@ -30323,7 +30653,7 @@ async function toolkitStatusData(configDirPath = configDir()) {
|
|
|
30323
30653
|
}
|
|
30324
30654
|
var youtrackJsonContent = (dir) => ({
|
|
30325
30655
|
baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://enghouseamg.youtrack.cloud",
|
|
30326
|
-
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ??
|
|
30656
|
+
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path23.join(dir, "youtrack.token"),
|
|
30327
30657
|
timezone: process.env.WORKFLOW_YT_TIMEZONE ?? "America/Santiago",
|
|
30328
30658
|
locale: "es-CL",
|
|
30329
30659
|
defaultMention: process.env.WORKFLOW_YT_MENTION ?? "Alejandra.Flores",
|
|
@@ -30362,11 +30692,11 @@ var vcsJsonContent = (dir, cwd) => {
|
|
|
30362
30692
|
gitlab: {
|
|
30363
30693
|
host: process.env.WORKFLOW_GITLAB_HOST ?? "gitlab.com",
|
|
30364
30694
|
apiUrl: process.env.WORKFLOW_GITLAB_API_URL ?? "https://gitlab.com/api/v4",
|
|
30365
|
-
tokenFile:
|
|
30695
|
+
tokenFile: path23.join(dir, "gitlab.token")
|
|
30366
30696
|
},
|
|
30367
30697
|
github: {
|
|
30368
30698
|
host: process.env.WORKFLOW_GITHUB_HOST ?? "github.com",
|
|
30369
|
-
tokenFile:
|
|
30699
|
+
tokenFile: path23.join(dir, "github.token")
|
|
30370
30700
|
},
|
|
30371
30701
|
pr: { squashOnMerge: true, removeSourceBranch: true, pushBranch: true, confirmSkip: true },
|
|
30372
30702
|
tokenDefaults: {
|
|
@@ -30383,16 +30713,16 @@ function initApplyData(action, env = process.env) {
|
|
|
30383
30713
|
fs11.mkdirSync(dir, { recursive: true });
|
|
30384
30714
|
switch (action) {
|
|
30385
30715
|
case "youtrack_json": {
|
|
30386
|
-
const out =
|
|
30716
|
+
const out = path23.join(dir, "youtrack.json");
|
|
30387
30717
|
fs11.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
30388
30718
|
`, "utf8");
|
|
30389
30719
|
return { action, ok: true, path: out };
|
|
30390
30720
|
}
|
|
30391
30721
|
case "youtrack_token_placeholder": {
|
|
30392
|
-
const p =
|
|
30722
|
+
const p = path23.join(dir, "youtrack.token");
|
|
30393
30723
|
const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER3 + `
|
|
30394
30724
|
`, 384) === "preserved";
|
|
30395
|
-
const abs =
|
|
30725
|
+
const abs = path23.resolve(p);
|
|
30396
30726
|
return {
|
|
30397
30727
|
action,
|
|
30398
30728
|
ok: true,
|
|
@@ -30404,14 +30734,14 @@ function initApplyData(action, env = process.env) {
|
|
|
30404
30734
|
};
|
|
30405
30735
|
}
|
|
30406
30736
|
case "youtrack_scaffold": {
|
|
30407
|
-
const jsonOut =
|
|
30408
|
-
const tokenOut =
|
|
30737
|
+
const jsonOut = path23.join(dir, "youtrack.json");
|
|
30738
|
+
const tokenOut = path23.join(dir, "youtrack.token");
|
|
30409
30739
|
fs11.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
30410
30740
|
`, "utf8");
|
|
30411
30741
|
const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER3 + `
|
|
30412
30742
|
`, 384) === "preserved";
|
|
30413
|
-
const configPath =
|
|
30414
|
-
const tokenPath =
|
|
30743
|
+
const configPath = path23.resolve(jsonOut);
|
|
30744
|
+
const tokenPath = path23.resolve(tokenOut);
|
|
30415
30745
|
const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
|
|
30416
30746
|
process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
|
|
30417
30747
|
let tokenCreate = {};
|
|
@@ -30465,19 +30795,19 @@ function initApplyData(action, env = process.env) {
|
|
|
30465
30795
|
}
|
|
30466
30796
|
}
|
|
30467
30797
|
case "vcs_scaffold": {
|
|
30468
|
-
const jsonOut =
|
|
30798
|
+
const jsonOut = path23.join(dir, "vcs.json");
|
|
30469
30799
|
fs11.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(dir), null, 2) + `
|
|
30470
30800
|
`, "utf8");
|
|
30471
|
-
const glPath =
|
|
30472
|
-
const ghPath =
|
|
30801
|
+
const glPath = path23.join(dir, "gitlab.token");
|
|
30802
|
+
const ghPath = path23.join(dir, "github.token");
|
|
30473
30803
|
const preservedTokens = [];
|
|
30474
30804
|
for (const p of [glPath, ghPath]) {
|
|
30475
30805
|
if (writeFileExclusive(p, TOKEN_PLACEHOLDER3 + `
|
|
30476
30806
|
`, 384) === "preserved") {
|
|
30477
|
-
preservedTokens.push(
|
|
30807
|
+
preservedTokens.push(path23.resolve(p));
|
|
30478
30808
|
}
|
|
30479
30809
|
}
|
|
30480
|
-
const configPath =
|
|
30810
|
+
const configPath = path23.resolve(jsonOut);
|
|
30481
30811
|
const prev = process.env.WORKFLOW_VCS_CONFIG;
|
|
30482
30812
|
process.env.WORKFLOW_VCS_CONFIG = configPath;
|
|
30483
30813
|
try {
|
|
@@ -30486,14 +30816,14 @@ function initApplyData(action, env = process.env) {
|
|
|
30486
30816
|
const provider = typeof rawProvider === "string" && rawProvider.trim() ? rawProvider : null;
|
|
30487
30817
|
const tokenUrls = vcsTokenCreateUrls();
|
|
30488
30818
|
const active = tokenUrls.active ?? {};
|
|
30489
|
-
const activePath = provider === "gitlab" ?
|
|
30819
|
+
const activePath = provider === "gitlab" ? path23.resolve(glPath) : provider === "github" ? path23.resolve(ghPath) : null;
|
|
30490
30820
|
return {
|
|
30491
30821
|
action,
|
|
30492
30822
|
ok: true,
|
|
30493
30823
|
vcs_json: configPath,
|
|
30494
30824
|
config_edit_path: configPath,
|
|
30495
|
-
gitlab_token:
|
|
30496
|
-
github_token:
|
|
30825
|
+
gitlab_token: path23.resolve(glPath),
|
|
30826
|
+
github_token: path23.resolve(ghPath),
|
|
30497
30827
|
token_edit_path: activePath,
|
|
30498
30828
|
token_create_url: active.createUrl,
|
|
30499
30829
|
token_create_urls: tokenUrls,
|
|
@@ -30548,6 +30878,109 @@ var normalizeLegacyResult = (value) => {
|
|
|
30548
30878
|
return ok(data);
|
|
30549
30879
|
};
|
|
30550
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
|
+
|
|
30551
30984
|
// packages/workit-opencode/src/tools/repo.ts
|
|
30552
30985
|
var defaultRuntime = {
|
|
30553
30986
|
git: (root, args) => run2(root, "git", args),
|
|
@@ -30565,15 +30998,7 @@ var defaultRuntime = {
|
|
|
30565
30998
|
cwd: root
|
|
30566
30999
|
};
|
|
30567
31000
|
},
|
|
30568
|
-
|
|
30569
|
-
const out = initApply({ action, confirmed: true, env });
|
|
30570
|
-
return {
|
|
30571
|
-
exitCode: out.error ? 1 : 0,
|
|
30572
|
-
stdout: JSON.stringify(out.data ?? out),
|
|
30573
|
-
stderr: "",
|
|
30574
|
-
cwd: root
|
|
30575
|
-
};
|
|
30576
|
-
},
|
|
31001
|
+
...initApplyRuntime,
|
|
30577
31002
|
initStatus: (root) => ({
|
|
30578
31003
|
exitCode: 0,
|
|
30579
31004
|
stdout: JSON.stringify(initStatusData()),
|
|
@@ -30587,36 +31012,7 @@ var defaultRuntime = {
|
|
|
30587
31012
|
cwd: root
|
|
30588
31013
|
})
|
|
30589
31014
|
};
|
|
30590
|
-
var output = (value) => JSON.stringify(value, null, 2);
|
|
30591
|
-
var diagnostics = ({ stdout, stderr, exitCode }) => ({ stdout, stderr, exitCode });
|
|
30592
|
-
var requireConfirmed = (confirmed) => confirmed === true ? null : output(fail("confirmed: true required"));
|
|
30593
|
-
function scriptResult(result, parse) {
|
|
30594
|
-
if (result.exitCode !== 0) {
|
|
30595
|
-
return fail(result.stderr.trim() || result.stdout.trim() || "workflow script failed", diagnostics(result));
|
|
30596
|
-
}
|
|
30597
|
-
try {
|
|
30598
|
-
return ok({
|
|
30599
|
-
...parse(result.stdout),
|
|
30600
|
-
exitCode: 0,
|
|
30601
|
-
...result.stderr ? { stderr: result.stderr } : {}
|
|
30602
|
-
});
|
|
30603
|
-
} catch (error) {
|
|
30604
|
-
return fail(error instanceof Error ? error.message : "workflow output parse failed", diagnostics(result));
|
|
30605
|
-
}
|
|
30606
|
-
}
|
|
30607
31015
|
var json2 = (stdout) => JSON.parse(stdout.trim());
|
|
30608
|
-
var legacyScriptResult = (result) => {
|
|
30609
|
-
let parsed = null;
|
|
30610
|
-
try {
|
|
30611
|
-
parsed = json2(result.stdout);
|
|
30612
|
-
} catch {}
|
|
30613
|
-
if (result.exitCode !== 0 || parsed?.error || parsed?.ok === false)
|
|
30614
|
-
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));
|
|
30615
|
-
if (!parsed)
|
|
30616
|
-
return fail("workflow output parse failed", diagnostics(result));
|
|
30617
|
-
const { ok: _legacyOk, ...data } = parsed;
|
|
30618
|
-
return ok({ ...data, exitCode: 0, ...result.stderr ? { stderr: result.stderr } : {} });
|
|
30619
|
-
};
|
|
30620
31016
|
var optionalJson = (value) => {
|
|
30621
31017
|
if (!value?.trim())
|
|
30622
31018
|
return null;
|
|
@@ -30771,7 +31167,7 @@ function createRepoTools(runtime = defaultRuntime) {
|
|
|
30771
31167
|
entries,
|
|
30772
31168
|
path: changelogPath,
|
|
30773
31169
|
normalize_only,
|
|
30774
|
-
workspace_root:
|
|
31170
|
+
workspace_root: realpathSync9(context.directory)
|
|
30775
31171
|
})));
|
|
30776
31172
|
}
|
|
30777
31173
|
}),
|
|
@@ -30892,77 +31288,11 @@ function createRepoTools(runtime = defaultRuntime) {
|
|
|
30892
31288
|
branch_policy_protected: tool.schema.array(tool.schema.string()).optional(),
|
|
30893
31289
|
include_open_source: tool.schema.boolean().optional()
|
|
30894
31290
|
},
|
|
30895
|
-
execute: async (
|
|
30896
|
-
confirmed,
|
|
30897
|
-
action,
|
|
30898
|
-
base_url,
|
|
30899
|
-
default_mention,
|
|
30900
|
-
meeting_issue,
|
|
30901
|
-
vcs_provider,
|
|
30902
|
-
vcs_target_branch,
|
|
30903
|
-
name,
|
|
30904
|
-
develop_branch,
|
|
30905
|
-
integration,
|
|
30906
|
-
locale,
|
|
30907
|
-
locale_options,
|
|
30908
|
-
timezone,
|
|
30909
|
-
branch_policy_preset,
|
|
30910
|
-
branch_policy_allowed,
|
|
30911
|
-
branch_policy_protected,
|
|
30912
|
-
include_open_source
|
|
30913
|
-
}, context) => {
|
|
30914
|
-
const rejected = requireConfirmed(confirmed);
|
|
30915
|
-
if (rejected)
|
|
30916
|
-
return rejected;
|
|
30917
|
-
if (action === "hygiene") {
|
|
30918
|
-
const result = ensureHygieneFiles(context.directory, {
|
|
30919
|
-
confirmed,
|
|
30920
|
-
includeOpenSource: include_open_source
|
|
30921
|
-
});
|
|
30922
|
-
return output(result.ok ? ok(result) : fail(result.error));
|
|
30923
|
-
}
|
|
30924
|
-
if (action === "gitignore") {
|
|
30925
|
-
const result = ensureProjectGitignore(context.directory, confirmed);
|
|
30926
|
-
return output(result.ok ? ok(result) : fail(result.error));
|
|
30927
|
-
}
|
|
30928
|
-
if (action === "config") {
|
|
30929
|
-
const LOCALE_RE = /^[a-z]{2,3}(-(?:[A-Z]{2}|[0-9]{3}))?$/;
|
|
30930
|
-
if (locale !== undefined && !LOCALE_RE.test(locale)) {
|
|
30931
|
-
return output(fail(`invalid locale: ${JSON.stringify(locale)} — expected BCP-47 like en or es-CL`));
|
|
30932
|
-
}
|
|
30933
|
-
const current = readConfig();
|
|
30934
|
-
const next = mergeConfigValues({
|
|
30935
|
-
locale,
|
|
30936
|
-
localeOptions: locale_options,
|
|
30937
|
-
timezone,
|
|
30938
|
-
preset: branch_policy_preset,
|
|
30939
|
-
allowed: branch_policy_allowed,
|
|
30940
|
-
protectedNames: branch_policy_protected
|
|
30941
|
-
}, current);
|
|
30942
|
-
writeConfig(next);
|
|
30943
|
-
return output(ok({ action: "config", path: path22.join(configDir(), "config.json"), ...next }));
|
|
30944
|
-
}
|
|
30945
|
-
const env = Object.fromEntries(Object.entries({
|
|
30946
|
-
WORKFLOW_YT_BASE_URL: base_url,
|
|
30947
|
-
WORKFLOW_YT_MENTION: default_mention,
|
|
30948
|
-
WORKFLOW_YT_MEETING_ISSUE: meeting_issue,
|
|
30949
|
-
WORKFLOW_VCS_PROVIDER: vcs_provider,
|
|
30950
|
-
WORKFLOW_VCS_TARGET_BRANCH: vcs_target_branch,
|
|
30951
|
-
WORKFLOW_WORKSPACE_ROOT: context.directory,
|
|
30952
|
-
WORKFLOW_BP_NAME: name,
|
|
30953
|
-
WORKFLOW_BP_DEVELOP: develop_branch,
|
|
30954
|
-
WORKFLOW_BP_INTEGRATION: integration
|
|
30955
|
-
}).filter((entry) => entry[1] !== undefined));
|
|
30956
|
-
return output(legacyScriptResult(runtime.initApply(context.directory, action, env)));
|
|
30957
|
-
}
|
|
31291
|
+
execute: async (args, context) => executeInitApply(args, context.directory, runtime)
|
|
30958
31292
|
})
|
|
30959
31293
|
};
|
|
30960
31294
|
}
|
|
30961
31295
|
|
|
30962
|
-
// packages/workit-opencode/src/tools/workit.ts
|
|
30963
|
-
import { realpathSync as realpathSync9 } from "node:fs";
|
|
30964
|
-
import path24 from "node:path";
|
|
30965
|
-
|
|
30966
31296
|
// packages/workit-core/src/core/tracker-issues.ts
|
|
30967
31297
|
import fs12 from "node:fs";
|
|
30968
31298
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
@@ -31234,7 +31564,7 @@ async function logTimeUpdate(input, operation = defaultOperations) {
|
|
|
31234
31564
|
|
|
31235
31565
|
// packages/workit-core/src/core/external-action-effects.ts
|
|
31236
31566
|
import fs13 from "node:fs";
|
|
31237
|
-
import
|
|
31567
|
+
import path25 from "node:path";
|
|
31238
31568
|
|
|
31239
31569
|
// packages/workit-core/src/core/commit-flavors.ts
|
|
31240
31570
|
var CONVENTIONAL_RE = /^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?(!)?: .+/;
|
|
@@ -32061,7 +32391,7 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
32061
32391
|
if (preview.error)
|
|
32062
32392
|
return failure("invalid_input", preview.error);
|
|
32063
32393
|
const target = String(preview.path);
|
|
32064
|
-
const relative =
|
|
32394
|
+
const relative = path25.relative(fs13.realpathSync(root), target).replaceAll(path25.sep, "/") || "CHANGELOG.md";
|
|
32065
32395
|
const normalized = {
|
|
32066
32396
|
operation: request.operation,
|
|
32067
32397
|
payload: {
|
|
@@ -32317,24 +32647,21 @@ var executeResolvedExternalAction = async (resolved, root, step, caller) => {
|
|
|
32317
32647
|
return executeConcreteExternalAction(fresh.data.request, root, fresh.data.marker, resolvedDate, step, typeof workText === "string" ? workText : undefined, caller, beforeDigest, beforeExists);
|
|
32318
32648
|
};
|
|
32319
32649
|
|
|
32320
|
-
// packages/workit-opencode/src/
|
|
32321
|
-
|
|
32322
|
-
|
|
32323
|
-
|
|
32324
|
-
if (
|
|
32325
|
-
return;
|
|
32326
|
-
const parentID = session.parentID;
|
|
32327
|
-
return typeof parentID === "string" && parentID.length > 0 ? parentID : null;
|
|
32328
|
-
};
|
|
32329
|
-
var rejectedDescription = "Reject this decision";
|
|
32330
|
-
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;
|
|
32331
32656
|
try {
|
|
32332
|
-
|
|
32333
|
-
return typeof value.operation === "string" && value.operation.length > 0;
|
|
32657
|
+
return realpathSync10(expected) === realpathSync10(observed);
|
|
32334
32658
|
} catch {
|
|
32335
|
-
return
|
|
32659
|
+
return path26.resolve(expected) === path26.resolve(observed);
|
|
32336
32660
|
}
|
|
32337
32661
|
};
|
|
32662
|
+
|
|
32663
|
+
// packages/workit-opencode/src/shared/decision-content.ts
|
|
32664
|
+
var rejectedDescription = "Reject this decision";
|
|
32338
32665
|
var decisionContent = (purpose, question, approvedContent) => ({
|
|
32339
32666
|
header: `Workit decision: ${purpose}`,
|
|
32340
32667
|
question,
|
|
@@ -32343,6 +32670,24 @@ var decisionContent = (purpose, question, approvedContent) => ({
|
|
|
32343
32670
|
{ label: "rejected", description: rejectedDescription }
|
|
32344
32671
|
]
|
|
32345
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
|
+
};
|
|
32346
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;
|
|
32347
32692
|
var normalizeReceiptLabel = (label) => label.trim().replace(/(?:\s*[([]\s*[^()[\]]*\s*[)\]]\s*)+$/u, "").trim().toLowerCase();
|
|
32348
32693
|
var purposeForQuestion = (question) => {
|
|
@@ -32541,15 +32886,6 @@ var sessionData = async (client, sessionID) => {
|
|
|
32541
32886
|
return null;
|
|
32542
32887
|
}
|
|
32543
32888
|
};
|
|
32544
|
-
var sameWorkspace = (expected, observed) => {
|
|
32545
|
-
if (typeof observed !== "string" || !observed)
|
|
32546
|
-
return false;
|
|
32547
|
-
try {
|
|
32548
|
-
return realpathSync9(expected) === realpathSync9(observed);
|
|
32549
|
-
} catch {
|
|
32550
|
-
return path24.resolve(expected) === path24.resolve(observed);
|
|
32551
|
-
}
|
|
32552
|
-
};
|
|
32553
32889
|
var hostRef = (handle) => ({ kind: "host", host: "opencode", handle });
|
|
32554
32890
|
var opencodeCapabilities = () => [
|
|
32555
32891
|
{
|
|
@@ -32963,6 +33299,16 @@ var createWorkitTools = ({
|
|
|
32963
33299
|
}
|
|
32964
33300
|
}
|
|
32965
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
|
+
}
|
|
32966
33312
|
if (!selected.ok && !planAuthorized && !autoApplies) {
|
|
32967
33313
|
if (selected.error.startsWith("no approved action")) {
|
|
32968
33314
|
const proposal = actionProposalQuestion(resolved.data.request, resolved.data.descriptorPayload);
|
|
@@ -33091,9 +33437,9 @@ var loadProvenance = (logger, pkgUrl) => {
|
|
|
33091
33437
|
}
|
|
33092
33438
|
};
|
|
33093
33439
|
|
|
33094
|
-
// packages/workit-opencode/src/
|
|
33095
|
-
var root =
|
|
33096
|
-
var skillsPath =
|
|
33440
|
+
// packages/workit-opencode/src/v1/server.ts
|
|
33441
|
+
var root = assetsRoot();
|
|
33442
|
+
var skillsPath = path27.join(root, "skills");
|
|
33097
33443
|
var logger = createLogger({
|
|
33098
33444
|
appLog: () => {
|
|
33099
33445
|
return;
|
|
@@ -33112,7 +33458,7 @@ var warnStaleSources = () => {
|
|
|
33112
33458
|
logger.warn(EVENT.hooks, {
|
|
33113
33459
|
boundary: "stale_sources",
|
|
33114
33460
|
reason: "workit sources changed after plugin load; restart the session for latest behavior",
|
|
33115
|
-
files: changed.map((file) =>
|
|
33461
|
+
files: changed.map((file) => path27.basename(file))
|
|
33116
33462
|
});
|
|
33117
33463
|
} catch {}
|
|
33118
33464
|
};
|
|
@@ -33141,7 +33487,7 @@ var plugin = async ({ client, directory }) => {
|
|
|
33141
33487
|
const dispatches = new Map;
|
|
33142
33488
|
try {
|
|
33143
33489
|
logger.info(EVENT.initialization, { host: "opencode", plugin_root: root });
|
|
33144
|
-
logger.info(EVENT.provenance, loadProvenance(logger,
|
|
33490
|
+
logger.info(EVENT.provenance, loadProvenance(logger, path27.join(packageRoot2(), "package.json")));
|
|
33145
33491
|
} catch (error) {
|
|
33146
33492
|
logger.warn(EVENT.hooks, { boundary: "initialization", ...errorDetail(error) });
|
|
33147
33493
|
}
|
|
@@ -33443,7 +33789,7 @@ var plugin = async ({ client, directory }) => {
|
|
|
33443
33789
|
config: async (config) => {
|
|
33444
33790
|
const mutable = config;
|
|
33445
33791
|
const paths = [...mutable.skills?.paths ?? []];
|
|
33446
|
-
if (
|
|
33792
|
+
if (existsSync14(skillsPath) && !paths.includes(skillsPath))
|
|
33447
33793
|
paths.push(skillsPath);
|
|
33448
33794
|
mutable.skills ??= {};
|
|
33449
33795
|
mutable.skills.paths = paths;
|
|
@@ -33513,7 +33859,835 @@ var plugin = async ({ client, directory }) => {
|
|
|
33513
33859
|
}
|
|
33514
33860
|
};
|
|
33515
33861
|
};
|
|
33516
|
-
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 };
|
|
33517
34690
|
export {
|
|
33518
|
-
|
|
34691
|
+
src_default as default,
|
|
34692
|
+
server_default as server
|
|
33519
34693
|
};
|