@workit-dev/core 0.0.1 → 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.
Files changed (234) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +22 -43
  3. package/package.json +56 -6
  4. package/schemas/config.json +74 -0
  5. package/schemas/vcs.json +79 -0
  6. package/schemas/workspaces.json +1029 -0
  7. package/schemas/youtrack.json +84 -0
  8. package/scripts/analyze-release-scope.ts +215 -0
  9. package/scripts/bundled-deps.json +131 -0
  10. package/scripts/fixtures/sample-plan.md +15 -0
  11. package/scripts/fixtures/sample-sdd/progress.md +2 -0
  12. package/scripts/install-codex-plugin.sh +28 -0
  13. package/scripts/install-cursor-plugin.sh +146 -0
  14. package/scripts/install-opencode-plugin.sh +79 -0
  15. package/scripts/install-pi-package.sh +56 -0
  16. package/scripts/lib/config-dir.sh +28 -0
  17. package/scripts/publish-changed-packages.ts +395 -0
  18. package/scripts/rewrite-workspace-deps.ts +53 -0
  19. package/scripts/run-cursor-mcp.sh +6 -0
  20. package/scripts/sync-release-manifests.ts +82 -0
  21. package/scripts/sync-runtime.sh +142 -0
  22. package/scripts/validate-cursor-marketplace.ts +171 -0
  23. package/scripts/verify-bundle-sources.ts +134 -0
  24. package/scripts/write-schemas.ts +16 -0
  25. package/skills/workit-architecture/SKILL.md +72 -0
  26. package/skills/workit-architecture/agents/openai.yaml +3 -0
  27. package/skills/workit-architecture/references/instruction-files.md +52 -0
  28. package/skills/workit-architecture/references/vocabulary.md +53 -0
  29. package/skills/workit-bdd/SKILL.md +53 -0
  30. package/skills/workit-continue/SKILL.md +54 -0
  31. package/skills/workit-debug/SKILL.md +45 -0
  32. package/skills/workit-deslop/SKILL.md +58 -0
  33. package/skills/workit-fanout/SKILL.md +71 -0
  34. package/skills/workit-fanout/references/brief.md +141 -0
  35. package/skills/workit-fanout/references/orchestration.md +169 -0
  36. package/skills/workit-implement/SKILL.md +71 -0
  37. package/skills/workit-retro/SKILL.md +72 -0
  38. package/skills/workit-retro/agents/openai.yaml +3 -0
  39. package/skills/workit-retro/references/sources.md +35 -0
  40. package/skills/workit-retro/references/steering.md +39 -0
  41. package/skills/workit-review/SKILL.md +66 -0
  42. package/skills/workit-review/references/impact.md +24 -0
  43. package/skills/workit-shape/SKILL.md +71 -0
  44. package/skills/workit-shape/references/diagrams.md +17 -0
  45. package/skills/workit-shape/references/knowledge.md +61 -0
  46. package/skills/workit-shape/references/mockups.md +15 -0
  47. package/skills/workit-shape/references/slicing.md +44 -0
  48. package/skills/workit-ship/SKILL.md +71 -0
  49. package/skills/workit-ship/references/changelog.md +44 -0
  50. package/skills/workit-ship/references/pr-body.md +32 -0
  51. package/skills/workit-ship/references/release.md +39 -0
  52. package/skills/workit-test-audit/SKILL.md +54 -0
  53. package/skills/workit-verify-app/SKILL.md +68 -0
  54. package/skills/workit-verify-app/references/template.md +51 -0
  55. package/src/autonomy.ts +910 -0
  56. package/src/check-config.ts +613 -0
  57. package/src/checks.ts +534 -0
  58. package/src/ci-waiter.ts +159 -0
  59. package/src/core/boundary.ts +55 -0
  60. package/src/core/branch-policy.ts +112 -0
  61. package/src/core/branch.ts +1192 -0
  62. package/src/core/changelog-gate.ts +349 -0
  63. package/src/core/changelog-merge.ts +214 -0
  64. package/src/core/changelog.ts +392 -0
  65. package/src/core/cleanup.ts +638 -0
  66. package/src/core/cli-locate.ts +173 -0
  67. package/src/core/commit-flavors.ts +65 -0
  68. package/src/core/config-schemas.ts +653 -0
  69. package/src/core/config.ts +351 -0
  70. package/src/core/context-read.ts +144 -0
  71. package/src/core/docs-gate.ts +69 -0
  72. package/src/core/finding-evidence.ts +89 -0
  73. package/src/core/finding-queue.ts +36 -0
  74. package/src/core/git.ts +73 -0
  75. package/src/core/gitignore.ts +52 -0
  76. package/src/core/heavy-eta.ts +170 -0
  77. package/src/core/heavy-lease.ts +290 -0
  78. package/src/core/heavy-rules.ts +373 -0
  79. package/src/core/heavy-scope.ts +131 -0
  80. package/src/core/heavy-words.ts +83 -0
  81. package/src/core/heavy.ts +535 -0
  82. package/src/core/hygiene.ts +149 -0
  83. package/src/core/init.ts +218 -0
  84. package/src/core/live-session.ts +333 -0
  85. package/src/core/logger.ts +310 -0
  86. package/src/core/methods.ts +185 -0
  87. package/src/core/model-fallback.ts +208 -0
  88. package/src/core/node-resolve.mjs +249 -0
  89. package/src/core/operation-input.ts +632 -0
  90. package/src/core/package-root.ts +28 -0
  91. package/src/core/policy/attributes.ts +97 -0
  92. package/src/core/policy/bookkeeping.ts +119 -0
  93. package/src/core/policy/carry.ts +122 -0
  94. package/src/core/policy/check-evidence.ts +115 -0
  95. package/src/core/policy/current-judgment.ts +20 -0
  96. package/src/core/policy/derive.ts +387 -0
  97. package/src/core/policy/judgment.ts +246 -0
  98. package/src/core/policy/lane.ts +562 -0
  99. package/src/core/policy/pr-lane.ts +32 -0
  100. package/src/core/policy/substance.ts +365 -0
  101. package/src/core/policy-resolver.ts +94 -0
  102. package/src/core/pr-create.ts +448 -0
  103. package/src/core/pr-template.ts +162 -0
  104. package/src/core/release-notes.ts +197 -0
  105. package/src/core/release-tracks.ts +779 -0
  106. package/src/core/remotes.ts +436 -0
  107. package/src/core/repo-context.ts +542 -0
  108. package/src/core/repo-paths.ts +29 -0
  109. package/src/core/resources.ts +1299 -0
  110. package/src/core/role-models.ts +603 -0
  111. package/src/core/route-intent.ts +305 -0
  112. package/src/core/rules.ts +65 -0
  113. package/src/core/runtime-identity.ts +63 -0
  114. package/src/core/safe-write.ts +87 -0
  115. package/src/core/scope-glob.ts +56 -0
  116. package/src/core/scripts.ts +6 -0
  117. package/src/core/skill-manifests.ts +173 -0
  118. package/src/core/store-lock.ts +374 -0
  119. package/src/core/support-matrix.ts +24 -0
  120. package/src/core/task-context.ts +247 -0
  121. package/src/core/task-contract.ts +1279 -0
  122. package/src/core/task-delivery.ts +109 -0
  123. package/src/core/task-engine.ts +2248 -0
  124. package/src/core/task-evaluation.ts +1147 -0
  125. package/src/core/task-store.ts +2649 -0
  126. package/src/core/task-view.ts +180 -0
  127. package/src/core/templates.ts +22 -0
  128. package/src/core/tracker-issues.ts +174 -0
  129. package/src/core/vcs-config.ts +540 -0
  130. package/src/core/workers.ts +62 -0
  131. package/src/core/workit-paths.mjs +242 -0
  132. package/src/core/workspace-branch-policy.ts +123 -0
  133. package/src/core/workspaces.ts +1745 -0
  134. package/src/core/youtrack-backend.ts +288 -0
  135. package/src/core/youtrack-tools.ts +260 -0
  136. package/src/core/youtrack-writes.ts +73 -0
  137. package/src/core/youtrack-ytrack.ts +217 -0
  138. package/src/core/youtrack.ts +454 -0
  139. package/src/core/ytrack-skill.ts +297 -0
  140. package/src/core.ts +171 -0
  141. package/src/deslop/lex.ts +271 -0
  142. package/src/deslop/scan.ts +232 -0
  143. package/src/duration.ts +27 -0
  144. package/src/fanout-brief.ts +486 -0
  145. package/src/fanout-check.ts +455 -0
  146. package/src/fanout-landed.ts +370 -0
  147. package/src/fanout-stack.ts +108 -0
  148. package/src/fanout-status.ts +362 -0
  149. package/src/fanout-worktree.ts +893 -0
  150. package/src/fanout.ts +1473 -0
  151. package/src/forge/exec.ts +385 -0
  152. package/src/forge/github.ts +831 -0
  153. package/src/forge/gitlab.ts +906 -0
  154. package/src/forge/issue-forge.ts +209 -0
  155. package/src/forge/issue-link.ts +468 -0
  156. package/src/forge/issue-ops.ts +314 -0
  157. package/src/forge/merge-gate-base-red.ts +72 -0
  158. package/src/forge/pr-body.ts +100 -0
  159. package/src/forge/pr-edit.ts +292 -0
  160. package/src/forge/pr-ops.ts +1109 -0
  161. package/src/forge/pr-status.ts +10 -0
  162. package/src/forge/redact.ts +195 -0
  163. package/src/forge/report.ts +994 -0
  164. package/src/forge/reruns.ts +132 -0
  165. package/src/forge/resolve.ts +509 -0
  166. package/src/forge/types.ts +242 -0
  167. package/src/forge/verify.ts +398 -0
  168. package/src/git/default-base.ts +130 -0
  169. package/src/git/env-redirect.ts +28 -0
  170. package/src/git/facts.ts +122 -0
  171. package/src/git/ops.ts +1823 -0
  172. package/src/git/refs-read.ts +332 -0
  173. package/src/git/release.ts +1020 -0
  174. package/src/git/rev.ts +1175 -0
  175. package/src/git/session-hook.ts +597 -0
  176. package/src/git/stage.ts +63 -0
  177. package/src/hooks/agent-model.ts +149 -0
  178. package/src/hooks/budget-signal.ts +284 -0
  179. package/src/hooks/context.ts +461 -0
  180. package/src/hooks/cwd-normalize.ts +79 -0
  181. package/src/hooks/deadline.ts +228 -0
  182. package/src/hooks/delivery-verbs.ts +182 -0
  183. package/src/hooks/descriptor.ts +124 -0
  184. package/src/hooks/handle.ts +433 -0
  185. package/src/hooks/heavy-gate.ts +99 -0
  186. package/src/hooks/hook-runs.ts +130 -0
  187. package/src/hooks/hosts/claude-code.ts +380 -0
  188. package/src/hooks/hosts/codex.ts +372 -0
  189. package/src/hooks/hosts/cursor.ts +419 -0
  190. package/src/hooks/hosts/fields.ts +98 -0
  191. package/src/hooks/hosts/opencode.ts +79 -0
  192. package/src/hooks/hosts/pi.ts +78 -0
  193. package/src/hooks/index.ts +60 -0
  194. package/src/hooks/model-limit.ts +378 -0
  195. package/src/hooks/model-switch.ts +35 -0
  196. package/src/hooks/policy.ts +29 -0
  197. package/src/hooks/process.ts +48 -0
  198. package/src/hooks/protocol.ts +135 -0
  199. package/src/hooks/raw-git.ts +2289 -0
  200. package/src/hooks/repeat-failure.ts +115 -0
  201. package/src/hooks/run.ts +54 -0
  202. package/src/hooks/session-marker.ts +151 -0
  203. package/src/hooks/shell-words.ts +532 -0
  204. package/src/hooks/skill-nudge.ts +295 -0
  205. package/src/hooks/spawn-gate.ts +72 -0
  206. package/src/hooks/standing-signal.ts +99 -0
  207. package/src/hooks/standing.ts +225 -0
  208. package/src/hooks/steering.ts +74 -0
  209. package/src/hooks/stop.ts +408 -0
  210. package/src/hooks/worktree-confine.ts +415 -0
  211. package/src/hooks/write-gate.ts +1174 -0
  212. package/src/host-session.ts +459 -0
  213. package/src/knowledge.ts +368 -0
  214. package/src/ledger.ts +2913 -0
  215. package/src/stack.ts +2570 -0
  216. package/src/store/event-log.ts +217 -0
  217. package/src/store/notes.ts +29 -0
  218. package/src/store/patch.ts +135 -0
  219. package/src/store/paths.ts +368 -0
  220. package/src/store/reduce.ts +203 -0
  221. package/src/test-audit-mocks.ts +309 -0
  222. package/src/verdict-cover.ts +151 -0
  223. package/src/worktree-deps.ts +142 -0
  224. package/templates/headers.md +3 -0
  225. package/templates/hygiene/.editorconfig +8 -0
  226. package/templates/hygiene/.gitattributes +3 -0
  227. package/templates/hygiene/CHANGELOG.md +14 -0
  228. package/templates/hygiene/CONTRIBUTING.md +3 -0
  229. package/templates/hygiene/LICENSE +21 -0
  230. package/templates/hygiene/README.md +3 -0
  231. package/templates/issue-update.md +5 -0
  232. package/templates/plan-template.md +26 -0
  233. package/templates/spec-template.md +64 -0
  234. package/templates/workit-contract.md +13 -0
@@ -0,0 +1,84 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://unpkg.com/@workit-dev/core/schemas/youtrack.json",
4
+ "title": "Workit YouTrack settings (youtrack.json in the Workit config dir)",
5
+ "type": "object",
6
+ "properties": {
7
+ "$schema": {
8
+ "type": "string",
9
+ "description": "The JSON Schema this file follows; Workit writes it and keeps your value."
10
+ },
11
+ "baseUrl": {
12
+ "description": "The YouTrack URL (required to use YouTrack).",
13
+ "type": "string"
14
+ },
15
+ "tokenFile": {
16
+ "description": "Obsolete: Workit reaches YouTrack only through ytrack and ignores this key.",
17
+ "type": "string"
18
+ },
19
+ "locale": {
20
+ "description": "The language of issue updates.",
21
+ "type": "string"
22
+ },
23
+ "timezone": {
24
+ "description": "The IANA timezone that decides a work item's day; default the process timezone.",
25
+ "type": "string"
26
+ },
27
+ "meetingIssue": {
28
+ "description": "The issue meeting time is logged on.",
29
+ "type": "string"
30
+ },
31
+ "meetingIssues": {
32
+ "description": "Named meetings, each with its issue, label, work item text and URL.",
33
+ "type": "object",
34
+ "propertyNames": {
35
+ "type": "string"
36
+ },
37
+ "additionalProperties": {
38
+ "type": "object",
39
+ "properties": {
40
+ "issue": {
41
+ "type": "string"
42
+ },
43
+ "label": {
44
+ "type": "string"
45
+ },
46
+ "workItemText": {
47
+ "type": "string"
48
+ },
49
+ "url": {
50
+ "type": "string"
51
+ }
52
+ },
53
+ "additionalProperties": {}
54
+ }
55
+ },
56
+ "meetingWorkItemText": {
57
+ "description": "The work item text for meeting time (default Meetings).",
58
+ "type": "string"
59
+ },
60
+ "tokenDefaults": {
61
+ "description": "Obsolete: Workit no longer creates YouTrack tokens and ignores this key.",
62
+ "type": "object",
63
+ "properties": {
64
+ "name": {
65
+ "type": "string"
66
+ },
67
+ "description": {
68
+ "type": "string"
69
+ },
70
+ "scopes": {
71
+ "type": "array",
72
+ "items": {
73
+ "type": "string"
74
+ }
75
+ },
76
+ "profileTab": {
77
+ "type": "string"
78
+ }
79
+ },
80
+ "additionalProperties": {}
81
+ }
82
+ },
83
+ "additionalProperties": {}
84
+ }
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env bun
2
+ // AR-16: path-gated releases. A releasable commit counts only when it touches
3
+ // a PRODUCT PATH (any of the four package dirs). Tooling-only merges produce no
4
+ // release at all. A payload change typed docs/chore still publishes as patch —
5
+ // skills and metadata live in the tarball, so installed users must receive
6
+ // them; merge-backs and the release's own manifest sync never do.
7
+ import { execFileSync } from "node:child_process";
8
+ import { resolve } from "node:path";
9
+ import BUNDLED_DEPS_JSON from "./bundled-deps.json" with { type: "json" };
10
+
11
+ export const RELEASE_PACKAGES = [
12
+ "workit-core",
13
+ "workit-mcp",
14
+ "workit-cli",
15
+ "workit-opencode",
16
+ "workit-cursor",
17
+ "workit-codex",
18
+ "workit-pi",
19
+ "workit-claude-code",
20
+ ] as const;
21
+
22
+ type ReleasePackage = (typeof RELEASE_PACKAGES)[number];
23
+
24
+ /**
25
+ * Sources a package's published dist/ inlines from OUTSIDE its own directory.
26
+ * Every adapter build is a no-external `bun build`, so whatever it imports
27
+ * from another workspace package is copied into its bundle: a runtime
28
+ * dependency on that package does not reach the shipped code. A change in
29
+ * these sources must therefore republish the bundling package too. Verified
30
+ * against the build entries' metafiles by
31
+ * test/workit-core/bundled-sources.test.ts.
32
+ */
33
+ const CORE = "packages/workit-core/"; // sources, skills, templates and package.json
34
+ // Cursor and Codex inline the MCP transport and its declared dependencies.
35
+ const MCP = ["packages/workit-mcp/src/", "packages/workit-mcp/package.json"];
36
+ // The bundled CLI inlines its sources, package.json (`workit --version`) and
37
+ // its third-party dependencies.
38
+ const CLI = ["packages/workit-cli/src/", "packages/workit-cli/package.json"];
39
+ export const BUNDLED_SOURCES: Partial<Record<ReleasePackage, readonly string[]>> = {
40
+ "workit-mcp": [CORE],
41
+ "workit-cli": [CORE],
42
+ "workit-opencode": [CORE],
43
+ "workit-cursor": [CORE, ...MCP],
44
+ "workit-codex": [CORE, ...MCP],
45
+ "workit-pi": [CORE],
46
+ "workit-claude-code": [CORE, ...CLI],
47
+ };
48
+
49
+ /**
50
+ * Third-party packages each dist/ inlines, as bun.lock `packages` keys
51
+ * (`zod`, or `@opencode-ai/plugin/zod` for a nested copy). A lockfile change
52
+ * republishes a package only when one of these resolves to a different
53
+ * version, so dev-tooling bumps never release. Kept in sync with the bundle
54
+ * metafiles by test/workit-core/bundled-sources.test.ts.
55
+ */
56
+ export const BUNDLED_DEPS = BUNDLED_DEPS_JSON as Partial<Record<ReleasePackage, string[]>>;
57
+
58
+ const LOCKFILE = "bun.lock";
59
+
60
+ /** `packages` key → resolved `name@version` from a bun.lock text (JSONC). */
61
+ export const lockResolutions = (text: string): Map<string, string> => {
62
+ // bun.lock is JSON with trailing commas.
63
+ const parsed = JSON.parse(text.replace(/,(\s*[}\]])/g, "$1")) as {
64
+ packages?: Record<string, unknown>;
65
+ };
66
+ const resolved = new Map<string, string>();
67
+ for (const [key, entry] of Object.entries(parsed.packages ?? {}))
68
+ if (Array.isArray(entry) && typeof entry[0] === "string") resolved.set(key, entry[0]);
69
+ return resolved;
70
+ };
71
+
72
+ const lockAt = (root: string, rev: string): Map<string, string> | null => {
73
+ try {
74
+ return lockResolutions(
75
+ execFileSync("git", ["show", `${rev}:${LOCKFILE}`], {
76
+ cwd: root,
77
+ encoding: "utf8",
78
+ stdio: ["ignore", "pipe", "ignore"],
79
+ maxBuffer: 64 * 1024 * 1024,
80
+ }),
81
+ );
82
+ } catch {
83
+ return null;
84
+ }
85
+ };
86
+
87
+ /** Packages whose inlined third-party resolutions differ between two revisions. */
88
+ export const lockChangedPackages = (root: string, from: string, to: string): ReleasePackage[] => {
89
+ const before = lockAt(root, from);
90
+ const after = lockAt(root, to);
91
+ if (before === null && after === null) return [];
92
+ return RELEASE_PACKAGES.filter((pkg) =>
93
+ (BUNDLED_DEPS[pkg] ?? []).some((key) => before?.get(key) !== after?.get(key)),
94
+ );
95
+ };
96
+
97
+ /** Every repository path whose change alters `pkg`'s published payload. */
98
+ export const payloadPaths = (pkg: ReleasePackage): string[] => [
99
+ `packages/${pkg}/`,
100
+ ...(BUNDLED_SOURCES[pkg] ?? []),
101
+ ];
102
+
103
+ /** The release pipeline's own version-sync commit: never a release trigger. */
104
+ const RELEASE_SYNC = /^chore\(release\): sync manifests\b/;
105
+
106
+ const g = (root: string, args: string[]): string =>
107
+ execFileSync("git", args, { cwd: root, encoding: "utf8" }).trim();
108
+
109
+ /** The npm scope every release package publishes under. */
110
+ export const NPM_SCOPE = "@workit-dev";
111
+
112
+ /** The npm name of a release package directory: `workit-cli` -> `@workit-dev/cli`. */
113
+ export const npmName = (pkg: string): string => `${NPM_SCOPE}/${pkg.replace(/^workit-/, "")}`;
114
+
115
+ /**
116
+ * Release tags are `workit-dev-v<semver>` (release.config.cjs tagFormat). The
117
+ * `v<semver>` tags before them belong to the pre-1.0.0 packages (14.x and
118
+ * earlier) and never seed a @workit-dev release.
119
+ */
120
+ export const TAG_PREFIX = "workit-dev-v";
121
+ export const TAG_GLOB = `${TAG_PREFIX}*`;
122
+ export const RELEASE_TAG = /^workit-dev-v(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)$/;
123
+
124
+ /** The newest release tag by version order, or null before the first release. */
125
+ export function latestTag(root = process.cwd()): string | null {
126
+ const tag = g(root, ["tag", "--list", TAG_GLOB, "--sort=-v:refname"])
127
+ .split("\n")
128
+ .map((l) => l.trim())
129
+ .find((l) => RELEASE_TAG.test(l));
130
+ return tag ?? null;
131
+ }
132
+
133
+ type Level = "major" | "minor" | "patch";
134
+ const LEVEL_RANK: Record<Level, number> = { patch: 1, minor: 2, major: 3 };
135
+ const TYPE_LEVEL: Record<string, Level> = { fix: "patch", perf: "patch", feat: "minor" };
136
+
137
+ const subjectLevel = (commit: string): Level | null => {
138
+ const firstLine = commit.split("\n")[0] ?? "";
139
+ const m = /^([a-z][a-z0-9-]*)(?:\([^)]*\))?(!)?:/.exec(firstLine);
140
+ if (!m) return null;
141
+ if (m[2]) return "major";
142
+ const body = commit.split("\n").slice(1).join("\n");
143
+ if (/BREAKING[- ]CHANGE:/.test(body)) return "major";
144
+ return TYPE_LEVEL[m[1]] ?? null;
145
+ };
146
+
147
+ // Two-pass collection (sanctioned by the task brief): the single-pass
148
+ // `%H<NUL>%s%n%b` + `--name-only` interleave is brittle because execFileSync
149
+ // rejects NUL bytes inside arguments. Bounded by commit count; acceptable for
150
+ // this repo's cadence.
151
+ //
152
+ // diff-tree with -m unions files across a merge's parents (a plain `show`
153
+ // combined diff drops files identical to either parent — e.g. hotfix-branch
154
+ // back-merges), and -z returns raw NUL-delimited paths so spaces/non-ASCII
155
+ // are never C-quoted. NUL is fine in captured output, never in argv.
156
+ const commitsSince = (
157
+ root: string,
158
+ from: string,
159
+ ): { hash: string; message: string; files: string[] }[] => {
160
+ const hashes = g(root, ["log", "--reverse", "--format=%H", `${from}..HEAD`])
161
+ .split("\n")
162
+ .filter(Boolean);
163
+ return hashes.map((h) => ({
164
+ hash: h,
165
+ message: g(root, ["show", "-s", "--format=%B", h]),
166
+ files: g(root, ["diff-tree", "--no-commit-id", "--name-only", "-r", "-m", "--root", "-z", h])
167
+ .split("\0")
168
+ .filter(Boolean),
169
+ }));
170
+ };
171
+
172
+ export function analyzeReleaseScope(root = process.cwd()): {
173
+ level: Level | null;
174
+ productPkgs: string[];
175
+ } {
176
+ const from = latestTag(root);
177
+ if (from === null) {
178
+ return { level: "minor", productPkgs: [...RELEASE_PACKAGES] };
179
+ }
180
+ const commits = commitsSince(root, from);
181
+ const levels: Level[] = [];
182
+ const pkgs = new Set<string>();
183
+ let payloadOnly = false;
184
+ for (const { hash, message, files } of commits) {
185
+ const subject = (message.split("\n")[0] ?? "").trim();
186
+ if (RELEASE_SYNC.test(subject)) continue;
187
+ const touched = files.filter((f) =>
188
+ RELEASE_PACKAGES.some((p) => payloadPaths(p).some((prefix) => f.startsWith(prefix))),
189
+ );
190
+ // A lockfile edit counts only for packages whose inlined deps moved.
191
+ const relocked = files.includes(LOCKFILE) ? lockChangedPackages(root, `${hash}^`, hash) : [];
192
+ if (touched.length === 0 && relocked.length === 0) continue;
193
+ const lvl = subjectLevel(message);
194
+ if (lvl) levels.push(lvl);
195
+ else if (!subject.startsWith("Merge ")) payloadOnly = true;
196
+ for (const pkg of RELEASE_PACKAGES)
197
+ if (
198
+ relocked.includes(pkg) ||
199
+ touched.some((f) => payloadPaths(pkg).some((prefix) => f.startsWith(prefix)))
200
+ )
201
+ pkgs.add(pkg);
202
+ }
203
+ if (levels.length === 0) return { level: payloadOnly ? "patch" : null, productPkgs: [...pkgs] };
204
+ const level = levels.reduce<Level>(
205
+ (best, l) => (LEVEL_RANK[l] > LEVEL_RANK[best] ? l : best),
206
+ "patch",
207
+ );
208
+ return { level, productPkgs: [...pkgs] };
209
+ }
210
+
211
+ if (import.meta.main) {
212
+ const root = process.argv[2] ? resolve(process.argv[2]) : process.cwd();
213
+ const { level } = analyzeReleaseScope(root);
214
+ if (level) process.stdout.write(`${level}\n`);
215
+ }
@@ -0,0 +1,131 @@
1
+ {
2
+ "workit-mcp": [
3
+ "@modelcontextprotocol/sdk",
4
+ "@openclaw/fs-safe",
5
+ "ajv",
6
+ "ajv-formats",
7
+ "fast-deep-equal",
8
+ "fast-uri",
9
+ "json-schema-traverse",
10
+ "zod",
11
+ "zod-to-json-schema"
12
+ ],
13
+ "workit-cli": [
14
+ "@alcalzone/ansi-tokenize",
15
+ "@babel/parser",
16
+ "@inkjs/ui",
17
+ "@inkjs/ui/chalk",
18
+ "@openclaw/fs-safe",
19
+ "ansi-escapes",
20
+ "ansi-regex",
21
+ "ansi-styles",
22
+ "auto-bind",
23
+ "cli-boxes",
24
+ "cli-cursor",
25
+ "cli-truncate",
26
+ "code-excerpt",
27
+ "convert-to-spaces",
28
+ "deepmerge",
29
+ "environment",
30
+ "es-toolkit",
31
+ "escape-string-regexp",
32
+ "figures",
33
+ "get-east-asian-width",
34
+ "indent-string",
35
+ "ink",
36
+ "ink/chalk",
37
+ "is-fullwidth-code-point",
38
+ "is-in-ci",
39
+ "is-unicode-supported",
40
+ "patch-console",
41
+ "react",
42
+ "react-devtools-core",
43
+ "react-reconciler",
44
+ "restore-cursor",
45
+ "restore-cursor/onetime",
46
+ "restore-cursor/onetime/mimic-fn",
47
+ "scheduler",
48
+ "signal-exit",
49
+ "slice-ansi",
50
+ "stack-utils",
51
+ "string-width",
52
+ "strip-ansi",
53
+ "terminal-size",
54
+ "widest-line",
55
+ "wrap-ansi",
56
+ "ws",
57
+ "yoga-layout",
58
+ "zod"
59
+ ],
60
+ "workit-opencode": ["@openclaw/fs-safe", "@opencode/plugin", "zod"],
61
+ "workit-cursor": [
62
+ "@modelcontextprotocol/sdk",
63
+ "@openclaw/fs-safe",
64
+ "ajv",
65
+ "ajv-formats",
66
+ "fast-deep-equal",
67
+ "fast-uri",
68
+ "json-schema-traverse",
69
+ "zod",
70
+ "zod-to-json-schema"
71
+ ],
72
+ "workit-codex": [
73
+ "@modelcontextprotocol/sdk",
74
+ "@openclaw/fs-safe",
75
+ "ajv",
76
+ "ajv-formats",
77
+ "fast-deep-equal",
78
+ "fast-uri",
79
+ "json-schema-traverse",
80
+ "zod",
81
+ "zod-to-json-schema"
82
+ ],
83
+ "workit-pi": ["@openclaw/fs-safe", "zod"],
84
+ "workit-claude-code": [
85
+ "@alcalzone/ansi-tokenize",
86
+ "@babel/parser",
87
+ "@inkjs/ui",
88
+ "@inkjs/ui/chalk",
89
+ "@openclaw/fs-safe",
90
+ "ansi-escapes",
91
+ "ansi-regex",
92
+ "ansi-styles",
93
+ "auto-bind",
94
+ "cli-boxes",
95
+ "cli-cursor",
96
+ "cli-truncate",
97
+ "code-excerpt",
98
+ "convert-to-spaces",
99
+ "deepmerge",
100
+ "environment",
101
+ "es-toolkit",
102
+ "escape-string-regexp",
103
+ "figures",
104
+ "get-east-asian-width",
105
+ "indent-string",
106
+ "ink",
107
+ "ink/chalk",
108
+ "is-fullwidth-code-point",
109
+ "is-in-ci",
110
+ "is-unicode-supported",
111
+ "patch-console",
112
+ "react",
113
+ "react-devtools-core",
114
+ "react-reconciler",
115
+ "restore-cursor",
116
+ "restore-cursor/onetime",
117
+ "restore-cursor/onetime/mimic-fn",
118
+ "scheduler",
119
+ "signal-exit",
120
+ "slice-ansi",
121
+ "stack-utils",
122
+ "string-width",
123
+ "strip-ansi",
124
+ "terminal-size",
125
+ "widest-line",
126
+ "wrap-ansi",
127
+ "ws",
128
+ "yoga-layout",
129
+ "zod"
130
+ ]
131
+ }
@@ -0,0 +1,15 @@
1
+ # Sample Plan
2
+
3
+ ## File Map
4
+
5
+ Not a task.
6
+
7
+ ### Task 1: First task
8
+
9
+ - [ ] Step 1: Do alpha
10
+ - [ ] Step 2: Do beta
11
+ - [ ] Step 3: Do gamma
12
+
13
+ ### Task 2: Second task
14
+
15
+ - [ ] Step 1: Finish
@@ -0,0 +1,2 @@
1
+ Task 1: complete (commits abc1234..def5678, review clean)
2
+ Task 2: complete (commits def5678..111aaaa, review skipped — trivial doc)
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env bash
2
+ # Install / refresh the Codex workit plugin from the local checkout or share clone.
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
6
+ ROOT="$(CDPATH= cd -- "$SCRIPT_DIR/../../.." && pwd)"
7
+ . "$SCRIPT_DIR/lib/config-dir.sh"
8
+ SHARE="$(workit_data_dir)"
9
+ DEV="${WORKIT_DEV:-$ROOT}"
10
+ PLUGIN_DIR="${HOME}/.codex/plugins/workit"
11
+
12
+ chmod +x "$ROOT/packages/workit-core/scripts/sync-runtime.sh"
13
+ WORKIT_DEV="$ROOT" "$ROOT/packages/workit-core/scripts/sync-runtime.sh"
14
+
15
+ SOURCE="$DEV"
16
+ if [ ! -d "$SOURCE/packages/workit-codex/.codex-plugin" ] && [ -d "$SHARE/packages/workit-codex/.codex-plugin" ]; then
17
+ SOURCE="$SHARE"
18
+ fi
19
+
20
+ mkdir -p "$PLUGIN_DIR"
21
+ rsync -a --delete "$SOURCE/packages/workit-codex/" "$PLUGIN_DIR/"
22
+
23
+ if ! bun "$ROOT/packages/workit-cli/scripts/doctor-check.ts" cli; then
24
+ echo "FATAL: post-install doctor found problems after Codex plugin sync" >&2
25
+ exit 1
26
+ fi
27
+
28
+ echo "Codex plugin installed at $PLUGIN_DIR"
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env bash
2
+ # Install / refresh Cursor plugin (+ OpenCode live loader via sync-runtime).
3
+ set -euo pipefail
4
+
5
+ REPO_SLUG="${WORKIT_REPO:-BrainerVirus/workit}"
6
+ # core/workit-paths.mjs DATA_DIR_SH, verbatim (a test holds it): this script
7
+ # also runs piped (curl | bash) without scripts/lib/.
8
+ workit_data_dir() {
9
+ case "${XDG_DATA_HOME:-}" in /*) printf '%s\n' "$XDG_DATA_HOME/workit"; return ;; esac
10
+ case "$(uname -s 2>/dev/null)" in
11
+ MINGW*|MSYS*|CYGWIN*) printf '%s\n' "${LOCALAPPDATA:-$HOME/AppData/Local}/workit" ;;
12
+ *) printf '%s\n' "$HOME/.local/share/workit" ;;
13
+ esac
14
+ }
15
+ SHARE="$(workit_data_dir)"
16
+ SKILLS_DIR="${HOME}/.cursor/skills"
17
+
18
+ FROM_GITHUB=0
19
+ LOCAL_DIST=0
20
+ LOCAL_ROOT=""
21
+ if [ -n "${BASH_SOURCE[0]:-}" ] && [ -f "${BASH_SOURCE[0]}" ]; then
22
+ SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
23
+ # RR-05: LOCAL_ROOT is the checkout root (scripts live under <root>/packages/workit-core/scripts)
24
+ LOCAL_ROOT="$(CDPATH= cd -- "$SCRIPT_DIR/../../.." && pwd)"
25
+ else
26
+ FROM_GITHUB=1
27
+ fi
28
+
29
+ for arg in "$@"; do
30
+ case "$arg" in
31
+ --github|--from-github) FROM_GITHUB=1 ;;
32
+ --local-dist) LOCAL_DIST=1 ;;
33
+ esac
34
+ done
35
+
36
+ if [ "$FROM_GITHUB" -eq 0 ] && [ ! -d "${LOCAL_ROOT}/packages/workit-cursor/.cursor-plugin" ]; then
37
+ FROM_GITHUB=1
38
+ fi
39
+
40
+ if [ "$FROM_GITHUB" -eq 1 ]; then
41
+ mkdir -p "$(dirname "$SHARE")"
42
+ if [ -d "$SHARE/.git" ]; then
43
+ git -C "$SHARE" fetch --tags --force origin
44
+ git -C "$SHARE" checkout main
45
+ git -C "$SHARE" pull --ff-only origin main
46
+ else
47
+ # Clone beside it and sync, so the owned CLI install (versions/, current, bin/) survives
48
+ TMP=$(mktemp -d)
49
+ git clone --depth 1 "https://github.com/${REPO_SLUG}.git" "$TMP"
50
+ mkdir -p "$SHARE"
51
+ rsync -a --delete --exclude '/versions' --exclude '/current' --exclude '/bin' "$TMP/" "$SHARE/"
52
+ rm -rf "$TMP"
53
+ fi
54
+ ROOT="$SHARE"
55
+ else
56
+ ROOT="$LOCAL_ROOT"
57
+ fi
58
+
59
+ # CA-02: self-heal pre-check. The doctor's stale_install check runs against the
60
+ # INSTALLED plugin dir (version/selectors vs the current runtime). Exit 2 means
61
+ # stale: the refresh below syncs the plugin dir from ROOT and the registration
62
+ # pass rewrites the workit MCP/hook entries to the canonical current selector.
63
+ # The check reads only workit-owned files, so a healthy install is byte-untouched;
64
+ # a registry-unreachable comparison reports registry_unreachable, never stale
65
+ # (CA-04) — no false stale_install and no install failure.
66
+ if bun "$ROOT/packages/workit-cli/scripts/doctor-check.ts" cursor --stale; then
67
+ :
68
+ elif [ $? -eq 2 ]; then
69
+ echo "workit: stale Cursor plugin install detected — self-healing (refresh + canonical re-registration)" >&2
70
+ else
71
+ echo "FATAL: pre-install doctor probe failed" >&2
72
+ exit 1
73
+ fi
74
+
75
+ chmod +x "$ROOT/packages/workit-core/scripts/sync-runtime.sh" "$ROOT/packages/workit-core/scripts/"*.sh
76
+ # Prefer syncing from this ROOT (dev or freshly cloned share)
77
+ export WORKIT_DEV="$ROOT"
78
+ "$ROOT/packages/workit-core/scripts/sync-runtime.sh"
79
+
80
+ # Drop stale CLI skill symlinks (duplicate /wf-* and /wk-* entries)
81
+ if [ -d "$SKILLS_DIR" ]; then
82
+ rm -f "$SKILLS_DIR"/wf-* "$SKILLS_DIR"/wk-*
83
+ fi
84
+
85
+ PLUGIN_DIR="$HOME/.cursor/plugins/local/workit"
86
+ REGISTRATION_TS="$ROOT/packages/workit-cli/src/admin/registration.ts"
87
+ CURSOR_SETTINGS="$HOME/.cursor/settings.json" CURSOR_MCP="$HOME/.cursor/mcp.json" PLUGIN_DIR="$PLUGIN_DIR" REGISTRATION_TS="$REGISTRATION_TS" LOCAL_DIST="$LOCAL_DIST" bun -e '
88
+ import fs from "node:fs";
89
+ import path from "node:path";
90
+ const {
91
+ mergeCursorSettings,
92
+ mergeCursorMcp,
93
+ mergeCursorHooks,
94
+ cursorMcpServerEntry,
95
+ cursorHooksEntry,
96
+ cursorMcpLocalDistEntry,
97
+ cursorHookLocalDistEntry,
98
+ } = await import(process.env.REGISTRATION_TS!);
99
+
100
+ // RR-06: collapse current + legacy identities; never replace unrelated settings.
101
+ const settingsPath = process.env.CURSOR_SETTINGS!;
102
+ const settings = fs.existsSync(settingsPath)
103
+ ? JSON.parse(fs.readFileSync(settingsPath, "utf8"))
104
+ : {};
105
+ const merged = mergeCursorSettings(settings, process.env.PLUGIN_DIR!);
106
+ fs.writeFileSync(settingsPath, JSON.stringify(merged.config, null, 2) + "\n");
107
+
108
+ // RR-06/CA-17: one portable workit MCP registration. Default is the published
109
+ // npx pin (Marketplace-safe); --local-dist runs the installed plugin'"'"'s own
110
+ // built dist through node so a checkout install carries the current code.
111
+ const pluginDir = process.env.PLUGIN_DIR!;
112
+ const localDist = process.env.LOCAL_DIST === "1";
113
+ const server = localDist
114
+ ? cursorMcpLocalDistEntry(pluginDir)
115
+ : cursorMcpServerEntry(pluginDir);
116
+ const mcpPath = process.env.CURSOR_MCP!;
117
+ const mcp = fs.existsSync(mcpPath)
118
+ ? JSON.parse(fs.readFileSync(mcpPath, "utf8"))
119
+ : { mcpServers: {} };
120
+ const mergedMcp = mergeCursorMcp(mcp, "workit", server);
121
+ fs.writeFileSync(mcpPath, JSON.stringify(mergedMcp.config, null, 2) + "\n");
122
+
123
+ // The shipped hooks-cursor.json reaches the launcher through
124
+ // ${CURSOR_PLUGIN_ROOT}, which only a Marketplace install needs. A local
125
+ // install writes the absolute launcher path into every event so hooks do not
126
+ // depend on Cursor expanding it; --local-dist also points sessionStart at the
127
+ // installed dist.
128
+ const hooksPath = path.join(pluginDir, "hooks", "hooks-cursor.json");
129
+ const hooks = fs.existsSync(hooksPath)
130
+ ? JSON.parse(fs.readFileSync(hooksPath, "utf8"))
131
+ : { version: 1, hooks: {} };
132
+ const sessionStart = localDist ? cursorHookLocalDistEntry(pluginDir) : cursorHooksEntry(pluginDir);
133
+ const mergedHooks = mergeCursorHooks(hooks, { command: sessionStart.command }, pluginDir);
134
+ if (mergedHooks.changed.length > 0)
135
+ fs.writeFileSync(hooksPath, JSON.stringify(mergedHooks.config, null, 2) + "\n");
136
+ '
137
+
138
+ # DG-09: verify the just-written Cursor registration with the shared offline doctor.
139
+ if ! bun "$ROOT/packages/workit-cli/scripts/doctor-check.ts" cursor; then
140
+ echo "FATAL: post-install doctor found an unhealthy Cursor registration" >&2
141
+ exit 1
142
+ fi
143
+
144
+ echo "Cursor plugin installed + auto-sync enabled (sessionStart)."
145
+ echo "Share: $SHARE"
146
+ ls "$HOME/.cursor/plugins/local/workit/skills" | grep '^wk-' || true