@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/youtrack.ts
CHANGED
|
@@ -2,7 +2,8 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { readTemplate } from "./templates";
|
|
4
4
|
import { resolveWorkspaceRoot } from "./scripts";
|
|
5
|
-
import { configDir, isConfigObject } from "./config";
|
|
5
|
+
import { configDir, isConfigObject, resolveConfigDir } from "./config";
|
|
6
|
+
import { resolveInside } from "../core";
|
|
6
7
|
|
|
7
8
|
const ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
8
9
|
const TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
@@ -12,6 +13,11 @@ const TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
|
12
13
|
export const youTrackConfigPath = (): string =>
|
|
13
14
|
process.env.WORKFLOW_YOUTRACK_CONFIG ?? path.join(configDir(), "youtrack.json");
|
|
14
15
|
|
|
16
|
+
/** Config path for read-only context. Unlike youTrackConfigPath(), this never
|
|
17
|
+
* calls configDir() and therefore cannot trigger legacy-config migration. */
|
|
18
|
+
export const youTrackReadOnlyConfigPath = (): string =>
|
|
19
|
+
process.env.WORKFLOW_YOUTRACK_CONFIG ?? path.join(resolveConfigDir(), "youtrack.json");
|
|
20
|
+
|
|
15
21
|
const youTrackTokenModeOk = (p: string): boolean => {
|
|
16
22
|
if (process.platform === "win32") return true;
|
|
17
23
|
const mode = fs.statSync(p).mode & 0o777;
|
|
@@ -42,6 +48,19 @@ function readYouTrackConfig(
|
|
|
42
48
|
return { config: parsed as Record<string, any>, path: cfgPath };
|
|
43
49
|
}
|
|
44
50
|
|
|
51
|
+
const readYouTrackContextConfig = (): { data: Record<string, any> } | { error: string } => {
|
|
52
|
+
const cfgPath = youTrackReadOnlyConfigPath();
|
|
53
|
+
if (!fs.existsSync(cfgPath)) return { data: {} };
|
|
54
|
+
let parsed: unknown;
|
|
55
|
+
try {
|
|
56
|
+
parsed = JSON.parse(fs.readFileSync(cfgPath, "utf8"));
|
|
57
|
+
} catch {
|
|
58
|
+
return { error: "YouTrack configuration is unavailable" };
|
|
59
|
+
}
|
|
60
|
+
if (!isConfigObject(parsed)) return { error: "YouTrack configuration is unavailable" };
|
|
61
|
+
return { data: readOnlyYouTrackConfig(parsed as Record<string, any>) };
|
|
62
|
+
};
|
|
63
|
+
|
|
45
64
|
// RL-01: typed load result — malformed carries {ok:false, error, configPath}
|
|
46
65
|
// mirroring vcsConfig, so risky consumers stop on the exact path.
|
|
47
66
|
export type YouTrackConfigResult =
|
|
@@ -211,7 +230,7 @@ export function youTrackWorkDateMs(
|
|
|
211
230
|
}
|
|
212
231
|
}
|
|
213
232
|
|
|
214
|
-
const youTrackToken = (): { token: string; base: string } | { error: string } => {
|
|
233
|
+
export const youTrackToken = (): { token: string; base: string } | { error: string } => {
|
|
215
234
|
const loaded = readYouTrackConfig(true);
|
|
216
235
|
if ("error" in loaded) return loaded;
|
|
217
236
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
@@ -237,7 +256,7 @@ const youTrackToken = (): { token: string; base: string } | { error: string } =>
|
|
|
237
256
|
|
|
238
257
|
// fetch replaces the previous curl -fsS request helper: check res.ok,
|
|
239
258
|
// surface HTTP errors without a token-bearing body, parse JSON on success.
|
|
240
|
-
async function youTrackRequest(
|
|
259
|
+
export async function youTrackRequest(
|
|
241
260
|
url: string,
|
|
242
261
|
init: { method: string; token: string; body?: unknown },
|
|
243
262
|
): Promise<{ status: number; stdout: string; stderr: string }> {
|
|
@@ -462,6 +481,13 @@ const defaultScripts: YouTrackScripts = {
|
|
|
462
481
|
api: (args) => youTrackApi(args, process.env.WORKFLOW_YT_WRITE ?? ""),
|
|
463
482
|
};
|
|
464
483
|
|
|
484
|
+
const contextScripts: YouTrackScripts = {
|
|
485
|
+
config: readYouTrackContextConfig,
|
|
486
|
+
greeting: () => youTrackGreeting(youTrackReadOnlyConfigPath()),
|
|
487
|
+
parseDuration: (text) => youTrackParseDuration(text),
|
|
488
|
+
api: () => ({ error: "YouTrack context is read-only" }),
|
|
489
|
+
};
|
|
490
|
+
|
|
465
491
|
export function verifyYouTrackToken(
|
|
466
492
|
scripts: YouTrackScripts = defaultScripts,
|
|
467
493
|
): Record<string, any> {
|
|
@@ -475,7 +501,12 @@ function resolveYouTrackFromPaths(
|
|
|
475
501
|
): string | null {
|
|
476
502
|
const root = resolveWorkspaceRoot(workspace_root);
|
|
477
503
|
for (const rel of [spec_path, plan_path].filter(Boolean) as string[]) {
|
|
478
|
-
|
|
504
|
+
let full: string;
|
|
505
|
+
try {
|
|
506
|
+
full = resolveInside(root, rel);
|
|
507
|
+
} catch {
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
479
510
|
if (!fs.existsSync(full)) continue;
|
|
480
511
|
const text = fs.readFileSync(full, "utf8");
|
|
481
512
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
@@ -507,6 +538,50 @@ function meetingOptionsFromConfig(cfg: any): Record<string, any>[] {
|
|
|
507
538
|
];
|
|
508
539
|
}
|
|
509
540
|
|
|
541
|
+
const readOnlyYouTrackConfig = (cfg: Record<string, any>): Record<string, any> => {
|
|
542
|
+
const publicUrl = (value: unknown): string | undefined => {
|
|
543
|
+
if (typeof value !== "string") return undefined;
|
|
544
|
+
try {
|
|
545
|
+
const url = new URL(value);
|
|
546
|
+
if (url.username || url.password)
|
|
547
|
+
return `${url.protocol}//${url.host}${url.pathname}${url.search}${url.hash}`;
|
|
548
|
+
return value;
|
|
549
|
+
} catch {
|
|
550
|
+
return undefined;
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
const safe: Record<string, any> = {};
|
|
554
|
+
const baseUrl = publicUrl(cfg.baseUrl);
|
|
555
|
+
if (baseUrl) safe.baseUrl = baseUrl;
|
|
556
|
+
for (const key of ["timezone", "greetingCutoff", "defaultMention", "meetingIssue"])
|
|
557
|
+
if (typeof cfg[key] === "string") safe[key] = cfg[key];
|
|
558
|
+
if (cfg.greetings && typeof cfg.greetings === "object" && !Array.isArray(cfg.greetings)) {
|
|
559
|
+
const greetings: Record<string, string> = {};
|
|
560
|
+
for (const key of ["morning", "afternoon"])
|
|
561
|
+
if (typeof cfg.greetings[key] === "string") greetings[key] = cfg.greetings[key];
|
|
562
|
+
if (Object.keys(greetings).length) safe.greetings = greetings;
|
|
563
|
+
}
|
|
564
|
+
if (
|
|
565
|
+
cfg.meetingIssues &&
|
|
566
|
+
typeof cfg.meetingIssues === "object" &&
|
|
567
|
+
!Array.isArray(cfg.meetingIssues)
|
|
568
|
+
) {
|
|
569
|
+
safe.meetingIssues = Object.fromEntries(
|
|
570
|
+
Object.entries(cfg.meetingIssues).flatMap(([key, value]) => {
|
|
571
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return [];
|
|
572
|
+
const item = value as Record<string, any>;
|
|
573
|
+
const safeItem: Record<string, string> = {};
|
|
574
|
+
for (const field of ["issue", "label", "workItemText"])
|
|
575
|
+
if (typeof item[field] === "string") safeItem[field] = item[field];
|
|
576
|
+
const url = publicUrl(item.url);
|
|
577
|
+
if (url) safeItem.url = url;
|
|
578
|
+
return [[key, safeItem]] as const;
|
|
579
|
+
}),
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
return safe;
|
|
583
|
+
};
|
|
584
|
+
|
|
510
585
|
export function context(
|
|
511
586
|
{
|
|
512
587
|
spec_path,
|
|
@@ -525,7 +600,7 @@ export function context(
|
|
|
525
600
|
mode?: string;
|
|
526
601
|
workspace_root: string;
|
|
527
602
|
},
|
|
528
|
-
scripts: YouTrackScripts =
|
|
603
|
+
scripts: YouTrackScripts = contextScripts,
|
|
529
604
|
): Record<string, any> {
|
|
530
605
|
const cfg = scripts.config();
|
|
531
606
|
if (cfg.error) return { error: cfg.error };
|
|
@@ -535,11 +610,12 @@ export function context(
|
|
|
535
610
|
return { error: (greeting.stderr || greeting.stdout || "greeting failed").trim() };
|
|
536
611
|
}
|
|
537
612
|
|
|
538
|
-
const
|
|
613
|
+
const safeConfig = readOnlyYouTrackConfig(cfg.data);
|
|
614
|
+
const meetingOptions = meetingOptionsFromConfig(safeConfig);
|
|
539
615
|
|
|
540
616
|
if (mode === "meetings" && !issue_id && !issue_url && !issue_ref) {
|
|
541
617
|
return {
|
|
542
|
-
config:
|
|
618
|
+
config: safeConfig,
|
|
543
619
|
greeting: greeting.stdout.trim(),
|
|
544
620
|
mode: "meetings",
|
|
545
621
|
requiresMeetingChoice: true,
|
|
@@ -554,7 +630,7 @@ export function context(
|
|
|
554
630
|
if ("error" in parsed) return { error: parsed.error };
|
|
555
631
|
issue = parsed.issueId;
|
|
556
632
|
}
|
|
557
|
-
if (!issue && mode === "meetings") issue = meetingOptions[0]?.issue ??
|
|
633
|
+
if (!issue && mode === "meetings") issue = meetingOptions[0]?.issue ?? safeConfig.meetingIssue;
|
|
558
634
|
if (!issue) issue = resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) ?? undefined;
|
|
559
635
|
if (!issue || !ISSUE_RE.test(issue)) {
|
|
560
636
|
return {
|
|
@@ -564,13 +640,13 @@ export function context(
|
|
|
564
640
|
};
|
|
565
641
|
}
|
|
566
642
|
|
|
567
|
-
const base = (
|
|
643
|
+
const base = (safeConfig.baseUrl || "").replace(/\/$/, "");
|
|
568
644
|
const issueUrl = base ? `${base}/issue/${issue}` : null;
|
|
569
645
|
|
|
570
646
|
const selectedMeeting = meetingOptions.find((m) => m.issue === issue);
|
|
571
647
|
|
|
572
648
|
return {
|
|
573
|
-
config:
|
|
649
|
+
config: safeConfig,
|
|
574
650
|
greeting: greeting.stdout.trim(),
|
|
575
651
|
issueId: issue,
|
|
576
652
|
issueUrl,
|
|
@@ -580,6 +656,65 @@ export function context(
|
|
|
580
656
|
};
|
|
581
657
|
}
|
|
582
658
|
|
|
659
|
+
/** Fetch a YouTrack issue body for context.read (titles alone mislead).
|
|
660
|
+
* Creds failure degrades (offline keeps the link shape); request failure
|
|
661
|
+
* with creds fails closed so sessions never mistake titles for bodies. */
|
|
662
|
+
export const fetchYouTrackIssueBody = async (
|
|
663
|
+
issue: string,
|
|
664
|
+
creds: () => { token: string; base: string } | { error: string } = youTrackToken,
|
|
665
|
+
request: typeof youTrackRequest = youTrackRequest,
|
|
666
|
+
): Promise<
|
|
667
|
+
| {
|
|
668
|
+
data: {
|
|
669
|
+
idReadable: string;
|
|
670
|
+
summary: string;
|
|
671
|
+
description: string | null;
|
|
672
|
+
state: string | null;
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
| { error: string; kind: "creds" | "request" }
|
|
676
|
+
> => {
|
|
677
|
+
const credentials = creds();
|
|
678
|
+
if ("error" in credentials) return { error: credentials.error, kind: "creds" };
|
|
679
|
+
const { token, base } = credentials;
|
|
680
|
+
const out = await request(
|
|
681
|
+
`${base}/api/issues/${encodeURIComponent(issue)}?fields=idReadable,summary,description,customFields(name,value(name))`,
|
|
682
|
+
{ method: "GET", token },
|
|
683
|
+
);
|
|
684
|
+
if (out.status !== 0)
|
|
685
|
+
return { error: out.stderr || "YouTrack issue fetch failed", kind: "request" };
|
|
686
|
+
try {
|
|
687
|
+
const parsed = JSON.parse(out.stdout) as Record<string, unknown>;
|
|
688
|
+
const customFields = Array.isArray(parsed.customFields) ? parsed.customFields : [];
|
|
689
|
+
const stateField = customFields.find(
|
|
690
|
+
(field): field is { name: unknown; value: unknown } =>
|
|
691
|
+
typeof field === "object" &&
|
|
692
|
+
field !== null &&
|
|
693
|
+
(field as { name: unknown }).name === "State",
|
|
694
|
+
);
|
|
695
|
+
const stateValue = stateField?.value;
|
|
696
|
+
const stateName =
|
|
697
|
+
typeof stateValue === "object" && stateValue !== null
|
|
698
|
+
? (stateValue as { name?: unknown }).name
|
|
699
|
+
: undefined;
|
|
700
|
+
return {
|
|
701
|
+
data: {
|
|
702
|
+
idReadable: String(parsed.idReadable ?? issue),
|
|
703
|
+
summary: String(parsed.summary ?? ""),
|
|
704
|
+
description: typeof parsed.description === "string" ? parsed.description : null,
|
|
705
|
+
state:
|
|
706
|
+
typeof stateValue === "string"
|
|
707
|
+
? stateValue
|
|
708
|
+
: typeof stateName === "string" && stateName
|
|
709
|
+
? stateName
|
|
710
|
+
: null,
|
|
711
|
+
},
|
|
712
|
+
};
|
|
713
|
+
} catch {
|
|
714
|
+
return { error: "invalid JSON from YouTrack API", kind: "request" };
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
|
|
583
718
|
export function parseDuration(
|
|
584
719
|
text: string,
|
|
585
720
|
_workspace_root: string,
|
package/src/core.ts
CHANGED
|
@@ -2,6 +2,132 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync, realpathSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
|
+
export {
|
|
6
|
+
SCHEMA_VERSION,
|
|
7
|
+
POLICY_VERSION,
|
|
8
|
+
OPERATION_FAMILIES,
|
|
9
|
+
operationSchemas,
|
|
10
|
+
operationJsonSchema,
|
|
11
|
+
boundedOperationJsonSchema,
|
|
12
|
+
OPERATION_SCHEMA_DEPTH,
|
|
13
|
+
OPERATION_SCHEMA_MAX_DEPTH,
|
|
14
|
+
canonicalFieldsDescription,
|
|
15
|
+
parseOperation,
|
|
16
|
+
success,
|
|
17
|
+
failure,
|
|
18
|
+
canonicalJson,
|
|
19
|
+
sha256,
|
|
20
|
+
newId,
|
|
21
|
+
newRevision,
|
|
22
|
+
decisionDigest,
|
|
23
|
+
candidateDigest,
|
|
24
|
+
requirementId,
|
|
25
|
+
} from "./core/task-contract";
|
|
26
|
+
export type {
|
|
27
|
+
OperationFamily,
|
|
28
|
+
OperationRequest,
|
|
29
|
+
TaskStartRequest,
|
|
30
|
+
Caller,
|
|
31
|
+
Scope,
|
|
32
|
+
Ref,
|
|
33
|
+
Assessment,
|
|
34
|
+
Dimension,
|
|
35
|
+
Requirement,
|
|
36
|
+
Constraint,
|
|
37
|
+
Intent,
|
|
38
|
+
Progress,
|
|
39
|
+
Policy,
|
|
40
|
+
Candidate,
|
|
41
|
+
Capability,
|
|
42
|
+
Entry,
|
|
43
|
+
EvidenceEvaluation,
|
|
44
|
+
Evidence,
|
|
45
|
+
Outcome,
|
|
46
|
+
Decision,
|
|
47
|
+
Finding,
|
|
48
|
+
Assignment,
|
|
49
|
+
WorkerReport,
|
|
50
|
+
Worker,
|
|
51
|
+
TaskRecord,
|
|
52
|
+
WorkspaceRecord,
|
|
53
|
+
TaskView,
|
|
54
|
+
TaskSummary,
|
|
55
|
+
ExportBundle,
|
|
56
|
+
Result as ContractResult,
|
|
57
|
+
} from "./core/task-contract";
|
|
58
|
+
export { TaskStore } from "./core/task-store";
|
|
59
|
+
export type { MetadataLock, ProcessEvidence, RecoveryInput } from "./core/task-store";
|
|
60
|
+
export { compactTaskContext, reconcileResume } from "./core/task-context";
|
|
61
|
+
export type {
|
|
62
|
+
CompactTaskContext,
|
|
63
|
+
ResumeObservation,
|
|
64
|
+
ResumeObservationInput,
|
|
65
|
+
ResumeReconciliation,
|
|
66
|
+
} from "./core/task-context";
|
|
67
|
+
export { METHODS, invariantBootstrap, selectMethods } from "./core/methods";
|
|
68
|
+
export type { MethodId, SelectedMethod } from "./core/methods";
|
|
69
|
+
export { applicableDecision, reserveAction, settleAction, reconcileAction } from "./core/authority";
|
|
70
|
+
export {
|
|
71
|
+
createAuthorizedExternalActionRunner,
|
|
72
|
+
runAuthorizedExternalAction,
|
|
73
|
+
} from "./core/external-action";
|
|
74
|
+
export type {
|
|
75
|
+
AuthorizedActionInput,
|
|
76
|
+
ExternalActionResult,
|
|
77
|
+
ExternalActionRunner,
|
|
78
|
+
ExternalActionEffect,
|
|
79
|
+
ExternalActionBinding,
|
|
80
|
+
NativeExternalActionObservation,
|
|
81
|
+
ExternalActionRequest,
|
|
82
|
+
ExternalActionOperation,
|
|
83
|
+
} from "./core/external-action";
|
|
84
|
+
export {
|
|
85
|
+
approvedExternalAction,
|
|
86
|
+
externalActionState,
|
|
87
|
+
priorExternalAction,
|
|
88
|
+
externalActionRef,
|
|
89
|
+
externalActionDescriptor,
|
|
90
|
+
externalActionHelp,
|
|
91
|
+
externalActionRequest,
|
|
92
|
+
matchesNativeExternalAction,
|
|
93
|
+
nativeExternalActionObservation,
|
|
94
|
+
readNativeExternalActionObservation,
|
|
95
|
+
} from "./core/external-action";
|
|
96
|
+
export type {
|
|
97
|
+
ActionReservation,
|
|
98
|
+
NativeAuthorityContext,
|
|
99
|
+
NativeAuthorityVerifier,
|
|
100
|
+
NativeActionVerification,
|
|
101
|
+
NativeReconciliationVerification,
|
|
102
|
+
NativeDecisionVerification,
|
|
103
|
+
ReserveActionInput,
|
|
104
|
+
SettleActionInput,
|
|
105
|
+
ReconcileActionInput,
|
|
106
|
+
} from "./core/authority";
|
|
107
|
+
export {
|
|
108
|
+
captureCandidate,
|
|
109
|
+
evaluateEvidence,
|
|
110
|
+
evaluateRequirements,
|
|
111
|
+
evaluateClosure,
|
|
112
|
+
} from "./core/task-evaluation";
|
|
113
|
+
export type { CandidateEnvironment, ClosureEvaluation } from "./core/task-evaluation";
|
|
114
|
+
export { WorkitCore } from "./core/task-engine";
|
|
115
|
+
export type { OperationContext } from "./core/task-engine";
|
|
116
|
+
export { assertProductWriteAllowed } from "./core/workers";
|
|
117
|
+
export type {
|
|
118
|
+
CallerContext,
|
|
119
|
+
HostSession,
|
|
120
|
+
NativeWorkerDispatchVerification,
|
|
121
|
+
NativeWorkerObservation,
|
|
122
|
+
NativeWorkerVerification,
|
|
123
|
+
NativeWorkerVerifier,
|
|
124
|
+
ProductWriteInput,
|
|
125
|
+
WorkerDispatch,
|
|
126
|
+
WorkerDispatchCommit,
|
|
127
|
+
WorkerDispatchRequest,
|
|
128
|
+
WorkerDispatchStage,
|
|
129
|
+
} from "./core/workers";
|
|
130
|
+
|
|
5
131
|
export type Result<T> =
|
|
6
132
|
| { ok: true; data: T; error: null }
|
|
7
133
|
| { ok: false; data: T | null; error: string };
|
|
@@ -1,73 +1,40 @@
|
|
|
1
|
-
Load
|
|
1
|
+
Load resolved method skills through the host skill loader when policy selects them. Implement the existing plan; do not re-plan.
|
|
2
2
|
|
|
3
3
|
**Spec:** <SPEC_PATH>
|
|
4
4
|
**Plan:** <PLAN_PATH>
|
|
5
5
|
**Branch:** <BRANCH>
|
|
6
|
-
**SDD:** `<SDD_DIR>`
|
|
7
|
-
|
|
8
|
-
## Handoff destination
|
|
9
|
-
|
|
10
|
-
This session is a handoff destination for a continued plan. The originating session already recorded the post-plan menu choice; present exactly these four choices plus model deferral and never re-offer the originating handoff option:
|
|
11
|
-
|
|
12
|
-
- Subagent-driven
|
|
13
|
-
- Inline
|
|
14
|
-
- Review spec first
|
|
15
|
-
- Review plan first
|
|
16
|
-
- Change model first
|
|
17
|
-
|
|
18
|
-
<workflow-handoff-destination>true</workflow-handoff-destination>
|
|
19
6
|
|
|
20
7
|
## Hard gates
|
|
21
8
|
|
|
22
|
-
-
|
|
23
|
-
- Never use a worktree. Branch changes are in-place through `
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
- Flow-tool confirmations are never agent-typed booleans and never caller-supplied evidence objects: on OpenCode the plugin records the user's native-`question` answer as a host-observed one-use receipt (`attested: true`, `callID`, `selectedLabel`, `recordedAt`) consumed by `workit_spec_approve` / `workit_plan_approve` / `workit_plan_menu` — no evidence argument exists, and delegated worker status comes from host session parentage (`parentID`), never a caller `role` field. On Cursor, confirmations are policy-only (`attested: false`), Subagent-driven execution is supported through the one-time `coordinator_lease` and per-task `delegation_token` minted by `workit_delegate` (fail-closed validation; no `parentID` identity exists on Cursor), and Inline runs single-agent in the current session.
|
|
28
|
-
- Delegated authority is direct-child-only: a worker is the session whose host `parentID` exactly equals the activating coordinator's recorded `coordinator_session_id`; missing, mismatched, or multi-owner lineage fails closed with `delegation_lineage_denied`, and nested `opencode` launches are denied during active delegated work. An authorized child receives only the compact worker contract (execute the supplied brief, follow TDD, land one contiguous non-empty commit range, report results) — never coordinator guidance, `wk-implement`, or ledger management; coordinator bookkeeping via `workit_sdd_*` stays with the coordinator session.
|
|
29
|
-
- On Cursor, for every repository-scoped `workit_*` call, pass the active Cursor workspace as `workspace_root`; never rely on the MCP process default.
|
|
30
|
-
- Use native `task` with only the built-in `explore` and `general` agents (OpenCode); on Cursor, Subagent-driven dispatches Cursor-native subagents with a task `delegation_token`, Inline runs single-agent in the current session.
|
|
31
|
-
|
|
32
|
-
## Flow gates (HARD)
|
|
33
|
-
|
|
34
|
-
- `wk-implement` refuses to run unless the plan is `approved` (flow.json) and the post-plan menu was presented.
|
|
35
|
-
- `wk-handoff` refuses to run unless both spec and plan are `approved`.
|
|
36
|
-
- Sequence is enforced by tools: `workit_spec_approve`, `workit_plan_approve`, `workit_plan_menu` — never skip a step (the spec/plan self-review runs automatically inside the transition; only the final approval asks for your confirmation).
|
|
9
|
+
- Inspect task state before acting. On OpenCode, Cursor, Codex, and Pi use the eight shared `workit_*` families (`workit_task`, `workit_policy`, `workit_evidence`, `workit_finding`, `workit_decision`, `workit_worker`, `workit_writer`, `workit_state`). On the CLI host use `workit <family> <action>` with the same actions (hyphenated on the CLI).
|
|
10
|
+
- Never use a worktree. Branch changes are in-place through the approved `git.branch_setup` external action (CLI: `workit action git.branch_setup --payload …`).
|
|
11
|
+
- Task metadata lives under `.workit/`; never edit it directly. Record progress, evidence, findings, decisions, and worker state only through the shared operations.
|
|
12
|
+
- Helpers cannot widen scope, record binding decisions, close or pause the task, assign further helpers, or resolve blockers for the lead.
|
|
13
|
+
- On Cursor, pass the active workspace as `workspace_root` on every repository-scoped call.
|
|
37
14
|
|
|
38
15
|
## Setup
|
|
39
16
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
3. Mark IDs in `completed_task_ids` completed and never redispatch them.
|
|
44
|
-
4. Call `workit_resolve_branch`, then show the current branch, target branch, and stash behavior before any in-place checkout/setup mutation.
|
|
45
|
-
5. Always use `question`: for a clean tree ask whether to proceed or cancel; for a dirty tree add the stash choice and describe what will be stashed.
|
|
46
|
-
6. Call `workit_branch_setup` with `confirmed: true` only after approval.
|
|
17
|
+
1. If there is no active or paused task, call `workit_task` with `action: "start"` then `workit_policy` with `action: "assess"` (CLI: `workit task start …` then `workit policy assess …`).
|
|
18
|
+
2. Load `workit-plan`, list tasks with `workit_task` `action: "list"`, and mirror visible todo state to the host UI.
|
|
19
|
+
3. When policy requires a feature branch, resolve it with read-only `context.read` and apply `git.branch_setup` only after native approval.
|
|
47
20
|
|
|
48
21
|
## Remaining-task loop
|
|
49
22
|
|
|
50
|
-
For each
|
|
23
|
+
For each bounded plan task:
|
|
51
24
|
|
|
52
|
-
1. Mark
|
|
53
|
-
2.
|
|
54
|
-
3.
|
|
55
|
-
4.
|
|
56
|
-
5. Delegate spec-compliance review and code-quality review to separate `general` agents.
|
|
57
|
-
6. **Blocking** findings (Critical, Important, or spec-compliance) may trigger at most **two** fix+re-review rounds per task. **Advisory** findings (Minor, style, YAGNI, taste) never pause the loop — append them with `workit_sdd_append_advisory` (`--task <id> --text <text>`, `confirmed: true`) instead of an unrestricted file edit.
|
|
58
|
-
7. Append the validated ledger entry with `workit_sdd_append_progress` and `confirmed: true`; mark the todo completed.
|
|
25
|
+
1. Mark the item in progress in the host todo UI and record boundary progress with `workit_task` `action: "progress"`.
|
|
26
|
+
2. Route by policy: assign bounded workers with `workit_worker` `action: "assign"` when delegation is available; otherwise implement inline. Acquire product-write ownership with `workit_writer` `action: "acquire"` before repository mutations; release it when done.
|
|
27
|
+
3. Record checks and artifacts with `workit_evidence` `action: "record"`. Record review concerns with `workit_finding` `action: "record"`; resolve or defer them with `finding.resolve`. Blocking findings may trigger at most **two** fix+re-review rounds per task; advisory taste/YAGNI items still use `finding.record` and never pause the loop by direct file edit.
|
|
28
|
+
4. Never advance while a foreign writer is active or blocking findings remain open for the current candidate.
|
|
59
29
|
|
|
60
30
|
## Final gate
|
|
61
31
|
|
|
62
|
-
Run
|
|
63
|
-
|
|
64
|
-
**Mandatory:** end the run by calling `workit_plan_complete` (OpenCode/Cursor) or the CLI `workit flow complete` (CLI host) after the final task once the SDD ledger is complete (all task IDs appended) and `workit_verify` passes — a complete ledger and green verification are the tool's gates. Never finish the run while the plan is still `active`.
|
|
32
|
+
Run repository verification (CLI: `workit doctor`; hosts: approved project verify when policy requires it). **Mandatory:** close the lead task with `workit_task` `action: "close"` (CLI: `workit task close --payload … [--confirm]`) once requirements are satisfied and verification passes — never finish while the task is still `active` or `paused`.
|
|
65
33
|
|
|
66
34
|
## Task order
|
|
67
35
|
|
|
68
36
|
<TASK_LIST>
|
|
69
37
|
|
|
70
|
-
## Quality gate
|
|
38
|
+
## Quality gate
|
|
71
39
|
|
|
72
|
-
- Specs/plans
|
|
73
|
-
- After `workit_docs_validate`, surface `quality` findings (spec scan). Hard findings (missing required section, missing CA-XX) block task start unless the user explicitly waives them. Warnings are advisory.
|
|
40
|
+
- Specs/plans follow `templates/spec-template.md` / `templates/plan-template.md`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# <Feature> Implementation Plan
|
|
2
2
|
|
|
3
|
-
> **For agentic workers:**
|
|
3
|
+
> **For agentic workers:** Load `workit-implement` when policy selects implementation. Use bounded `workit_worker` delegation when the host supports it; otherwise execute inline within writer scope. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
4
|
|
|
5
5
|
**Spec:** `docs/<slug>/spec.md`
|
|
6
6
|
**Branch:** `feature/<slug>`
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
## Global Constraints
|
|
11
11
|
|
|
12
|
-
- Each task lands exactly one contiguous non-empty commit range (`base..head`): fix rounds append commits to that range and never rewrite/amend an active review range;
|
|
13
|
-
- The final task
|
|
12
|
+
- Each task lands exactly one contiguous non-empty commit range (`base..head`): fix rounds append commits to that range and never rewrite/amend an active review range; record the real base..head shas in task progress.
|
|
13
|
+
- The final task closes the lead Workit task with `workit_task` `action: "close"` (CLI: `workit task close --payload … [--confirm]`) once requirements are satisfied and repository verification passes — never finish while the task is still `active` or `paused`.
|
|
14
14
|
- <project-wide requirements, one line each>
|
|
15
15
|
|
|
16
16
|
---
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
**Branch:** `feature/<slug>`
|
|
4
4
|
|
|
5
|
+
**Change:** ADDED|MODIFIED|REMOVED — <one-sentence intent>. (Small work with
|
|
6
|
+
no durable agreement: `Spec: none (<reason>)`, no file.)
|
|
7
|
+
|
|
5
8
|
## Context
|
|
6
9
|
|
|
7
10
|
<!-- Why does this exist? What problem does it solve? 1-3 sentences. -->
|
|
@@ -16,13 +19,14 @@
|
|
|
16
19
|
|
|
17
20
|
## Architecture
|
|
18
21
|
|
|
19
|
-
<!--
|
|
22
|
+
<!-- May be `N/A (<reason>)` when the change has no flows or architecture. -->
|
|
23
|
+
<!-- Otherwise REQUIRED: render a mermaid diagram (workit-diagram skill). -->
|
|
20
24
|
```mermaid
|
|
21
25
|
flowchart TD
|
|
22
26
|
A[Start] --> B[Step]
|
|
23
27
|
```
|
|
24
28
|
|
|
25
|
-
<!-- REQUIRED if this spec touches UI: render an ASCII wireframe (
|
|
29
|
+
<!-- REQUIRED if this spec touches UI: render an ASCII wireframe (workit-mockup skill). -->
|
|
26
30
|
```text
|
|
27
31
|
┌──────────────┐
|
|
28
32
|
│ Header │
|
|
@@ -39,8 +43,16 @@ flowchart TD
|
|
|
39
43
|
## Acceptance criteria
|
|
40
44
|
|
|
41
45
|
<!-- REQUIRED: enumerable, each verifiable. Numbered CA-01, CA-02, ... -->
|
|
46
|
+
<!-- Requirements use SHALL/MUST (one per bullet, observable, no HOW); each
|
|
47
|
+
carries at least one GIVEN/WHEN/THEN, including the most-regretted edge. -->
|
|
42
48
|
- CA-01 …
|
|
43
|
-
|
|
49
|
+
|
|
50
|
+
## Review checklist
|
|
51
|
+
|
|
52
|
+
<!-- Before implementation: intent matches; nothing extra; each requirement
|
|
53
|
+
testable with an exercising scenario; most-cared case covered;
|
|
54
|
+
tasks trace to requirements; you would sign if built exactly as written. -->
|
|
55
|
+
- [ ] …
|
|
44
56
|
|
|
45
57
|
## Decisions
|
|
46
58
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Workit contract
|
|
2
|
+
|
|
3
|
+
Workit keeps one accountable lead and one shared task state. Inspect current
|
|
4
|
+
task state before acting; use only the shared operations for task, policy,
|
|
5
|
+
evidence, finding, decision, worker, writer, and state changes. Authority is
|
|
6
|
+
bounded by the requested scope, current revision, caller/session provenance, and
|
|
7
|
+
observed capabilities. Never claim host enforcement or evidence that the host
|
|
8
|
+
cannot provide. Preserve unresolved requirements, gaps, and uncertain workers.
|
|
9
|
+
|
|
10
|
+
Focused methods are loaded only when the resolved policy requires them. A method
|
|
11
|
+
must call the shared operations and must not create a second lifecycle, approval
|
|
12
|
+
chain, or task-state representation.
|
package/commands/wk-changelog.md
DELETED
package/commands/wk-commit.md
DELETED
package/commands/wk-handoff.md
DELETED
package/commands/wk-implement.md
DELETED
package/commands/wk-init.md
DELETED
package/commands/wk-meetings.md
DELETED
package/commands/wk-pr.md
DELETED
package/commands/wk-status.md
DELETED
package/commands/wk-verify.md
DELETED