@brainervirus/workit-core 2.1.4 → 2.1.5
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/package.json +2 -2
- package/scripts/analyze-release-scope.ts +3 -3
- package/scripts/doctor-check.ts +1 -2
- package/scripts/sync-release-manifests.ts +4 -4
- package/src/core/authority.ts +8 -8
- package/src/core/auto-approval.ts +6 -6
- package/src/core/branch.ts +4 -4
- package/src/core/config-conversion.ts +1 -1
- package/src/core/config.ts +3 -3
- package/src/core/cutover.ts +6 -6
- package/src/core/doctor.ts +4 -3
- package/src/core/external-action-effects.ts +15 -15
- package/src/core/external-action.ts +16 -19
- package/src/core/logger.ts +2 -2
- package/src/core/policy-resolver.ts +9 -6
- package/src/core/registration.ts +3 -5
- package/src/core/repo-context.ts +1 -1
- package/src/core/session-context.ts +2 -2
- package/src/core/setup.ts +2 -2
- package/src/core/skill-manifests.ts +1 -1
- package/src/core/support-matrix.ts +1 -1
- package/src/core/task-context.ts +3 -3
- package/src/core/task-contract.ts +8 -8
- package/src/core/task-engine.ts +159 -159
- package/src/core/task-evaluation.ts +3 -3
- package/src/core/task-store.ts +12 -12
- package/src/core/tracker-issues.ts +1 -1
- package/src/core/uninstall.ts +3 -3
- package/src/core/workers.ts +3 -3
- package/src/core/workspaces.ts +4 -6
- package/src/core/youtrack-tools.ts +1 -1
|
@@ -94,7 +94,7 @@ const gitPaths = (root: string): Inventory => {
|
|
|
94
94
|
return {
|
|
95
95
|
paths: [],
|
|
96
96
|
uncertain: !/not a git repository/i.test(stderr),
|
|
97
|
-
git:
|
|
97
|
+
git: !/not a git repository/i.test(stderr),
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
100
|
const paths = result.stdout
|
|
@@ -219,7 +219,7 @@ export function captureCandidate(
|
|
|
219
219
|
|
|
220
220
|
const files: Candidate["files"] = [];
|
|
221
221
|
let completeness: Candidate["completeness"] = uncertain ? "uncertain" : "known";
|
|
222
|
-
for (const item of [...names].
|
|
222
|
+
for (const item of [...names].toSorted()) {
|
|
223
223
|
if (!scopeMatches(item, normalizedScope)) continue;
|
|
224
224
|
const target = path.join(checkout, item);
|
|
225
225
|
try {
|
|
@@ -264,7 +264,7 @@ export function captureCandidate(
|
|
|
264
264
|
)
|
|
265
265
|
return failure("invalid_input", "candidate environment names must be unique and non-empty");
|
|
266
266
|
const values = supplied
|
|
267
|
-
.
|
|
267
|
+
.toSorted((left, right) => compareCodeUnits(left.name, right.name))
|
|
268
268
|
.map(({ name, value }) => ({ name, value, refs: [] }));
|
|
269
269
|
if (values.some(({ value }) => value === null)) completeness = "uncertain";
|
|
270
270
|
const headResult = spawnSync("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
|
package/src/core/task-store.ts
CHANGED
|
@@ -208,10 +208,10 @@ const isNewerVersion = (candidate: string, current: string): boolean => {
|
|
|
208
208
|
.split("-")[0]
|
|
209
209
|
.split(".")
|
|
210
210
|
.map((item) => Number.parseInt(item, 10) || 0);
|
|
211
|
-
const [
|
|
211
|
+
const [candidateParts, currentParts] = [parts(candidate), parts(current)];
|
|
212
212
|
for (let index = 0; index < 3; index += 1) {
|
|
213
|
-
const left =
|
|
214
|
-
const right =
|
|
213
|
+
const left = candidateParts[index] ?? 0;
|
|
214
|
+
const right = currentParts[index] ?? 0;
|
|
215
215
|
if (left !== right) return left > right;
|
|
216
216
|
}
|
|
217
217
|
return false;
|
|
@@ -288,7 +288,7 @@ export class TaskStore {
|
|
|
288
288
|
return failure("recovery_required", "task filename and record ID differ", { taskId });
|
|
289
289
|
if (result.result.ok) {
|
|
290
290
|
const workspace = this.readWorkspace();
|
|
291
|
-
if (!workspace.ok) return workspace
|
|
291
|
+
if (!workspace.ok) return workspace;
|
|
292
292
|
if (!workspace.data || result.result.data.workspaceId !== workspace.data.id)
|
|
293
293
|
return failure("recovery_required", "task workspace binding is invalid", { taskId });
|
|
294
294
|
}
|
|
@@ -306,13 +306,13 @@ export class TaskStore {
|
|
|
306
306
|
});
|
|
307
307
|
}
|
|
308
308
|
const workspace = this.readWorkspace();
|
|
309
|
-
if (!workspace.ok) return workspace
|
|
309
|
+
if (!workspace.ok) return workspace;
|
|
310
310
|
if (!workspace.data)
|
|
311
311
|
return failure("recovery_required", "task workspace binding is invalid", {
|
|
312
312
|
path: this.workspacePath,
|
|
313
313
|
});
|
|
314
314
|
const tasks: TaskRecord[] = [];
|
|
315
|
-
for (const name of names.
|
|
315
|
+
for (const name of names.toSorted()) {
|
|
316
316
|
const item = this.readRecord<TaskRecord>(path.join(this.tasksDir, name), taskRecordSchema);
|
|
317
317
|
if (!item.exists) continue;
|
|
318
318
|
if (!item.result.ok) return item.result;
|
|
@@ -342,7 +342,7 @@ export class TaskStore {
|
|
|
342
342
|
});
|
|
343
343
|
}
|
|
344
344
|
const workspace = this.readWorkspace();
|
|
345
|
-
if (!workspace.ok) return workspace
|
|
345
|
+
if (!workspace.ok) return workspace;
|
|
346
346
|
if (!workspace.data)
|
|
347
347
|
return failure("recovery_required", "task workspace binding is invalid", {
|
|
348
348
|
path: this.workspacePath,
|
|
@@ -350,7 +350,7 @@ export class TaskStore {
|
|
|
350
350
|
const stored = this.readIndex(workspace.data.id);
|
|
351
351
|
const entries: TaskIndexEntry[] = [];
|
|
352
352
|
let changed = Object.keys(stored).length !== names.length;
|
|
353
|
-
for (const name of names.
|
|
353
|
+
for (const name of names.toSorted()) {
|
|
354
354
|
const file = path.join(this.tasksDir, name);
|
|
355
355
|
const signature = fileSignature(file);
|
|
356
356
|
if (signature === null) {
|
|
@@ -364,7 +364,7 @@ export class TaskStore {
|
|
|
364
364
|
}
|
|
365
365
|
const item = this.readRecord<TaskRecord>(file, taskRecordSchema);
|
|
366
366
|
if (!item.exists) continue;
|
|
367
|
-
if (!item.result.ok) return item.result
|
|
367
|
+
if (!item.result.ok) return item.result;
|
|
368
368
|
if (!validId(name.slice(0, -5)) || item.result.data.id !== name.slice(0, -5))
|
|
369
369
|
return failure("recovery_required", "task filename and record ID differ", { path: name });
|
|
370
370
|
if (item.result.data.workspaceId !== workspace.data.id)
|
|
@@ -514,11 +514,11 @@ export class TaskStore {
|
|
|
514
514
|
if (origin.workspaceId === current.data.id) {
|
|
515
515
|
const source = this.readTask(origin.taskId);
|
|
516
516
|
if (source.ok) return success(source.data.revision, current.data.revision, source.data);
|
|
517
|
-
if (source.code !== "not_found") return source
|
|
517
|
+
if (source.code !== "not_found") return source;
|
|
518
518
|
return failure("not_found", "source task is not present in this shared workspace");
|
|
519
519
|
}
|
|
520
520
|
const tasks = this.listTasks();
|
|
521
|
-
if (!tasks.ok) return tasks
|
|
521
|
+
if (!tasks.ok) return tasks;
|
|
522
522
|
const prior = tasks.data.filter(
|
|
523
523
|
(task) =>
|
|
524
524
|
task.origin?.workspaceId === origin.workspaceId &&
|
|
@@ -530,7 +530,7 @@ export class TaskStore {
|
|
|
530
530
|
`source task has multiple imported mappings: ${prior.map((task) => task.id).join(", ")}`,
|
|
531
531
|
);
|
|
532
532
|
if (prior.length === 1) {
|
|
533
|
-
const existing = prior[0]
|
|
533
|
+
const existing = prior[0];
|
|
534
534
|
if (existing.origin!.exportDigest !== origin.exportDigest)
|
|
535
535
|
return failure(
|
|
536
536
|
"revision_conflict",
|
|
@@ -55,7 +55,7 @@ const defaultCli = (bin: string, available: boolean): TrackerCli | null => {
|
|
|
55
55
|
return Promise.resolve({
|
|
56
56
|
status: out.status ?? 1,
|
|
57
57
|
stdout: out.stdout ?? "",
|
|
58
|
-
stderr:
|
|
58
|
+
stderr: out.error?.message ?? out.stderr ?? "",
|
|
59
59
|
});
|
|
60
60
|
};
|
|
61
61
|
};
|
package/src/core/uninstall.ts
CHANGED
|
@@ -125,7 +125,7 @@ function cleanCursorSettings(
|
|
|
125
125
|
const next = { ...settings };
|
|
126
126
|
let changed = false;
|
|
127
127
|
if (isRecord(next.enabled_plugins)) {
|
|
128
|
-
const enabled = { ...
|
|
128
|
+
const enabled = { ...next.enabled_plugins };
|
|
129
129
|
for (const identity of ["workit", ...CURSOR_LEGACY_IDENTITIES]) {
|
|
130
130
|
if (identity in enabled) {
|
|
131
131
|
delete enabled[identity];
|
|
@@ -177,7 +177,7 @@ function cleanCursorMcp(mcp: Record<string, unknown>): {
|
|
|
177
177
|
const next = { ...mcp };
|
|
178
178
|
let changed = false;
|
|
179
179
|
if (isRecord(next.mcpServers)) {
|
|
180
|
-
const servers = { ...
|
|
180
|
+
const servers = { ...next.mcpServers };
|
|
181
181
|
for (const name of ["workit", "workflow-toolkit"]) {
|
|
182
182
|
if (name in servers) {
|
|
183
183
|
delete servers[name];
|
|
@@ -211,7 +211,7 @@ function cleanPiConfig(config: Record<string, unknown>): {
|
|
|
211
211
|
changed = true;
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
|
-
const pi = isRecord(next.pi) ? { ...
|
|
214
|
+
const pi = isRecord(next.pi) ? { ...next.pi } : null;
|
|
215
215
|
if (pi && Array.isArray(pi.extensions)) {
|
|
216
216
|
const kept = pi.extensions.filter((entry) => !String(entry).includes("workit"));
|
|
217
217
|
if (kept.length !== pi.extensions.length) {
|
package/src/core/workers.ts
CHANGED
|
@@ -117,7 +117,7 @@ const callerSession = (caller: CallerContext): HostSession =>
|
|
|
117
117
|
const validPath = (value: unknown): value is string =>
|
|
118
118
|
typeof value === "string" &&
|
|
119
119
|
value.length > 0 &&
|
|
120
|
-
!
|
|
120
|
+
!Array.from(value).some((char) => {
|
|
121
121
|
const code = char.charCodeAt(0);
|
|
122
122
|
return code < 32 || code === 127;
|
|
123
123
|
}) &&
|
|
@@ -157,9 +157,9 @@ export function assertProductWriteAllowed(input: ProductWriteInput): Result<Owne
|
|
|
157
157
|
return failure("invalid_input", "invalid product write path");
|
|
158
158
|
if (!input.store) return failure("permission_denied", "write authorization requires core state");
|
|
159
159
|
const task = input.store.readTask(input.task.id);
|
|
160
|
-
if (!task.ok) return task
|
|
160
|
+
if (!task.ok) return task;
|
|
161
161
|
const workspace = input.store.readWorkspace();
|
|
162
|
-
if (!workspace.ok) return workspace
|
|
162
|
+
if (!workspace.ok) return workspace;
|
|
163
163
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
164
164
|
const workspaceRecord = workspace.data;
|
|
165
165
|
if (task.data.status !== "active")
|
package/src/core/workspaces.ts
CHANGED
|
@@ -552,6 +552,7 @@ const resolveRuntimeWorkspaceCandidate = (
|
|
|
552
552
|
if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
|
|
553
553
|
throw new Error(
|
|
554
554
|
`${file} could not be read: ${error instanceof Error ? error.message : String(error)}`,
|
|
555
|
+
{ cause: error },
|
|
555
556
|
);
|
|
556
557
|
}
|
|
557
558
|
let parsed: unknown;
|
|
@@ -567,7 +568,7 @@ const resolveRuntimeWorkspaceCandidate = (
|
|
|
567
568
|
.join("; ");
|
|
568
569
|
throw new Error(`${file} has invalid workspace matching data: ${detail}`);
|
|
569
570
|
}
|
|
570
|
-
const entries = validated.data.workspaces
|
|
571
|
+
const entries = validated.data.workspaces;
|
|
571
572
|
for (const entry of entries ?? []) {
|
|
572
573
|
const glob = validateWorkspaceGlob(entry.glob);
|
|
573
574
|
if (!glob.ok) throw new Error(`${file} workspace ${entry.name}: ${glob.error}`);
|
|
@@ -619,10 +620,7 @@ export const resolveRuntimeWorkspacePolicy = (
|
|
|
619
620
|
`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(profileName)}`,
|
|
620
621
|
);
|
|
621
622
|
const profilePolicy = profile?.[key] as WorkspaceBranchPolicy | WorkspaceCommitPolicy | undefined;
|
|
622
|
-
const workspacePolicy = candidate[key]
|
|
623
|
-
| WorkspaceBranchPolicy
|
|
624
|
-
| WorkspaceCommitPolicy
|
|
625
|
-
| undefined;
|
|
623
|
+
const workspacePolicy = candidate[key];
|
|
626
624
|
return {
|
|
627
625
|
policy: profilePolicy ?? workspacePolicy,
|
|
628
626
|
source: profilePolicy
|
|
@@ -700,7 +698,7 @@ export const selectReleaseTrack = (
|
|
|
700
698
|
requestedTrack?: string,
|
|
701
699
|
): ReleaseTrackSelection => {
|
|
702
700
|
const tracks = workspace.releaseTracks ?? {};
|
|
703
|
-
const choices = Object.keys(tracks).
|
|
701
|
+
const choices = Object.keys(tracks).toSorted();
|
|
704
702
|
if (requestedTrack === undefined) {
|
|
705
703
|
if (choices.length === 0) return { status: "not_configured" };
|
|
706
704
|
if (choices.length > 1) return { status: "choice_required", choices };
|