@brainervirus/workit-cursor 1.2.1 → 1.3.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/.cursor-plugin/plugin.json +1 -1
- package/commands/wk-babysit.md +1 -1
- package/dist/cursor-session-start.js +913 -170
- package/dist/mcp-server.js +850 -282
- package/dist/workit-hook.js +921 -163
- package/package.json +3 -3
- package/rules/workit-contract.mdc +20 -22
- package/skills/workit-babysit/SKILL.md +20 -26
- package/skills/workit-behavioral-tdd/SKILL.md +0 -5
- package/skills/workit-blast-radius/SKILL.md +0 -5
- package/skills/workit-challenge/SKILL.md +30 -47
- package/skills/workit-debug/SKILL.md +5 -27
- package/skills/workit-deslop/SKILL.md +0 -5
- package/skills/workit-diagram/SKILL.md +0 -5
- package/skills/workit-green-run/SKILL.md +0 -5
- package/skills/workit-handoff/SKILL.md +2 -8
- package/skills/workit-implement/SKILL.md +14 -20
- package/skills/workit-mockup/SKILL.md +0 -5
- package/skills/workit-plan/SKILL.md +30 -63
- package/skills/workit-review/SKILL.md +0 -5
- package/skills/workit-steer/SKILL.md +27 -27
package/dist/mcp-server.js
CHANGED
|
@@ -6622,11 +6622,11 @@ var require_dist = __commonJS(function(exports, module) {
|
|
|
6622
6622
|
|
|
6623
6623
|
// packages/workit-mcp/src/index.ts
|
|
6624
6624
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
6625
|
-
import
|
|
6625
|
+
import path16 from "node:path";
|
|
6626
6626
|
|
|
6627
6627
|
// packages/workit-mcp/src/server.ts
|
|
6628
|
-
import { existsSync as existsSync7, readFileSync as readFileSync8, realpathSync as
|
|
6629
|
-
import
|
|
6628
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, realpathSync as realpathSync8 } from "node:fs";
|
|
6629
|
+
import path15 from "node:path";
|
|
6630
6630
|
|
|
6631
6631
|
// node_modules/@modelcontextprotocol/sdk/node_modules/zod/v4/core/core.js
|
|
6632
6632
|
var NEVER = Object.freeze({
|
|
@@ -12479,8 +12479,8 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
|
12479
12479
|
|
|
12480
12480
|
// packages/workit-core/src/core.ts
|
|
12481
12481
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
12482
|
-
import { existsSync as existsSync5, realpathSync as
|
|
12483
|
-
import
|
|
12482
|
+
import { existsSync as existsSync5, realpathSync as realpathSync7 } from "node:fs";
|
|
12483
|
+
import path11 from "node:path";
|
|
12484
12484
|
|
|
12485
12485
|
// packages/workit-core/src/core/task-contract.ts
|
|
12486
12486
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -17787,6 +17787,17 @@ function _array2(Class, element, params) {
|
|
|
17787
17787
|
...normalizeParams2(params)
|
|
17788
17788
|
});
|
|
17789
17789
|
}
|
|
17790
|
+
function _custom2(Class, fn, _params) {
|
|
17791
|
+
const norm = normalizeParams2(_params);
|
|
17792
|
+
norm.abort ?? (norm.abort = true);
|
|
17793
|
+
const schema = new Class({
|
|
17794
|
+
type: "custom",
|
|
17795
|
+
check: "custom",
|
|
17796
|
+
fn,
|
|
17797
|
+
...norm
|
|
17798
|
+
});
|
|
17799
|
+
return schema;
|
|
17800
|
+
}
|
|
17790
17801
|
function _refine2(Class, fn, _params) {
|
|
17791
17802
|
const schema = new Class({
|
|
17792
17803
|
type: "custom",
|
|
@@ -19940,6 +19951,9 @@ var ZodCustom2 = /* @__PURE__ */ $constructor2("ZodCustom", (inst, def) => {
|
|
|
19940
19951
|
ZodType2.init(inst, def);
|
|
19941
19952
|
inst._zod.processJSONSchema = (ctx, json, params) => customProcessor(inst, ctx, json, params);
|
|
19942
19953
|
});
|
|
19954
|
+
function custom2(fn, _params) {
|
|
19955
|
+
return _custom2(ZodCustom2, fn ?? (() => true), _params);
|
|
19956
|
+
}
|
|
19943
19957
|
function refine2(fn, _params = {}) {
|
|
19944
19958
|
return _refine2(ZodCustom2, fn, _params);
|
|
19945
19959
|
}
|
|
@@ -20373,7 +20387,8 @@ var decisionSchema = object4({
|
|
|
20373
20387
|
consumption: object4({
|
|
20374
20388
|
state: _enum2(["reserved", "consumed", "uncertain"]),
|
|
20375
20389
|
at: utc,
|
|
20376
|
-
actionRef: refSchema
|
|
20390
|
+
actionRef: refSchema,
|
|
20391
|
+
historicalActionId: text.optional()
|
|
20377
20392
|
}).strict().nullable()
|
|
20378
20393
|
}).strict();
|
|
20379
20394
|
var findingSchema = object4({
|
|
@@ -20516,7 +20531,7 @@ var taskSummarySchema = object4({
|
|
|
20516
20531
|
requirements: array3(requirementEvaluationSchema),
|
|
20517
20532
|
writer: workspaceRecordSchema.shape.writer
|
|
20518
20533
|
}).strict();
|
|
20519
|
-
var taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true });
|
|
20534
|
+
var taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true }).extend({ source: provenanceSchema.pick({ host: true, kind: true }) }).strict();
|
|
20520
20535
|
var exportBundleSchema = object4({
|
|
20521
20536
|
schemaVersion: literal3(1),
|
|
20522
20537
|
exportedAt: utc,
|
|
@@ -20541,6 +20556,7 @@ var taskOperations = {
|
|
|
20541
20556
|
list: operation({
|
|
20542
20557
|
action: literal3("list"),
|
|
20543
20558
|
status: _enum2(["open", "closed", "all"]).optional(),
|
|
20559
|
+
query: string5().trim().min(1).max(200).optional(),
|
|
20544
20560
|
limit: number5().int().min(1).max(50).optional()
|
|
20545
20561
|
}),
|
|
20546
20562
|
inspect: operation({
|
|
@@ -20888,6 +20904,7 @@ function requirementId(input) {
|
|
|
20888
20904
|
}
|
|
20889
20905
|
// packages/workit-core/src/core/task-store.ts
|
|
20890
20906
|
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
20907
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
20891
20908
|
import * as fs4 from "node:fs";
|
|
20892
20909
|
import { hostname as hostname2 } from "node:os";
|
|
20893
20910
|
import path4 from "node:path";
|
|
@@ -21523,7 +21540,8 @@ var metadataLockSchema = object4({
|
|
|
21523
21540
|
pid: number5().int().nonnegative().safe(),
|
|
21524
21541
|
processStart: string5().nullable(),
|
|
21525
21542
|
host: string5().min(1),
|
|
21526
|
-
nonce: string5().min(1)
|
|
21543
|
+
nonce: string5().min(1),
|
|
21544
|
+
externalAction: literal3(true).optional()
|
|
21527
21545
|
}).strict();
|
|
21528
21546
|
var now = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
21529
21547
|
var cachedRuntimeVersion = null;
|
|
@@ -21575,6 +21593,28 @@ var sameMetadataLock = (left, right) => {
|
|
|
21575
21593
|
const parsed = metadataLockSchema.safeParse(left);
|
|
21576
21594
|
return parsed.success && canonicalJson(parsed.data) === canonicalJson(right);
|
|
21577
21595
|
};
|
|
21596
|
+
var sameDirectoryIdentity = (left, right) => {
|
|
21597
|
+
if (!path4.isAbsolute(left) || !path4.isAbsolute(right))
|
|
21598
|
+
return false;
|
|
21599
|
+
const normalizedLeft = path4.resolve(left);
|
|
21600
|
+
const normalizedRight = path4.resolve(right);
|
|
21601
|
+
try {
|
|
21602
|
+
const a = fs4.statSync(normalizedLeft, { bigint: true });
|
|
21603
|
+
const b = fs4.statSync(normalizedRight, { bigint: true });
|
|
21604
|
+
if (!a.isDirectory() || !b.isDirectory())
|
|
21605
|
+
return false;
|
|
21606
|
+
if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
|
|
21607
|
+
return true;
|
|
21608
|
+
const canonicalLeft = fs4.realpathSync(normalizedLeft);
|
|
21609
|
+
const canonicalRight = fs4.realpathSync(normalizedRight);
|
|
21610
|
+
if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
|
|
21611
|
+
return true;
|
|
21612
|
+
return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
|
|
21613
|
+
} catch {
|
|
21614
|
+
return false;
|
|
21615
|
+
}
|
|
21616
|
+
};
|
|
21617
|
+
var externalActionLockRoots = new AsyncLocalStorage;
|
|
21578
21618
|
|
|
21579
21619
|
class TaskStore {
|
|
21580
21620
|
root;
|
|
@@ -21635,7 +21675,7 @@ class TaskStore {
|
|
|
21635
21675
|
const item = this.readRecord(this.workspacePath, workspaceRecordSchema);
|
|
21636
21676
|
if (!item.exists)
|
|
21637
21677
|
return success(null, null, null);
|
|
21638
|
-
if (item.result.ok && item.result.data.root !== this.root)
|
|
21678
|
+
if (item.result.ok && item.result.data.root !== this.root && !sameDirectoryIdentity(item.result.data.root, this.root))
|
|
21639
21679
|
return failure("recovery_required", "workspace root binding is invalid", {
|
|
21640
21680
|
path: this.workspacePath
|
|
21641
21681
|
});
|
|
@@ -21726,6 +21766,29 @@ class TaskStore {
|
|
|
21726
21766
|
const current = this.readWorkspace();
|
|
21727
21767
|
if (!current.ok)
|
|
21728
21768
|
return current;
|
|
21769
|
+
const origin = input.task.origin;
|
|
21770
|
+
if (current.data && origin) {
|
|
21771
|
+
if (origin.workspaceId === current.data.id) {
|
|
21772
|
+
const source = this.readTask(origin.taskId);
|
|
21773
|
+
if (source.ok)
|
|
21774
|
+
return success(source.data.revision, current.data.revision, source.data);
|
|
21775
|
+
if (source.code !== "not_found")
|
|
21776
|
+
return source;
|
|
21777
|
+
return failure("not_found", "source task is not present in this shared workspace");
|
|
21778
|
+
}
|
|
21779
|
+
const tasks = this.listTasks();
|
|
21780
|
+
if (!tasks.ok)
|
|
21781
|
+
return tasks;
|
|
21782
|
+
const prior = tasks.data.filter((task) => task.origin?.workspaceId === origin.workspaceId && task.origin?.taskId === origin.taskId);
|
|
21783
|
+
if (prior.length > 1)
|
|
21784
|
+
return failure("recovery_required", `source task has multiple imported mappings: ${prior.map((task) => task.id).join(", ")}`);
|
|
21785
|
+
if (prior.length === 1) {
|
|
21786
|
+
const existing = prior[0];
|
|
21787
|
+
if (existing.origin.exportDigest !== origin.exportDigest)
|
|
21788
|
+
return failure("revision_conflict", "source task export changed; reconcile the mapped destination task before importing", { taskId: existing.id });
|
|
21789
|
+
return success(existing.revision, current.data.revision, existing);
|
|
21790
|
+
}
|
|
21791
|
+
}
|
|
21729
21792
|
if (current.data ? input.expectedWorkspaceRevision !== current.data.revision : input.expectedWorkspaceRevision !== null)
|
|
21730
21793
|
return failure("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", {
|
|
21731
21794
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
@@ -22114,6 +22177,73 @@ class TaskStore {
|
|
|
22114
22177
|
})
|
|
22115
22178
|
};
|
|
22116
22179
|
}
|
|
22180
|
+
externalActionLockOptions() {
|
|
22181
|
+
const options = this.metadataLockOptions();
|
|
22182
|
+
return { ...options, payload: () => ({ ...options.payload(), externalAction: true }) };
|
|
22183
|
+
}
|
|
22184
|
+
async withExternalActionLock(operation, reentrant = false) {
|
|
22185
|
+
const activeRoots = externalActionLockRoots.getStore();
|
|
22186
|
+
if (activeRoots && [...activeRoots].some((root) => root === this.root || sameDirectoryIdentity(root, this.root)))
|
|
22187
|
+
return operation();
|
|
22188
|
+
try {
|
|
22189
|
+
this.initializeMutationStorage();
|
|
22190
|
+
} catch (error) {
|
|
22191
|
+
return failure("storage_error", `unable to initialize store: ${String(error)}`, {
|
|
22192
|
+
path: this.workitDir
|
|
22193
|
+
});
|
|
22194
|
+
}
|
|
22195
|
+
let handle;
|
|
22196
|
+
try {
|
|
22197
|
+
handle = acquireFileLockSync(this.workspacePath, this.externalActionLockOptions());
|
|
22198
|
+
} catch (error) {
|
|
22199
|
+
return this.lockFailure(error);
|
|
22200
|
+
}
|
|
22201
|
+
let result;
|
|
22202
|
+
try {
|
|
22203
|
+
if (!handle.verifyStillHeld())
|
|
22204
|
+
result = failure("recovery_required", "metadata lock was compromised", {
|
|
22205
|
+
path: this.lockPath
|
|
22206
|
+
});
|
|
22207
|
+
else {
|
|
22208
|
+
try {
|
|
22209
|
+
const run = () => operation();
|
|
22210
|
+
result = await (reentrant ? externalActionLockRoots.run(new Set([...activeRoots ?? [], this.root]), run) : run());
|
|
22211
|
+
} catch {
|
|
22212
|
+
result = failure("external_outcome_unknown", "external action outcome is unknown", {
|
|
22213
|
+
outcome: "unknown"
|
|
22214
|
+
});
|
|
22215
|
+
}
|
|
22216
|
+
try {
|
|
22217
|
+
if (!handle.verifyStillHeld())
|
|
22218
|
+
result = failure("external_outcome_unknown", "metadata lock was compromised", {
|
|
22219
|
+
outcome: "unknown",
|
|
22220
|
+
path: this.lockPath
|
|
22221
|
+
});
|
|
22222
|
+
} catch {
|
|
22223
|
+
result = failure("external_outcome_unknown", "metadata lock could not be verified", {
|
|
22224
|
+
outcome: "unknown",
|
|
22225
|
+
path: this.lockPath
|
|
22226
|
+
});
|
|
22227
|
+
}
|
|
22228
|
+
}
|
|
22229
|
+
} catch {
|
|
22230
|
+
result = failure("external_outcome_unknown", "external action lock operation failed", {
|
|
22231
|
+
outcome: "unknown",
|
|
22232
|
+
path: this.lockPath
|
|
22233
|
+
});
|
|
22234
|
+
}
|
|
22235
|
+
try {
|
|
22236
|
+
handle.release();
|
|
22237
|
+
} catch (error) {
|
|
22238
|
+
const released = this.lockFailure(error);
|
|
22239
|
+
const releaseError = released.ok ? "metadata lock release failed" : released.error;
|
|
22240
|
+
return result.ok ? failure("external_outcome_unknown", "external action metadata lock release is uncertain", {
|
|
22241
|
+
outcome: "unknown",
|
|
22242
|
+
path: this.lockPath
|
|
22243
|
+
}) : failure("recovery_required", `${result.error}; ${releaseError}`, result.details);
|
|
22244
|
+
}
|
|
22245
|
+
return result;
|
|
22246
|
+
}
|
|
22117
22247
|
recoveryLockOptions(observed, evidence, gate) {
|
|
22118
22248
|
const options = this.metadataLockOptions();
|
|
22119
22249
|
options.staleRecovery = "remove-if-unchanged";
|
|
@@ -22183,6 +22313,14 @@ class TaskStore {
|
|
|
22183
22313
|
lockFailure(error) {
|
|
22184
22314
|
const value = error;
|
|
22185
22315
|
const code = typeof value?.code === "string" ? value.code : "";
|
|
22316
|
+
if (code === "EEXIST" || code === "file_lock_timeout") {
|
|
22317
|
+
const lock = this.readLockSnapshot();
|
|
22318
|
+
if (lock.ok && lock.data?.data.externalAction)
|
|
22319
|
+
return failure("writer_conflict", "workspace is reserved by a managed external action", {
|
|
22320
|
+
outcome: "not_started",
|
|
22321
|
+
path: this.lockPath
|
|
22322
|
+
});
|
|
22323
|
+
}
|
|
22186
22324
|
const recovery = code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || code === "metadata_lock_invalid" || code === "not-file" || /metadata lock|file lock|reclaim/i.test(String(value?.message ?? error));
|
|
22187
22325
|
return failure(recovery ? "recovery_required" : "storage_error", `metadata lock operation failed: ${String(error)}`, { path: this.lockPath });
|
|
22188
22326
|
}
|
|
@@ -22450,21 +22588,23 @@ function assertProductWriteAllowed(input) {
|
|
|
22450
22588
|
}
|
|
22451
22589
|
|
|
22452
22590
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
22453
|
-
import { createHash as
|
|
22591
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
22454
22592
|
import * as fs7 from "node:fs";
|
|
22455
22593
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
22456
|
-
import
|
|
22594
|
+
import path10 from "node:path";
|
|
22457
22595
|
|
|
22458
22596
|
// packages/workit-core/src/core/authority.ts
|
|
22459
|
-
import { createHash as
|
|
22597
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
22460
22598
|
import * as fs6 from "node:fs";
|
|
22461
|
-
import
|
|
22599
|
+
import path9 from "node:path";
|
|
22462
22600
|
|
|
22463
22601
|
// packages/workit-core/src/core/auto-approval.ts
|
|
22464
|
-
import { createHash as
|
|
22465
|
-
import { readFileSync as readFileSync4 } from "node:fs";
|
|
22602
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
22603
|
+
import { readFileSync as readFileSync4, realpathSync as realpathSync3 } from "node:fs";
|
|
22604
|
+
import path8 from "node:path";
|
|
22466
22605
|
|
|
22467
22606
|
// packages/workit-core/src/core/workspaces.ts
|
|
22607
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
22468
22608
|
import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
22469
22609
|
import path6 from "node:path";
|
|
22470
22610
|
|
|
@@ -22482,7 +22622,7 @@ import os from "node:os";
|
|
|
22482
22622
|
import path5 from "node:path";
|
|
22483
22623
|
|
|
22484
22624
|
// packages/workit-core/src/core/boundary.ts
|
|
22485
|
-
import { statSync } from "node:fs";
|
|
22625
|
+
import { statSync as statSync2 } from "node:fs";
|
|
22486
22626
|
var EVENT = {
|
|
22487
22627
|
initialization: "initialization",
|
|
22488
22628
|
provenance: "provenance",
|
|
@@ -22510,7 +22650,7 @@ var changedSourcesSinceLoad = (marker) => {
|
|
|
22510
22650
|
const changed = [];
|
|
22511
22651
|
for (const file of marker.files) {
|
|
22512
22652
|
try {
|
|
22513
|
-
if (
|
|
22653
|
+
if (statSync2(file).mtimeMs > marker.loadedAt)
|
|
22514
22654
|
changed.push(file);
|
|
22515
22655
|
} catch {}
|
|
22516
22656
|
}
|
|
@@ -22669,33 +22809,197 @@ var COMMIT_PRESETS = [
|
|
|
22669
22809
|
"custom",
|
|
22670
22810
|
"auto"
|
|
22671
22811
|
];
|
|
22812
|
+
var resolveCommitPolicy = (config, workspace) => {
|
|
22813
|
+
const wp = workspace?.commitPolicy ?? {};
|
|
22814
|
+
if (COMMIT_PRESETS.includes(String(wp.preset ?? ""))) {
|
|
22815
|
+
return {
|
|
22816
|
+
preset: wp.preset,
|
|
22817
|
+
...typeof wp.pattern === "string" ? { pattern: wp.pattern } : {}
|
|
22818
|
+
};
|
|
22819
|
+
}
|
|
22820
|
+
return config.commitPolicy ?? { preset: "conventional" };
|
|
22821
|
+
};
|
|
22672
22822
|
|
|
22673
22823
|
// packages/workit-core/src/core/workspaces.ts
|
|
22824
|
+
var nonBlank = string5().refine((value) => value.trim().length > 0, "must not be blank");
|
|
22825
|
+
var branchPreset = custom2((value) => typeof value === "string" && Object.hasOwn(PRESETS, value), "unsupported branch preset");
|
|
22826
|
+
var commitPreset = custom2((value) => typeof value === "string" && COMMIT_PRESETS.includes(value), "unsupported commit preset");
|
|
22827
|
+
var workspaceBranchPolicySchema = object4({
|
|
22828
|
+
preset: branchPreset,
|
|
22829
|
+
developBranch: nonBlank.optional(),
|
|
22830
|
+
prefixes: object4({
|
|
22831
|
+
feature: nonBlank,
|
|
22832
|
+
bugfix: nonBlank,
|
|
22833
|
+
release: nonBlank,
|
|
22834
|
+
hotfix: nonBlank
|
|
22835
|
+
}).passthrough().optional(),
|
|
22836
|
+
allowed: array3(nonBlank).optional(),
|
|
22837
|
+
protected: array3(nonBlank).optional(),
|
|
22838
|
+
integration: _enum2(["pr", "merge"]).optional()
|
|
22839
|
+
}).strict();
|
|
22840
|
+
var workspaceCommitPolicySchema = object4({ preset: commitPreset, pattern: string5().optional() }).strict();
|
|
22841
|
+
var workspaceProfileSchema = object4({
|
|
22842
|
+
branchPolicy: workspaceBranchPolicySchema.optional(),
|
|
22843
|
+
commitPolicy: workspaceCommitPolicySchema.optional()
|
|
22844
|
+
}).strict().refine((profile) => profile.branchPolicy !== undefined || profile.commitPolicy !== undefined, {
|
|
22845
|
+
message: "a profile must define branchPolicy or commitPolicy"
|
|
22846
|
+
});
|
|
22847
|
+
var versionSourceSchema = discriminatedUnion2("kind", [
|
|
22848
|
+
object4({
|
|
22849
|
+
kind: literal3("package-json"),
|
|
22850
|
+
path: nonBlank,
|
|
22851
|
+
field: nonBlank
|
|
22852
|
+
}).strict(),
|
|
22853
|
+
object4({ kind: literal3("git-tag") }).strict(),
|
|
22854
|
+
object4({ kind: literal3("manual") }).strict()
|
|
22855
|
+
]);
|
|
22856
|
+
var releaseTrackSchema = object4({
|
|
22857
|
+
strategy: branchPreset,
|
|
22858
|
+
productionBranch: nonBlank,
|
|
22859
|
+
integrationBranch: nonBlank,
|
|
22860
|
+
naming: object4({ feature: nonBlank, release: nonBlank, hotfix: nonBlank }).strict(),
|
|
22861
|
+
baseBranch: nonBlank,
|
|
22862
|
+
mergeBackBranches: array3(nonBlank),
|
|
22863
|
+
pullRequestTarget: nonBlank,
|
|
22864
|
+
tagNamespace: string5(),
|
|
22865
|
+
versionSource: versionSourceSchema,
|
|
22866
|
+
requiredChecks: array3(nonBlank)
|
|
22867
|
+
}).strict().superRefine((track, context) => {
|
|
22868
|
+
for (const [field, values] of [
|
|
22869
|
+
["mergeBackBranches", track.mergeBackBranches],
|
|
22870
|
+
["requiredChecks", track.requiredChecks]
|
|
22871
|
+
]) {
|
|
22872
|
+
if (new Set(values).size !== values.length) {
|
|
22873
|
+
context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
|
|
22874
|
+
}
|
|
22875
|
+
}
|
|
22876
|
+
if (track.versionSource.kind === "package-json" && (path6.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
|
|
22877
|
+
context.addIssue({
|
|
22878
|
+
code: "custom",
|
|
22879
|
+
path: ["versionSource", "path"],
|
|
22880
|
+
message: "must stay inside the repository"
|
|
22881
|
+
});
|
|
22882
|
+
}
|
|
22883
|
+
});
|
|
22884
|
+
var workspaceConfigSchema = object4({
|
|
22885
|
+
name: nonBlank,
|
|
22886
|
+
glob: nonBlank,
|
|
22887
|
+
vcs: object4({
|
|
22888
|
+
provider: _enum2(["gitlab", "github"]),
|
|
22889
|
+
defaultTargetBranch: nonBlank.optional(),
|
|
22890
|
+
tokenFile: nonBlank.optional(),
|
|
22891
|
+
account: nonBlank.optional()
|
|
22892
|
+
}).passthrough().optional(),
|
|
22893
|
+
autoApprove: union3([boolean5(), array3(nonBlank)]).optional(),
|
|
22894
|
+
youtrack: object4({ baseUrl: nonBlank.optional(), link_issues: boolean5().optional() }).passthrough().optional(),
|
|
22895
|
+
issues: object4({ provider: literal3("github").optional(), link_on_pr: boolean5().optional() }).passthrough().optional(),
|
|
22896
|
+
branchPolicy: workspaceBranchPolicySchema.optional(),
|
|
22897
|
+
commitPolicy: workspaceCommitPolicySchema.optional(),
|
|
22898
|
+
defaultProfile: nonBlank.optional(),
|
|
22899
|
+
profiles: record3(nonBlank, workspaceProfileSchema).optional(),
|
|
22900
|
+
releaseTracks: record3(nonBlank, releaseTrackSchema).optional()
|
|
22901
|
+
}).passthrough().superRefine((workspace, context) => {
|
|
22902
|
+
if (workspace.youtrack && workspace.vcs?.provider !== "gitlab") {
|
|
22903
|
+
context.addIssue({
|
|
22904
|
+
code: "custom",
|
|
22905
|
+
path: ["youtrack"],
|
|
22906
|
+
message: `YouTrack issue linking requires the gitlab provider, got ${workspace.vcs?.provider ?? "unset"}`
|
|
22907
|
+
});
|
|
22908
|
+
}
|
|
22909
|
+
if (workspace.issues && workspace.vcs?.provider !== "github") {
|
|
22910
|
+
context.addIssue({
|
|
22911
|
+
code: "custom",
|
|
22912
|
+
path: ["issues"],
|
|
22913
|
+
message: `GitHub issue linking requires the github provider, got ${workspace.vcs?.provider ?? "unset"}`
|
|
22914
|
+
});
|
|
22915
|
+
}
|
|
22916
|
+
if (workspace.defaultProfile !== undefined && !Object.hasOwn(workspace.profiles ?? {}, workspace.defaultProfile)) {
|
|
22917
|
+
context.addIssue({
|
|
22918
|
+
code: "custom",
|
|
22919
|
+
path: ["defaultProfile"],
|
|
22920
|
+
message: `does not name a configured profile`
|
|
22921
|
+
});
|
|
22922
|
+
}
|
|
22923
|
+
});
|
|
22924
|
+
var workspacesFileSchema = object4({ workspaces: array3(workspaceConfigSchema).optional() }).passthrough().superRefine((file, context) => {
|
|
22925
|
+
const names = new Set;
|
|
22926
|
+
for (const [index, workspace] of (file.workspaces ?? []).entries()) {
|
|
22927
|
+
if (names.has(workspace.name)) {
|
|
22928
|
+
context.addIssue({
|
|
22929
|
+
code: "custom",
|
|
22930
|
+
path: ["workspaces", index, "name"],
|
|
22931
|
+
message: `duplicate workspace name ${JSON.stringify(workspace.name)}`
|
|
22932
|
+
});
|
|
22933
|
+
}
|
|
22934
|
+
names.add(workspace.name);
|
|
22935
|
+
}
|
|
22936
|
+
});
|
|
22937
|
+
var workspacesRevision = (contents) => createHash3("sha256").update(contents ?? "<missing>").digest("hex");
|
|
22938
|
+
var validateWorkspacesDocument = (parsed, file) => {
|
|
22939
|
+
if (!isConfigObject(parsed)) {
|
|
22940
|
+
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
22941
|
+
}
|
|
22942
|
+
const validated = workspacesFileSchema.safeParse(parsed, { reportInput: true });
|
|
22943
|
+
if (!validated.success) {
|
|
22944
|
+
const detail = validated.error.issues.map((issue) => {
|
|
22945
|
+
const field = String(issue.path[issue.path.length - 1] ?? "");
|
|
22946
|
+
const attempted = ["provider", "preset", "strategy"].includes(field) && typeof issue.input === "string" ? ` (${JSON.stringify(issue.input)})` : "";
|
|
22947
|
+
return `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}${attempted}`;
|
|
22948
|
+
}).join("; ");
|
|
22949
|
+
return {
|
|
22950
|
+
status: "invalid",
|
|
22951
|
+
path: file,
|
|
22952
|
+
entries: [],
|
|
22953
|
+
error: `${file} has invalid workspace configuration: ${detail}`
|
|
22954
|
+
};
|
|
22955
|
+
}
|
|
22956
|
+
for (const [index, workspace] of (validated.data.workspaces ?? []).entries()) {
|
|
22957
|
+
const glob = validateWorkspaceGlob(workspace.glob);
|
|
22958
|
+
if (!glob.ok) {
|
|
22959
|
+
return {
|
|
22960
|
+
status: "invalid",
|
|
22961
|
+
path: file,
|
|
22962
|
+
entries: [],
|
|
22963
|
+
error: `${file} workspaces.${index}.glob: ${glob.error}`
|
|
22964
|
+
};
|
|
22965
|
+
}
|
|
22966
|
+
}
|
|
22967
|
+
return {
|
|
22968
|
+
status: "valid",
|
|
22969
|
+
path: file,
|
|
22970
|
+
entries: validated.data.workspaces ?? [],
|
|
22971
|
+
document: parsed
|
|
22972
|
+
};
|
|
22973
|
+
};
|
|
22674
22974
|
var readWorkspacesResult = (dir = configDir()) => {
|
|
22675
22975
|
const file = path6.join(dir, "workspaces.json");
|
|
22676
22976
|
let raw;
|
|
22677
22977
|
try {
|
|
22678
22978
|
raw = readFileSync3(file, "utf8");
|
|
22679
|
-
} catch {
|
|
22680
|
-
|
|
22979
|
+
} catch (error) {
|
|
22980
|
+
if (error.code === "ENOENT")
|
|
22981
|
+
return { status: "missing", path: file, entries: [], revision: workspacesRevision(null) };
|
|
22982
|
+
return {
|
|
22983
|
+
status: "malformed",
|
|
22984
|
+
path: file,
|
|
22985
|
+
entries: [],
|
|
22986
|
+
error: `${file} could not be read: ${error instanceof Error ? error.message : String(error)}`
|
|
22987
|
+
};
|
|
22681
22988
|
}
|
|
22682
22989
|
let parsed;
|
|
22683
22990
|
try {
|
|
22684
22991
|
parsed = JSON.parse(raw);
|
|
22685
22992
|
} catch {
|
|
22686
|
-
return {
|
|
22687
|
-
|
|
22688
|
-
|
|
22689
|
-
|
|
22993
|
+
return {
|
|
22994
|
+
status: "malformed",
|
|
22995
|
+
path: file,
|
|
22996
|
+
entries: [],
|
|
22997
|
+
revision: workspacesRevision(raw),
|
|
22998
|
+
error: `${file} is not valid JSON`
|
|
22999
|
+
};
|
|
22690
23000
|
}
|
|
22691
|
-
|
|
22692
|
-
return {
|
|
22693
|
-
status: "valid",
|
|
22694
|
-
path: file,
|
|
22695
|
-
entries: Array.isArray(list) ? list : []
|
|
22696
|
-
};
|
|
23001
|
+
return { ...validateWorkspacesDocument(parsed, file), revision: workspacesRevision(raw) };
|
|
22697
23002
|
};
|
|
22698
|
-
var loadWorkspacesFrom = (dir) => readWorkspacesResult(dir).entries;
|
|
22699
23003
|
var globToRegExp = (glob) => {
|
|
22700
23004
|
let out = "";
|
|
22701
23005
|
for (let i = 0;i < glob.length; i++) {
|
|
@@ -22730,6 +23034,30 @@ var globToRegExp = (glob) => {
|
|
|
22730
23034
|
return new RegExp(`^${out}$`);
|
|
22731
23035
|
};
|
|
22732
23036
|
var matchWorkspace = (glob, target) => globToRegExp(glob.replaceAll("\\", "/")).test(target.replaceAll("\\", "/"));
|
|
23037
|
+
var UNSUPPORTED_GLOB = /[?[\]{}]/;
|
|
23038
|
+
var UNSUPPORTED_EXTGLOB = /[@+*]\(|!/;
|
|
23039
|
+
var validateWorkspaceGlob = (glob) => {
|
|
23040
|
+
const trimmed = glob.trim();
|
|
23041
|
+
if (!trimmed)
|
|
23042
|
+
return { ok: false, error: "workspace pattern is required" };
|
|
23043
|
+
const m = UNSUPPORTED_GLOB.exec(trimmed);
|
|
23044
|
+
if (m) {
|
|
23045
|
+
return {
|
|
23046
|
+
ok: false,
|
|
23047
|
+
error: `unsupported glob character ${JSON.stringify(m[0])} in workspace pattern ${JSON.stringify(glob)} — the matcher supports * and ** only (e.g. /work/**)`
|
|
23048
|
+
};
|
|
23049
|
+
}
|
|
23050
|
+
const ext = UNSUPPORTED_EXTGLOB.exec(trimmed);
|
|
23051
|
+
if (ext) {
|
|
23052
|
+
const token = ext[0];
|
|
23053
|
+
const kind = token === "!" ? "negation (!)" : `extglob (${token})`;
|
|
23054
|
+
return {
|
|
23055
|
+
ok: false,
|
|
23056
|
+
error: `unsupported glob ${kind} in workspace pattern ${JSON.stringify(glob)} — the matcher supports * and ** only (e.g. /work/**)`
|
|
23057
|
+
};
|
|
23058
|
+
}
|
|
23059
|
+
return { ok: true };
|
|
23060
|
+
};
|
|
22733
23061
|
var realpathOf = (p) => {
|
|
22734
23062
|
const real = realpathSync2.native ?? realpathSync2;
|
|
22735
23063
|
try {
|
|
@@ -22747,29 +23075,137 @@ var canonicalGlob = (glob) => {
|
|
|
22747
23075
|
const real = realpathOf(prefix.replace(/\/+$/, "") || "/");
|
|
22748
23076
|
return real + (prefix.endsWith("/") ? "/" : "") + rest;
|
|
22749
23077
|
};
|
|
22750
|
-
var resolveWorkspaceFrom = (cwd, dir) => {
|
|
23078
|
+
var resolveWorkspaceFrom = (cwd, dir, workspaceName) => {
|
|
23079
|
+
const result = readWorkspacesResult(dir);
|
|
23080
|
+
if (result.status === "malformed" || result.status === "invalid")
|
|
23081
|
+
throw new Error(result.error);
|
|
22751
23082
|
const targets = [cwd, realpathOf(cwd)].map((p) => p.replaceAll("\\", "/"));
|
|
22752
|
-
|
|
22753
|
-
|
|
22754
|
-
continue;
|
|
22755
|
-
const ws = entry;
|
|
22756
|
-
if (typeof ws.glob !== "string" || !ws.glob)
|
|
22757
|
-
continue;
|
|
23083
|
+
const matches = [];
|
|
23084
|
+
for (const ws of result.entries) {
|
|
22758
23085
|
const glob = ws.glob.replaceAll("\\", "/");
|
|
22759
23086
|
const canonical = canonicalGlob(glob);
|
|
23087
|
+
let matched = false;
|
|
22760
23088
|
for (const target of targets) {
|
|
22761
|
-
if (matchWorkspace(glob, target))
|
|
22762
|
-
|
|
22763
|
-
|
|
22764
|
-
|
|
23089
|
+
if (matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target)) {
|
|
23090
|
+
matched = true;
|
|
23091
|
+
break;
|
|
23092
|
+
}
|
|
22765
23093
|
}
|
|
23094
|
+
if (matched)
|
|
23095
|
+
matches.push(ws);
|
|
22766
23096
|
}
|
|
22767
|
-
|
|
23097
|
+
if (workspaceName !== undefined) {
|
|
23098
|
+
const selected = matches.find((ws) => ws.name === workspaceName);
|
|
23099
|
+
if (!selected && matches.length > 0) {
|
|
23100
|
+
const matchingNames = matches.map((ws) => ws.name);
|
|
23101
|
+
throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matchingNames.join(", ")}`);
|
|
23102
|
+
}
|
|
23103
|
+
if (selected)
|
|
23104
|
+
return selected;
|
|
23105
|
+
throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
|
|
23106
|
+
}
|
|
23107
|
+
if (matches.length > 1) {
|
|
23108
|
+
throw new Error(`ambiguous workspace for ${cwd}; choose one of: ${matches.map((ws) => ws.name).join(", ")}`);
|
|
23109
|
+
}
|
|
23110
|
+
return matches[0] ?? null;
|
|
23111
|
+
};
|
|
23112
|
+
var runtimeWorkspacePolicySchema = (kind) => {
|
|
23113
|
+
const key = kind === "branch" ? "branchPolicy" : kind === "commit" ? "commitPolicy" : "vcs";
|
|
23114
|
+
const policySchema = kind === "branch" ? workspaceBranchPolicySchema : kind === "commit" ? workspaceCommitPolicySchema : object4({
|
|
23115
|
+
provider: _enum2(["gitlab", "github"]),
|
|
23116
|
+
defaultTargetBranch: nonBlank.optional(),
|
|
23117
|
+
account: nonBlank.optional()
|
|
23118
|
+
}).passthrough();
|
|
23119
|
+
const profileSchema = object4({ [key]: policySchema.optional() }).passthrough();
|
|
23120
|
+
const entrySchema = object4({
|
|
23121
|
+
name: nonBlank,
|
|
23122
|
+
glob: nonBlank,
|
|
23123
|
+
[key]: policySchema.optional(),
|
|
23124
|
+
defaultProfile: nonBlank.optional(),
|
|
23125
|
+
profiles: record3(nonBlank, profileSchema).optional()
|
|
23126
|
+
}).passthrough();
|
|
23127
|
+
return entrySchema;
|
|
23128
|
+
};
|
|
23129
|
+
var resolveRuntimeWorkspaceVcs = (cwd) => resolveRuntimeWorkspaceCandidate(cwd, "vcs");
|
|
23130
|
+
var runtimeWorkspaceIndexSchema = object4({
|
|
23131
|
+
workspaces: array3(object4({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
23132
|
+
}).passthrough();
|
|
23133
|
+
var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
23134
|
+
const file = path6.join(configDir(), "workspaces.json");
|
|
23135
|
+
let raw;
|
|
23136
|
+
try {
|
|
23137
|
+
raw = readFileSync3(file, "utf8");
|
|
23138
|
+
} catch (error) {
|
|
23139
|
+
if (error.code === "ENOENT")
|
|
23140
|
+
return null;
|
|
23141
|
+
throw new Error(`${file} could not be read: ${error instanceof Error ? error.message : String(error)}`);
|
|
23142
|
+
}
|
|
23143
|
+
let parsed;
|
|
23144
|
+
try {
|
|
23145
|
+
parsed = JSON.parse(raw);
|
|
23146
|
+
} catch {
|
|
23147
|
+
throw new Error(`${file} is not valid JSON`);
|
|
23148
|
+
}
|
|
23149
|
+
const validated = runtimeWorkspaceIndexSchema.safeParse(parsed);
|
|
23150
|
+
if (!validated.success) {
|
|
23151
|
+
const detail = validated.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}`).join("; ");
|
|
23152
|
+
throw new Error(`${file} has invalid workspace matching data: ${detail}`);
|
|
23153
|
+
}
|
|
23154
|
+
const entries = validated.data.workspaces;
|
|
23155
|
+
for (const entry of entries ?? []) {
|
|
23156
|
+
const glob = validateWorkspaceGlob(entry.glob);
|
|
23157
|
+
if (!glob.ok)
|
|
23158
|
+
throw new Error(`${file} workspace ${entry.name}: ${glob.error}`);
|
|
23159
|
+
}
|
|
23160
|
+
const targets = [cwd, realpathOf(cwd)].map((value) => value.replaceAll("\\", "/"));
|
|
23161
|
+
const matches = (entries ?? []).filter((entry) => {
|
|
23162
|
+
const glob = entry.glob.replaceAll("\\", "/");
|
|
23163
|
+
const canonical = canonicalGlob(glob);
|
|
23164
|
+
return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
|
|
23165
|
+
});
|
|
23166
|
+
const workspaceName = process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined;
|
|
23167
|
+
let selected;
|
|
23168
|
+
if (workspaceName) {
|
|
23169
|
+
const named = matches.filter((entry) => entry.name === workspaceName);
|
|
23170
|
+
if (named.length === 1)
|
|
23171
|
+
selected = named[0];
|
|
23172
|
+
else if (named.length > 1)
|
|
23173
|
+
throw new Error(`ambiguous workspace ${JSON.stringify(workspaceName)} for ${cwd}`);
|
|
23174
|
+
else if (matches.length)
|
|
23175
|
+
throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matches.map((entry) => entry.name).join(", ")}`);
|
|
23176
|
+
else
|
|
23177
|
+
throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
|
|
23178
|
+
} else if (matches.length > 1)
|
|
23179
|
+
throw new Error(`ambiguous workspace for ${cwd}; choose one of: ${matches.map((entry) => entry.name).join(", ")}`);
|
|
23180
|
+
else
|
|
23181
|
+
selected = matches[0];
|
|
23182
|
+
if (!selected)
|
|
23183
|
+
return null;
|
|
23184
|
+
const policy = runtimeWorkspacePolicySchema(kind).safeParse(selected);
|
|
23185
|
+
if (!policy.success) {
|
|
23186
|
+
const detail = policy.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspace"}: ${issue.message}`).join("; ");
|
|
23187
|
+
throw new Error(`${file} has invalid ${kind} policy configuration: ${detail}`);
|
|
23188
|
+
}
|
|
23189
|
+
return policy.data;
|
|
23190
|
+
};
|
|
23191
|
+
var resolveRuntimeWorkspacePolicy = (cwd, kind, requestedProfile) => {
|
|
23192
|
+
const candidate = resolveRuntimeWorkspaceCandidate(cwd, kind);
|
|
23193
|
+
if (!candidate)
|
|
23194
|
+
return { policy: undefined, source: "user-default" };
|
|
23195
|
+
const key = kind === "branch" ? "branchPolicy" : "commitPolicy";
|
|
23196
|
+
const profileName = requestedProfile?.trim() || candidate.defaultProfile;
|
|
23197
|
+
if (candidate.defaultProfile && !Object.hasOwn(candidate.profiles ?? {}, candidate.defaultProfile))
|
|
23198
|
+
throw new Error(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(candidate.defaultProfile)}`);
|
|
23199
|
+
const profile = profileName ? candidate.profiles?.[profileName] : undefined;
|
|
23200
|
+
if (profileName && !profile)
|
|
23201
|
+
throw new Error(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(profileName)}`);
|
|
23202
|
+
const profilePolicy = profile?.[key];
|
|
23203
|
+
const workspacePolicy = candidate[key];
|
|
23204
|
+
return {
|
|
23205
|
+
policy: profilePolicy ?? workspacePolicy,
|
|
23206
|
+
source: profilePolicy ? `profile:${profileName}` : workspacePolicy ? `workspace:${candidate.name}` : "user-default"
|
|
23207
|
+
};
|
|
22768
23208
|
};
|
|
22769
|
-
var resolveWorkspace = (cwd) => resolveWorkspaceFrom(cwd, configDir());
|
|
22770
|
-
|
|
22771
|
-
// packages/workit-core/src/core/branch.ts
|
|
22772
|
-
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
22773
23209
|
|
|
22774
23210
|
// packages/workit-core/src/core/git.ts
|
|
22775
23211
|
import { execFileSync } from "node:child_process";
|
|
@@ -22843,7 +23279,6 @@ var gitContext = (workspaceRoot, paths = []) => {
|
|
|
22843
23279
|
import fs5 from "node:fs";
|
|
22844
23280
|
import path7 from "node:path";
|
|
22845
23281
|
import { spawnSync } from "node:child_process";
|
|
22846
|
-
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
22847
23282
|
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path7.join(configDir(), "vcs.json");
|
|
22848
23283
|
var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
22849
23284
|
var remoteProvider = (cwd) => {
|
|
@@ -22857,6 +23292,79 @@ var remoteProvider = (cwd) => {
|
|
|
22857
23292
|
return "gitlab";
|
|
22858
23293
|
return null;
|
|
22859
23294
|
};
|
|
23295
|
+
var remoteEndpoint = (remote) => {
|
|
23296
|
+
const raw = remote.trim();
|
|
23297
|
+
if (!raw)
|
|
23298
|
+
return null;
|
|
23299
|
+
const scp = /^(?:[^@\s]+@)?([^:]+):(.+)$/u.exec(raw);
|
|
23300
|
+
if (scp && !raw.includes("://"))
|
|
23301
|
+
return {
|
|
23302
|
+
host: scp[1].toLowerCase(),
|
|
23303
|
+
path: scp[2].replace(/^\//u, "").replace(/\.git$/u, ""),
|
|
23304
|
+
protocol: "ssh:",
|
|
23305
|
+
port: ""
|
|
23306
|
+
};
|
|
23307
|
+
try {
|
|
23308
|
+
const url = new URL(raw);
|
|
23309
|
+
return {
|
|
23310
|
+
host: url.hostname.toLowerCase(),
|
|
23311
|
+
path: url.pathname.replace(/^\//u, "").replace(/\.git$/u, "").replace(/\/$/u, ""),
|
|
23312
|
+
protocol: url.protocol,
|
|
23313
|
+
port: url.port
|
|
23314
|
+
};
|
|
23315
|
+
} catch {
|
|
23316
|
+
return null;
|
|
23317
|
+
}
|
|
23318
|
+
};
|
|
23319
|
+
var configuredSshEndpoint = (host) => {
|
|
23320
|
+
if (host.startsWith("-"))
|
|
23321
|
+
return null;
|
|
23322
|
+
const resolved = spawnSync("ssh", ["-G", host], {
|
|
23323
|
+
encoding: "utf8",
|
|
23324
|
+
timeout: 5000,
|
|
23325
|
+
maxBuffer: 1024 * 1024,
|
|
23326
|
+
env: { ...process.env, PATH: process.env.PATH ?? "" }
|
|
23327
|
+
});
|
|
23328
|
+
if (resolved.status !== 0)
|
|
23329
|
+
return null;
|
|
23330
|
+
const output = resolved.stdout ?? "";
|
|
23331
|
+
const hostname = /^hostname\s+(\S+)/imu.exec(output)?.[1]?.toLowerCase();
|
|
23332
|
+
if (!hostname)
|
|
23333
|
+
return null;
|
|
23334
|
+
return { host: hostname, port: /^port\s+(\d+)/imu.exec(output)?.[1] ?? "22" };
|
|
23335
|
+
};
|
|
23336
|
+
var sshApiHostAliases = {
|
|
23337
|
+
"github.com": [{ host: "ssh.github.com", port: "443" }],
|
|
23338
|
+
"gitlab.com": [{ host: "altssh.gitlab.com", port: "443" }]
|
|
23339
|
+
};
|
|
23340
|
+
var hostingApiHostMatches = (remote, apiHost, resolveSshEndpoint = configuredSshEndpoint) => {
|
|
23341
|
+
const endpoint = remoteEndpoint(remote);
|
|
23342
|
+
if (!endpoint)
|
|
23343
|
+
return false;
|
|
23344
|
+
let expected;
|
|
23345
|
+
try {
|
|
23346
|
+
const url = new URL(`https://${apiHost}`);
|
|
23347
|
+
if (url.pathname !== "/" || url.search || url.hash || url.username || url.password)
|
|
23348
|
+
return false;
|
|
23349
|
+
expected = {
|
|
23350
|
+
host: url.hostname.toLowerCase().replace(/\.$/u, ""),
|
|
23351
|
+
port: url.port
|
|
23352
|
+
};
|
|
23353
|
+
} catch {
|
|
23354
|
+
return false;
|
|
23355
|
+
}
|
|
23356
|
+
if (endpoint.protocol !== "ssh:") {
|
|
23357
|
+
return endpoint.host.replace(/\.$/u, "") === expected.host && endpoint.port === expected.port;
|
|
23358
|
+
}
|
|
23359
|
+
const resolved = resolveSshEndpoint(endpoint.host);
|
|
23360
|
+
if (!resolved)
|
|
23361
|
+
return false;
|
|
23362
|
+
const hostname = resolved.host.toLowerCase().replace(/\.$/u, "");
|
|
23363
|
+
const port = endpoint.port || resolved.port;
|
|
23364
|
+
if (hostname === expected.host)
|
|
23365
|
+
return port === (expected.port || "22");
|
|
23366
|
+
return Boolean(sshApiHostAliases[expected.host]?.some((alias) => alias.host === hostname && alias.port === port && (!expected.port || expected.port === port)));
|
|
23367
|
+
};
|
|
22860
23368
|
var readVcsConfig = () => {
|
|
22861
23369
|
const file = vcsConfigPath();
|
|
22862
23370
|
let raw;
|
|
@@ -22877,7 +23385,7 @@ var readVcsConfig = () => {
|
|
|
22877
23385
|
return { status: "malformed", path: file, config: {}, error: `${file} is not a JSON object` };
|
|
22878
23386
|
};
|
|
22879
23387
|
function vcsConfig(mode, cwd) {
|
|
22880
|
-
const ws =
|
|
23388
|
+
const ws = resolveRuntimeWorkspaceVcs(vcsCwd(cwd));
|
|
22881
23389
|
const wsVcs = ws?.vcs ?? {};
|
|
22882
23390
|
const wsYt = ws?.youtrack ?? {};
|
|
22883
23391
|
const wsIssues = ws?.issues ?? {};
|
|
@@ -22890,8 +23398,8 @@ function vcsConfig(mode, cwd) {
|
|
|
22890
23398
|
const hasWorkspacePolicy = typeof wp.preset === "string" && Object.hasOwn(PRESETS, wp.preset);
|
|
22891
23399
|
const policyDefault = resolveBranchPolicyFor(root).defaultTargetBranch;
|
|
22892
23400
|
const defaultTarget = String(wsVcs.defaultTargetBranch ?? (hasWorkspacePolicy ? policyDefault : cfg.defaultTargetBranch ?? policyDefault) ?? "develop");
|
|
22893
|
-
const linkIssues = typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
|
|
22894
|
-
const youtrackBaseUrl = typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
|
|
23401
|
+
const linkIssues = provider === "gitlab" && typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
|
|
23402
|
+
const youtrackBaseUrl = provider === "gitlab" && typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
|
|
22895
23403
|
let issuesProvider = null;
|
|
22896
23404
|
let linkOnPr = null;
|
|
22897
23405
|
if (provider === "github" && typeof wsIssues.provider === "string" && wsIssues.provider.toLowerCase() === "github") {
|
|
@@ -22915,9 +23423,8 @@ function vcsConfig(mode, cwd) {
|
|
|
22915
23423
|
if (cfgStatus === "malformed") {
|
|
22916
23424
|
return { ok: false, error: cfgError, configPath: cfgPath };
|
|
22917
23425
|
}
|
|
22918
|
-
if (cfgStatus === "missing")
|
|
23426
|
+
if (cfgStatus === "missing" && provider === null)
|
|
22919
23427
|
return { ok: false, error: `vcs.json is missing: ${cfgPath}`, configPath: cfgPath };
|
|
22920
|
-
}
|
|
22921
23428
|
if (provider === null) {
|
|
22922
23429
|
return {
|
|
22923
23430
|
ok: false,
|
|
@@ -22926,24 +23433,12 @@ function vcsConfig(mode, cwd) {
|
|
|
22926
23433
|
};
|
|
22927
23434
|
}
|
|
22928
23435
|
const prov = cfg[provider] ?? {};
|
|
22929
|
-
const wsTokenFile = typeof wsVcs.tokenFile === "string" && wsVcs.tokenFile.trim() !== "" ? wsVcs.tokenFile : null;
|
|
22930
|
-
const tokenFile = String(wsTokenFile ?? prov.tokenFile ?? path7.join(configDir(), `${provider}.token`));
|
|
22931
|
-
const tokenPath = path7.resolve(tokenFile);
|
|
22932
|
-
let tokenOk = false;
|
|
22933
|
-
if (fs5.existsSync(tokenPath)) {
|
|
22934
|
-
const token = fs5.readFileSync(tokenPath, "utf8").trim();
|
|
22935
|
-
const placeholder = !token || token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER);
|
|
22936
|
-
tokenOk = !placeholder;
|
|
22937
|
-
}
|
|
22938
23436
|
const out = {
|
|
22939
23437
|
ok: true,
|
|
22940
23438
|
configPath: path7.resolve(cfgPath),
|
|
22941
23439
|
provider,
|
|
22942
23440
|
defaultTargetBranch: defaultTarget,
|
|
22943
23441
|
pr: cfg.pr ?? {},
|
|
22944
|
-
tokenPath,
|
|
22945
|
-
tokenPresent: fs5.existsSync(tokenPath),
|
|
22946
|
-
tokenReady: tokenOk,
|
|
22947
23442
|
workspace_name: ws?.name ?? null,
|
|
22948
23443
|
link_issues: linkIssues,
|
|
22949
23444
|
youtrack_base_url: youtrackBaseUrl,
|
|
@@ -22958,16 +23453,58 @@ function vcsConfig(mode, cwd) {
|
|
|
22958
23453
|
} else if (provider === "github") {
|
|
22959
23454
|
out.github = { host: prov.host ?? "github.com" };
|
|
22960
23455
|
}
|
|
22961
|
-
if (mode === "summary")
|
|
22962
|
-
delete out.tokenPath;
|
|
22963
23456
|
return out;
|
|
22964
23457
|
}
|
|
23458
|
+
function vcsCliIdentity(cwd) {
|
|
23459
|
+
const root = vcsCwd(cwd);
|
|
23460
|
+
const cfg = vcsConfig("load", root);
|
|
23461
|
+
if (!cfg.ok)
|
|
23462
|
+
return { ok: false, error: cfg.error ?? "vcs config not ready" };
|
|
23463
|
+
const provider = cfg.provider;
|
|
23464
|
+
if (provider !== "gitlab" && provider !== "github")
|
|
23465
|
+
return { ok: false, error: `unsupported provider: ${provider}` };
|
|
23466
|
+
const bin = provider === "gitlab" ? "glab" : "gh";
|
|
23467
|
+
const host = String(provider === "github" ? cfg.github?.host ?? "github.com" : cfg.gitlab?.host ?? "gitlab.com").toLowerCase();
|
|
23468
|
+
const env = {
|
|
23469
|
+
...process.env,
|
|
23470
|
+
PATH: process.env.PATH ?? "",
|
|
23471
|
+
...provider === "github" ? { GH_HOST: host } : { GITLAB_HOST: host }
|
|
23472
|
+
};
|
|
23473
|
+
const result = spawnSync(bin, ["api", "user"], {
|
|
23474
|
+
cwd: root,
|
|
23475
|
+
encoding: "utf8",
|
|
23476
|
+
env
|
|
23477
|
+
});
|
|
23478
|
+
if (result.status !== 0)
|
|
23479
|
+
return { ok: false, provider, error: `${bin} is missing or not authenticated` };
|
|
23480
|
+
try {
|
|
23481
|
+
const user = JSON.parse(result.stdout ?? "");
|
|
23482
|
+
const username = provider === "gitlab" ? user.username : user.login;
|
|
23483
|
+
if (typeof username !== "string" || !username)
|
|
23484
|
+
return { ok: false, provider, error: `invalid ${bin} identity response` };
|
|
23485
|
+
const expected = resolveRuntimeWorkspaceVcs(root)?.vcs?.account;
|
|
23486
|
+
if (expected && username.toLowerCase() !== expected.toLowerCase())
|
|
23487
|
+
return {
|
|
23488
|
+
ok: false,
|
|
23489
|
+
provider,
|
|
23490
|
+
error: `${bin} account ${username} does not match ${expected}`
|
|
23491
|
+
};
|
|
23492
|
+
return { ok: true, provider, host, username, name: user.name };
|
|
23493
|
+
} catch {
|
|
23494
|
+
return { ok: false, provider, error: `invalid JSON from ${bin} api user` };
|
|
23495
|
+
}
|
|
23496
|
+
}
|
|
22965
23497
|
function mergedPrStyle(limit = 6, cwd) {
|
|
22966
23498
|
const cfg = vcsConfig("load", cwd);
|
|
22967
|
-
if (!cfg.ok
|
|
23499
|
+
if (!cfg.ok)
|
|
22968
23500
|
return { ok: false, error: "vcs not configured" };
|
|
22969
23501
|
const provider = cfg.provider;
|
|
22970
|
-
const
|
|
23502
|
+
const host = String(provider === "github" ? cfg.github?.host ?? "github.com" : cfg.gitlab?.host ?? "gitlab.com").toLowerCase();
|
|
23503
|
+
const env = {
|
|
23504
|
+
...process.env,
|
|
23505
|
+
PATH: process.env.PATH ?? "",
|
|
23506
|
+
...provider === "github" ? { GH_HOST: host } : { GITLAB_HOST: host }
|
|
23507
|
+
};
|
|
22971
23508
|
const examples = [];
|
|
22972
23509
|
const descInfo = (desc, caseInsensitiveNotes = false) => ({
|
|
22973
23510
|
hasNotesSection: caseInsensitiveNotes ? /##\s*notes/i.test(desc) : /##\s*Notes/.test(desc),
|
|
@@ -22975,23 +23512,30 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
22975
23512
|
`).filter((l) => l.startsWith("## ")).map((l) => l.trim()),
|
|
22976
23513
|
descriptionPreview: desc.slice(0, 600)
|
|
22977
23514
|
});
|
|
23515
|
+
const remoteResult = spawnSync("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
23516
|
+
cwd,
|
|
23517
|
+
encoding: "utf8"
|
|
23518
|
+
});
|
|
23519
|
+
const remotes = (remoteResult.stdout ?? "").split(/\r?\n/u).map((value) => value.trim()).filter(Boolean);
|
|
23520
|
+
if (remotes.length === 0)
|
|
23521
|
+
return { ok: false, error: "no origin remote" };
|
|
23522
|
+
if (remotes.length !== 1)
|
|
23523
|
+
return { ok: false, error: "origin has ambiguous push destinations" };
|
|
23524
|
+
const remote = remotes[0];
|
|
23525
|
+
const endpoint = remoteEndpoint(remote);
|
|
23526
|
+
if (!endpoint?.path)
|
|
23527
|
+
return { ok: false, error: "hosting repository could not be resolved" };
|
|
23528
|
+
if (!hostingApiHostMatches(remote, host))
|
|
23529
|
+
return { ok: false, error: "PR style target does not match the provider host" };
|
|
22978
23530
|
if (provider === "gitlab") {
|
|
22979
|
-
const
|
|
22980
|
-
|
|
22981
|
-
|
|
22982
|
-
|
|
22983
|
-
|
|
22984
|
-
if (!m)
|
|
22985
|
-
return { ok: false, error: "not a gitlab.com origin" };
|
|
22986
|
-
const project = m[1];
|
|
22987
|
-
const env = { ...process.env, GITLAB_TOKEN: token };
|
|
22988
|
-
const run = (args) => spawnSync("glab", ["api", ...args], { cwd, encoding: "utf8", env });
|
|
23531
|
+
const run = (args) => spawnSync("glab", ["api", ...args], {
|
|
23532
|
+
cwd,
|
|
23533
|
+
encoding: "utf8",
|
|
23534
|
+
env
|
|
23535
|
+
});
|
|
22989
23536
|
let r = run([
|
|
22990
|
-
`projects/${
|
|
23537
|
+
`projects/${endpoint.path.replaceAll("/", "%2F")}/merge_requests?state=merged&per_page=${limit}&order_by=updated_at&sort=desc`
|
|
22991
23538
|
]);
|
|
22992
|
-
if (r.status !== 0) {
|
|
22993
|
-
r = run([`merge_requests?state=merged&per_page=${limit}&order_by=updated_at&sort=desc`]);
|
|
22994
|
-
}
|
|
22995
23539
|
if (r.status !== 0)
|
|
22996
23540
|
return { ok: false, error: "could not list merge requests" };
|
|
22997
23541
|
for (const mr of JSON.parse(r.stdout ?? "[]")) {
|
|
@@ -23004,7 +23548,18 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
23004
23548
|
});
|
|
23005
23549
|
}
|
|
23006
23550
|
} else if (provider === "github") {
|
|
23007
|
-
const r = spawnSync("gh", [
|
|
23551
|
+
const r = spawnSync("gh", [
|
|
23552
|
+
"pr",
|
|
23553
|
+
"list",
|
|
23554
|
+
"--repo",
|
|
23555
|
+
`${host}/${endpoint.path}`,
|
|
23556
|
+
"--state",
|
|
23557
|
+
"merged",
|
|
23558
|
+
"--limit",
|
|
23559
|
+
String(limit),
|
|
23560
|
+
"--json",
|
|
23561
|
+
"title,url,body"
|
|
23562
|
+
], { cwd, encoding: "utf8", env });
|
|
23008
23563
|
if (r.status !== 0)
|
|
23009
23564
|
return { ok: false, error: "could not list pull requests" };
|
|
23010
23565
|
for (const pr of JSON.parse(r.stdout ?? "[]")) {
|
|
@@ -23026,26 +23581,28 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
23026
23581
|
}
|
|
23027
23582
|
|
|
23028
23583
|
// packages/workit-core/src/core/branch.ts
|
|
23029
|
-
|
|
23584
|
+
function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
|
|
23585
|
+
const selected = resolveRuntimeWorkspacePolicy(workspaceRoot, kind, profileName?.trim() || process.env.WORKFLOW_PROFILE?.trim() || undefined);
|
|
23586
|
+
const config = readConfig();
|
|
23587
|
+
return kind === "branch" ? {
|
|
23588
|
+
branchPolicy: resolveBranchPolicy(config, selected.policy ? { branchPolicy: selected.policy } : null),
|
|
23589
|
+
provenance: { branchPolicy: selected.source }
|
|
23590
|
+
} : {
|
|
23591
|
+
commitPolicy: resolveCommitPolicy(config, selected.policy ? { commitPolicy: selected.policy } : null),
|
|
23592
|
+
provenance: { commitPolicy: selected.source }
|
|
23593
|
+
};
|
|
23594
|
+
}
|
|
23595
|
+
var resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy;
|
|
23030
23596
|
var verifyPushIdentity = (root, provider, account) => {
|
|
23031
23597
|
if (!account || typeof account !== "string")
|
|
23032
23598
|
return { ok: false, error: "push identity requires a configured area account" };
|
|
23033
|
-
const
|
|
23034
|
-
|
|
23035
|
-
|
|
23036
|
-
|
|
23037
|
-
login = execFileSync2(bin, [...probe], {
|
|
23038
|
-
cwd: root,
|
|
23039
|
-
encoding: "utf8",
|
|
23040
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
23041
|
-
}).trim();
|
|
23042
|
-
} catch {
|
|
23043
|
-
return { ok: false, error: `${bin} identity could not be resolved` };
|
|
23044
|
-
}
|
|
23045
|
-
if (!login || login.toLowerCase() !== account.toLowerCase())
|
|
23599
|
+
const identity = vcsCliIdentity(root);
|
|
23600
|
+
if (!identity.ok || identity.provider !== provider)
|
|
23601
|
+
return { ok: false, error: identity.error ?? "hosting CLI identity could not be resolved" };
|
|
23602
|
+
if (identity.username?.toLowerCase() !== account.toLowerCase())
|
|
23046
23603
|
return {
|
|
23047
23604
|
ok: false,
|
|
23048
|
-
error: `push identity ${
|
|
23605
|
+
error: `push identity ${identity.username || "(unknown)"} does not match area account ${account}`
|
|
23049
23606
|
};
|
|
23050
23607
|
return { ok: true };
|
|
23051
23608
|
};
|
|
@@ -23063,7 +23620,7 @@ var operationAutoClass = (operation) => typeof operation === "string" ? OPERATIO
|
|
|
23063
23620
|
var digestOf = (configDirPath) => {
|
|
23064
23621
|
try {
|
|
23065
23622
|
const raw = readFileSync4(`${configDirPath}/workspaces.json`, "utf8");
|
|
23066
|
-
return
|
|
23623
|
+
return createHash4("sha256").update(raw).digest("hex");
|
|
23067
23624
|
} catch {
|
|
23068
23625
|
return null;
|
|
23069
23626
|
}
|
|
@@ -23101,14 +23658,30 @@ var resolveAutoApproval = (root, dir = configDir()) => {
|
|
|
23101
23658
|
};
|
|
23102
23659
|
};
|
|
23103
23660
|
var isStandingReceipt = (value) => typeof value === "object" && value !== null && value.kind === "standing";
|
|
23104
|
-
var
|
|
23661
|
+
var targetOf = (root, descriptor) => {
|
|
23662
|
+
try {
|
|
23663
|
+
const parsed = typeof descriptor === "string" ? JSON.parse(descriptor) : descriptor;
|
|
23664
|
+
const cwd = parsed?.payload?.cwd;
|
|
23665
|
+
if (cwd === undefined)
|
|
23666
|
+
return root;
|
|
23667
|
+
if (typeof cwd !== "string" || !path8.isAbsolute(cwd))
|
|
23668
|
+
return null;
|
|
23669
|
+
return realpathSync3(cwd);
|
|
23670
|
+
} catch {
|
|
23671
|
+
return null;
|
|
23672
|
+
}
|
|
23673
|
+
};
|
|
23674
|
+
var standingApprovalLive = (root, receipt, cls, dir, descriptor) => {
|
|
23105
23675
|
if (!isStandingReceipt(receipt))
|
|
23106
23676
|
return false;
|
|
23107
23677
|
if (typeof cls !== "string" || !AUTO_CLASSES.includes(cls))
|
|
23108
23678
|
return false;
|
|
23109
23679
|
if (receipt.class !== cls)
|
|
23110
23680
|
return false;
|
|
23111
|
-
const
|
|
23681
|
+
const target = targetOf(root, descriptor);
|
|
23682
|
+
if (!target)
|
|
23683
|
+
return false;
|
|
23684
|
+
const resolved = resolveAutoApproval(target, dir);
|
|
23112
23685
|
if (resolved.status !== "on")
|
|
23113
23686
|
return false;
|
|
23114
23687
|
if (resolved.workspace !== receipt.workspace)
|
|
@@ -23144,16 +23717,19 @@ var verifyStandingApproval = (root, task, caller, binding) => {
|
|
|
23144
23717
|
const cls = operationAutoClass(operation);
|
|
23145
23718
|
if (!cls || cls !== standing.class)
|
|
23146
23719
|
return failure("permission_denied", "standing approval does not cover this operation");
|
|
23147
|
-
const
|
|
23720
|
+
const target = targetOf(root, binding.approvedContent);
|
|
23721
|
+
if (!target)
|
|
23722
|
+
return failure("permission_denied", "standing action target is invalid");
|
|
23723
|
+
const resolved = resolveAutoApproval(target);
|
|
23148
23724
|
if (resolved.status !== "on" || resolved.workspace !== standing.workspace || !resolved.classes.includes(cls))
|
|
23149
23725
|
return failure("permission_denied", "standing auto-approval is not live for this operation");
|
|
23150
23726
|
if (cls === "push") {
|
|
23151
|
-
const workspace = resolveWorkspaceFrom(
|
|
23727
|
+
const workspace = resolveWorkspaceFrom(target, configDir());
|
|
23152
23728
|
const provider = workspace?.vcs?.provider;
|
|
23153
23729
|
const account = resolved.account;
|
|
23154
23730
|
if (!provider || !account)
|
|
23155
23731
|
return failure("permission_denied", "auto-push requires a workspace provider and area account");
|
|
23156
|
-
const identity = verifyPushIdentity(
|
|
23732
|
+
const identity = verifyPushIdentity(target, provider, account);
|
|
23157
23733
|
if (!identity.ok)
|
|
23158
23734
|
return failure("permission_denied", identity.error);
|
|
23159
23735
|
}
|
|
@@ -23162,7 +23738,7 @@ var verifyStandingApproval = (root, task, caller, binding) => {
|
|
|
23162
23738
|
host: caller.host,
|
|
23163
23739
|
session: { kind: "host", host: caller.host, handle: caller.actor },
|
|
23164
23740
|
workerId: null,
|
|
23165
|
-
receipts: [standingReceiptFor(
|
|
23741
|
+
receipts: [standingReceiptFor(target, standing.workspace, cls)]
|
|
23166
23742
|
});
|
|
23167
23743
|
};
|
|
23168
23744
|
|
|
@@ -23313,7 +23889,7 @@ function applicableDecision(task, purpose, binding, checkoutRoot) {
|
|
|
23313
23889
|
return [];
|
|
23314
23890
|
return task.decisions.filter((entry) => entry.provenance.kind !== "imported" && decisionMatches(entry, purpose, binding) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, entry.data.binding).ok : entry.data.binding.contentRefs.every((reference) => reference.kind !== "file"))).map((entry) => entry.data);
|
|
23315
23891
|
}
|
|
23316
|
-
var digestBytes2 = (bytes) =>
|
|
23892
|
+
var digestBytes2 = (bytes) => createHash5("sha256").update(bytes).digest("hex");
|
|
23317
23893
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
23318
23894
|
let root;
|
|
23319
23895
|
try {
|
|
@@ -23326,23 +23902,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
23326
23902
|
continue;
|
|
23327
23903
|
if (!reference.digest)
|
|
23328
23904
|
return failure("invalid_input", "document references require a byte digest");
|
|
23329
|
-
const target =
|
|
23330
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
23905
|
+
const target = path9.resolve(root, reference.path);
|
|
23906
|
+
if (target !== root && !target.startsWith(`${root}${path9.sep}`))
|
|
23331
23907
|
return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
23332
23908
|
fields: [
|
|
23333
23909
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
23334
23910
|
]
|
|
23335
23911
|
});
|
|
23336
23912
|
try {
|
|
23337
|
-
const relative =
|
|
23913
|
+
const relative = path9.relative(root, target);
|
|
23338
23914
|
let current = root;
|
|
23339
|
-
for (const segment of relative.split(
|
|
23915
|
+
for (const segment of relative.split(path9.sep)) {
|
|
23340
23916
|
if (!segment)
|
|
23341
23917
|
continue;
|
|
23342
|
-
current =
|
|
23918
|
+
current = path9.join(current, segment);
|
|
23343
23919
|
const stat = fs6.lstatSync(current);
|
|
23344
23920
|
const real = fs6.realpathSync(current);
|
|
23345
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
23921
|
+
if (real !== root && !real.startsWith(`${root}${path9.sep}`))
|
|
23346
23922
|
return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
23347
23923
|
fields: [
|
|
23348
23924
|
{
|
|
@@ -23420,7 +23996,7 @@ var validateAction = (store, task, workspaceId, input, authority) => {
|
|
|
23420
23996
|
operation = null;
|
|
23421
23997
|
}
|
|
23422
23998
|
const cls = operationAutoClass(operation);
|
|
23423
|
-
if (!cls || !standing.some((receipt) => standingApprovalLive(store.root, receipt, cls)))
|
|
23999
|
+
if (!cls || !standing.some((receipt) => standingApprovalLive(store.root, receipt, cls, undefined, decision.binding.approvedContent)))
|
|
23424
24000
|
return failure("permission_denied", "standing auto-approval is not live for this operation");
|
|
23425
24001
|
}
|
|
23426
24002
|
if (decision.revoked)
|
|
@@ -23722,15 +24298,15 @@ var bindingCovers = scopeCovers;
|
|
|
23722
24298
|
var verifyDecisionContent = verifyContentRefs;
|
|
23723
24299
|
|
|
23724
24300
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
23725
|
-
var digestBytes3 = (value) =>
|
|
23726
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
23727
|
-
var relative = (root, candidate) =>
|
|
24301
|
+
var digestBytes3 = (value) => createHash6("sha256").update(value).digest("hex");
|
|
24302
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path10.sep}`);
|
|
24303
|
+
var relative = (root, candidate) => path10.relative(root, candidate).split(path10.sep).join("/") || ".";
|
|
23728
24304
|
var canonicalPath = (value) => {
|
|
23729
24305
|
if (typeof value !== "string" || !value)
|
|
23730
24306
|
return null;
|
|
23731
24307
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
23732
24308
|
return null;
|
|
23733
|
-
const normalized =
|
|
24309
|
+
const normalized = path10.posix.normalize(value);
|
|
23734
24310
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
23735
24311
|
return null;
|
|
23736
24312
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -23778,14 +24354,14 @@ var gitPaths = (root) => {
|
|
|
23778
24354
|
git: /not a git repository/i.test(stderr) ? false : true
|
|
23779
24355
|
};
|
|
23780
24356
|
}
|
|
23781
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
24357
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path10.sep).join("/")) : [];
|
|
23782
24358
|
const stagedDeleted = spawnSync2("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
23783
24359
|
if (stagedDeleted.status !== 0) {
|
|
23784
24360
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
23785
24361
|
if (!/not a git repository/i.test(stderr))
|
|
23786
24362
|
return { paths, uncertain: true, git: true };
|
|
23787
24363
|
} else if (stagedDeleted.stdout) {
|
|
23788
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
24364
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path10.sep).join("/")));
|
|
23789
24365
|
}
|
|
23790
24366
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
23791
24367
|
};
|
|
@@ -23800,7 +24376,7 @@ var walk = (root, directory, output) => {
|
|
|
23800
24376
|
for (const name of names) {
|
|
23801
24377
|
if (name === ".git" || name === ".workit")
|
|
23802
24378
|
continue;
|
|
23803
|
-
const target =
|
|
24379
|
+
const target = path10.join(directory, name);
|
|
23804
24380
|
let stat;
|
|
23805
24381
|
try {
|
|
23806
24382
|
stat = fs7.lstatSync(target);
|
|
@@ -23818,12 +24394,12 @@ var walk = (root, directory, output) => {
|
|
|
23818
24394
|
var scopeRoots = (root, scope) => {
|
|
23819
24395
|
const roots = [];
|
|
23820
24396
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
23821
|
-
const target =
|
|
24397
|
+
const target = path10.resolve(root, item);
|
|
23822
24398
|
if (!inside(root, target))
|
|
23823
24399
|
return failure("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` }] });
|
|
23824
24400
|
let ancestor = target;
|
|
23825
24401
|
while (!fs7.existsSync(ancestor) && ancestor !== root)
|
|
23826
|
-
ancestor =
|
|
24402
|
+
ancestor = path10.dirname(ancestor);
|
|
23827
24403
|
try {
|
|
23828
24404
|
if (!inside(root, fs7.realpathSync(ancestor)))
|
|
23829
24405
|
return failure("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` }] });
|
|
@@ -23875,7 +24451,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
23875
24451
|
for (const item of [...names].sort()) {
|
|
23876
24452
|
if (!scopeMatches(item, normalizedScope))
|
|
23877
24453
|
continue;
|
|
23878
|
-
const target =
|
|
24454
|
+
const target = path10.join(checkout, item);
|
|
23879
24455
|
try {
|
|
23880
24456
|
const stat = fs7.lstatSync(target);
|
|
23881
24457
|
if (stat.isSymbolicLink()) {
|
|
@@ -24257,7 +24833,8 @@ var externalActionSchema = discriminatedUnion2("operation", [
|
|
|
24257
24833
|
action: _enum2(["setup", "reapply_stash"]).optional(),
|
|
24258
24834
|
sdd_dir: string5().optional(),
|
|
24259
24835
|
target_branch: string5().optional(),
|
|
24260
|
-
stash: _enum2(["yes", "no"]).optional()
|
|
24836
|
+
stash: _enum2(["yes", "no"]).optional(),
|
|
24837
|
+
cwd: string5().min(1).optional()
|
|
24261
24838
|
}).strict()
|
|
24262
24839
|
}).strict(),
|
|
24263
24840
|
object4({
|
|
@@ -24265,12 +24842,13 @@ var externalActionSchema = discriminatedUnion2("operation", [
|
|
|
24265
24842
|
payload: object4({
|
|
24266
24843
|
message: string5().min(1).optional(),
|
|
24267
24844
|
plan_steps: array3(chainStepSchema).min(1).max(32).optional(),
|
|
24268
|
-
plan_branch: string5().min(1).optional()
|
|
24845
|
+
plan_branch: string5().min(1).optional(),
|
|
24846
|
+
cwd: string5().min(1).optional()
|
|
24269
24847
|
}).strict()
|
|
24270
24848
|
}).strict(),
|
|
24271
24849
|
object4({
|
|
24272
24850
|
operation: literal3("git.push"),
|
|
24273
|
-
payload: object4({ branch: string5().min(1).optional() }).strict()
|
|
24851
|
+
payload: object4({ branch: string5().min(1).optional(), cwd: string5().min(1).optional() }).strict()
|
|
24274
24852
|
}).strict(),
|
|
24275
24853
|
object4({
|
|
24276
24854
|
operation: literal3("hosting.pull_request"),
|
|
@@ -24279,16 +24857,22 @@ var externalActionSchema = discriminatedUnion2("operation", [
|
|
|
24279
24857
|
body: string5().optional(),
|
|
24280
24858
|
draft: boolean5().optional(),
|
|
24281
24859
|
target_branch: string5().optional(),
|
|
24282
|
-
babysit: boolean5().optional()
|
|
24860
|
+
babysit: boolean5().optional(),
|
|
24861
|
+
cwd: string5().min(1).optional()
|
|
24283
24862
|
}).strict()
|
|
24284
24863
|
}).strict(),
|
|
24285
24864
|
object4({
|
|
24286
24865
|
operation: literal3("hosting.merge"),
|
|
24287
24866
|
payload: object4({
|
|
24288
24867
|
target_branch: string5().min(1).optional(),
|
|
24289
|
-
source_branch: string5().min(1).optional()
|
|
24868
|
+
source_branch: string5().min(1).optional(),
|
|
24869
|
+
cwd: string5().min(1).optional()
|
|
24290
24870
|
}).strict()
|
|
24291
24871
|
}).strict(),
|
|
24872
|
+
object4({
|
|
24873
|
+
operation: literal3("hosting.delete_branch"),
|
|
24874
|
+
payload: object4({ branch: string5().min(1), cwd: string5().min(1).optional() }).strict()
|
|
24875
|
+
}).strict(),
|
|
24292
24876
|
object4({
|
|
24293
24877
|
operation: literal3("youtrack.update"),
|
|
24294
24878
|
payload: object4({
|
|
@@ -24344,12 +24928,13 @@ var externalActionSchema = discriminatedUnion2("operation", [
|
|
|
24344
24928
|
issueRef: string5().optional(),
|
|
24345
24929
|
mode: string5().optional(),
|
|
24346
24930
|
specPath: string5().optional(),
|
|
24347
|
-
planPath: string5().optional()
|
|
24931
|
+
planPath: string5().optional(),
|
|
24932
|
+
cwd: string5().min(1).optional()
|
|
24348
24933
|
}).strict()
|
|
24349
24934
|
}).strict()
|
|
24350
24935
|
]);
|
|
24351
24936
|
// packages/workit-core/src/core/task-engine.ts
|
|
24352
|
-
import { realpathSync as
|
|
24937
|
+
import { realpathSync as realpathSync6 } from "node:fs";
|
|
24353
24938
|
|
|
24354
24939
|
// packages/workit-core/src/core/policy-resolver.ts
|
|
24355
24940
|
var resolverInputSchema = object4({
|
|
@@ -24894,6 +25479,18 @@ var portableTask = (task) => {
|
|
|
24894
25479
|
};
|
|
24895
25480
|
for (const entry of clone.assessments)
|
|
24896
25481
|
demote(entry.data.facts, entry.data.signals, entry.data.consequences);
|
|
25482
|
+
const sourceDecisions = new Map(task.decisions.map((entry) => [entry.id, entry]));
|
|
25483
|
+
for (const entry of clone.decisions) {
|
|
25484
|
+
const consumption = sourceDecisions.get(entry.id)?.data.consumption;
|
|
25485
|
+
if (!consumption)
|
|
25486
|
+
continue;
|
|
25487
|
+
entry.data.consumption = {
|
|
25488
|
+
...consumption,
|
|
25489
|
+
state: consumption.state === "reserved" ? "uncertain" : consumption.state,
|
|
25490
|
+
actionRef: { kind: "record", collection: "decisions", id: entry.id },
|
|
25491
|
+
...consumption.historicalActionId ? { historicalActionId: consumption.historicalActionId } : consumption.actionRef.kind === "host" ? { historicalActionId: consumption.actionRef.handle } : {}
|
|
25492
|
+
};
|
|
25493
|
+
}
|
|
24897
25494
|
clone.candidates = [];
|
|
24898
25495
|
return clone;
|
|
24899
25496
|
};
|
|
@@ -25019,7 +25616,11 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
25019
25616
|
taskId: "",
|
|
25020
25617
|
workspaceId: destinationWorkspaceId
|
|
25021
25618
|
},
|
|
25022
|
-
consumption:
|
|
25619
|
+
consumption: data.consumption ? {
|
|
25620
|
+
...data.consumption,
|
|
25621
|
+
state: data.consumption.state === "reserved" ? "uncertain" : data.consumption.state,
|
|
25622
|
+
actionRef: { kind: "record", collection: "decisions", id: entry.id }
|
|
25623
|
+
} : null
|
|
25023
25624
|
}
|
|
25024
25625
|
};
|
|
25025
25626
|
}),
|
|
@@ -25060,7 +25661,7 @@ class WorkitCore {
|
|
|
25060
25661
|
}
|
|
25061
25662
|
contextRootError() {
|
|
25062
25663
|
try {
|
|
25063
|
-
if (
|
|
25664
|
+
if (realpathSync6(this.context.root) !== this.store.root)
|
|
25064
25665
|
return failure("invalid_input", "operation context root does not match the task store root");
|
|
25065
25666
|
} catch {
|
|
25066
25667
|
return failure("invalid_input", "operation context root cannot be resolved");
|
|
@@ -25219,7 +25820,7 @@ class WorkitCore {
|
|
|
25219
25820
|
return parsed;
|
|
25220
25821
|
const input = parsed.data;
|
|
25221
25822
|
if ((this.context.workerId ?? null) !== null) {
|
|
25222
|
-
if (input.action !== "inspect")
|
|
25823
|
+
if (input.action !== "inspect" && input.action !== "list")
|
|
25223
25824
|
return failure("permission_denied", "helpers cannot control task lifecycle or scope");
|
|
25224
25825
|
}
|
|
25225
25826
|
switch (input.action) {
|
|
@@ -25246,7 +25847,18 @@ class WorkitCore {
|
|
|
25246
25847
|
return listed;
|
|
25247
25848
|
const status = input.status ?? "open";
|
|
25248
25849
|
const limit = input.limit ?? 20;
|
|
25249
|
-
const
|
|
25850
|
+
const query = input.query?.toLowerCase().split(/\s+/);
|
|
25851
|
+
const selected = listed.data.filter((task) => status === "all" ? true : status === "closed" ? task.status === "closed" : task.status !== "closed").filter((task) => !query || query.every((term) => JSON.stringify({
|
|
25852
|
+
id: task.id,
|
|
25853
|
+
workspaceId: task.workspaceId,
|
|
25854
|
+
origin: task.origin,
|
|
25855
|
+
createdAt: task.createdAt,
|
|
25856
|
+
updatedAt: task.updatedAt,
|
|
25857
|
+
source: task.intent.provenance,
|
|
25858
|
+
objective: task.intent.data.objective,
|
|
25859
|
+
progress: task.progress,
|
|
25860
|
+
decisions: task.decisions.map(({ data }) => data)
|
|
25861
|
+
}).toLowerCase().includes(term))).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, limit);
|
|
25250
25862
|
if (selected.length === 0)
|
|
25251
25863
|
return success(null, null, []);
|
|
25252
25864
|
const workspace = this.store.readWorkspace();
|
|
@@ -25265,7 +25877,11 @@ class WorkitCore {
|
|
|
25265
25877
|
status: task.status,
|
|
25266
25878
|
closure: task.closure,
|
|
25267
25879
|
progress: task.progress,
|
|
25268
|
-
writer: task.status === "closed" ? null : workspace.data.writer
|
|
25880
|
+
writer: task.status === "closed" ? null : workspace.data.writer,
|
|
25881
|
+
source: {
|
|
25882
|
+
host: task.intent.provenance.host,
|
|
25883
|
+
kind: task.intent.provenance.kind
|
|
25884
|
+
}
|
|
25269
25885
|
}));
|
|
25270
25886
|
return success(null, null, tasks);
|
|
25271
25887
|
}
|
|
@@ -26361,7 +26977,7 @@ class WorkitCore {
|
|
|
26361
26977
|
if (view.task.workspaceId !== view.workspace.id)
|
|
26362
26978
|
return failure("invalid_input", "task and workspace bindings are invalid");
|
|
26363
26979
|
try {
|
|
26364
|
-
if (
|
|
26980
|
+
if (realpathSync6(view.workspace.root) !== this.store.root)
|
|
26365
26981
|
return failure("invalid_input", "task view root does not match the task store");
|
|
26366
26982
|
} catch {
|
|
26367
26983
|
return failure("invalid_input", "task view root cannot be resolved");
|
|
@@ -26638,22 +27254,22 @@ function resolveGitRevision(root, value) {
|
|
|
26638
27254
|
}
|
|
26639
27255
|
}
|
|
26640
27256
|
function resolveInside(root, candidate) {
|
|
26641
|
-
const base =
|
|
26642
|
-
const target =
|
|
26643
|
-
if (target !== base && !target.startsWith(base +
|
|
27257
|
+
const base = realpathSync7(root);
|
|
27258
|
+
const target = path11.resolve(base, candidate);
|
|
27259
|
+
if (target !== base && !target.startsWith(base + path11.sep)) {
|
|
26644
27260
|
throw new Error("path must stay inside repository root");
|
|
26645
27261
|
}
|
|
26646
27262
|
let ancestor = target;
|
|
26647
27263
|
while (!existsSync5(ancestor))
|
|
26648
|
-
ancestor =
|
|
26649
|
-
const canonicalAncestor =
|
|
26650
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
27264
|
+
ancestor = path11.dirname(ancestor);
|
|
27265
|
+
const canonicalAncestor = realpathSync7(ancestor);
|
|
27266
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path11.sep)) {
|
|
26651
27267
|
throw new Error("path must stay inside repository root");
|
|
26652
27268
|
}
|
|
26653
27269
|
return target;
|
|
26654
27270
|
}
|
|
26655
27271
|
function run2(root, executable, args, env = {}) {
|
|
26656
|
-
const cwd =
|
|
27272
|
+
const cwd = realpathSync7(root);
|
|
26657
27273
|
const result = spawnSync3(executable, args, {
|
|
26658
27274
|
cwd,
|
|
26659
27275
|
encoding: "utf8",
|
|
@@ -26726,18 +27342,19 @@ var redactSecrets = (value) => applyPatterns(value);
|
|
|
26726
27342
|
|
|
26727
27343
|
// packages/workit-core/src/core/pr-create.ts
|
|
26728
27344
|
import fs8 from "node:fs";
|
|
26729
|
-
import
|
|
27345
|
+
import path12 from "node:path";
|
|
27346
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
26730
27347
|
function parseGhIssue(value) {
|
|
26731
27348
|
const m = /issues\/(\d+)/.exec(value);
|
|
26732
27349
|
return m ? m[1] : String(value).trim().replace(/^#/, "");
|
|
26733
27350
|
}
|
|
26734
27351
|
function whichOnPath(tool) {
|
|
26735
27352
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
26736
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
27353
|
+
for (const dir of (process.env.PATH ?? "").split(path12.delimiter)) {
|
|
26737
27354
|
if (!dir)
|
|
26738
27355
|
continue;
|
|
26739
27356
|
for (const name of names) {
|
|
26740
|
-
const candidate =
|
|
27357
|
+
const candidate = path12.join(dir, name);
|
|
26741
27358
|
try {
|
|
26742
27359
|
fs8.accessSync(candidate, fs8.constants.X_OK);
|
|
26743
27360
|
return candidate;
|
|
@@ -26747,11 +27364,21 @@ function whichOnPath(tool) {
|
|
|
26747
27364
|
return null;
|
|
26748
27365
|
}
|
|
26749
27366
|
var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
|
|
27367
|
+
var pushRemote = (cwd) => {
|
|
27368
|
+
const result = spawnSync4("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
27369
|
+
cwd,
|
|
27370
|
+
encoding: "utf8"
|
|
27371
|
+
});
|
|
27372
|
+
if (result.status !== 0)
|
|
27373
|
+
return null;
|
|
27374
|
+
const urls = (result.stdout ?? "").split(/\r?\n/u).map((url) => url.trim()).filter(Boolean);
|
|
27375
|
+
return urls.length === 1 ? urls[0] : null;
|
|
27376
|
+
};
|
|
26750
27377
|
|
|
26751
27378
|
// packages/workit-core/src/core/repo-context.ts
|
|
26752
|
-
import { spawnSync as
|
|
27379
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
26753
27380
|
import { existsSync as existsSync6, readFileSync as readFileSync7, readdirSync as readdirSync4 } from "node:fs";
|
|
26754
|
-
import
|
|
27381
|
+
import path13 from "node:path";
|
|
26755
27382
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
26756
27383
|
try {
|
|
26757
27384
|
gitRevisionParts(value);
|
|
@@ -26778,7 +27405,7 @@ var invalidRange = (cwd) => ({
|
|
|
26778
27405
|
cwd
|
|
26779
27406
|
});
|
|
26780
27407
|
var runGit = (cwd, args) => {
|
|
26781
|
-
const result =
|
|
27408
|
+
const result = spawnSync5("git", args, { cwd, encoding: "utf8" });
|
|
26782
27409
|
return {
|
|
26783
27410
|
stdout: (result.stdout ?? "").trimEnd(),
|
|
26784
27411
|
stderr: (result.stderr ?? "").trimEnd(),
|
|
@@ -26885,7 +27512,7 @@ var findPrTemplate = (cwd) => {
|
|
|
26885
27512
|
]) {
|
|
26886
27513
|
const parts = rel.split("/");
|
|
26887
27514
|
const base = parts[parts.length - 1];
|
|
26888
|
-
const dir = parts.length > 1 ?
|
|
27515
|
+
const dir = parts.length > 1 ? path13.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
26889
27516
|
const actual = probeCaseInsensitive(dir, base);
|
|
26890
27517
|
if (actual)
|
|
26891
27518
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -26916,7 +27543,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
26916
27543
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
26917
27544
|
};
|
|
26918
27545
|
var packageScripts = (cwd, keys) => {
|
|
26919
|
-
const pkgPath =
|
|
27546
|
+
const pkgPath = path13.join(cwd, "package.json");
|
|
26920
27547
|
if (!existsSync6(pkgPath))
|
|
26921
27548
|
return [];
|
|
26922
27549
|
try {
|
|
@@ -26942,9 +27569,9 @@ var documentationFiles = (cwd) => {
|
|
|
26942
27569
|
if (entry.isDirectory()) {
|
|
26943
27570
|
if (excluded.has(entry.name))
|
|
26944
27571
|
continue;
|
|
26945
|
-
walk(
|
|
27572
|
+
walk(path13.join(dir, entry.name), depth + 1);
|
|
26946
27573
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
26947
|
-
matches.push("./" +
|
|
27574
|
+
matches.push("./" + path13.relative(cwd, path13.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
26948
27575
|
}
|
|
26949
27576
|
}
|
|
26950
27577
|
};
|
|
@@ -26960,7 +27587,7 @@ var readTrimmed = (file, maxLines) => {
|
|
|
26960
27587
|
`);
|
|
26961
27588
|
};
|
|
26962
27589
|
function prReadyContext(root, range) {
|
|
26963
|
-
const cwd =
|
|
27590
|
+
const cwd = path13.resolve(repoRoot(root));
|
|
26964
27591
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
26965
27592
|
return invalidRange(cwd);
|
|
26966
27593
|
let stdout = "";
|
|
@@ -27018,7 +27645,7 @@ ${status.stderr}` : "");
|
|
|
27018
27645
|
stdout += `template_path: ${template}
|
|
27019
27646
|
|
|
27020
27647
|
`;
|
|
27021
|
-
stdout += readTrimmed(
|
|
27648
|
+
stdout += readTrimmed(path13.join(cwd, template), 220);
|
|
27022
27649
|
} else {
|
|
27023
27650
|
stdout += `template_path: none
|
|
27024
27651
|
|
|
@@ -27028,7 +27655,7 @@ ${status.stderr}` : "");
|
|
|
27028
27655
|
stdout += `
|
|
27029
27656
|
`;
|
|
27030
27657
|
stdout += printSection("Recent Validation Signals");
|
|
27031
|
-
if (existsSync6(
|
|
27658
|
+
if (existsSync6(path13.join(cwd, "package.json"))) {
|
|
27032
27659
|
stdout += `package.json detected. Common scripts:
|
|
27033
27660
|
`;
|
|
27034
27661
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -27037,7 +27664,7 @@ ${status.stderr}` : "");
|
|
|
27037
27664
|
`) + `
|
|
27038
27665
|
`;
|
|
27039
27666
|
}
|
|
27040
|
-
if (existsSync6(
|
|
27667
|
+
if (existsSync6(path13.join(cwd, "Cargo.toml")) || existsSync6(path13.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
27041
27668
|
stdout += `Rust project detected.
|
|
27042
27669
|
`;
|
|
27043
27670
|
}
|
|
@@ -27070,7 +27697,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
27070
27697
|
- Apply with the native workit_changelog_apply tool only (not hand-edits under Unreleased).
|
|
27071
27698
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
27072
27699
|
function changelogContext(root, range) {
|
|
27073
|
-
const cwd =
|
|
27700
|
+
const cwd = path13.resolve(repoRoot(root));
|
|
27074
27701
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27075
27702
|
return invalidRange(cwd);
|
|
27076
27703
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -27088,7 +27715,7 @@ function changelogContext(root, range) {
|
|
|
27088
27715
|
stdout += CHANGELOG_RULES + `
|
|
27089
27716
|
`;
|
|
27090
27717
|
stdout += printSection("Existing CHANGELOG.md");
|
|
27091
|
-
const changelogPath =
|
|
27718
|
+
const changelogPath = path13.join(cwd, "CHANGELOG.md");
|
|
27092
27719
|
if (existsSync6(changelogPath)) {
|
|
27093
27720
|
stdout += readTrimmed(changelogPath, 260) + `
|
|
27094
27721
|
`;
|
|
@@ -27108,7 +27735,7 @@ function changelogContext(root, range) {
|
|
|
27108
27735
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
27109
27736
|
}
|
|
27110
27737
|
function docsRefreshContext(root, range) {
|
|
27111
|
-
const cwd =
|
|
27738
|
+
const cwd = path13.resolve(repoRoot(root));
|
|
27112
27739
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27113
27740
|
return invalidRange(cwd);
|
|
27114
27741
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -27130,7 +27757,7 @@ function docsRefreshContext(root, range) {
|
|
|
27130
27757
|
`) + `
|
|
27131
27758
|
`;
|
|
27132
27759
|
stdout += printSection("README Preview");
|
|
27133
|
-
const readme =
|
|
27760
|
+
const readme = path13.join(cwd, "README.md");
|
|
27134
27761
|
if (existsSync6(readme)) {
|
|
27135
27762
|
stdout += readTrimmed(readme, 220) + `
|
|
27136
27763
|
`;
|
|
@@ -27139,7 +27766,7 @@ function docsRefreshContext(root, range) {
|
|
|
27139
27766
|
`;
|
|
27140
27767
|
}
|
|
27141
27768
|
stdout += printSection("Package Scripts");
|
|
27142
|
-
const pkgPath =
|
|
27769
|
+
const pkgPath = path13.join(cwd, "package.json");
|
|
27143
27770
|
if (existsSync6(pkgPath)) {
|
|
27144
27771
|
try {
|
|
27145
27772
|
const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
@@ -27153,7 +27780,7 @@ function docsRefreshContext(root, range) {
|
|
|
27153
27780
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
27154
27781
|
}
|
|
27155
27782
|
function releaseNotesContext(root, rangeOrTag) {
|
|
27156
|
-
const cwd =
|
|
27783
|
+
const cwd = path13.resolve(repoRoot(root));
|
|
27157
27784
|
if (!rangeOrTag) {
|
|
27158
27785
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
27159
27786
|
`, exitCode: 1, cwd };
|
|
@@ -27191,7 +27818,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
27191
27818
|
`;
|
|
27192
27819
|
stdout += printSection("Existing Release Files");
|
|
27193
27820
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
27194
|
-
if (existsSync6(
|
|
27821
|
+
if (existsSync6(path13.join(cwd, rel)))
|
|
27195
27822
|
stdout += rel + `
|
|
27196
27823
|
`;
|
|
27197
27824
|
}
|
|
@@ -27219,19 +27846,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
27219
27846
|
}
|
|
27220
27847
|
|
|
27221
27848
|
// packages/workit-core/src/core/tracker-issues.ts
|
|
27222
|
-
import
|
|
27223
|
-
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
27224
|
-
var trackerRequest = async (url, init) => {
|
|
27225
|
-
try {
|
|
27226
|
-
const res = await fetch(url, { method: init.method, headers: init.headers });
|
|
27227
|
-
const text = await res.text();
|
|
27228
|
-
if (!res.ok)
|
|
27229
|
-
return { status: res.status, stdout: "", stderr: text.slice(0, 200) };
|
|
27230
|
-
return { status: 0, stdout: text, stderr: "" };
|
|
27231
|
-
} catch (err) {
|
|
27232
|
-
return { status: 1, stdout: "", stderr: err instanceof Error ? err.message : "network error" };
|
|
27233
|
-
}
|
|
27234
|
-
};
|
|
27849
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
27235
27850
|
var repoPathFromRemote = (remote) => {
|
|
27236
27851
|
let rest = (remote || "").trim().replace(/\/+$/, "");
|
|
27237
27852
|
if (!rest)
|
|
@@ -27252,7 +27867,11 @@ var defaultCli = (bin, available) => {
|
|
|
27252
27867
|
if (!available)
|
|
27253
27868
|
return null;
|
|
27254
27869
|
return (args, root) => {
|
|
27255
|
-
const out =
|
|
27870
|
+
const out = spawnSync6(bin, args, {
|
|
27871
|
+
cwd: root,
|
|
27872
|
+
encoding: "utf8",
|
|
27873
|
+
env: { ...process.env, PATH: process.env.PATH ?? "" }
|
|
27874
|
+
});
|
|
27256
27875
|
return Promise.resolve({
|
|
27257
27876
|
status: out.status ?? 1,
|
|
27258
27877
|
stdout: out.stdout ?? "",
|
|
@@ -27260,49 +27879,22 @@ var defaultCli = (bin, available) => {
|
|
|
27260
27879
|
});
|
|
27261
27880
|
};
|
|
27262
27881
|
};
|
|
27263
|
-
var tryCliTriple = async (cli, args, root, id, idKeys, bodyKeys) => {
|
|
27882
|
+
var tryCliTriple = async (cli, name, args, root, id, idKeys, bodyKeys) => {
|
|
27264
27883
|
if (!cli)
|
|
27265
|
-
return
|
|
27884
|
+
return { error: `${name} CLI is unavailable`, kind: "creds" };
|
|
27266
27885
|
const run = cli(args, root);
|
|
27267
27886
|
if (!run)
|
|
27268
|
-
return
|
|
27887
|
+
return { error: `${name} CLI is unavailable`, kind: "creds" };
|
|
27269
27888
|
const out = await run;
|
|
27270
27889
|
if (out.status !== 0)
|
|
27271
|
-
return
|
|
27890
|
+
return { error: `${name} CLI is not authenticated or the issue is unavailable`, kind: "creds" };
|
|
27272
27891
|
try {
|
|
27273
|
-
|
|
27892
|
+
const triple = parseTriple(JSON.parse(out.stdout), id, idKeys, bodyKeys);
|
|
27893
|
+
return triple ? { data: triple } : { error: `unexpected ${name} issue response`, kind: "request" };
|
|
27274
27894
|
} catch {
|
|
27275
|
-
return
|
|
27895
|
+
return { error: `invalid ${name} issue response`, kind: "request" };
|
|
27276
27896
|
}
|
|
27277
27897
|
};
|
|
27278
|
-
var originRemote = (root) => {
|
|
27279
|
-
const out = spawnSync5("git", ["remote", "get-url", "origin"], { cwd: root, encoding: "utf8" });
|
|
27280
|
-
return out.status === 0 ? (out.stdout ?? "").trim() || null : null;
|
|
27281
|
-
};
|
|
27282
|
-
var trackerCreds = (root, provider) => {
|
|
27283
|
-
const cfg = vcsConfig("load", root);
|
|
27284
|
-
if (!cfg || cfg.provider !== provider || typeof cfg.tokenPath !== "string" || !cfg.tokenPath)
|
|
27285
|
-
return { error: `unconfigured ${provider} provider` };
|
|
27286
|
-
let token = "";
|
|
27287
|
-
try {
|
|
27288
|
-
token = fs9.readFileSync(cfg.tokenPath, "utf8").trim();
|
|
27289
|
-
} catch {
|
|
27290
|
-
return { error: `missing ${provider} token` };
|
|
27291
|
-
}
|
|
27292
|
-
if (!token)
|
|
27293
|
-
return { error: `empty ${provider} token file` };
|
|
27294
|
-
if (provider === "gitlab") {
|
|
27295
|
-
const gitlab = cfg.gitlab;
|
|
27296
|
-
const apiUrl = String(gitlab?.apiUrl ?? "https://gitlab.com/api/v4").replace(/\/+$/, "");
|
|
27297
|
-
return { token, api: apiUrl };
|
|
27298
|
-
}
|
|
27299
|
-
const github = cfg.github;
|
|
27300
|
-
const host = String(github?.host ?? "github.com").toLowerCase();
|
|
27301
|
-
return {
|
|
27302
|
-
token,
|
|
27303
|
-
api: host === "github.com" ? "https://api.github.com" : `https://${host}/api/v3`
|
|
27304
|
-
};
|
|
27305
|
-
};
|
|
27306
27898
|
var parseTriple = (parsed, id, idKeys, bodyKeys) => {
|
|
27307
27899
|
const title = parsed.title;
|
|
27308
27900
|
if (typeof title !== "string")
|
|
@@ -27325,62 +27917,36 @@ var fetchGitHubIssueBody = async (ref, root, deps = {}) => {
|
|
|
27325
27917
|
const id = parseGhIssue(ref);
|
|
27326
27918
|
if (!/^\d+$/.test(id))
|
|
27327
27919
|
return { error: `invalid GitHub issue ref "${ref}"`, kind: "input" };
|
|
27328
|
-
const
|
|
27920
|
+
const remote = (deps.remote ?? pushRemote)(root);
|
|
27921
|
+
const repo = repoPathFromRemote(remote ?? "");
|
|
27329
27922
|
if (!repo)
|
|
27330
27923
|
return { error: "no origin remote to resolve the repository", kind: "input" };
|
|
27331
|
-
const
|
|
27332
|
-
|
|
27333
|
-
|
|
27334
|
-
|
|
27335
|
-
|
|
27336
|
-
|
|
27337
|
-
const out = await (deps.request ?? trackerRequest)(`${credentials.api}/repos/${repo}/issues/${id}`, {
|
|
27338
|
-
method: "GET",
|
|
27339
|
-
headers: {
|
|
27340
|
-
Authorization: `Bearer ${credentials.token}`,
|
|
27341
|
-
Accept: "application/vnd.github+json"
|
|
27342
|
-
}
|
|
27343
|
-
});
|
|
27344
|
-
if (out.status !== 0)
|
|
27345
|
-
return { error: out.stderr || "GitHub issue fetch failed", kind: "request" };
|
|
27346
|
-
try {
|
|
27347
|
-
const triple = parseTriple(JSON.parse(out.stdout), id, ["number"], ["body"]);
|
|
27348
|
-
if (!triple)
|
|
27349
|
-
return { error: "unexpected GitHub issue shape", kind: "request" };
|
|
27350
|
-
return { data: triple };
|
|
27351
|
-
} catch {
|
|
27352
|
-
return { error: "invalid GitHub issue response", kind: "request" };
|
|
27353
|
-
}
|
|
27924
|
+
const cfg = vcsConfig("load", root);
|
|
27925
|
+
const host = (deps.host ?? (cfg.ok && cfg.provider === "github" ? cfg.github?.host : null) ?? "github.com").toLowerCase();
|
|
27926
|
+
if (!remote || !hostingApiHostMatches(remote, host))
|
|
27927
|
+
return { error: "hosting API host does not match the push destination", kind: "input" };
|
|
27928
|
+
const repoArg = host === "github.com" ? repo : `${host}/${repo}`;
|
|
27929
|
+
return tryCliTriple(deps.cli ?? defaultCli("gh", hostingCliAvailable("github")), "gh", ["issue", "view", id, "--repo", repoArg, "--json", "number,title,body,state"], root, id, ["number"], ["body"]);
|
|
27354
27930
|
};
|
|
27355
27931
|
var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
27356
27932
|
const id = parseGhIssue(ref);
|
|
27357
27933
|
if (!/^\d+$/.test(id))
|
|
27358
27934
|
return { error: `invalid GitLab issue ref "${ref}"`, kind: "input" };
|
|
27359
|
-
const
|
|
27935
|
+
const remote = (deps.remote ?? pushRemote)(root);
|
|
27936
|
+
const repo = repoPathFromRemote(remote ?? "");
|
|
27360
27937
|
if (!repo)
|
|
27361
27938
|
return { error: "no origin remote to resolve the project", kind: "input" };
|
|
27362
|
-
const
|
|
27363
|
-
|
|
27364
|
-
|
|
27365
|
-
|
|
27366
|
-
|
|
27367
|
-
|
|
27368
|
-
const out = await (deps.request ?? trackerRequest)(`${credentials.api}/projects/${encodeURIComponent(repo)}/issues/${id}`, { method: "GET", headers: { "PRIVATE-TOKEN": credentials.token } });
|
|
27369
|
-
if (out.status !== 0)
|
|
27370
|
-
return { error: out.stderr || "GitLab issue fetch failed", kind: "request" };
|
|
27371
|
-
try {
|
|
27372
|
-
const triple = parseTriple(JSON.parse(out.stdout), id, ["iid"], ["description"]);
|
|
27373
|
-
if (!triple)
|
|
27374
|
-
return { error: "unexpected GitLab issue shape", kind: "request" };
|
|
27375
|
-
return { data: triple };
|
|
27376
|
-
} catch {
|
|
27377
|
-
return { error: "invalid GitLab issue response", kind: "request" };
|
|
27378
|
-
}
|
|
27939
|
+
const cfg = vcsConfig("load", root);
|
|
27940
|
+
const host = (deps.host ?? (cfg.ok && cfg.provider === "gitlab" ? cfg.gitlab?.host : null) ?? "gitlab.com").toLowerCase();
|
|
27941
|
+
if (!remote || !hostingApiHostMatches(remote, host))
|
|
27942
|
+
return { error: "hosting API host does not match the push destination", kind: "input" };
|
|
27943
|
+
const repoArg = host === "gitlab.com" ? repo : `https://${host}/${repo}`;
|
|
27944
|
+
return tryCliTriple(deps.cli ?? defaultCli("glab", hostingCliAvailable("gitlab")), "glab", ["issue", "view", id, "-R", repoArg, "-F", "json"], root, id, ["iid"], ["description"]);
|
|
27379
27945
|
};
|
|
27380
27946
|
|
|
27381
27947
|
// packages/workit-core/src/core/youtrack.ts
|
|
27382
|
-
import
|
|
27383
|
-
import
|
|
27948
|
+
import fs9 from "node:fs";
|
|
27949
|
+
import path14 from "node:path";
|
|
27384
27950
|
|
|
27385
27951
|
// packages/workit-core/src/core/templates.ts
|
|
27386
27952
|
var repoRoot2 = assetRoot();
|
|
@@ -27392,23 +27958,23 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
27392
27958
|
|
|
27393
27959
|
// packages/workit-core/src/core/youtrack.ts
|
|
27394
27960
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
27395
|
-
var
|
|
27396
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
27397
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
27961
|
+
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
27962
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path14.join(configDir(), "youtrack.json");
|
|
27963
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path14.join(resolveConfigDir(), "youtrack.json");
|
|
27398
27964
|
var youTrackTokenModeOk = (p) => {
|
|
27399
27965
|
if (process.platform === "win32")
|
|
27400
27966
|
return true;
|
|
27401
|
-
const mode =
|
|
27967
|
+
const mode = fs9.statSync(p).mode & 511;
|
|
27402
27968
|
return mode === 384;
|
|
27403
27969
|
};
|
|
27404
27970
|
function readYouTrackConfig(required) {
|
|
27405
27971
|
const cfgPath = youTrackConfigPath();
|
|
27406
|
-
if (!
|
|
27972
|
+
if (!fs9.existsSync(cfgPath)) {
|
|
27407
27973
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
27408
27974
|
}
|
|
27409
27975
|
let parsed;
|
|
27410
27976
|
try {
|
|
27411
|
-
parsed = JSON.parse(
|
|
27977
|
+
parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
|
|
27412
27978
|
} catch {
|
|
27413
27979
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
27414
27980
|
}
|
|
@@ -27419,11 +27985,11 @@ function readYouTrackConfig(required) {
|
|
|
27419
27985
|
}
|
|
27420
27986
|
var readYouTrackContextConfig = () => {
|
|
27421
27987
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
27422
|
-
if (!
|
|
27988
|
+
if (!fs9.existsSync(cfgPath))
|
|
27423
27989
|
return { data: {} };
|
|
27424
27990
|
let parsed;
|
|
27425
27991
|
try {
|
|
27426
|
-
parsed = JSON.parse(
|
|
27992
|
+
parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
|
|
27427
27993
|
} catch {
|
|
27428
27994
|
return { error: "YouTrack configuration is unavailable" };
|
|
27429
27995
|
}
|
|
@@ -27448,12 +28014,12 @@ function youTrackGreeting(configOverride) {
|
|
|
27448
28014
|
const cfgPath = configOverride ?? youTrackConfigPath();
|
|
27449
28015
|
let parsed;
|
|
27450
28016
|
try {
|
|
27451
|
-
parsed = JSON.parse(
|
|
28017
|
+
parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
|
|
27452
28018
|
} catch {
|
|
27453
28019
|
return {
|
|
27454
28020
|
stdout: "",
|
|
27455
28021
|
exitCode: 1,
|
|
27456
|
-
stderr:
|
|
28022
|
+
stderr: fs9.existsSync(cfgPath) ? `${cfgPath} is not valid JSON` : `missing youtrack.json: ${cfgPath}`
|
|
27457
28023
|
};
|
|
27458
28024
|
}
|
|
27459
28025
|
if (!isConfigObject(parsed)) {
|
|
@@ -27499,15 +28065,15 @@ var youTrackToken = () => {
|
|
|
27499
28065
|
if ("error" in loaded)
|
|
27500
28066
|
return loaded;
|
|
27501
28067
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
27502
|
-
const tokenPath = tokenFile ?
|
|
27503
|
-
if (!tokenPath || !
|
|
28068
|
+
const tokenPath = tokenFile ? path14.isAbsolute(tokenFile) ? path14.resolve(tokenFile) : path14.resolve(process.cwd(), tokenFile) : "";
|
|
28069
|
+
if (!tokenPath || !fs9.existsSync(tokenPath))
|
|
27504
28070
|
return { error: "missing youtrack.token" };
|
|
27505
28071
|
if (!youTrackTokenModeOk(tokenPath))
|
|
27506
28072
|
return { error: "youtrack.token mode must be 0600" };
|
|
27507
|
-
const token =
|
|
28073
|
+
const token = fs9.readFileSync(tokenPath, "utf8").trim();
|
|
27508
28074
|
if (!token)
|
|
27509
28075
|
return { error: "empty token file" };
|
|
27510
|
-
if (token ===
|
|
28076
|
+
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
27511
28077
|
return {
|
|
27512
28078
|
error: "token file still has placeholder YOUR_TOKEN_HERE — edit the file locally, then run /wk-status"
|
|
27513
28079
|
};
|
|
@@ -27570,9 +28136,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
27570
28136
|
} catch {
|
|
27571
28137
|
continue;
|
|
27572
28138
|
}
|
|
27573
|
-
if (!
|
|
28139
|
+
if (!fs9.existsSync(full))
|
|
27574
28140
|
continue;
|
|
27575
|
-
const text =
|
|
28141
|
+
const text = fs9.readFileSync(full, "utf8");
|
|
27576
28142
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
27577
28143
|
if (m)
|
|
27578
28144
|
return m[1];
|
|
@@ -27806,6 +28372,7 @@ var readExternalContext = async (root, payload) => {
|
|
|
27806
28372
|
}
|
|
27807
28373
|
}
|
|
27808
28374
|
};
|
|
28375
|
+
var targetWriterActionLease = Symbol("target-writer-action-lease");
|
|
27809
28376
|
|
|
27810
28377
|
// packages/workit-mcp/src/server.ts
|
|
27811
28378
|
class McpCapabilityUnavailableError extends Error {
|
|
@@ -27850,13 +28417,13 @@ var rootVariants = (workspaceRoot) => {
|
|
|
27850
28417
|
if (!workspaceRoot)
|
|
27851
28418
|
return [];
|
|
27852
28419
|
const roots = new Set;
|
|
27853
|
-
for (const candidate of [workspaceRoot,
|
|
27854
|
-
if (
|
|
28420
|
+
for (const candidate of [workspaceRoot, path15.resolve(workspaceRoot)]) {
|
|
28421
|
+
if (path15.isAbsolute(candidate) && candidate !== path15.parse(candidate).root)
|
|
27855
28422
|
roots.add(candidate);
|
|
27856
28423
|
}
|
|
27857
28424
|
try {
|
|
27858
|
-
const canonical =
|
|
27859
|
-
if (canonical !==
|
|
28425
|
+
const canonical = realpathSync8(workspaceRoot);
|
|
28426
|
+
if (canonical !== path15.parse(canonical).root)
|
|
27860
28427
|
roots.add(canonical);
|
|
27861
28428
|
} catch {}
|
|
27862
28429
|
return [...roots].sort((left, right) => right.length - left.length);
|
|
@@ -27957,11 +28524,11 @@ var thrownResult = (tool, error, workspaceRoot) => {
|
|
|
27957
28524
|
};
|
|
27958
28525
|
function createMcpServer(host, contextProvider) {
|
|
27959
28526
|
assertMcpHost(host);
|
|
27960
|
-
const here =
|
|
28527
|
+
const here = path15.dirname(fileURLToPath2(import.meta.url));
|
|
27961
28528
|
const sourceMarker = markSourcesLoaded([
|
|
27962
|
-
|
|
27963
|
-
|
|
27964
|
-
|
|
28529
|
+
path15.join(here, "server.ts"),
|
|
28530
|
+
path15.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
|
|
28531
|
+
path15.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
|
|
27965
28532
|
].filter((file) => existsSync7(file)));
|
|
27966
28533
|
let staleWarned = false;
|
|
27967
28534
|
const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
|
|
@@ -28141,8 +28708,8 @@ var main = async () => {
|
|
|
28141
28708
|
process.exitCode = 2;
|
|
28142
28709
|
}
|
|
28143
28710
|
};
|
|
28144
|
-
var entryPath = process.argv[1] ?
|
|
28145
|
-
if (entryPath ===
|
|
28711
|
+
var entryPath = process.argv[1] ? path16.resolve(process.argv[1]) : "";
|
|
28712
|
+
if (entryPath === path16.resolve(fileURLToPath3(import.meta.url)))
|
|
28146
28713
|
await main();
|
|
28147
28714
|
|
|
28148
28715
|
// packages/workit-cursor/hooks/workit-hook.ts
|
|
@@ -28209,6 +28776,7 @@ var eventNames = new Set([
|
|
|
28209
28776
|
"subagentStop",
|
|
28210
28777
|
"preCompact"
|
|
28211
28778
|
]);
|
|
28779
|
+
var historyOfferSessions = new Set;
|
|
28212
28780
|
if (false)
|
|
28213
28781
|
;
|
|
28214
28782
|
// packages/workit-cursor/mcp/run-server.ts
|