@brainervirus/workit-core 0.11.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/package.json +8 -3
- package/scripts/analyze-release-scope.ts +4 -1
- package/scripts/doctor-check.ts +3 -1
- package/scripts/install-codex-plugin.sh +28 -0
- package/scripts/install-cursor-plugin.sh +1 -0
- package/scripts/install-opencode-plugin.sh +1 -0
- package/scripts/install-pi-package.sh +56 -0
- package/scripts/rewrite-workspace-deps.ts +22 -5
- package/scripts/sync-release-manifests.ts +8 -5
- package/scripts/sync-runtime.sh +7 -3
- package/scripts/validate-cursor-marketplace.ts +16 -62
- package/skills/workit-babysit/SKILL.md +33 -0
- package/skills/workit-behavioral-tdd/SKILL.md +53 -0
- package/skills/workit-blast-radius/SKILL.md +31 -0
- package/skills/workit-challenge/SKILL.md +62 -0
- package/skills/workit-debug/SKILL.md +61 -0
- package/skills/workit-deslop/SKILL.md +36 -0
- package/skills/workit-diagram/SKILL.md +32 -0
- package/skills/workit-green-run/SKILL.md +29 -0
- package/skills/workit-handoff/SKILL.md +43 -0
- package/skills/workit-implement/SKILL.md +46 -0
- package/skills/workit-mockup/SKILL.md +28 -0
- package/skills/workit-plan/SKILL.md +66 -0
- package/skills/workit-review/SKILL.md +60 -0
- package/skills/workit-steer/SKILL.md +32 -0
- package/src/core/authority.ts +918 -0
- package/src/core/boundary.ts +29 -0
- package/src/core/branch.ts +26 -255
- package/src/core/changelog.ts +95 -18
- package/src/core/commit-flavors.ts +65 -0
- package/src/core/config-conversion.ts +183 -0
- package/src/core/config.ts +42 -0
- package/src/core/cutover.ts +721 -0
- package/src/core/detect-hosts.ts +107 -0
- package/src/core/doctor.ts +428 -18
- package/src/core/external-action-effects.ts +1171 -0
- package/src/core/external-action.ts +541 -0
- package/src/core/init.ts +83 -32
- package/src/core/methods.ts +125 -0
- package/src/core/policy-resolver.ts +523 -0
- package/src/core/pr-create.ts +7 -1
- package/src/core/registration.ts +69 -4
- package/src/core/repo-context.ts +59 -1
- package/src/core/setup.ts +28 -0
- package/src/core/skill-manifests.ts +40 -42
- package/src/core/support-matrix.ts +8 -3
- package/src/core/sync-runtime.ts +15 -3
- package/src/core/task-context.ts +179 -0
- package/src/core/task-contract.ts +1136 -0
- package/src/core/task-engine.ts +2562 -0
- package/src/core/task-evaluation.ts +679 -0
- package/src/core/task-store.ts +1071 -0
- package/src/core/tracker-issues.ts +237 -0
- package/src/core/uninstall.ts +67 -3
- package/src/core/vcs-config.ts +53 -22
- package/src/core/workers.ts +166 -0
- package/src/core/workspaces.ts +3 -1
- package/src/core/youtrack-tools.ts +2 -0
- package/src/core/youtrack.ts +145 -10
- package/src/core.ts +126 -0
- package/templates/execution-contract.md +17 -50
- package/templates/plan-template.md +3 -3
- package/templates/spec-template.md +15 -3
- package/templates/workit-contract.md +12 -0
- package/commands/wk-changelog.md +0 -2
- package/commands/wk-commit.md +0 -2
- package/commands/wk-docs-refresh.md +0 -2
- package/commands/wk-handoff.md +0 -2
- package/commands/wk-implement.md +0 -2
- package/commands/wk-init.md +0 -2
- package/commands/wk-issue-update.md +0 -2
- package/commands/wk-meetings.md +0 -2
- package/commands/wk-pr.md +0 -2
- package/commands/wk-release-notes.md +0 -2
- package/commands/wk-status.md +0 -2
- package/commands/wk-verify.md +0 -2
- package/scripts/update-superpowers.sh +0 -82
- package/scripts/vendor-assets.ts +0 -37
- package/skills/wk-changelog/SKILL.md +0 -15
- package/skills/wk-commit/SKILL.md +0 -16
- package/skills/wk-docs-refresh/SKILL.md +0 -15
- package/skills/wk-handoff/SKILL.md +0 -20
- package/skills/wk-implement/SKILL.md +0 -47
- package/skills/wk-init/SKILL.md +0 -31
- package/skills/wk-issue-update/SKILL.md +0 -27
- package/skills/wk-issue-update/references/youtrack-update-style.md +0 -81
- package/skills/wk-meetings/SKILL.md +0 -17
- package/skills/wk-pr/SKILL.md +0 -27
- package/skills/wk-release-notes/SKILL.md +0 -15
- package/skills/wk-status/SKILL.md +0 -16
- package/skills/wk-verify/SKILL.md +0 -16
- package/src/core/detector.ts +0 -239
- package/src/core/flow-state.ts +0 -3308
- package/src/core/handoff-context.ts +0 -136
- package/src/core/handoff-tools.ts +0 -133
- package/src/core/menu.ts +0 -70
- package/src/core/plan-tasks.ts +0 -43
- package/src/core/reminder.ts +0 -124
- package/src/core/sdd.ts +0 -353
- package/src/state.ts +0 -22
- package/templates/superpowers-doc-contract.md +0 -75
- package/vendor/superpowers/skills/brainstorming/SKILL.md +0 -159
- package/vendor/superpowers/skills/brainstorming/scripts/frame-template.html +0 -213
- package/vendor/superpowers/skills/brainstorming/scripts/helper.js +0 -167
- package/vendor/superpowers/skills/brainstorming/scripts/server.cjs +0 -723
- package/vendor/superpowers/skills/brainstorming/scripts/start-server.sh +0 -209
- package/vendor/superpowers/skills/brainstorming/scripts/stop-server.sh +0 -120
- package/vendor/superpowers/skills/brainstorming/spec-document-reviewer-prompt.md +0 -49
- package/vendor/superpowers/skills/brainstorming/visual-companion.md +0 -291
- package/vendor/superpowers/skills/dispatching-parallel-agents/SKILL.md +0 -185
- package/vendor/superpowers/skills/executing-plans/SKILL.md +0 -70
- package/vendor/superpowers/skills/finishing-a-development-branch/SKILL.md +0 -241
- package/vendor/superpowers/skills/receiving-code-review/SKILL.md +0 -213
- package/vendor/superpowers/skills/requesting-code-review/SKILL.md +0 -103
- package/vendor/superpowers/skills/requesting-code-review/code-reviewer.md +0 -172
- package/vendor/superpowers/skills/subagent-driven-development/SKILL.md +0 -428
- package/vendor/superpowers/skills/subagent-driven-development/implementer-prompt.md +0 -139
- package/vendor/superpowers/skills/subagent-driven-development/scripts/review-package +0 -44
- package/vendor/superpowers/skills/subagent-driven-development/scripts/sdd-workspace +0 -22
- package/vendor/superpowers/skills/subagent-driven-development/scripts/task-brief +0 -40
- package/vendor/superpowers/skills/subagent-driven-development/task-reviewer-prompt.md +0 -188
- package/vendor/superpowers/skills/systematic-debugging/CREATION-LOG.md +0 -119
- package/vendor/superpowers/skills/systematic-debugging/SKILL.md +0 -296
- package/vendor/superpowers/skills/systematic-debugging/condition-based-waiting-example.ts +0 -158
- package/vendor/superpowers/skills/systematic-debugging/condition-based-waiting.md +0 -115
- package/vendor/superpowers/skills/systematic-debugging/defense-in-depth.md +0 -122
- package/vendor/superpowers/skills/systematic-debugging/find-polluter.sh +0 -63
- package/vendor/superpowers/skills/systematic-debugging/root-cause-tracing.md +0 -169
- package/vendor/superpowers/skills/systematic-debugging/test-academic.md +0 -14
- package/vendor/superpowers/skills/systematic-debugging/test-pressure-1.md +0 -58
- package/vendor/superpowers/skills/systematic-debugging/test-pressure-2.md +0 -68
- package/vendor/superpowers/skills/systematic-debugging/test-pressure-3.md +0 -69
- package/vendor/superpowers/skills/test-driven-development/SKILL.md +0 -371
- package/vendor/superpowers/skills/test-driven-development/testing-anti-patterns.md +0 -299
- package/vendor/superpowers/skills/using-git-worktrees/SKILL.md +0 -202
- package/vendor/superpowers/skills/using-superpowers/SKILL.md +0 -62
- package/vendor/superpowers/skills/using-superpowers/references/antigravity-tools.md +0 -23
- package/vendor/superpowers/skills/using-superpowers/references/codex-tools.md +0 -39
- package/vendor/superpowers/skills/using-superpowers/references/pi-tools.md +0 -16
- package/vendor/superpowers/skills/verification-before-completion/SKILL.md +0 -139
- package/vendor/superpowers/skills/writing-plans/SKILL.md +0 -174
- package/vendor/superpowers/skills/writing-plans/plan-document-reviewer-prompt.md +0 -49
- package/vendor/superpowers/skills/writing-skills/SKILL.md +0 -689
- package/vendor/superpowers/skills/writing-skills/anthropic-best-practices.md +0 -1150
- package/vendor/superpowers/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +0 -189
- package/vendor/superpowers/skills/writing-skills/graphviz-conventions.dot +0 -172
- package/vendor/superpowers/skills/writing-skills/persuasion-principles.md +0 -187
- package/vendor/superpowers/skills/writing-skills/render-graphs.js +0 -168
- package/vendor/superpowers/skills/writing-skills/testing-skills-with-subagents.md +0 -384
package/src/core/repo-context.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { vcsConfig, mergedPrStyle } from "./vcs-config";
|
|
5
|
+
import { gitRevisionParts, resolveGitRevision } from "../core";
|
|
5
6
|
|
|
6
7
|
// Ports of scripts/_shared/common.sh + the four maintained context generators
|
|
7
8
|
// (pr-ready-context.sh, changelog-context.sh, docs-refresh-context.sh,
|
|
@@ -10,6 +11,37 @@ import { vcsConfig, mergedPrStyle } from "./vcs-config";
|
|
|
10
11
|
|
|
11
12
|
export type ContextResult = { stdout: string; stderr: string; exitCode: number; cwd: string };
|
|
12
13
|
|
|
14
|
+
// Range/revision values are passed as positional git arguments. Reject option
|
|
15
|
+
// syntax and control characters before any context generator invokes git; the
|
|
16
|
+
// trailing `--` used for file paths cannot protect an earlier revision arg.
|
|
17
|
+
export const isSafeContextRange = (value: string): boolean =>
|
|
18
|
+
value.length <= 4096 &&
|
|
19
|
+
(() => {
|
|
20
|
+
try {
|
|
21
|
+
gitRevisionParts(value);
|
|
22
|
+
return true;
|
|
23
|
+
} catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
})();
|
|
27
|
+
|
|
28
|
+
const isResolvableContextRange = (cwd: string, value: string): boolean => {
|
|
29
|
+
if (!isSafeContextRange(value)) return false;
|
|
30
|
+
try {
|
|
31
|
+
resolveGitRevision(cwd, value);
|
|
32
|
+
return true;
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const invalidRange = (cwd: string): ContextResult => ({
|
|
39
|
+
stdout: "",
|
|
40
|
+
stderr: "ERROR: invalid revision range\n",
|
|
41
|
+
exitCode: 2,
|
|
42
|
+
cwd,
|
|
43
|
+
});
|
|
44
|
+
|
|
13
45
|
const runGit = (
|
|
14
46
|
cwd: string,
|
|
15
47
|
args: string[],
|
|
@@ -238,6 +270,7 @@ const readTrimmed = (file: string, maxLines: number): string => {
|
|
|
238
270
|
/** Port of pr-ready-context.sh — PR-ready repository context. */
|
|
239
271
|
export function prReadyContext(root: string, range?: string): ContextResult {
|
|
240
272
|
const cwd = path.resolve(repoRoot(root));
|
|
273
|
+
if (range !== undefined && !isResolvableContextRange(cwd, range)) return invalidRange(cwd);
|
|
241
274
|
let stdout = "";
|
|
242
275
|
const autoRange = range === undefined;
|
|
243
276
|
|
|
@@ -306,7 +339,7 @@ export function prReadyContext(root: string, range?: string): ContextResult {
|
|
|
306
339
|
const vc = vcsConfig("resolve", cwd);
|
|
307
340
|
if (vc.ok) {
|
|
308
341
|
stdout += `workspace: ${String(vc.workspace_name ?? "none")}\n`;
|
|
309
|
-
stdout += `provider: ${String(vc.provider ?? "
|
|
342
|
+
stdout += `provider: ${String(vc.provider ?? "unconfigured")}\n`;
|
|
310
343
|
} else {
|
|
311
344
|
// RL-01: never report silent defaults for malformed vcs.json — surface the
|
|
312
345
|
// exact-path diagnostic instead.
|
|
@@ -336,7 +369,9 @@ const CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
336
369
|
/** Port of changelog-context.sh — changelog update context. */
|
|
337
370
|
export function changelogContext(root: string, range?: string): ContextResult {
|
|
338
371
|
const cwd = path.resolve(repoRoot(root));
|
|
372
|
+
if (range !== undefined && !isResolvableContextRange(cwd, range)) return invalidRange(cwd);
|
|
339
373
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
374
|
+
if (!isResolvableContextRange(cwd, resolvedRange)) return invalidRange(cwd);
|
|
340
375
|
let stdout = "";
|
|
341
376
|
|
|
342
377
|
stdout += printSection("Repository");
|
|
@@ -370,7 +405,9 @@ export function changelogContext(root: string, range?: string): ContextResult {
|
|
|
370
405
|
/** Port of docs-refresh-context.sh — documentation refresh context. */
|
|
371
406
|
export function docsRefreshContext(root: string, range?: string): ContextResult {
|
|
372
407
|
const cwd = path.resolve(repoRoot(root));
|
|
408
|
+
if (range !== undefined && !isResolvableContextRange(cwd, range)) return invalidRange(cwd);
|
|
373
409
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
410
|
+
if (!isResolvableContextRange(cwd, resolvedRange)) return invalidRange(cwd);
|
|
374
411
|
let stdout = "";
|
|
375
412
|
|
|
376
413
|
stdout += printSection("Repository");
|
|
@@ -416,7 +453,9 @@ export function releaseNotesContext(root: string, rangeOrTag: string): ContextRe
|
|
|
416
453
|
if (!rangeOrTag) {
|
|
417
454
|
return { stdout: "", stderr: "ERROR: release tag or range required\n", exitCode: 1, cwd };
|
|
418
455
|
}
|
|
456
|
+
if (!isResolvableContextRange(cwd, rangeOrTag)) return invalidRange(cwd);
|
|
419
457
|
const resolvedRange = rangeArgOrDefault(rangeOrTag, cwd);
|
|
458
|
+
if (!isResolvableContextRange(cwd, resolvedRange)) return invalidRange(cwd);
|
|
420
459
|
let stdout = "";
|
|
421
460
|
|
|
422
461
|
stdout += printSection("Repository");
|
|
@@ -443,5 +482,24 @@ export function releaseNotesContext(root: string, rangeOrTag: string): ContextRe
|
|
|
443
482
|
if (existsSync(path.join(cwd, rel))) stdout += rel + "\n";
|
|
444
483
|
}
|
|
445
484
|
|
|
485
|
+
// A deterministic draft is useful to every read-only host surface. It is
|
|
486
|
+
// derived only from the already-resolved range; publication and file edits
|
|
487
|
+
// remain separate, writer-guarded actions.
|
|
488
|
+
stdout += printSection("Release Notes Draft");
|
|
489
|
+
stdout += `## Release notes (${resolvedRange})\n\n`;
|
|
490
|
+
const commits = commitLogForRange(cwd, resolvedRange)
|
|
491
|
+
.split("\n")
|
|
492
|
+
.map((line) => line.trim())
|
|
493
|
+
.filter(Boolean);
|
|
494
|
+
stdout += commits.length
|
|
495
|
+
? `${commits.map((line) => `- ${line}`).join("\n")}\n`
|
|
496
|
+
: "- No commits in range.\n";
|
|
497
|
+
stdout += "\n### Changed files\n\n";
|
|
498
|
+
const files = changedFilesForRange(cwd, resolvedRange)
|
|
499
|
+
.split("\n")
|
|
500
|
+
.map((line) => line.trim())
|
|
501
|
+
.filter(Boolean);
|
|
502
|
+
stdout += files.length ? `${files.map((line) => `- ${line}`).join("\n")}\n` : "- None.\n";
|
|
503
|
+
|
|
446
504
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
447
505
|
}
|
package/src/core/setup.ts
CHANGED
|
@@ -1231,3 +1231,31 @@ export function applyWorkspaceBranchPolicy(opts: {
|
|
|
1231
1231
|
config_path: wsPath,
|
|
1232
1232
|
};
|
|
1233
1233
|
}
|
|
1234
|
+
|
|
1235
|
+
export {
|
|
1236
|
+
applyCutover,
|
|
1237
|
+
applyRollback,
|
|
1238
|
+
classifyHostGeneration,
|
|
1239
|
+
readCutoverReceipt,
|
|
1240
|
+
detectCursorLatest,
|
|
1241
|
+
detectSourceLinkedOpenCode,
|
|
1242
|
+
previewCutover,
|
|
1243
|
+
previewRollback,
|
|
1244
|
+
readGenerationState,
|
|
1245
|
+
type CutoverDecision,
|
|
1246
|
+
type CutoverHost,
|
|
1247
|
+
type CutoverPaths,
|
|
1248
|
+
type CutoverPlan,
|
|
1249
|
+
type CutoverReceipt,
|
|
1250
|
+
type GenerationState,
|
|
1251
|
+
type RollbackPreview,
|
|
1252
|
+
} from "./cutover";
|
|
1253
|
+
export {
|
|
1254
|
+
applyConversionConfig,
|
|
1255
|
+
previewConversion,
|
|
1256
|
+
redactConversionPreview,
|
|
1257
|
+
type ConversionApplyResult,
|
|
1258
|
+
type ConversionInput,
|
|
1259
|
+
type ConversionMapping,
|
|
1260
|
+
type ConversionPreview,
|
|
1261
|
+
} from "./config-conversion";
|
|
@@ -1,37 +1,40 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
|
-
export const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
4
|
+
export const WORKIT_METHOD_SKILLS = [
|
|
5
|
+
"workit-challenge",
|
|
6
|
+
"workit-behavioral-tdd",
|
|
7
|
+
"workit-review",
|
|
8
|
+
"workit-plan",
|
|
9
|
+
"workit-implement",
|
|
10
|
+
"workit-debug",
|
|
11
|
+
"workit-handoff",
|
|
12
|
+
"workit-babysit",
|
|
13
|
+
"workit-blast-radius",
|
|
14
|
+
"workit-deslop",
|
|
15
|
+
"workit-diagram",
|
|
16
|
+
"workit-mockup",
|
|
17
|
+
"workit-green-run",
|
|
18
|
+
"workit-steer",
|
|
19
|
+
] as const;
|
|
20
|
+
|
|
21
|
+
/** wk- slash aliases (one per skill): alias → method skill. An alias routes
|
|
22
|
+
* through policy to model skills; an alias never calls another alias. */
|
|
23
|
+
export const WORKIT_SKILL_ALIASES = {
|
|
24
|
+
"wk-challenge": "workit-challenge",
|
|
25
|
+
"wk-babysit": "workit-babysit",
|
|
26
|
+
"wk-implement": "workit-implement",
|
|
27
|
+
"wk-plan": "workit-plan",
|
|
28
|
+
"wk-debug": "workit-debug",
|
|
29
|
+
"wk-review": "workit-review",
|
|
30
|
+
"wk-handoff": "workit-handoff",
|
|
31
|
+
"wk-tdd": "workit-behavioral-tdd",
|
|
32
|
+
"wk-blast-radius": "workit-blast-radius",
|
|
33
|
+
"wk-deslop": "workit-deslop",
|
|
34
|
+
"wk-diagram": "workit-diagram",
|
|
35
|
+
"wk-mockup": "workit-mockup",
|
|
36
|
+
"wk-green-run": "workit-green-run",
|
|
37
|
+
"wk-steer": "workit-steer",
|
|
35
38
|
} as const;
|
|
36
39
|
|
|
37
40
|
export const skillManifestNames = (root: string): string[] =>
|
|
@@ -54,22 +57,17 @@ export const validateSkillManifests = (
|
|
|
54
57
|
: `${label} mismatch at ${root} (missing: ${missing.join(", ") || "none"}; extra: ${extra.join(", ") || "none"})`;
|
|
55
58
|
};
|
|
56
59
|
|
|
57
|
-
export const validateCursorSkills = (
|
|
60
|
+
export const validateCursorSkills = (
|
|
61
|
+
pluginDir: string,
|
|
62
|
+
expected: readonly string[] = WORKIT_METHOD_SKILLS,
|
|
63
|
+
): string | null => {
|
|
58
64
|
const workit = validateSkillManifests(
|
|
59
65
|
path.join(pluginDir, "skills"),
|
|
60
|
-
|
|
66
|
+
expected,
|
|
61
67
|
"Cursor Workit skills",
|
|
62
68
|
);
|
|
63
69
|
if (workit) return workit;
|
|
64
|
-
const
|
|
65
|
-
const superpowers = validateSkillManifests(
|
|
66
|
-
vendor,
|
|
67
|
-
CANONICAL_SKILLS.superpowers,
|
|
68
|
-
"Cursor Superpowers skills",
|
|
69
|
-
);
|
|
70
|
-
if (superpowers) return superpowers;
|
|
71
|
-
|
|
72
|
-
const pending = [vendor];
|
|
70
|
+
const pending = [path.join(pluginDir, "skills")];
|
|
73
71
|
while (pending.length > 0) {
|
|
74
72
|
const dir = pending.pop()!;
|
|
75
73
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
@@ -5,8 +5,13 @@
|
|
|
5
5
|
// Deno is intentionally not part of the matrix (PT-12): nothing advertises a
|
|
6
6
|
// host that has no executable artifact/test evidence.
|
|
7
7
|
export const SUPPORT_MATRIX = {
|
|
8
|
-
bun: "1.
|
|
9
|
-
node: { minimum: "
|
|
10
|
-
opencode: { minimum: "1.15.0", current: "1.
|
|
8
|
+
bun: "1.4.1",
|
|
9
|
+
node: { minimum: "24", current: "24.20.0" },
|
|
10
|
+
opencode: { minimum: "1.15.0", current: "1.18.30" },
|
|
11
|
+
// Codex is a qualification host, not a runtime dependency: the CLI version
|
|
12
|
+
// below is the one live qualification evidence covers. The doctor warns when
|
|
13
|
+
// an installed CLI drifts ahead so a fresh install never silently outruns
|
|
14
|
+
// the qualification pin.
|
|
15
|
+
codex: { cli: "0.153.4", desktopPackage: "26.901.20858", bundledCodexCli: "0.153.0-alpha.5" },
|
|
11
16
|
os: ["ubuntu-latest", "macos-latest", "windows-latest"],
|
|
12
17
|
} as const;
|
package/src/core/sync-runtime.ts
CHANGED
|
@@ -165,10 +165,15 @@ export async function syncRuntime(options: SyncRuntimeOptions = {}): Promise<Syn
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
const cursorSrc = path.join(src, "packages/workit-cursor");
|
|
168
|
-
const cursorEntries = ["mcp-server.js", "cursor-session-start.js"];
|
|
168
|
+
const cursorEntries = ["mcp-server.js", "cursor-session-start.js", "workit-hook.js"];
|
|
169
169
|
const bun = resolveBun(env, home);
|
|
170
170
|
if (!bun.ok) return bun;
|
|
171
|
-
const dependencies = [
|
|
171
|
+
const dependencies = [
|
|
172
|
+
"@brainervirus/workit-core",
|
|
173
|
+
"@brainervirus/workit-mcp",
|
|
174
|
+
"@modelcontextprotocol/sdk",
|
|
175
|
+
"zod",
|
|
176
|
+
];
|
|
172
177
|
if (
|
|
173
178
|
dependencies.some((dependency) => !existsSync(path.join(src, "node_modules", dependency)))
|
|
174
179
|
) {
|
|
@@ -183,6 +188,13 @@ export async function syncRuntime(options: SyncRuntimeOptions = {}): Promise<Syn
|
|
|
183
188
|
};
|
|
184
189
|
}
|
|
185
190
|
}
|
|
191
|
+
const mcpBuild = run(bun.path, [path.join(src, "packages/workit-mcp/scripts/build.ts")], {
|
|
192
|
+
cwd: src,
|
|
193
|
+
env: { ...env, PATH: `${path.dirname(bun.path)}${path.delimiter}${env.PATH ?? ""}` },
|
|
194
|
+
});
|
|
195
|
+
if (mcpBuild.exitCode !== 0) {
|
|
196
|
+
return { ok: false, error: `FATAL: shared MCP build failed in ${src}: ${mcpBuild.stderr}` };
|
|
197
|
+
}
|
|
186
198
|
const build = run(bun.path, [path.join(cursorSrc, "scripts/build.ts")], {
|
|
187
199
|
cwd: src,
|
|
188
200
|
env: { ...env, PATH: `${path.dirname(bun.path)}${path.delimiter}${env.PATH ?? ""}` },
|
|
@@ -301,7 +313,7 @@ export async function syncRuntime(options: SyncRuntimeOptions = {}): Promise<Syn
|
|
|
301
313
|
dependencies?: Record<string, string>;
|
|
302
314
|
};
|
|
303
315
|
data.dependencies = data.dependencies ?? {};
|
|
304
|
-
data.dependencies["@opencode-ai/plugin"] ??= "1.
|
|
316
|
+
data.dependencies["@opencode-ai/plugin"] ??= "1.18.30";
|
|
305
317
|
writeFileSync(pkg, JSON.stringify(data, null, 2) + "\n");
|
|
306
318
|
} catch {
|
|
307
319
|
/* best-effort */
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import {
|
|
2
|
+
POLICY_VERSION,
|
|
3
|
+
failure,
|
|
4
|
+
sha256,
|
|
5
|
+
success,
|
|
6
|
+
type Candidate,
|
|
7
|
+
type Decision,
|
|
8
|
+
type Provenance,
|
|
9
|
+
type Progress,
|
|
10
|
+
type Ref,
|
|
11
|
+
type Result,
|
|
12
|
+
type TaskView,
|
|
13
|
+
type Utc,
|
|
14
|
+
} from "./task-contract";
|
|
15
|
+
import { captureCandidate, evaluateEvidence } from "./task-evaluation";
|
|
16
|
+
import type { NativeWorkerObservation } from "./workers";
|
|
17
|
+
|
|
18
|
+
export type ResumeObservation = {
|
|
19
|
+
observation: NativeWorkerObservation;
|
|
20
|
+
authority: Provenance | null;
|
|
21
|
+
};
|
|
22
|
+
export type ResumeObservationInput = ResumeObservation | NativeWorkerObservation;
|
|
23
|
+
|
|
24
|
+
export type ResumeReconciliation = {
|
|
25
|
+
candidate: Candidate;
|
|
26
|
+
staleEvidenceIds: string[];
|
|
27
|
+
workerUpdates: NativeWorkerObservation[];
|
|
28
|
+
blockers: Progress["blockers"];
|
|
29
|
+
reassessmentRequired: boolean;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export function reconcileResume(
|
|
33
|
+
view: TaskView,
|
|
34
|
+
observations: ResumeObservationInput[] = [],
|
|
35
|
+
): Result<ResumeReconciliation> {
|
|
36
|
+
if (observations.length > 0)
|
|
37
|
+
return failure("permission_denied", "worker observations require native host verification");
|
|
38
|
+
const candidate = captureCandidate(view.workspace.root, view.task.intent.data.scope, []);
|
|
39
|
+
if (!candidate.ok) return candidate as Result<never>;
|
|
40
|
+
const staleEvidenceIds = evaluateEvidence(view.task, candidate.data)
|
|
41
|
+
.filter((entry) => entry.status === "stale")
|
|
42
|
+
.map((entry) => entry.evidenceId);
|
|
43
|
+
const blockers = [...view.task.progress.blockers];
|
|
44
|
+
if (
|
|
45
|
+
view.task.workers.some((entry) =>
|
|
46
|
+
["running", "cancelling", "unknown"].includes(entry.data.state),
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
blockers.push({
|
|
50
|
+
reason: "worker state requires reconciliation",
|
|
51
|
+
dependentAction: "resume",
|
|
52
|
+
refs: [],
|
|
53
|
+
});
|
|
54
|
+
return success(null, null, {
|
|
55
|
+
candidate: candidate.data,
|
|
56
|
+
staleEvidenceIds,
|
|
57
|
+
workerUpdates: [],
|
|
58
|
+
blockers,
|
|
59
|
+
reassessmentRequired: view.task.policy?.policyVersion !== POLICY_VERSION,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
type CompactReference = { kind: Ref["kind"] } & Partial<
|
|
64
|
+
Pick<Extract<Ref, { kind: "file" }>, "path">
|
|
65
|
+
> &
|
|
66
|
+
Partial<Pick<Extract<Ref, { kind: "record" }>, "collection" | "id">>;
|
|
67
|
+
type CompactDecision = {
|
|
68
|
+
id: string;
|
|
69
|
+
purpose: Decision["purpose"];
|
|
70
|
+
status: Decision["response"];
|
|
71
|
+
digest: string;
|
|
72
|
+
references: CompactReference[];
|
|
73
|
+
};
|
|
74
|
+
export type CompactTaskContext = {
|
|
75
|
+
objective: string;
|
|
76
|
+
status: TaskView["task"]["status"];
|
|
77
|
+
decisions: CompactDecision[];
|
|
78
|
+
gaps: string[];
|
|
79
|
+
nextAction: string | null;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const COMPACT_MAX_BYTES = 4096;
|
|
83
|
+
const COMPACT_MAX_ITEMS = 8;
|
|
84
|
+
const COMPACT_TEXT_BYTES = 128;
|
|
85
|
+
const sensitiveUrl = /\bhttps?:\/\/[^/\s:@]+:[^@\s]+@/gi;
|
|
86
|
+
const sensitiveValue =
|
|
87
|
+
/(?:token|secret|password|passwd|api[-_ ]?key|private[-_ ]?key|authorization)\s*[:=]\s*(?:bearer\s+)?\S+|\bbearer\s+\S+/gi;
|
|
88
|
+
const privateKey = /-----BEGIN [^-]*PRIVATE KEY-----[\s\S]*?-----END [^-]*PRIVATE KEY-----/gi;
|
|
89
|
+
const compactText = (value: string | null, limit: number): string | null =>
|
|
90
|
+
value === null
|
|
91
|
+
? null
|
|
92
|
+
: value
|
|
93
|
+
.replace(privateKey, "<redacted-private-key>")
|
|
94
|
+
.replace(sensitiveUrl, "<redacted-url>")
|
|
95
|
+
.replace(sensitiveValue, (match) => {
|
|
96
|
+
const separator = match.search(/[:=]/u);
|
|
97
|
+
if (separator >= 0) return `${match.slice(0, separator)}=<redacted>`;
|
|
98
|
+
const space = match.search(/\s/u);
|
|
99
|
+
return `${match.slice(0, space)} <redacted>`;
|
|
100
|
+
})
|
|
101
|
+
.slice(0, limit);
|
|
102
|
+
const compactBytes = (value: string): number => new TextEncoder().encode(value).byteLength;
|
|
103
|
+
const compactReference = (ref: Ref): CompactReference => {
|
|
104
|
+
if (ref.kind === "file")
|
|
105
|
+
return {
|
|
106
|
+
kind: "file",
|
|
107
|
+
path: compactText(ref.path, COMPACT_TEXT_BYTES) ?? "",
|
|
108
|
+
};
|
|
109
|
+
if (ref.kind === "record")
|
|
110
|
+
return {
|
|
111
|
+
kind: "record",
|
|
112
|
+
collection: ref.collection,
|
|
113
|
+
id: ref.id,
|
|
114
|
+
};
|
|
115
|
+
return { kind: ref.kind };
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export function compactTaskContext(view: TaskView): string {
|
|
119
|
+
const decisions: CompactDecision[] = view.task.decisions
|
|
120
|
+
.slice()
|
|
121
|
+
.sort((left, right) => (left.id < right.id ? -1 : left.id > right.id ? 1 : 0))
|
|
122
|
+
.slice(0, COMPACT_MAX_ITEMS)
|
|
123
|
+
.map((entry) => ({
|
|
124
|
+
id: entry.id,
|
|
125
|
+
purpose: entry.data.purpose,
|
|
126
|
+
status: entry.data.response,
|
|
127
|
+
digest: entry.data.digest,
|
|
128
|
+
references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference),
|
|
129
|
+
}));
|
|
130
|
+
const gaps = Array.from(
|
|
131
|
+
new Set([
|
|
132
|
+
...view.evidence
|
|
133
|
+
.filter((entry) => entry.status === "stale")
|
|
134
|
+
.map((entry) => `stale evidence: ${entry.evidenceId}`),
|
|
135
|
+
...view.requirements
|
|
136
|
+
.filter((entry) => entry.status !== "satisfied")
|
|
137
|
+
.map((entry) => entry.reason),
|
|
138
|
+
...view.task.progress.blockers.map((entry) => entry.reason),
|
|
139
|
+
]),
|
|
140
|
+
)
|
|
141
|
+
.sort()
|
|
142
|
+
.slice(0, COMPACT_MAX_ITEMS)
|
|
143
|
+
.map((gap) => compactText(gap, COMPACT_TEXT_BYTES)!);
|
|
144
|
+
const context: CompactTaskContext = {
|
|
145
|
+
objective: compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "",
|
|
146
|
+
status: view.task.status,
|
|
147
|
+
decisions,
|
|
148
|
+
gaps,
|
|
149
|
+
nextAction: compactText(view.task.progress.nextAction, COMPACT_TEXT_BYTES),
|
|
150
|
+
};
|
|
151
|
+
let encoded = JSON.stringify(context);
|
|
152
|
+
while (
|
|
153
|
+
compactBytes(encoded) > COMPACT_MAX_BYTES &&
|
|
154
|
+
(context.decisions.length || context.gaps.length)
|
|
155
|
+
) {
|
|
156
|
+
if (context.gaps.length) context.gaps.pop();
|
|
157
|
+
else context.decisions.pop();
|
|
158
|
+
encoded = JSON.stringify(context);
|
|
159
|
+
}
|
|
160
|
+
if (compactBytes(encoded) > COMPACT_MAX_BYTES) {
|
|
161
|
+
context.objective = compactText(context.objective, 64) ?? "";
|
|
162
|
+
context.nextAction = compactText(context.nextAction, 64);
|
|
163
|
+
encoded = JSON.stringify(context);
|
|
164
|
+
}
|
|
165
|
+
return encoded;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export const exportDigest = (bundle: {
|
|
169
|
+
schemaVersion: 1;
|
|
170
|
+
exportedAt: Utc;
|
|
171
|
+
sourceWorkspaceId: string;
|
|
172
|
+
task: unknown;
|
|
173
|
+
}): string =>
|
|
174
|
+
sha256({
|
|
175
|
+
schemaVersion: bundle.schemaVersion,
|
|
176
|
+
exportedAt: bundle.exportedAt,
|
|
177
|
+
sourceWorkspaceId: bundle.sourceWorkspaceId,
|
|
178
|
+
task: bundle.task,
|
|
179
|
+
});
|