@brainervirus/workit-codex 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/.codex-plugin/plugin.json +1 -1
- package/dist/launch-mcp.js +856 -288
- package/dist/workit-hook.js +1002 -228
- package/package.json +3 -3
- 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/launch-mcp.js
CHANGED
|
@@ -6621,13 +6621,13 @@ var require_dist = __commonJS(function(exports, module) {
|
|
|
6621
6621
|
});
|
|
6622
6622
|
|
|
6623
6623
|
// packages/workit-codex/scripts/launch-mcp.ts
|
|
6624
|
-
import { existsSync as existsSync8, realpathSync as
|
|
6625
|
-
import
|
|
6624
|
+
import { existsSync as existsSync8, realpathSync as realpathSync9, statSync as statSync3 } from "node:fs";
|
|
6625
|
+
import path16 from "node:path";
|
|
6626
6626
|
|
|
6627
6627
|
// packages/workit-core/src/core.ts
|
|
6628
6628
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
6629
|
-
import { existsSync as existsSync5, realpathSync as
|
|
6630
|
-
import
|
|
6629
|
+
import { existsSync as existsSync5, realpathSync as realpathSync7 } from "node:fs";
|
|
6630
|
+
import path11 from "node:path";
|
|
6631
6631
|
|
|
6632
6632
|
// packages/workit-core/src/core/task-contract.ts
|
|
6633
6633
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -11934,6 +11934,17 @@ function _array(Class, element, params) {
|
|
|
11934
11934
|
...normalizeParams(params)
|
|
11935
11935
|
});
|
|
11936
11936
|
}
|
|
11937
|
+
function _custom(Class, fn, _params) {
|
|
11938
|
+
const norm = normalizeParams(_params);
|
|
11939
|
+
norm.abort ?? (norm.abort = true);
|
|
11940
|
+
const schema = new Class({
|
|
11941
|
+
type: "custom",
|
|
11942
|
+
check: "custom",
|
|
11943
|
+
fn,
|
|
11944
|
+
...norm
|
|
11945
|
+
});
|
|
11946
|
+
return schema;
|
|
11947
|
+
}
|
|
11937
11948
|
function _refine(Class, fn, _params) {
|
|
11938
11949
|
const schema = new Class({
|
|
11939
11950
|
type: "custom",
|
|
@@ -14087,6 +14098,9 @@ var ZodCustom = /* @__PURE__ */ $constructor("ZodCustom", (inst, def) => {
|
|
|
14087
14098
|
ZodType.init(inst, def);
|
|
14088
14099
|
inst._zod.processJSONSchema = (ctx, json, params) => customProcessor(inst, ctx, json, params);
|
|
14089
14100
|
});
|
|
14101
|
+
function custom(fn, _params) {
|
|
14102
|
+
return _custom(ZodCustom, fn ?? (() => true), _params);
|
|
14103
|
+
}
|
|
14090
14104
|
function refine(fn, _params = {}) {
|
|
14091
14105
|
return _refine(ZodCustom, fn, _params);
|
|
14092
14106
|
}
|
|
@@ -14520,7 +14534,8 @@ var decisionSchema = object({
|
|
|
14520
14534
|
consumption: object({
|
|
14521
14535
|
state: _enum(["reserved", "consumed", "uncertain"]),
|
|
14522
14536
|
at: utc,
|
|
14523
|
-
actionRef: refSchema
|
|
14537
|
+
actionRef: refSchema,
|
|
14538
|
+
historicalActionId: text.optional()
|
|
14524
14539
|
}).strict().nullable()
|
|
14525
14540
|
}).strict();
|
|
14526
14541
|
var findingSchema = object({
|
|
@@ -14663,7 +14678,7 @@ var taskSummarySchema = object({
|
|
|
14663
14678
|
requirements: array(requirementEvaluationSchema),
|
|
14664
14679
|
writer: workspaceRecordSchema.shape.writer
|
|
14665
14680
|
}).strict();
|
|
14666
|
-
var taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true });
|
|
14681
|
+
var taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true }).extend({ source: provenanceSchema.pick({ host: true, kind: true }) }).strict();
|
|
14667
14682
|
var exportBundleSchema = object({
|
|
14668
14683
|
schemaVersion: literal(1),
|
|
14669
14684
|
exportedAt: utc,
|
|
@@ -14688,6 +14703,7 @@ var taskOperations = {
|
|
|
14688
14703
|
list: operation({
|
|
14689
14704
|
action: literal("list"),
|
|
14690
14705
|
status: _enum(["open", "closed", "all"]).optional(),
|
|
14706
|
+
query: string2().trim().min(1).max(200).optional(),
|
|
14691
14707
|
limit: number2().int().min(1).max(50).optional()
|
|
14692
14708
|
}),
|
|
14693
14709
|
inspect: operation({
|
|
@@ -15035,6 +15051,7 @@ function requirementId(input) {
|
|
|
15035
15051
|
}
|
|
15036
15052
|
// packages/workit-core/src/core/task-store.ts
|
|
15037
15053
|
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
15054
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
15038
15055
|
import * as fs4 from "node:fs";
|
|
15039
15056
|
import { hostname } from "node:os";
|
|
15040
15057
|
import path4 from "node:path";
|
|
@@ -15670,7 +15687,8 @@ var metadataLockSchema = object({
|
|
|
15670
15687
|
pid: number2().int().nonnegative().safe(),
|
|
15671
15688
|
processStart: string2().nullable(),
|
|
15672
15689
|
host: string2().min(1),
|
|
15673
|
-
nonce: string2().min(1)
|
|
15690
|
+
nonce: string2().min(1),
|
|
15691
|
+
externalAction: literal(true).optional()
|
|
15674
15692
|
}).strict();
|
|
15675
15693
|
var now = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
15676
15694
|
var cachedRuntimeVersion = null;
|
|
@@ -15722,6 +15740,28 @@ var sameMetadataLock = (left, right) => {
|
|
|
15722
15740
|
const parsed = metadataLockSchema.safeParse(left);
|
|
15723
15741
|
return parsed.success && canonicalJson(parsed.data) === canonicalJson(right);
|
|
15724
15742
|
};
|
|
15743
|
+
var sameDirectoryIdentity = (left, right) => {
|
|
15744
|
+
if (!path4.isAbsolute(left) || !path4.isAbsolute(right))
|
|
15745
|
+
return false;
|
|
15746
|
+
const normalizedLeft = path4.resolve(left);
|
|
15747
|
+
const normalizedRight = path4.resolve(right);
|
|
15748
|
+
try {
|
|
15749
|
+
const a = fs4.statSync(normalizedLeft, { bigint: true });
|
|
15750
|
+
const b = fs4.statSync(normalizedRight, { bigint: true });
|
|
15751
|
+
if (!a.isDirectory() || !b.isDirectory())
|
|
15752
|
+
return false;
|
|
15753
|
+
if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
|
|
15754
|
+
return true;
|
|
15755
|
+
const canonicalLeft = fs4.realpathSync(normalizedLeft);
|
|
15756
|
+
const canonicalRight = fs4.realpathSync(normalizedRight);
|
|
15757
|
+
if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
|
|
15758
|
+
return true;
|
|
15759
|
+
return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
|
|
15760
|
+
} catch {
|
|
15761
|
+
return false;
|
|
15762
|
+
}
|
|
15763
|
+
};
|
|
15764
|
+
var externalActionLockRoots = new AsyncLocalStorage;
|
|
15725
15765
|
|
|
15726
15766
|
class TaskStore {
|
|
15727
15767
|
root;
|
|
@@ -15782,7 +15822,7 @@ class TaskStore {
|
|
|
15782
15822
|
const item = this.readRecord(this.workspacePath, workspaceRecordSchema);
|
|
15783
15823
|
if (!item.exists)
|
|
15784
15824
|
return success(null, null, null);
|
|
15785
|
-
if (item.result.ok && item.result.data.root !== this.root)
|
|
15825
|
+
if (item.result.ok && item.result.data.root !== this.root && !sameDirectoryIdentity(item.result.data.root, this.root))
|
|
15786
15826
|
return failure("recovery_required", "workspace root binding is invalid", {
|
|
15787
15827
|
path: this.workspacePath
|
|
15788
15828
|
});
|
|
@@ -15873,6 +15913,29 @@ class TaskStore {
|
|
|
15873
15913
|
const current = this.readWorkspace();
|
|
15874
15914
|
if (!current.ok)
|
|
15875
15915
|
return current;
|
|
15916
|
+
const origin = input.task.origin;
|
|
15917
|
+
if (current.data && origin) {
|
|
15918
|
+
if (origin.workspaceId === current.data.id) {
|
|
15919
|
+
const source = this.readTask(origin.taskId);
|
|
15920
|
+
if (source.ok)
|
|
15921
|
+
return success(source.data.revision, current.data.revision, source.data);
|
|
15922
|
+
if (source.code !== "not_found")
|
|
15923
|
+
return source;
|
|
15924
|
+
return failure("not_found", "source task is not present in this shared workspace");
|
|
15925
|
+
}
|
|
15926
|
+
const tasks = this.listTasks();
|
|
15927
|
+
if (!tasks.ok)
|
|
15928
|
+
return tasks;
|
|
15929
|
+
const prior = tasks.data.filter((task) => task.origin?.workspaceId === origin.workspaceId && task.origin?.taskId === origin.taskId);
|
|
15930
|
+
if (prior.length > 1)
|
|
15931
|
+
return failure("recovery_required", `source task has multiple imported mappings: ${prior.map((task) => task.id).join(", ")}`);
|
|
15932
|
+
if (prior.length === 1) {
|
|
15933
|
+
const existing = prior[0];
|
|
15934
|
+
if (existing.origin.exportDigest !== origin.exportDigest)
|
|
15935
|
+
return failure("revision_conflict", "source task export changed; reconcile the mapped destination task before importing", { taskId: existing.id });
|
|
15936
|
+
return success(existing.revision, current.data.revision, existing);
|
|
15937
|
+
}
|
|
15938
|
+
}
|
|
15876
15939
|
if (current.data ? input.expectedWorkspaceRevision !== current.data.revision : input.expectedWorkspaceRevision !== null)
|
|
15877
15940
|
return failure("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", {
|
|
15878
15941
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
@@ -16261,6 +16324,73 @@ class TaskStore {
|
|
|
16261
16324
|
})
|
|
16262
16325
|
};
|
|
16263
16326
|
}
|
|
16327
|
+
externalActionLockOptions() {
|
|
16328
|
+
const options = this.metadataLockOptions();
|
|
16329
|
+
return { ...options, payload: () => ({ ...options.payload(), externalAction: true }) };
|
|
16330
|
+
}
|
|
16331
|
+
async withExternalActionLock(operation, reentrant = false) {
|
|
16332
|
+
const activeRoots = externalActionLockRoots.getStore();
|
|
16333
|
+
if (activeRoots && [...activeRoots].some((root) => root === this.root || sameDirectoryIdentity(root, this.root)))
|
|
16334
|
+
return operation();
|
|
16335
|
+
try {
|
|
16336
|
+
this.initializeMutationStorage();
|
|
16337
|
+
} catch (error) {
|
|
16338
|
+
return failure("storage_error", `unable to initialize store: ${String(error)}`, {
|
|
16339
|
+
path: this.workitDir
|
|
16340
|
+
});
|
|
16341
|
+
}
|
|
16342
|
+
let handle;
|
|
16343
|
+
try {
|
|
16344
|
+
handle = acquireFileLockSync(this.workspacePath, this.externalActionLockOptions());
|
|
16345
|
+
} catch (error) {
|
|
16346
|
+
return this.lockFailure(error);
|
|
16347
|
+
}
|
|
16348
|
+
let result;
|
|
16349
|
+
try {
|
|
16350
|
+
if (!handle.verifyStillHeld())
|
|
16351
|
+
result = failure("recovery_required", "metadata lock was compromised", {
|
|
16352
|
+
path: this.lockPath
|
|
16353
|
+
});
|
|
16354
|
+
else {
|
|
16355
|
+
try {
|
|
16356
|
+
const run = () => operation();
|
|
16357
|
+
result = await (reentrant ? externalActionLockRoots.run(new Set([...activeRoots ?? [], this.root]), run) : run());
|
|
16358
|
+
} catch {
|
|
16359
|
+
result = failure("external_outcome_unknown", "external action outcome is unknown", {
|
|
16360
|
+
outcome: "unknown"
|
|
16361
|
+
});
|
|
16362
|
+
}
|
|
16363
|
+
try {
|
|
16364
|
+
if (!handle.verifyStillHeld())
|
|
16365
|
+
result = failure("external_outcome_unknown", "metadata lock was compromised", {
|
|
16366
|
+
outcome: "unknown",
|
|
16367
|
+
path: this.lockPath
|
|
16368
|
+
});
|
|
16369
|
+
} catch {
|
|
16370
|
+
result = failure("external_outcome_unknown", "metadata lock could not be verified", {
|
|
16371
|
+
outcome: "unknown",
|
|
16372
|
+
path: this.lockPath
|
|
16373
|
+
});
|
|
16374
|
+
}
|
|
16375
|
+
}
|
|
16376
|
+
} catch {
|
|
16377
|
+
result = failure("external_outcome_unknown", "external action lock operation failed", {
|
|
16378
|
+
outcome: "unknown",
|
|
16379
|
+
path: this.lockPath
|
|
16380
|
+
});
|
|
16381
|
+
}
|
|
16382
|
+
try {
|
|
16383
|
+
handle.release();
|
|
16384
|
+
} catch (error) {
|
|
16385
|
+
const released = this.lockFailure(error);
|
|
16386
|
+
const releaseError = released.ok ? "metadata lock release failed" : released.error;
|
|
16387
|
+
return result.ok ? failure("external_outcome_unknown", "external action metadata lock release is uncertain", {
|
|
16388
|
+
outcome: "unknown",
|
|
16389
|
+
path: this.lockPath
|
|
16390
|
+
}) : failure("recovery_required", `${result.error}; ${releaseError}`, result.details);
|
|
16391
|
+
}
|
|
16392
|
+
return result;
|
|
16393
|
+
}
|
|
16264
16394
|
recoveryLockOptions(observed, evidence, gate) {
|
|
16265
16395
|
const options = this.metadataLockOptions();
|
|
16266
16396
|
options.staleRecovery = "remove-if-unchanged";
|
|
@@ -16330,6 +16460,14 @@ class TaskStore {
|
|
|
16330
16460
|
lockFailure(error) {
|
|
16331
16461
|
const value = error;
|
|
16332
16462
|
const code = typeof value?.code === "string" ? value.code : "";
|
|
16463
|
+
if (code === "EEXIST" || code === "file_lock_timeout") {
|
|
16464
|
+
const lock = this.readLockSnapshot();
|
|
16465
|
+
if (lock.ok && lock.data?.data.externalAction)
|
|
16466
|
+
return failure("writer_conflict", "workspace is reserved by a managed external action", {
|
|
16467
|
+
outcome: "not_started",
|
|
16468
|
+
path: this.lockPath
|
|
16469
|
+
});
|
|
16470
|
+
}
|
|
16333
16471
|
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));
|
|
16334
16472
|
return failure(recovery ? "recovery_required" : "storage_error", `metadata lock operation failed: ${String(error)}`, { path: this.lockPath });
|
|
16335
16473
|
}
|
|
@@ -16597,21 +16735,23 @@ function assertProductWriteAllowed(input) {
|
|
|
16597
16735
|
}
|
|
16598
16736
|
|
|
16599
16737
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
16600
|
-
import { createHash as
|
|
16738
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
16601
16739
|
import * as fs7 from "node:fs";
|
|
16602
16740
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
16603
|
-
import
|
|
16741
|
+
import path10 from "node:path";
|
|
16604
16742
|
|
|
16605
16743
|
// packages/workit-core/src/core/authority.ts
|
|
16606
|
-
import { createHash as
|
|
16744
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
16607
16745
|
import * as fs6 from "node:fs";
|
|
16608
|
-
import
|
|
16746
|
+
import path9 from "node:path";
|
|
16609
16747
|
|
|
16610
16748
|
// packages/workit-core/src/core/auto-approval.ts
|
|
16611
|
-
import { createHash as
|
|
16612
|
-
import { readFileSync as readFileSync4 } from "node:fs";
|
|
16749
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
16750
|
+
import { readFileSync as readFileSync4, realpathSync as realpathSync3 } from "node:fs";
|
|
16751
|
+
import path8 from "node:path";
|
|
16613
16752
|
|
|
16614
16753
|
// packages/workit-core/src/core/workspaces.ts
|
|
16754
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
16615
16755
|
import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
16616
16756
|
import path6 from "node:path";
|
|
16617
16757
|
|
|
@@ -16629,7 +16769,7 @@ import os from "node:os";
|
|
|
16629
16769
|
import path5 from "node:path";
|
|
16630
16770
|
|
|
16631
16771
|
// packages/workit-core/src/core/boundary.ts
|
|
16632
|
-
import { statSync } from "node:fs";
|
|
16772
|
+
import { statSync as statSync2 } from "node:fs";
|
|
16633
16773
|
var EVENT = {
|
|
16634
16774
|
initialization: "initialization",
|
|
16635
16775
|
provenance: "provenance",
|
|
@@ -16657,7 +16797,7 @@ var changedSourcesSinceLoad = (marker) => {
|
|
|
16657
16797
|
const changed = [];
|
|
16658
16798
|
for (const file of marker.files) {
|
|
16659
16799
|
try {
|
|
16660
|
-
if (
|
|
16800
|
+
if (statSync2(file).mtimeMs > marker.loadedAt)
|
|
16661
16801
|
changed.push(file);
|
|
16662
16802
|
} catch {}
|
|
16663
16803
|
}
|
|
@@ -16816,33 +16956,197 @@ var COMMIT_PRESETS = [
|
|
|
16816
16956
|
"custom",
|
|
16817
16957
|
"auto"
|
|
16818
16958
|
];
|
|
16959
|
+
var resolveCommitPolicy = (config, workspace) => {
|
|
16960
|
+
const wp = workspace?.commitPolicy ?? {};
|
|
16961
|
+
if (COMMIT_PRESETS.includes(String(wp.preset ?? ""))) {
|
|
16962
|
+
return {
|
|
16963
|
+
preset: wp.preset,
|
|
16964
|
+
...typeof wp.pattern === "string" ? { pattern: wp.pattern } : {}
|
|
16965
|
+
};
|
|
16966
|
+
}
|
|
16967
|
+
return config.commitPolicy ?? { preset: "conventional" };
|
|
16968
|
+
};
|
|
16819
16969
|
|
|
16820
16970
|
// packages/workit-core/src/core/workspaces.ts
|
|
16971
|
+
var nonBlank = string2().refine((value) => value.trim().length > 0, "must not be blank");
|
|
16972
|
+
var branchPreset = custom((value) => typeof value === "string" && Object.hasOwn(PRESETS, value), "unsupported branch preset");
|
|
16973
|
+
var commitPreset = custom((value) => typeof value === "string" && COMMIT_PRESETS.includes(value), "unsupported commit preset");
|
|
16974
|
+
var workspaceBranchPolicySchema = object({
|
|
16975
|
+
preset: branchPreset,
|
|
16976
|
+
developBranch: nonBlank.optional(),
|
|
16977
|
+
prefixes: object({
|
|
16978
|
+
feature: nonBlank,
|
|
16979
|
+
bugfix: nonBlank,
|
|
16980
|
+
release: nonBlank,
|
|
16981
|
+
hotfix: nonBlank
|
|
16982
|
+
}).passthrough().optional(),
|
|
16983
|
+
allowed: array(nonBlank).optional(),
|
|
16984
|
+
protected: array(nonBlank).optional(),
|
|
16985
|
+
integration: _enum(["pr", "merge"]).optional()
|
|
16986
|
+
}).strict();
|
|
16987
|
+
var workspaceCommitPolicySchema = object({ preset: commitPreset, pattern: string2().optional() }).strict();
|
|
16988
|
+
var workspaceProfileSchema = object({
|
|
16989
|
+
branchPolicy: workspaceBranchPolicySchema.optional(),
|
|
16990
|
+
commitPolicy: workspaceCommitPolicySchema.optional()
|
|
16991
|
+
}).strict().refine((profile) => profile.branchPolicy !== undefined || profile.commitPolicy !== undefined, {
|
|
16992
|
+
message: "a profile must define branchPolicy or commitPolicy"
|
|
16993
|
+
});
|
|
16994
|
+
var versionSourceSchema = discriminatedUnion("kind", [
|
|
16995
|
+
object({
|
|
16996
|
+
kind: literal("package-json"),
|
|
16997
|
+
path: nonBlank,
|
|
16998
|
+
field: nonBlank
|
|
16999
|
+
}).strict(),
|
|
17000
|
+
object({ kind: literal("git-tag") }).strict(),
|
|
17001
|
+
object({ kind: literal("manual") }).strict()
|
|
17002
|
+
]);
|
|
17003
|
+
var releaseTrackSchema = object({
|
|
17004
|
+
strategy: branchPreset,
|
|
17005
|
+
productionBranch: nonBlank,
|
|
17006
|
+
integrationBranch: nonBlank,
|
|
17007
|
+
naming: object({ feature: nonBlank, release: nonBlank, hotfix: nonBlank }).strict(),
|
|
17008
|
+
baseBranch: nonBlank,
|
|
17009
|
+
mergeBackBranches: array(nonBlank),
|
|
17010
|
+
pullRequestTarget: nonBlank,
|
|
17011
|
+
tagNamespace: string2(),
|
|
17012
|
+
versionSource: versionSourceSchema,
|
|
17013
|
+
requiredChecks: array(nonBlank)
|
|
17014
|
+
}).strict().superRefine((track, context) => {
|
|
17015
|
+
for (const [field, values] of [
|
|
17016
|
+
["mergeBackBranches", track.mergeBackBranches],
|
|
17017
|
+
["requiredChecks", track.requiredChecks]
|
|
17018
|
+
]) {
|
|
17019
|
+
if (new Set(values).size !== values.length) {
|
|
17020
|
+
context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
|
|
17021
|
+
}
|
|
17022
|
+
}
|
|
17023
|
+
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(".."))) {
|
|
17024
|
+
context.addIssue({
|
|
17025
|
+
code: "custom",
|
|
17026
|
+
path: ["versionSource", "path"],
|
|
17027
|
+
message: "must stay inside the repository"
|
|
17028
|
+
});
|
|
17029
|
+
}
|
|
17030
|
+
});
|
|
17031
|
+
var workspaceConfigSchema = object({
|
|
17032
|
+
name: nonBlank,
|
|
17033
|
+
glob: nonBlank,
|
|
17034
|
+
vcs: object({
|
|
17035
|
+
provider: _enum(["gitlab", "github"]),
|
|
17036
|
+
defaultTargetBranch: nonBlank.optional(),
|
|
17037
|
+
tokenFile: nonBlank.optional(),
|
|
17038
|
+
account: nonBlank.optional()
|
|
17039
|
+
}).passthrough().optional(),
|
|
17040
|
+
autoApprove: union([boolean2(), array(nonBlank)]).optional(),
|
|
17041
|
+
youtrack: object({ baseUrl: nonBlank.optional(), link_issues: boolean2().optional() }).passthrough().optional(),
|
|
17042
|
+
issues: object({ provider: literal("github").optional(), link_on_pr: boolean2().optional() }).passthrough().optional(),
|
|
17043
|
+
branchPolicy: workspaceBranchPolicySchema.optional(),
|
|
17044
|
+
commitPolicy: workspaceCommitPolicySchema.optional(),
|
|
17045
|
+
defaultProfile: nonBlank.optional(),
|
|
17046
|
+
profiles: record(nonBlank, workspaceProfileSchema).optional(),
|
|
17047
|
+
releaseTracks: record(nonBlank, releaseTrackSchema).optional()
|
|
17048
|
+
}).passthrough().superRefine((workspace, context) => {
|
|
17049
|
+
if (workspace.youtrack && workspace.vcs?.provider !== "gitlab") {
|
|
17050
|
+
context.addIssue({
|
|
17051
|
+
code: "custom",
|
|
17052
|
+
path: ["youtrack"],
|
|
17053
|
+
message: `YouTrack issue linking requires the gitlab provider, got ${workspace.vcs?.provider ?? "unset"}`
|
|
17054
|
+
});
|
|
17055
|
+
}
|
|
17056
|
+
if (workspace.issues && workspace.vcs?.provider !== "github") {
|
|
17057
|
+
context.addIssue({
|
|
17058
|
+
code: "custom",
|
|
17059
|
+
path: ["issues"],
|
|
17060
|
+
message: `GitHub issue linking requires the github provider, got ${workspace.vcs?.provider ?? "unset"}`
|
|
17061
|
+
});
|
|
17062
|
+
}
|
|
17063
|
+
if (workspace.defaultProfile !== undefined && !Object.hasOwn(workspace.profiles ?? {}, workspace.defaultProfile)) {
|
|
17064
|
+
context.addIssue({
|
|
17065
|
+
code: "custom",
|
|
17066
|
+
path: ["defaultProfile"],
|
|
17067
|
+
message: `does not name a configured profile`
|
|
17068
|
+
});
|
|
17069
|
+
}
|
|
17070
|
+
});
|
|
17071
|
+
var workspacesFileSchema = object({ workspaces: array(workspaceConfigSchema).optional() }).passthrough().superRefine((file, context) => {
|
|
17072
|
+
const names = new Set;
|
|
17073
|
+
for (const [index, workspace] of (file.workspaces ?? []).entries()) {
|
|
17074
|
+
if (names.has(workspace.name)) {
|
|
17075
|
+
context.addIssue({
|
|
17076
|
+
code: "custom",
|
|
17077
|
+
path: ["workspaces", index, "name"],
|
|
17078
|
+
message: `duplicate workspace name ${JSON.stringify(workspace.name)}`
|
|
17079
|
+
});
|
|
17080
|
+
}
|
|
17081
|
+
names.add(workspace.name);
|
|
17082
|
+
}
|
|
17083
|
+
});
|
|
17084
|
+
var workspacesRevision = (contents) => createHash3("sha256").update(contents ?? "<missing>").digest("hex");
|
|
17085
|
+
var validateWorkspacesDocument = (parsed, file) => {
|
|
17086
|
+
if (!isConfigObject(parsed)) {
|
|
17087
|
+
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
17088
|
+
}
|
|
17089
|
+
const validated = workspacesFileSchema.safeParse(parsed, { reportInput: true });
|
|
17090
|
+
if (!validated.success) {
|
|
17091
|
+
const detail = validated.error.issues.map((issue) => {
|
|
17092
|
+
const field = String(issue.path[issue.path.length - 1] ?? "");
|
|
17093
|
+
const attempted = ["provider", "preset", "strategy"].includes(field) && typeof issue.input === "string" ? ` (${JSON.stringify(issue.input)})` : "";
|
|
17094
|
+
return `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}${attempted}`;
|
|
17095
|
+
}).join("; ");
|
|
17096
|
+
return {
|
|
17097
|
+
status: "invalid",
|
|
17098
|
+
path: file,
|
|
17099
|
+
entries: [],
|
|
17100
|
+
error: `${file} has invalid workspace configuration: ${detail}`
|
|
17101
|
+
};
|
|
17102
|
+
}
|
|
17103
|
+
for (const [index, workspace] of (validated.data.workspaces ?? []).entries()) {
|
|
17104
|
+
const glob = validateWorkspaceGlob(workspace.glob);
|
|
17105
|
+
if (!glob.ok) {
|
|
17106
|
+
return {
|
|
17107
|
+
status: "invalid",
|
|
17108
|
+
path: file,
|
|
17109
|
+
entries: [],
|
|
17110
|
+
error: `${file} workspaces.${index}.glob: ${glob.error}`
|
|
17111
|
+
};
|
|
17112
|
+
}
|
|
17113
|
+
}
|
|
17114
|
+
return {
|
|
17115
|
+
status: "valid",
|
|
17116
|
+
path: file,
|
|
17117
|
+
entries: validated.data.workspaces ?? [],
|
|
17118
|
+
document: parsed
|
|
17119
|
+
};
|
|
17120
|
+
};
|
|
16821
17121
|
var readWorkspacesResult = (dir = configDir()) => {
|
|
16822
17122
|
const file = path6.join(dir, "workspaces.json");
|
|
16823
17123
|
let raw;
|
|
16824
17124
|
try {
|
|
16825
17125
|
raw = readFileSync3(file, "utf8");
|
|
16826
|
-
} catch {
|
|
16827
|
-
|
|
17126
|
+
} catch (error) {
|
|
17127
|
+
if (error.code === "ENOENT")
|
|
17128
|
+
return { status: "missing", path: file, entries: [], revision: workspacesRevision(null) };
|
|
17129
|
+
return {
|
|
17130
|
+
status: "malformed",
|
|
17131
|
+
path: file,
|
|
17132
|
+
entries: [],
|
|
17133
|
+
error: `${file} could not be read: ${error instanceof Error ? error.message : String(error)}`
|
|
17134
|
+
};
|
|
16828
17135
|
}
|
|
16829
17136
|
let parsed;
|
|
16830
17137
|
try {
|
|
16831
17138
|
parsed = JSON.parse(raw);
|
|
16832
17139
|
} catch {
|
|
16833
|
-
return {
|
|
16834
|
-
|
|
16835
|
-
|
|
16836
|
-
|
|
17140
|
+
return {
|
|
17141
|
+
status: "malformed",
|
|
17142
|
+
path: file,
|
|
17143
|
+
entries: [],
|
|
17144
|
+
revision: workspacesRevision(raw),
|
|
17145
|
+
error: `${file} is not valid JSON`
|
|
17146
|
+
};
|
|
16837
17147
|
}
|
|
16838
|
-
|
|
16839
|
-
return {
|
|
16840
|
-
status: "valid",
|
|
16841
|
-
path: file,
|
|
16842
|
-
entries: Array.isArray(list) ? list : []
|
|
16843
|
-
};
|
|
17148
|
+
return { ...validateWorkspacesDocument(parsed, file), revision: workspacesRevision(raw) };
|
|
16844
17149
|
};
|
|
16845
|
-
var loadWorkspacesFrom = (dir) => readWorkspacesResult(dir).entries;
|
|
16846
17150
|
var globToRegExp = (glob) => {
|
|
16847
17151
|
let out = "";
|
|
16848
17152
|
for (let i = 0;i < glob.length; i++) {
|
|
@@ -16877,6 +17181,30 @@ var globToRegExp = (glob) => {
|
|
|
16877
17181
|
return new RegExp(`^${out}$`);
|
|
16878
17182
|
};
|
|
16879
17183
|
var matchWorkspace = (glob, target) => globToRegExp(glob.replaceAll("\\", "/")).test(target.replaceAll("\\", "/"));
|
|
17184
|
+
var UNSUPPORTED_GLOB = /[?[\]{}]/;
|
|
17185
|
+
var UNSUPPORTED_EXTGLOB = /[@+*]\(|!/;
|
|
17186
|
+
var validateWorkspaceGlob = (glob) => {
|
|
17187
|
+
const trimmed = glob.trim();
|
|
17188
|
+
if (!trimmed)
|
|
17189
|
+
return { ok: false, error: "workspace pattern is required" };
|
|
17190
|
+
const m = UNSUPPORTED_GLOB.exec(trimmed);
|
|
17191
|
+
if (m) {
|
|
17192
|
+
return {
|
|
17193
|
+
ok: false,
|
|
17194
|
+
error: `unsupported glob character ${JSON.stringify(m[0])} in workspace pattern ${JSON.stringify(glob)} — the matcher supports * and ** only (e.g. /work/**)`
|
|
17195
|
+
};
|
|
17196
|
+
}
|
|
17197
|
+
const ext = UNSUPPORTED_EXTGLOB.exec(trimmed);
|
|
17198
|
+
if (ext) {
|
|
17199
|
+
const token = ext[0];
|
|
17200
|
+
const kind = token === "!" ? "negation (!)" : `extglob (${token})`;
|
|
17201
|
+
return {
|
|
17202
|
+
ok: false,
|
|
17203
|
+
error: `unsupported glob ${kind} in workspace pattern ${JSON.stringify(glob)} — the matcher supports * and ** only (e.g. /work/**)`
|
|
17204
|
+
};
|
|
17205
|
+
}
|
|
17206
|
+
return { ok: true };
|
|
17207
|
+
};
|
|
16880
17208
|
var realpathOf = (p) => {
|
|
16881
17209
|
const real = realpathSync2.native ?? realpathSync2;
|
|
16882
17210
|
try {
|
|
@@ -16894,29 +17222,137 @@ var canonicalGlob = (glob) => {
|
|
|
16894
17222
|
const real = realpathOf(prefix.replace(/\/+$/, "") || "/");
|
|
16895
17223
|
return real + (prefix.endsWith("/") ? "/" : "") + rest;
|
|
16896
17224
|
};
|
|
16897
|
-
var resolveWorkspaceFrom = (cwd, dir) => {
|
|
17225
|
+
var resolveWorkspaceFrom = (cwd, dir, workspaceName) => {
|
|
17226
|
+
const result = readWorkspacesResult(dir);
|
|
17227
|
+
if (result.status === "malformed" || result.status === "invalid")
|
|
17228
|
+
throw new Error(result.error);
|
|
16898
17229
|
const targets = [cwd, realpathOf(cwd)].map((p) => p.replaceAll("\\", "/"));
|
|
16899
|
-
|
|
16900
|
-
|
|
16901
|
-
continue;
|
|
16902
|
-
const ws = entry;
|
|
16903
|
-
if (typeof ws.glob !== "string" || !ws.glob)
|
|
16904
|
-
continue;
|
|
17230
|
+
const matches = [];
|
|
17231
|
+
for (const ws of result.entries) {
|
|
16905
17232
|
const glob = ws.glob.replaceAll("\\", "/");
|
|
16906
17233
|
const canonical = canonicalGlob(glob);
|
|
17234
|
+
let matched = false;
|
|
16907
17235
|
for (const target of targets) {
|
|
16908
|
-
if (matchWorkspace(glob, target))
|
|
16909
|
-
|
|
16910
|
-
|
|
16911
|
-
|
|
17236
|
+
if (matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target)) {
|
|
17237
|
+
matched = true;
|
|
17238
|
+
break;
|
|
17239
|
+
}
|
|
16912
17240
|
}
|
|
17241
|
+
if (matched)
|
|
17242
|
+
matches.push(ws);
|
|
16913
17243
|
}
|
|
16914
|
-
|
|
17244
|
+
if (workspaceName !== undefined) {
|
|
17245
|
+
const selected = matches.find((ws) => ws.name === workspaceName);
|
|
17246
|
+
if (!selected && matches.length > 0) {
|
|
17247
|
+
const matchingNames = matches.map((ws) => ws.name);
|
|
17248
|
+
throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matchingNames.join(", ")}`);
|
|
17249
|
+
}
|
|
17250
|
+
if (selected)
|
|
17251
|
+
return selected;
|
|
17252
|
+
throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
|
|
17253
|
+
}
|
|
17254
|
+
if (matches.length > 1) {
|
|
17255
|
+
throw new Error(`ambiguous workspace for ${cwd}; choose one of: ${matches.map((ws) => ws.name).join(", ")}`);
|
|
17256
|
+
}
|
|
17257
|
+
return matches[0] ?? null;
|
|
17258
|
+
};
|
|
17259
|
+
var runtimeWorkspacePolicySchema = (kind) => {
|
|
17260
|
+
const key = kind === "branch" ? "branchPolicy" : kind === "commit" ? "commitPolicy" : "vcs";
|
|
17261
|
+
const policySchema = kind === "branch" ? workspaceBranchPolicySchema : kind === "commit" ? workspaceCommitPolicySchema : object({
|
|
17262
|
+
provider: _enum(["gitlab", "github"]),
|
|
17263
|
+
defaultTargetBranch: nonBlank.optional(),
|
|
17264
|
+
account: nonBlank.optional()
|
|
17265
|
+
}).passthrough();
|
|
17266
|
+
const profileSchema = object({ [key]: policySchema.optional() }).passthrough();
|
|
17267
|
+
const entrySchema = object({
|
|
17268
|
+
name: nonBlank,
|
|
17269
|
+
glob: nonBlank,
|
|
17270
|
+
[key]: policySchema.optional(),
|
|
17271
|
+
defaultProfile: nonBlank.optional(),
|
|
17272
|
+
profiles: record(nonBlank, profileSchema).optional()
|
|
17273
|
+
}).passthrough();
|
|
17274
|
+
return entrySchema;
|
|
17275
|
+
};
|
|
17276
|
+
var resolveRuntimeWorkspaceVcs = (cwd) => resolveRuntimeWorkspaceCandidate(cwd, "vcs");
|
|
17277
|
+
var runtimeWorkspaceIndexSchema = object({
|
|
17278
|
+
workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
17279
|
+
}).passthrough();
|
|
17280
|
+
var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
17281
|
+
const file = path6.join(configDir(), "workspaces.json");
|
|
17282
|
+
let raw;
|
|
17283
|
+
try {
|
|
17284
|
+
raw = readFileSync3(file, "utf8");
|
|
17285
|
+
} catch (error) {
|
|
17286
|
+
if (error.code === "ENOENT")
|
|
17287
|
+
return null;
|
|
17288
|
+
throw new Error(`${file} could not be read: ${error instanceof Error ? error.message : String(error)}`);
|
|
17289
|
+
}
|
|
17290
|
+
let parsed;
|
|
17291
|
+
try {
|
|
17292
|
+
parsed = JSON.parse(raw);
|
|
17293
|
+
} catch {
|
|
17294
|
+
throw new Error(`${file} is not valid JSON`);
|
|
17295
|
+
}
|
|
17296
|
+
const validated = runtimeWorkspaceIndexSchema.safeParse(parsed);
|
|
17297
|
+
if (!validated.success) {
|
|
17298
|
+
const detail = validated.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}`).join("; ");
|
|
17299
|
+
throw new Error(`${file} has invalid workspace matching data: ${detail}`);
|
|
17300
|
+
}
|
|
17301
|
+
const entries = validated.data.workspaces;
|
|
17302
|
+
for (const entry of entries ?? []) {
|
|
17303
|
+
const glob = validateWorkspaceGlob(entry.glob);
|
|
17304
|
+
if (!glob.ok)
|
|
17305
|
+
throw new Error(`${file} workspace ${entry.name}: ${glob.error}`);
|
|
17306
|
+
}
|
|
17307
|
+
const targets = [cwd, realpathOf(cwd)].map((value) => value.replaceAll("\\", "/"));
|
|
17308
|
+
const matches = (entries ?? []).filter((entry) => {
|
|
17309
|
+
const glob = entry.glob.replaceAll("\\", "/");
|
|
17310
|
+
const canonical = canonicalGlob(glob);
|
|
17311
|
+
return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
|
|
17312
|
+
});
|
|
17313
|
+
const workspaceName = process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined;
|
|
17314
|
+
let selected;
|
|
17315
|
+
if (workspaceName) {
|
|
17316
|
+
const named = matches.filter((entry) => entry.name === workspaceName);
|
|
17317
|
+
if (named.length === 1)
|
|
17318
|
+
selected = named[0];
|
|
17319
|
+
else if (named.length > 1)
|
|
17320
|
+
throw new Error(`ambiguous workspace ${JSON.stringify(workspaceName)} for ${cwd}`);
|
|
17321
|
+
else if (matches.length)
|
|
17322
|
+
throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matches.map((entry) => entry.name).join(", ")}`);
|
|
17323
|
+
else
|
|
17324
|
+
throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
|
|
17325
|
+
} else if (matches.length > 1)
|
|
17326
|
+
throw new Error(`ambiguous workspace for ${cwd}; choose one of: ${matches.map((entry) => entry.name).join(", ")}`);
|
|
17327
|
+
else
|
|
17328
|
+
selected = matches[0];
|
|
17329
|
+
if (!selected)
|
|
17330
|
+
return null;
|
|
17331
|
+
const policy = runtimeWorkspacePolicySchema(kind).safeParse(selected);
|
|
17332
|
+
if (!policy.success) {
|
|
17333
|
+
const detail = policy.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspace"}: ${issue.message}`).join("; ");
|
|
17334
|
+
throw new Error(`${file} has invalid ${kind} policy configuration: ${detail}`);
|
|
17335
|
+
}
|
|
17336
|
+
return policy.data;
|
|
17337
|
+
};
|
|
17338
|
+
var resolveRuntimeWorkspacePolicy = (cwd, kind, requestedProfile) => {
|
|
17339
|
+
const candidate = resolveRuntimeWorkspaceCandidate(cwd, kind);
|
|
17340
|
+
if (!candidate)
|
|
17341
|
+
return { policy: undefined, source: "user-default" };
|
|
17342
|
+
const key = kind === "branch" ? "branchPolicy" : "commitPolicy";
|
|
17343
|
+
const profileName = requestedProfile?.trim() || candidate.defaultProfile;
|
|
17344
|
+
if (candidate.defaultProfile && !Object.hasOwn(candidate.profiles ?? {}, candidate.defaultProfile))
|
|
17345
|
+
throw new Error(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(candidate.defaultProfile)}`);
|
|
17346
|
+
const profile = profileName ? candidate.profiles?.[profileName] : undefined;
|
|
17347
|
+
if (profileName && !profile)
|
|
17348
|
+
throw new Error(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(profileName)}`);
|
|
17349
|
+
const profilePolicy = profile?.[key];
|
|
17350
|
+
const workspacePolicy = candidate[key];
|
|
17351
|
+
return {
|
|
17352
|
+
policy: profilePolicy ?? workspacePolicy,
|
|
17353
|
+
source: profilePolicy ? `profile:${profileName}` : workspacePolicy ? `workspace:${candidate.name}` : "user-default"
|
|
17354
|
+
};
|
|
16915
17355
|
};
|
|
16916
|
-
var resolveWorkspace = (cwd) => resolveWorkspaceFrom(cwd, configDir());
|
|
16917
|
-
|
|
16918
|
-
// packages/workit-core/src/core/branch.ts
|
|
16919
|
-
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
16920
17356
|
|
|
16921
17357
|
// packages/workit-core/src/core/git.ts
|
|
16922
17358
|
import { execFileSync } from "node:child_process";
|
|
@@ -16990,7 +17426,6 @@ var gitContext = (workspaceRoot, paths = []) => {
|
|
|
16990
17426
|
import fs5 from "node:fs";
|
|
16991
17427
|
import path7 from "node:path";
|
|
16992
17428
|
import { spawnSync } from "node:child_process";
|
|
16993
|
-
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
16994
17429
|
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path7.join(configDir(), "vcs.json");
|
|
16995
17430
|
var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
16996
17431
|
var remoteProvider = (cwd) => {
|
|
@@ -17004,6 +17439,79 @@ var remoteProvider = (cwd) => {
|
|
|
17004
17439
|
return "gitlab";
|
|
17005
17440
|
return null;
|
|
17006
17441
|
};
|
|
17442
|
+
var remoteEndpoint = (remote) => {
|
|
17443
|
+
const raw = remote.trim();
|
|
17444
|
+
if (!raw)
|
|
17445
|
+
return null;
|
|
17446
|
+
const scp = /^(?:[^@\s]+@)?([^:]+):(.+)$/u.exec(raw);
|
|
17447
|
+
if (scp && !raw.includes("://"))
|
|
17448
|
+
return {
|
|
17449
|
+
host: scp[1].toLowerCase(),
|
|
17450
|
+
path: scp[2].replace(/^\//u, "").replace(/\.git$/u, ""),
|
|
17451
|
+
protocol: "ssh:",
|
|
17452
|
+
port: ""
|
|
17453
|
+
};
|
|
17454
|
+
try {
|
|
17455
|
+
const url = new URL(raw);
|
|
17456
|
+
return {
|
|
17457
|
+
host: url.hostname.toLowerCase(),
|
|
17458
|
+
path: url.pathname.replace(/^\//u, "").replace(/\.git$/u, "").replace(/\/$/u, ""),
|
|
17459
|
+
protocol: url.protocol,
|
|
17460
|
+
port: url.port
|
|
17461
|
+
};
|
|
17462
|
+
} catch {
|
|
17463
|
+
return null;
|
|
17464
|
+
}
|
|
17465
|
+
};
|
|
17466
|
+
var configuredSshEndpoint = (host) => {
|
|
17467
|
+
if (host.startsWith("-"))
|
|
17468
|
+
return null;
|
|
17469
|
+
const resolved = spawnSync("ssh", ["-G", host], {
|
|
17470
|
+
encoding: "utf8",
|
|
17471
|
+
timeout: 5000,
|
|
17472
|
+
maxBuffer: 1024 * 1024,
|
|
17473
|
+
env: { ...process.env, PATH: process.env.PATH ?? "" }
|
|
17474
|
+
});
|
|
17475
|
+
if (resolved.status !== 0)
|
|
17476
|
+
return null;
|
|
17477
|
+
const output = resolved.stdout ?? "";
|
|
17478
|
+
const hostname = /^hostname\s+(\S+)/imu.exec(output)?.[1]?.toLowerCase();
|
|
17479
|
+
if (!hostname)
|
|
17480
|
+
return null;
|
|
17481
|
+
return { host: hostname, port: /^port\s+(\d+)/imu.exec(output)?.[1] ?? "22" };
|
|
17482
|
+
};
|
|
17483
|
+
var sshApiHostAliases = {
|
|
17484
|
+
"github.com": [{ host: "ssh.github.com", port: "443" }],
|
|
17485
|
+
"gitlab.com": [{ host: "altssh.gitlab.com", port: "443" }]
|
|
17486
|
+
};
|
|
17487
|
+
var hostingApiHostMatches = (remote, apiHost, resolveSshEndpoint = configuredSshEndpoint) => {
|
|
17488
|
+
const endpoint = remoteEndpoint(remote);
|
|
17489
|
+
if (!endpoint)
|
|
17490
|
+
return false;
|
|
17491
|
+
let expected;
|
|
17492
|
+
try {
|
|
17493
|
+
const url = new URL(`https://${apiHost}`);
|
|
17494
|
+
if (url.pathname !== "/" || url.search || url.hash || url.username || url.password)
|
|
17495
|
+
return false;
|
|
17496
|
+
expected = {
|
|
17497
|
+
host: url.hostname.toLowerCase().replace(/\.$/u, ""),
|
|
17498
|
+
port: url.port
|
|
17499
|
+
};
|
|
17500
|
+
} catch {
|
|
17501
|
+
return false;
|
|
17502
|
+
}
|
|
17503
|
+
if (endpoint.protocol !== "ssh:") {
|
|
17504
|
+
return endpoint.host.replace(/\.$/u, "") === expected.host && endpoint.port === expected.port;
|
|
17505
|
+
}
|
|
17506
|
+
const resolved = resolveSshEndpoint(endpoint.host);
|
|
17507
|
+
if (!resolved)
|
|
17508
|
+
return false;
|
|
17509
|
+
const hostname = resolved.host.toLowerCase().replace(/\.$/u, "");
|
|
17510
|
+
const port = endpoint.port || resolved.port;
|
|
17511
|
+
if (hostname === expected.host)
|
|
17512
|
+
return port === (expected.port || "22");
|
|
17513
|
+
return Boolean(sshApiHostAliases[expected.host]?.some((alias) => alias.host === hostname && alias.port === port && (!expected.port || expected.port === port)));
|
|
17514
|
+
};
|
|
17007
17515
|
var readVcsConfig = () => {
|
|
17008
17516
|
const file = vcsConfigPath();
|
|
17009
17517
|
let raw;
|
|
@@ -17024,7 +17532,7 @@ var readVcsConfig = () => {
|
|
|
17024
17532
|
return { status: "malformed", path: file, config: {}, error: `${file} is not a JSON object` };
|
|
17025
17533
|
};
|
|
17026
17534
|
function vcsConfig(mode, cwd) {
|
|
17027
|
-
const ws =
|
|
17535
|
+
const ws = resolveRuntimeWorkspaceVcs(vcsCwd(cwd));
|
|
17028
17536
|
const wsVcs = ws?.vcs ?? {};
|
|
17029
17537
|
const wsYt = ws?.youtrack ?? {};
|
|
17030
17538
|
const wsIssues = ws?.issues ?? {};
|
|
@@ -17037,8 +17545,8 @@ function vcsConfig(mode, cwd) {
|
|
|
17037
17545
|
const hasWorkspacePolicy = typeof wp.preset === "string" && Object.hasOwn(PRESETS, wp.preset);
|
|
17038
17546
|
const policyDefault = resolveBranchPolicyFor(root).defaultTargetBranch;
|
|
17039
17547
|
const defaultTarget = String(wsVcs.defaultTargetBranch ?? (hasWorkspacePolicy ? policyDefault : cfg.defaultTargetBranch ?? policyDefault) ?? "develop");
|
|
17040
|
-
const linkIssues = typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
|
|
17041
|
-
const youtrackBaseUrl = typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
|
|
17548
|
+
const linkIssues = provider === "gitlab" && typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
|
|
17549
|
+
const youtrackBaseUrl = provider === "gitlab" && typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
|
|
17042
17550
|
let issuesProvider = null;
|
|
17043
17551
|
let linkOnPr = null;
|
|
17044
17552
|
if (provider === "github" && typeof wsIssues.provider === "string" && wsIssues.provider.toLowerCase() === "github") {
|
|
@@ -17062,9 +17570,8 @@ function vcsConfig(mode, cwd) {
|
|
|
17062
17570
|
if (cfgStatus === "malformed") {
|
|
17063
17571
|
return { ok: false, error: cfgError, configPath: cfgPath };
|
|
17064
17572
|
}
|
|
17065
|
-
if (cfgStatus === "missing")
|
|
17573
|
+
if (cfgStatus === "missing" && provider === null)
|
|
17066
17574
|
return { ok: false, error: `vcs.json is missing: ${cfgPath}`, configPath: cfgPath };
|
|
17067
|
-
}
|
|
17068
17575
|
if (provider === null) {
|
|
17069
17576
|
return {
|
|
17070
17577
|
ok: false,
|
|
@@ -17073,24 +17580,12 @@ function vcsConfig(mode, cwd) {
|
|
|
17073
17580
|
};
|
|
17074
17581
|
}
|
|
17075
17582
|
const prov = cfg[provider] ?? {};
|
|
17076
|
-
const wsTokenFile = typeof wsVcs.tokenFile === "string" && wsVcs.tokenFile.trim() !== "" ? wsVcs.tokenFile : null;
|
|
17077
|
-
const tokenFile = String(wsTokenFile ?? prov.tokenFile ?? path7.join(configDir(), `${provider}.token`));
|
|
17078
|
-
const tokenPath = path7.resolve(tokenFile);
|
|
17079
|
-
let tokenOk = false;
|
|
17080
|
-
if (fs5.existsSync(tokenPath)) {
|
|
17081
|
-
const token = fs5.readFileSync(tokenPath, "utf8").trim();
|
|
17082
|
-
const placeholder = !token || token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER);
|
|
17083
|
-
tokenOk = !placeholder;
|
|
17084
|
-
}
|
|
17085
17583
|
const out = {
|
|
17086
17584
|
ok: true,
|
|
17087
17585
|
configPath: path7.resolve(cfgPath),
|
|
17088
17586
|
provider,
|
|
17089
17587
|
defaultTargetBranch: defaultTarget,
|
|
17090
17588
|
pr: cfg.pr ?? {},
|
|
17091
|
-
tokenPath,
|
|
17092
|
-
tokenPresent: fs5.existsSync(tokenPath),
|
|
17093
|
-
tokenReady: tokenOk,
|
|
17094
17589
|
workspace_name: ws?.name ?? null,
|
|
17095
17590
|
link_issues: linkIssues,
|
|
17096
17591
|
youtrack_base_url: youtrackBaseUrl,
|
|
@@ -17105,16 +17600,58 @@ function vcsConfig(mode, cwd) {
|
|
|
17105
17600
|
} else if (provider === "github") {
|
|
17106
17601
|
out.github = { host: prov.host ?? "github.com" };
|
|
17107
17602
|
}
|
|
17108
|
-
if (mode === "summary")
|
|
17109
|
-
delete out.tokenPath;
|
|
17110
17603
|
return out;
|
|
17111
17604
|
}
|
|
17605
|
+
function vcsCliIdentity(cwd) {
|
|
17606
|
+
const root = vcsCwd(cwd);
|
|
17607
|
+
const cfg = vcsConfig("load", root);
|
|
17608
|
+
if (!cfg.ok)
|
|
17609
|
+
return { ok: false, error: cfg.error ?? "vcs config not ready" };
|
|
17610
|
+
const provider = cfg.provider;
|
|
17611
|
+
if (provider !== "gitlab" && provider !== "github")
|
|
17612
|
+
return { ok: false, error: `unsupported provider: ${provider}` };
|
|
17613
|
+
const bin = provider === "gitlab" ? "glab" : "gh";
|
|
17614
|
+
const host = String(provider === "github" ? cfg.github?.host ?? "github.com" : cfg.gitlab?.host ?? "gitlab.com").toLowerCase();
|
|
17615
|
+
const env = {
|
|
17616
|
+
...process.env,
|
|
17617
|
+
PATH: process.env.PATH ?? "",
|
|
17618
|
+
...provider === "github" ? { GH_HOST: host } : { GITLAB_HOST: host }
|
|
17619
|
+
};
|
|
17620
|
+
const result = spawnSync(bin, ["api", "user"], {
|
|
17621
|
+
cwd: root,
|
|
17622
|
+
encoding: "utf8",
|
|
17623
|
+
env
|
|
17624
|
+
});
|
|
17625
|
+
if (result.status !== 0)
|
|
17626
|
+
return { ok: false, provider, error: `${bin} is missing or not authenticated` };
|
|
17627
|
+
try {
|
|
17628
|
+
const user = JSON.parse(result.stdout ?? "");
|
|
17629
|
+
const username = provider === "gitlab" ? user.username : user.login;
|
|
17630
|
+
if (typeof username !== "string" || !username)
|
|
17631
|
+
return { ok: false, provider, error: `invalid ${bin} identity response` };
|
|
17632
|
+
const expected = resolveRuntimeWorkspaceVcs(root)?.vcs?.account;
|
|
17633
|
+
if (expected && username.toLowerCase() !== expected.toLowerCase())
|
|
17634
|
+
return {
|
|
17635
|
+
ok: false,
|
|
17636
|
+
provider,
|
|
17637
|
+
error: `${bin} account ${username} does not match ${expected}`
|
|
17638
|
+
};
|
|
17639
|
+
return { ok: true, provider, host, username, name: user.name };
|
|
17640
|
+
} catch {
|
|
17641
|
+
return { ok: false, provider, error: `invalid JSON from ${bin} api user` };
|
|
17642
|
+
}
|
|
17643
|
+
}
|
|
17112
17644
|
function mergedPrStyle(limit = 6, cwd) {
|
|
17113
17645
|
const cfg = vcsConfig("load", cwd);
|
|
17114
|
-
if (!cfg.ok
|
|
17646
|
+
if (!cfg.ok)
|
|
17115
17647
|
return { ok: false, error: "vcs not configured" };
|
|
17116
17648
|
const provider = cfg.provider;
|
|
17117
|
-
const
|
|
17649
|
+
const host = String(provider === "github" ? cfg.github?.host ?? "github.com" : cfg.gitlab?.host ?? "gitlab.com").toLowerCase();
|
|
17650
|
+
const env = {
|
|
17651
|
+
...process.env,
|
|
17652
|
+
PATH: process.env.PATH ?? "",
|
|
17653
|
+
...provider === "github" ? { GH_HOST: host } : { GITLAB_HOST: host }
|
|
17654
|
+
};
|
|
17118
17655
|
const examples = [];
|
|
17119
17656
|
const descInfo = (desc, caseInsensitiveNotes = false) => ({
|
|
17120
17657
|
hasNotesSection: caseInsensitiveNotes ? /##\s*notes/i.test(desc) : /##\s*Notes/.test(desc),
|
|
@@ -17122,23 +17659,30 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
17122
17659
|
`).filter((l) => l.startsWith("## ")).map((l) => l.trim()),
|
|
17123
17660
|
descriptionPreview: desc.slice(0, 600)
|
|
17124
17661
|
});
|
|
17662
|
+
const remoteResult = spawnSync("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
17663
|
+
cwd,
|
|
17664
|
+
encoding: "utf8"
|
|
17665
|
+
});
|
|
17666
|
+
const remotes = (remoteResult.stdout ?? "").split(/\r?\n/u).map((value) => value.trim()).filter(Boolean);
|
|
17667
|
+
if (remotes.length === 0)
|
|
17668
|
+
return { ok: false, error: "no origin remote" };
|
|
17669
|
+
if (remotes.length !== 1)
|
|
17670
|
+
return { ok: false, error: "origin has ambiguous push destinations" };
|
|
17671
|
+
const remote = remotes[0];
|
|
17672
|
+
const endpoint = remoteEndpoint(remote);
|
|
17673
|
+
if (!endpoint?.path)
|
|
17674
|
+
return { ok: false, error: "hosting repository could not be resolved" };
|
|
17675
|
+
if (!hostingApiHostMatches(remote, host))
|
|
17676
|
+
return { ok: false, error: "PR style target does not match the provider host" };
|
|
17125
17677
|
if (provider === "gitlab") {
|
|
17126
|
-
const
|
|
17127
|
-
|
|
17128
|
-
|
|
17129
|
-
|
|
17130
|
-
|
|
17131
|
-
if (!m)
|
|
17132
|
-
return { ok: false, error: "not a gitlab.com origin" };
|
|
17133
|
-
const project = m[1];
|
|
17134
|
-
const env = { ...process.env, GITLAB_TOKEN: token };
|
|
17135
|
-
const run = (args) => spawnSync("glab", ["api", ...args], { cwd, encoding: "utf8", env });
|
|
17678
|
+
const run = (args) => spawnSync("glab", ["api", ...args], {
|
|
17679
|
+
cwd,
|
|
17680
|
+
encoding: "utf8",
|
|
17681
|
+
env
|
|
17682
|
+
});
|
|
17136
17683
|
let r = run([
|
|
17137
|
-
`projects/${
|
|
17684
|
+
`projects/${endpoint.path.replaceAll("/", "%2F")}/merge_requests?state=merged&per_page=${limit}&order_by=updated_at&sort=desc`
|
|
17138
17685
|
]);
|
|
17139
|
-
if (r.status !== 0) {
|
|
17140
|
-
r = run([`merge_requests?state=merged&per_page=${limit}&order_by=updated_at&sort=desc`]);
|
|
17141
|
-
}
|
|
17142
17686
|
if (r.status !== 0)
|
|
17143
17687
|
return { ok: false, error: "could not list merge requests" };
|
|
17144
17688
|
for (const mr of JSON.parse(r.stdout ?? "[]")) {
|
|
@@ -17151,7 +17695,18 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
17151
17695
|
});
|
|
17152
17696
|
}
|
|
17153
17697
|
} else if (provider === "github") {
|
|
17154
|
-
const r = spawnSync("gh", [
|
|
17698
|
+
const r = spawnSync("gh", [
|
|
17699
|
+
"pr",
|
|
17700
|
+
"list",
|
|
17701
|
+
"--repo",
|
|
17702
|
+
`${host}/${endpoint.path}`,
|
|
17703
|
+
"--state",
|
|
17704
|
+
"merged",
|
|
17705
|
+
"--limit",
|
|
17706
|
+
String(limit),
|
|
17707
|
+
"--json",
|
|
17708
|
+
"title,url,body"
|
|
17709
|
+
], { cwd, encoding: "utf8", env });
|
|
17155
17710
|
if (r.status !== 0)
|
|
17156
17711
|
return { ok: false, error: "could not list pull requests" };
|
|
17157
17712
|
for (const pr of JSON.parse(r.stdout ?? "[]")) {
|
|
@@ -17173,26 +17728,28 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
17173
17728
|
}
|
|
17174
17729
|
|
|
17175
17730
|
// packages/workit-core/src/core/branch.ts
|
|
17176
|
-
|
|
17731
|
+
function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
|
|
17732
|
+
const selected = resolveRuntimeWorkspacePolicy(workspaceRoot, kind, profileName?.trim() || process.env.WORKFLOW_PROFILE?.trim() || undefined);
|
|
17733
|
+
const config = readConfig();
|
|
17734
|
+
return kind === "branch" ? {
|
|
17735
|
+
branchPolicy: resolveBranchPolicy(config, selected.policy ? { branchPolicy: selected.policy } : null),
|
|
17736
|
+
provenance: { branchPolicy: selected.source }
|
|
17737
|
+
} : {
|
|
17738
|
+
commitPolicy: resolveCommitPolicy(config, selected.policy ? { commitPolicy: selected.policy } : null),
|
|
17739
|
+
provenance: { commitPolicy: selected.source }
|
|
17740
|
+
};
|
|
17741
|
+
}
|
|
17742
|
+
var resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy;
|
|
17177
17743
|
var verifyPushIdentity = (root, provider, account) => {
|
|
17178
17744
|
if (!account || typeof account !== "string")
|
|
17179
17745
|
return { ok: false, error: "push identity requires a configured area account" };
|
|
17180
|
-
const
|
|
17181
|
-
|
|
17182
|
-
|
|
17183
|
-
|
|
17184
|
-
login = execFileSync2(bin, [...probe], {
|
|
17185
|
-
cwd: root,
|
|
17186
|
-
encoding: "utf8",
|
|
17187
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
17188
|
-
}).trim();
|
|
17189
|
-
} catch {
|
|
17190
|
-
return { ok: false, error: `${bin} identity could not be resolved` };
|
|
17191
|
-
}
|
|
17192
|
-
if (!login || login.toLowerCase() !== account.toLowerCase())
|
|
17746
|
+
const identity = vcsCliIdentity(root);
|
|
17747
|
+
if (!identity.ok || identity.provider !== provider)
|
|
17748
|
+
return { ok: false, error: identity.error ?? "hosting CLI identity could not be resolved" };
|
|
17749
|
+
if (identity.username?.toLowerCase() !== account.toLowerCase())
|
|
17193
17750
|
return {
|
|
17194
17751
|
ok: false,
|
|
17195
|
-
error: `push identity ${
|
|
17752
|
+
error: `push identity ${identity.username || "(unknown)"} does not match area account ${account}`
|
|
17196
17753
|
};
|
|
17197
17754
|
return { ok: true };
|
|
17198
17755
|
};
|
|
@@ -17210,7 +17767,7 @@ var operationAutoClass = (operation) => typeof operation === "string" ? OPERATIO
|
|
|
17210
17767
|
var digestOf = (configDirPath) => {
|
|
17211
17768
|
try {
|
|
17212
17769
|
const raw = readFileSync4(`${configDirPath}/workspaces.json`, "utf8");
|
|
17213
|
-
return
|
|
17770
|
+
return createHash4("sha256").update(raw).digest("hex");
|
|
17214
17771
|
} catch {
|
|
17215
17772
|
return null;
|
|
17216
17773
|
}
|
|
@@ -17248,14 +17805,30 @@ var resolveAutoApproval = (root, dir = configDir()) => {
|
|
|
17248
17805
|
};
|
|
17249
17806
|
};
|
|
17250
17807
|
var isStandingReceipt = (value) => typeof value === "object" && value !== null && value.kind === "standing";
|
|
17251
|
-
var
|
|
17808
|
+
var targetOf = (root, descriptor) => {
|
|
17809
|
+
try {
|
|
17810
|
+
const parsed = typeof descriptor === "string" ? JSON.parse(descriptor) : descriptor;
|
|
17811
|
+
const cwd = parsed?.payload?.cwd;
|
|
17812
|
+
if (cwd === undefined)
|
|
17813
|
+
return root;
|
|
17814
|
+
if (typeof cwd !== "string" || !path8.isAbsolute(cwd))
|
|
17815
|
+
return null;
|
|
17816
|
+
return realpathSync3(cwd);
|
|
17817
|
+
} catch {
|
|
17818
|
+
return null;
|
|
17819
|
+
}
|
|
17820
|
+
};
|
|
17821
|
+
var standingApprovalLive = (root, receipt, cls, dir, descriptor) => {
|
|
17252
17822
|
if (!isStandingReceipt(receipt))
|
|
17253
17823
|
return false;
|
|
17254
17824
|
if (typeof cls !== "string" || !AUTO_CLASSES.includes(cls))
|
|
17255
17825
|
return false;
|
|
17256
17826
|
if (receipt.class !== cls)
|
|
17257
17827
|
return false;
|
|
17258
|
-
const
|
|
17828
|
+
const target = targetOf(root, descriptor);
|
|
17829
|
+
if (!target)
|
|
17830
|
+
return false;
|
|
17831
|
+
const resolved = resolveAutoApproval(target, dir);
|
|
17259
17832
|
if (resolved.status !== "on")
|
|
17260
17833
|
return false;
|
|
17261
17834
|
if (resolved.workspace !== receipt.workspace)
|
|
@@ -17291,16 +17864,19 @@ var verifyStandingApproval = (root, task, caller, binding) => {
|
|
|
17291
17864
|
const cls = operationAutoClass(operation);
|
|
17292
17865
|
if (!cls || cls !== standing.class)
|
|
17293
17866
|
return failure("permission_denied", "standing approval does not cover this operation");
|
|
17294
|
-
const
|
|
17867
|
+
const target = targetOf(root, binding.approvedContent);
|
|
17868
|
+
if (!target)
|
|
17869
|
+
return failure("permission_denied", "standing action target is invalid");
|
|
17870
|
+
const resolved = resolveAutoApproval(target);
|
|
17295
17871
|
if (resolved.status !== "on" || resolved.workspace !== standing.workspace || !resolved.classes.includes(cls))
|
|
17296
17872
|
return failure("permission_denied", "standing auto-approval is not live for this operation");
|
|
17297
17873
|
if (cls === "push") {
|
|
17298
|
-
const workspace = resolveWorkspaceFrom(
|
|
17874
|
+
const workspace = resolveWorkspaceFrom(target, configDir());
|
|
17299
17875
|
const provider = workspace?.vcs?.provider;
|
|
17300
17876
|
const account = resolved.account;
|
|
17301
17877
|
if (!provider || !account)
|
|
17302
17878
|
return failure("permission_denied", "auto-push requires a workspace provider and area account");
|
|
17303
|
-
const identity = verifyPushIdentity(
|
|
17879
|
+
const identity = verifyPushIdentity(target, provider, account);
|
|
17304
17880
|
if (!identity.ok)
|
|
17305
17881
|
return failure("permission_denied", identity.error);
|
|
17306
17882
|
}
|
|
@@ -17309,7 +17885,7 @@ var verifyStandingApproval = (root, task, caller, binding) => {
|
|
|
17309
17885
|
host: caller.host,
|
|
17310
17886
|
session: { kind: "host", host: caller.host, handle: caller.actor },
|
|
17311
17887
|
workerId: null,
|
|
17312
|
-
receipts: [standingReceiptFor(
|
|
17888
|
+
receipts: [standingReceiptFor(target, standing.workspace, cls)]
|
|
17313
17889
|
});
|
|
17314
17890
|
};
|
|
17315
17891
|
|
|
@@ -17460,7 +18036,7 @@ function applicableDecision(task, purpose, binding, checkoutRoot) {
|
|
|
17460
18036
|
return [];
|
|
17461
18037
|
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);
|
|
17462
18038
|
}
|
|
17463
|
-
var digestBytes2 = (bytes) =>
|
|
18039
|
+
var digestBytes2 = (bytes) => createHash5("sha256").update(bytes).digest("hex");
|
|
17464
18040
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
17465
18041
|
let root;
|
|
17466
18042
|
try {
|
|
@@ -17473,23 +18049,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
17473
18049
|
continue;
|
|
17474
18050
|
if (!reference.digest)
|
|
17475
18051
|
return failure("invalid_input", "document references require a byte digest");
|
|
17476
|
-
const target =
|
|
17477
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
18052
|
+
const target = path9.resolve(root, reference.path);
|
|
18053
|
+
if (target !== root && !target.startsWith(`${root}${path9.sep}`))
|
|
17478
18054
|
return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
17479
18055
|
fields: [
|
|
17480
18056
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
17481
18057
|
]
|
|
17482
18058
|
});
|
|
17483
18059
|
try {
|
|
17484
|
-
const relative =
|
|
18060
|
+
const relative = path9.relative(root, target);
|
|
17485
18061
|
let current = root;
|
|
17486
|
-
for (const segment of relative.split(
|
|
18062
|
+
for (const segment of relative.split(path9.sep)) {
|
|
17487
18063
|
if (!segment)
|
|
17488
18064
|
continue;
|
|
17489
|
-
current =
|
|
18065
|
+
current = path9.join(current, segment);
|
|
17490
18066
|
const stat = fs6.lstatSync(current);
|
|
17491
18067
|
const real = fs6.realpathSync(current);
|
|
17492
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
18068
|
+
if (real !== root && !real.startsWith(`${root}${path9.sep}`))
|
|
17493
18069
|
return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
17494
18070
|
fields: [
|
|
17495
18071
|
{
|
|
@@ -17567,7 +18143,7 @@ var validateAction = (store, task, workspaceId, input, authority) => {
|
|
|
17567
18143
|
operation = null;
|
|
17568
18144
|
}
|
|
17569
18145
|
const cls = operationAutoClass(operation);
|
|
17570
|
-
if (!cls || !standing.some((receipt) => standingApprovalLive(store.root, receipt, cls)))
|
|
18146
|
+
if (!cls || !standing.some((receipt) => standingApprovalLive(store.root, receipt, cls, undefined, decision.binding.approvedContent)))
|
|
17571
18147
|
return failure("permission_denied", "standing auto-approval is not live for this operation");
|
|
17572
18148
|
}
|
|
17573
18149
|
if (decision.revoked)
|
|
@@ -17869,15 +18445,15 @@ var bindingCovers = scopeCovers;
|
|
|
17869
18445
|
var verifyDecisionContent = verifyContentRefs;
|
|
17870
18446
|
|
|
17871
18447
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
17872
|
-
var digestBytes3 = (value) =>
|
|
17873
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
17874
|
-
var relative = (root, candidate) =>
|
|
18448
|
+
var digestBytes3 = (value) => createHash6("sha256").update(value).digest("hex");
|
|
18449
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path10.sep}`);
|
|
18450
|
+
var relative = (root, candidate) => path10.relative(root, candidate).split(path10.sep).join("/") || ".";
|
|
17875
18451
|
var canonicalPath = (value) => {
|
|
17876
18452
|
if (typeof value !== "string" || !value)
|
|
17877
18453
|
return null;
|
|
17878
18454
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
17879
18455
|
return null;
|
|
17880
|
-
const normalized =
|
|
18456
|
+
const normalized = path10.posix.normalize(value);
|
|
17881
18457
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
17882
18458
|
return null;
|
|
17883
18459
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -17925,14 +18501,14 @@ var gitPaths = (root) => {
|
|
|
17925
18501
|
git: /not a git repository/i.test(stderr) ? false : true
|
|
17926
18502
|
};
|
|
17927
18503
|
}
|
|
17928
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
18504
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path10.sep).join("/")) : [];
|
|
17929
18505
|
const stagedDeleted = spawnSync2("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
17930
18506
|
if (stagedDeleted.status !== 0) {
|
|
17931
18507
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
17932
18508
|
if (!/not a git repository/i.test(stderr))
|
|
17933
18509
|
return { paths, uncertain: true, git: true };
|
|
17934
18510
|
} else if (stagedDeleted.stdout) {
|
|
17935
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
18511
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path10.sep).join("/")));
|
|
17936
18512
|
}
|
|
17937
18513
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
17938
18514
|
};
|
|
@@ -17947,7 +18523,7 @@ var walk = (root, directory, output) => {
|
|
|
17947
18523
|
for (const name of names) {
|
|
17948
18524
|
if (name === ".git" || name === ".workit")
|
|
17949
18525
|
continue;
|
|
17950
|
-
const target =
|
|
18526
|
+
const target = path10.join(directory, name);
|
|
17951
18527
|
let stat;
|
|
17952
18528
|
try {
|
|
17953
18529
|
stat = fs7.lstatSync(target);
|
|
@@ -17965,12 +18541,12 @@ var walk = (root, directory, output) => {
|
|
|
17965
18541
|
var scopeRoots = (root, scope) => {
|
|
17966
18542
|
const roots = [];
|
|
17967
18543
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
17968
|
-
const target =
|
|
18544
|
+
const target = path10.resolve(root, item);
|
|
17969
18545
|
if (!inside(root, target))
|
|
17970
18546
|
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` }] });
|
|
17971
18547
|
let ancestor = target;
|
|
17972
18548
|
while (!fs7.existsSync(ancestor) && ancestor !== root)
|
|
17973
|
-
ancestor =
|
|
18549
|
+
ancestor = path10.dirname(ancestor);
|
|
17974
18550
|
try {
|
|
17975
18551
|
if (!inside(root, fs7.realpathSync(ancestor)))
|
|
17976
18552
|
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` }] });
|
|
@@ -18022,7 +18598,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
18022
18598
|
for (const item of [...names].sort()) {
|
|
18023
18599
|
if (!scopeMatches(item, normalizedScope))
|
|
18024
18600
|
continue;
|
|
18025
|
-
const target =
|
|
18601
|
+
const target = path10.join(checkout, item);
|
|
18026
18602
|
try {
|
|
18027
18603
|
const stat = fs7.lstatSync(target);
|
|
18028
18604
|
if (stat.isSymbolicLink()) {
|
|
@@ -18404,7 +18980,8 @@ var externalActionSchema = discriminatedUnion("operation", [
|
|
|
18404
18980
|
action: _enum(["setup", "reapply_stash"]).optional(),
|
|
18405
18981
|
sdd_dir: string2().optional(),
|
|
18406
18982
|
target_branch: string2().optional(),
|
|
18407
|
-
stash: _enum(["yes", "no"]).optional()
|
|
18983
|
+
stash: _enum(["yes", "no"]).optional(),
|
|
18984
|
+
cwd: string2().min(1).optional()
|
|
18408
18985
|
}).strict()
|
|
18409
18986
|
}).strict(),
|
|
18410
18987
|
object({
|
|
@@ -18412,12 +18989,13 @@ var externalActionSchema = discriminatedUnion("operation", [
|
|
|
18412
18989
|
payload: object({
|
|
18413
18990
|
message: string2().min(1).optional(),
|
|
18414
18991
|
plan_steps: array(chainStepSchema).min(1).max(32).optional(),
|
|
18415
|
-
plan_branch: string2().min(1).optional()
|
|
18992
|
+
plan_branch: string2().min(1).optional(),
|
|
18993
|
+
cwd: string2().min(1).optional()
|
|
18416
18994
|
}).strict()
|
|
18417
18995
|
}).strict(),
|
|
18418
18996
|
object({
|
|
18419
18997
|
operation: literal("git.push"),
|
|
18420
|
-
payload: object({ branch: string2().min(1).optional() }).strict()
|
|
18998
|
+
payload: object({ branch: string2().min(1).optional(), cwd: string2().min(1).optional() }).strict()
|
|
18421
18999
|
}).strict(),
|
|
18422
19000
|
object({
|
|
18423
19001
|
operation: literal("hosting.pull_request"),
|
|
@@ -18426,16 +19004,22 @@ var externalActionSchema = discriminatedUnion("operation", [
|
|
|
18426
19004
|
body: string2().optional(),
|
|
18427
19005
|
draft: boolean2().optional(),
|
|
18428
19006
|
target_branch: string2().optional(),
|
|
18429
|
-
babysit: boolean2().optional()
|
|
19007
|
+
babysit: boolean2().optional(),
|
|
19008
|
+
cwd: string2().min(1).optional()
|
|
18430
19009
|
}).strict()
|
|
18431
19010
|
}).strict(),
|
|
18432
19011
|
object({
|
|
18433
19012
|
operation: literal("hosting.merge"),
|
|
18434
19013
|
payload: object({
|
|
18435
19014
|
target_branch: string2().min(1).optional(),
|
|
18436
|
-
source_branch: string2().min(1).optional()
|
|
19015
|
+
source_branch: string2().min(1).optional(),
|
|
19016
|
+
cwd: string2().min(1).optional()
|
|
18437
19017
|
}).strict()
|
|
18438
19018
|
}).strict(),
|
|
19019
|
+
object({
|
|
19020
|
+
operation: literal("hosting.delete_branch"),
|
|
19021
|
+
payload: object({ branch: string2().min(1), cwd: string2().min(1).optional() }).strict()
|
|
19022
|
+
}).strict(),
|
|
18439
19023
|
object({
|
|
18440
19024
|
operation: literal("youtrack.update"),
|
|
18441
19025
|
payload: object({
|
|
@@ -18491,12 +19075,13 @@ var externalActionSchema = discriminatedUnion("operation", [
|
|
|
18491
19075
|
issueRef: string2().optional(),
|
|
18492
19076
|
mode: string2().optional(),
|
|
18493
19077
|
specPath: string2().optional(),
|
|
18494
|
-
planPath: string2().optional()
|
|
19078
|
+
planPath: string2().optional(),
|
|
19079
|
+
cwd: string2().min(1).optional()
|
|
18495
19080
|
}).strict()
|
|
18496
19081
|
}).strict()
|
|
18497
19082
|
]);
|
|
18498
19083
|
// packages/workit-core/src/core/task-engine.ts
|
|
18499
|
-
import { realpathSync as
|
|
19084
|
+
import { realpathSync as realpathSync6 } from "node:fs";
|
|
18500
19085
|
|
|
18501
19086
|
// packages/workit-core/src/core/policy-resolver.ts
|
|
18502
19087
|
var resolverInputSchema = object({
|
|
@@ -19041,6 +19626,18 @@ var portableTask = (task) => {
|
|
|
19041
19626
|
};
|
|
19042
19627
|
for (const entry of clone.assessments)
|
|
19043
19628
|
demote(entry.data.facts, entry.data.signals, entry.data.consequences);
|
|
19629
|
+
const sourceDecisions = new Map(task.decisions.map((entry) => [entry.id, entry]));
|
|
19630
|
+
for (const entry of clone.decisions) {
|
|
19631
|
+
const consumption = sourceDecisions.get(entry.id)?.data.consumption;
|
|
19632
|
+
if (!consumption)
|
|
19633
|
+
continue;
|
|
19634
|
+
entry.data.consumption = {
|
|
19635
|
+
...consumption,
|
|
19636
|
+
state: consumption.state === "reserved" ? "uncertain" : consumption.state,
|
|
19637
|
+
actionRef: { kind: "record", collection: "decisions", id: entry.id },
|
|
19638
|
+
...consumption.historicalActionId ? { historicalActionId: consumption.historicalActionId } : consumption.actionRef.kind === "host" ? { historicalActionId: consumption.actionRef.handle } : {}
|
|
19639
|
+
};
|
|
19640
|
+
}
|
|
19044
19641
|
clone.candidates = [];
|
|
19045
19642
|
return clone;
|
|
19046
19643
|
};
|
|
@@ -19166,7 +19763,11 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
19166
19763
|
taskId: "",
|
|
19167
19764
|
workspaceId: destinationWorkspaceId
|
|
19168
19765
|
},
|
|
19169
|
-
consumption:
|
|
19766
|
+
consumption: data.consumption ? {
|
|
19767
|
+
...data.consumption,
|
|
19768
|
+
state: data.consumption.state === "reserved" ? "uncertain" : data.consumption.state,
|
|
19769
|
+
actionRef: { kind: "record", collection: "decisions", id: entry.id }
|
|
19770
|
+
} : null
|
|
19170
19771
|
}
|
|
19171
19772
|
};
|
|
19172
19773
|
}),
|
|
@@ -19207,7 +19808,7 @@ class WorkitCore {
|
|
|
19207
19808
|
}
|
|
19208
19809
|
contextRootError() {
|
|
19209
19810
|
try {
|
|
19210
|
-
if (
|
|
19811
|
+
if (realpathSync6(this.context.root) !== this.store.root)
|
|
19211
19812
|
return failure("invalid_input", "operation context root does not match the task store root");
|
|
19212
19813
|
} catch {
|
|
19213
19814
|
return failure("invalid_input", "operation context root cannot be resolved");
|
|
@@ -19366,7 +19967,7 @@ class WorkitCore {
|
|
|
19366
19967
|
return parsed;
|
|
19367
19968
|
const input = parsed.data;
|
|
19368
19969
|
if ((this.context.workerId ?? null) !== null) {
|
|
19369
|
-
if (input.action !== "inspect")
|
|
19970
|
+
if (input.action !== "inspect" && input.action !== "list")
|
|
19370
19971
|
return failure("permission_denied", "helpers cannot control task lifecycle or scope");
|
|
19371
19972
|
}
|
|
19372
19973
|
switch (input.action) {
|
|
@@ -19393,7 +19994,18 @@ class WorkitCore {
|
|
|
19393
19994
|
return listed;
|
|
19394
19995
|
const status = input.status ?? "open";
|
|
19395
19996
|
const limit = input.limit ?? 20;
|
|
19396
|
-
const
|
|
19997
|
+
const query = input.query?.toLowerCase().split(/\s+/);
|
|
19998
|
+
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({
|
|
19999
|
+
id: task.id,
|
|
20000
|
+
workspaceId: task.workspaceId,
|
|
20001
|
+
origin: task.origin,
|
|
20002
|
+
createdAt: task.createdAt,
|
|
20003
|
+
updatedAt: task.updatedAt,
|
|
20004
|
+
source: task.intent.provenance,
|
|
20005
|
+
objective: task.intent.data.objective,
|
|
20006
|
+
progress: task.progress,
|
|
20007
|
+
decisions: task.decisions.map(({ data }) => data)
|
|
20008
|
+
}).toLowerCase().includes(term))).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, limit);
|
|
19397
20009
|
if (selected.length === 0)
|
|
19398
20010
|
return success(null, null, []);
|
|
19399
20011
|
const workspace = this.store.readWorkspace();
|
|
@@ -19412,7 +20024,11 @@ class WorkitCore {
|
|
|
19412
20024
|
status: task.status,
|
|
19413
20025
|
closure: task.closure,
|
|
19414
20026
|
progress: task.progress,
|
|
19415
|
-
writer: task.status === "closed" ? null : workspace.data.writer
|
|
20027
|
+
writer: task.status === "closed" ? null : workspace.data.writer,
|
|
20028
|
+
source: {
|
|
20029
|
+
host: task.intent.provenance.host,
|
|
20030
|
+
kind: task.intent.provenance.kind
|
|
20031
|
+
}
|
|
19416
20032
|
}));
|
|
19417
20033
|
return success(null, null, tasks);
|
|
19418
20034
|
}
|
|
@@ -20508,7 +21124,7 @@ class WorkitCore {
|
|
|
20508
21124
|
if (view.task.workspaceId !== view.workspace.id)
|
|
20509
21125
|
return failure("invalid_input", "task and workspace bindings are invalid");
|
|
20510
21126
|
try {
|
|
20511
|
-
if (
|
|
21127
|
+
if (realpathSync6(view.workspace.root) !== this.store.root)
|
|
20512
21128
|
return failure("invalid_input", "task view root does not match the task store");
|
|
20513
21129
|
} catch {
|
|
20514
21130
|
return failure("invalid_input", "task view root cannot be resolved");
|
|
@@ -20785,22 +21401,22 @@ function resolveGitRevision(root, value) {
|
|
|
20785
21401
|
}
|
|
20786
21402
|
}
|
|
20787
21403
|
function resolveInside(root, candidate) {
|
|
20788
|
-
const base =
|
|
20789
|
-
const target =
|
|
20790
|
-
if (target !== base && !target.startsWith(base +
|
|
21404
|
+
const base = realpathSync7(root);
|
|
21405
|
+
const target = path11.resolve(base, candidate);
|
|
21406
|
+
if (target !== base && !target.startsWith(base + path11.sep)) {
|
|
20791
21407
|
throw new Error("path must stay inside repository root");
|
|
20792
21408
|
}
|
|
20793
21409
|
let ancestor = target;
|
|
20794
21410
|
while (!existsSync5(ancestor))
|
|
20795
|
-
ancestor =
|
|
20796
|
-
const canonicalAncestor =
|
|
20797
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
21411
|
+
ancestor = path11.dirname(ancestor);
|
|
21412
|
+
const canonicalAncestor = realpathSync7(ancestor);
|
|
21413
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path11.sep)) {
|
|
20798
21414
|
throw new Error("path must stay inside repository root");
|
|
20799
21415
|
}
|
|
20800
21416
|
return target;
|
|
20801
21417
|
}
|
|
20802
21418
|
function run2(root, executable, args, env = {}) {
|
|
20803
|
-
const cwd =
|
|
21419
|
+
const cwd = realpathSync7(root);
|
|
20804
21420
|
const result = spawnSync3(executable, args, {
|
|
20805
21421
|
cwd,
|
|
20806
21422
|
encoding: "utf8",
|
|
@@ -20824,8 +21440,8 @@ var SUPPORT_MATRIX = {
|
|
|
20824
21440
|
};
|
|
20825
21441
|
|
|
20826
21442
|
// packages/workit-mcp/src/server.ts
|
|
20827
|
-
import { existsSync as existsSync7, readFileSync as readFileSync8, realpathSync as
|
|
20828
|
-
import
|
|
21443
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, realpathSync as realpathSync8 } from "node:fs";
|
|
21444
|
+
import path15 from "node:path";
|
|
20829
21445
|
|
|
20830
21446
|
// node_modules/@modelcontextprotocol/sdk/node_modules/zod/v4/core/core.js
|
|
20831
21447
|
var NEVER2 = Object.freeze({
|
|
@@ -24352,7 +24968,7 @@ function check(fn) {
|
|
|
24352
24968
|
ch._zod.check = fn;
|
|
24353
24969
|
return ch;
|
|
24354
24970
|
}
|
|
24355
|
-
function
|
|
24971
|
+
function custom2(fn, _params) {
|
|
24356
24972
|
return _custom2(ZodCustom2, fn ?? (() => true), _params);
|
|
24357
24973
|
}
|
|
24358
24974
|
function refine2(fn, _params = {}) {
|
|
@@ -24389,7 +25005,7 @@ var LATEST_PROTOCOL_VERSION = "2025-11-25";
|
|
|
24389
25005
|
var SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, "2025-06-18", "2025-03-26", "2024-11-05", "2024-10-07"];
|
|
24390
25006
|
var RELATED_TASK_META_KEY = "io.modelcontextprotocol/related-task";
|
|
24391
25007
|
var JSONRPC_VERSION = "2.0";
|
|
24392
|
-
var AssertObjectSchema =
|
|
25008
|
+
var AssertObjectSchema = custom2((v) => v !== null && (typeof v === "object" || typeof v === "function"));
|
|
24393
25009
|
var ProgressTokenSchema = union2([string4(), number4().int()]);
|
|
24394
25010
|
var CursorSchema = string4();
|
|
24395
25011
|
var TaskCreationParamsSchema = looseObject({
|
|
@@ -26735,18 +27351,19 @@ var redactSecrets = (value) => applyPatterns(value);
|
|
|
26735
27351
|
|
|
26736
27352
|
// packages/workit-core/src/core/pr-create.ts
|
|
26737
27353
|
import fs8 from "node:fs";
|
|
26738
|
-
import
|
|
27354
|
+
import path12 from "node:path";
|
|
27355
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
26739
27356
|
function parseGhIssue(value) {
|
|
26740
27357
|
const m = /issues\/(\d+)/.exec(value);
|
|
26741
27358
|
return m ? m[1] : String(value).trim().replace(/^#/, "");
|
|
26742
27359
|
}
|
|
26743
27360
|
function whichOnPath(tool) {
|
|
26744
27361
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
26745
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
27362
|
+
for (const dir of (process.env.PATH ?? "").split(path12.delimiter)) {
|
|
26746
27363
|
if (!dir)
|
|
26747
27364
|
continue;
|
|
26748
27365
|
for (const name of names) {
|
|
26749
|
-
const candidate =
|
|
27366
|
+
const candidate = path12.join(dir, name);
|
|
26750
27367
|
try {
|
|
26751
27368
|
fs8.accessSync(candidate, fs8.constants.X_OK);
|
|
26752
27369
|
return candidate;
|
|
@@ -26756,11 +27373,21 @@ function whichOnPath(tool) {
|
|
|
26756
27373
|
return null;
|
|
26757
27374
|
}
|
|
26758
27375
|
var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
|
|
27376
|
+
var pushRemote = (cwd) => {
|
|
27377
|
+
const result = spawnSync4("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
27378
|
+
cwd,
|
|
27379
|
+
encoding: "utf8"
|
|
27380
|
+
});
|
|
27381
|
+
if (result.status !== 0)
|
|
27382
|
+
return null;
|
|
27383
|
+
const urls = (result.stdout ?? "").split(/\r?\n/u).map((url) => url.trim()).filter(Boolean);
|
|
27384
|
+
return urls.length === 1 ? urls[0] : null;
|
|
27385
|
+
};
|
|
26759
27386
|
|
|
26760
27387
|
// packages/workit-core/src/core/repo-context.ts
|
|
26761
|
-
import { spawnSync as
|
|
27388
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
26762
27389
|
import { existsSync as existsSync6, readFileSync as readFileSync7, readdirSync as readdirSync4 } from "node:fs";
|
|
26763
|
-
import
|
|
27390
|
+
import path13 from "node:path";
|
|
26764
27391
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
26765
27392
|
try {
|
|
26766
27393
|
gitRevisionParts(value);
|
|
@@ -26787,7 +27414,7 @@ var invalidRange = (cwd) => ({
|
|
|
26787
27414
|
cwd
|
|
26788
27415
|
});
|
|
26789
27416
|
var runGit = (cwd, args) => {
|
|
26790
|
-
const result =
|
|
27417
|
+
const result = spawnSync5("git", args, { cwd, encoding: "utf8" });
|
|
26791
27418
|
return {
|
|
26792
27419
|
stdout: (result.stdout ?? "").trimEnd(),
|
|
26793
27420
|
stderr: (result.stderr ?? "").trimEnd(),
|
|
@@ -26894,7 +27521,7 @@ var findPrTemplate = (cwd) => {
|
|
|
26894
27521
|
]) {
|
|
26895
27522
|
const parts = rel.split("/");
|
|
26896
27523
|
const base = parts[parts.length - 1];
|
|
26897
|
-
const dir = parts.length > 1 ?
|
|
27524
|
+
const dir = parts.length > 1 ? path13.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
26898
27525
|
const actual = probeCaseInsensitive(dir, base);
|
|
26899
27526
|
if (actual)
|
|
26900
27527
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -26925,7 +27552,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
26925
27552
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
26926
27553
|
};
|
|
26927
27554
|
var packageScripts = (cwd, keys) => {
|
|
26928
|
-
const pkgPath =
|
|
27555
|
+
const pkgPath = path13.join(cwd, "package.json");
|
|
26929
27556
|
if (!existsSync6(pkgPath))
|
|
26930
27557
|
return [];
|
|
26931
27558
|
try {
|
|
@@ -26951,9 +27578,9 @@ var documentationFiles = (cwd) => {
|
|
|
26951
27578
|
if (entry.isDirectory()) {
|
|
26952
27579
|
if (excluded.has(entry.name))
|
|
26953
27580
|
continue;
|
|
26954
|
-
walk(
|
|
27581
|
+
walk(path13.join(dir, entry.name), depth + 1);
|
|
26955
27582
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
26956
|
-
matches.push("./" +
|
|
27583
|
+
matches.push("./" + path13.relative(cwd, path13.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
26957
27584
|
}
|
|
26958
27585
|
}
|
|
26959
27586
|
};
|
|
@@ -26969,7 +27596,7 @@ var readTrimmed = (file, maxLines) => {
|
|
|
26969
27596
|
`);
|
|
26970
27597
|
};
|
|
26971
27598
|
function prReadyContext(root, range) {
|
|
26972
|
-
const cwd =
|
|
27599
|
+
const cwd = path13.resolve(repoRoot(root));
|
|
26973
27600
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
26974
27601
|
return invalidRange(cwd);
|
|
26975
27602
|
let stdout = "";
|
|
@@ -27027,7 +27654,7 @@ ${status.stderr}` : "");
|
|
|
27027
27654
|
stdout += `template_path: ${template}
|
|
27028
27655
|
|
|
27029
27656
|
`;
|
|
27030
|
-
stdout += readTrimmed(
|
|
27657
|
+
stdout += readTrimmed(path13.join(cwd, template), 220);
|
|
27031
27658
|
} else {
|
|
27032
27659
|
stdout += `template_path: none
|
|
27033
27660
|
|
|
@@ -27037,7 +27664,7 @@ ${status.stderr}` : "");
|
|
|
27037
27664
|
stdout += `
|
|
27038
27665
|
`;
|
|
27039
27666
|
stdout += printSection("Recent Validation Signals");
|
|
27040
|
-
if (existsSync6(
|
|
27667
|
+
if (existsSync6(path13.join(cwd, "package.json"))) {
|
|
27041
27668
|
stdout += `package.json detected. Common scripts:
|
|
27042
27669
|
`;
|
|
27043
27670
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -27046,7 +27673,7 @@ ${status.stderr}` : "");
|
|
|
27046
27673
|
`) + `
|
|
27047
27674
|
`;
|
|
27048
27675
|
}
|
|
27049
|
-
if (existsSync6(
|
|
27676
|
+
if (existsSync6(path13.join(cwd, "Cargo.toml")) || existsSync6(path13.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
27050
27677
|
stdout += `Rust project detected.
|
|
27051
27678
|
`;
|
|
27052
27679
|
}
|
|
@@ -27079,7 +27706,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
27079
27706
|
- Apply with the native workit_changelog_apply tool only (not hand-edits under Unreleased).
|
|
27080
27707
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
27081
27708
|
function changelogContext(root, range) {
|
|
27082
|
-
const cwd =
|
|
27709
|
+
const cwd = path13.resolve(repoRoot(root));
|
|
27083
27710
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27084
27711
|
return invalidRange(cwd);
|
|
27085
27712
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -27097,7 +27724,7 @@ function changelogContext(root, range) {
|
|
|
27097
27724
|
stdout += CHANGELOG_RULES + `
|
|
27098
27725
|
`;
|
|
27099
27726
|
stdout += printSection("Existing CHANGELOG.md");
|
|
27100
|
-
const changelogPath =
|
|
27727
|
+
const changelogPath = path13.join(cwd, "CHANGELOG.md");
|
|
27101
27728
|
if (existsSync6(changelogPath)) {
|
|
27102
27729
|
stdout += readTrimmed(changelogPath, 260) + `
|
|
27103
27730
|
`;
|
|
@@ -27117,7 +27744,7 @@ function changelogContext(root, range) {
|
|
|
27117
27744
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
27118
27745
|
}
|
|
27119
27746
|
function docsRefreshContext(root, range) {
|
|
27120
|
-
const cwd =
|
|
27747
|
+
const cwd = path13.resolve(repoRoot(root));
|
|
27121
27748
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27122
27749
|
return invalidRange(cwd);
|
|
27123
27750
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -27139,7 +27766,7 @@ function docsRefreshContext(root, range) {
|
|
|
27139
27766
|
`) + `
|
|
27140
27767
|
`;
|
|
27141
27768
|
stdout += printSection("README Preview");
|
|
27142
|
-
const readme =
|
|
27769
|
+
const readme = path13.join(cwd, "README.md");
|
|
27143
27770
|
if (existsSync6(readme)) {
|
|
27144
27771
|
stdout += readTrimmed(readme, 220) + `
|
|
27145
27772
|
`;
|
|
@@ -27148,7 +27775,7 @@ function docsRefreshContext(root, range) {
|
|
|
27148
27775
|
`;
|
|
27149
27776
|
}
|
|
27150
27777
|
stdout += printSection("Package Scripts");
|
|
27151
|
-
const pkgPath =
|
|
27778
|
+
const pkgPath = path13.join(cwd, "package.json");
|
|
27152
27779
|
if (existsSync6(pkgPath)) {
|
|
27153
27780
|
try {
|
|
27154
27781
|
const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
@@ -27162,7 +27789,7 @@ function docsRefreshContext(root, range) {
|
|
|
27162
27789
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
27163
27790
|
}
|
|
27164
27791
|
function releaseNotesContext(root, rangeOrTag) {
|
|
27165
|
-
const cwd =
|
|
27792
|
+
const cwd = path13.resolve(repoRoot(root));
|
|
27166
27793
|
if (!rangeOrTag) {
|
|
27167
27794
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
27168
27795
|
`, exitCode: 1, cwd };
|
|
@@ -27200,7 +27827,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
27200
27827
|
`;
|
|
27201
27828
|
stdout += printSection("Existing Release Files");
|
|
27202
27829
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
27203
|
-
if (existsSync6(
|
|
27830
|
+
if (existsSync6(path13.join(cwd, rel)))
|
|
27204
27831
|
stdout += rel + `
|
|
27205
27832
|
`;
|
|
27206
27833
|
}
|
|
@@ -27228,19 +27855,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
27228
27855
|
}
|
|
27229
27856
|
|
|
27230
27857
|
// packages/workit-core/src/core/tracker-issues.ts
|
|
27231
|
-
import
|
|
27232
|
-
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
27233
|
-
var trackerRequest = async (url, init) => {
|
|
27234
|
-
try {
|
|
27235
|
-
const res = await fetch(url, { method: init.method, headers: init.headers });
|
|
27236
|
-
const text = await res.text();
|
|
27237
|
-
if (!res.ok)
|
|
27238
|
-
return { status: res.status, stdout: "", stderr: text.slice(0, 200) };
|
|
27239
|
-
return { status: 0, stdout: text, stderr: "" };
|
|
27240
|
-
} catch (err) {
|
|
27241
|
-
return { status: 1, stdout: "", stderr: err instanceof Error ? err.message : "network error" };
|
|
27242
|
-
}
|
|
27243
|
-
};
|
|
27858
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
27244
27859
|
var repoPathFromRemote = (remote) => {
|
|
27245
27860
|
let rest = (remote || "").trim().replace(/\/+$/, "");
|
|
27246
27861
|
if (!rest)
|
|
@@ -27261,7 +27876,11 @@ var defaultCli = (bin, available) => {
|
|
|
27261
27876
|
if (!available)
|
|
27262
27877
|
return null;
|
|
27263
27878
|
return (args, root) => {
|
|
27264
|
-
const out =
|
|
27879
|
+
const out = spawnSync6(bin, args, {
|
|
27880
|
+
cwd: root,
|
|
27881
|
+
encoding: "utf8",
|
|
27882
|
+
env: { ...process.env, PATH: process.env.PATH ?? "" }
|
|
27883
|
+
});
|
|
27265
27884
|
return Promise.resolve({
|
|
27266
27885
|
status: out.status ?? 1,
|
|
27267
27886
|
stdout: out.stdout ?? "",
|
|
@@ -27269,49 +27888,22 @@ var defaultCli = (bin, available) => {
|
|
|
27269
27888
|
});
|
|
27270
27889
|
};
|
|
27271
27890
|
};
|
|
27272
|
-
var tryCliTriple = async (cli, args, root, id, idKeys, bodyKeys) => {
|
|
27891
|
+
var tryCliTriple = async (cli, name, args, root, id, idKeys, bodyKeys) => {
|
|
27273
27892
|
if (!cli)
|
|
27274
|
-
return
|
|
27893
|
+
return { error: `${name} CLI is unavailable`, kind: "creds" };
|
|
27275
27894
|
const run = cli(args, root);
|
|
27276
27895
|
if (!run)
|
|
27277
|
-
return
|
|
27896
|
+
return { error: `${name} CLI is unavailable`, kind: "creds" };
|
|
27278
27897
|
const out = await run;
|
|
27279
27898
|
if (out.status !== 0)
|
|
27280
|
-
return
|
|
27899
|
+
return { error: `${name} CLI is not authenticated or the issue is unavailable`, kind: "creds" };
|
|
27281
27900
|
try {
|
|
27282
|
-
|
|
27901
|
+
const triple = parseTriple(JSON.parse(out.stdout), id, idKeys, bodyKeys);
|
|
27902
|
+
return triple ? { data: triple } : { error: `unexpected ${name} issue response`, kind: "request" };
|
|
27283
27903
|
} catch {
|
|
27284
|
-
return
|
|
27904
|
+
return { error: `invalid ${name} issue response`, kind: "request" };
|
|
27285
27905
|
}
|
|
27286
27906
|
};
|
|
27287
|
-
var originRemote = (root) => {
|
|
27288
|
-
const out = spawnSync5("git", ["remote", "get-url", "origin"], { cwd: root, encoding: "utf8" });
|
|
27289
|
-
return out.status === 0 ? (out.stdout ?? "").trim() || null : null;
|
|
27290
|
-
};
|
|
27291
|
-
var trackerCreds = (root, provider) => {
|
|
27292
|
-
const cfg = vcsConfig("load", root);
|
|
27293
|
-
if (!cfg || cfg.provider !== provider || typeof cfg.tokenPath !== "string" || !cfg.tokenPath)
|
|
27294
|
-
return { error: `unconfigured ${provider} provider` };
|
|
27295
|
-
let token = "";
|
|
27296
|
-
try {
|
|
27297
|
-
token = fs9.readFileSync(cfg.tokenPath, "utf8").trim();
|
|
27298
|
-
} catch {
|
|
27299
|
-
return { error: `missing ${provider} token` };
|
|
27300
|
-
}
|
|
27301
|
-
if (!token)
|
|
27302
|
-
return { error: `empty ${provider} token file` };
|
|
27303
|
-
if (provider === "gitlab") {
|
|
27304
|
-
const gitlab = cfg.gitlab;
|
|
27305
|
-
const apiUrl = String(gitlab?.apiUrl ?? "https://gitlab.com/api/v4").replace(/\/+$/, "");
|
|
27306
|
-
return { token, api: apiUrl };
|
|
27307
|
-
}
|
|
27308
|
-
const github = cfg.github;
|
|
27309
|
-
const host = String(github?.host ?? "github.com").toLowerCase();
|
|
27310
|
-
return {
|
|
27311
|
-
token,
|
|
27312
|
-
api: host === "github.com" ? "https://api.github.com" : `https://${host}/api/v3`
|
|
27313
|
-
};
|
|
27314
|
-
};
|
|
27315
27907
|
var parseTriple = (parsed, id, idKeys, bodyKeys) => {
|
|
27316
27908
|
const title = parsed.title;
|
|
27317
27909
|
if (typeof title !== "string")
|
|
@@ -27334,62 +27926,36 @@ var fetchGitHubIssueBody = async (ref, root, deps = {}) => {
|
|
|
27334
27926
|
const id = parseGhIssue(ref);
|
|
27335
27927
|
if (!/^\d+$/.test(id))
|
|
27336
27928
|
return { error: `invalid GitHub issue ref "${ref}"`, kind: "input" };
|
|
27337
|
-
const
|
|
27929
|
+
const remote = (deps.remote ?? pushRemote)(root);
|
|
27930
|
+
const repo = repoPathFromRemote(remote ?? "");
|
|
27338
27931
|
if (!repo)
|
|
27339
27932
|
return { error: "no origin remote to resolve the repository", kind: "input" };
|
|
27340
|
-
const
|
|
27341
|
-
|
|
27342
|
-
|
|
27343
|
-
|
|
27344
|
-
|
|
27345
|
-
|
|
27346
|
-
const out = await (deps.request ?? trackerRequest)(`${credentials.api}/repos/${repo}/issues/${id}`, {
|
|
27347
|
-
method: "GET",
|
|
27348
|
-
headers: {
|
|
27349
|
-
Authorization: `Bearer ${credentials.token}`,
|
|
27350
|
-
Accept: "application/vnd.github+json"
|
|
27351
|
-
}
|
|
27352
|
-
});
|
|
27353
|
-
if (out.status !== 0)
|
|
27354
|
-
return { error: out.stderr || "GitHub issue fetch failed", kind: "request" };
|
|
27355
|
-
try {
|
|
27356
|
-
const triple = parseTriple(JSON.parse(out.stdout), id, ["number"], ["body"]);
|
|
27357
|
-
if (!triple)
|
|
27358
|
-
return { error: "unexpected GitHub issue shape", kind: "request" };
|
|
27359
|
-
return { data: triple };
|
|
27360
|
-
} catch {
|
|
27361
|
-
return { error: "invalid GitHub issue response", kind: "request" };
|
|
27362
|
-
}
|
|
27933
|
+
const cfg = vcsConfig("load", root);
|
|
27934
|
+
const host = (deps.host ?? (cfg.ok && cfg.provider === "github" ? cfg.github?.host : null) ?? "github.com").toLowerCase();
|
|
27935
|
+
if (!remote || !hostingApiHostMatches(remote, host))
|
|
27936
|
+
return { error: "hosting API host does not match the push destination", kind: "input" };
|
|
27937
|
+
const repoArg = host === "github.com" ? repo : `${host}/${repo}`;
|
|
27938
|
+
return tryCliTriple(deps.cli ?? defaultCli("gh", hostingCliAvailable("github")), "gh", ["issue", "view", id, "--repo", repoArg, "--json", "number,title,body,state"], root, id, ["number"], ["body"]);
|
|
27363
27939
|
};
|
|
27364
27940
|
var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
27365
27941
|
const id = parseGhIssue(ref);
|
|
27366
27942
|
if (!/^\d+$/.test(id))
|
|
27367
27943
|
return { error: `invalid GitLab issue ref "${ref}"`, kind: "input" };
|
|
27368
|
-
const
|
|
27944
|
+
const remote = (deps.remote ?? pushRemote)(root);
|
|
27945
|
+
const repo = repoPathFromRemote(remote ?? "");
|
|
27369
27946
|
if (!repo)
|
|
27370
27947
|
return { error: "no origin remote to resolve the project", kind: "input" };
|
|
27371
|
-
const
|
|
27372
|
-
|
|
27373
|
-
|
|
27374
|
-
|
|
27375
|
-
|
|
27376
|
-
|
|
27377
|
-
const out = await (deps.request ?? trackerRequest)(`${credentials.api}/projects/${encodeURIComponent(repo)}/issues/${id}`, { method: "GET", headers: { "PRIVATE-TOKEN": credentials.token } });
|
|
27378
|
-
if (out.status !== 0)
|
|
27379
|
-
return { error: out.stderr || "GitLab issue fetch failed", kind: "request" };
|
|
27380
|
-
try {
|
|
27381
|
-
const triple = parseTriple(JSON.parse(out.stdout), id, ["iid"], ["description"]);
|
|
27382
|
-
if (!triple)
|
|
27383
|
-
return { error: "unexpected GitLab issue shape", kind: "request" };
|
|
27384
|
-
return { data: triple };
|
|
27385
|
-
} catch {
|
|
27386
|
-
return { error: "invalid GitLab issue response", kind: "request" };
|
|
27387
|
-
}
|
|
27948
|
+
const cfg = vcsConfig("load", root);
|
|
27949
|
+
const host = (deps.host ?? (cfg.ok && cfg.provider === "gitlab" ? cfg.gitlab?.host : null) ?? "gitlab.com").toLowerCase();
|
|
27950
|
+
if (!remote || !hostingApiHostMatches(remote, host))
|
|
27951
|
+
return { error: "hosting API host does not match the push destination", kind: "input" };
|
|
27952
|
+
const repoArg = host === "gitlab.com" ? repo : `https://${host}/${repo}`;
|
|
27953
|
+
return tryCliTriple(deps.cli ?? defaultCli("glab", hostingCliAvailable("gitlab")), "glab", ["issue", "view", id, "-R", repoArg, "-F", "json"], root, id, ["iid"], ["description"]);
|
|
27388
27954
|
};
|
|
27389
27955
|
|
|
27390
27956
|
// packages/workit-core/src/core/youtrack.ts
|
|
27391
|
-
import
|
|
27392
|
-
import
|
|
27957
|
+
import fs9 from "node:fs";
|
|
27958
|
+
import path14 from "node:path";
|
|
27393
27959
|
|
|
27394
27960
|
// packages/workit-core/src/core/templates.ts
|
|
27395
27961
|
var repoRoot2 = assetRoot();
|
|
@@ -27401,23 +27967,23 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
27401
27967
|
|
|
27402
27968
|
// packages/workit-core/src/core/youtrack.ts
|
|
27403
27969
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
27404
|
-
var
|
|
27405
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
27406
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
27970
|
+
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
27971
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path14.join(configDir(), "youtrack.json");
|
|
27972
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path14.join(resolveConfigDir(), "youtrack.json");
|
|
27407
27973
|
var youTrackTokenModeOk = (p) => {
|
|
27408
27974
|
if (process.platform === "win32")
|
|
27409
27975
|
return true;
|
|
27410
|
-
const mode =
|
|
27976
|
+
const mode = fs9.statSync(p).mode & 511;
|
|
27411
27977
|
return mode === 384;
|
|
27412
27978
|
};
|
|
27413
27979
|
function readYouTrackConfig(required) {
|
|
27414
27980
|
const cfgPath = youTrackConfigPath();
|
|
27415
|
-
if (!
|
|
27981
|
+
if (!fs9.existsSync(cfgPath)) {
|
|
27416
27982
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
27417
27983
|
}
|
|
27418
27984
|
let parsed;
|
|
27419
27985
|
try {
|
|
27420
|
-
parsed = JSON.parse(
|
|
27986
|
+
parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
|
|
27421
27987
|
} catch {
|
|
27422
27988
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
27423
27989
|
}
|
|
@@ -27428,11 +27994,11 @@ function readYouTrackConfig(required) {
|
|
|
27428
27994
|
}
|
|
27429
27995
|
var readYouTrackContextConfig = () => {
|
|
27430
27996
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
27431
|
-
if (!
|
|
27997
|
+
if (!fs9.existsSync(cfgPath))
|
|
27432
27998
|
return { data: {} };
|
|
27433
27999
|
let parsed;
|
|
27434
28000
|
try {
|
|
27435
|
-
parsed = JSON.parse(
|
|
28001
|
+
parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
|
|
27436
28002
|
} catch {
|
|
27437
28003
|
return { error: "YouTrack configuration is unavailable" };
|
|
27438
28004
|
}
|
|
@@ -27457,12 +28023,12 @@ function youTrackGreeting(configOverride) {
|
|
|
27457
28023
|
const cfgPath = configOverride ?? youTrackConfigPath();
|
|
27458
28024
|
let parsed;
|
|
27459
28025
|
try {
|
|
27460
|
-
parsed = JSON.parse(
|
|
28026
|
+
parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
|
|
27461
28027
|
} catch {
|
|
27462
28028
|
return {
|
|
27463
28029
|
stdout: "",
|
|
27464
28030
|
exitCode: 1,
|
|
27465
|
-
stderr:
|
|
28031
|
+
stderr: fs9.existsSync(cfgPath) ? `${cfgPath} is not valid JSON` : `missing youtrack.json: ${cfgPath}`
|
|
27466
28032
|
};
|
|
27467
28033
|
}
|
|
27468
28034
|
if (!isConfigObject(parsed)) {
|
|
@@ -27508,15 +28074,15 @@ var youTrackToken = () => {
|
|
|
27508
28074
|
if ("error" in loaded)
|
|
27509
28075
|
return loaded;
|
|
27510
28076
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
27511
|
-
const tokenPath = tokenFile ?
|
|
27512
|
-
if (!tokenPath || !
|
|
28077
|
+
const tokenPath = tokenFile ? path14.isAbsolute(tokenFile) ? path14.resolve(tokenFile) : path14.resolve(process.cwd(), tokenFile) : "";
|
|
28078
|
+
if (!tokenPath || !fs9.existsSync(tokenPath))
|
|
27513
28079
|
return { error: "missing youtrack.token" };
|
|
27514
28080
|
if (!youTrackTokenModeOk(tokenPath))
|
|
27515
28081
|
return { error: "youtrack.token mode must be 0600" };
|
|
27516
|
-
const token =
|
|
28082
|
+
const token = fs9.readFileSync(tokenPath, "utf8").trim();
|
|
27517
28083
|
if (!token)
|
|
27518
28084
|
return { error: "empty token file" };
|
|
27519
|
-
if (token ===
|
|
28085
|
+
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
27520
28086
|
return {
|
|
27521
28087
|
error: "token file still has placeholder YOUR_TOKEN_HERE — edit the file locally, then run /wk-status"
|
|
27522
28088
|
};
|
|
@@ -27579,9 +28145,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
27579
28145
|
} catch {
|
|
27580
28146
|
continue;
|
|
27581
28147
|
}
|
|
27582
|
-
if (!
|
|
28148
|
+
if (!fs9.existsSync(full))
|
|
27583
28149
|
continue;
|
|
27584
|
-
const text =
|
|
28150
|
+
const text = fs9.readFileSync(full, "utf8");
|
|
27585
28151
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
27586
28152
|
if (m)
|
|
27587
28153
|
return m[1];
|
|
@@ -27815,6 +28381,7 @@ var readExternalContext = async (root, payload) => {
|
|
|
27815
28381
|
}
|
|
27816
28382
|
}
|
|
27817
28383
|
};
|
|
28384
|
+
var targetWriterActionLease = Symbol("target-writer-action-lease");
|
|
27818
28385
|
|
|
27819
28386
|
// packages/workit-mcp/src/server.ts
|
|
27820
28387
|
class McpCapabilityUnavailableError extends Error {
|
|
@@ -27859,13 +28426,13 @@ var rootVariants = (workspaceRoot) => {
|
|
|
27859
28426
|
if (!workspaceRoot)
|
|
27860
28427
|
return [];
|
|
27861
28428
|
const roots = new Set;
|
|
27862
|
-
for (const candidate of [workspaceRoot,
|
|
27863
|
-
if (
|
|
28429
|
+
for (const candidate of [workspaceRoot, path15.resolve(workspaceRoot)]) {
|
|
28430
|
+
if (path15.isAbsolute(candidate) && candidate !== path15.parse(candidate).root)
|
|
27864
28431
|
roots.add(candidate);
|
|
27865
28432
|
}
|
|
27866
28433
|
try {
|
|
27867
|
-
const canonical =
|
|
27868
|
-
if (canonical !==
|
|
28434
|
+
const canonical = realpathSync8(workspaceRoot);
|
|
28435
|
+
if (canonical !== path15.parse(canonical).root)
|
|
27869
28436
|
roots.add(canonical);
|
|
27870
28437
|
} catch {}
|
|
27871
28438
|
return [...roots].sort((left, right) => right.length - left.length);
|
|
@@ -27966,11 +28533,11 @@ var thrownResult = (tool, error, workspaceRoot) => {
|
|
|
27966
28533
|
};
|
|
27967
28534
|
function createMcpServer(host, contextProvider) {
|
|
27968
28535
|
assertMcpHost(host);
|
|
27969
|
-
const here =
|
|
28536
|
+
const here = path15.dirname(fileURLToPath2(import.meta.url));
|
|
27970
28537
|
const sourceMarker = markSourcesLoaded([
|
|
27971
|
-
|
|
27972
|
-
|
|
27973
|
-
|
|
28538
|
+
path15.join(here, "server.ts"),
|
|
28539
|
+
path15.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
|
|
28540
|
+
path15.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
|
|
27974
28541
|
].filter((file) => existsSync7(file)));
|
|
27975
28542
|
let staleWarned = false;
|
|
27976
28543
|
const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
|
|
@@ -28251,6 +28818,7 @@ var allowedKeys = {
|
|
|
28251
28818
|
"stop_hook_active"
|
|
28252
28819
|
])
|
|
28253
28820
|
};
|
|
28821
|
+
var historyOfferSessions = new Set;
|
|
28254
28822
|
if (false)
|
|
28255
28823
|
;
|
|
28256
28824
|
|
|
@@ -28263,14 +28831,14 @@ var codexQualification = (host) => host === "codex_cli" ? { surface: host, cli:
|
|
|
28263
28831
|
var resolveCodexWorkspaceRoot = (pluginRoot, env) => {
|
|
28264
28832
|
const explicit = env.WORKFLOW_WORKSPACE_ROOT;
|
|
28265
28833
|
const inherited = env.PWD;
|
|
28266
|
-
const candidate = explicit ?? (inherited &&
|
|
28267
|
-
if (!candidate || !
|
|
28834
|
+
const candidate = explicit ?? (inherited && path16.resolve(inherited) !== path16.resolve(pluginRoot) ? inherited : undefined);
|
|
28835
|
+
if (!candidate || !path16.isAbsolute(candidate) || !existsSync8(candidate))
|
|
28268
28836
|
return null;
|
|
28269
28837
|
try {
|
|
28270
|
-
if (!
|
|
28838
|
+
if (!statSync3(candidate).isDirectory())
|
|
28271
28839
|
return null;
|
|
28272
|
-
const canonical =
|
|
28273
|
-
if (canonical ===
|
|
28840
|
+
const canonical = realpathSync9(candidate);
|
|
28841
|
+
if (canonical === realpathSync9(pluginRoot)) {
|
|
28274
28842
|
if (!explicit)
|
|
28275
28843
|
return null;
|
|
28276
28844
|
const workspace = new TaskStore(canonical).readWorkspace();
|