@brainervirus/workit-opencode 6.0.1 → 7.0.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 +5 -2
- package/assets/skills/workit-deslop/SKILL.md +1 -1
- package/assets/skills/workit-implement/SKILL.md +9 -4
- package/assets/skills/workit-review/SKILL.md +2 -0
- package/assets/skills/workit-shape/SKILL.md +1 -1
- package/assets/skills/workit-ship/SKILL.md +1 -1
- package/dist/plugin.js +2018 -917
- package/package.json +2 -2
package/dist/plugin.js
CHANGED
|
@@ -4,9 +4,9 @@ function define(plugin) {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
// packages/workit-core/src/core.ts
|
|
7
|
-
import { spawnSync as
|
|
8
|
-
import { existsSync as
|
|
9
|
-
import
|
|
7
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
8
|
+
import { existsSync as existsSync7, realpathSync as realpathSync5 } from "node:fs";
|
|
9
|
+
import path19 from "node:path";
|
|
10
10
|
|
|
11
11
|
// packages/workit-core/src/core/task-contract.ts
|
|
12
12
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -7430,6 +7430,14 @@ function object(shape, params) {
|
|
|
7430
7430
|
};
|
|
7431
7431
|
return new ZodObject(def);
|
|
7432
7432
|
}
|
|
7433
|
+
function looseObject(shape, params) {
|
|
7434
|
+
return new ZodObject({
|
|
7435
|
+
type: "object",
|
|
7436
|
+
shape,
|
|
7437
|
+
catchall: unknown(),
|
|
7438
|
+
...normalizeParams(params)
|
|
7439
|
+
});
|
|
7440
|
+
}
|
|
7433
7441
|
var ZodUnion = /* @__PURE__ */ $constructor("ZodUnion", (inst, def) => {
|
|
7434
7442
|
$ZodUnion.init(inst, def);
|
|
7435
7443
|
ZodType.init(inst, def);
|
|
@@ -7930,76 +7938,16 @@ var provenanceSchema = object({
|
|
|
7930
7938
|
workerId: nullableId
|
|
7931
7939
|
}).strict();
|
|
7932
7940
|
var entrySchema = (data) => object({ id, recordedAt: utc, provenance: provenanceSchema, data }).strict();
|
|
7933
|
-
var
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
code: "custom",
|
|
7941
|
-
input: ctx.value,
|
|
7942
|
-
message: "observed facts require supporting references",
|
|
7943
|
-
path: ["refs"]
|
|
7944
|
-
});
|
|
7945
|
-
});
|
|
7946
|
-
var signalSchema = object({
|
|
7947
|
-
value: union([boolean2(), literal("unknown")]),
|
|
7948
|
-
basis: _enum(["observed", "inferred", "unknown"]),
|
|
7949
|
-
reason: text,
|
|
7941
|
+
var RISK_TIERS = ["trivial", "normal", "high"];
|
|
7942
|
+
var judgmentSchema = object({
|
|
7943
|
+
riskTier: _enum(RISK_TIERS),
|
|
7944
|
+
behaviorChange: boolean2(),
|
|
7945
|
+
productChoiceOpen: boolean2(),
|
|
7946
|
+
needsPlan: boolean2(),
|
|
7947
|
+
note: text.nullable(),
|
|
7950
7948
|
refs: array(refSchema)
|
|
7951
|
-
}).strict().check((ctx) => {
|
|
7952
|
-
const signal = ctx.value;
|
|
7953
|
-
const valid = signal.value === "unknown" ? signal.basis === "unknown" : signal.basis === "observed" || signal.basis === "inferred";
|
|
7954
|
-
if (!valid)
|
|
7955
|
-
ctx.issues.push({
|
|
7956
|
-
code: "custom",
|
|
7957
|
-
input: signal,
|
|
7958
|
-
message: "signal value and basis disagree",
|
|
7959
|
-
path: []
|
|
7960
|
-
});
|
|
7961
|
-
if (signal.basis === "observed" && signal.refs.length === 0)
|
|
7962
|
-
ctx.issues.push({
|
|
7963
|
-
code: "custom",
|
|
7964
|
-
input: signal,
|
|
7965
|
-
message: "observed signals require supporting references",
|
|
7966
|
-
path: ["refs"]
|
|
7967
|
-
});
|
|
7968
|
-
});
|
|
7969
|
-
var signalSet = object({
|
|
7970
|
-
approachUnknown: signalSchema,
|
|
7971
|
-
productChoiceOpen: signalSchema,
|
|
7972
|
-
behaviorChange: signalSchema,
|
|
7973
|
-
mechanicalLowRisk: signalSchema,
|
|
7974
|
-
durableAgreementNeeded: signalSchema,
|
|
7975
|
-
coordinationPlanNeeded: signalSchema,
|
|
7976
|
-
helperUseful: signalSchema,
|
|
7977
|
-
testFirstPractical: signalSchema
|
|
7978
|
-
}).strict();
|
|
7979
|
-
var consequenceSchema = object({
|
|
7980
|
-
area: _enum([
|
|
7981
|
-
"behavior",
|
|
7982
|
-
"public_contract",
|
|
7983
|
-
"data",
|
|
7984
|
-
"security",
|
|
7985
|
-
"operations",
|
|
7986
|
-
"reversibility",
|
|
7987
|
-
"consumers"
|
|
7988
|
-
]),
|
|
7989
|
-
fact: factSchema
|
|
7990
|
-
}).strict();
|
|
7991
|
-
var verificationSchema = object({
|
|
7992
|
-
claim: text,
|
|
7993
|
-
scope: scopeSchema,
|
|
7994
|
-
availableChecks: array(refSchema),
|
|
7995
|
-
gaps: array(text)
|
|
7996
|
-
}).strict();
|
|
7997
|
-
var assessmentSchema = object({
|
|
7998
|
-
facts: array(factSchema),
|
|
7999
|
-
signals: signalSet,
|
|
8000
|
-
consequences: array(consequenceSchema),
|
|
8001
|
-
verification: array(verificationSchema)
|
|
8002
7949
|
}).strict();
|
|
7950
|
+
var legacyAssessmentSchema = looseObject({});
|
|
8003
7951
|
var constraintSchema = object({
|
|
8004
7952
|
id: nonEmpty,
|
|
8005
7953
|
kind: _enum(["host", "project", "user"]),
|
|
@@ -8236,7 +8184,8 @@ var taskRecordSchema = object({
|
|
|
8236
8184
|
progress: progressSchema,
|
|
8237
8185
|
pauseReason: text.nullable().optional(),
|
|
8238
8186
|
runtime: runtimeSchema.optional(),
|
|
8239
|
-
assessments: array(entrySchema(
|
|
8187
|
+
assessments: array(entrySchema(legacyAssessmentSchema)),
|
|
8188
|
+
judgments: array(entrySchema(judgmentSchema)).optional(),
|
|
8240
8189
|
policy: policySchema.nullable(),
|
|
8241
8190
|
policyChanges: array(policyChangeSchema),
|
|
8242
8191
|
candidates: array(candidateSchema),
|
|
@@ -8412,14 +8361,15 @@ var taskOperations = {
|
|
|
8412
8361
|
decisionIds: array(id).optional()
|
|
8413
8362
|
})
|
|
8414
8363
|
};
|
|
8364
|
+
var rawJudgment = record(string2(), unknown());
|
|
8415
8365
|
var policyOperations = {
|
|
8416
8366
|
assess: operation({
|
|
8417
8367
|
action: literal("assess"),
|
|
8418
8368
|
...taskId,
|
|
8419
8369
|
expectedRevision: revision.optional(),
|
|
8420
|
-
|
|
8370
|
+
judgment: rawJudgment
|
|
8421
8371
|
}),
|
|
8422
|
-
preview: operation({ action: literal("preview"), ...taskId,
|
|
8372
|
+
preview: operation({ action: literal("preview"), ...taskId, judgment: rawJudgment }),
|
|
8423
8373
|
explain: operation({ action: literal("explain"), ...taskId })
|
|
8424
8374
|
};
|
|
8425
8375
|
var evidenceOperations = {
|
|
@@ -8458,7 +8408,10 @@ var decisionOperations = {
|
|
|
8458
8408
|
...taskId,
|
|
8459
8409
|
expectedRevision: revision.optional(),
|
|
8460
8410
|
purpose: decisionSchema.shape.purpose,
|
|
8461
|
-
binding: decisionSchema.shape.binding
|
|
8411
|
+
binding: decisionSchema.shape.binding.extend({
|
|
8412
|
+
taskId: id.optional(),
|
|
8413
|
+
workspaceId: id.optional()
|
|
8414
|
+
}),
|
|
8462
8415
|
response: decisionSchema.shape.response,
|
|
8463
8416
|
requirementIds: array(digest)
|
|
8464
8417
|
}),
|
|
@@ -8512,6 +8465,15 @@ var operationSchemas = {
|
|
|
8512
8465
|
worker: discriminatedUnion("action", Object.values(workerOperations)),
|
|
8513
8466
|
state: discriminatedUnion("action", Object.values(stateOperations))
|
|
8514
8467
|
};
|
|
8468
|
+
var OPERATION_ACTIONS = {
|
|
8469
|
+
task: Object.keys(taskOperations),
|
|
8470
|
+
policy: Object.keys(policyOperations),
|
|
8471
|
+
evidence: Object.keys(evidenceOperations),
|
|
8472
|
+
finding: Object.keys(findingOperations),
|
|
8473
|
+
decision: Object.keys(decisionOperations),
|
|
8474
|
+
worker: Object.keys(workerOperations),
|
|
8475
|
+
state: Object.keys(stateOperations)
|
|
8476
|
+
};
|
|
8515
8477
|
var withImplicitTask = (options) => Object.values(options).map((option) => ("taskId" in option.shape) ? option.extend({ taskId: id.optional() }) : option);
|
|
8516
8478
|
var advertised = (options) => discriminatedUnion("action", withImplicitTask(options));
|
|
8517
8479
|
var advertisedOperationSchemas = {
|
|
@@ -8545,103 +8507,6 @@ function parseOperation(family, input) {
|
|
|
8545
8507
|
}));
|
|
8546
8508
|
return failure2("invalid_input", "operation input is invalid", { fields });
|
|
8547
8509
|
}
|
|
8548
|
-
function operationJsonSchema(family) {
|
|
8549
|
-
return toJSONSchema(advertisedOperationSchemas[family], { target: "draft-2020-12" });
|
|
8550
|
-
}
|
|
8551
|
-
var OPERATION_SCHEMA_DEPTH = 1;
|
|
8552
|
-
var schemaShapeDescription = (value, depth = 0) => {
|
|
8553
|
-
if (!value || typeof value !== "object")
|
|
8554
|
-
return "value";
|
|
8555
|
-
const schema = value;
|
|
8556
|
-
if (Array.isArray(schema.oneOf) || Array.isArray(schema.anyOf)) {
|
|
8557
|
-
const options = Array.isArray(schema.oneOf) ? schema.oneOf : schema.anyOf;
|
|
8558
|
-
return `(${options.map((option) => schemaShapeDescription(option, depth)).join("|")})`;
|
|
8559
|
-
}
|
|
8560
|
-
if (schema.const !== undefined)
|
|
8561
|
-
return `=${JSON.stringify(schema.const)}`;
|
|
8562
|
-
if (Array.isArray(schema.enum))
|
|
8563
|
-
return `[${schema.enum.map(String).join("|")}]`;
|
|
8564
|
-
const type = Array.isArray(schema.type) ? schema.type.join("/") : schema.type;
|
|
8565
|
-
if (type === "array")
|
|
8566
|
-
return `arr<${schemaShapeDescription(schema.items, depth)}>`;
|
|
8567
|
-
if (type === "object" && schema.properties && typeof schema.properties === "object") {
|
|
8568
|
-
if (depth >= 2)
|
|
8569
|
-
return "obj";
|
|
8570
|
-
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
8571
|
-
const properties = schema.properties;
|
|
8572
|
-
const fields = Object.entries(properties).map(([name, child]) => `${name}${required.has(name) ? "!" : ""}:${schemaShapeDescription(child, depth + 1)}`);
|
|
8573
|
-
return `obj{${fields.join(",")}}`;
|
|
8574
|
-
}
|
|
8575
|
-
if (typeof type !== "string")
|
|
8576
|
-
return "?";
|
|
8577
|
-
return type.split("/").map((part) => ({ string: "str", boolean: "bool", number: "num", null: "null" })[part] ?? part).join("|");
|
|
8578
|
-
};
|
|
8579
|
-
var canonicalFieldsDescription = (value) => {
|
|
8580
|
-
if (Array.isArray(value))
|
|
8581
|
-
return `Canonical object fields: ${value.join(", ")}. Workit validates the complete nested value.`;
|
|
8582
|
-
const isZodType = (schema) => ("_zod" in schema) && typeof schema._zod === "object";
|
|
8583
|
-
const schema = isZodType(value) ? toJSONSchema(value, { target: "draft-2020-12" }) : value;
|
|
8584
|
-
if (!schema.properties || typeof schema.properties !== "object") {
|
|
8585
|
-
const variants = Array.isArray(schema.oneOf) ? schema.oneOf : Array.isArray(schema.anyOf) ? schema.anyOf : null;
|
|
8586
|
-
return variants ? `Canonical shapes: ${variants.map((variant) => schemaShapeDescription(variant)).join(" | ")}. Full nested value is validated.` : "Canonical nested value; Workit validates the complete value.";
|
|
8587
|
-
}
|
|
8588
|
-
const properties = schema.properties;
|
|
8589
|
-
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
8590
|
-
const fields = Object.entries(properties).map(([name, child]) => `${name}${required.has(name) ? "!" : ""}:${schemaShapeDescription(child)}`);
|
|
8591
|
-
return `Fields (! required): ${fields.join(", ")}. Full nested value is validated.`;
|
|
8592
|
-
};
|
|
8593
|
-
function boundedOperationJsonSchema(family, maxDepth = OPERATION_SCHEMA_DEPTH, stringifiedObjects = false) {
|
|
8594
|
-
const tolerate = (projected) => {
|
|
8595
|
-
if (!stringifiedObjects)
|
|
8596
|
-
return projected;
|
|
8597
|
-
if (projected.properties && typeof projected.properties === "object")
|
|
8598
|
-
return { ...projected, type: ["object", "string"] };
|
|
8599
|
-
if (projected.items)
|
|
8600
|
-
return { ...projected, type: ["array", "string"] };
|
|
8601
|
-
if ("const" in projected && (typeof projected.const === "number" || typeof projected.const === "boolean"))
|
|
8602
|
-
return { anyOf: [projected, { const: String(projected.const) }] };
|
|
8603
|
-
if (projected.type === "object")
|
|
8604
|
-
return { ...projected, type: ["object", "string"] };
|
|
8605
|
-
if (projected.type === "array")
|
|
8606
|
-
return { ...projected, type: ["array", "string"] };
|
|
8607
|
-
return projected;
|
|
8608
|
-
};
|
|
8609
|
-
const collapse = (node, currentDepth) => {
|
|
8610
|
-
if (Array.isArray(node))
|
|
8611
|
-
return node.map((item) => collapse(item, currentDepth));
|
|
8612
|
-
if (typeof node !== "object" || node === null)
|
|
8613
|
-
return node;
|
|
8614
|
-
const record = node;
|
|
8615
|
-
if (record.properties && typeof record.properties === "object") {
|
|
8616
|
-
const properties = record.properties;
|
|
8617
|
-
const fields = Object.keys(properties);
|
|
8618
|
-
if (currentDepth >= maxDepth || fields.length === 0) {
|
|
8619
|
-
const description = stringifiedObjects ? `${canonicalFieldsDescription(record)} A JSON-encoded string is also accepted.` : canonicalFieldsDescription(record);
|
|
8620
|
-
return tolerate({ type: "object", description });
|
|
8621
|
-
}
|
|
8622
|
-
return tolerate({
|
|
8623
|
-
...record,
|
|
8624
|
-
properties: Object.fromEntries(Object.entries(record.properties).map(([key, child]) => [
|
|
8625
|
-
key,
|
|
8626
|
-
collapse(child, currentDepth + 1)
|
|
8627
|
-
]))
|
|
8628
|
-
});
|
|
8629
|
-
}
|
|
8630
|
-
if (record.items)
|
|
8631
|
-
return tolerate({ ...record, items: collapse(record.items, currentDepth) });
|
|
8632
|
-
const composed = ["anyOf", "oneOf", "allOf"].filter((key) => Array.isArray(record[key]));
|
|
8633
|
-
if (composed.length > 0) {
|
|
8634
|
-
const projected = { ...record };
|
|
8635
|
-
for (const key of composed)
|
|
8636
|
-
projected[key] = record[key].map((item) => collapse(item, currentDepth));
|
|
8637
|
-
return projected;
|
|
8638
|
-
}
|
|
8639
|
-
if ("const" in record)
|
|
8640
|
-
return tolerate({ ...record });
|
|
8641
|
-
return node;
|
|
8642
|
-
};
|
|
8643
|
-
return collapse(operationJsonSchema(family), 0);
|
|
8644
|
-
}
|
|
8645
8510
|
function canonical(value, seen) {
|
|
8646
8511
|
if (value === null || typeof value === "boolean" || typeof value === "string") {
|
|
8647
8512
|
if (typeof value === "string" && invalidUnicode(value))
|
|
@@ -8730,6 +8595,402 @@ function requirementId(input) {
|
|
|
8730
8595
|
}
|
|
8731
8596
|
});
|
|
8732
8597
|
}
|
|
8598
|
+
// packages/workit-core/src/core/operation-input.ts
|
|
8599
|
+
var str = (d, values) => ({
|
|
8600
|
+
type: "string",
|
|
8601
|
+
...values ? { enum: values } : {},
|
|
8602
|
+
...d ? { d } : {}
|
|
8603
|
+
});
|
|
8604
|
+
var list = (d) => ({ type: "array", ...d ? { d } : {} });
|
|
8605
|
+
var bool = (d) => ({ type: "boolean", ...d ? { d } : {} });
|
|
8606
|
+
var int2 = (d) => ({ type: "integer", ...d ? { d } : {} });
|
|
8607
|
+
var scopeFields = {
|
|
8608
|
+
paths: list("Scope paths (default: whole checkout)."),
|
|
8609
|
+
exclusions: list(),
|
|
8610
|
+
scope: str("Scope description.")
|
|
8611
|
+
};
|
|
8612
|
+
var refs = list("Paths or URLs.");
|
|
8613
|
+
var FLAT = {
|
|
8614
|
+
task: {
|
|
8615
|
+
fields: {
|
|
8616
|
+
objective: str(),
|
|
8617
|
+
...scopeFields,
|
|
8618
|
+
refs,
|
|
8619
|
+
reason: str(),
|
|
8620
|
+
summary: str(),
|
|
8621
|
+
nextAction: str(),
|
|
8622
|
+
blockers: list(),
|
|
8623
|
+
outcome: str(undefined, ["verified", "accepted_limitations", "stopped"]),
|
|
8624
|
+
decisionIds: list(),
|
|
8625
|
+
status: str(undefined, ["open", "closed", "all"]),
|
|
8626
|
+
query: str(),
|
|
8627
|
+
limit: int2(),
|
|
8628
|
+
view: str(undefined, ["summary", "full"])
|
|
8629
|
+
}
|
|
8630
|
+
},
|
|
8631
|
+
policy: {
|
|
8632
|
+
fields: {
|
|
8633
|
+
riskTier: str("low=trivial, medium=normal.", ["trivial", "normal", "high", "low", "medium"]),
|
|
8634
|
+
behaviorChange: bool("Observable behavior changes."),
|
|
8635
|
+
productChoiceOpen: bool("A product/preference choice is still open."),
|
|
8636
|
+
needsPlan: bool("Needs a written plan or spec first."),
|
|
8637
|
+
note: str(),
|
|
8638
|
+
refs: list("Plan/spec paths or URLs.")
|
|
8639
|
+
}
|
|
8640
|
+
},
|
|
8641
|
+
evidence: {
|
|
8642
|
+
fields: {
|
|
8643
|
+
kind: str(undefined, ["check", "review", "investigation", "artifact"]),
|
|
8644
|
+
claim: str(),
|
|
8645
|
+
result: str(undefined, ["passed", "failed", "missing", "skipped"]),
|
|
8646
|
+
summary: str(),
|
|
8647
|
+
exitCode: int2(),
|
|
8648
|
+
refs,
|
|
8649
|
+
requirementIds: list(),
|
|
8650
|
+
candidateId: str(),
|
|
8651
|
+
beforeCandidateId: str(),
|
|
8652
|
+
reviewSession: str("Reviewer session handle (review evidence).")
|
|
8653
|
+
}
|
|
8654
|
+
},
|
|
8655
|
+
finding: {
|
|
8656
|
+
fields: {
|
|
8657
|
+
claim: str(),
|
|
8658
|
+
consequence: str(),
|
|
8659
|
+
...scopeFields,
|
|
8660
|
+
candidateId: str(),
|
|
8661
|
+
refs,
|
|
8662
|
+
findingId: str(),
|
|
8663
|
+
disposition: str(undefined, ["open", "fixed", "dismissed", "deferred"]),
|
|
8664
|
+
reason: str(),
|
|
8665
|
+
evidenceIds: list(),
|
|
8666
|
+
decisionIds: list()
|
|
8667
|
+
}
|
|
8668
|
+
},
|
|
8669
|
+
decision: {
|
|
8670
|
+
fields: {
|
|
8671
|
+
purpose: str(undefined, ["design", "action", "limitation", "preference"]),
|
|
8672
|
+
response: str(undefined, ["approved", "rejected", "stated"]),
|
|
8673
|
+
presented: str("What the user was asked."),
|
|
8674
|
+
choice: str("The user's stated choice (response=stated)."),
|
|
8675
|
+
...scopeFields,
|
|
8676
|
+
refs,
|
|
8677
|
+
requirementIds: list(),
|
|
8678
|
+
decisionId: str(),
|
|
8679
|
+
reason: str()
|
|
8680
|
+
}
|
|
8681
|
+
},
|
|
8682
|
+
worker: {
|
|
8683
|
+
fields: {
|
|
8684
|
+
role: str(undefined, ["investigator", "reviewer", "implementer"]),
|
|
8685
|
+
objective: str(),
|
|
8686
|
+
...scopeFields,
|
|
8687
|
+
decisionIds: list(),
|
|
8688
|
+
requirementIds: list(),
|
|
8689
|
+
candidateId: str(),
|
|
8690
|
+
stoppingCondition: str(),
|
|
8691
|
+
workerId: str(),
|
|
8692
|
+
outcome: str(undefined, ["completed", "failed", "cancelled"]),
|
|
8693
|
+
summary: str(),
|
|
8694
|
+
evidenceIds: list(),
|
|
8695
|
+
findingIds: list(),
|
|
8696
|
+
reason: str()
|
|
8697
|
+
}
|
|
8698
|
+
},
|
|
8699
|
+
state: {
|
|
8700
|
+
fields: { bundle: { type: "object", d: "An export bundle (object or JSON string)." } }
|
|
8701
|
+
}
|
|
8702
|
+
};
|
|
8703
|
+
var COMMON = {
|
|
8704
|
+
taskId: str("Omit for this branch's task."),
|
|
8705
|
+
expectedRevision: str(),
|
|
8706
|
+
expectedWorkspaceRevision: str()
|
|
8707
|
+
};
|
|
8708
|
+
var DESCRIPTIONS = {
|
|
8709
|
+
task: "Workit task lifecycle for this branch's task (optional continuity).",
|
|
8710
|
+
policy: "Judge the work in four flat calls (riskTier, behaviorChange, productChoiceOpen, needsPlan); Workit derives the checks, verdicts, decisions and plan it needs.",
|
|
8711
|
+
evidence: "Record a note-level evidence entry; only `workit check` runs satisfy check gates.",
|
|
8712
|
+
finding: "Record or resolve a review finding.",
|
|
8713
|
+
decision: "Record or revoke a user decision (never an authorization).",
|
|
8714
|
+
worker: "Assign, report or cancel a bounded helper.",
|
|
8715
|
+
state: "Export or import a task handoff bundle."
|
|
8716
|
+
};
|
|
8717
|
+
var operationDescription = (family) => `${DESCRIPTIONS[family]} Flat fields; omit taskId and revisions.`;
|
|
8718
|
+
var READ_TASK_FIELDS = new Set(["status", "query", "limit", "view"]);
|
|
8719
|
+
var READ_ONLY_ACTIONS = new Set(["list", "inspect", "preview", "explain", "export"]);
|
|
8720
|
+
function flatOperationJsonSchema(family, options = {}) {
|
|
8721
|
+
const spec = FLAT[family];
|
|
8722
|
+
const actions = options.readOnly ? OPERATION_ACTIONS[family].filter((action) => READ_ONLY_ACTIONS.has(action)) : [...OPERATION_ACTIONS[family]];
|
|
8723
|
+
if (!actions.length)
|
|
8724
|
+
return null;
|
|
8725
|
+
const property = (field) => {
|
|
8726
|
+
const base = field.type === "array" ? { type: "array", items: { type: "string" } } : field.type === "object" ? { type: "object" } : { type: field.type };
|
|
8727
|
+
const tolerant = options.stringTolerant && field.type !== "string" ? { type: [field.type, "string"] } : {};
|
|
8728
|
+
return {
|
|
8729
|
+
...base,
|
|
8730
|
+
...tolerant,
|
|
8731
|
+
...field.enum ? { enum: field.enum } : {},
|
|
8732
|
+
...field.d ? { description: field.d } : {}
|
|
8733
|
+
};
|
|
8734
|
+
};
|
|
8735
|
+
const properties = { action: { type: "string", enum: actions } };
|
|
8736
|
+
const readField = (name) => name === "taskId" || (family === "task" ? READ_TASK_FIELDS.has(name) : family === "policy");
|
|
8737
|
+
for (const [name, field] of Object.entries({ ...COMMON, ...spec.fields }))
|
|
8738
|
+
if (options.readOnly ? readField(name) : !(family === "state" && name === "expectedRevision"))
|
|
8739
|
+
properties[name] = property(field);
|
|
8740
|
+
return {
|
|
8741
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
8742
|
+
type: "object",
|
|
8743
|
+
properties,
|
|
8744
|
+
required: ["action"]
|
|
8745
|
+
};
|
|
8746
|
+
}
|
|
8747
|
+
var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8748
|
+
var decoded = (value) => {
|
|
8749
|
+
if (typeof value !== "string")
|
|
8750
|
+
return value;
|
|
8751
|
+
const trimmed = value.trim();
|
|
8752
|
+
if (!/^[[{]/.test(trimmed) && !/^(true|false|null|-?\d+)$/.test(trimmed))
|
|
8753
|
+
return value;
|
|
8754
|
+
try {
|
|
8755
|
+
return JSON.parse(trimmed);
|
|
8756
|
+
} catch {
|
|
8757
|
+
return value;
|
|
8758
|
+
}
|
|
8759
|
+
};
|
|
8760
|
+
var strings = (value) => {
|
|
8761
|
+
const plain = decoded(value);
|
|
8762
|
+
if (typeof plain === "string")
|
|
8763
|
+
return plain.trim() ? [plain] : [];
|
|
8764
|
+
return plain;
|
|
8765
|
+
};
|
|
8766
|
+
var refOf = (value) => {
|
|
8767
|
+
if (typeof value !== "string")
|
|
8768
|
+
return value;
|
|
8769
|
+
return /^https?:\/\//i.test(value) ? { kind: "external", url: value } : { kind: "file", path: value.replace(/^\.\//, ""), digest: null };
|
|
8770
|
+
};
|
|
8771
|
+
var refList = (value) => {
|
|
8772
|
+
const items = strings(value);
|
|
8773
|
+
return Array.isArray(items) ? items.map(refOf) : items;
|
|
8774
|
+
};
|
|
8775
|
+
var take = (flat, keys, map = decoded) => {
|
|
8776
|
+
const out = {};
|
|
8777
|
+
for (const key of keys)
|
|
8778
|
+
if (key in flat) {
|
|
8779
|
+
out[key] = map(flat[key]);
|
|
8780
|
+
delete flat[key];
|
|
8781
|
+
}
|
|
8782
|
+
return out;
|
|
8783
|
+
};
|
|
8784
|
+
var nest = (request, key, flat, defaults = {}) => {
|
|
8785
|
+
const nested = isObject2(decoded(request[key])) ? decoded(request[key]) : null;
|
|
8786
|
+
if (!nested && !Object.keys(flat).length)
|
|
8787
|
+
return;
|
|
8788
|
+
request[key] = { ...defaults, ...flat, ...nested };
|
|
8789
|
+
};
|
|
8790
|
+
var scopeOf = (flat, description) => {
|
|
8791
|
+
const scope = decoded(flat.scope);
|
|
8792
|
+
const has = "paths" in flat || "exclusions" in flat || typeof scope === "string";
|
|
8793
|
+
const out = isObject2(scope) ? scope : has ? {
|
|
8794
|
+
description: typeof scope === "string" ? scope : String(description ?? ""),
|
|
8795
|
+
paths: strings(flat.paths ?? ["."]),
|
|
8796
|
+
exclusions: strings(flat.exclusions ?? [])
|
|
8797
|
+
} : null;
|
|
8798
|
+
delete flat.scope;
|
|
8799
|
+
delete flat.paths;
|
|
8800
|
+
delete flat.exclusions;
|
|
8801
|
+
return out;
|
|
8802
|
+
};
|
|
8803
|
+
var defaultScope = (description) => ({
|
|
8804
|
+
description: typeof description === "string" ? description : "",
|
|
8805
|
+
paths: ["."],
|
|
8806
|
+
exclusions: []
|
|
8807
|
+
});
|
|
8808
|
+
var LISTS = [
|
|
8809
|
+
"decisionIds",
|
|
8810
|
+
"evidenceIds",
|
|
8811
|
+
"findingIds",
|
|
8812
|
+
"requirementIds",
|
|
8813
|
+
"refs",
|
|
8814
|
+
"blockers"
|
|
8815
|
+
];
|
|
8816
|
+
var ENVELOPE = new Set([
|
|
8817
|
+
"schemaVersion",
|
|
8818
|
+
"action",
|
|
8819
|
+
"taskId",
|
|
8820
|
+
"expectedRevision",
|
|
8821
|
+
"expectedWorkspaceRevision",
|
|
8822
|
+
"judgment"
|
|
8823
|
+
]);
|
|
8824
|
+
function normalizeOperationInput(family, input, host) {
|
|
8825
|
+
if (!isObject2(input))
|
|
8826
|
+
return input;
|
|
8827
|
+
const request = { ...input };
|
|
8828
|
+
if (!("schemaVersion" in request))
|
|
8829
|
+
request.schemaVersion = 1;
|
|
8830
|
+
else
|
|
8831
|
+
request.schemaVersion = decoded(request.schemaVersion);
|
|
8832
|
+
for (const key of ["limit", "exitCode"])
|
|
8833
|
+
if (key in request)
|
|
8834
|
+
request[key] = decoded(request[key]);
|
|
8835
|
+
const action = request.action;
|
|
8836
|
+
const flat = request;
|
|
8837
|
+
switch (family) {
|
|
8838
|
+
case "task": {
|
|
8839
|
+
if (action === "start" || action === "revise") {
|
|
8840
|
+
const intent = isObject2(decoded(request.intent)) ? decoded(request.intent) : {};
|
|
8841
|
+
const objective = flat.objective ?? intent.objective;
|
|
8842
|
+
const scope = scopeOf(flat, objective);
|
|
8843
|
+
const fields = take(flat, ["objective"]);
|
|
8844
|
+
const authorityRefs = "refs" in flat ? refList(flat.refs) : undefined;
|
|
8845
|
+
delete flat.refs;
|
|
8846
|
+
nest(request, "intent", {
|
|
8847
|
+
...fields,
|
|
8848
|
+
...scope ? { scope } : {},
|
|
8849
|
+
...authorityRefs ? { authorityRefs } : {}
|
|
8850
|
+
}, { scope: defaultScope(objective), authorityRefs: [] });
|
|
8851
|
+
} else if (action === "progress") {
|
|
8852
|
+
const fields = take(flat, ["summary", "nextAction"]);
|
|
8853
|
+
const blockers = "blockers" in flat ? strings(flat.blockers) : undefined;
|
|
8854
|
+
delete flat.blockers;
|
|
8855
|
+
nest(request, "progress", {
|
|
8856
|
+
...fields,
|
|
8857
|
+
...Array.isArray(blockers) ? {
|
|
8858
|
+
blockers: blockers.map((item) => typeof item === "string" ? { reason: item, dependentAction: "", refs: [] } : item)
|
|
8859
|
+
} : {}
|
|
8860
|
+
}, { nextAction: null, blockers: [] });
|
|
8861
|
+
} else if (action === "close" && "decisionIds" in flat)
|
|
8862
|
+
flat.decisionIds = strings(flat.decisionIds);
|
|
8863
|
+
break;
|
|
8864
|
+
}
|
|
8865
|
+
case "policy": {
|
|
8866
|
+
if (action === "assess" || action === "preview") {
|
|
8867
|
+
const judgment = isObject2(decoded(request.judgment)) ? { ...decoded(request.judgment) } : {};
|
|
8868
|
+
for (const key of Object.keys(flat))
|
|
8869
|
+
if (!ENVELOPE.has(key)) {
|
|
8870
|
+
if (!(key in judgment))
|
|
8871
|
+
judgment[key] = decoded(flat[key]);
|
|
8872
|
+
delete flat[key];
|
|
8873
|
+
}
|
|
8874
|
+
request.judgment = judgment;
|
|
8875
|
+
}
|
|
8876
|
+
break;
|
|
8877
|
+
}
|
|
8878
|
+
case "evidence": {
|
|
8879
|
+
if (action === "record") {
|
|
8880
|
+
const review = "reviewSession" in flat ? decoded(flat.reviewSession) : undefined;
|
|
8881
|
+
delete flat.reviewSession;
|
|
8882
|
+
const fields = take(flat, [
|
|
8883
|
+
"kind",
|
|
8884
|
+
"claim",
|
|
8885
|
+
"result",
|
|
8886
|
+
"summary",
|
|
8887
|
+
"exitCode",
|
|
8888
|
+
"candidateId",
|
|
8889
|
+
"beforeCandidateId",
|
|
8890
|
+
"reviewContext"
|
|
8891
|
+
]);
|
|
8892
|
+
if ("refs" in flat)
|
|
8893
|
+
fields.refs = refList(flat.refs);
|
|
8894
|
+
if ("requirementIds" in flat)
|
|
8895
|
+
fields.requirementIds = strings(flat.requirementIds);
|
|
8896
|
+
delete flat.refs;
|
|
8897
|
+
delete flat.requirementIds;
|
|
8898
|
+
if (typeof review === "string" && review)
|
|
8899
|
+
fields.reviewContext = { kind: "host", host, handle: review };
|
|
8900
|
+
nest(request, "evidence", fields, {
|
|
8901
|
+
summary: "",
|
|
8902
|
+
refs: [],
|
|
8903
|
+
requirementIds: [],
|
|
8904
|
+
exitCode: null,
|
|
8905
|
+
reviewContext: null
|
|
8906
|
+
});
|
|
8907
|
+
}
|
|
8908
|
+
break;
|
|
8909
|
+
}
|
|
8910
|
+
case "finding": {
|
|
8911
|
+
if (action === "record") {
|
|
8912
|
+
const scope = scopeOf(flat, flat.claim);
|
|
8913
|
+
if (scope)
|
|
8914
|
+
request.scope = scope;
|
|
8915
|
+
else if (!("scope" in request))
|
|
8916
|
+
request.scope = defaultScope(request.claim);
|
|
8917
|
+
request.refs = "refs" in flat ? refList(flat.refs) : [];
|
|
8918
|
+
}
|
|
8919
|
+
break;
|
|
8920
|
+
}
|
|
8921
|
+
case "decision": {
|
|
8922
|
+
if (action === "record") {
|
|
8923
|
+
const binding = isObject2(decoded(request.binding)) ? decoded(request.binding) : {};
|
|
8924
|
+
const presented = flat.presented ?? binding.presented;
|
|
8925
|
+
const scope = scopeOf(flat, presented);
|
|
8926
|
+
const fields = take(flat, ["presented"]);
|
|
8927
|
+
const choice = flat.choice;
|
|
8928
|
+
delete flat.choice;
|
|
8929
|
+
const contentRefs = "refs" in flat ? refList(flat.refs) : undefined;
|
|
8930
|
+
delete flat.refs;
|
|
8931
|
+
nest(request, "binding", {
|
|
8932
|
+
...fields,
|
|
8933
|
+
...scope ? { scope } : {},
|
|
8934
|
+
...contentRefs ? { contentRefs } : {},
|
|
8935
|
+
...typeof choice === "string" ? { statedChoice: { ref: "choice", text: choice } } : {}
|
|
8936
|
+
}, { scope: defaultScope(presented), contentRefs: [] });
|
|
8937
|
+
}
|
|
8938
|
+
break;
|
|
8939
|
+
}
|
|
8940
|
+
case "worker": {
|
|
8941
|
+
if (action === "assign") {
|
|
8942
|
+
const objective = flat.objective;
|
|
8943
|
+
const scope = scopeOf(flat, objective);
|
|
8944
|
+
const fields = take(flat, ["role", "objective", "candidateId", "stoppingCondition"]);
|
|
8945
|
+
for (const key of ["decisionIds", "requirementIds"])
|
|
8946
|
+
if (key in flat) {
|
|
8947
|
+
fields[key] = strings(flat[key]);
|
|
8948
|
+
delete flat[key];
|
|
8949
|
+
}
|
|
8950
|
+
nest(request, "assignment", { ...fields, ...scope ? { scope } : {} }, { scope: defaultScope(objective), decisionIds: [], requirementIds: [] });
|
|
8951
|
+
} else if (action === "report") {
|
|
8952
|
+
const fields = take(flat, ["outcome", "summary"]);
|
|
8953
|
+
for (const key of ["evidenceIds", "findingIds"])
|
|
8954
|
+
if (key in flat) {
|
|
8955
|
+
fields[key] = strings(flat[key]);
|
|
8956
|
+
delete flat[key];
|
|
8957
|
+
}
|
|
8958
|
+
nest(request, "report", fields, { evidenceIds: [], findingIds: [] });
|
|
8959
|
+
}
|
|
8960
|
+
break;
|
|
8961
|
+
}
|
|
8962
|
+
case "state": {
|
|
8963
|
+
if ("bundle" in request)
|
|
8964
|
+
request.bundle = decoded(request.bundle);
|
|
8965
|
+
break;
|
|
8966
|
+
}
|
|
8967
|
+
}
|
|
8968
|
+
const defaults = {
|
|
8969
|
+
finding: { resolve: ["evidenceIds", "decisionIds"] },
|
|
8970
|
+
decision: { record: ["requirementIds"] }
|
|
8971
|
+
};
|
|
8972
|
+
for (const key of defaults[family]?.[String(action)] ?? [])
|
|
8973
|
+
if (!(key in request))
|
|
8974
|
+
request[key] = [];
|
|
8975
|
+
for (const key of LISTS)
|
|
8976
|
+
if (key in request && typeof request[key] === "string")
|
|
8977
|
+
request[key] = strings(request[key]);
|
|
8978
|
+
return request;
|
|
8979
|
+
}
|
|
8980
|
+
function parseAdvertisedOperation(family, input, host) {
|
|
8981
|
+
const request = normalizeOperationInput(family, input, host);
|
|
8982
|
+
if (isObject2(request) && request.schemaVersion !== SCHEMA_VERSION)
|
|
8983
|
+
return failure2("unsupported_version", "unsupported schema version", { operation: family });
|
|
8984
|
+
const parsed = advertisedOperationSchemas[family].safeParse(request);
|
|
8985
|
+
if (parsed.success)
|
|
8986
|
+
return success(null, null, request);
|
|
8987
|
+
return failure2("invalid_input", "operation input is invalid", {
|
|
8988
|
+
fields: parsed.error.issues.map((issue) => ({
|
|
8989
|
+
path: issue.code === "unrecognized_keys" ? issue.keys.join(".") : issue.path.join("."),
|
|
8990
|
+
reason: issue.message
|
|
8991
|
+
}))
|
|
8992
|
+
});
|
|
8993
|
+
}
|
|
8733
8994
|
// packages/workit-core/src/core/task-store.ts
|
|
8734
8995
|
import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
|
|
8735
8996
|
import * as fs7 from "node:fs";
|
|
@@ -9784,7 +10045,7 @@ class LogDamage extends Error {
|
|
|
9784
10045
|
this.upgrade = upgrade;
|
|
9785
10046
|
}
|
|
9786
10047
|
}
|
|
9787
|
-
var
|
|
10048
|
+
var isObject3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9788
10049
|
var parseEvent = (line, file, offset) => {
|
|
9789
10050
|
let value;
|
|
9790
10051
|
try {
|
|
@@ -9792,11 +10053,11 @@ var parseEvent = (line, file, offset) => {
|
|
|
9792
10053
|
} catch {
|
|
9793
10054
|
throw new LogDamage(`${file}: unreadable event at byte ${offset}`);
|
|
9794
10055
|
}
|
|
9795
|
-
if (!
|
|
10056
|
+
if (!isObject3(value) || typeof value.v !== "number")
|
|
9796
10057
|
throw new LogDamage(`${file}: malformed event at byte ${offset}`);
|
|
9797
10058
|
if (value.v > EVENT_VERSION)
|
|
9798
10059
|
throw new LogDamage(`${file}: event format v${value.v} was written by a newer Workit; upgrade Workit to read this task`, true);
|
|
9799
|
-
if (typeof value.seq !== "number" || !Number.isInteger(value.seq) || typeof value.type !== "string" || typeof value.id !== "string" || typeof value.task !== "string" || !
|
|
10060
|
+
if (typeof value.seq !== "number" || !Number.isInteger(value.seq) || typeof value.type !== "string" || typeof value.id !== "string" || typeof value.task !== "string" || !isObject3(value.data))
|
|
9800
10061
|
throw new LogDamage(`${file}: malformed event at byte ${offset}`);
|
|
9801
10062
|
return value;
|
|
9802
10063
|
};
|
|
@@ -10067,13 +10328,13 @@ var KNOWN_TYPES = new Set([
|
|
|
10067
10328
|
"decision.updated",
|
|
10068
10329
|
"worker.updated"
|
|
10069
10330
|
]);
|
|
10070
|
-
var
|
|
10071
|
-
var isKey = (value) =>
|
|
10331
|
+
var isObject4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10332
|
+
var isKey = (value) => isObject4(value) && typeof value.key === "string" && (value.kind === "branch" || value.kind === "detached" || value.kind === "dir") && (value.branch === null || typeof value.branch === "string");
|
|
10072
10333
|
var BLOB_REF = "$blob";
|
|
10073
|
-
var blobRef = (value) =>
|
|
10334
|
+
var blobRef = (value) => isObject4(value) && Object.keys(value).length === 1 && typeof value[BLOB_REF] === "string" && /^[0-9a-f]{64}$/.test(value[BLOB_REF]) ? value[BLOB_REF] : null;
|
|
10074
10335
|
var blobDigest = (value) => createHash3("sha256").update(JSON.stringify(value)).digest("hex");
|
|
10075
10336
|
var toBlob = (value, blobs) => {
|
|
10076
|
-
if (!
|
|
10337
|
+
if (!isObject4(value))
|
|
10077
10338
|
return value;
|
|
10078
10339
|
const digest = blobDigest(value);
|
|
10079
10340
|
blobs.write(digest, value);
|
|
@@ -10097,7 +10358,7 @@ var decode2 = (value, blobs) => {
|
|
|
10097
10358
|
return blobs.read(digest);
|
|
10098
10359
|
if (Array.isArray(value))
|
|
10099
10360
|
return value.map((item) => decode2(item, blobs));
|
|
10100
|
-
if (
|
|
10361
|
+
if (isObject4(value)) {
|
|
10101
10362
|
const out = {};
|
|
10102
10363
|
for (const [key, item] of Object.entries(value))
|
|
10103
10364
|
out[key] = decode2(item, blobs);
|
|
@@ -10116,7 +10377,7 @@ function reduce(state, event, blobs) {
|
|
|
10116
10377
|
}
|
|
10117
10378
|
let next = state;
|
|
10118
10379
|
if ("record" in data) {
|
|
10119
|
-
if (!
|
|
10380
|
+
if (!isObject4(data.record))
|
|
10120
10381
|
throw new LogDamage(`event ${event.seq} carries a malformed record`);
|
|
10121
10382
|
next = {
|
|
10122
10383
|
record: decode2(data.record, blobs),
|
|
@@ -10138,7 +10399,7 @@ function reduce(state, event, blobs) {
|
|
|
10138
10399
|
}
|
|
10139
10400
|
if ("key" in data)
|
|
10140
10401
|
next.key = isKey(data.key) ? data.key : null;
|
|
10141
|
-
if ("legacy" in data &&
|
|
10402
|
+
if ("legacy" in data && isObject4(data.legacy))
|
|
10142
10403
|
next.legacy = {
|
|
10143
10404
|
path: String(data.legacy.path ?? ""),
|
|
10144
10405
|
digest: String(data.legacy.digest ?? "")
|
|
@@ -10194,8 +10455,8 @@ var AUTO_COMPACT_BYTES = 2 * 1024 * 1024;
|
|
|
10194
10455
|
var TOMBSTONE = Symbol("tombstone");
|
|
10195
10456
|
var STALE_TEMPORARY_MS = 60 * 60000;
|
|
10196
10457
|
var INDEX_VERSION = 3;
|
|
10197
|
-
var
|
|
10198
|
-
var hostSession = (value) =>
|
|
10458
|
+
var isObject5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10459
|
+
var hostSession = (value) => isObject5(value) && value.kind === "host" && typeof value.host === "string" && typeof value.handle === "string" ? { host: value.host, handle: value.handle } : null;
|
|
10199
10460
|
var indexEntry = (task, key, legacy, file) => {
|
|
10200
10461
|
const sessions = [];
|
|
10201
10462
|
const intent = hostSession(task.intent.provenance.session);
|
|
@@ -11008,7 +11269,7 @@ class TaskStore {
|
|
|
11008
11269
|
readSnapshot(taskId) {
|
|
11009
11270
|
try {
|
|
11010
11271
|
const value = JSON.parse(fs7.readFileSync(this.snapshotPath(taskId), "utf8"));
|
|
11011
|
-
if (!
|
|
11272
|
+
if (!isObject5(value) || value.v !== 1 || value.task !== taskId || typeof value.seq !== "number" || typeof value.lastId !== "string" || typeof value.lastStart !== "number" || typeof value.end !== "number" || !isObject5(value.record))
|
|
11012
11273
|
return null;
|
|
11013
11274
|
return value;
|
|
11014
11275
|
} catch {
|
|
@@ -11354,11 +11615,11 @@ class TaskStore {
|
|
|
11354
11615
|
readIndex() {
|
|
11355
11616
|
try {
|
|
11356
11617
|
const value = JSON.parse(fs7.readFileSync(this.paths().index, "utf8"));
|
|
11357
|
-
if (!
|
|
11618
|
+
if (!isObject5(value) || value.version !== INDEX_VERSION || !isObject5(value.tasks))
|
|
11358
11619
|
return {};
|
|
11359
11620
|
const tasks = {};
|
|
11360
11621
|
for (const [id, entry] of Object.entries(value.tasks))
|
|
11361
|
-
if (
|
|
11622
|
+
if (isObject5(entry) && entry.id === id && typeof entry.file === "string" && typeof entry.status === "string" && typeof entry.updatedAt === "string" && typeof entry.workspaceId === "string" && Array.isArray(entry.sessions) && isObject5(entry.progress) && isObject5(entry.source))
|
|
11362
11623
|
tasks[id] = entry;
|
|
11363
11624
|
return tasks;
|
|
11364
11625
|
} catch {
|
|
@@ -11402,12 +11663,12 @@ class TaskStore {
|
|
|
11402
11663
|
return this.parseValue(value, schema, label);
|
|
11403
11664
|
}
|
|
11404
11665
|
parseValue(value, schema, label) {
|
|
11405
|
-
if (
|
|
11666
|
+
if (isObject5(value) && "schemaVersion" in value && value.schemaVersion !== SCHEMA_VERSION)
|
|
11406
11667
|
return failure2("unsupported_version", `unsupported ${label} schema version`);
|
|
11407
11668
|
const parsed = parseStoredRecord(schema, value);
|
|
11408
11669
|
if (parsed.success)
|
|
11409
11670
|
return success(null, null, parsed.data);
|
|
11410
|
-
const writerVersion =
|
|
11671
|
+
const writerVersion = isObject5(value) && isObject5(value.runtime) ? value.runtime.updatedWith : null;
|
|
11411
11672
|
if (parsed.critical.length > 0)
|
|
11412
11673
|
return failure2("recovery_required", `${label} record requires fields this Workit cannot read (${parsed.critical.join(", ")}); upgrade Workit before mutating this checkout`);
|
|
11413
11674
|
if (typeof writerVersion === "string" && isNewerVersion(writerVersion, runtimeVersion()))
|
|
@@ -11609,7 +11870,7 @@ class TaskStore {
|
|
|
11609
11870
|
}
|
|
11610
11871
|
try {
|
|
11611
11872
|
const value = JSON.parse(bytes);
|
|
11612
|
-
if (
|
|
11873
|
+
if (isObject5(value) && isObject5(value.store) && value.store.format === "workit-store")
|
|
11613
11874
|
return TOMBSTONE;
|
|
11614
11875
|
} catch {}
|
|
11615
11876
|
return { bytes };
|
|
@@ -11654,7 +11915,7 @@ class TaskStore {
|
|
|
11654
11915
|
isStub(file) {
|
|
11655
11916
|
try {
|
|
11656
11917
|
const value = JSON.parse(fs7.readFileSync(file, "utf8"));
|
|
11657
|
-
return
|
|
11918
|
+
return isObject5(value) && isObject5(value.store) && value.store.format === "workit-store";
|
|
11658
11919
|
} catch {
|
|
11659
11920
|
return false;
|
|
11660
11921
|
}
|
|
@@ -11825,10 +12086,10 @@ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
|
|
|
11825
12086
|
var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
|
|
11826
12087
|
|
|
11827
12088
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
11828
|
-
import { createHash as
|
|
11829
|
-
import * as
|
|
11830
|
-
import { spawnSync as
|
|
11831
|
-
import
|
|
12089
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
12090
|
+
import * as fs11 from "node:fs";
|
|
12091
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
12092
|
+
import path11 from "node:path";
|
|
11832
12093
|
|
|
11833
12094
|
// packages/workit-core/src/check-config.ts
|
|
11834
12095
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
@@ -12068,6 +12329,7 @@ var line = (run) => {
|
|
|
12068
12329
|
const value = run.stdout.trim();
|
|
12069
12330
|
return value ? value : null;
|
|
12070
12331
|
};
|
|
12332
|
+
var safeArg = (value) => value.length > 0 && !value.startsWith("-");
|
|
12071
12333
|
function headSha(cwd) {
|
|
12072
12334
|
return line(git2(cwd, ["rev-parse", "--verify", "-q", "HEAD^{commit}"]));
|
|
12073
12335
|
}
|
|
@@ -12159,124 +12421,704 @@ function worktreeSignal(cwd, options = {}) {
|
|
|
12159
12421
|
}
|
|
12160
12422
|
return `sig:${hash.digest("hex")}`;
|
|
12161
12423
|
}
|
|
12162
|
-
|
|
12163
|
-
|
|
12164
|
-
var digestBytes = (value) => createHash6("sha256").update(value).digest("hex");
|
|
12165
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path9.sep}`);
|
|
12166
|
-
var relative = (root, candidate) => path9.relative(root, candidate).split(path9.sep).join("/") || ".";
|
|
12167
|
-
var canonicalPath = (value) => {
|
|
12168
|
-
if (typeof value !== "string" || !value)
|
|
12169
|
-
return null;
|
|
12170
|
-
if (value.includes("\\") || value.split("/").includes(".."))
|
|
12424
|
+
function patchId(cwd, base, head) {
|
|
12425
|
+
if (!safeArg(base) || !safeArg(head))
|
|
12171
12426
|
return null;
|
|
12172
|
-
const
|
|
12173
|
-
|
|
12174
|
-
|
|
12175
|
-
|
|
12176
|
-
|
|
12177
|
-
|
|
12178
|
-
|
|
12179
|
-
|
|
12180
|
-
|
|
12181
|
-
|
|
12427
|
+
const diff = git2(cwd, [
|
|
12428
|
+
"diff",
|
|
12429
|
+
"-U3",
|
|
12430
|
+
"--diff-algorithm=myers",
|
|
12431
|
+
"--indent-heuristic",
|
|
12432
|
+
"--no-color",
|
|
12433
|
+
"--no-ext-diff",
|
|
12434
|
+
"--no-textconv",
|
|
12435
|
+
"--no-renames",
|
|
12436
|
+
"--full-index",
|
|
12437
|
+
"--src-prefix=a/",
|
|
12438
|
+
"--dst-prefix=b/",
|
|
12439
|
+
`${base}...${head}`,
|
|
12440
|
+
"--"
|
|
12441
|
+
]);
|
|
12442
|
+
if (!diff.ok || !diff.stdout.trim())
|
|
12182
12443
|
return null;
|
|
12444
|
+
const id = git2(cwd, ["patch-id", "--stable"], { input: diff.stdout });
|
|
12445
|
+
return line(id)?.split(/\s+/u)[0] ?? null;
|
|
12446
|
+
}
|
|
12447
|
+
function currentBranch(cwd) {
|
|
12448
|
+
return line(git2(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
|
|
12449
|
+
}
|
|
12450
|
+
|
|
12451
|
+
// packages/workit-core/src/core/policy/derive.ts
|
|
12452
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
12453
|
+
import path10 from "node:path";
|
|
12454
|
+
|
|
12455
|
+
// packages/workit-core/src/ledger.ts
|
|
12456
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
12457
|
+
import { createHash as createHash6, randomBytes as randomBytes2 } from "node:crypto";
|
|
12458
|
+
import fs10 from "node:fs";
|
|
12459
|
+
import path9 from "node:path";
|
|
12460
|
+
var LEDGER_VERSION = 1;
|
|
12461
|
+
var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
|
|
12462
|
+
var FAILING = new Set(["failed", "blocked"]);
|
|
12463
|
+
var STRONG_RESULTS = new Set(["verified", "tests-verified"]);
|
|
12464
|
+
var PR_ROW_TYPES = new Set(["pr.created", "pr.status", "pr.merged"]);
|
|
12465
|
+
var err = (code, error, unblock) => ({
|
|
12466
|
+
ok: false,
|
|
12467
|
+
code,
|
|
12468
|
+
error,
|
|
12469
|
+
...unblock ? { unblock } : {}
|
|
12470
|
+
});
|
|
12471
|
+
var gitRun2 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
|
|
12472
|
+
const run = spawnSync5("git", args, {
|
|
12473
|
+
cwd,
|
|
12474
|
+
encoding: "utf8",
|
|
12475
|
+
timeout: timeoutMs,
|
|
12476
|
+
killSignal: "SIGKILL",
|
|
12477
|
+
windowsHide: true,
|
|
12478
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
12479
|
+
env: {
|
|
12480
|
+
...process.env,
|
|
12481
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
12482
|
+
GIT_OPTIONAL_LOCKS: "0",
|
|
12483
|
+
LC_ALL: "C",
|
|
12484
|
+
LANGUAGE: "C"
|
|
12485
|
+
}
|
|
12486
|
+
});
|
|
12183
12487
|
return {
|
|
12184
|
-
|
|
12185
|
-
|
|
12186
|
-
|
|
12488
|
+
ok: run.status === 0,
|
|
12489
|
+
stdout: run.stdout ?? "",
|
|
12490
|
+
stderr: run.error ? String(run.error.message) : run.stderr ?? ""
|
|
12187
12491
|
};
|
|
12188
12492
|
};
|
|
12189
|
-
var
|
|
12190
|
-
|
|
12191
|
-
const
|
|
12192
|
-
|
|
12193
|
-
return false;
|
|
12194
|
-
const paths = normalized.paths;
|
|
12195
|
-
return paths.some((item) => item === "." || value === item || value.startsWith(`${item}/`)) && !excluded(value, normalized.exclusions);
|
|
12196
|
-
};
|
|
12197
|
-
var pathRelevant = (value, scope) => scopeMatches(value, scope);
|
|
12198
|
-
var compareCodeUnits2 = (left, right) => {
|
|
12199
|
-
const length = Math.min(left.length, right.length);
|
|
12200
|
-
for (let index = 0;index < length; index += 1) {
|
|
12201
|
-
const difference = left.charCodeAt(index) - right.charCodeAt(index);
|
|
12202
|
-
if (difference !== 0)
|
|
12203
|
-
return difference;
|
|
12204
|
-
}
|
|
12205
|
-
return left.length - right.length;
|
|
12493
|
+
var git3 = (cwd, args) => {
|
|
12494
|
+
const run = gitRun2(cwd, args);
|
|
12495
|
+
const value = run.ok ? run.stdout.trim() : "";
|
|
12496
|
+
return value ? value : null;
|
|
12206
12497
|
};
|
|
12207
|
-
var
|
|
12208
|
-
|
|
12209
|
-
|
|
12210
|
-
|
|
12211
|
-
});
|
|
12212
|
-
|
|
12213
|
-
|
|
12214
|
-
|
|
12215
|
-
|
|
12216
|
-
|
|
12217
|
-
|
|
12218
|
-
|
|
12219
|
-
|
|
12220
|
-
|
|
12221
|
-
|
|
12222
|
-
|
|
12223
|
-
|
|
12224
|
-
|
|
12225
|
-
|
|
12226
|
-
|
|
12227
|
-
|
|
12498
|
+
var safeRef = (value) => value.length > 0 && !value.startsWith("-");
|
|
12499
|
+
function resolveCommit(cwd, ref) {
|
|
12500
|
+
if (!safeRef(ref))
|
|
12501
|
+
return null;
|
|
12502
|
+
return git3(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]);
|
|
12503
|
+
}
|
|
12504
|
+
function branchHead(cwd, branch) {
|
|
12505
|
+
if (!safeRef(branch))
|
|
12506
|
+
return null;
|
|
12507
|
+
return resolveCommit(cwd, `refs/heads/${branch}`) ?? resolveCommit(cwd, `refs/remotes/origin/${branch}`);
|
|
12508
|
+
}
|
|
12509
|
+
function defaultBase(cwd) {
|
|
12510
|
+
const originHead = git3(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]);
|
|
12511
|
+
if (originHead && resolveCommit(cwd, originHead))
|
|
12512
|
+
return originHead;
|
|
12513
|
+
for (const candidate of ["origin/main", "origin/master", "main", "master"])
|
|
12514
|
+
if (resolveCommit(cwd, candidate))
|
|
12515
|
+
return candidate;
|
|
12516
|
+
return null;
|
|
12517
|
+
}
|
|
12518
|
+
function diffHash(cwd, base, head) {
|
|
12519
|
+
if (!safeRef(base) || !safeRef(head))
|
|
12520
|
+
return null;
|
|
12521
|
+
const diff = gitRun2(cwd, [
|
|
12522
|
+
"diff",
|
|
12523
|
+
"-U3",
|
|
12524
|
+
"--diff-algorithm=myers",
|
|
12525
|
+
"--indent-heuristic",
|
|
12526
|
+
"--no-color",
|
|
12527
|
+
"--no-ext-diff",
|
|
12528
|
+
"--no-textconv",
|
|
12529
|
+
"--no-renames",
|
|
12530
|
+
"--full-index",
|
|
12531
|
+
"--binary",
|
|
12532
|
+
"--src-prefix=a/",
|
|
12533
|
+
"--dst-prefix=b/",
|
|
12534
|
+
`${base}...${head}`,
|
|
12535
|
+
"--"
|
|
12536
|
+
], GIT_TIMEOUTS.worktree);
|
|
12537
|
+
if (!diff.ok || !diff.stdout)
|
|
12538
|
+
return null;
|
|
12539
|
+
const hash = createHash6("sha256");
|
|
12540
|
+
for (const line of diff.stdout.split(`
|
|
12541
|
+
`)) {
|
|
12542
|
+
if (line.startsWith("index "))
|
|
12543
|
+
continue;
|
|
12544
|
+
hash.update(line.startsWith("@@ ") ? "@@" : line);
|
|
12545
|
+
hash.update(`
|
|
12546
|
+
`);
|
|
12228
12547
|
}
|
|
12229
|
-
return {
|
|
12548
|
+
return `sha256:${hash.digest("hex")}`;
|
|
12549
|
+
}
|
|
12550
|
+
function storeRoot(cwd) {
|
|
12551
|
+
const location = resolveStore(path9.resolve(cwd));
|
|
12552
|
+
if (location instanceof Error)
|
|
12553
|
+
return err("unavailable", location.message.replace(/; fix the repository.*$/u, ""), "fix the repository (git status should work here) or run from a non-git directory");
|
|
12554
|
+
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
12555
|
+
}
|
|
12556
|
+
function ledgerPath(cwd) {
|
|
12557
|
+
const root = storeRoot(cwd);
|
|
12558
|
+
return root.ok ? { ok: true, value: path9.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
12559
|
+
}
|
|
12560
|
+
var NETWORK_FS_TYPES = new Set([
|
|
12561
|
+
26985,
|
|
12562
|
+
20859,
|
|
12563
|
+
4266872130,
|
|
12564
|
+
4283649346,
|
|
12565
|
+
1702057286,
|
|
12566
|
+
1397113167,
|
|
12567
|
+
12805120,
|
|
12568
|
+
16914839,
|
|
12569
|
+
1196443219,
|
|
12570
|
+
198183888
|
|
12571
|
+
]);
|
|
12572
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12573
|
+
var str2 = (value) => typeof value === "string" ? value : null;
|
|
12574
|
+
var normalizeActor = (value) => {
|
|
12575
|
+
const actor = isRecord2(value) ? value : {};
|
|
12576
|
+
return {
|
|
12577
|
+
host: str2(actor.host) ?? "unknown",
|
|
12578
|
+
session: str2(actor.session),
|
|
12579
|
+
agentId: str2(actor.agentId)
|
|
12580
|
+
};
|
|
12230
12581
|
};
|
|
12231
|
-
|
|
12232
|
-
|
|
12582
|
+
function supersedeAllowed(by, target) {
|
|
12583
|
+
if (by.type !== target.type)
|
|
12584
|
+
return `a ${by.type} row cannot supersede a ${target.type} row`;
|
|
12585
|
+
if (!by.actor.session || by.actor.session !== target.actor.session)
|
|
12586
|
+
return "only the session that wrote a row can supersede it";
|
|
12587
|
+
if (by.type === "verdict" && by.self === true && target.self !== true)
|
|
12588
|
+
return "a self verdict cannot replace an independent verdict";
|
|
12589
|
+
return null;
|
|
12590
|
+
}
|
|
12591
|
+
function readLedger(cwd) {
|
|
12592
|
+
const file = ledgerPath(cwd);
|
|
12593
|
+
if (!file.ok)
|
|
12594
|
+
return file;
|
|
12595
|
+
let text = "";
|
|
12233
12596
|
try {
|
|
12234
|
-
|
|
12235
|
-
} catch {
|
|
12236
|
-
|
|
12597
|
+
text = fs10.readFileSync(file.value, "utf8");
|
|
12598
|
+
} catch (error) {
|
|
12599
|
+
if (error.code === "ENOENT")
|
|
12600
|
+
return { ok: true, value: { path: file.value, rows: [], skipped: 0 } };
|
|
12601
|
+
return err("unavailable", `cannot read ${file.value}: ${error.message}`);
|
|
12237
12602
|
}
|
|
12238
|
-
|
|
12239
|
-
|
|
12240
|
-
|
|
12603
|
+
const rows = [];
|
|
12604
|
+
let skipped = 0;
|
|
12605
|
+
for (const raw of text.split(`
|
|
12606
|
+
`)) {
|
|
12607
|
+
const trimmed = raw.trim();
|
|
12608
|
+
if (!trimmed)
|
|
12241
12609
|
continue;
|
|
12242
|
-
|
|
12243
|
-
let stat;
|
|
12610
|
+
let parsed;
|
|
12244
12611
|
try {
|
|
12245
|
-
|
|
12612
|
+
parsed = JSON.parse(trimmed);
|
|
12246
12613
|
} catch {
|
|
12247
|
-
|
|
12614
|
+
skipped += 1;
|
|
12248
12615
|
continue;
|
|
12249
12616
|
}
|
|
12250
|
-
|
|
12251
|
-
|
|
12252
|
-
|
|
12253
|
-
uncertain = walk(root, target, output) || uncertain;
|
|
12254
|
-
}
|
|
12255
|
-
return uncertain;
|
|
12256
|
-
};
|
|
12257
|
-
var scopeRoots = (root, scope) => {
|
|
12258
|
-
const roots = [];
|
|
12259
|
-
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
12260
|
-
const target = path9.resolve(root, item);
|
|
12261
|
-
if (!inside(root, target))
|
|
12262
|
-
return failure2("invalid_input", `candidate scope escapes checkout: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} is outside the checkout` }] });
|
|
12263
|
-
let ancestor = target;
|
|
12264
|
-
while (!fs10.existsSync(ancestor) && ancestor !== root)
|
|
12265
|
-
ancestor = path9.dirname(ancestor);
|
|
12266
|
-
try {
|
|
12267
|
-
if (!inside(root, fs10.realpathSync(ancestor)))
|
|
12268
|
-
return failure2("invalid_input", `candidate scope escapes checkout through a symlink: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} resolves outside the checkout` }] });
|
|
12269
|
-
} catch {
|
|
12270
|
-
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
12617
|
+
if (!isRecord2(parsed) || !str2(parsed.id) || !str2(parsed.type) || !str2(parsed.at)) {
|
|
12618
|
+
skipped += 1;
|
|
12619
|
+
continue;
|
|
12271
12620
|
}
|
|
12272
|
-
|
|
12621
|
+
const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
|
|
12622
|
+
rows.push({
|
|
12623
|
+
...parsed,
|
|
12624
|
+
v: typeof parsed.v === "number" ? parsed.v : LEDGER_VERSION,
|
|
12625
|
+
id: parsed.id,
|
|
12626
|
+
at: parsed.at,
|
|
12627
|
+
type: parsed.type,
|
|
12628
|
+
actor: normalizeActor(parsed.actor),
|
|
12629
|
+
branch: str2(parsed.branch),
|
|
12630
|
+
head: str2(parsed.head),
|
|
12631
|
+
base: str2(parsed.base),
|
|
12632
|
+
baseSha: str2(parsed.baseSha),
|
|
12633
|
+
patchId: str2(parsed.patchId),
|
|
12634
|
+
diffHash: str2(parsed.diffHash),
|
|
12635
|
+
tree: str2(parsed.tree),
|
|
12636
|
+
dirty: typeof parsed.dirty === "boolean" ? parsed.dirty : null,
|
|
12637
|
+
...pr === undefined ? {} : { pr },
|
|
12638
|
+
...str2(parsed.supersedes) ? { supersedes: parsed.supersedes } : {},
|
|
12639
|
+
seq: rows.length + 1,
|
|
12640
|
+
superseded: false,
|
|
12641
|
+
supersedeIgnored: false
|
|
12642
|
+
});
|
|
12273
12643
|
}
|
|
12274
|
-
|
|
12644
|
+
const byId = new Map;
|
|
12645
|
+
for (const row of rows) {
|
|
12646
|
+
const target = row.supersedes ? byId.get(row.supersedes) : undefined;
|
|
12647
|
+
if (target && supersedeAllowed(row, target) === null)
|
|
12648
|
+
target.superseded = true;
|
|
12649
|
+
else if (row.supersedes)
|
|
12650
|
+
row.supersedeIgnored = true;
|
|
12651
|
+
if (!byId.has(row.id))
|
|
12652
|
+
byId.set(row.id, row);
|
|
12653
|
+
}
|
|
12654
|
+
return { ok: true, value: { path: file.value, rows, skipped } };
|
|
12655
|
+
}
|
|
12656
|
+
function verdictBasis(verdict, current) {
|
|
12657
|
+
if (verdict.head && verdict.head === current.head)
|
|
12658
|
+
return "fresh";
|
|
12659
|
+
if (verdict.patchId && verdict.patchId === current.patchId && verdict.diffHash && verdict.diffHash === current.diffHash)
|
|
12660
|
+
return "carried";
|
|
12661
|
+
return "stale";
|
|
12662
|
+
}
|
|
12663
|
+
var isLiveVerdict = (row) => row.type === "verdict" && typeof row.result === "string" && !row.superseded;
|
|
12664
|
+
function trailerSessions(cwd, base, head) {
|
|
12665
|
+
if (!base || !head || !safeRef(base) || !safeRef(head))
|
|
12666
|
+
return [];
|
|
12667
|
+
const out = git3(cwd, [
|
|
12668
|
+
"log",
|
|
12669
|
+
"--format=%(trailers:key=Workit-Session,valueonly,separator=%x00)",
|
|
12670
|
+
`${base}..${head}`
|
|
12671
|
+
]);
|
|
12672
|
+
return (out ?? "").split(/[\0\n]/u).map((value) => value.trim()).filter(Boolean);
|
|
12673
|
+
}
|
|
12674
|
+
function authorSessions(cwd, rows, branch, range = { base: null, head: null }) {
|
|
12675
|
+
const sessions = new Set;
|
|
12676
|
+
for (const row of rows) {
|
|
12677
|
+
if (row.branch !== branch || row.type !== "commit.recorded" && row.type !== "task.opened")
|
|
12678
|
+
continue;
|
|
12679
|
+
const session = str2(row.session) ?? row.actor.session;
|
|
12680
|
+
if (session)
|
|
12681
|
+
sessions.add(session);
|
|
12682
|
+
}
|
|
12683
|
+
for (const session of trailerSessions(cwd, range.base, range.head))
|
|
12684
|
+
sessions.add(session);
|
|
12685
|
+
return sessions;
|
|
12686
|
+
}
|
|
12687
|
+
var independenceReasons = (row, authors) => {
|
|
12688
|
+
const reasons = [];
|
|
12689
|
+
if (!row.actor.session)
|
|
12690
|
+
reasons.push("no_session");
|
|
12691
|
+
else if (authors.has(row.actor.session))
|
|
12692
|
+
reasons.push("author_session");
|
|
12693
|
+
if (row.self === true && !reasons.includes("no_session"))
|
|
12694
|
+
reasons.push("self");
|
|
12695
|
+
return reasons;
|
|
12696
|
+
};
|
|
12697
|
+
function effectiveOf(rows, authors) {
|
|
12698
|
+
let effective = null;
|
|
12699
|
+
for (const row of rows) {
|
|
12700
|
+
if (!effective) {
|
|
12701
|
+
effective = row;
|
|
12702
|
+
continue;
|
|
12703
|
+
}
|
|
12704
|
+
const rowIndependent = independenceReasons(row, authors).length === 0;
|
|
12705
|
+
const effIndependent = independenceReasons(effective, authors).length === 0;
|
|
12706
|
+
if (effIndependent && !rowIndependent)
|
|
12707
|
+
continue;
|
|
12708
|
+
if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session)
|
|
12709
|
+
continue;
|
|
12710
|
+
effective = row;
|
|
12711
|
+
}
|
|
12712
|
+
return effective;
|
|
12713
|
+
}
|
|
12714
|
+
function restackCarries(rows, branch, from, to) {
|
|
12715
|
+
if (!from || !to)
|
|
12716
|
+
return false;
|
|
12717
|
+
const edges = new Map;
|
|
12718
|
+
for (const row of rows)
|
|
12719
|
+
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
12720
|
+
edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
|
|
12721
|
+
const seen = new Set([from]);
|
|
12722
|
+
const queue = [from];
|
|
12723
|
+
while (queue.length) {
|
|
12724
|
+
const next = queue.shift();
|
|
12725
|
+
if (next === to)
|
|
12726
|
+
return true;
|
|
12727
|
+
for (const target of edges.get(next) ?? [])
|
|
12728
|
+
if (!seen.has(target)) {
|
|
12729
|
+
seen.add(target);
|
|
12730
|
+
queue.push(target);
|
|
12731
|
+
}
|
|
12732
|
+
}
|
|
12733
|
+
return false;
|
|
12734
|
+
}
|
|
12735
|
+
function checkVerdicts(cwd, branch, rows) {
|
|
12736
|
+
const head = branchHead(cwd, branch);
|
|
12737
|
+
const fallbackBase = defaultBase(cwd);
|
|
12738
|
+
const keyCache = new Map;
|
|
12739
|
+
const keyFor = (base) => {
|
|
12740
|
+
if (!base || !head)
|
|
12741
|
+
return { patchId: null, diffHash: null };
|
|
12742
|
+
let key = keyCache.get(base);
|
|
12743
|
+
if (!key) {
|
|
12744
|
+
key = { patchId: patchId(cwd, base, head), diffHash: diffHash(cwd, base, head) };
|
|
12745
|
+
keyCache.set(base, key);
|
|
12746
|
+
}
|
|
12747
|
+
return key;
|
|
12748
|
+
};
|
|
12749
|
+
const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head });
|
|
12750
|
+
const byKind = new Map;
|
|
12751
|
+
for (const row of rows)
|
|
12752
|
+
if (row.branch === branch && isLiveVerdict(row)) {
|
|
12753
|
+
const kind = typeof row.kind === "string" ? row.kind : "review";
|
|
12754
|
+
byKind.set(kind, [...byKind.get(kind) ?? [], row]);
|
|
12755
|
+
}
|
|
12756
|
+
const verdicts = [];
|
|
12757
|
+
for (const [kind, list] of byKind) {
|
|
12758
|
+
const row = effectiveOf(list, authors);
|
|
12759
|
+
if (!row)
|
|
12760
|
+
continue;
|
|
12761
|
+
let basis = verdictBasis(row, { head, ...keyFor(row.base ?? fallbackBase) });
|
|
12762
|
+
if (basis === "stale" && restackCarries(rows, branch, row.head, head))
|
|
12763
|
+
basis = "carried";
|
|
12764
|
+
const current = basis !== "stale";
|
|
12765
|
+
const independence = independenceReasons(row, authors);
|
|
12766
|
+
const reasons = [
|
|
12767
|
+
...current ? [] : ["stale"],
|
|
12768
|
+
...PASSING.has(String(row.result)) ? [] : ["not_passing"],
|
|
12769
|
+
...independence
|
|
12770
|
+
];
|
|
12771
|
+
verdicts.push({
|
|
12772
|
+
kind,
|
|
12773
|
+
basis,
|
|
12774
|
+
current,
|
|
12775
|
+
independent: independence.length === 0,
|
|
12776
|
+
accepted: reasons.length === 0,
|
|
12777
|
+
reasons,
|
|
12778
|
+
verdict: row
|
|
12779
|
+
});
|
|
12780
|
+
}
|
|
12781
|
+
verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
|
|
12782
|
+
const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
|
|
12783
|
+
const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
|
|
12784
|
+
const newestAccepted = verdicts.findLast((entry) => entry.accepted) ?? null;
|
|
12785
|
+
const accepted = newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
12786
|
+
accepted: false,
|
|
12787
|
+
verdict: failing?.verdict ?? null,
|
|
12788
|
+
reasons: failing ? ["failing_verdict"] : newestCurrent?.reasons ?? ["no_verdict"]
|
|
12789
|
+
};
|
|
12790
|
+
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
12791
|
+
return {
|
|
12792
|
+
branch,
|
|
12793
|
+
head,
|
|
12794
|
+
review: accepted.accepted ? "verified" : selfEntry ? "self-reviewed" : "unreviewed",
|
|
12795
|
+
selfVerdict: accepted.accepted ? null : selfEntry?.verdict ?? null,
|
|
12796
|
+
base: newestCurrent?.verdict.base ?? fallbackBase,
|
|
12797
|
+
patchId: keyFor(newestCurrent?.verdict.base ?? fallbackBase).patchId,
|
|
12798
|
+
current: { basis: newestCurrent?.basis ?? "none", verdict: newestCurrent?.verdict ?? null },
|
|
12799
|
+
accepted,
|
|
12800
|
+
verdicts,
|
|
12801
|
+
authors: [...authors].toSorted()
|
|
12802
|
+
};
|
|
12803
|
+
}
|
|
12804
|
+
var OBSERVED_ONLY = new Set(["check", ...PR_ROW_TYPES]);
|
|
12805
|
+
|
|
12806
|
+
// packages/workit-core/src/core/policy/derive.ts
|
|
12807
|
+
var RULES = {
|
|
12808
|
+
test: "check:test",
|
|
12809
|
+
self: "verdict:self",
|
|
12810
|
+
nonAuthor: "verdict:non-author",
|
|
12811
|
+
verified: "verdict:verified",
|
|
12812
|
+
product: "decision:product",
|
|
12813
|
+
plan: "plan"
|
|
12814
|
+
};
|
|
12815
|
+
var derived2 = (spec) => ({
|
|
12816
|
+
...spec,
|
|
12817
|
+
id: requirementId({
|
|
12818
|
+
ruleId: spec.ruleId,
|
|
12819
|
+
scope: spec.scope,
|
|
12820
|
+
satisfaction: spec.satisfaction,
|
|
12821
|
+
before: spec.before,
|
|
12822
|
+
dependentAction: spec.dependentAction
|
|
12823
|
+
})
|
|
12824
|
+
});
|
|
12825
|
+
var sorted = (values) => [...new Set(values)].toSorted();
|
|
12826
|
+
var normalizedScope = (scope) => ({
|
|
12827
|
+
description: scope.description,
|
|
12828
|
+
paths: sorted(scope.paths),
|
|
12829
|
+
exclusions: sorted(scope.exclusions)
|
|
12830
|
+
});
|
|
12831
|
+
function deriveRequirements(judgment, scope, constraints, verification = "self") {
|
|
12832
|
+
const out = [];
|
|
12833
|
+
const base = { scope, dependentAction: null };
|
|
12834
|
+
if (judgment.behaviorChange)
|
|
12835
|
+
out.push(derived2({
|
|
12836
|
+
...base,
|
|
12837
|
+
ruleId: RULES.test,
|
|
12838
|
+
dimension: "testing",
|
|
12839
|
+
reason: "Behavior changes: the configured test check must pass on the final tree.",
|
|
12840
|
+
satisfaction: "Run `workit check test` on the final tree.",
|
|
12841
|
+
before: "close",
|
|
12842
|
+
acceptanceAllowed: false
|
|
12843
|
+
}));
|
|
12844
|
+
if (judgment.behaviorChange && judgment.riskTier === "normal")
|
|
12845
|
+
out.push(verification === "independent" ? derived2({
|
|
12846
|
+
...base,
|
|
12847
|
+
ruleId: RULES.nonAuthor,
|
|
12848
|
+
dimension: "review",
|
|
12849
|
+
reason: "This workspace requires independent verification: a session that did not author the change records the verdict.",
|
|
12850
|
+
satisfaction: "An independent session records a passing verdict: `workit ledger verdict verified --how <what it exercised>`.",
|
|
12851
|
+
before: "close",
|
|
12852
|
+
acceptanceAllowed: false
|
|
12853
|
+
}) : derived2({
|
|
12854
|
+
...base,
|
|
12855
|
+
ruleId: RULES.self,
|
|
12856
|
+
dimension: "review",
|
|
12857
|
+
reason: "A behavior change needs a recorded verdict; the author's own is allowed and is shown as self-reviewed, never verified.",
|
|
12858
|
+
satisfaction: "After an observed passing `workit check test`, record `workit ledger verdict tests-verified --self --how <what you ran>` (or have a verifier record one).",
|
|
12859
|
+
before: "close",
|
|
12860
|
+
acceptanceAllowed: false
|
|
12861
|
+
}));
|
|
12862
|
+
if (judgment.riskTier === "high")
|
|
12863
|
+
out.push(derived2({
|
|
12864
|
+
...base,
|
|
12865
|
+
ruleId: RULES.verified,
|
|
12866
|
+
dimension: "review",
|
|
12867
|
+
reason: "High risk: an independent verifier must exercise the live surface.",
|
|
12868
|
+
satisfaction: "An independent verifier runs the real surface and records `workit ledger verdict verified --kind live --how <what it exercised>`.",
|
|
12869
|
+
before: "close",
|
|
12870
|
+
acceptanceAllowed: false
|
|
12871
|
+
}));
|
|
12872
|
+
if (judgment.productChoiceOpen)
|
|
12873
|
+
out.push(derived2({
|
|
12874
|
+
...base,
|
|
12875
|
+
ruleId: RULES.product,
|
|
12876
|
+
dimension: "decisions",
|
|
12877
|
+
reason: "A product or preference choice is open; the user decides it before code is written.",
|
|
12878
|
+
satisfaction: 'Ask the user (with your recommendation), then record the answer: `workit ledger decision "<choice>" --why "<reason>"`.',
|
|
12879
|
+
before: "write",
|
|
12880
|
+
acceptanceAllowed: false
|
|
12881
|
+
}));
|
|
12882
|
+
if (judgment.needsPlan || judgment.riskTier === "high")
|
|
12883
|
+
out.push(derived2({
|
|
12884
|
+
...base,
|
|
12885
|
+
ruleId: RULES.plan,
|
|
12886
|
+
dimension: "artifacts",
|
|
12887
|
+
reason: judgment.needsPlan ? "The work needs a written plan or spec before implementation." : "High-risk work needs a written plan before implementation.",
|
|
12888
|
+
satisfaction: "Write the plan or spec (e.g. docs/plans/<slug>.md; docs and Markdown writes stay allowed), then record it: `workit policy assess --ref <path>`.",
|
|
12889
|
+
before: "write",
|
|
12890
|
+
acceptanceAllowed: true
|
|
12891
|
+
}));
|
|
12892
|
+
for (const constraint of constraints)
|
|
12893
|
+
for (const obligation of constraint.requires)
|
|
12894
|
+
out.push(derived2({
|
|
12895
|
+
ruleId: `project-constraint:${constraint.id}:${obligation.kind}:${sha256({
|
|
12896
|
+
kind: obligation.kind,
|
|
12897
|
+
scope: normalizedScope(obligation.scope),
|
|
12898
|
+
refs: obligation.refs.map((ref) => JSON.stringify(ref)).toSorted(),
|
|
12899
|
+
method: obligation.method,
|
|
12900
|
+
before: obligation.before,
|
|
12901
|
+
dependentAction: obligation.dependentAction
|
|
12902
|
+
})}`,
|
|
12903
|
+
dimension: obligation.method ?? (obligation.kind === "check" ? "verification" : obligation.kind === "decision" ? "decisions" : "challenge"),
|
|
12904
|
+
scope: obligation.scope,
|
|
12905
|
+
reason: `${constraint.kind} constraint requires ${obligation.kind}: ${constraint.statement}`,
|
|
12906
|
+
satisfaction: `Satisfy the constraint obligation using its referenced ${obligation.kind}.`,
|
|
12907
|
+
before: obligation.before,
|
|
12908
|
+
dependentAction: obligation.dependentAction,
|
|
12909
|
+
acceptanceAllowed: constraint.acceptanceAllowed && obligation.kind !== "decision"
|
|
12910
|
+
}));
|
|
12911
|
+
const seen = new Set;
|
|
12912
|
+
return out.filter((item) => seen.has(item.id) ? false : (seen.add(item.id), true));
|
|
12913
|
+
}
|
|
12914
|
+
var latestJudgment = (task) => task.judgments?.at(-1)?.data ?? null;
|
|
12915
|
+
var ledgerView = (root) => {
|
|
12916
|
+
let branch;
|
|
12917
|
+
let rows;
|
|
12918
|
+
return {
|
|
12919
|
+
branch: () => branch === undefined ? branch = currentBranch(root) : branch,
|
|
12920
|
+
rows: () => {
|
|
12921
|
+
if (rows === undefined) {
|
|
12922
|
+
const read = readLedger(root);
|
|
12923
|
+
rows = read.ok ? read.value.rows : [];
|
|
12924
|
+
}
|
|
12925
|
+
return rows;
|
|
12926
|
+
}
|
|
12927
|
+
};
|
|
12928
|
+
};
|
|
12929
|
+
var productDecision = (task, ledger) => {
|
|
12930
|
+
const decisions = task.decisions.filter((entry) => entry.data.revoked === null && (entry.data.response === "approved" || entry.data.response === "stated") && entry.data.purpose !== "limitation");
|
|
12931
|
+
if (decisions.length)
|
|
12932
|
+
return {
|
|
12933
|
+
met: true,
|
|
12934
|
+
reason: "the user's decision is recorded on the task",
|
|
12935
|
+
decisionIds: decisions.map((entry) => entry.id)
|
|
12936
|
+
};
|
|
12937
|
+
const since = Date.parse(task.createdAt);
|
|
12938
|
+
const branch = ledger.branch();
|
|
12939
|
+
const row = ledger.rows().findLast((item) => item.type === "decision" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
|
|
12940
|
+
return row ? { met: true, reason: `ledger decision ${row.id} records the choice`, decisionIds: [] } : {
|
|
12941
|
+
met: false,
|
|
12942
|
+
reason: 'open product choice: ask the user, then `workit ledger decision "<choice>" --why "<reason>"` (or re-judge productChoiceOpen=false if it is not open)',
|
|
12943
|
+
decisionIds: []
|
|
12944
|
+
};
|
|
12945
|
+
};
|
|
12946
|
+
var planRecorded = (task, root) => {
|
|
12947
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync3(path10.resolve(root, ref.path)));
|
|
12948
|
+
if (cited)
|
|
12949
|
+
return {
|
|
12950
|
+
met: true,
|
|
12951
|
+
reason: `plan recorded: ${cited.kind === "file" ? cited.path : cited.kind === "external" ? cited.url : ""}`,
|
|
12952
|
+
decisionIds: []
|
|
12953
|
+
};
|
|
12954
|
+
const artifact = task.evidence.some((entry) => entry.data.kind === "artifact" && entry.data.result === "passed");
|
|
12955
|
+
return artifact ? { met: true, reason: "a passing artifact records the plan", decisionIds: [] } : {
|
|
12956
|
+
met: false,
|
|
12957
|
+
reason: "no plan recorded: write it (docs/ and Markdown writes stay allowed), then `workit policy assess --ref <path>`",
|
|
12958
|
+
decisionIds: []
|
|
12959
|
+
};
|
|
12960
|
+
};
|
|
12961
|
+
var verdict = (requirement, root, ledger) => {
|
|
12962
|
+
const branch = ledger.branch();
|
|
12963
|
+
if (!branch)
|
|
12964
|
+
return { met: false, reason: "no branch is checked out to hold a verdict", decisionIds: [] };
|
|
12965
|
+
const check = checkVerdicts(root, branch, ledger.rows());
|
|
12966
|
+
const failing = check.accepted.reasons.includes("failing_verdict");
|
|
12967
|
+
const strong = (entry) => STRONG_RESULTS.has(String(entry.verdict.result));
|
|
12968
|
+
const met = !failing && (requirement.ruleId === RULES.verified ? check.verdicts.find((entry) => entry.accepted && entry.kind === "live" && String(entry.verdict.result) === "verified") : requirement.ruleId === RULES.nonAuthor ? check.verdicts.find((entry) => entry.accepted && strong(entry)) : check.verdicts.find((entry) => entry.current && strong(entry)));
|
|
12969
|
+
if (met)
|
|
12970
|
+
return {
|
|
12971
|
+
met: true,
|
|
12972
|
+
reason: `${met.accepted ? "accepted independent" : "self-reviewed (author's own)"} ${String(met.verdict.result)} verdict ${met.verdict.id}`,
|
|
12973
|
+
decisionIds: []
|
|
12974
|
+
};
|
|
12975
|
+
const why = failing ? "a current independent verdict fails" : check.verdicts.some((entry) => entry.current && entry.verdict.result === "type-check-only") ? "type-check-only does not prove a behavior change" : (check.accepted.reasons.join(", ") || "no_verdict").replace(/_/g, " ");
|
|
12976
|
+
const unblock = requirement.ruleId === RULES.self ? "record your own verdict after an observed `workit check test`: `workit ledger verdict tests-verified --self --how <what you ran>` (shown as self-reviewed), or have a verifier record one" : `an independent session records \`workit ledger verdict verified${requirement.ruleId === RULES.verified ? " --kind live" : ""} --how <what it exercised>\``;
|
|
12977
|
+
return { met: false, reason: `${why}: ${unblock}`, decisionIds: [] };
|
|
12978
|
+
};
|
|
12979
|
+
function derivedRuleStatus(task, requirement, root, ledger) {
|
|
12980
|
+
switch (requirement.ruleId) {
|
|
12981
|
+
case RULES.product:
|
|
12982
|
+
return productDecision(task, ledger);
|
|
12983
|
+
case RULES.plan:
|
|
12984
|
+
return planRecorded(task, root);
|
|
12985
|
+
case RULES.self:
|
|
12986
|
+
case RULES.nonAuthor:
|
|
12987
|
+
case RULES.verified:
|
|
12988
|
+
return verdict(requirement, root, ledger);
|
|
12989
|
+
default:
|
|
12990
|
+
return null;
|
|
12991
|
+
}
|
|
12992
|
+
}
|
|
12993
|
+
function writeBlockers(task, root) {
|
|
12994
|
+
if (task.status === "closed" || !task.policy)
|
|
12995
|
+
return [];
|
|
12996
|
+
const ledger = ledgerView(root);
|
|
12997
|
+
return task.policy.requirements.flatMap((requirement) => {
|
|
12998
|
+
if (requirement.before !== "write")
|
|
12999
|
+
return [];
|
|
13000
|
+
const status = derivedRuleStatus(task, requirement, root, ledger);
|
|
13001
|
+
return status && !status.met ? [{ ruleId: requirement.ruleId, reason: status.reason }] : [];
|
|
13002
|
+
});
|
|
13003
|
+
}
|
|
13004
|
+
|
|
13005
|
+
// packages/workit-core/src/core/task-evaluation.ts
|
|
13006
|
+
var digestBytes = (value) => createHash7("sha256").update(value).digest("hex");
|
|
13007
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path11.sep}`);
|
|
13008
|
+
var relative = (root, candidate) => path11.relative(root, candidate).split(path11.sep).join("/") || ".";
|
|
13009
|
+
var canonicalPath = (value) => {
|
|
13010
|
+
if (typeof value !== "string" || !value)
|
|
13011
|
+
return null;
|
|
13012
|
+
if (value.includes("\\") || value.split("/").includes(".."))
|
|
13013
|
+
return null;
|
|
13014
|
+
const normalized = path11.posix.normalize(value);
|
|
13015
|
+
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
13016
|
+
return null;
|
|
13017
|
+
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
13018
|
+
};
|
|
13019
|
+
var canonicalScope = (scope) => {
|
|
13020
|
+
const paths = scope.paths.length ? scope.paths : ["."];
|
|
13021
|
+
const normalizedPaths = paths.map(canonicalPath);
|
|
13022
|
+
const normalizedExclusions = scope.exclusions.map(canonicalPath);
|
|
13023
|
+
if (normalizedPaths.some((value) => value === null) || normalizedExclusions.some((value) => value === null))
|
|
13024
|
+
return null;
|
|
13025
|
+
return {
|
|
13026
|
+
description: scope.description,
|
|
13027
|
+
paths: [...new Set(normalizedPaths)],
|
|
13028
|
+
exclusions: [...new Set(normalizedExclusions)]
|
|
13029
|
+
};
|
|
13030
|
+
};
|
|
13031
|
+
var excluded = (value, exclusions) => exclusions.some((item) => item === value || item !== "." && value.startsWith(`${item}/`));
|
|
13032
|
+
var scopeMatches = (value, scope) => {
|
|
13033
|
+
const normalized = canonicalScope(scope);
|
|
13034
|
+
if (!normalized)
|
|
13035
|
+
return false;
|
|
13036
|
+
const paths = normalized.paths;
|
|
13037
|
+
return paths.some((item) => item === "." || value === item || value.startsWith(`${item}/`)) && !excluded(value, normalized.exclusions);
|
|
13038
|
+
};
|
|
13039
|
+
var pathRelevant = (value, scope) => scopeMatches(value, scope);
|
|
13040
|
+
var compareCodeUnits2 = (left, right) => {
|
|
13041
|
+
const length = Math.min(left.length, right.length);
|
|
13042
|
+
for (let index = 0;index < length; index += 1) {
|
|
13043
|
+
const difference = left.charCodeAt(index) - right.charCodeAt(index);
|
|
13044
|
+
if (difference !== 0)
|
|
13045
|
+
return difference;
|
|
13046
|
+
}
|
|
13047
|
+
return left.length - right.length;
|
|
13048
|
+
};
|
|
13049
|
+
var gitPaths = (root) => {
|
|
13050
|
+
const result = spawnSync6("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
13051
|
+
cwd: root,
|
|
13052
|
+
encoding: "buffer"
|
|
13053
|
+
});
|
|
13054
|
+
if (result.status !== 0) {
|
|
13055
|
+
const stderr = result.stderr?.toString("utf8") ?? "";
|
|
13056
|
+
return {
|
|
13057
|
+
paths: [],
|
|
13058
|
+
uncertain: !/not a git repository/i.test(stderr),
|
|
13059
|
+
git: !/not a git repository/i.test(stderr)
|
|
13060
|
+
};
|
|
13061
|
+
}
|
|
13062
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path11.sep).join("/")) : [];
|
|
13063
|
+
const stagedDeleted = spawnSync6("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
13064
|
+
if (stagedDeleted.status !== 0) {
|
|
13065
|
+
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
13066
|
+
if (!/not a git repository/i.test(stderr))
|
|
13067
|
+
return { paths, uncertain: true, git: true };
|
|
13068
|
+
} else if (stagedDeleted.stdout) {
|
|
13069
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path11.sep).join("/")));
|
|
13070
|
+
}
|
|
13071
|
+
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
13072
|
+
};
|
|
13073
|
+
var walk = (root, directory, output) => {
|
|
13074
|
+
let names;
|
|
13075
|
+
try {
|
|
13076
|
+
names = fs11.readdirSync(directory);
|
|
13077
|
+
} catch {
|
|
13078
|
+
return true;
|
|
13079
|
+
}
|
|
13080
|
+
let uncertain = false;
|
|
13081
|
+
for (const name of names) {
|
|
13082
|
+
if (name === ".git" || name === ".workit")
|
|
13083
|
+
continue;
|
|
13084
|
+
const target = path11.join(directory, name);
|
|
13085
|
+
let stat;
|
|
13086
|
+
try {
|
|
13087
|
+
stat = fs11.lstatSync(target);
|
|
13088
|
+
} catch {
|
|
13089
|
+
uncertain = true;
|
|
13090
|
+
continue;
|
|
13091
|
+
}
|
|
13092
|
+
const item = relative(root, target);
|
|
13093
|
+
output.add(item);
|
|
13094
|
+
if (stat.isDirectory() && !stat.isSymbolicLink())
|
|
13095
|
+
uncertain = walk(root, target, output) || uncertain;
|
|
13096
|
+
}
|
|
13097
|
+
return uncertain;
|
|
13098
|
+
};
|
|
13099
|
+
var scopeRoots = (root, scope) => {
|
|
13100
|
+
const roots = [];
|
|
13101
|
+
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
13102
|
+
const target = path11.resolve(root, item);
|
|
13103
|
+
if (!inside(root, target))
|
|
13104
|
+
return failure2("invalid_input", `candidate scope escapes checkout: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} is outside the checkout` }] });
|
|
13105
|
+
let ancestor = target;
|
|
13106
|
+
while (!fs11.existsSync(ancestor) && ancestor !== root)
|
|
13107
|
+
ancestor = path11.dirname(ancestor);
|
|
13108
|
+
try {
|
|
13109
|
+
if (!inside(root, fs11.realpathSync(ancestor)))
|
|
13110
|
+
return failure2("invalid_input", `candidate scope escapes checkout through a symlink: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} resolves outside the checkout` }] });
|
|
13111
|
+
} catch {
|
|
13112
|
+
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
13113
|
+
}
|
|
13114
|
+
roots.push(target);
|
|
13115
|
+
}
|
|
13116
|
+
return success(null, null, roots);
|
|
12275
13117
|
};
|
|
12276
13118
|
function captureCandidate(root, scope, environment = []) {
|
|
12277
13119
|
let checkout;
|
|
12278
13120
|
try {
|
|
12279
|
-
checkout =
|
|
13121
|
+
checkout = fs11.realpathSync(root);
|
|
12280
13122
|
} catch {
|
|
12281
13123
|
return failure2("invalid_input", "candidate root does not exist");
|
|
12282
13124
|
}
|
|
@@ -12294,11 +13136,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
12294
13136
|
if (item === ".git" || item === ".workit")
|
|
12295
13137
|
continue;
|
|
12296
13138
|
names.add(item);
|
|
12297
|
-
if (!
|
|
13139
|
+
if (!fs11.existsSync(target))
|
|
12298
13140
|
continue;
|
|
12299
13141
|
let stat;
|
|
12300
13142
|
try {
|
|
12301
|
-
stat =
|
|
13143
|
+
stat = fs11.lstatSync(target);
|
|
12302
13144
|
} catch {
|
|
12303
13145
|
uncertain = true;
|
|
12304
13146
|
continue;
|
|
@@ -12314,17 +13156,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
12314
13156
|
for (const item of [...names].toSorted()) {
|
|
12315
13157
|
if (!scopeMatches(item, normalizedScope))
|
|
12316
13158
|
continue;
|
|
12317
|
-
const target =
|
|
13159
|
+
const target = path11.join(checkout, item);
|
|
12318
13160
|
try {
|
|
12319
|
-
const stat =
|
|
13161
|
+
const stat = fs11.lstatSync(target);
|
|
12320
13162
|
if (stat.isSymbolicLink()) {
|
|
12321
|
-
const link =
|
|
13163
|
+
const link = fs11.readlinkSync(target);
|
|
12322
13164
|
files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
|
|
12323
13165
|
} else if (stat.isFile()) {
|
|
12324
13166
|
files.push({
|
|
12325
13167
|
path: item,
|
|
12326
13168
|
kind: "file",
|
|
12327
|
-
digest: digestBytes(
|
|
13169
|
+
digest: digestBytes(fs11.readFileSync(target)),
|
|
12328
13170
|
executable: (stat.mode & 73) !== 0
|
|
12329
13171
|
});
|
|
12330
13172
|
} else if (!stat.isDirectory()) {
|
|
@@ -12349,7 +13191,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
12349
13191
|
const values = supplied.toSorted((left, right) => compareCodeUnits2(left.name, right.name)).map(({ name, value }) => ({ name, value, refs: [] }));
|
|
12350
13192
|
if (values.some(({ value }) => value === null))
|
|
12351
13193
|
completeness = "uncertain";
|
|
12352
|
-
const headResult =
|
|
13194
|
+
const headResult = spawnSync6("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
|
|
12353
13195
|
if (headResult.status !== 0 && !/not a git repository/i.test(headResult.stderr ?? ""))
|
|
12354
13196
|
completeness = "uncertain";
|
|
12355
13197
|
const head = headResult.status === 0 ? headResult.stdout.trim() : null;
|
|
@@ -12489,7 +13331,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
|
|
|
12489
13331
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
12490
13332
|
let root;
|
|
12491
13333
|
try {
|
|
12492
|
-
root =
|
|
13334
|
+
root = fs11.realpathSync(checkoutRoot);
|
|
12493
13335
|
} catch {
|
|
12494
13336
|
return failure2("permission_denied", "checkout root is unavailable");
|
|
12495
13337
|
}
|
|
@@ -12498,23 +13340,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
12498
13340
|
continue;
|
|
12499
13341
|
if (!reference.digest)
|
|
12500
13342
|
return failure2("invalid_input", "document references require a byte digest");
|
|
12501
|
-
const target =
|
|
12502
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
13343
|
+
const target = path11.resolve(root, reference.path);
|
|
13344
|
+
if (target !== root && !target.startsWith(`${root}${path11.sep}`))
|
|
12503
13345
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
12504
13346
|
fields: [
|
|
12505
13347
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
12506
13348
|
]
|
|
12507
13349
|
});
|
|
12508
13350
|
try {
|
|
12509
|
-
const below =
|
|
13351
|
+
const below = path11.relative(root, target);
|
|
12510
13352
|
let current = root;
|
|
12511
|
-
for (const segment of below.split(
|
|
13353
|
+
for (const segment of below.split(path11.sep)) {
|
|
12512
13354
|
if (!segment)
|
|
12513
13355
|
continue;
|
|
12514
|
-
current =
|
|
12515
|
-
const stat =
|
|
12516
|
-
const real =
|
|
12517
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
13356
|
+
current = path11.join(current, segment);
|
|
13357
|
+
const stat = fs11.lstatSync(current);
|
|
13358
|
+
const real = fs11.realpathSync(current);
|
|
13359
|
+
if (real !== root && !real.startsWith(`${root}${path11.sep}`))
|
|
12518
13360
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
12519
13361
|
fields: [
|
|
12520
13362
|
{
|
|
@@ -12530,7 +13372,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
12530
13372
|
if (current !== target && !stat.isDirectory())
|
|
12531
13373
|
return failure2("invalid_input", "document reference ancestor is not a directory");
|
|
12532
13374
|
}
|
|
12533
|
-
if (digestBytes(
|
|
13375
|
+
if (digestBytes(fs11.readFileSync(target)) !== reference.digest)
|
|
12534
13376
|
return failure2("permission_denied", "decision document bytes have changed");
|
|
12535
13377
|
} catch {
|
|
12536
13378
|
return failure2("invalid_input", "decision document is unavailable");
|
|
@@ -12615,7 +13457,35 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
12615
13457
|
decisionIds: [],
|
|
12616
13458
|
reason: "stored policy version is unsupported; reassessment is required"
|
|
12617
13459
|
}));
|
|
13460
|
+
const ledger = ledgerView(checkoutRoot);
|
|
12618
13461
|
return (task.policy?.requirements ?? []).map((requirement) => {
|
|
13462
|
+
const derived = derivedRuleStatus(task, requirement, checkoutRoot, ledger);
|
|
13463
|
+
if (derived) {
|
|
13464
|
+
if (derived.met)
|
|
13465
|
+
return {
|
|
13466
|
+
requirementId: requirement.id,
|
|
13467
|
+
status: "satisfied",
|
|
13468
|
+
evidenceIds: [],
|
|
13469
|
+
decisionIds: derived.decisionIds,
|
|
13470
|
+
reason: derived.reason
|
|
13471
|
+
};
|
|
13472
|
+
const waived = requirement.acceptanceAllowed ? applicableDecision(task, workspace, requirement, checkoutRoot) : [];
|
|
13473
|
+
if (waived.length)
|
|
13474
|
+
return {
|
|
13475
|
+
requirementId: requirement.id,
|
|
13476
|
+
status: "accepted_limitation",
|
|
13477
|
+
evidenceIds: [],
|
|
13478
|
+
decisionIds: waived.map((decision) => task.decisions.find((entry) => entry.data === decision).id),
|
|
13479
|
+
reason: "an applicable approved limitation permits the missing evidence"
|
|
13480
|
+
};
|
|
13481
|
+
return {
|
|
13482
|
+
requirementId: requirement.id,
|
|
13483
|
+
status: "unsatisfied",
|
|
13484
|
+
evidenceIds: [],
|
|
13485
|
+
decisionIds: [],
|
|
13486
|
+
reason: derived.reason
|
|
13487
|
+
};
|
|
13488
|
+
}
|
|
12619
13489
|
const gate = observedGate(requirement) ? evaluateObservedGate(task, requirement, statuses, configFor()) : null;
|
|
12620
13490
|
if (gate?.satisfied.length)
|
|
12621
13491
|
return {
|
|
@@ -12870,10 +13740,10 @@ Autonomy contract:
|
|
|
12870
13740
|
inferred, or guess. Prose is a note: only a configured check the CLI ran
|
|
12871
13741
|
(\`workit check <name>\`) satisfies a gate; an ad-hoc
|
|
12872
13742
|
\`workit check -- <cmd>\` never does.
|
|
12873
|
-
- Author is not verifier:
|
|
12874
|
-
|
|
12875
|
-
|
|
12876
|
-
|
|
13743
|
+
- Author is not verifier: your own verdict is \`--self\` (after an observed
|
|
13744
|
+
\`workit check test\`), shown as self-reviewed, never verified. High risk,
|
|
13745
|
+
\`verification: independent\` and merge need a verifier with its own
|
|
13746
|
+
session (\`WORKIT_SESSION_ID=<lead>-v<n>\`; Claude Code subagents get one).
|
|
12877
13747
|
- A local-commit endpoint does not imply PR readiness, and a local commit alone
|
|
12878
13748
|
is not evidence of a requested remote push. Before reporting delivery,
|
|
12879
13749
|
reconcile every requested item and observe it (\`workit verify-delivery\`).
|
|
@@ -12886,7 +13756,9 @@ PATH, \`npx -y @brainervirus/workit-cli\`). \`busy\` is retryable
|
|
|
12886
13756
|
The task families (task, policy, evidence, finding, decision, worker, state)
|
|
12887
13757
|
are optional continuity for tracked work; each branch has one implicit task
|
|
12888
13758
|
that the first recording creates, and a solo edit needs no task. A decision
|
|
12889
|
-
records a user choice; it never authorizes an effect.
|
|
13759
|
+
records a user choice; it never authorizes an effect. Judge work once with
|
|
13760
|
+
\`workit policy assess --judge risk= behavior= product-choice= plan=\`; an
|
|
13761
|
+
open choice or missing plan blocks edits until recorded.
|
|
12890
13762
|
Omit revisions; a revision_conflict means one you passed is stale, so re-read.
|
|
12891
13763
|
Across repositories, bind each item to its checkout, branch and endpoint,
|
|
12892
13764
|
resolve competing targets before a mutation, and reconcile an uncertain
|
|
@@ -13024,7 +13896,7 @@ var exportDigest = (bundle) => sha256({
|
|
|
13024
13896
|
task: bundle.task
|
|
13025
13897
|
});
|
|
13026
13898
|
// packages/workit-core/src/core/branch.ts
|
|
13027
|
-
import { createHash as
|
|
13899
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
13028
13900
|
|
|
13029
13901
|
// packages/workit-core/src/core/git.ts
|
|
13030
13902
|
import { execFileSync } from "node:child_process";
|
|
@@ -13098,14 +13970,14 @@ var gitContext = (workspaceRoot, paths = []) => {
|
|
|
13098
13970
|
import {
|
|
13099
13971
|
copyFileSync as copyFileSync2,
|
|
13100
13972
|
cpSync as cpSync2,
|
|
13101
|
-
existsSync as
|
|
13973
|
+
existsSync as existsSync5,
|
|
13102
13974
|
mkdirSync as mkdirSync2,
|
|
13103
13975
|
readFileSync as readFileSync3,
|
|
13104
13976
|
readdirSync as readdirSync3,
|
|
13105
13977
|
writeFileSync as writeFileSync2
|
|
13106
13978
|
} from "node:fs";
|
|
13107
13979
|
import os2 from "node:os";
|
|
13108
|
-
import
|
|
13980
|
+
import path12 from "node:path";
|
|
13109
13981
|
|
|
13110
13982
|
// packages/workit-core/src/core/boundary.ts
|
|
13111
13983
|
import { statSync as statSync2 } from "node:fs";
|
|
@@ -13164,7 +14036,7 @@ var mergePreset = (preset, input = {}, current = {
|
|
|
13164
14036
|
protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
|
|
13165
14037
|
};
|
|
13166
14038
|
};
|
|
13167
|
-
var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ??
|
|
14039
|
+
var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path12.join(process.env.XDG_CONFIG_HOME || path12.join(os2.homedir(), ".config"), "workit");
|
|
13168
14040
|
var migratedDir = null;
|
|
13169
14041
|
var migrationFailed = false;
|
|
13170
14042
|
var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
@@ -13174,12 +14046,12 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
13174
14046
|
migratedDir = dir;
|
|
13175
14047
|
return dir;
|
|
13176
14048
|
}
|
|
13177
|
-
const legacy =
|
|
13178
|
-
if (!
|
|
14049
|
+
const legacy = path12.join(process.env.XDG_CONFIG_HOME || path12.join(os2.homedir(), ".config"), "workflow-toolkit");
|
|
14050
|
+
if (!existsSync5(legacy)) {
|
|
13179
14051
|
migratedDir = dir;
|
|
13180
14052
|
return dir;
|
|
13181
14053
|
}
|
|
13182
|
-
if (!migrationFailed &&
|
|
14054
|
+
if (!migrationFailed && existsSync5(dir)) {
|
|
13183
14055
|
migratedDir = dir;
|
|
13184
14056
|
return dir;
|
|
13185
14057
|
}
|
|
@@ -13187,9 +14059,9 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
13187
14059
|
mkdirSync2(dir, { recursive: true });
|
|
13188
14060
|
diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
|
|
13189
14061
|
for (const entry of readdirSync3(legacy, { withFileTypes: true })) {
|
|
13190
|
-
const src =
|
|
13191
|
-
const dest =
|
|
13192
|
-
if (
|
|
14062
|
+
const src = path12.join(legacy, entry.name);
|
|
14063
|
+
const dest = path12.join(dir, entry.name);
|
|
14064
|
+
if (existsSync5(dest))
|
|
13193
14065
|
continue;
|
|
13194
14066
|
try {
|
|
13195
14067
|
if (entry.isDirectory())
|
|
@@ -13260,7 +14132,7 @@ var parseConfigResult = (raw, file) => {
|
|
|
13260
14132
|
};
|
|
13261
14133
|
};
|
|
13262
14134
|
var readConfigTyped = (dir) => {
|
|
13263
|
-
const file =
|
|
14135
|
+
const file = path12.join(dir ?? configDir(), "config.json");
|
|
13264
14136
|
return parseConfigResult(readSafe(file), file);
|
|
13265
14137
|
};
|
|
13266
14138
|
var readConfig = () => {
|
|
@@ -13278,7 +14150,7 @@ var mergeConfigValues = (input, current) => ({
|
|
|
13278
14150
|
var writeConfig = (config) => {
|
|
13279
14151
|
const dir = configDir();
|
|
13280
14152
|
mkdirSync2(dir, { recursive: true });
|
|
13281
|
-
writeFileSync2(
|
|
14153
|
+
writeFileSync2(path12.join(dir, "config.json"), JSON.stringify(config, null, 2) + `
|
|
13282
14154
|
`, "utf8");
|
|
13283
14155
|
};
|
|
13284
14156
|
var resolveBranchPolicy = (config, workspace) => {
|
|
@@ -13317,9 +14189,9 @@ var resolveCommitPolicy = (config, workspace) => {
|
|
|
13317
14189
|
};
|
|
13318
14190
|
|
|
13319
14191
|
// packages/workit-core/src/core/workspaces.ts
|
|
13320
|
-
import { createHash as
|
|
14192
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
13321
14193
|
import { readFileSync as readFileSync4, realpathSync as realpathSync3 } from "node:fs";
|
|
13322
|
-
import
|
|
14194
|
+
import path13 from "node:path";
|
|
13323
14195
|
var nonBlank = string2().refine((value) => value.trim().length > 0, "must not be blank");
|
|
13324
14196
|
var branchPreset = custom((value) => typeof value === "string" && Object.hasOwn(PRESETS, value), "unsupported branch preset");
|
|
13325
14197
|
var commitPreset = custom((value) => typeof value === "string" && COMMIT_PRESETS.includes(value), "unsupported commit preset");
|
|
@@ -13372,7 +14244,7 @@ var releaseTrackSchema = object({
|
|
|
13372
14244
|
context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
|
|
13373
14245
|
}
|
|
13374
14246
|
}
|
|
13375
|
-
if (track.versionSource.kind === "package-json" && (
|
|
14247
|
+
if (track.versionSource.kind === "package-json" && (path13.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
|
|
13376
14248
|
context.addIssue({
|
|
13377
14249
|
code: "custom",
|
|
13378
14250
|
path: ["versionSource", "path"],
|
|
@@ -13391,6 +14263,7 @@ var workspaceConfigSchema = object({
|
|
|
13391
14263
|
}).passthrough().optional(),
|
|
13392
14264
|
autoApprove: union([boolean2(), array(nonBlank)]).optional(),
|
|
13393
14265
|
defaultEndpoint: _enum(["commit", "pr"]).optional(),
|
|
14266
|
+
verification: _enum(["self", "independent"]).optional(),
|
|
13394
14267
|
autonomy: object(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
|
|
13395
14268
|
kind,
|
|
13396
14269
|
union([boolean2(), literal("verified")]).optional()
|
|
@@ -13431,7 +14304,7 @@ var workspacesFileSchema = object({ workspaces: array(workspaceConfigSchema).opt
|
|
|
13431
14304
|
names.add(workspace.name);
|
|
13432
14305
|
}
|
|
13433
14306
|
});
|
|
13434
|
-
var workspacesRevision = (contents) =>
|
|
14307
|
+
var workspacesRevision = (contents) => createHash8("sha256").update(contents ?? "<missing>").digest("hex");
|
|
13435
14308
|
var validateWorkspacesDocument = (parsed, file) => {
|
|
13436
14309
|
if (!isConfigObject(parsed)) {
|
|
13437
14310
|
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
@@ -13469,7 +14342,7 @@ var validateWorkspacesDocument = (parsed, file) => {
|
|
|
13469
14342
|
};
|
|
13470
14343
|
};
|
|
13471
14344
|
var readWorkspacesResult = (dir = configDir()) => {
|
|
13472
|
-
const file =
|
|
14345
|
+
const file = path13.join(dir, "workspaces.json");
|
|
13473
14346
|
let raw;
|
|
13474
14347
|
try {
|
|
13475
14348
|
raw = readFileSync4(file, "utf8");
|
|
@@ -13640,7 +14513,7 @@ var runtimeWorkspaceIndexSchema = object({
|
|
|
13640
14513
|
workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
13641
14514
|
}).passthrough();
|
|
13642
14515
|
var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
13643
|
-
const file =
|
|
14516
|
+
const file = path13.join(configDir(), "workspaces.json");
|
|
13644
14517
|
let raw;
|
|
13645
14518
|
try {
|
|
13646
14519
|
raw = readFileSync4(file, "utf8");
|
|
@@ -13703,13 +14576,13 @@ var resolveRuntimeWorkspacePolicy = (cwd, kind, requestedProfile) => {
|
|
|
13703
14576
|
};
|
|
13704
14577
|
|
|
13705
14578
|
// packages/workit-core/src/core/vcs-config.ts
|
|
13706
|
-
import
|
|
13707
|
-
import
|
|
13708
|
-
import { spawnSync as
|
|
13709
|
-
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ??
|
|
14579
|
+
import fs12 from "node:fs";
|
|
14580
|
+
import path14 from "node:path";
|
|
14581
|
+
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
14582
|
+
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path14.join(configDir(), "vcs.json");
|
|
13710
14583
|
var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
13711
14584
|
var remoteProvider = (cwd) => {
|
|
13712
|
-
const r =
|
|
14585
|
+
const r = spawnSync7("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
|
|
13713
14586
|
if (r.status !== 0)
|
|
13714
14587
|
return null;
|
|
13715
14588
|
const url = (r.stdout ?? "").trim();
|
|
@@ -13746,7 +14619,7 @@ var remoteEndpoint = (remote) => {
|
|
|
13746
14619
|
var configuredSshEndpoint = (host) => {
|
|
13747
14620
|
if (host.startsWith("-"))
|
|
13748
14621
|
return null;
|
|
13749
|
-
const resolved =
|
|
14622
|
+
const resolved = spawnSync7("ssh", ["-G", host], {
|
|
13750
14623
|
encoding: "utf8",
|
|
13751
14624
|
timeout: 5000,
|
|
13752
14625
|
maxBuffer: 1024 * 1024,
|
|
@@ -13796,7 +14669,7 @@ var readVcsConfig = () => {
|
|
|
13796
14669
|
const file = vcsConfigPath();
|
|
13797
14670
|
let raw;
|
|
13798
14671
|
try {
|
|
13799
|
-
raw =
|
|
14672
|
+
raw = fs12.readFileSync(file, "utf8");
|
|
13800
14673
|
} catch {
|
|
13801
14674
|
return { status: "missing", path: file, config: {} };
|
|
13802
14675
|
}
|
|
@@ -13862,7 +14735,7 @@ function vcsConfig(mode, cwd) {
|
|
|
13862
14735
|
const prov = cfg[provider] ?? {};
|
|
13863
14736
|
const out = {
|
|
13864
14737
|
ok: true,
|
|
13865
|
-
configPath:
|
|
14738
|
+
configPath: path14.resolve(cfgPath),
|
|
13866
14739
|
provider,
|
|
13867
14740
|
defaultTargetBranch: defaultTarget,
|
|
13868
14741
|
pr: cfg.pr ?? {},
|
|
@@ -13900,7 +14773,7 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
13900
14773
|
`).filter((l) => l.startsWith("## ")).map((l) => l.trim()),
|
|
13901
14774
|
descriptionPreview: desc.slice(0, 600)
|
|
13902
14775
|
});
|
|
13903
|
-
const remoteResult =
|
|
14776
|
+
const remoteResult = spawnSync7("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
13904
14777
|
cwd,
|
|
13905
14778
|
encoding: "utf8"
|
|
13906
14779
|
});
|
|
@@ -13916,7 +14789,7 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
13916
14789
|
if (!hostingApiHostMatches(remote, host))
|
|
13917
14790
|
return { ok: false, error: "PR style target does not match the provider host" };
|
|
13918
14791
|
if (provider === "gitlab") {
|
|
13919
|
-
const run = (args) =>
|
|
14792
|
+
const run = (args) => spawnSync7("glab", ["api", ...args], {
|
|
13920
14793
|
cwd,
|
|
13921
14794
|
encoding: "utf8",
|
|
13922
14795
|
env
|
|
@@ -13936,7 +14809,7 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
13936
14809
|
});
|
|
13937
14810
|
}
|
|
13938
14811
|
} else if (provider === "github") {
|
|
13939
|
-
const r =
|
|
14812
|
+
const r = spawnSync7("gh", [
|
|
13940
14813
|
"pr",
|
|
13941
14814
|
"list",
|
|
13942
14815
|
"--repo",
|
|
@@ -14006,356 +14879,85 @@ var validateBranchNamePolicy = (name, policy, source) => {
|
|
|
14006
14879
|
}
|
|
14007
14880
|
return { ok: true };
|
|
14008
14881
|
};
|
|
14009
|
-
var branchPolicySnapshotFor = (workspaceRoot) => {
|
|
14010
|
-
const resolved = effectiveWorkspacePolicy(workspaceRoot, "branch");
|
|
14011
|
-
const branchPolicy = resolved.branchPolicy;
|
|
14012
|
-
const source = resolved.provenance.branchPolicy;
|
|
14013
|
-
const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
|
|
14014
|
-
const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
|
|
14015
|
-
const fingerprint =
|
|
14016
|
-
return {
|
|
14017
|
-
branchPolicy,
|
|
14018
|
-
namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
|
|
14019
|
-
source,
|
|
14020
|
-
fingerprint
|
|
14021
|
-
};
|
|
14022
|
-
};
|
|
14023
|
-
var validateBranchNameFor = (workspaceRoot, name) => {
|
|
14024
|
-
try {
|
|
14025
|
-
const resolved = branchPolicySnapshotFor(workspaceRoot);
|
|
14026
|
-
return validateBranchNamePolicy(name, resolved.namePolicy, resolved.source);
|
|
14027
|
-
} catch (error) {
|
|
14028
|
-
const correction = "repair or select a valid workspace policy before creating a branch";
|
|
14029
|
-
return {
|
|
14030
|
-
ok: false,
|
|
14031
|
-
rule: "policy_unavailable",
|
|
14032
|
-
source: "workspaces.json",
|
|
14033
|
-
attempted: name,
|
|
14034
|
-
correction,
|
|
14035
|
-
error: `policy_unavailable: cannot validate branch ${JSON.stringify(name)} because ${error instanceof Error ? error.message : String(error)}; ${correction}`
|
|
14036
|
-
};
|
|
14037
|
-
}
|
|
14038
|
-
};
|
|
14039
|
-
var resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy;
|
|
14040
|
-
|
|
14041
|
-
// packages/workit-core/src/core/route-intent.ts
|
|
14042
|
-
var shellBranchTarget = (command) => {
|
|
14043
|
-
if (!command.trim() || /[\r\n;&|<>`'"$()\\*?{}~!#]/u.test(command) || command.includes("[") || command.includes("]"))
|
|
14044
|
-
return null;
|
|
14045
|
-
const args = command.trim().split(/\s+/u);
|
|
14046
|
-
if (args[0] !== "git")
|
|
14047
|
-
return null;
|
|
14048
|
-
const option = args[2];
|
|
14049
|
-
const target = args[1] === "switch" && ["-c", "--create", "-C", "--force-create"].includes(option ?? "") && (args.length === 4 || args.length === 5) ? args[3] : args[1] === "checkout" && ["-b", "-B"].includes(option ?? "") && (args.length === 4 || args.length === 5) ? args[3] : args[1] === "branch" && (args.length === 3 || args.length === 4) ? args[2] : null;
|
|
14050
|
-
return target && !target.startsWith("-") ? target : null;
|
|
14051
|
-
};
|
|
14052
|
-
var shellBranchPolicyViolation = (root, command) => {
|
|
14053
|
-
const target = shellBranchTarget(command);
|
|
14054
|
-
return target === null ? null : validateBranchNameFor(root, target);
|
|
14055
|
-
};
|
|
14056
|
-
// packages/workit-core/src/core/task-engine.ts
|
|
14057
|
-
import { realpathSync as realpathSync4 } from "node:fs";
|
|
14058
|
-
|
|
14059
|
-
// packages/workit-core/src/core/policy-resolver.ts
|
|
14060
|
-
var resolverInputSchema = object({
|
|
14061
|
-
intent: intentSchema,
|
|
14062
|
-
assessment: assessmentSchema,
|
|
14063
|
-
constraints: array(constraintSchema),
|
|
14064
|
-
prior: object({
|
|
14065
|
-
decisions: array(decisionSchema),
|
|
14066
|
-
findings: array(findingSchema),
|
|
14067
|
-
requirements: array(requirementSchema)
|
|
14068
|
-
}).strict(),
|
|
14069
|
-
preferences: object({ fast: boolean2().optional(), thorough: boolean2().optional() }).strict().optional()
|
|
14070
|
-
}).strict();
|
|
14071
|
-
var compareCodeUnits3 = (left, right) => {
|
|
14072
|
-
const length = Math.min(left.length, right.length);
|
|
14073
|
-
for (let index = 0;index < length; index += 1) {
|
|
14074
|
-
const difference = left.charCodeAt(index) - right.charCodeAt(index);
|
|
14075
|
-
if (difference !== 0)
|
|
14076
|
-
return difference;
|
|
14077
|
-
}
|
|
14078
|
-
return left.length - right.length;
|
|
14079
|
-
};
|
|
14080
|
-
var stableUnique = (values) => [...new Set(values)].toSorted(compareCodeUnits3);
|
|
14081
|
-
var stableList = (values) => [...values].toSorted((left, right) => compareCodeUnits3(canonicalJson(left), canonicalJson(right)));
|
|
14082
|
-
var normalizedScope = (scope) => ({
|
|
14083
|
-
description: scope.description,
|
|
14084
|
-
paths: stableUnique(scope.paths),
|
|
14085
|
-
exclusions: stableUnique(scope.exclusions)
|
|
14086
|
-
});
|
|
14087
|
-
var normalizedRefList = (values) => stableList(values);
|
|
14088
|
-
var normalizeAssessment = (assessment) => ({
|
|
14089
|
-
facts: stableList(assessment.facts.map((fact) => ({ ...fact, refs: normalizedRefList(fact.refs) }))),
|
|
14090
|
-
signals: Object.fromEntries(Object.entries(assessment.signals).map(([name, signal]) => [
|
|
14091
|
-
name,
|
|
14092
|
-
{ ...signal, refs: normalizedRefList(signal.refs) }
|
|
14093
|
-
])),
|
|
14094
|
-
consequences: stableList(assessment.consequences.map((consequence) => ({
|
|
14095
|
-
...consequence,
|
|
14096
|
-
fact: { ...consequence.fact, refs: normalizedRefList(consequence.fact.refs) }
|
|
14097
|
-
}))),
|
|
14098
|
-
verification: stableList(assessment.verification.map((verification) => ({
|
|
14099
|
-
...verification,
|
|
14100
|
-
scope: normalizedScope(verification.scope),
|
|
14101
|
-
availableChecks: normalizedRefList(verification.availableChecks),
|
|
14102
|
-
gaps: stableUnique(verification.gaps)
|
|
14103
|
-
})))
|
|
14104
|
-
});
|
|
14105
|
-
var normalizeConstraint = (constraint) => ({
|
|
14106
|
-
...constraint,
|
|
14107
|
-
source: constraint.source,
|
|
14108
|
-
requires: stableList(constraint.requires.map((requirement) => ({
|
|
14109
|
-
...requirement,
|
|
14110
|
-
scope: normalizedScope(requirement.scope),
|
|
14111
|
-
refs: normalizedRefList(requirement.refs)
|
|
14112
|
-
})))
|
|
14113
|
-
});
|
|
14114
|
-
var normalizeDecision = (decision) => ({
|
|
14115
|
-
...decision,
|
|
14116
|
-
binding: {
|
|
14117
|
-
...decision.binding,
|
|
14118
|
-
scope: normalizedScope(decision.binding.scope),
|
|
14119
|
-
contentRefs: normalizedRefList(decision.binding.contentRefs)
|
|
14120
|
-
},
|
|
14121
|
-
requirementIds: stableUnique(decision.requirementIds)
|
|
14122
|
-
});
|
|
14123
|
-
var normalizeIntent = (intent) => ({
|
|
14124
|
-
...intent,
|
|
14125
|
-
scope: normalizedScope(intent.scope),
|
|
14126
|
-
authorityRefs: normalizedRefList(intent.authorityRefs)
|
|
14127
|
-
});
|
|
14128
|
-
var normalizePrior = (prior) => ({
|
|
14129
|
-
decisions: stableList(prior.decisions.map(normalizeDecision)),
|
|
14130
|
-
findings: stableList(prior.findings.map((finding) => ({
|
|
14131
|
-
...finding,
|
|
14132
|
-
scope: normalizedScope(finding.scope),
|
|
14133
|
-
refs: normalizedRefList(finding.refs),
|
|
14134
|
-
resolution: finding.resolution ? {
|
|
14135
|
-
...finding.resolution,
|
|
14136
|
-
evidenceIds: stableUnique(finding.resolution.evidenceIds),
|
|
14137
|
-
decisionIds: stableUnique(finding.resolution.decisionIds)
|
|
14138
|
-
} : null
|
|
14139
|
-
}))),
|
|
14140
|
-
requirements: stableList(prior.requirements.map((requirement) => ({
|
|
14141
|
-
...requirement,
|
|
14142
|
-
scope: normalizedScope(requirement.scope)
|
|
14143
|
-
})))
|
|
14144
|
-
});
|
|
14145
|
-
var invalidFields = (error) => ({
|
|
14146
|
-
fields: error.issues.map((issue) => ({
|
|
14147
|
-
path: issue.code === "unrecognized_keys" ? issue.keys.join(".") : issue.path.join("."),
|
|
14148
|
-
reason: issue.message
|
|
14149
|
-
}))
|
|
14150
|
-
});
|
|
14151
|
-
function normalizeResolverInput(input) {
|
|
14152
|
-
const parsed = resolverInputSchema.safeParse(input);
|
|
14153
|
-
if (!parsed.success)
|
|
14154
|
-
return failure2("invalid_input", "resolver input is invalid", invalidFields(parsed.error));
|
|
14155
|
-
const value = parsed.data;
|
|
14156
|
-
const signals = value.assessment.signals;
|
|
14157
|
-
if (signals.behaviorChange.value === true && signals.mechanicalLowRisk.value === true)
|
|
14158
|
-
return failure2("invalid_input", "behavioral change cannot be wholly mechanical", {
|
|
14159
|
-
fields: [
|
|
14160
|
-
{
|
|
14161
|
-
path: "assessment.signals",
|
|
14162
|
-
reason: "contradictory behaviorChange and mechanicalLowRisk"
|
|
14163
|
-
}
|
|
14164
|
-
]
|
|
14165
|
-
});
|
|
14166
|
-
const ids = value.constraints.map((constraint) => constraint.id);
|
|
14167
|
-
if (new Set(ids).size !== ids.length)
|
|
14168
|
-
return failure2("invalid_input", "constraint IDs must be unique", {
|
|
14169
|
-
fields: [{ path: "constraints", reason: "duplicate constraint id" }]
|
|
14170
|
-
});
|
|
14171
|
-
if (value.preferences?.fast && value.preferences.thorough)
|
|
14172
|
-
return failure2("invalid_input", "fast and thorough preferences contradict", {
|
|
14173
|
-
fields: [{ path: "preferences", reason: "fast and thorough cannot both be enabled" }]
|
|
14174
|
-
});
|
|
14175
|
-
const normalized = {
|
|
14176
|
-
intent: normalizeIntent(value.intent),
|
|
14177
|
-
assessment: normalizeAssessment(value.assessment),
|
|
14178
|
-
constraints: stableList(value.constraints.map(normalizeConstraint)),
|
|
14179
|
-
prior: normalizePrior(value.prior),
|
|
14180
|
-
...value.preferences ? { preferences: { ...value.preferences } } : {},
|
|
14181
|
-
policyVersion: POLICY_VERSION
|
|
14182
|
-
};
|
|
14183
|
-
return success(null, null, normalized);
|
|
14184
|
-
}
|
|
14185
|
-
var requirement = (spec) => ({
|
|
14186
|
-
...spec,
|
|
14187
|
-
id: requirementId({
|
|
14188
|
-
ruleId: spec.ruleId,
|
|
14189
|
-
scope: spec.scope,
|
|
14190
|
-
satisfaction: spec.satisfaction,
|
|
14191
|
-
before: spec.before,
|
|
14192
|
-
dependentAction: spec.dependentAction
|
|
14193
|
-
})
|
|
14194
|
-
});
|
|
14195
|
-
var signalReason = (input, name) => input.assessment.signals[name].reason;
|
|
14196
|
-
function resolveRequirements(input) {
|
|
14197
|
-
const { signals } = input.assessment;
|
|
14198
|
-
const scope = input.intent.scope;
|
|
14199
|
-
const requirements = [];
|
|
14200
|
-
const hasUnknownSignal = Object.values(signals).some((signal) => signal.value === "unknown");
|
|
14201
|
-
const hasUnknownFact = [
|
|
14202
|
-
...input.assessment.facts,
|
|
14203
|
-
...input.assessment.consequences.map(({ fact }) => fact)
|
|
14204
|
-
].some((fact) => fact.basis === "unknown");
|
|
14205
|
-
if (hasUnknownSignal || hasUnknownFact)
|
|
14206
|
-
requirements.push(requirement({
|
|
14207
|
-
ruleId: "consequential-unknown",
|
|
14208
|
-
dimension: "investigation",
|
|
14209
|
-
scope,
|
|
14210
|
-
reason: `Resolve consequential uncertainty before the dependent action: ${[
|
|
14211
|
-
...Object.entries(signals).filter(([, signal]) => signal.value === "unknown").map(([name]) => name),
|
|
14212
|
-
...hasUnknownFact ? ["unknown fact"] : []
|
|
14213
|
-
].join(", ")}`,
|
|
14214
|
-
satisfaction: "An observed or inferred fact resolves the uncertainty and records its supporting references.",
|
|
14215
|
-
before: "dependent_action",
|
|
14216
|
-
dependentAction: null,
|
|
14217
|
-
acceptanceAllowed: false
|
|
14218
|
-
}));
|
|
14219
|
-
if (signals.productChoiceOpen.value === true)
|
|
14220
|
-
requirements.push(requirement({
|
|
14221
|
-
ruleId: "product-decision",
|
|
14222
|
-
dimension: "decisions",
|
|
14223
|
-
scope,
|
|
14224
|
-
reason: `A consequential product choice remains open: ${signalReason(input, "productChoiceOpen")}`,
|
|
14225
|
-
satisfaction: "Present the material alternatives and record the user's decision before the dependent action.",
|
|
14226
|
-
before: "dependent_action",
|
|
14227
|
-
dependentAction: null,
|
|
14228
|
-
acceptanceAllowed: false
|
|
14229
|
-
}));
|
|
14230
|
-
const independentBehaviorReview = input.preferences?.thorough === true || input.assessment.consequences.some((consequence) => ["security", "data", "public_contract", "operations"].includes(consequence.area));
|
|
14231
|
-
if (signals.behaviorChange.value === true)
|
|
14232
|
-
requirements.push(requirement({
|
|
14233
|
-
ruleId: "behavioral-verification",
|
|
14234
|
-
dimension: "testing",
|
|
14235
|
-
scope,
|
|
14236
|
-
reason: `Behavior, side effects, permissions, or data handling may change: ${signalReason(input, "behaviorChange")}`,
|
|
14237
|
-
satisfaction: "A useful behavioral check passes against the affected behavior and current candidate.",
|
|
14238
|
-
before: "close",
|
|
14239
|
-
dependentAction: null,
|
|
14240
|
-
acceptanceAllowed: false
|
|
14241
|
-
}), requirement({
|
|
14242
|
-
ruleId: independentBehaviorReview ? "fresh-context-review" : "self-review",
|
|
14243
|
-
dimension: "review",
|
|
14244
|
-
scope,
|
|
14245
|
-
reason: independentBehaviorReview ? "Consequential behavior or the thorough preference requires fresh-context review." : "Bounded behavior changes use self-review unless consequential risks or trusted rules require more.",
|
|
14246
|
-
satisfaction: independentBehaviorReview ? "A separate review context examines the current candidate and records its result." : "The lead reviews the resulting diff and records the relevant scope and checks.",
|
|
14247
|
-
before: "close",
|
|
14248
|
-
dependentAction: null,
|
|
14249
|
-
acceptanceAllowed: !independentBehaviorReview
|
|
14250
|
-
}));
|
|
14251
|
-
else if (signals.mechanicalLowRisk.value === true)
|
|
14252
|
-
requirements.push(requirement({
|
|
14253
|
-
ruleId: "mechanical-existing-checks",
|
|
14254
|
-
dimension: "verification",
|
|
14255
|
-
scope,
|
|
14256
|
-
reason: `The change is demonstrably mechanical and low-risk: ${signalReason(input, "mechanicalLowRisk")}`,
|
|
14257
|
-
satisfaction: "Run the relevant existing checks for affected consumers; do not add tautological tests.",
|
|
14258
|
-
before: "close",
|
|
14259
|
-
dependentAction: null,
|
|
14260
|
-
acceptanceAllowed: true
|
|
14261
|
-
}), requirement({
|
|
14262
|
-
ruleId: input.preferences?.thorough ? "fresh-context-review" : "self-review",
|
|
14263
|
-
dimension: "review",
|
|
14264
|
-
scope,
|
|
14265
|
-
reason: input.preferences?.thorough ? "The thorough preference upgrades mechanical self-review to fresh-context review." : "Mechanical low-risk work uses self-review rather than mandatory fresh-context review.",
|
|
14266
|
-
satisfaction: input.preferences?.thorough ? "A separate review context examines the current candidate and records its result." : "The lead reviews the resulting diff and records the relevant scope and checks.",
|
|
14267
|
-
before: "close",
|
|
14268
|
-
dependentAction: null,
|
|
14269
|
-
acceptanceAllowed: !input.preferences?.thorough
|
|
14270
|
-
}));
|
|
14271
|
-
if (signals.behaviorChange.value === true || signals.mechanicalLowRisk.value === true)
|
|
14272
|
-
requirements.push(requirement({
|
|
14273
|
-
ruleId: "pre-pr-cleanup",
|
|
14274
|
-
dimension: "verification",
|
|
14275
|
-
scope,
|
|
14276
|
-
reason: "Implementation output must be deslopped before it is delivered.",
|
|
14277
|
-
satisfaction: "Run the workit-deslop pass and record passing check evidence, or record an approved limitation decision waiving cleanup.",
|
|
14278
|
-
before: "dependent_action",
|
|
14279
|
-
dependentAction: "hosting.pull_request",
|
|
14280
|
-
acceptanceAllowed: true
|
|
14281
|
-
}));
|
|
14282
|
-
if (signals.durableAgreementNeeded.value === true)
|
|
14283
|
-
requirements.push(requirement({
|
|
14284
|
-
ruleId: "durable-spec",
|
|
14285
|
-
dimension: "artifacts",
|
|
14286
|
-
scope,
|
|
14287
|
-
reason: `The work needs a durable agreement about intended behavior: ${signalReason(input, "durableAgreementNeeded")}`,
|
|
14288
|
-
satisfaction: "Record the agreed behavior in a durable specification before implementation writes.",
|
|
14289
|
-
before: "write",
|
|
14290
|
-
dependentAction: null,
|
|
14291
|
-
acceptanceAllowed: true
|
|
14292
|
-
}));
|
|
14293
|
-
if (signals.coordinationPlanNeeded.value === true)
|
|
14294
|
-
requirements.push(requirement({
|
|
14295
|
-
ruleId: "coordination-plan",
|
|
14296
|
-
dimension: "continuity",
|
|
14297
|
-
scope,
|
|
14298
|
-
reason: `Dependencies, sequencing, or resumption need durable coordination: ${signalReason(input, "coordinationPlanNeeded")}`,
|
|
14299
|
-
satisfaction: "Record useful sequencing, dependencies, and the next action in a compact plan.",
|
|
14300
|
-
before: "dependent_action",
|
|
14301
|
-
dependentAction: null,
|
|
14302
|
-
acceptanceAllowed: true
|
|
14303
|
-
}));
|
|
14304
|
-
if (signals.helperUseful.value === true && !input.preferences?.fast)
|
|
14305
|
-
requirements.push(requirement({
|
|
14306
|
-
ruleId: "helper-usefulness",
|
|
14307
|
-
dimension: "delegation",
|
|
14308
|
-
scope,
|
|
14309
|
-
reason: `Independent judgment or investigation justifies a scoped helper: ${signalReason(input, "helperUseful")}`,
|
|
14310
|
-
satisfaction: "Use a bounded helper only for the independent objective and reconcile its result.",
|
|
14311
|
-
before: "dependent_action",
|
|
14312
|
-
dependentAction: null,
|
|
14313
|
-
acceptanceAllowed: true
|
|
14314
|
-
}));
|
|
14315
|
-
for (const constraint of input.constraints)
|
|
14316
|
-
for (const obligation of constraint.requires) {
|
|
14317
|
-
const dimension = obligation.method ?? (obligation.kind === "check" ? "verification" : obligation.kind === "decision" ? "decisions" : "challenge");
|
|
14318
|
-
requirements.push(requirement({
|
|
14319
|
-
ruleId: `project-constraint:${constraint.id}:${obligation.kind}:${sha256({
|
|
14320
|
-
kind: obligation.kind,
|
|
14321
|
-
scope: normalizedScope(obligation.scope),
|
|
14322
|
-
refs: normalizedRefList(obligation.refs),
|
|
14323
|
-
method: obligation.method,
|
|
14324
|
-
before: obligation.before,
|
|
14325
|
-
dependentAction: obligation.dependentAction
|
|
14326
|
-
})}`,
|
|
14327
|
-
dimension,
|
|
14328
|
-
scope: obligation.scope,
|
|
14329
|
-
reason: `${constraint.kind} constraint requires ${obligation.kind}: ${constraint.statement}`,
|
|
14330
|
-
satisfaction: `Satisfy the constraint obligation using its referenced ${obligation.kind}.`,
|
|
14331
|
-
before: obligation.before,
|
|
14332
|
-
dependentAction: obligation.dependentAction,
|
|
14333
|
-
acceptanceAllowed: constraint.acceptanceAllowed && obligation.kind !== "decision"
|
|
14334
|
-
}));
|
|
14335
|
-
}
|
|
14336
|
-
const seen = new Set;
|
|
14337
|
-
return requirements.filter((item) => {
|
|
14338
|
-
if (seen.has(item.id))
|
|
14339
|
-
return false;
|
|
14340
|
-
seen.add(item.id);
|
|
14341
|
-
return true;
|
|
14342
|
-
});
|
|
14343
|
-
}
|
|
14882
|
+
var branchPolicySnapshotFor = (workspaceRoot) => {
|
|
14883
|
+
const resolved = effectiveWorkspacePolicy(workspaceRoot, "branch");
|
|
14884
|
+
const branchPolicy = resolved.branchPolicy;
|
|
14885
|
+
const source = resolved.provenance.branchPolicy;
|
|
14886
|
+
const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
|
|
14887
|
+
const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
|
|
14888
|
+
const fingerprint = createHash9("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
|
|
14889
|
+
return {
|
|
14890
|
+
branchPolicy,
|
|
14891
|
+
namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
|
|
14892
|
+
source,
|
|
14893
|
+
fingerprint
|
|
14894
|
+
};
|
|
14895
|
+
};
|
|
14896
|
+
var validateBranchNameFor = (workspaceRoot, name) => {
|
|
14897
|
+
try {
|
|
14898
|
+
const resolved = branchPolicySnapshotFor(workspaceRoot);
|
|
14899
|
+
return validateBranchNamePolicy(name, resolved.namePolicy, resolved.source);
|
|
14900
|
+
} catch (error) {
|
|
14901
|
+
const correction = "repair or select a valid workspace policy before creating a branch";
|
|
14902
|
+
return {
|
|
14903
|
+
ok: false,
|
|
14904
|
+
rule: "policy_unavailable",
|
|
14905
|
+
source: "workspaces.json",
|
|
14906
|
+
attempted: name,
|
|
14907
|
+
correction,
|
|
14908
|
+
error: `policy_unavailable: cannot validate branch ${JSON.stringify(name)} because ${error instanceof Error ? error.message : String(error)}; ${correction}`
|
|
14909
|
+
};
|
|
14910
|
+
}
|
|
14911
|
+
};
|
|
14912
|
+
var resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy;
|
|
14913
|
+
|
|
14914
|
+
// packages/workit-core/src/core/route-intent.ts
|
|
14915
|
+
var shellBranchTarget = (command) => {
|
|
14916
|
+
if (!command.trim() || /[\r\n;&|<>`'"$()\\*?{}~!#]/u.test(command) || command.includes("[") || command.includes("]"))
|
|
14917
|
+
return null;
|
|
14918
|
+
const args = command.trim().split(/\s+/u);
|
|
14919
|
+
if (args[0] !== "git")
|
|
14920
|
+
return null;
|
|
14921
|
+
const option = args[2];
|
|
14922
|
+
const target = args[1] === "switch" && ["-c", "--create", "-C", "--force-create"].includes(option ?? "") && (args.length === 4 || args.length === 5) ? args[3] : args[1] === "checkout" && ["-b", "-B"].includes(option ?? "") && (args.length === 4 || args.length === 5) ? args[3] : args[1] === "branch" && (args.length === 3 || args.length === 4) ? args[2] : null;
|
|
14923
|
+
return target && !target.startsWith("-") ? target : null;
|
|
14924
|
+
};
|
|
14925
|
+
var shellBranchPolicyViolation = (root, command) => {
|
|
14926
|
+
const target = shellBranchTarget(command);
|
|
14927
|
+
return target === null ? null : validateBranchNameFor(root, target);
|
|
14928
|
+
};
|
|
14929
|
+
// packages/workit-core/src/core/task-engine.ts
|
|
14930
|
+
import { realpathSync as realpathSync4 } from "node:fs";
|
|
14931
|
+
|
|
14932
|
+
// packages/workit-core/src/core/policy-resolver.ts
|
|
14344
14933
|
function resolvePolicy(input) {
|
|
14345
|
-
const
|
|
14346
|
-
if (
|
|
14347
|
-
return
|
|
14348
|
-
|
|
14934
|
+
const ids = input.constraints.map((constraint) => constraint.id);
|
|
14935
|
+
if (new Set(ids).size !== ids.length)
|
|
14936
|
+
return failure2("invalid_input", "constraint IDs must be unique", {
|
|
14937
|
+
fields: [{ path: "constraints", reason: "duplicate constraint id" }]
|
|
14938
|
+
});
|
|
14349
14939
|
const policy = {
|
|
14350
14940
|
policyVersion: POLICY_VERSION,
|
|
14351
|
-
inputDigest: sha256(canonicalJson(
|
|
14352
|
-
|
|
14941
|
+
inputDigest: sha256(canonicalJson({
|
|
14942
|
+
judgment: input.judgment,
|
|
14943
|
+
scope: input.intent.scope,
|
|
14944
|
+
constraints: input.constraints,
|
|
14945
|
+
verification: input.verification ?? "self",
|
|
14946
|
+
policyVersion: POLICY_VERSION
|
|
14947
|
+
})),
|
|
14948
|
+
requirements: deriveRequirements(input.judgment, input.intent.scope, input.constraints, input.verification)
|
|
14353
14949
|
};
|
|
14354
14950
|
const parsed = policySchema.safeParse(policy);
|
|
14355
14951
|
if (!parsed.success)
|
|
14356
|
-
return failure2("invalid_input", "resolved policy is invalid",
|
|
14952
|
+
return failure2("invalid_input", "resolved policy is invalid", {
|
|
14953
|
+
fields: parsed.error.issues.map((issue) => ({
|
|
14954
|
+
path: issue.path.join("."),
|
|
14955
|
+
reason: issue.message
|
|
14956
|
+
}))
|
|
14957
|
+
});
|
|
14357
14958
|
return success(null, null, parsed.data);
|
|
14358
14959
|
}
|
|
14960
|
+
var compareCodeUnits3 = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
14359
14961
|
function diffPolicy(previous, next, reason, now) {
|
|
14360
14962
|
if (!policySchema.safeParse(previous).success && previous !== null)
|
|
14361
14963
|
throw new TypeError("previous policy is invalid");
|
|
@@ -14383,7 +14985,247 @@ function diffPolicy(previous, next, reason, now) {
|
|
|
14383
14985
|
return parsed.data;
|
|
14384
14986
|
}
|
|
14385
14987
|
|
|
14988
|
+
// packages/workit-core/src/core/policy/judgment.ts
|
|
14989
|
+
var isObject6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14990
|
+
var squash = (key) => key.toLowerCase().replace(/[_-]/g, "");
|
|
14991
|
+
var ALIASES = {
|
|
14992
|
+
riskTier: ["risktier", "risk", "risklevel", "tier"],
|
|
14993
|
+
behaviorChange: ["behaviorchange", "behavior", "behaviour", "behaviourchange"],
|
|
14994
|
+
productChoiceOpen: ["productchoiceopen", "openproductchoice", "productchoice", "openchoice"],
|
|
14995
|
+
needsPlan: ["needsplan", "plan", "needsspec", "spec"],
|
|
14996
|
+
note: ["note", "notes", "reason", "why"],
|
|
14997
|
+
refs: ["refs", "ref", "references"]
|
|
14998
|
+
};
|
|
14999
|
+
var LEGACY_KEYS = new Set(["assessment", "facts", "signals", "consequences", "verification"]);
|
|
15000
|
+
var RISK = {
|
|
15001
|
+
trivial: "trivial",
|
|
15002
|
+
low: "trivial",
|
|
15003
|
+
minor: "trivial",
|
|
15004
|
+
mechanical: "trivial",
|
|
15005
|
+
none: "trivial",
|
|
15006
|
+
normal: "normal",
|
|
15007
|
+
medium: "normal",
|
|
15008
|
+
med: "normal",
|
|
15009
|
+
moderate: "normal",
|
|
15010
|
+
high: "high",
|
|
15011
|
+
critical: "high",
|
|
15012
|
+
major: "high"
|
|
15013
|
+
};
|
|
15014
|
+
var YES = new Set(["true", "yes", "y", "1", "on"]);
|
|
15015
|
+
var NO = new Set(["false", "no", "n", "0", "off", "none"]);
|
|
15016
|
+
var flag = (value, unknownAs) => {
|
|
15017
|
+
if (typeof value === "boolean")
|
|
15018
|
+
return { ok: true, value };
|
|
15019
|
+
if (typeof value === "number" && (value === 0 || value === 1))
|
|
15020
|
+
return { ok: true, value: value === 1 };
|
|
15021
|
+
if (typeof value !== "string")
|
|
15022
|
+
return { ok: false };
|
|
15023
|
+
const text = value.trim().toLowerCase();
|
|
15024
|
+
if (YES.has(text))
|
|
15025
|
+
return { ok: true, value: true };
|
|
15026
|
+
if (NO.has(text))
|
|
15027
|
+
return { ok: true, value: false };
|
|
15028
|
+
if (text === "unknown" || text === "maybe")
|
|
15029
|
+
return { ok: true, value: unknownAs };
|
|
15030
|
+
return { ok: false };
|
|
15031
|
+
};
|
|
15032
|
+
var fromLegacy = (raw, previous) => {
|
|
15033
|
+
const source = isObject6(raw.assessment) ? raw.assessment : raw;
|
|
15034
|
+
const signals = isObject6(source.signals) ? source.signals : {};
|
|
15035
|
+
const value = (name) => isObject6(signals[name]) ? signals[name].value : undefined;
|
|
15036
|
+
const known = (name, fallback) => typeof value(name) === "boolean" ? value(name) : fallback;
|
|
15037
|
+
const behaviorChange = known("behaviorChange", previous?.behaviorChange ?? false);
|
|
15038
|
+
const consequences = Array.isArray(source.consequences) ? source.consequences : [];
|
|
15039
|
+
const freshReview = value("behaviorChange") === true && consequences.some((item) => isObject6(item) && ["security", "data", "public_contract", "operations"].includes(String(item.area)));
|
|
15040
|
+
return {
|
|
15041
|
+
riskTier: freshReview ? "high" : value("mechanicalLowRisk") === true && !behaviorChange ? "trivial" : "normal",
|
|
15042
|
+
behaviorChange,
|
|
15043
|
+
productChoiceOpen: known("productChoiceOpen", previous?.productChoiceOpen ?? false),
|
|
15044
|
+
needsPlan: known("durableAgreementNeeded", previous?.needsPlan ?? false) || value("coordinationPlanNeeded") === true
|
|
15045
|
+
};
|
|
15046
|
+
};
|
|
15047
|
+
var DEFAULT = {
|
|
15048
|
+
riskTier: "normal",
|
|
15049
|
+
behaviorChange: false,
|
|
15050
|
+
productChoiceOpen: false,
|
|
15051
|
+
needsPlan: false,
|
|
15052
|
+
note: null,
|
|
15053
|
+
refs: []
|
|
15054
|
+
};
|
|
15055
|
+
function normalizeJudgment(raw, previous) {
|
|
15056
|
+
const input = isObject6(raw) ? raw : {};
|
|
15057
|
+
const legacy = Object.keys(input).some((key) => LEGACY_KEYS.has(key));
|
|
15058
|
+
const base = { ...DEFAULT, ...previous, refs: [...previous?.refs ?? []] };
|
|
15059
|
+
const judgment = legacy ? { ...base, ...fromLegacy(input, previous) } : { ...base };
|
|
15060
|
+
const ignored = [];
|
|
15061
|
+
const fields = [];
|
|
15062
|
+
for (const [key, value] of Object.entries(input)) {
|
|
15063
|
+
if (LEGACY_KEYS.has(key))
|
|
15064
|
+
continue;
|
|
15065
|
+
const name = Object.keys(ALIASES).find((field) => ALIASES[field].includes(squash(key)));
|
|
15066
|
+
if (!name) {
|
|
15067
|
+
ignored.push(key);
|
|
15068
|
+
continue;
|
|
15069
|
+
}
|
|
15070
|
+
if (name === "riskTier") {
|
|
15071
|
+
const tier = typeof value === "string" ? RISK[value.trim().toLowerCase()] : undefined;
|
|
15072
|
+
if (tier)
|
|
15073
|
+
judgment.riskTier = tier;
|
|
15074
|
+
else
|
|
15075
|
+
fields.push({ path: key, reason: "expected trivial|normal|high (or low|medium)" });
|
|
15076
|
+
} else if (name === "note") {
|
|
15077
|
+
judgment.note = typeof value === "string" && value.trim() ? value.trim() : null;
|
|
15078
|
+
} else if (name === "refs") {
|
|
15079
|
+
const items = Array.isArray(value) ? value : [value];
|
|
15080
|
+
judgment.refs.push(...items.filter((item) => item !== null && item !== "").map(refOf));
|
|
15081
|
+
} else {
|
|
15082
|
+
if (name === "needsPlan" && typeof value === "string" && !flag(value, false).ok && value.trim()) {
|
|
15083
|
+
judgment.needsPlan = true;
|
|
15084
|
+
judgment.refs.push(refOf(value.trim()));
|
|
15085
|
+
continue;
|
|
15086
|
+
}
|
|
15087
|
+
const parsed = flag(value, name === "behaviorChange");
|
|
15088
|
+
if (parsed.ok)
|
|
15089
|
+
judgment[name] = parsed.value;
|
|
15090
|
+
else
|
|
15091
|
+
fields.push({ path: key, reason: "expected true|false (yes|no accepted)" });
|
|
15092
|
+
}
|
|
15093
|
+
}
|
|
15094
|
+
if (fields.length)
|
|
15095
|
+
return failure2("invalid_input", "judgment values are invalid", { fields });
|
|
15096
|
+
const seen = new Set;
|
|
15097
|
+
judgment.refs = judgment.refs.filter((ref) => {
|
|
15098
|
+
const key = JSON.stringify(ref);
|
|
15099
|
+
return seen.has(key) ? false : (seen.add(key), true);
|
|
15100
|
+
});
|
|
15101
|
+
const parsed = judgmentSchema.safeParse(judgment);
|
|
15102
|
+
if (!parsed.success)
|
|
15103
|
+
return failure2("invalid_input", "judgment is invalid", {
|
|
15104
|
+
fields: parsed.error.issues.map((issue) => ({
|
|
15105
|
+
path: issue.path.join("."),
|
|
15106
|
+
reason: issue.message
|
|
15107
|
+
}))
|
|
15108
|
+
});
|
|
15109
|
+
return success(null, null, { judgment: parsed.data, ignored, legacy });
|
|
15110
|
+
}
|
|
15111
|
+
|
|
15112
|
+
// packages/workit-core/src/autonomy.ts
|
|
15113
|
+
import os3 from "node:os";
|
|
15114
|
+
import path15 from "node:path";
|
|
15115
|
+
|
|
15116
|
+
// packages/workit-core/src/core/safe-write.ts
|
|
15117
|
+
import {
|
|
15118
|
+
closeSync as closeSync2,
|
|
15119
|
+
fchmodSync,
|
|
15120
|
+
fsyncSync,
|
|
15121
|
+
lstatSync as lstatSync3,
|
|
15122
|
+
mkdirSync as mkdirSync3,
|
|
15123
|
+
openSync as openSync2,
|
|
15124
|
+
renameSync as renameSync2,
|
|
15125
|
+
unlinkSync,
|
|
15126
|
+
writeFileSync as writeFileSync3
|
|
15127
|
+
} from "node:fs";
|
|
15128
|
+
function writeFileExclusive(file, content, mode) {
|
|
15129
|
+
try {
|
|
15130
|
+
writeFileSync3(file, content, { encoding: "utf8", flag: "wx", mode });
|
|
15131
|
+
return "created";
|
|
15132
|
+
} catch (err) {
|
|
15133
|
+
if (err.code === "EEXIST")
|
|
15134
|
+
return "preserved";
|
|
15135
|
+
throw err;
|
|
15136
|
+
}
|
|
15137
|
+
}
|
|
15138
|
+
|
|
15139
|
+
// packages/workit-core/src/autonomy.ts
|
|
15140
|
+
var accountHome = () => {
|
|
15141
|
+
try {
|
|
15142
|
+
return os3.userInfo().homedir;
|
|
15143
|
+
} catch {
|
|
15144
|
+
return os3.homedir();
|
|
15145
|
+
}
|
|
15146
|
+
};
|
|
15147
|
+
var grantsHome = { resolve: accountHome };
|
|
15148
|
+
var grantsDir = () => path15.join(grantsHome.resolve(), ".config", "workit");
|
|
15149
|
+
var grantsOverride = () => {
|
|
15150
|
+
const configured = path15.resolve(resolveConfigDir());
|
|
15151
|
+
return configured === path15.resolve(grantsDir()) ? null : `the config directory is redirected to ${configured} (WORKFLOW_TOOLKIT_CONFIG, WORKFLOW_TOOLKIT_CONFIG_DIR or XDG_CONFIG_HOME); grants are read only from ${path15.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
|
|
15152
|
+
};
|
|
15153
|
+
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
15154
|
+
var DEFAULT_GRANTS = Object.freeze({
|
|
15155
|
+
push: true,
|
|
15156
|
+
pr: true,
|
|
15157
|
+
merge: false,
|
|
15158
|
+
release: false,
|
|
15159
|
+
rerun: true
|
|
15160
|
+
});
|
|
15161
|
+
var DEFAULT_ENDPOINT = "commit";
|
|
15162
|
+
var DEFAULT_VERIFICATION = "self";
|
|
15163
|
+
var verificationOf = (entry) => entry?.verification === "independent" ? "independent" : "self";
|
|
15164
|
+
var endpointOf = (entry) => entry?.defaultEndpoint === "pr" ? "pr" : "commit";
|
|
15165
|
+
var isGrantValue = (value) => value === true || value === false || value === "verified";
|
|
15166
|
+
var normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value;
|
|
15167
|
+
var fromAutoApprove = (value) => {
|
|
15168
|
+
if (value === true)
|
|
15169
|
+
return { push: true, pr: true, merge: "verified" };
|
|
15170
|
+
if (!Array.isArray(value))
|
|
15171
|
+
return null;
|
|
15172
|
+
const grants = {};
|
|
15173
|
+
for (const item of value)
|
|
15174
|
+
if (item === "push" || item === "pr")
|
|
15175
|
+
grants[item] = true;
|
|
15176
|
+
else if (item === "merge")
|
|
15177
|
+
grants.merge = "verified";
|
|
15178
|
+
return Object.keys(grants).length > 0 ? grants : null;
|
|
15179
|
+
};
|
|
15180
|
+
function configuredGrants(entry) {
|
|
15181
|
+
const record = entry ?? {};
|
|
15182
|
+
const raw = record.autonomy;
|
|
15183
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
15184
|
+
const grants = {};
|
|
15185
|
+
for (const kind of GRANT_KINDS) {
|
|
15186
|
+
const value = raw[kind];
|
|
15187
|
+
if (isGrantValue(value))
|
|
15188
|
+
grants[kind] = normalized(kind, value);
|
|
15189
|
+
}
|
|
15190
|
+
return { grants, source: "autonomy" };
|
|
15191
|
+
}
|
|
15192
|
+
const legacy = fromAutoApprove(record.autoApprove);
|
|
15193
|
+
return legacy ? { grants: legacy, source: "autoApprove" } : { grants: {}, source: "default" };
|
|
15194
|
+
}
|
|
15195
|
+
function resolveAutonomy(cwd) {
|
|
15196
|
+
const override = grantsOverride();
|
|
15197
|
+
if (override)
|
|
15198
|
+
return {
|
|
15199
|
+
workspace: null,
|
|
15200
|
+
grants: { ...DEFAULT_GRANTS },
|
|
15201
|
+
configured: [],
|
|
15202
|
+
source: "default",
|
|
15203
|
+
accountConfigured: false,
|
|
15204
|
+
defaultEndpoint: DEFAULT_ENDPOINT,
|
|
15205
|
+
verification: DEFAULT_VERIFICATION,
|
|
15206
|
+
note: override
|
|
15207
|
+
};
|
|
15208
|
+
const workspace = resolveWorkspaceFrom(cwd, grantsDir(), process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined);
|
|
15209
|
+
const { grants, source } = configuredGrants(workspace);
|
|
15210
|
+
return {
|
|
15211
|
+
workspace: workspace?.name ?? null,
|
|
15212
|
+
grants: { ...DEFAULT_GRANTS, ...grants },
|
|
15213
|
+
configured: GRANT_KINDS.filter((kind) => grants[kind] !== undefined),
|
|
15214
|
+
source,
|
|
15215
|
+
accountConfigured: Boolean(workspace?.vcs?.account),
|
|
15216
|
+
defaultEndpoint: endpointOf(workspace),
|
|
15217
|
+
verification: verificationOf(workspace)
|
|
15218
|
+
};
|
|
15219
|
+
}
|
|
15220
|
+
|
|
14386
15221
|
// packages/workit-core/src/core/task-engine.ts
|
|
15222
|
+
var workspaceVerification = (root) => {
|
|
15223
|
+
try {
|
|
15224
|
+
return resolveAutonomy(root).verification;
|
|
15225
|
+
} catch {
|
|
15226
|
+
return "self";
|
|
15227
|
+
}
|
|
15228
|
+
};
|
|
14387
15229
|
var provenance = (context, kind = context.provenanceKind ?? "host_observed") => ({
|
|
14388
15230
|
kind,
|
|
14389
15231
|
host: context.caller.host,
|
|
@@ -14576,22 +15418,7 @@ var mapRef = (ref, ids) => {
|
|
|
14576
15418
|
return ref;
|
|
14577
15419
|
return { ...ref, id: ids.get(ref.id) ?? ref.id };
|
|
14578
15420
|
};
|
|
14579
|
-
var portableRefs = (refs) => refs.filter((ref) => ref.kind === "file" || ref.kind === "record");
|
|
14580
15421
|
var portableRef = (ref) => ref && (ref.kind === "file" || ref.kind === "record") ? ref : null;
|
|
14581
|
-
var portableFact = (fact) => {
|
|
14582
|
-
const refs = portableRefs(fact.refs);
|
|
14583
|
-
return fact.basis === "observed" && refs.length === 0 ? { ...fact, basis: "unknown", refs } : { ...fact, refs };
|
|
14584
|
-
};
|
|
14585
|
-
var portableSignal = (signal) => {
|
|
14586
|
-
const refs = portableRefs(signal.refs);
|
|
14587
|
-
return signal.basis === "observed" && refs.length === 0 ? {
|
|
14588
|
-
...signal,
|
|
14589
|
-
value: "unknown",
|
|
14590
|
-
basis: "unknown",
|
|
14591
|
-
reason: "portable supporting reference was removed",
|
|
14592
|
-
refs
|
|
14593
|
-
} : { ...signal, refs };
|
|
14594
|
-
};
|
|
14595
15422
|
var importedProvenance = (context) => ({
|
|
14596
15423
|
kind: "imported",
|
|
14597
15424
|
host: context.caller.host,
|
|
@@ -14603,18 +15430,6 @@ var portableTask = (task) => {
|
|
|
14603
15430
|
if (typeof stripped !== "object" || stripped === null || Array.isArray(stripped))
|
|
14604
15431
|
throw new TypeError("portable task rewrite produced a non-record");
|
|
14605
15432
|
const clone = structuredClone(stripped);
|
|
14606
|
-
const demote = (facts, signals, consequences) => {
|
|
14607
|
-
for (const fact of facts)
|
|
14608
|
-
Object.assign(fact, portableFact(fact));
|
|
14609
|
-
for (const name of Object.keys(signals)) {
|
|
14610
|
-
const signal = signals[name];
|
|
14611
|
-
Object.assign(signal, portableSignal(signal));
|
|
14612
|
-
}
|
|
14613
|
-
for (const consequence of consequences)
|
|
14614
|
-
Object.assign(consequence.fact, portableFact(consequence.fact));
|
|
14615
|
-
};
|
|
14616
|
-
for (const entry of clone.assessments)
|
|
14617
|
-
demote(entry.data.facts, entry.data.signals, entry.data.consequences);
|
|
14618
15433
|
clone.candidates = [];
|
|
14619
15434
|
return clone;
|
|
14620
15435
|
};
|
|
@@ -14631,6 +15446,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
14631
15446
|
for (const entry of [
|
|
14632
15447
|
source.intent,
|
|
14633
15448
|
...source.assessments,
|
|
15449
|
+
...source.judgments ?? [],
|
|
14634
15450
|
...source.evidence,
|
|
14635
15451
|
...source.decisions,
|
|
14636
15452
|
...source.findings,
|
|
@@ -14654,9 +15470,16 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
14654
15470
|
id: fresh(entry.id),
|
|
14655
15471
|
recordedAt: timestamp,
|
|
14656
15472
|
provenance: imported,
|
|
14657
|
-
data: rewriteRecordRefs(data,
|
|
15473
|
+
data: rewriteRecordRefs(data, legacyAssessmentSchema, remap)
|
|
14658
15474
|
};
|
|
14659
15475
|
});
|
|
15476
|
+
const judgments = source.judgments?.map((entry) => ({
|
|
15477
|
+
...entry,
|
|
15478
|
+
id: fresh(entry.id),
|
|
15479
|
+
recordedAt: timestamp,
|
|
15480
|
+
provenance: imported,
|
|
15481
|
+
data: rewriteRecordRefs(entry.data, judgmentSchema, remap)
|
|
15482
|
+
}));
|
|
14660
15483
|
const evidence = source.evidence.map((entry) => ({
|
|
14661
15484
|
...entry,
|
|
14662
15485
|
id: fresh(entry.id),
|
|
@@ -14724,6 +15547,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
14724
15547
|
status: "paused",
|
|
14725
15548
|
closure: null,
|
|
14726
15549
|
assessments,
|
|
15550
|
+
...judgments ? { judgments } : {},
|
|
14727
15551
|
evidence,
|
|
14728
15552
|
decisions: decisions.map((entry) => {
|
|
14729
15553
|
const data = rewriteRecordRefs(entry.data, decisionSchema, remap);
|
|
@@ -14793,7 +15617,8 @@ class WorkitCore {
|
|
|
14793
15617
|
retryOmittedRevisions(request, run) {
|
|
14794
15618
|
return retryFilledRevisions(filledRevisions(request), run);
|
|
14795
15619
|
}
|
|
14796
|
-
implicitTask(family,
|
|
15620
|
+
implicitTask(family, raw) {
|
|
15621
|
+
const request = normalizeOperationInput(family, raw, this.context.caller.host);
|
|
14797
15622
|
if (typeof request !== "object" || request === null || Array.isArray(request))
|
|
14798
15623
|
return success(null, null, request);
|
|
14799
15624
|
const value = request;
|
|
@@ -15047,7 +15872,11 @@ class WorkitCore {
|
|
|
15047
15872
|
if ((this.context.workerId ?? null) !== null)
|
|
15048
15873
|
return failure2("permission_denied", "helpers cannot change task requirements");
|
|
15049
15874
|
this.fillRevisions(input, task.data);
|
|
15050
|
-
const
|
|
15875
|
+
const judged = normalizeJudgment(input.judgment, latestJudgment(task.data));
|
|
15876
|
+
if (!judged.ok)
|
|
15877
|
+
return judged;
|
|
15878
|
+
const judgment = judged.data.judgment;
|
|
15879
|
+
const resolved = this.resolve(task.data, judgment);
|
|
15051
15880
|
if (!resolved.ok)
|
|
15052
15881
|
return resolved;
|
|
15053
15882
|
if (input.action === "preview")
|
|
@@ -15058,16 +15887,17 @@ class WorkitCore {
|
|
|
15058
15887
|
if (current.status === "closed")
|
|
15059
15888
|
return failure2("invalid_transition", "closed task cannot be assessed");
|
|
15060
15889
|
const nextChange = diffPolicy(current.policy, resolved.data, "policy reassessed", mutation.now);
|
|
15061
|
-
const
|
|
15890
|
+
const entry = {
|
|
15062
15891
|
id: newId(),
|
|
15063
15892
|
recordedAt: mutation.now,
|
|
15064
15893
|
provenance: provenance(this.context),
|
|
15065
|
-
data:
|
|
15894
|
+
data: judgment
|
|
15066
15895
|
};
|
|
15067
15896
|
const next = {
|
|
15068
15897
|
...current,
|
|
15069
15898
|
constraints: this.context.constraints,
|
|
15070
|
-
|
|
15899
|
+
judgments: [...current.judgments ?? [], entry],
|
|
15900
|
+
critical: [...new Set([...current.critical ?? [], "judgments"])],
|
|
15071
15901
|
policy: resolved.data,
|
|
15072
15902
|
policyChanges: nextChange ? [...current.policyChanges, nextChange] : current.policyChanges
|
|
15073
15903
|
};
|
|
@@ -15269,9 +16099,14 @@ class WorkitCore {
|
|
|
15269
16099
|
return workspace;
|
|
15270
16100
|
if (!workspace.data)
|
|
15271
16101
|
return failure2("not_found", "workspace not found");
|
|
15272
|
-
|
|
16102
|
+
const binding = {
|
|
16103
|
+
...input.binding,
|
|
16104
|
+
taskId: input.binding.taskId ?? task.data.id,
|
|
16105
|
+
workspaceId: input.binding.workspaceId ?? workspace.data.id
|
|
16106
|
+
};
|
|
16107
|
+
if (binding.taskId !== task.data.id || binding.workspaceId !== workspace.data.id)
|
|
15273
16108
|
return failure2("invalid_input", "decision task or workspace binding is invalid");
|
|
15274
|
-
const content = verifyDecisionContentAtRoot(this.store.root,
|
|
16109
|
+
const content = verifyDecisionContentAtRoot(this.store.root, binding);
|
|
15275
16110
|
if (!content.ok)
|
|
15276
16111
|
return content;
|
|
15277
16112
|
const knownRequirements = new Set(task.data.policy?.requirements.map((item) => item.id) ?? []);
|
|
@@ -15279,7 +16114,7 @@ class WorkitCore {
|
|
|
15279
16114
|
return failure2("invalid_input", "decision references an unknown requirement");
|
|
15280
16115
|
const base = {
|
|
15281
16116
|
purpose: input.purpose,
|
|
15282
|
-
binding
|
|
16117
|
+
binding,
|
|
15283
16118
|
response: input.response,
|
|
15284
16119
|
requirementIds: input.requirementIds,
|
|
15285
16120
|
revoked: null
|
|
@@ -15638,16 +16473,12 @@ class WorkitCore {
|
|
|
15638
16473
|
now: trustedNow(this.context)
|
|
15639
16474
|
});
|
|
15640
16475
|
}
|
|
15641
|
-
resolve(task,
|
|
16476
|
+
resolve(task, judgment) {
|
|
15642
16477
|
return resolvePolicy({
|
|
15643
16478
|
intent: task.intent.data,
|
|
15644
|
-
|
|
16479
|
+
judgment,
|
|
15645
16480
|
constraints: this.context.constraints,
|
|
15646
|
-
|
|
15647
|
-
decisions: task.decisions.filter((entry) => entry.provenance.kind !== "imported").map((entry) => entry.data),
|
|
15648
|
-
findings: task.findings.filter((entry) => entry.provenance.kind !== "imported").map((entry) => entry.data),
|
|
15649
|
-
requirements: task.origin || task.policy === null ? [] : task.policy.requirements
|
|
15650
|
-
}
|
|
16481
|
+
verification: workspaceVerification(this.context.root)
|
|
15651
16482
|
});
|
|
15652
16483
|
}
|
|
15653
16484
|
summary(task) {
|
|
@@ -15973,9 +16804,9 @@ class WorkitCore {
|
|
|
15973
16804
|
}
|
|
15974
16805
|
}
|
|
15975
16806
|
// packages/workit-core/src/core/repo-context.ts
|
|
15976
|
-
import { spawnSync as
|
|
15977
|
-
import { existsSync as
|
|
15978
|
-
import
|
|
16807
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
16808
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
|
|
16809
|
+
import path16 from "node:path";
|
|
15979
16810
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
15980
16811
|
try {
|
|
15981
16812
|
gitRevisionParts(value);
|
|
@@ -16002,7 +16833,7 @@ var invalidRange = (cwd) => ({
|
|
|
16002
16833
|
cwd
|
|
16003
16834
|
});
|
|
16004
16835
|
var runGit = (cwd, args) => {
|
|
16005
|
-
const result =
|
|
16836
|
+
const result = spawnSync8("git", args, { cwd, encoding: "utf8" });
|
|
16006
16837
|
return {
|
|
16007
16838
|
stdout: (result.stdout ?? "").trimEnd(),
|
|
16008
16839
|
stderr: (result.stderr ?? "").trimEnd(),
|
|
@@ -16013,11 +16844,11 @@ var repoRoot = (cwd) => {
|
|
|
16013
16844
|
const r = runGit(cwd, ["rev-parse", "--show-toplevel"]);
|
|
16014
16845
|
return r.exitCode === 0 && r.stdout ? r.stdout : cwd;
|
|
16015
16846
|
};
|
|
16016
|
-
var
|
|
16847
|
+
var currentBranch2 = (cwd) => {
|
|
16017
16848
|
const r = runGit(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
16018
16849
|
return r.exitCode === 0 && r.stdout ? r.stdout : "unknown";
|
|
16019
16850
|
};
|
|
16020
|
-
var
|
|
16851
|
+
var defaultBase2 = (cwd) => {
|
|
16021
16852
|
for (const ref of [
|
|
16022
16853
|
"origin/main",
|
|
16023
16854
|
"main",
|
|
@@ -16031,11 +16862,11 @@ var defaultBase = (cwd) => {
|
|
|
16031
16862
|
}
|
|
16032
16863
|
return "HEAD~1";
|
|
16033
16864
|
};
|
|
16034
|
-
var rangeArgOrDefault = (arg, cwd) => arg ? arg : `${
|
|
16865
|
+
var rangeArgOrDefault = (arg, cwd) => arg ? arg : `${defaultBase2(cwd)}...HEAD`;
|
|
16035
16866
|
var isProtectedBranch = (branch) => ["main", "master", "develop", "prod", "production"].includes(branch);
|
|
16036
16867
|
var isPrBranch = (branch) => branch.startsWith("feature/") || branch.startsWith("bugfix/");
|
|
16037
16868
|
function resolvePrBranchContext(cwd) {
|
|
16038
|
-
const branch =
|
|
16869
|
+
const branch = currentBranch2(cwd);
|
|
16039
16870
|
if (isProtectedBranch(branch)) {
|
|
16040
16871
|
return {
|
|
16041
16872
|
ok: false,
|
|
@@ -16109,7 +16940,7 @@ var findPrTemplate = (cwd) => {
|
|
|
16109
16940
|
]) {
|
|
16110
16941
|
const parts = rel.split("/");
|
|
16111
16942
|
const base = parts[parts.length - 1];
|
|
16112
|
-
const dir = parts.length > 1 ?
|
|
16943
|
+
const dir = parts.length > 1 ? path16.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
16113
16944
|
const actual = probeCaseInsensitive(dir, base);
|
|
16114
16945
|
if (actual)
|
|
16115
16946
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -16140,8 +16971,8 @@ var diffStatForRange = (cwd, range) => {
|
|
|
16140
16971
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
16141
16972
|
};
|
|
16142
16973
|
var packageScripts = (cwd, keys) => {
|
|
16143
|
-
const pkgPath =
|
|
16144
|
-
if (!
|
|
16974
|
+
const pkgPath = path16.join(cwd, "package.json");
|
|
16975
|
+
if (!existsSync6(pkgPath))
|
|
16145
16976
|
return [];
|
|
16146
16977
|
try {
|
|
16147
16978
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
@@ -16166,9 +16997,9 @@ var documentationFiles = (cwd) => {
|
|
|
16166
16997
|
if (entry.isDirectory()) {
|
|
16167
16998
|
if (excluded.has(entry.name))
|
|
16168
16999
|
continue;
|
|
16169
|
-
walk(
|
|
17000
|
+
walk(path16.join(dir, entry.name), depth + 1);
|
|
16170
17001
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
16171
|
-
matches.push("./" +
|
|
17002
|
+
matches.push("./" + path16.relative(cwd, path16.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
16172
17003
|
}
|
|
16173
17004
|
}
|
|
16174
17005
|
};
|
|
@@ -16176,7 +17007,7 @@ var documentationFiles = (cwd) => {
|
|
|
16176
17007
|
return matches.toSorted().slice(0, 200);
|
|
16177
17008
|
};
|
|
16178
17009
|
var readTrimmed2 = (file, maxLines) => {
|
|
16179
|
-
if (!
|
|
17010
|
+
if (!existsSync6(file))
|
|
16180
17011
|
return "";
|
|
16181
17012
|
const lines = readFileSync5(file, "utf8").split(`
|
|
16182
17013
|
`);
|
|
@@ -16184,7 +17015,7 @@ var readTrimmed2 = (file, maxLines) => {
|
|
|
16184
17015
|
`);
|
|
16185
17016
|
};
|
|
16186
17017
|
function prReadyContext(root, range) {
|
|
16187
|
-
const cwd =
|
|
17018
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
16188
17019
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
16189
17020
|
return invalidRange(cwd);
|
|
16190
17021
|
let stdout = "";
|
|
@@ -16200,7 +17031,7 @@ function prReadyContext(root, range) {
|
|
|
16200
17031
|
stdout += printSection("Repository");
|
|
16201
17032
|
stdout += `root: ${cwd}
|
|
16202
17033
|
`;
|
|
16203
|
-
stdout += `branch: ${
|
|
17034
|
+
stdout += `branch: ${currentBranch2(cwd)}
|
|
16204
17035
|
`;
|
|
16205
17036
|
stdout += `range: ${range}
|
|
16206
17037
|
`;
|
|
@@ -16242,7 +17073,7 @@ ${status.stderr}` : "");
|
|
|
16242
17073
|
stdout += `template_path: ${template}
|
|
16243
17074
|
|
|
16244
17075
|
`;
|
|
16245
|
-
stdout += readTrimmed2(
|
|
17076
|
+
stdout += readTrimmed2(path16.join(cwd, template), 220);
|
|
16246
17077
|
} else {
|
|
16247
17078
|
stdout += `template_path: none
|
|
16248
17079
|
|
|
@@ -16252,7 +17083,7 @@ ${status.stderr}` : "");
|
|
|
16252
17083
|
stdout += `
|
|
16253
17084
|
`;
|
|
16254
17085
|
stdout += printSection("Recent Validation Signals");
|
|
16255
|
-
if (
|
|
17086
|
+
if (existsSync6(path16.join(cwd, "package.json"))) {
|
|
16256
17087
|
stdout += `package.json detected. Common scripts:
|
|
16257
17088
|
`;
|
|
16258
17089
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -16261,7 +17092,7 @@ ${status.stderr}` : "");
|
|
|
16261
17092
|
`) + `
|
|
16262
17093
|
`;
|
|
16263
17094
|
}
|
|
16264
|
-
if (
|
|
17095
|
+
if (existsSync6(path16.join(cwd, "Cargo.toml")) || existsSync6(path16.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
16265
17096
|
stdout += `Rust project detected.
|
|
16266
17097
|
`;
|
|
16267
17098
|
}
|
|
@@ -16294,7 +17125,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
16294
17125
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
16295
17126
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
16296
17127
|
function changelogContext(root, range) {
|
|
16297
|
-
const cwd =
|
|
17128
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
16298
17129
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
16299
17130
|
return invalidRange(cwd);
|
|
16300
17131
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -16304,7 +17135,7 @@ function changelogContext(root, range) {
|
|
|
16304
17135
|
stdout += printSection("Repository");
|
|
16305
17136
|
stdout += `root: ${cwd}
|
|
16306
17137
|
`;
|
|
16307
|
-
stdout += `branch: ${
|
|
17138
|
+
stdout += `branch: ${currentBranch2(cwd)}
|
|
16308
17139
|
`;
|
|
16309
17140
|
stdout += `range: ${resolvedRange}
|
|
16310
17141
|
`;
|
|
@@ -16312,8 +17143,8 @@ function changelogContext(root, range) {
|
|
|
16312
17143
|
stdout += CHANGELOG_RULES + `
|
|
16313
17144
|
`;
|
|
16314
17145
|
stdout += printSection("Existing CHANGELOG.md");
|
|
16315
|
-
const changelogPath =
|
|
16316
|
-
if (
|
|
17146
|
+
const changelogPath = path16.join(cwd, "CHANGELOG.md");
|
|
17147
|
+
if (existsSync6(changelogPath)) {
|
|
16317
17148
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
16318
17149
|
`;
|
|
16319
17150
|
} else {
|
|
@@ -16332,7 +17163,7 @@ function changelogContext(root, range) {
|
|
|
16332
17163
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
16333
17164
|
}
|
|
16334
17165
|
function docsRefreshContext(root, range) {
|
|
16335
|
-
const cwd =
|
|
17166
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
16336
17167
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
16337
17168
|
return invalidRange(cwd);
|
|
16338
17169
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -16342,7 +17173,7 @@ function docsRefreshContext(root, range) {
|
|
|
16342
17173
|
stdout += printSection("Repository");
|
|
16343
17174
|
stdout += `root: ${cwd}
|
|
16344
17175
|
`;
|
|
16345
|
-
stdout += `branch: ${
|
|
17176
|
+
stdout += `branch: ${currentBranch2(cwd)}
|
|
16346
17177
|
`;
|
|
16347
17178
|
stdout += `range: ${resolvedRange}
|
|
16348
17179
|
`;
|
|
@@ -16354,8 +17185,8 @@ function docsRefreshContext(root, range) {
|
|
|
16354
17185
|
`) + `
|
|
16355
17186
|
`;
|
|
16356
17187
|
stdout += printSection("README Preview");
|
|
16357
|
-
const readme =
|
|
16358
|
-
if (
|
|
17188
|
+
const readme = path16.join(cwd, "README.md");
|
|
17189
|
+
if (existsSync6(readme)) {
|
|
16359
17190
|
stdout += readTrimmed2(readme, 220) + `
|
|
16360
17191
|
`;
|
|
16361
17192
|
} else {
|
|
@@ -16363,8 +17194,8 @@ function docsRefreshContext(root, range) {
|
|
|
16363
17194
|
`;
|
|
16364
17195
|
}
|
|
16365
17196
|
stdout += printSection("Package Scripts");
|
|
16366
|
-
const pkgPath =
|
|
16367
|
-
if (
|
|
17197
|
+
const pkgPath = path16.join(cwd, "package.json");
|
|
17198
|
+
if (existsSync6(pkgPath)) {
|
|
16368
17199
|
try {
|
|
16369
17200
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
16370
17201
|
stdout += JSON.stringify({ name: pkg.name, version: pkg.version, scripts: pkg.scripts }, null, 2) + `
|
|
@@ -16377,7 +17208,7 @@ function docsRefreshContext(root, range) {
|
|
|
16377
17208
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
16378
17209
|
}
|
|
16379
17210
|
function releaseNotesContext(root, rangeOrTag) {
|
|
16380
|
-
const cwd =
|
|
17211
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
16381
17212
|
if (!rangeOrTag) {
|
|
16382
17213
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
16383
17214
|
`, exitCode: 1, cwd };
|
|
@@ -16391,7 +17222,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
16391
17222
|
stdout += printSection("Repository");
|
|
16392
17223
|
stdout += `root: ${cwd}
|
|
16393
17224
|
`;
|
|
16394
|
-
stdout += `branch: ${
|
|
17225
|
+
stdout += `branch: ${currentBranch2(cwd)}
|
|
16395
17226
|
`;
|
|
16396
17227
|
stdout += `requested: ${rangeOrTag}
|
|
16397
17228
|
`;
|
|
@@ -16415,7 +17246,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
16415
17246
|
`;
|
|
16416
17247
|
stdout += printSection("Existing Release Files");
|
|
16417
17248
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
16418
|
-
if (
|
|
17249
|
+
if (existsSync6(path16.join(cwd, rel)))
|
|
16419
17250
|
stdout += rel + `
|
|
16420
17251
|
`;
|
|
16421
17252
|
}
|
|
@@ -16443,12 +17274,12 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
16443
17274
|
}
|
|
16444
17275
|
|
|
16445
17276
|
// packages/workit-core/src/core/tracker-issues.ts
|
|
16446
|
-
import { spawnSync as
|
|
17277
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
16447
17278
|
|
|
16448
17279
|
// packages/workit-core/src/core/pr-create.ts
|
|
16449
|
-
import
|
|
16450
|
-
import
|
|
16451
|
-
import { spawnSync as
|
|
17280
|
+
import fs13 from "node:fs";
|
|
17281
|
+
import path17 from "node:path";
|
|
17282
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
16452
17283
|
// packages/workit-core/src/forge/pr-body.ts
|
|
16453
17284
|
function parseGhIssue(value) {
|
|
16454
17285
|
const m = /issues\/(\d+)/.exec(value);
|
|
@@ -16458,13 +17289,13 @@ function parseGhIssue(value) {
|
|
|
16458
17289
|
// packages/workit-core/src/core/pr-create.ts
|
|
16459
17290
|
function whichOnPath(tool) {
|
|
16460
17291
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
16461
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
17292
|
+
for (const dir of (process.env.PATH ?? "").split(path17.delimiter)) {
|
|
16462
17293
|
if (!dir)
|
|
16463
17294
|
continue;
|
|
16464
17295
|
for (const name of names) {
|
|
16465
|
-
const candidate =
|
|
17296
|
+
const candidate = path17.join(dir, name);
|
|
16466
17297
|
try {
|
|
16467
|
-
|
|
17298
|
+
fs13.accessSync(candidate, fs13.constants.X_OK);
|
|
16468
17299
|
return candidate;
|
|
16469
17300
|
} catch {}
|
|
16470
17301
|
}
|
|
@@ -16473,7 +17304,7 @@ function whichOnPath(tool) {
|
|
|
16473
17304
|
}
|
|
16474
17305
|
var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
|
|
16475
17306
|
var pushRemote = (cwd) => {
|
|
16476
|
-
const result =
|
|
17307
|
+
const result = spawnSync9("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
16477
17308
|
cwd,
|
|
16478
17309
|
encoding: "utf8"
|
|
16479
17310
|
});
|
|
@@ -16504,7 +17335,7 @@ var defaultCli = (bin, available) => {
|
|
|
16504
17335
|
if (!available)
|
|
16505
17336
|
return null;
|
|
16506
17337
|
return (args, root) => {
|
|
16507
|
-
const out =
|
|
17338
|
+
const out = spawnSync10(bin, args, {
|
|
16508
17339
|
cwd: root,
|
|
16509
17340
|
encoding: "utf8",
|
|
16510
17341
|
env: { ...process.env, PATH: process.env.PATH ?? "" }
|
|
@@ -16582,8 +17413,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
16582
17413
|
};
|
|
16583
17414
|
|
|
16584
17415
|
// packages/workit-core/src/core/youtrack.ts
|
|
16585
|
-
import
|
|
16586
|
-
import
|
|
17416
|
+
import fs14 from "node:fs";
|
|
17417
|
+
import path18 from "node:path";
|
|
16587
17418
|
|
|
16588
17419
|
// packages/workit-core/src/core/templates.ts
|
|
16589
17420
|
var repoRoot2 = assetRoot();
|
|
@@ -16596,22 +17427,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
16596
17427
|
// packages/workit-core/src/core/youtrack.ts
|
|
16597
17428
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
16598
17429
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
16599
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
16600
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
17430
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path18.join(configDir(), "youtrack.json");
|
|
17431
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path18.join(resolveConfigDir(), "youtrack.json");
|
|
16601
17432
|
var youTrackTokenModeOk = (p) => {
|
|
16602
17433
|
if (process.platform === "win32")
|
|
16603
17434
|
return true;
|
|
16604
|
-
const mode =
|
|
17435
|
+
const mode = fs14.statSync(p).mode & 511;
|
|
16605
17436
|
return mode === 384;
|
|
16606
17437
|
};
|
|
16607
17438
|
function readYouTrackConfig(required) {
|
|
16608
17439
|
const cfgPath = youTrackConfigPath();
|
|
16609
|
-
if (!
|
|
17440
|
+
if (!fs14.existsSync(cfgPath)) {
|
|
16610
17441
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
16611
17442
|
}
|
|
16612
17443
|
let parsed;
|
|
16613
17444
|
try {
|
|
16614
|
-
parsed = JSON.parse(
|
|
17445
|
+
parsed = JSON.parse(fs14.readFileSync(cfgPath, "utf8"));
|
|
16615
17446
|
} catch {
|
|
16616
17447
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
16617
17448
|
}
|
|
@@ -16622,11 +17453,11 @@ function readYouTrackConfig(required) {
|
|
|
16622
17453
|
}
|
|
16623
17454
|
var readYouTrackContextConfig = () => {
|
|
16624
17455
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
16625
|
-
if (!
|
|
17456
|
+
if (!fs14.existsSync(cfgPath))
|
|
16626
17457
|
return { data: {} };
|
|
16627
17458
|
let parsed;
|
|
16628
17459
|
try {
|
|
16629
|
-
parsed = JSON.parse(
|
|
17460
|
+
parsed = JSON.parse(fs14.readFileSync(cfgPath, "utf8"));
|
|
16630
17461
|
} catch {
|
|
16631
17462
|
return { error: "YouTrack configuration is unavailable" };
|
|
16632
17463
|
}
|
|
@@ -16665,12 +17496,12 @@ var youTrackToken = () => {
|
|
|
16665
17496
|
if ("error" in loaded)
|
|
16666
17497
|
return loaded;
|
|
16667
17498
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
16668
|
-
const tokenPath = tokenFile ?
|
|
16669
|
-
if (!tokenPath || !
|
|
17499
|
+
const tokenPath = tokenFile ? path18.isAbsolute(tokenFile) ? path18.resolve(tokenFile) : path18.resolve(process.cwd(), tokenFile) : "";
|
|
17500
|
+
if (!tokenPath || !fs14.existsSync(tokenPath))
|
|
16670
17501
|
return { error: "missing youtrack.token" };
|
|
16671
17502
|
if (!youTrackTokenModeOk(tokenPath))
|
|
16672
17503
|
return { error: "youtrack.token mode must be 0600" };
|
|
16673
|
-
const token =
|
|
17504
|
+
const token = fs14.readFileSync(tokenPath, "utf8").trim();
|
|
16674
17505
|
if (!token)
|
|
16675
17506
|
return { error: "empty token file" };
|
|
16676
17507
|
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
@@ -16716,7 +17547,7 @@ function youTrackTokenCreateUrl() {
|
|
|
16716
17547
|
const desc = String(defaults.description ?? "OpenCode workit — /wk-issue-update and /wk-meetings");
|
|
16717
17548
|
const scopes = Array.isArray(defaults.scopes) ? defaults.scopes : ["YouTrack"];
|
|
16718
17549
|
const base = String(config.baseUrl ?? "").replace(/\/+$/, "");
|
|
16719
|
-
const tokenFile = String(config.tokenFile ??
|
|
17550
|
+
const tokenFile = String(config.tokenFile ?? path18.join(path18.dirname(loaded.path), "youtrack.token"));
|
|
16720
17551
|
const tab = String(defaults.profileTab ?? "account-security");
|
|
16721
17552
|
const createUrl = base ? `${base}/users/me?${new URLSearchParams({ tab })}` : null;
|
|
16722
17553
|
const docsUrl = "https://www.jetbrains.com/help/youtrack/cloud/manage-permanent-token.html";
|
|
@@ -16725,7 +17556,7 @@ function youTrackTokenCreateUrl() {
|
|
|
16725
17556
|
tokenName: name,
|
|
16726
17557
|
tokenDescription: desc,
|
|
16727
17558
|
scopes,
|
|
16728
|
-
tokenFile:
|
|
17559
|
+
tokenFile: path18.resolve(tokenFile),
|
|
16729
17560
|
createUrl,
|
|
16730
17561
|
docsUrl,
|
|
16731
17562
|
prefillSupported: false,
|
|
@@ -16771,9 +17602,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
16771
17602
|
} catch {
|
|
16772
17603
|
continue;
|
|
16773
17604
|
}
|
|
16774
|
-
if (!
|
|
17605
|
+
if (!fs14.existsSync(full))
|
|
16775
17606
|
continue;
|
|
16776
|
-
const text =
|
|
17607
|
+
const text = fs14.readFileSync(full, "utf8");
|
|
16777
17608
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
16778
17609
|
if (m)
|
|
16779
17610
|
return m[1];
|
|
@@ -17065,22 +17896,22 @@ function resolveGitRevision(root, value) {
|
|
|
17065
17896
|
}
|
|
17066
17897
|
function resolveInside(root, candidate) {
|
|
17067
17898
|
const base = realpathSync5(root);
|
|
17068
|
-
const target =
|
|
17069
|
-
if (target !== base && !target.startsWith(base +
|
|
17899
|
+
const target = path19.resolve(base, candidate);
|
|
17900
|
+
if (target !== base && !target.startsWith(base + path19.sep)) {
|
|
17070
17901
|
throw new Error("path must stay inside repository root");
|
|
17071
17902
|
}
|
|
17072
17903
|
let ancestor = target;
|
|
17073
|
-
while (!
|
|
17074
|
-
ancestor =
|
|
17904
|
+
while (!existsSync7(ancestor))
|
|
17905
|
+
ancestor = path19.dirname(ancestor);
|
|
17075
17906
|
const canonicalAncestor = realpathSync5(ancestor);
|
|
17076
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
17907
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path19.sep)) {
|
|
17077
17908
|
throw new Error("path must stay inside repository root");
|
|
17078
17909
|
}
|
|
17079
17910
|
return target;
|
|
17080
17911
|
}
|
|
17081
17912
|
function run2(root, executable, args, env = {}) {
|
|
17082
17913
|
const cwd = realpathSync5(root);
|
|
17083
|
-
const result =
|
|
17914
|
+
const result = spawnSync11(executable, args, {
|
|
17084
17915
|
cwd,
|
|
17085
17916
|
encoding: "utf8",
|
|
17086
17917
|
env: { ...process.env, ...env }
|
|
@@ -17117,7 +17948,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
17117
17948
|
};
|
|
17118
17949
|
});
|
|
17119
17950
|
// packages/workit-core/src/hooks/context.ts
|
|
17120
|
-
import
|
|
17951
|
+
import path20 from "node:path";
|
|
17121
17952
|
var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
|
|
17122
17953
|
var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
|
|
17123
17954
|
var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
|
|
@@ -17196,7 +18027,7 @@ function entryCompactContext(store, entry, session, context) {
|
|
|
17196
18027
|
return null;
|
|
17197
18028
|
const freshness = { mode: "signal", current: () => recentSignal(store.root) };
|
|
17198
18029
|
const observed = task.data.evidence.some(isObservedCheck);
|
|
17199
|
-
const files = task.data.decisions.flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [
|
|
18030
|
+
const files = task.data.decisions.flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path20.resolve(store.root, ref.path)] : []));
|
|
17200
18031
|
const observedFiles = filesKey(files);
|
|
17201
18032
|
const compact = new WorkitCore(store, context).compactContext(entry.id, freshness);
|
|
17202
18033
|
if (!compact.ok)
|
|
@@ -17218,12 +18049,291 @@ var shellPolicy = (cwd, command) => {
|
|
|
17218
18049
|
const policy = shellBranchPolicyViolation(cwd, command);
|
|
17219
18050
|
return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
|
|
17220
18051
|
};
|
|
18052
|
+
// packages/workit-core/src/hooks/write-gate.ts
|
|
18053
|
+
import { realpathSync as realpathSync6 } from "node:fs";
|
|
18054
|
+
import path21 from "node:path";
|
|
18055
|
+
var NONE2 = { kind: "none" };
|
|
18056
|
+
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
18057
|
+
var canonical2 = (file) => {
|
|
18058
|
+
let rest = "";
|
|
18059
|
+
for (let dir = path21.resolve(file);; dir = path21.dirname(dir)) {
|
|
18060
|
+
try {
|
|
18061
|
+
return path21.join(realpathSync6(dir), rest);
|
|
18062
|
+
} catch {
|
|
18063
|
+
if (path21.dirname(dir) === dir)
|
|
18064
|
+
return path21.resolve(file);
|
|
18065
|
+
rest = rest ? path21.join(path21.basename(dir), rest) : path21.basename(dir);
|
|
18066
|
+
}
|
|
18067
|
+
}
|
|
18068
|
+
};
|
|
18069
|
+
var exempt = (cwd, root, file, plans) => {
|
|
18070
|
+
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
18071
|
+
const relative = path21.relative(fold(canonical2(root)), fold(canonical2(path21.resolve(cwd, file))));
|
|
18072
|
+
if (relative.startsWith("..") || path21.isAbsolute(relative))
|
|
18073
|
+
return true;
|
|
18074
|
+
const posix = relative.split(path21.sep).join("/");
|
|
18075
|
+
const segments = posix.split("/");
|
|
18076
|
+
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
18077
|
+
};
|
|
18078
|
+
var segmentsOf = (command) => {
|
|
18079
|
+
const segments = [];
|
|
18080
|
+
let current = { words: [], redirects: [] };
|
|
18081
|
+
let word = "";
|
|
18082
|
+
let inWord = false;
|
|
18083
|
+
let pending = null;
|
|
18084
|
+
const heredocs = [];
|
|
18085
|
+
const flushWord = () => {
|
|
18086
|
+
if (!inWord)
|
|
18087
|
+
return;
|
|
18088
|
+
if (pending === "redirect") {
|
|
18089
|
+
if (!word.startsWith("&"))
|
|
18090
|
+
current.redirects.push(word);
|
|
18091
|
+
} else if (pending === "heredoc")
|
|
18092
|
+
heredocs.push(word.replace(/^-/, ""));
|
|
18093
|
+
else if (pending !== "input")
|
|
18094
|
+
current.words.push(word);
|
|
18095
|
+
pending = null;
|
|
18096
|
+
word = "";
|
|
18097
|
+
inWord = false;
|
|
18098
|
+
};
|
|
18099
|
+
const flushSegment = () => {
|
|
18100
|
+
flushWord();
|
|
18101
|
+
if (current.words.length || current.redirects.length)
|
|
18102
|
+
segments.push(current);
|
|
18103
|
+
current = { words: [], redirects: [] };
|
|
18104
|
+
};
|
|
18105
|
+
for (let index = 0;index < command.length; index++) {
|
|
18106
|
+
const char = command[index];
|
|
18107
|
+
if (char === "'" || char === '"') {
|
|
18108
|
+
const end = command.indexOf(char, index + 1);
|
|
18109
|
+
word += command.slice(index + 1, end < 0 ? undefined : end);
|
|
18110
|
+
inWord = true;
|
|
18111
|
+
index = end < 0 ? command.length : end;
|
|
18112
|
+
} else if (char === "\\" && index + 1 < command.length) {
|
|
18113
|
+
word += command[++index];
|
|
18114
|
+
inWord = true;
|
|
18115
|
+
} else if (char === `
|
|
18116
|
+
`) {
|
|
18117
|
+
flushSegment();
|
|
18118
|
+
while (heredocs.length) {
|
|
18119
|
+
const delimiter = heredocs.shift();
|
|
18120
|
+
const lines = command.slice(index + 1).split(`
|
|
18121
|
+
`);
|
|
18122
|
+
const at = lines.findIndex((line) => line.trim() === delimiter);
|
|
18123
|
+
const skipped = (at < 0 ? lines : lines.slice(0, at + 1)).join(`
|
|
18124
|
+
`).length + 1;
|
|
18125
|
+
index += skipped;
|
|
18126
|
+
}
|
|
18127
|
+
} else if (/\s/.test(char))
|
|
18128
|
+
flushWord();
|
|
18129
|
+
else if (";&|()".includes(char)) {
|
|
18130
|
+
if (char === "&" && command[index + 1] === ">")
|
|
18131
|
+
continue;
|
|
18132
|
+
flushSegment();
|
|
18133
|
+
} else if (char === ">") {
|
|
18134
|
+
if (inWord && /^\d$/.test(word)) {
|
|
18135
|
+
word = "";
|
|
18136
|
+
inWord = false;
|
|
18137
|
+
} else
|
|
18138
|
+
flushWord();
|
|
18139
|
+
if (command[index + 1] === ">")
|
|
18140
|
+
index++;
|
|
18141
|
+
if (command[index + 1] === "&") {
|
|
18142
|
+
index++;
|
|
18143
|
+
word = "&";
|
|
18144
|
+
inWord = true;
|
|
18145
|
+
}
|
|
18146
|
+
pending = "redirect";
|
|
18147
|
+
} else if (char === "<") {
|
|
18148
|
+
flushWord();
|
|
18149
|
+
if (command[index + 1] === "<") {
|
|
18150
|
+
index++;
|
|
18151
|
+
if (command[index + 1] === "<")
|
|
18152
|
+
index++;
|
|
18153
|
+
pending = "heredoc";
|
|
18154
|
+
} else
|
|
18155
|
+
pending = "input";
|
|
18156
|
+
} else {
|
|
18157
|
+
word += char;
|
|
18158
|
+
inWord = true;
|
|
18159
|
+
}
|
|
18160
|
+
}
|
|
18161
|
+
flushSegment();
|
|
18162
|
+
return segments;
|
|
18163
|
+
};
|
|
18164
|
+
var PREFIXES = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
18165
|
+
var operands = (words) => words.filter((item) => !item.startsWith("-"));
|
|
18166
|
+
var editsOf = (input) => {
|
|
18167
|
+
let words = input;
|
|
18168
|
+
while (words.length && (PREFIXES.has(words[0]) || /^\w+=/.test(words[0])))
|
|
18169
|
+
words = words.slice(1);
|
|
18170
|
+
const [verb, ...args] = words;
|
|
18171
|
+
const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
|
|
18172
|
+
if (!name || name === "workit" || name === "npx" && args.some((item) => /workit/.test(item)))
|
|
18173
|
+
return [];
|
|
18174
|
+
switch (name) {
|
|
18175
|
+
case "touch":
|
|
18176
|
+
case "mkdir":
|
|
18177
|
+
case "rm":
|
|
18178
|
+
case "rmdir":
|
|
18179
|
+
case "tee":
|
|
18180
|
+
case "mv":
|
|
18181
|
+
case "truncate":
|
|
18182
|
+
return operands(args);
|
|
18183
|
+
case "cp":
|
|
18184
|
+
case "ln":
|
|
18185
|
+
case "install": {
|
|
18186
|
+
const target = args.indexOf("-t");
|
|
18187
|
+
if (target >= 0)
|
|
18188
|
+
return args[target + 1] ? [args[target + 1]] : null;
|
|
18189
|
+
const last = operands(args).at(-1);
|
|
18190
|
+
return last ? [last] : null;
|
|
18191
|
+
}
|
|
18192
|
+
case "dd":
|
|
18193
|
+
return args.filter((item) => item.startsWith("of=")).map((item) => item.slice(3));
|
|
18194
|
+
case "sed":
|
|
18195
|
+
case "perl": {
|
|
18196
|
+
if (!args.some((item) => /^-[a-zA-Z]*i|^--in-place/.test(item)))
|
|
18197
|
+
return [];
|
|
18198
|
+
const scripted = args.some((item) => item === "-e" || item === "-f");
|
|
18199
|
+
const files = [];
|
|
18200
|
+
for (let index = 0;index < args.length; index++) {
|
|
18201
|
+
if (args[index] === "-e" || args[index] === "-f")
|
|
18202
|
+
index++;
|
|
18203
|
+
else if (!args[index].startsWith("-"))
|
|
18204
|
+
files.push(args[index]);
|
|
18205
|
+
}
|
|
18206
|
+
return scripted ? files : files.slice(1);
|
|
18207
|
+
}
|
|
18208
|
+
case "patch":
|
|
18209
|
+
return null;
|
|
18210
|
+
case "set-content":
|
|
18211
|
+
case "add-content":
|
|
18212
|
+
case "out-file":
|
|
18213
|
+
case "new-item":
|
|
18214
|
+
case "ni":
|
|
18215
|
+
case "remove-item":
|
|
18216
|
+
case "del":
|
|
18217
|
+
case "copy-item":
|
|
18218
|
+
case "move-item": {
|
|
18219
|
+
const flag = args.findIndex((item) => /^-(?:path|filepath|literalpath|destination)$/i.test(item));
|
|
18220
|
+
if (flag >= 0)
|
|
18221
|
+
return args[flag + 1] ? [args[flag + 1]] : null;
|
|
18222
|
+
const files = operands(args);
|
|
18223
|
+
if (name === "move-item")
|
|
18224
|
+
return files.length ? files : null;
|
|
18225
|
+
const file = name === "copy-item" ? files.at(-1) : files[0];
|
|
18226
|
+
return file ? [file] : null;
|
|
18227
|
+
}
|
|
18228
|
+
case "git": {
|
|
18229
|
+
const sub = operands(args)[0];
|
|
18230
|
+
if (sub === "apply")
|
|
18231
|
+
return null;
|
|
18232
|
+
if (sub === "restore" || sub === "mv" || sub === "rm")
|
|
18233
|
+
return operands(args).slice(1);
|
|
18234
|
+
if (sub === "checkout" && args.includes("--"))
|
|
18235
|
+
return args.slice(args.indexOf("--") + 1);
|
|
18236
|
+
return [];
|
|
18237
|
+
}
|
|
18238
|
+
default:
|
|
18239
|
+
return [];
|
|
18240
|
+
}
|
|
18241
|
+
};
|
|
18242
|
+
var shellWrites = (command) => {
|
|
18243
|
+
const targets = [];
|
|
18244
|
+
let unknown = false;
|
|
18245
|
+
for (const segment of segmentsOf(command)) {
|
|
18246
|
+
targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
|
|
18247
|
+
const edits = editsOf(segment.words);
|
|
18248
|
+
if (edits === null)
|
|
18249
|
+
unknown = true;
|
|
18250
|
+
else
|
|
18251
|
+
targets.push(...edits);
|
|
18252
|
+
}
|
|
18253
|
+
if (!unknown && targets.length === 0)
|
|
18254
|
+
return { writes: false, targets: null };
|
|
18255
|
+
return { writes: true, targets: unknown ? null : targets };
|
|
18256
|
+
};
|
|
18257
|
+
var cache2 = new Map;
|
|
18258
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path21.resolve(root, file)) ?? "-").join("\x00");
|
|
18259
|
+
var gateFor = (cwd) => {
|
|
18260
|
+
const store = new TaskStore(cwd);
|
|
18261
|
+
const listed = store.listTaskIndex();
|
|
18262
|
+
if (!listed.ok)
|
|
18263
|
+
return null;
|
|
18264
|
+
const key = store.currentKey();
|
|
18265
|
+
if (!key.ok)
|
|
18266
|
+
return null;
|
|
18267
|
+
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
18268
|
+
if (!entry)
|
|
18269
|
+
return { blockers: [], root: store.root, plans: new Set };
|
|
18270
|
+
const ledger = ledgerPath(store.root);
|
|
18271
|
+
const cacheKey = `${entry.id}\x00${entry.revision}\x00${ledger.ok && fileSignature(ledger.value) || "-"}`;
|
|
18272
|
+
const hit = cache2.get(store.root);
|
|
18273
|
+
if (hit && hit.key === cacheKey && hit.planKey === planKeyOf(store.root, hit.plans))
|
|
18274
|
+
return hit;
|
|
18275
|
+
const task = store.readTask(entry.id);
|
|
18276
|
+
if (!task.ok)
|
|
18277
|
+
return null;
|
|
18278
|
+
const workspace = store.readWorkspace();
|
|
18279
|
+
const plans = new Set((latestJudgment(task.data)?.refs ?? []).flatMap((ref) => ref.kind === "file" ? [ref.path] : []));
|
|
18280
|
+
const blockers = writeBlockers(task.data, store.root).filter((blocker) => {
|
|
18281
|
+
const requirement = task.data.policy?.requirements.find((item) => item.ruleId === blocker.ruleId && item.before === "write");
|
|
18282
|
+
return !(requirement?.acceptanceAllowed && workspace.ok && workspace.data && applicableDecision(task.data, workspace.data, requirement, store.root).length > 0);
|
|
18283
|
+
});
|
|
18284
|
+
const gate = {
|
|
18285
|
+
blockers,
|
|
18286
|
+
root: store.root,
|
|
18287
|
+
plans,
|
|
18288
|
+
key: cacheKey,
|
|
18289
|
+
planKey: planKeyOf(store.root, plans)
|
|
18290
|
+
};
|
|
18291
|
+
cache2.set(store.root, gate);
|
|
18292
|
+
if (cache2.size > 32)
|
|
18293
|
+
cache2.delete(cache2.keys().next().value);
|
|
18294
|
+
return gate;
|
|
18295
|
+
};
|
|
18296
|
+
function writeGate(cwd, paths) {
|
|
18297
|
+
try {
|
|
18298
|
+
const gate = gateFor(cwd);
|
|
18299
|
+
if (!gate || gate.blockers.length === 0)
|
|
18300
|
+
return NONE2;
|
|
18301
|
+
if (paths && paths.every((file) => exempt(cwd, gate.root, file, gate.plans)))
|
|
18302
|
+
return NONE2;
|
|
18303
|
+
const reasons = gate.blockers.map((item) => `${item.ruleId}: ${item.reason}`).join("; ");
|
|
18304
|
+
return {
|
|
18305
|
+
kind: "deny",
|
|
18306
|
+
reason: `workit before-write gate: ${reasons}. Markdown, docs/, plans/ and files outside the checkout stay writable.`,
|
|
18307
|
+
unblock: gate.blockers[0].reason
|
|
18308
|
+
};
|
|
18309
|
+
} catch {
|
|
18310
|
+
return NONE2;
|
|
18311
|
+
}
|
|
18312
|
+
}
|
|
18313
|
+
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
18314
|
+
var SHELLS = new Set(["sh", "bash", "zsh", "dash", "pwsh", "powershell", "cmd"]);
|
|
18315
|
+
var WRITE_TOOLS = new Set([
|
|
18316
|
+
"write",
|
|
18317
|
+
"edit",
|
|
18318
|
+
"multiedit",
|
|
18319
|
+
"notebookedit",
|
|
18320
|
+
"applypatch",
|
|
18321
|
+
"patch",
|
|
18322
|
+
"delete",
|
|
18323
|
+
"remove",
|
|
18324
|
+
"rename",
|
|
18325
|
+
"mkdir",
|
|
18326
|
+
"mv",
|
|
18327
|
+
"cp",
|
|
18328
|
+
"touch",
|
|
18329
|
+
"strreplace",
|
|
18330
|
+
"searchreplace",
|
|
18331
|
+
"editfile",
|
|
18332
|
+
"writefile"
|
|
18333
|
+
]);
|
|
17221
18334
|
// packages/workit-core/src/hooks/handle.ts
|
|
17222
18335
|
var offered = new Set;
|
|
17223
18336
|
var CLAUDE_JUDGES = new Set(["workit:verifier", "workit:reviewer"]);
|
|
17224
|
-
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
17225
|
-
var SHELLS = new Set(["sh", "bash", "zsh", "dash", "pwsh", "powershell", "cmd"]);
|
|
17226
|
-
|
|
17227
18337
|
// packages/workit-core/src/hooks/hosts/claude-code.ts
|
|
17228
18338
|
var SOURCES = new Set(["startup", "resume", "clear", "compact", "fork"]);
|
|
17229
18339
|
var SHELL_TOOLS = new Set(["Bash", "PowerShell"]);
|
|
@@ -17250,6 +18360,7 @@ var OPENCODE_DESCRIPTOR = {
|
|
|
17250
18360
|
"context.turn": { support: "native", native: 'session.hook("context")' },
|
|
17251
18361
|
"shell.pre": { support: "native", native: 'permission.hook("evaluate")' },
|
|
17252
18362
|
"tool.pre": { support: "native", native: 'tool.hook("execute.before")' },
|
|
18363
|
+
"write.pre": { support: "native", native: 'permission.hook("evaluate")' },
|
|
17253
18364
|
"shell.post": { support: "native", native: 'tool.hook("execute.after")' },
|
|
17254
18365
|
"subagent.start": { support: "native", native: 'tool.hook("execute.before") subagent' },
|
|
17255
18366
|
"subagent.stop": { support: "native", native: 'tool.hook("execute.after") subagent' },
|
|
@@ -17304,11 +18415,11 @@ var OPENCODE_DESCRIPTOR = {
|
|
|
17304
18415
|
]
|
|
17305
18416
|
};
|
|
17306
18417
|
// packages/workit-opencode/src/shared/init-apply.ts
|
|
17307
|
-
import
|
|
18418
|
+
import path26 from "node:path";
|
|
17308
18419
|
|
|
17309
18420
|
// packages/workit-core/src/core/gitignore.ts
|
|
17310
|
-
import { existsSync as
|
|
17311
|
-
import
|
|
18421
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
18422
|
+
import path22 from "node:path";
|
|
17312
18423
|
var GITIGNORE_ENTRIES = [
|
|
17313
18424
|
"# workit: SDD working state (never commit)",
|
|
17314
18425
|
"docs/*/sdd/",
|
|
@@ -17328,8 +18439,8 @@ var GITIGNORE_ENTRIES = [
|
|
|
17328
18439
|
var ensureProjectGitignore = (workspaceRoot, confirmed) => {
|
|
17329
18440
|
if (!confirmed)
|
|
17330
18441
|
return { ok: false, error: "confirmed: true required" };
|
|
17331
|
-
const file =
|
|
17332
|
-
const existing =
|
|
18442
|
+
const file = path22.join(workspaceRoot, ".gitignore");
|
|
18443
|
+
const existing = existsSync8(file) ? readFileSync6(file, "utf8") : "";
|
|
17333
18444
|
const existingLines = new Set(existing.split(`
|
|
17334
18445
|
`).map((l) => l.trim()).filter(Boolean));
|
|
17335
18446
|
const added = [];
|
|
@@ -17344,37 +18455,37 @@ var ensureProjectGitignore = (workspaceRoot, confirmed) => {
|
|
|
17344
18455
|
const separator = existing && !existing.endsWith(`
|
|
17345
18456
|
`) ? `
|
|
17346
18457
|
` : "";
|
|
17347
|
-
|
|
18458
|
+
writeFileSync4(file, existing + separator + (existing ? `
|
|
17348
18459
|
` : "") + append.join(`
|
|
17349
18460
|
`) + `
|
|
17350
18461
|
`, "utf8");
|
|
17351
|
-
} else if (!
|
|
17352
|
-
|
|
18462
|
+
} else if (!existsSync8(file)) {
|
|
18463
|
+
writeFileSync4(file, "", "utf8");
|
|
17353
18464
|
}
|
|
17354
18465
|
return { ok: true, path: file, added };
|
|
17355
18466
|
};
|
|
17356
18467
|
|
|
17357
18468
|
// packages/workit-core/src/core/hygiene.ts
|
|
17358
|
-
import { existsSync as
|
|
17359
|
-
import
|
|
18469
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
|
|
18470
|
+
import path23 from "node:path";
|
|
17360
18471
|
var repoRoot3 = assetRoot();
|
|
17361
|
-
var templatesDir = () =>
|
|
18472
|
+
var templatesDir = () => path23.join(repoRoot3, "templates", "hygiene");
|
|
17362
18473
|
var packageJson = (root) => {
|
|
17363
|
-
if (!
|
|
18474
|
+
if (!existsSync9(path23.join(root, "package.json")))
|
|
17364
18475
|
return null;
|
|
17365
18476
|
try {
|
|
17366
|
-
return JSON.parse(readFileSync7(
|
|
18477
|
+
return JSON.parse(readFileSync7(path23.join(root, "package.json"), "utf8"));
|
|
17367
18478
|
} catch {
|
|
17368
18479
|
return null;
|
|
17369
18480
|
}
|
|
17370
18481
|
};
|
|
17371
18482
|
var isOpenSource = (root) => {
|
|
17372
|
-
if (
|
|
18483
|
+
if (existsSync9(path23.join(root, "LICENSE")))
|
|
17373
18484
|
return true;
|
|
17374
18485
|
const pkg = packageJson(root);
|
|
17375
18486
|
if (pkg && !pkg.private)
|
|
17376
18487
|
return true;
|
|
17377
|
-
return
|
|
18488
|
+
return path23.basename(root) === "workflow-toolkit";
|
|
17378
18489
|
};
|
|
17379
18490
|
var licenseHolder = (root) => {
|
|
17380
18491
|
const pkg = packageJson(root);
|
|
@@ -17397,13 +18508,13 @@ var planHygieneFiles = (root, opts = {}) => {
|
|
|
17397
18508
|
files.push("LICENSE", "CONTRIBUTING.md");
|
|
17398
18509
|
const planned = [];
|
|
17399
18510
|
for (const file of files) {
|
|
17400
|
-
if (
|
|
18511
|
+
if (existsSync9(path23.join(root, file)))
|
|
17401
18512
|
continue;
|
|
17402
|
-
const tpl =
|
|
17403
|
-
if (!
|
|
18513
|
+
const tpl = path23.join(templatesDir(), file);
|
|
18514
|
+
if (!existsSync9(tpl))
|
|
17404
18515
|
continue;
|
|
17405
|
-
const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g,
|
|
17406
|
-
planned.push({ path:
|
|
18516
|
+
const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g, path23.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
|
|
18517
|
+
planned.push({ path: path23.join(root, file), content });
|
|
17407
18518
|
}
|
|
17408
18519
|
return planned;
|
|
17409
18520
|
};
|
|
@@ -17412,48 +18523,25 @@ var ensureHygieneFiles = (root, opts) => {
|
|
|
17412
18523
|
return { ok: false, error: "confirmed: true required" };
|
|
17413
18524
|
const created = [];
|
|
17414
18525
|
for (const planned of planHygieneFiles(root, opts)) {
|
|
17415
|
-
|
|
17416
|
-
created.push(
|
|
18526
|
+
writeFileSync5(planned.path, planned.content, "utf8");
|
|
18527
|
+
created.push(path23.basename(planned.path));
|
|
17417
18528
|
}
|
|
17418
18529
|
return { ok: true, created };
|
|
17419
18530
|
};
|
|
17420
18531
|
|
|
17421
18532
|
// packages/workit-core/src/core/init.ts
|
|
17422
|
-
import
|
|
17423
|
-
import
|
|
17424
|
-
import { spawnSync as
|
|
17425
|
-
|
|
17426
|
-
// packages/workit-core/src/core/safe-write.ts
|
|
17427
|
-
import {
|
|
17428
|
-
closeSync as closeSync2,
|
|
17429
|
-
fchmodSync,
|
|
17430
|
-
fsyncSync,
|
|
17431
|
-
lstatSync as lstatSync3,
|
|
17432
|
-
mkdirSync as mkdirSync3,
|
|
17433
|
-
openSync as openSync2,
|
|
17434
|
-
renameSync as renameSync2,
|
|
17435
|
-
unlinkSync,
|
|
17436
|
-
writeFileSync as writeFileSync5
|
|
17437
|
-
} from "node:fs";
|
|
17438
|
-
function writeFileExclusive(file, content, mode) {
|
|
17439
|
-
try {
|
|
17440
|
-
writeFileSync5(file, content, { encoding: "utf8", flag: "wx", mode });
|
|
17441
|
-
return "created";
|
|
17442
|
-
} catch (err) {
|
|
17443
|
-
if (err.code === "EEXIST")
|
|
17444
|
-
return "preserved";
|
|
17445
|
-
throw err;
|
|
17446
|
-
}
|
|
17447
|
-
}
|
|
18533
|
+
import fs15 from "node:fs";
|
|
18534
|
+
import path25 from "node:path";
|
|
18535
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
17448
18536
|
|
|
17449
18537
|
// packages/workit-core/src/core/workspace-branch-policy.ts
|
|
17450
18538
|
import { mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
|
|
17451
|
-
import
|
|
18539
|
+
import path24 from "node:path";
|
|
17452
18540
|
import { isDeepStrictEqual } from "node:util";
|
|
17453
18541
|
|
|
17454
18542
|
// packages/workit-core/src/core/branch-policy.ts
|
|
17455
|
-
import { spawnSync as
|
|
17456
|
-
var
|
|
18543
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
18544
|
+
var PREFIXES2 = {
|
|
17457
18545
|
feature: "feature/*",
|
|
17458
18546
|
bugfix: "bugfix/*",
|
|
17459
18547
|
release: "release/*",
|
|
@@ -17461,7 +18549,7 @@ var PREFIXES = {
|
|
|
17461
18549
|
};
|
|
17462
18550
|
var detectBranchPolicy = (workspaceRoot) => {
|
|
17463
18551
|
const branchExists = (name) => {
|
|
17464
|
-
const r =
|
|
18552
|
+
const r = spawnSync12("git", ["branch", "--list", name], {
|
|
17465
18553
|
cwd: workspaceRoot,
|
|
17466
18554
|
encoding: "utf8"
|
|
17467
18555
|
});
|
|
@@ -17478,7 +18566,7 @@ var detectBranchPolicy = (workspaceRoot) => {
|
|
|
17478
18566
|
integration: "merge",
|
|
17479
18567
|
protected: root ? [root, "develop"] : ["develop"],
|
|
17480
18568
|
allowed: [...PRESETS.gitflow.allowed],
|
|
17481
|
-
prefixes:
|
|
18569
|
+
prefixes: PREFIXES2
|
|
17482
18570
|
};
|
|
17483
18571
|
}
|
|
17484
18572
|
if (root === "main") {
|
|
@@ -17488,7 +18576,7 @@ var detectBranchPolicy = (workspaceRoot) => {
|
|
|
17488
18576
|
integration: "pr",
|
|
17489
18577
|
protected: [root],
|
|
17490
18578
|
allowed: ["*"],
|
|
17491
|
-
prefixes:
|
|
18579
|
+
prefixes: PREFIXES2
|
|
17492
18580
|
};
|
|
17493
18581
|
}
|
|
17494
18582
|
if (root === "master") {
|
|
@@ -17498,7 +18586,7 @@ var detectBranchPolicy = (workspaceRoot) => {
|
|
|
17498
18586
|
integration: "pr",
|
|
17499
18587
|
protected: [root],
|
|
17500
18588
|
allowed: ["*"],
|
|
17501
|
-
prefixes:
|
|
18589
|
+
prefixes: PREFIXES2
|
|
17502
18590
|
};
|
|
17503
18591
|
}
|
|
17504
18592
|
return {
|
|
@@ -17507,7 +18595,7 @@ var detectBranchPolicy = (workspaceRoot) => {
|
|
|
17507
18595
|
integration: "merge",
|
|
17508
18596
|
protected: [],
|
|
17509
18597
|
allowed: [],
|
|
17510
|
-
prefixes:
|
|
18598
|
+
prefixes: PREFIXES2
|
|
17511
18599
|
};
|
|
17512
18600
|
};
|
|
17513
18601
|
|
|
@@ -17521,7 +18609,7 @@ var readFileSafe = (p) => {
|
|
|
17521
18609
|
};
|
|
17522
18610
|
function applyWorkspaceBranchPolicy(opts) {
|
|
17523
18611
|
const { workspace_root, env = process.env } = opts;
|
|
17524
|
-
const dir =
|
|
18612
|
+
const dir = path24.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
|
|
17525
18613
|
const {
|
|
17526
18614
|
status,
|
|
17527
18615
|
path: wsPath,
|
|
@@ -17533,7 +18621,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
17533
18621
|
if (status === "malformed" || status === "invalid")
|
|
17534
18622
|
return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
|
|
17535
18623
|
const detection = detectBranchPolicy(workspace_root);
|
|
17536
|
-
const name = String(env.WORKFLOW_BP_NAME ??
|
|
18624
|
+
const name = String(env.WORKFLOW_BP_NAME ?? path24.basename(workspace_root));
|
|
17537
18625
|
const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
|
|
17538
18626
|
const policy = {
|
|
17539
18627
|
preset: detection.preset,
|
|
@@ -17582,7 +18670,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
17582
18670
|
ok: false,
|
|
17583
18671
|
error: `workspaces.json changed while applying the branch policy: ${wsPath}`
|
|
17584
18672
|
};
|
|
17585
|
-
mkdirSync4(
|
|
18673
|
+
mkdirSync4(path24.dirname(wsPath), { recursive: true });
|
|
17586
18674
|
const tmp = `${wsPath}.${process.pid}.tmp`;
|
|
17587
18675
|
try {
|
|
17588
18676
|
writeFileSync6(tmp, JSON.stringify({ ...document, workspaces: next }, null, 2) + `
|
|
@@ -17617,7 +18705,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
17617
18705
|
var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE";
|
|
17618
18706
|
var readJson = (p) => {
|
|
17619
18707
|
try {
|
|
17620
|
-
const parsed = JSON.parse(
|
|
18708
|
+
const parsed = JSON.parse(fs15.readFileSync(p, "utf8"));
|
|
17621
18709
|
return isConfigObject(parsed) ? parsed : null;
|
|
17622
18710
|
} catch {
|
|
17623
18711
|
return null;
|
|
@@ -17629,7 +18717,7 @@ var resolveInitProvider = (cwd) => {
|
|
|
17629
18717
|
return env.toLowerCase();
|
|
17630
18718
|
const root = cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
17631
18719
|
try {
|
|
17632
|
-
const out =
|
|
18720
|
+
const out = spawnSync13("git", ["remote", "get-url", "origin"], {
|
|
17633
18721
|
cwd: root,
|
|
17634
18722
|
encoding: "utf8"
|
|
17635
18723
|
});
|
|
@@ -17647,7 +18735,7 @@ var youtrackJsonContent = (dir) => {
|
|
|
17647
18735
|
const meetingIssue = process.env.WORKFLOW_YT_MEETING_ISSUE;
|
|
17648
18736
|
return {
|
|
17649
18737
|
baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://youtrack.example.com",
|
|
17650
|
-
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ??
|
|
18738
|
+
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path25.join(dir, "youtrack.token"),
|
|
17651
18739
|
...meetingIssue ? {
|
|
17652
18740
|
meetingIssue,
|
|
17653
18741
|
meetingIssues: { general: { issue: meetingIssue, label: "General meetings" } }
|
|
@@ -17677,19 +18765,19 @@ var vcsJsonContent = () => {
|
|
|
17677
18765
|
};
|
|
17678
18766
|
function initApplyData(action, env = process.env) {
|
|
17679
18767
|
const dir = String(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
|
|
17680
|
-
|
|
18768
|
+
fs15.mkdirSync(dir, { recursive: true });
|
|
17681
18769
|
switch (action) {
|
|
17682
18770
|
case "youtrack_json": {
|
|
17683
|
-
const out =
|
|
17684
|
-
|
|
18771
|
+
const out = path25.join(dir, "youtrack.json");
|
|
18772
|
+
fs15.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
17685
18773
|
`, "utf8");
|
|
17686
18774
|
return { action, ok: true, path: out };
|
|
17687
18775
|
}
|
|
17688
18776
|
case "youtrack_token_placeholder": {
|
|
17689
|
-
const p =
|
|
18777
|
+
const p = path25.join(dir, "youtrack.token");
|
|
17690
18778
|
const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER2 + `
|
|
17691
18779
|
`, 384) === "preserved";
|
|
17692
|
-
const abs =
|
|
18780
|
+
const abs = path25.resolve(p);
|
|
17693
18781
|
return {
|
|
17694
18782
|
action,
|
|
17695
18783
|
ok: true,
|
|
@@ -17701,14 +18789,14 @@ function initApplyData(action, env = process.env) {
|
|
|
17701
18789
|
};
|
|
17702
18790
|
}
|
|
17703
18791
|
case "youtrack_scaffold": {
|
|
17704
|
-
const jsonOut =
|
|
17705
|
-
const tokenOut =
|
|
17706
|
-
|
|
18792
|
+
const jsonOut = path25.join(dir, "youtrack.json");
|
|
18793
|
+
const tokenOut = path25.join(dir, "youtrack.token");
|
|
18794
|
+
fs15.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
17707
18795
|
`, "utf8");
|
|
17708
18796
|
const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER2 + `
|
|
17709
18797
|
`, 384) === "preserved";
|
|
17710
|
-
const configPath =
|
|
17711
|
-
const tokenPath =
|
|
18798
|
+
const configPath = path25.resolve(jsonOut);
|
|
18799
|
+
const tokenPath = path25.resolve(tokenOut);
|
|
17712
18800
|
const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
|
|
17713
18801
|
process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
|
|
17714
18802
|
let tokenCreate = {};
|
|
@@ -17760,10 +18848,10 @@ function initApplyData(action, env = process.env) {
|
|
|
17760
18848
|
}
|
|
17761
18849
|
}
|
|
17762
18850
|
case "vcs_scaffold": {
|
|
17763
|
-
const jsonOut =
|
|
17764
|
-
|
|
18851
|
+
const jsonOut = path25.join(dir, "vcs.json");
|
|
18852
|
+
fs15.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
|
|
17765
18853
|
`, "utf8");
|
|
17766
|
-
const configPath =
|
|
18854
|
+
const configPath = path25.resolve(jsonOut);
|
|
17767
18855
|
const prev = process.env.WORKFLOW_VCS_CONFIG;
|
|
17768
18856
|
process.env.WORKFLOW_VCS_CONFIG = configPath;
|
|
17769
18857
|
try {
|
|
@@ -17882,7 +18970,7 @@ var executeInitApply = (args, directory, runtime) => {
|
|
|
17882
18970
|
protectedNames: args.branch_policy_protected
|
|
17883
18971
|
}, current);
|
|
17884
18972
|
writeConfig(next);
|
|
17885
|
-
return output(ok({ action: "config", path:
|
|
18973
|
+
return output(ok({ action: "config", path: path26.join(configDir(), "config.json"), ...next }));
|
|
17886
18974
|
}
|
|
17887
18975
|
const env = Object.fromEntries(Object.entries({
|
|
17888
18976
|
WORKFLOW_YT_BASE_URL: args.base_url,
|
|
@@ -17898,15 +18986,15 @@ var executeInitApply = (args, directory, runtime) => {
|
|
|
17898
18986
|
};
|
|
17899
18987
|
|
|
17900
18988
|
// packages/workit-opencode/src/shared/session.ts
|
|
17901
|
-
import { realpathSync as
|
|
17902
|
-
import
|
|
18989
|
+
import { realpathSync as realpathSync7 } from "node:fs";
|
|
18990
|
+
import path27 from "node:path";
|
|
17903
18991
|
var sameWorkspace = (expected, observed) => {
|
|
17904
18992
|
if (typeof observed !== "string" || !observed)
|
|
17905
18993
|
return false;
|
|
17906
18994
|
try {
|
|
17907
|
-
return
|
|
18995
|
+
return realpathSync7(expected) === realpathSync7(observed);
|
|
17908
18996
|
} catch {
|
|
17909
|
-
return
|
|
18997
|
+
return path27.resolve(expected) === path27.resolve(observed);
|
|
17910
18998
|
}
|
|
17911
18999
|
};
|
|
17912
19000
|
|
|
@@ -17919,8 +19007,8 @@ var objectSchema = (properties, required) => ({
|
|
|
17919
19007
|
});
|
|
17920
19008
|
var familyTools = OPERATION_FAMILIES.map((family) => ({
|
|
17921
19009
|
name: `workit_${family}`,
|
|
17922
|
-
description:
|
|
17923
|
-
input:
|
|
19010
|
+
description: operationDescription(family),
|
|
19011
|
+
input: flatOperationJsonSchema(family)
|
|
17924
19012
|
}));
|
|
17925
19013
|
var WORKIT_TOOL_CATALOG = [
|
|
17926
19014
|
...familyTools,
|
|
@@ -17977,8 +19065,8 @@ var workitFamilyOf = (toolName) => {
|
|
|
17977
19065
|
};
|
|
17978
19066
|
|
|
17979
19067
|
// packages/workit-opencode/src/tools/context.ts
|
|
17980
|
-
import { realpathSync as
|
|
17981
|
-
import
|
|
19068
|
+
import { realpathSync as realpathSync8 } from "node:fs";
|
|
19069
|
+
import path28 from "node:path";
|
|
17982
19070
|
var createContextTool = () => ({
|
|
17983
19071
|
description: "Read Git, pull request, YouTrack, issue, changelog, release, or affected-file context.",
|
|
17984
19072
|
execute: async (args, context) => {
|
|
@@ -17988,7 +19076,7 @@ var createContextTool = () => ({
|
|
|
17988
19076
|
let root;
|
|
17989
19077
|
try {
|
|
17990
19078
|
const cwd = parsed.data.cwd;
|
|
17991
|
-
root =
|
|
19079
|
+
root = realpathSync8(cwd ? path28.resolve(context.directory, cwd) : context.directory);
|
|
17992
19080
|
} catch {
|
|
17993
19081
|
return JSON.stringify(failure2("invalid_input", "context target directory cannot be resolved"), null, 2);
|
|
17994
19082
|
}
|
|
@@ -18510,24 +19598,37 @@ var injectCompactionContext = (root, sessionID, system) => {
|
|
|
18510
19598
|
};
|
|
18511
19599
|
|
|
18512
19600
|
// packages/workit-opencode/src/v2/permissions.ts
|
|
19601
|
+
var WRITE_ACTIONS = new Set(["edit", "write", "patch"]);
|
|
18513
19602
|
var evaluateShellPermission = (root, event) => {
|
|
18514
|
-
if (event.
|
|
19603
|
+
if (event.effect === "deny")
|
|
18515
19604
|
return;
|
|
18516
|
-
|
|
18517
|
-
|
|
18518
|
-
|
|
19605
|
+
const deny = (reason) => {
|
|
19606
|
+
event.effect = "deny";
|
|
19607
|
+
event.message = reason;
|
|
19608
|
+
};
|
|
19609
|
+
const resources = event.resources.filter((resource) => typeof resource === "string");
|
|
19610
|
+
if (WRITE_ACTIONS.has(event.action)) {
|
|
19611
|
+
const decision = writeGate(root, resources);
|
|
19612
|
+
if (decision.kind === "deny")
|
|
19613
|
+
deny(decision.reason);
|
|
19614
|
+
return;
|
|
19615
|
+
}
|
|
19616
|
+
if (event.action !== "shell")
|
|
19617
|
+
return;
|
|
19618
|
+
for (const resource of resources) {
|
|
18519
19619
|
const decision = shellPolicy(root, resource);
|
|
18520
|
-
if (decision.kind === "deny")
|
|
18521
|
-
|
|
18522
|
-
|
|
18523
|
-
|
|
18524
|
-
|
|
19620
|
+
if (decision.kind === "deny")
|
|
19621
|
+
return deny(decision.reason);
|
|
19622
|
+
const writes = shellWrites(resource);
|
|
19623
|
+
const gated = writes.writes ? writeGate(root, writes.targets) : null;
|
|
19624
|
+
if (gated?.kind === "deny")
|
|
19625
|
+
return deny(gated.reason);
|
|
18525
19626
|
}
|
|
18526
19627
|
};
|
|
18527
19628
|
|
|
18528
19629
|
// packages/workit-opencode/src/v2/registry.ts
|
|
18529
|
-
import { existsSync as
|
|
18530
|
-
import
|
|
19630
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
|
|
19631
|
+
import path30 from "node:path";
|
|
18531
19632
|
|
|
18532
19633
|
// packages/workit-core/src/core/skill-manifests.ts
|
|
18533
19634
|
var WORKIT_METHOD_SKILLS = [
|
|
@@ -18559,34 +19660,34 @@ var WORKIT_SKILL_ALIASES = {
|
|
|
18559
19660
|
if (false) {}
|
|
18560
19661
|
|
|
18561
19662
|
// packages/workit-opencode/src/shared/assets.ts
|
|
18562
|
-
import { existsSync as
|
|
18563
|
-
import
|
|
19663
|
+
import { existsSync as existsSync10, realpathSync as realpathSync9 } from "node:fs";
|
|
19664
|
+
import path29 from "node:path";
|
|
18564
19665
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
18565
19666
|
var moduleDir = () => {
|
|
18566
19667
|
try {
|
|
18567
|
-
return
|
|
19668
|
+
return realpathSync9(path29.dirname(fileURLToPath2(import.meta.url)));
|
|
18568
19669
|
} catch {
|
|
18569
|
-
return
|
|
19670
|
+
return path29.dirname(fileURLToPath2(import.meta.url));
|
|
18570
19671
|
}
|
|
18571
19672
|
};
|
|
18572
19673
|
var packageRoot2 = () => {
|
|
18573
19674
|
const dir = moduleDir();
|
|
18574
19675
|
for (const candidate of [
|
|
18575
|
-
|
|
18576
|
-
|
|
18577
|
-
|
|
19676
|
+
path29.resolve(dir, ".."),
|
|
19677
|
+
path29.resolve(dir, "..", ".."),
|
|
19678
|
+
path29.resolve(dir, "..", "..", "..")
|
|
18578
19679
|
]) {
|
|
18579
|
-
if (
|
|
19680
|
+
if (existsSync10(path29.join(candidate, "package.json")))
|
|
18580
19681
|
return candidate;
|
|
18581
19682
|
}
|
|
18582
|
-
return
|
|
19683
|
+
return path29.resolve(dir, "..");
|
|
18583
19684
|
};
|
|
18584
19685
|
var skillsRoot = () => {
|
|
18585
19686
|
const root = packageRoot2();
|
|
18586
|
-
const canonical =
|
|
18587
|
-
if (
|
|
19687
|
+
const canonical = path29.resolve(root, "..", "workit-core", "skills");
|
|
19688
|
+
if (existsSync10(path29.join(root, "src")) && existsSync10(canonical))
|
|
18588
19689
|
return canonical;
|
|
18589
|
-
return
|
|
19690
|
+
return path29.join(root, "assets", "skills");
|
|
18590
19691
|
};
|
|
18591
19692
|
|
|
18592
19693
|
// packages/workit-opencode/src/v2/registry.ts
|
|
@@ -18610,15 +19711,15 @@ var parseFrontmatter = (markdown) => {
|
|
|
18610
19711
|
};
|
|
18611
19712
|
var registerSkills = async (ctx) => {
|
|
18612
19713
|
const skillsDir = skillsRoot();
|
|
18613
|
-
if (!
|
|
19714
|
+
if (!existsSync11(skillsDir))
|
|
18614
19715
|
return new Set;
|
|
18615
19716
|
const listed = await ctx.skill.list().catch(() => ({ data: [] }));
|
|
18616
|
-
const own = (skill) => typeof skill.path === "string" &&
|
|
19717
|
+
const own = (skill) => typeof skill.path === "string" && path30.resolve(skill.path).startsWith(skillsDir + path30.sep);
|
|
18617
19718
|
const userSkills = new Set((listed?.data ?? []).filter((skill) => !own(skill)).map((skill) => skill.id));
|
|
18618
19719
|
const packaged = new Map;
|
|
18619
19720
|
for (const id of WORKIT_METHOD_SKILLS) {
|
|
18620
|
-
const file =
|
|
18621
|
-
if (!userSkills.has(id) &&
|
|
19721
|
+
const file = path30.join(skillsDir, id, "SKILL.md");
|
|
19722
|
+
if (!userSkills.has(id) && existsSync11(file))
|
|
18622
19723
|
packaged.set(id, file);
|
|
18623
19724
|
}
|
|
18624
19725
|
await ctx.skill.transform((editor) => {
|
|
@@ -18664,25 +19765,25 @@ var registerCommands = async (ctx, registeredSkills) => {
|
|
|
18664
19765
|
};
|
|
18665
19766
|
|
|
18666
19767
|
// packages/workit-opencode/src/stale-sources.ts
|
|
18667
|
-
import { existsSync as
|
|
18668
|
-
import
|
|
19768
|
+
import { existsSync as existsSync12, readdirSync as readdirSync5 } from "node:fs";
|
|
19769
|
+
import path31 from "node:path";
|
|
18669
19770
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
18670
19771
|
var typescriptFiles = (directory) => {
|
|
18671
|
-
if (!
|
|
19772
|
+
if (!existsSync12(directory))
|
|
18672
19773
|
return [];
|
|
18673
19774
|
return readdirSync5(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
18674
|
-
const file =
|
|
19775
|
+
const file = path31.join(directory, entry.name);
|
|
18675
19776
|
return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
|
|
18676
19777
|
});
|
|
18677
19778
|
};
|
|
18678
19779
|
var pluginSourceFiles = [
|
|
18679
19780
|
...new Set([
|
|
18680
19781
|
fileURLToPath3(import.meta.url),
|
|
18681
|
-
...typescriptFiles(
|
|
18682
|
-
|
|
18683
|
-
...typescriptFiles(
|
|
19782
|
+
...typescriptFiles(path31.join(packageRoot2(), "src")),
|
|
19783
|
+
path31.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
|
|
19784
|
+
...typescriptFiles(path31.join(packageRoot2(), "..", "workit-core", "src", "core"))
|
|
18684
19785
|
])
|
|
18685
|
-
].filter((file) =>
|
|
19786
|
+
].filter((file) => existsSync12(file)).toSorted();
|
|
18686
19787
|
|
|
18687
19788
|
// packages/workit-opencode/src/v2/plugin.ts
|
|
18688
19789
|
var sessionFacts = async (ctx, sessionID) => {
|
|
@@ -18736,7 +19837,7 @@ var setup = async (ctx) => {
|
|
|
18736
19837
|
});
|
|
18737
19838
|
const contextTool = createContextTool();
|
|
18738
19839
|
const runFamily = (family, input, session, store, workerId) => {
|
|
18739
|
-
const parsed =
|
|
19840
|
+
const parsed = parseAdvertisedOperation(family, input, "opencode");
|
|
18740
19841
|
if (!parsed.ok)
|
|
18741
19842
|
return parsed;
|
|
18742
19843
|
const core = new WorkitCore(store, {
|