@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.
- package/LICENSE +21 -0
- package/README.md +22 -43
- package/package.json +56 -6
- package/schemas/config.json +74 -0
- package/schemas/vcs.json +79 -0
- package/schemas/workspaces.json +1029 -0
- package/schemas/youtrack.json +84 -0
- package/scripts/analyze-release-scope.ts +215 -0
- package/scripts/bundled-deps.json +131 -0
- package/scripts/fixtures/sample-plan.md +15 -0
- package/scripts/fixtures/sample-sdd/progress.md +2 -0
- package/scripts/install-codex-plugin.sh +28 -0
- package/scripts/install-cursor-plugin.sh +146 -0
- package/scripts/install-opencode-plugin.sh +79 -0
- package/scripts/install-pi-package.sh +56 -0
- package/scripts/lib/config-dir.sh +28 -0
- package/scripts/publish-changed-packages.ts +395 -0
- package/scripts/rewrite-workspace-deps.ts +53 -0
- package/scripts/run-cursor-mcp.sh +6 -0
- package/scripts/sync-release-manifests.ts +82 -0
- package/scripts/sync-runtime.sh +142 -0
- package/scripts/validate-cursor-marketplace.ts +171 -0
- package/scripts/verify-bundle-sources.ts +134 -0
- package/scripts/write-schemas.ts +16 -0
- package/skills/workit-architecture/SKILL.md +72 -0
- package/skills/workit-architecture/agents/openai.yaml +3 -0
- package/skills/workit-architecture/references/instruction-files.md +52 -0
- package/skills/workit-architecture/references/vocabulary.md +53 -0
- package/skills/workit-bdd/SKILL.md +53 -0
- package/skills/workit-continue/SKILL.md +54 -0
- package/skills/workit-debug/SKILL.md +45 -0
- package/skills/workit-deslop/SKILL.md +58 -0
- package/skills/workit-fanout/SKILL.md +71 -0
- package/skills/workit-fanout/references/brief.md +141 -0
- package/skills/workit-fanout/references/orchestration.md +169 -0
- package/skills/workit-implement/SKILL.md +71 -0
- package/skills/workit-retro/SKILL.md +72 -0
- package/skills/workit-retro/agents/openai.yaml +3 -0
- package/skills/workit-retro/references/sources.md +35 -0
- package/skills/workit-retro/references/steering.md +39 -0
- package/skills/workit-review/SKILL.md +66 -0
- package/skills/workit-review/references/impact.md +24 -0
- package/skills/workit-shape/SKILL.md +71 -0
- package/skills/workit-shape/references/diagrams.md +17 -0
- package/skills/workit-shape/references/knowledge.md +61 -0
- package/skills/workit-shape/references/mockups.md +15 -0
- package/skills/workit-shape/references/slicing.md +44 -0
- package/skills/workit-ship/SKILL.md +71 -0
- package/skills/workit-ship/references/changelog.md +44 -0
- package/skills/workit-ship/references/pr-body.md +32 -0
- package/skills/workit-ship/references/release.md +39 -0
- package/skills/workit-test-audit/SKILL.md +54 -0
- package/skills/workit-verify-app/SKILL.md +68 -0
- package/skills/workit-verify-app/references/template.md +51 -0
- package/src/autonomy.ts +910 -0
- package/src/check-config.ts +613 -0
- package/src/checks.ts +534 -0
- package/src/ci-waiter.ts +159 -0
- package/src/core/boundary.ts +55 -0
- package/src/core/branch-policy.ts +112 -0
- package/src/core/branch.ts +1192 -0
- package/src/core/changelog-gate.ts +349 -0
- package/src/core/changelog-merge.ts +214 -0
- package/src/core/changelog.ts +392 -0
- package/src/core/cleanup.ts +638 -0
- package/src/core/cli-locate.ts +173 -0
- package/src/core/commit-flavors.ts +65 -0
- package/src/core/config-schemas.ts +653 -0
- package/src/core/config.ts +351 -0
- package/src/core/context-read.ts +144 -0
- package/src/core/docs-gate.ts +69 -0
- package/src/core/finding-evidence.ts +89 -0
- package/src/core/finding-queue.ts +36 -0
- package/src/core/git.ts +73 -0
- package/src/core/gitignore.ts +52 -0
- package/src/core/heavy-eta.ts +170 -0
- package/src/core/heavy-lease.ts +290 -0
- package/src/core/heavy-rules.ts +373 -0
- package/src/core/heavy-scope.ts +131 -0
- package/src/core/heavy-words.ts +83 -0
- package/src/core/heavy.ts +535 -0
- package/src/core/hygiene.ts +149 -0
- package/src/core/init.ts +218 -0
- package/src/core/live-session.ts +333 -0
- package/src/core/logger.ts +310 -0
- package/src/core/methods.ts +185 -0
- package/src/core/model-fallback.ts +208 -0
- package/src/core/node-resolve.mjs +249 -0
- package/src/core/operation-input.ts +632 -0
- package/src/core/package-root.ts +28 -0
- package/src/core/policy/attributes.ts +97 -0
- package/src/core/policy/bookkeeping.ts +119 -0
- package/src/core/policy/carry.ts +122 -0
- package/src/core/policy/check-evidence.ts +115 -0
- package/src/core/policy/current-judgment.ts +20 -0
- package/src/core/policy/derive.ts +387 -0
- package/src/core/policy/judgment.ts +246 -0
- package/src/core/policy/lane.ts +562 -0
- package/src/core/policy/pr-lane.ts +32 -0
- package/src/core/policy/substance.ts +365 -0
- package/src/core/policy-resolver.ts +94 -0
- package/src/core/pr-create.ts +448 -0
- package/src/core/pr-template.ts +162 -0
- package/src/core/release-notes.ts +197 -0
- package/src/core/release-tracks.ts +779 -0
- package/src/core/remotes.ts +436 -0
- package/src/core/repo-context.ts +542 -0
- package/src/core/repo-paths.ts +29 -0
- package/src/core/resources.ts +1299 -0
- package/src/core/role-models.ts +603 -0
- package/src/core/route-intent.ts +305 -0
- package/src/core/rules.ts +65 -0
- package/src/core/runtime-identity.ts +63 -0
- package/src/core/safe-write.ts +87 -0
- package/src/core/scope-glob.ts +56 -0
- package/src/core/scripts.ts +6 -0
- package/src/core/skill-manifests.ts +173 -0
- package/src/core/store-lock.ts +374 -0
- package/src/core/support-matrix.ts +24 -0
- package/src/core/task-context.ts +247 -0
- package/src/core/task-contract.ts +1279 -0
- package/src/core/task-delivery.ts +109 -0
- package/src/core/task-engine.ts +2248 -0
- package/src/core/task-evaluation.ts +1147 -0
- package/src/core/task-store.ts +2649 -0
- package/src/core/task-view.ts +180 -0
- package/src/core/templates.ts +22 -0
- package/src/core/tracker-issues.ts +174 -0
- package/src/core/vcs-config.ts +540 -0
- package/src/core/workers.ts +62 -0
- package/src/core/workit-paths.mjs +242 -0
- package/src/core/workspace-branch-policy.ts +123 -0
- package/src/core/workspaces.ts +1745 -0
- package/src/core/youtrack-backend.ts +288 -0
- package/src/core/youtrack-tools.ts +260 -0
- package/src/core/youtrack-writes.ts +73 -0
- package/src/core/youtrack-ytrack.ts +217 -0
- package/src/core/youtrack.ts +454 -0
- package/src/core/ytrack-skill.ts +297 -0
- package/src/core.ts +171 -0
- package/src/deslop/lex.ts +271 -0
- package/src/deslop/scan.ts +232 -0
- package/src/duration.ts +27 -0
- package/src/fanout-brief.ts +486 -0
- package/src/fanout-check.ts +455 -0
- package/src/fanout-landed.ts +370 -0
- package/src/fanout-stack.ts +108 -0
- package/src/fanout-status.ts +362 -0
- package/src/fanout-worktree.ts +893 -0
- package/src/fanout.ts +1473 -0
- package/src/forge/exec.ts +385 -0
- package/src/forge/github.ts +831 -0
- package/src/forge/gitlab.ts +906 -0
- package/src/forge/issue-forge.ts +209 -0
- package/src/forge/issue-link.ts +468 -0
- package/src/forge/issue-ops.ts +314 -0
- package/src/forge/merge-gate-base-red.ts +72 -0
- package/src/forge/pr-body.ts +100 -0
- package/src/forge/pr-edit.ts +292 -0
- package/src/forge/pr-ops.ts +1109 -0
- package/src/forge/pr-status.ts +10 -0
- package/src/forge/redact.ts +195 -0
- package/src/forge/report.ts +994 -0
- package/src/forge/reruns.ts +132 -0
- package/src/forge/resolve.ts +509 -0
- package/src/forge/types.ts +242 -0
- package/src/forge/verify.ts +398 -0
- package/src/git/default-base.ts +130 -0
- package/src/git/env-redirect.ts +28 -0
- package/src/git/facts.ts +122 -0
- package/src/git/ops.ts +1823 -0
- package/src/git/refs-read.ts +332 -0
- package/src/git/release.ts +1020 -0
- package/src/git/rev.ts +1175 -0
- package/src/git/session-hook.ts +597 -0
- package/src/git/stage.ts +63 -0
- package/src/hooks/agent-model.ts +149 -0
- package/src/hooks/budget-signal.ts +284 -0
- package/src/hooks/context.ts +461 -0
- package/src/hooks/cwd-normalize.ts +79 -0
- package/src/hooks/deadline.ts +228 -0
- package/src/hooks/delivery-verbs.ts +182 -0
- package/src/hooks/descriptor.ts +124 -0
- package/src/hooks/handle.ts +433 -0
- package/src/hooks/heavy-gate.ts +99 -0
- package/src/hooks/hook-runs.ts +130 -0
- package/src/hooks/hosts/claude-code.ts +380 -0
- package/src/hooks/hosts/codex.ts +372 -0
- package/src/hooks/hosts/cursor.ts +419 -0
- package/src/hooks/hosts/fields.ts +98 -0
- package/src/hooks/hosts/opencode.ts +79 -0
- package/src/hooks/hosts/pi.ts +78 -0
- package/src/hooks/index.ts +60 -0
- package/src/hooks/model-limit.ts +378 -0
- package/src/hooks/model-switch.ts +35 -0
- package/src/hooks/policy.ts +29 -0
- package/src/hooks/process.ts +48 -0
- package/src/hooks/protocol.ts +135 -0
- package/src/hooks/raw-git.ts +2289 -0
- package/src/hooks/repeat-failure.ts +115 -0
- package/src/hooks/run.ts +54 -0
- package/src/hooks/session-marker.ts +151 -0
- package/src/hooks/shell-words.ts +532 -0
- package/src/hooks/skill-nudge.ts +295 -0
- package/src/hooks/spawn-gate.ts +72 -0
- package/src/hooks/standing-signal.ts +99 -0
- package/src/hooks/standing.ts +225 -0
- package/src/hooks/steering.ts +74 -0
- package/src/hooks/stop.ts +408 -0
- package/src/hooks/worktree-confine.ts +415 -0
- package/src/hooks/write-gate.ts +1174 -0
- package/src/host-session.ts +459 -0
- package/src/knowledge.ts +368 -0
- package/src/ledger.ts +2913 -0
- package/src/stack.ts +2570 -0
- package/src/store/event-log.ts +217 -0
- package/src/store/notes.ts +29 -0
- package/src/store/patch.ts +135 -0
- package/src/store/paths.ts +368 -0
- package/src/store/reduce.ts +203 -0
- package/src/test-audit-mocks.ts +309 -0
- package/src/verdict-cover.ts +151 -0
- package/src/worktree-deps.ts +142 -0
- package/templates/headers.md +3 -0
- package/templates/hygiene/.editorconfig +8 -0
- package/templates/hygiene/.gitattributes +3 -0
- package/templates/hygiene/CHANGELOG.md +14 -0
- package/templates/hygiene/CONTRIBUTING.md +3 -0
- package/templates/hygiene/LICENSE +21 -0
- package/templates/hygiene/README.md +3 -0
- package/templates/issue-update.md +5 -0
- package/templates/plan-template.md +26 -0
- package/templates/spec-template.md +64 -0
- 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,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
|