@brainervirus/workit-cursor 0.6.1 → 0.8.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/.cursor-plugin/plugin.json +33 -5
- package/README.md +88 -6
- package/assets/logo.svg +4 -0
- package/assets/templates/execution-contract.md +58 -0
- package/assets/templates/greeting.md +1 -0
- package/assets/templates/headers.md +3 -0
- package/assets/templates/hygiene/.editorconfig +8 -0
- package/assets/templates/hygiene/.gitattributes +3 -0
- package/assets/templates/hygiene/CHANGELOG.md +14 -0
- package/assets/templates/hygiene/CONTRIBUTING.md +3 -0
- package/assets/templates/hygiene/LICENSE +21 -0
- package/assets/templates/hygiene/README.md +3 -0
- package/assets/templates/issue-update.md +6 -0
- package/assets/templates/plan-template.md +25 -0
- package/assets/templates/spec-template.md +51 -0
- package/assets/templates/superpowers-doc-contract.md +70 -0
- package/dist/cursor-session-start.js +351 -0
- package/dist/mcp-server.js +27687 -0
- package/hooks/hooks-cursor.json +1 -1
- package/mcp.json +2 -6
- package/package.json +20 -7
- package/rules/ask-question-only.mdc +1 -1
- package/rules/sdd-docs-path.mdc +2 -2
- package/skills/wk-changelog/SKILL.md +1 -1
- package/skills/wk-commit/SKILL.md +1 -1
- package/skills/wk-docs-refresh/SKILL.md +1 -1
- package/skills/wk-handoff/SKILL.md +2 -2
- package/skills/wk-implement/SKILL.md +5 -5
- package/skills/wk-issue-update/SKILL.md +2 -0
- package/skills/wk-pr/SKILL.md +2 -0
- package/skills/wk-release-notes/SKILL.md +1 -1
- package/skills/wk-verify/SKILL.md +1 -1
- package/vendor/superpowers/skills/brainstorming/SKILL.md +159 -0
- package/vendor/superpowers/skills/brainstorming/scripts/frame-template.html +213 -0
- package/vendor/superpowers/skills/brainstorming/scripts/helper.js +167 -0
- package/vendor/superpowers/skills/brainstorming/scripts/server.cjs +723 -0
- package/vendor/superpowers/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/vendor/superpowers/skills/brainstorming/visual-companion.md +222 -0
- package/vendor/superpowers/skills/dispatching-parallel-agents/SKILL.md +185 -0
- package/vendor/superpowers/skills/executing-plans/SKILL.md +70 -0
- package/vendor/superpowers/skills/finishing-a-development-branch/SKILL.md +241 -0
- package/vendor/superpowers/skills/receiving-code-review/SKILL.md +213 -0
- package/vendor/superpowers/skills/requesting-code-review/SKILL.md +103 -0
- package/vendor/superpowers/skills/requesting-code-review/code-reviewer.md +172 -0
- package/vendor/superpowers/skills/subagent-driven-development/SKILL.md +418 -0
- package/vendor/superpowers/skills/subagent-driven-development/implementer-prompt.md +139 -0
- package/vendor/superpowers/skills/subagent-driven-development/task-reviewer-prompt.md +188 -0
- package/vendor/superpowers/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/vendor/superpowers/skills/systematic-debugging/SKILL.md +296 -0
- package/vendor/superpowers/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/vendor/superpowers/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/vendor/superpowers/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/vendor/superpowers/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/vendor/superpowers/skills/systematic-debugging/test-academic.md +14 -0
- package/vendor/superpowers/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/vendor/superpowers/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/vendor/superpowers/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/vendor/superpowers/skills/test-driven-development/SKILL.md +371 -0
- package/vendor/superpowers/skills/test-driven-development/testing-anti-patterns.md +299 -0
- package/vendor/superpowers/skills/using-git-worktrees/SKILL.md +202 -0
- package/vendor/superpowers/skills/using-superpowers/SKILL.md +62 -0
- package/vendor/superpowers/skills/using-superpowers/references/antigravity-tools.md +23 -0
- package/vendor/superpowers/skills/using-superpowers/references/codex-tools.md +39 -0
- package/vendor/superpowers/skills/using-superpowers/references/pi-tools.md +16 -0
- package/vendor/superpowers/skills/verification-before-completion/SKILL.md +139 -0
- package/vendor/superpowers/skills/writing-plans/SKILL.md +174 -0
- package/vendor/superpowers/skills/writing-plans/plan-document-reviewer-prompt.md +49 -0
- package/vendor/superpowers/skills/writing-skills/SKILL.md +689 -0
- package/vendor/superpowers/skills/writing-skills/anthropic-best-practices.md +1150 -0
- package/vendor/superpowers/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/vendor/superpowers/skills/writing-skills/graphviz-conventions.dot +172 -0
- package/vendor/superpowers/skills/writing-skills/persuasion-principles.md +187 -0
- package/vendor/superpowers/skills/writing-skills/testing-skills-with-subagents.md +384 -0
- package/hooks/session-start +0 -53
- package/marketplace.json +0 -10
- package/mcp/run-server.sh +0 -56
- package/mcp/server.ts +0 -1163
package/mcp/server.ts
DELETED
|
@@ -1,1163 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import { z } from "zod";
|
|
5
|
-
import { runScript } from "@brainervirus/workit-core/src/core/scripts";
|
|
6
|
-
import {
|
|
7
|
-
parseSections,
|
|
8
|
-
parseKeyValueLines,
|
|
9
|
-
} from "@brainervirus/workit-core/src/core/parse-sections";
|
|
10
|
-
import { parseVerifyOutput } from "@brainervirus/workit-core/src/core/verify-parse";
|
|
11
|
-
import { gitContext } from "@brainervirus/workit-core/src/core/git";
|
|
12
|
-
import {
|
|
13
|
-
parsePlanTasks,
|
|
14
|
-
resolveHandoffBranch,
|
|
15
|
-
} from "@brainervirus/workit-core/src/core/plan-tasks";
|
|
16
|
-
import { buildHandoffPrompt } from "@brainervirus/workit-core/src/tools/handoff";
|
|
17
|
-
import {
|
|
18
|
-
readFlowState,
|
|
19
|
-
transitionSpec,
|
|
20
|
-
transitionPlan,
|
|
21
|
-
recordMenuChoice,
|
|
22
|
-
slugFromPath,
|
|
23
|
-
} from "@brainervirus/workit-core/src/core/flow-state";
|
|
24
|
-
import { linkDocsRepo, listSpecs, promoteSpec } from "@brainervirus/workit-core/src/core/docs-repo";
|
|
25
|
-
import { configDir, readConfig, writeConfig } from "@brainervirus/workit-core/src/core/config";
|
|
26
|
-
import { ensureProjectGitignore } from "@brainervirus/workit-core/src/core/gitignore";
|
|
27
|
-
import { ensureHygieneFiles } from "@brainervirus/workit-core/src/core/hygiene";
|
|
28
|
-
import { listTemplates, writeTemplate } from "@brainervirus/workit-core/src/core/templates";
|
|
29
|
-
import { listRules, writeRule } from "@brainervirus/workit-core/src/core/rules";
|
|
30
|
-
import { initStatus, initApply, toolkitStatus } from "@brainervirus/workit-core/src/core/init";
|
|
31
|
-
import { resolveBranch, branchSetup } from "@brainervirus/workit-core/src/core/branch";
|
|
32
|
-
import { docsValidate } from "@brainervirus/workit-core/src/core/docs-validate";
|
|
33
|
-
import { docsBranch } from "@brainervirus/workit-core/src/core/branch";
|
|
34
|
-
import {
|
|
35
|
-
sddContext,
|
|
36
|
-
sddTaskBrief,
|
|
37
|
-
sddReviewPackage,
|
|
38
|
-
sddAppendProgress,
|
|
39
|
-
} from "@brainervirus/workit-core/src/core/sdd";
|
|
40
|
-
import {
|
|
41
|
-
verifyYouTrackToken,
|
|
42
|
-
context as youtrackContext,
|
|
43
|
-
parseIssueRef as youtrackParseIssueRef,
|
|
44
|
-
parseDuration as youtrackParseDuration,
|
|
45
|
-
logTime as youtrackLogTime,
|
|
46
|
-
buildDraft as youtrackBuildDraft,
|
|
47
|
-
postUpdate as youtrackPostUpdate,
|
|
48
|
-
} from "@brainervirus/workit-core/src/core/youtrack";
|
|
49
|
-
import { asciiWireframe, flowDiagram } from "@brainervirus/workit-core/src/core/present";
|
|
50
|
-
import { withWorkspace } from "@brainervirus/workit-core/src/core/repo-tool";
|
|
51
|
-
import {
|
|
52
|
-
changelogApply,
|
|
53
|
-
changelogUnreleasedStats,
|
|
54
|
-
} from "@brainervirus/workit-core/src/core/changelog";
|
|
55
|
-
|
|
56
|
-
const workspaceRootSchema = z
|
|
57
|
-
.string()
|
|
58
|
-
.optional()
|
|
59
|
-
.describe("Git repository root. Defaults to the Cursor workspace folder (${workspaceFolder}).");
|
|
60
|
-
|
|
61
|
-
const server = new McpServer({
|
|
62
|
-
name: "workit",
|
|
63
|
-
// Runtime read, not hardcoded: semantic-release bumps versions only in CI
|
|
64
|
-
// (no commit-back), so any literal here would drift from the published tag.
|
|
65
|
-
// package.json ships in the tarball regardless of the files whitelist.
|
|
66
|
-
version: JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version,
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
function jsonResult(data) {
|
|
70
|
-
return {
|
|
71
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
72
|
-
structuredContent: data,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
server.registerTool(
|
|
77
|
-
"workflow_verify",
|
|
78
|
-
{
|
|
79
|
-
description:
|
|
80
|
-
"Run project validation scripts (verify-project.sh). Defaults to Cursor workspace; pass workspace_root when the target repo differs.",
|
|
81
|
-
inputSchema: {
|
|
82
|
-
dry_run: z.boolean().optional(),
|
|
83
|
-
workspace_root: workspaceRootSchema,
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
async ({ dry_run, workspace_root }) => {
|
|
87
|
-
const args = dry_run ? ["--dry-run"] : [];
|
|
88
|
-
const { stdout, stderr, exitCode, cwd } = runScript("verify-project.sh", args, workspace_root);
|
|
89
|
-
const parsed = parseVerifyOutput(stdout);
|
|
90
|
-
return jsonResult(
|
|
91
|
-
withWorkspace(workspace_root, {
|
|
92
|
-
...parsed,
|
|
93
|
-
exitCode,
|
|
94
|
-
stderr: stderr || undefined,
|
|
95
|
-
stdout,
|
|
96
|
-
workspace_root: cwd,
|
|
97
|
-
}),
|
|
98
|
-
);
|
|
99
|
-
},
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
server.registerTool(
|
|
103
|
-
"workflow_pr_context",
|
|
104
|
-
{
|
|
105
|
-
description:
|
|
106
|
-
"Gather PR-ready repository context. On feature/* or bugfix/*, fetches and fast-forwards develop + current branch before diffing against develop.",
|
|
107
|
-
inputSchema: {
|
|
108
|
-
range: z.string().optional(),
|
|
109
|
-
workspace_root: workspaceRootSchema,
|
|
110
|
-
},
|
|
111
|
-
},
|
|
112
|
-
async ({ range, workspace_root }) => {
|
|
113
|
-
const args = range ? [range] : [];
|
|
114
|
-
const { stdout, stderr, exitCode, cwd } = runScript(
|
|
115
|
-
"pr-ready-context.sh",
|
|
116
|
-
args,
|
|
117
|
-
workspace_root,
|
|
118
|
-
);
|
|
119
|
-
if (exitCode !== 0) {
|
|
120
|
-
const errLines = (stderr + "\n" + stdout).split("\n");
|
|
121
|
-
const error =
|
|
122
|
-
errLines.find((l) => l.startsWith("ERROR:")) ?? stderr.trim() ?? "pr-ready-context failed";
|
|
123
|
-
return jsonResult(
|
|
124
|
-
withWorkspace(workspace_root, {
|
|
125
|
-
error,
|
|
126
|
-
exitCode,
|
|
127
|
-
workspace_root: cwd,
|
|
128
|
-
stderr: stderr || undefined,
|
|
129
|
-
}),
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
const sections = parseSections(stdout);
|
|
133
|
-
const repo = parseKeyValueLines(sections.Repository ?? "", [
|
|
134
|
-
"branch",
|
|
135
|
-
"range",
|
|
136
|
-
"base_ref",
|
|
137
|
-
"merge_base",
|
|
138
|
-
"diff_range",
|
|
139
|
-
"range_mode",
|
|
140
|
-
"git_sync",
|
|
141
|
-
]);
|
|
142
|
-
let vcsConfig = null;
|
|
143
|
-
let mergedPrStyle = null;
|
|
144
|
-
try {
|
|
145
|
-
vcsConfig = JSON.parse((sections["VCS Config"] ?? "").trim());
|
|
146
|
-
} catch {
|
|
147
|
-
/* optional */
|
|
148
|
-
}
|
|
149
|
-
try {
|
|
150
|
-
mergedPrStyle = JSON.parse((sections["Merged PR Style"] ?? "").trim());
|
|
151
|
-
} catch {
|
|
152
|
-
/* optional */
|
|
153
|
-
}
|
|
154
|
-
return jsonResult(
|
|
155
|
-
withWorkspace(workspace_root, {
|
|
156
|
-
branch: repo.branch,
|
|
157
|
-
range: repo.range,
|
|
158
|
-
base_ref: repo.base_ref,
|
|
159
|
-
merge_base: repo.merge_base,
|
|
160
|
-
diff_range: repo.diff_range,
|
|
161
|
-
range_mode: repo.range_mode,
|
|
162
|
-
git_sync: repo.git_sync,
|
|
163
|
-
workspace_root: cwd,
|
|
164
|
-
commits: sections.Commits ?? "",
|
|
165
|
-
diff_stat: sections["Diff Stat"] ?? "",
|
|
166
|
-
files: sections["Changed Files"] ?? "",
|
|
167
|
-
pr_template: sections["PR Template"] ?? "",
|
|
168
|
-
vcs_config: vcsConfig,
|
|
169
|
-
merged_pr_style: mergedPrStyle,
|
|
170
|
-
body_style_rules: [
|
|
171
|
-
"Use ## Summary (short outcome bullets) and ## Validation or ## Test plan only",
|
|
172
|
-
"Never add ## Notes with branch names, commit counts, diff stats, or scope disclaimers",
|
|
173
|
-
"Never paste commit log, diff stat, or changed-files list into the MR/PR body",
|
|
174
|
-
"Commits/diff in context are for drafting only — not for the published description",
|
|
175
|
-
],
|
|
176
|
-
stdout,
|
|
177
|
-
exitCode,
|
|
178
|
-
stderr: stderr || undefined,
|
|
179
|
-
}),
|
|
180
|
-
);
|
|
181
|
-
},
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
server.registerTool(
|
|
185
|
-
"workflow_pr_create",
|
|
186
|
-
{
|
|
187
|
-
description:
|
|
188
|
-
"Create GitLab MR or GitHub PR via glab/gh using vcs.json — requires confirmed: true",
|
|
189
|
-
inputSchema: {
|
|
190
|
-
confirmed: z.boolean(),
|
|
191
|
-
title: z.string(),
|
|
192
|
-
body: z.string().optional(),
|
|
193
|
-
draft: z.boolean().optional(),
|
|
194
|
-
target_branch: z.string().optional(),
|
|
195
|
-
workspace_root: workspaceRootSchema,
|
|
196
|
-
},
|
|
197
|
-
},
|
|
198
|
-
async ({ confirmed, title, body, draft, target_branch, workspace_root }) => {
|
|
199
|
-
if (!confirmed) return jsonResult({ error: "confirmed: true required" });
|
|
200
|
-
const { stdout, stderr, exitCode, cwd } = runScript("pr-create.sh", [], workspace_root, {
|
|
201
|
-
WF_PR_TITLE: title,
|
|
202
|
-
WF_PR_BODY: body ?? "",
|
|
203
|
-
WF_PR_CONFIRMED: "true",
|
|
204
|
-
WF_PR_DRAFT: draft ? "true" : "false",
|
|
205
|
-
WF_PR_TARGET: target_branch ?? "",
|
|
206
|
-
});
|
|
207
|
-
try {
|
|
208
|
-
const data = JSON.parse(stdout.trim());
|
|
209
|
-
if (data.error) {
|
|
210
|
-
return jsonResult(withWorkspace(workspace_root, { error: data.error, ...data }));
|
|
211
|
-
}
|
|
212
|
-
return jsonResult(withWorkspace(workspace_root, { ...data, workspace_root: cwd }));
|
|
213
|
-
} catch {
|
|
214
|
-
return jsonResult(
|
|
215
|
-
withWorkspace(workspace_root, {
|
|
216
|
-
error: stderr.trim() || stdout.trim() || "pr-create failed",
|
|
217
|
-
exitCode,
|
|
218
|
-
}),
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
},
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
server.registerTool(
|
|
225
|
-
"workflow_changelog_context",
|
|
226
|
-
{
|
|
227
|
-
description:
|
|
228
|
-
"Gather changelog update context. Defaults to Cursor workspace; pass workspace_root when the target repo differs.",
|
|
229
|
-
inputSchema: {
|
|
230
|
-
range: z.string().optional(),
|
|
231
|
-
workspace_root: workspaceRootSchema,
|
|
232
|
-
},
|
|
233
|
-
},
|
|
234
|
-
async ({ range, workspace_root }) => {
|
|
235
|
-
const args = range ? [range] : [];
|
|
236
|
-
const { stdout, stderr, exitCode, cwd } = runScript(
|
|
237
|
-
"changelog-context.sh",
|
|
238
|
-
args,
|
|
239
|
-
workspace_root,
|
|
240
|
-
);
|
|
241
|
-
const sections = parseSections(stdout);
|
|
242
|
-
const unreleased = changelogUnreleasedStats(workspace_root);
|
|
243
|
-
return jsonResult(
|
|
244
|
-
withWorkspace(workspace_root, {
|
|
245
|
-
changelog_excerpt: sections["Existing CHANGELOG.md"] ?? "",
|
|
246
|
-
rules: sections["Keep a Changelog Rules"] ?? "",
|
|
247
|
-
commits: sections.Commits ?? "",
|
|
248
|
-
diff_stat: sections["Diff Stat"] ?? "",
|
|
249
|
-
files: sections["Changed Files"] ?? "",
|
|
250
|
-
unreleased,
|
|
251
|
-
apply_hint:
|
|
252
|
-
"ALWAYS merge via workflow_changelog_apply — never hand-edit ### Added/Changed/… under [Unreleased].",
|
|
253
|
-
workspace_root: cwd,
|
|
254
|
-
stdout,
|
|
255
|
-
exitCode,
|
|
256
|
-
stderr: stderr || undefined,
|
|
257
|
-
}),
|
|
258
|
-
);
|
|
259
|
-
},
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
const changelogCategorySchema = z.enum([
|
|
263
|
-
"Added",
|
|
264
|
-
"Changed",
|
|
265
|
-
"Deprecated",
|
|
266
|
-
"Removed",
|
|
267
|
-
"Fixed",
|
|
268
|
-
"Security",
|
|
269
|
-
]);
|
|
270
|
-
|
|
271
|
-
server.registerTool(
|
|
272
|
-
"workflow_changelog_apply",
|
|
273
|
-
{
|
|
274
|
-
description:
|
|
275
|
-
"Merge Keep a Changelog bullets into ## [Unreleased] under the correct ### category. Collapses duplicate category headings. NEVER append a second ### Added block.",
|
|
276
|
-
inputSchema: {
|
|
277
|
-
entries: z
|
|
278
|
-
.union([
|
|
279
|
-
z.record(changelogCategorySchema, z.array(z.string())),
|
|
280
|
-
z.array(
|
|
281
|
-
z.object({
|
|
282
|
-
category: changelogCategorySchema,
|
|
283
|
-
text: z.string(),
|
|
284
|
-
}),
|
|
285
|
-
),
|
|
286
|
-
])
|
|
287
|
-
.optional(),
|
|
288
|
-
normalize_only: z
|
|
289
|
-
.boolean()
|
|
290
|
-
.optional()
|
|
291
|
-
.describe(
|
|
292
|
-
"If true, only collapse duplicate ### headings under [Unreleased] (no new bullets).",
|
|
293
|
-
),
|
|
294
|
-
path: z.string().optional().describe("Defaults to CHANGELOG.md"),
|
|
295
|
-
workspace_root: workspaceRootSchema,
|
|
296
|
-
},
|
|
297
|
-
},
|
|
298
|
-
async ({ entries, normalize_only, path: changelogPath, workspace_root }) => {
|
|
299
|
-
const data = changelogApply({
|
|
300
|
-
entries,
|
|
301
|
-
path: changelogPath,
|
|
302
|
-
normalize_only,
|
|
303
|
-
workspace_root,
|
|
304
|
-
});
|
|
305
|
-
if (data.error) return jsonResult({ error: data.error });
|
|
306
|
-
return jsonResult(
|
|
307
|
-
withWorkspace(workspace_root, {
|
|
308
|
-
...data,
|
|
309
|
-
unreleased: changelogUnreleasedStats(workspace_root, changelogPath),
|
|
310
|
-
}),
|
|
311
|
-
);
|
|
312
|
-
},
|
|
313
|
-
);
|
|
314
|
-
|
|
315
|
-
server.registerTool(
|
|
316
|
-
"workflow_release_notes_context",
|
|
317
|
-
{
|
|
318
|
-
description:
|
|
319
|
-
"Gather release notes context. Defaults to Cursor workspace; pass workspace_root when the target repo differs.",
|
|
320
|
-
inputSchema: {
|
|
321
|
-
range_or_tag: z.string().min(1),
|
|
322
|
-
workspace_root: workspaceRootSchema,
|
|
323
|
-
},
|
|
324
|
-
},
|
|
325
|
-
async ({ range_or_tag, workspace_root }) => {
|
|
326
|
-
const args = [range_or_tag];
|
|
327
|
-
const { stdout, stderr, exitCode, cwd } = runScript(
|
|
328
|
-
"release-notes-context.sh",
|
|
329
|
-
args,
|
|
330
|
-
workspace_root,
|
|
331
|
-
);
|
|
332
|
-
const sections = parseSections(stdout);
|
|
333
|
-
const repo = parseKeyValueLines(sections.Repository ?? "", ["requested", "range"]);
|
|
334
|
-
return jsonResult(
|
|
335
|
-
withWorkspace(workspace_root, {
|
|
336
|
-
requested: repo.requested,
|
|
337
|
-
range: repo.range,
|
|
338
|
-
tags: sections.Tags ?? "",
|
|
339
|
-
commits: sections.Commits ?? "",
|
|
340
|
-
diff_stat: sections["Diff Stat"] ?? "",
|
|
341
|
-
files: sections["Changed Files"] ?? "",
|
|
342
|
-
release_files: sections["Existing Release Files"] ?? "",
|
|
343
|
-
workspace_root: cwd,
|
|
344
|
-
stdout,
|
|
345
|
-
exitCode,
|
|
346
|
-
stderr: stderr || undefined,
|
|
347
|
-
}),
|
|
348
|
-
);
|
|
349
|
-
},
|
|
350
|
-
);
|
|
351
|
-
|
|
352
|
-
server.registerTool(
|
|
353
|
-
"workflow_docs_context",
|
|
354
|
-
{
|
|
355
|
-
description:
|
|
356
|
-
"Gather docs refresh context. Defaults to Cursor workspace; pass workspace_root when the target repo differs.",
|
|
357
|
-
inputSchema: {
|
|
358
|
-
range: z.string().optional(),
|
|
359
|
-
workspace_root: workspaceRootSchema,
|
|
360
|
-
},
|
|
361
|
-
},
|
|
362
|
-
async ({ range, workspace_root }) => {
|
|
363
|
-
const args = range ? [range] : [];
|
|
364
|
-
const { stdout, stderr, exitCode, cwd } = runScript(
|
|
365
|
-
"docs-refresh-context.sh",
|
|
366
|
-
args,
|
|
367
|
-
workspace_root,
|
|
368
|
-
);
|
|
369
|
-
const sections = parseSections(stdout);
|
|
370
|
-
return jsonResult(
|
|
371
|
-
withWorkspace(workspace_root, {
|
|
372
|
-
changed_files: sections["Changed Files"] ?? "",
|
|
373
|
-
readme_preview: sections["README Preview"] ?? "",
|
|
374
|
-
package_scripts: sections["Package Scripts"] ?? "",
|
|
375
|
-
files: sections["Documentation Files"] ?? "",
|
|
376
|
-
workspace_root: cwd,
|
|
377
|
-
stdout,
|
|
378
|
-
exitCode,
|
|
379
|
-
stderr: stderr || undefined,
|
|
380
|
-
}),
|
|
381
|
-
);
|
|
382
|
-
},
|
|
383
|
-
);
|
|
384
|
-
|
|
385
|
-
server.registerTool(
|
|
386
|
-
"workflow_git_context",
|
|
387
|
-
{
|
|
388
|
-
description:
|
|
389
|
-
"Gather git status for commit skill. Defaults to Cursor workspace; pass workspace_root when the target repo differs.",
|
|
390
|
-
inputSchema: {
|
|
391
|
-
paths: z.array(z.string()).optional(),
|
|
392
|
-
workspace_root: workspaceRootSchema,
|
|
393
|
-
},
|
|
394
|
-
},
|
|
395
|
-
async ({ paths, workspace_root }) => {
|
|
396
|
-
const data = gitContext(workspace_root, paths ?? []);
|
|
397
|
-
return jsonResult(withWorkspace(workspace_root, data));
|
|
398
|
-
},
|
|
399
|
-
);
|
|
400
|
-
|
|
401
|
-
server.registerTool(
|
|
402
|
-
"workflow_resolve_branch",
|
|
403
|
-
{
|
|
404
|
-
description:
|
|
405
|
-
"Resolve feature/* or bugfix/* branch from spec/plan. Returns current_branch, dirty, needs_checkout. No worktrees.",
|
|
406
|
-
inputSchema: {
|
|
407
|
-
spec_path: z.string(),
|
|
408
|
-
plan_path: z.string(),
|
|
409
|
-
workspace_root: workspaceRootSchema,
|
|
410
|
-
},
|
|
411
|
-
},
|
|
412
|
-
async ({ spec_path, plan_path, workspace_root }) => {
|
|
413
|
-
const data = resolveBranch({ spec_path, plan_path, workspace_root });
|
|
414
|
-
if (data.error) return jsonResult({ error: data.error });
|
|
415
|
-
return jsonResult(withWorkspace(workspace_root, data));
|
|
416
|
-
},
|
|
417
|
-
);
|
|
418
|
-
|
|
419
|
-
server.registerTool(
|
|
420
|
-
"workflow_branch_setup",
|
|
421
|
-
{
|
|
422
|
-
description:
|
|
423
|
-
"In-place checkout of feature/* or bugfix/* branch. Stash required when dirty. NEVER uses worktrees.",
|
|
424
|
-
inputSchema: {
|
|
425
|
-
action: z.enum(["setup", "reapply_stash"]).optional(),
|
|
426
|
-
sdd_dir: z.string().optional(),
|
|
427
|
-
target_branch: z.string().optional(),
|
|
428
|
-
stash: z.enum(["yes", "no"]).optional(),
|
|
429
|
-
workspace_root: workspaceRootSchema,
|
|
430
|
-
},
|
|
431
|
-
},
|
|
432
|
-
async ({ action, sdd_dir, target_branch, stash, workspace_root }) => {
|
|
433
|
-
const data = branchSetup({
|
|
434
|
-
action,
|
|
435
|
-
sdd_dir,
|
|
436
|
-
target_branch,
|
|
437
|
-
stash,
|
|
438
|
-
workspace_root,
|
|
439
|
-
});
|
|
440
|
-
if (data.error) return jsonResult({ error: data.error });
|
|
441
|
-
return jsonResult(withWorkspace(workspace_root, data));
|
|
442
|
-
},
|
|
443
|
-
);
|
|
444
|
-
|
|
445
|
-
server.registerTool(
|
|
446
|
-
"workflow_sdd_context",
|
|
447
|
-
{
|
|
448
|
-
description:
|
|
449
|
-
"Ensure docs/<slug>/sdd/ exists; return progress ledger + Cursor TodoWrite todos[]. NEVER use .superpowers/sdd.",
|
|
450
|
-
inputSchema: {
|
|
451
|
-
slug: z.string().optional(),
|
|
452
|
-
plan_path: z.string().optional(),
|
|
453
|
-
workspace_root: workspaceRootSchema,
|
|
454
|
-
},
|
|
455
|
-
},
|
|
456
|
-
async ({ slug, plan_path, workspace_root }) => {
|
|
457
|
-
const data = sddContext({ slug, plan_path, workspace_root });
|
|
458
|
-
if (data.error) return jsonResult({ error: data.error });
|
|
459
|
-
return jsonResult(withWorkspace(workspace_root, data));
|
|
460
|
-
},
|
|
461
|
-
);
|
|
462
|
-
|
|
463
|
-
server.registerTool(
|
|
464
|
-
"workflow_sdd_task_brief",
|
|
465
|
-
{
|
|
466
|
-
description: "Write task-N-brief.md under docs/<slug>/sdd/",
|
|
467
|
-
inputSchema: {
|
|
468
|
-
sdd_dir: z.string(),
|
|
469
|
-
task_id: z.number(),
|
|
470
|
-
section_text: z.string(),
|
|
471
|
-
workspace_root: workspaceRootSchema,
|
|
472
|
-
},
|
|
473
|
-
},
|
|
474
|
-
async ({ sdd_dir, task_id, section_text, workspace_root }) => {
|
|
475
|
-
const data = sddTaskBrief({
|
|
476
|
-
sdd_dir,
|
|
477
|
-
task_id,
|
|
478
|
-
section_text,
|
|
479
|
-
workspace_root,
|
|
480
|
-
});
|
|
481
|
-
if (data.error) return jsonResult({ error: data.error });
|
|
482
|
-
return jsonResult(withWorkspace(workspace_root, data));
|
|
483
|
-
},
|
|
484
|
-
);
|
|
485
|
-
|
|
486
|
-
server.registerTool(
|
|
487
|
-
"workflow_sdd_review_package",
|
|
488
|
-
{
|
|
489
|
-
description: "Write review diff under SDD dir between base and head SHAs",
|
|
490
|
-
inputSchema: {
|
|
491
|
-
sdd_dir: z.string(),
|
|
492
|
-
base_sha: z.string(),
|
|
493
|
-
head_sha: z.string(),
|
|
494
|
-
workspace_root: workspaceRootSchema,
|
|
495
|
-
},
|
|
496
|
-
},
|
|
497
|
-
async ({ sdd_dir, base_sha, head_sha, workspace_root }) => {
|
|
498
|
-
const data = sddReviewPackage({
|
|
499
|
-
sdd_dir,
|
|
500
|
-
base_sha,
|
|
501
|
-
head_sha,
|
|
502
|
-
workspace_root,
|
|
503
|
-
});
|
|
504
|
-
if (data.error) return jsonResult({ error: data.error });
|
|
505
|
-
return jsonResult(withWorkspace(workspace_root, data));
|
|
506
|
-
},
|
|
507
|
-
);
|
|
508
|
-
|
|
509
|
-
server.registerTool(
|
|
510
|
-
"workflow_sdd_append_progress",
|
|
511
|
-
{
|
|
512
|
-
description: "Append one validated line to docs/<slug>/sdd/progress.md",
|
|
513
|
-
inputSchema: {
|
|
514
|
-
progress_path: z.string(),
|
|
515
|
-
line: z.string(),
|
|
516
|
-
workspace_root: workspaceRootSchema,
|
|
517
|
-
},
|
|
518
|
-
},
|
|
519
|
-
async ({ progress_path, line, workspace_root }) => {
|
|
520
|
-
const data = sddAppendProgress({ progress_path, line, workspace_root });
|
|
521
|
-
if (data.error) return jsonResult({ error: data.error });
|
|
522
|
-
return jsonResult(withWorkspace(workspace_root, data));
|
|
523
|
-
},
|
|
524
|
-
);
|
|
525
|
-
|
|
526
|
-
server.registerTool(
|
|
527
|
-
"workflow_docs_branch",
|
|
528
|
-
{
|
|
529
|
-
description:
|
|
530
|
-
"Resolve branch for spec/plan authors: keep current feature|bugfix or create from the configured base.",
|
|
531
|
-
inputSchema: {
|
|
532
|
-
plan_path: z.string().optional(),
|
|
533
|
-
kind: z.enum(["feature", "bugfix"]).optional(),
|
|
534
|
-
workspace_root: workspaceRootSchema,
|
|
535
|
-
},
|
|
536
|
-
},
|
|
537
|
-
async ({ plan_path, kind, workspace_root }) => {
|
|
538
|
-
const data = docsBranch({ plan_path, kind, workspace_root });
|
|
539
|
-
if (data.error) return jsonResult(withWorkspace(workspace_root, { error: data.error }));
|
|
540
|
-
return jsonResult(withWorkspace(workspace_root, data));
|
|
541
|
-
},
|
|
542
|
-
);
|
|
543
|
-
|
|
544
|
-
server.registerTool(
|
|
545
|
-
"workflow_docs_validate",
|
|
546
|
-
{
|
|
547
|
-
description:
|
|
548
|
-
"Hard-fail validate spec/plan headers, link, branch, task order; returns quality findings (hard/warning). Defaults to Cursor workspace; pass workspace_root when paths are relative to another repo.",
|
|
549
|
-
inputSchema: {
|
|
550
|
-
spec_path: z.string(),
|
|
551
|
-
plan_path: z.string(),
|
|
552
|
-
workspace_root: workspaceRootSchema,
|
|
553
|
-
},
|
|
554
|
-
},
|
|
555
|
-
async ({ spec_path, plan_path, workspace_root }) => {
|
|
556
|
-
const data = docsValidate({ spec_path, plan_path, workspace_root });
|
|
557
|
-
if (data.error) {
|
|
558
|
-
return jsonResult(
|
|
559
|
-
withWorkspace(workspace_root, { error: data.error, errors: data.errors ?? [] }),
|
|
560
|
-
);
|
|
561
|
-
}
|
|
562
|
-
return jsonResult(withWorkspace(workspace_root, data));
|
|
563
|
-
},
|
|
564
|
-
);
|
|
565
|
-
|
|
566
|
-
server.registerTool(
|
|
567
|
-
"workflow_plan_tasks",
|
|
568
|
-
{
|
|
569
|
-
description:
|
|
570
|
-
"Parse plan ### Task N sections into structured tasks with section_text. Defaults to Cursor workspace; pass workspace_root when paths are relative to another repo.",
|
|
571
|
-
inputSchema: {
|
|
572
|
-
plan_path: z.string(),
|
|
573
|
-
spec_path: z.string().optional(),
|
|
574
|
-
workspace_root: workspaceRootSchema,
|
|
575
|
-
},
|
|
576
|
-
},
|
|
577
|
-
async ({ plan_path, spec_path, workspace_root }) => {
|
|
578
|
-
const data = parsePlanTasks(plan_path, workspace_root);
|
|
579
|
-
if (data.error) {
|
|
580
|
-
return jsonResult(withWorkspace(workspace_root, { error: data.error }));
|
|
581
|
-
}
|
|
582
|
-
if (spec_path) {
|
|
583
|
-
const branchData = resolveHandoffBranch(spec_path, plan_path, workspace_root);
|
|
584
|
-
if (branchData.error) {
|
|
585
|
-
return jsonResult(
|
|
586
|
-
withWorkspace(workspace_root, {
|
|
587
|
-
...data,
|
|
588
|
-
branch_error: branchData.error,
|
|
589
|
-
}),
|
|
590
|
-
);
|
|
591
|
-
}
|
|
592
|
-
return jsonResult(withWorkspace(workspace_root, { ...data, branch: branchData.branch }));
|
|
593
|
-
}
|
|
594
|
-
return jsonResult(withWorkspace(workspace_root, data));
|
|
595
|
-
},
|
|
596
|
-
);
|
|
597
|
-
|
|
598
|
-
server.registerTool(
|
|
599
|
-
"workflow_handoff_prompt",
|
|
600
|
-
{
|
|
601
|
-
description:
|
|
602
|
-
"Build copy-paste handoff prompt for next session. Defaults to Cursor workspace; pass workspace_root when spec/plan paths are relative to another repo.",
|
|
603
|
-
inputSchema: {
|
|
604
|
-
message: z.string(),
|
|
605
|
-
workspace_root: workspaceRootSchema,
|
|
606
|
-
},
|
|
607
|
-
},
|
|
608
|
-
async ({ message, workspace_root }) => {
|
|
609
|
-
const root = workspace_root ?? process.cwd();
|
|
610
|
-
const built = buildHandoffPrompt(root, message ?? "");
|
|
611
|
-
if ("error" in built) {
|
|
612
|
-
return jsonResult(withWorkspace(workspace_root, { error: built.error }));
|
|
613
|
-
}
|
|
614
|
-
const { prompt, spec: specPath, plan: planPath } = built;
|
|
615
|
-
|
|
616
|
-
if (planPath) {
|
|
617
|
-
const tasksData = parsePlanTasks(planPath, root);
|
|
618
|
-
if (tasksData.error) {
|
|
619
|
-
return jsonResult(
|
|
620
|
-
withWorkspace(workspace_root, {
|
|
621
|
-
prompt,
|
|
622
|
-
error: tasksData.error,
|
|
623
|
-
}),
|
|
624
|
-
);
|
|
625
|
-
}
|
|
626
|
-
const payload: Record<string, unknown> = {
|
|
627
|
-
prompt,
|
|
628
|
-
tasks: tasksData.tasks,
|
|
629
|
-
task_count: tasksData.task_count,
|
|
630
|
-
workspace_root: root,
|
|
631
|
-
};
|
|
632
|
-
if (specPath) {
|
|
633
|
-
const branchData = resolveHandoffBranch(specPath, planPath, root);
|
|
634
|
-
if (!branchData.error) {
|
|
635
|
-
payload.branch = branchData.branch;
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
const sdd = sddContext({ plan_path: planPath, workspace_root: root });
|
|
639
|
-
if (!sdd.error) {
|
|
640
|
-
payload.slug = sdd.slug;
|
|
641
|
-
payload.sdd_dir = sdd.sdd_dir;
|
|
642
|
-
payload.progress_path = sdd.progress_path;
|
|
643
|
-
payload.completed_task_ids = sdd.completed_task_ids;
|
|
644
|
-
payload.todos = sdd.todos;
|
|
645
|
-
payload.todo_write_required = true;
|
|
646
|
-
}
|
|
647
|
-
return jsonResult(withWorkspace(workspace_root, payload));
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
return jsonResult(
|
|
651
|
-
withWorkspace(workspace_root, {
|
|
652
|
-
error: "Could not resolve spec and plan for handoff",
|
|
653
|
-
}),
|
|
654
|
-
);
|
|
655
|
-
},
|
|
656
|
-
);
|
|
657
|
-
|
|
658
|
-
server.registerTool(
|
|
659
|
-
"workflow_toolkit_init_status",
|
|
660
|
-
{
|
|
661
|
-
description: "Check workit setup (MCP deps, YouTrack config, token)",
|
|
662
|
-
inputSchema: {},
|
|
663
|
-
},
|
|
664
|
-
async () => {
|
|
665
|
-
const data = initStatus();
|
|
666
|
-
if (data.error) return jsonResult({ error: data.error });
|
|
667
|
-
return jsonResult(data);
|
|
668
|
-
},
|
|
669
|
-
);
|
|
670
|
-
|
|
671
|
-
server.registerTool(
|
|
672
|
-
"workflow_toolkit_status",
|
|
673
|
-
{
|
|
674
|
-
description:
|
|
675
|
-
"Full health check: MCP deps, config files, YouTrack API verify. Use after editing token file.",
|
|
676
|
-
inputSchema: {},
|
|
677
|
-
},
|
|
678
|
-
async () => {
|
|
679
|
-
const data = toolkitStatus();
|
|
680
|
-
if (data.error) return jsonResult({ error: data.error });
|
|
681
|
-
return jsonResult(data);
|
|
682
|
-
},
|
|
683
|
-
);
|
|
684
|
-
|
|
685
|
-
server.registerTool(
|
|
686
|
-
"workflow_toolkit_init_apply",
|
|
687
|
-
{
|
|
688
|
-
description:
|
|
689
|
-
"Apply init action. Requires confirmed: true. Token is NOT accepted here — user edits token file locally.",
|
|
690
|
-
inputSchema: {
|
|
691
|
-
action: z.enum([
|
|
692
|
-
"npm_install",
|
|
693
|
-
"youtrack_scaffold",
|
|
694
|
-
"youtrack_json",
|
|
695
|
-
"youtrack_token_placeholder",
|
|
696
|
-
"vcs_scaffold",
|
|
697
|
-
"config",
|
|
698
|
-
"gitignore",
|
|
699
|
-
"hygiene",
|
|
700
|
-
]),
|
|
701
|
-
confirmed: z.boolean(),
|
|
702
|
-
base_url: z.string().optional(),
|
|
703
|
-
default_mention: z.string().optional(),
|
|
704
|
-
meeting_issue: z.string().optional(),
|
|
705
|
-
vcs_provider: z.enum(["gitlab", "github"]).optional(),
|
|
706
|
-
vcs_target_branch: z.string().optional(),
|
|
707
|
-
locale: z.string().optional(),
|
|
708
|
-
locale_options: z.array(z.string()).optional(),
|
|
709
|
-
timezone: z.string().optional(),
|
|
710
|
-
branch_policy_preset: z.enum(["gitflow", "github-flow", "trunk-based", "custom"]).optional(),
|
|
711
|
-
branch_policy_allowed: z.array(z.string()).optional(),
|
|
712
|
-
branch_policy_protected: z.array(z.string()).optional(),
|
|
713
|
-
include_open_source: z.boolean().optional(),
|
|
714
|
-
},
|
|
715
|
-
},
|
|
716
|
-
async ({
|
|
717
|
-
action,
|
|
718
|
-
confirmed,
|
|
719
|
-
base_url,
|
|
720
|
-
default_mention,
|
|
721
|
-
meeting_issue,
|
|
722
|
-
vcs_provider,
|
|
723
|
-
vcs_target_branch,
|
|
724
|
-
locale,
|
|
725
|
-
locale_options,
|
|
726
|
-
timezone,
|
|
727
|
-
branch_policy_preset,
|
|
728
|
-
branch_policy_allowed,
|
|
729
|
-
branch_policy_protected,
|
|
730
|
-
include_open_source,
|
|
731
|
-
}) => {
|
|
732
|
-
if (action === "hygiene") {
|
|
733
|
-
const result = ensureHygieneFiles(workspace_root ?? process.cwd(), {
|
|
734
|
-
confirmed,
|
|
735
|
-
includeOpenSource: include_open_source,
|
|
736
|
-
});
|
|
737
|
-
if (!result.ok) return jsonResult({ error: result.error });
|
|
738
|
-
return jsonResult(result);
|
|
739
|
-
}
|
|
740
|
-
if (action === "gitignore") {
|
|
741
|
-
const result = ensureProjectGitignore(workspace_root ?? process.cwd(), confirmed);
|
|
742
|
-
if (!result.ok) return jsonResult({ error: result.error });
|
|
743
|
-
return jsonResult(result);
|
|
744
|
-
}
|
|
745
|
-
if (action === "config") {
|
|
746
|
-
if (locale !== undefined && !/^[a-z]{2,3}(-[A-Z]{2})?$/.test(locale)) {
|
|
747
|
-
return jsonResult({
|
|
748
|
-
error: `invalid locale: ${JSON.stringify(locale)} — expected BCP-47 like en or es-CL`,
|
|
749
|
-
});
|
|
750
|
-
}
|
|
751
|
-
const current = readConfig();
|
|
752
|
-
const next = {
|
|
753
|
-
locale: locale ?? current.locale,
|
|
754
|
-
localeOptions: locale_options ?? current.localeOptions,
|
|
755
|
-
timezone: timezone ?? current.timezone,
|
|
756
|
-
branchPolicy: {
|
|
757
|
-
preset: (branch_policy_preset as any) ?? current.branchPolicy.preset,
|
|
758
|
-
allowed: branch_policy_allowed ?? current.branchPolicy.allowed,
|
|
759
|
-
protected: branch_policy_protected ?? current.branchPolicy.protected,
|
|
760
|
-
},
|
|
761
|
-
};
|
|
762
|
-
writeConfig(next);
|
|
763
|
-
return jsonResult({ action: "config", path: `${configDir()}/config.json`, ...next });
|
|
764
|
-
}
|
|
765
|
-
const env = {};
|
|
766
|
-
if (base_url) env.WORKFLOW_YT_BASE_URL = base_url;
|
|
767
|
-
if (default_mention) env.WORKFLOW_YT_MENTION = default_mention;
|
|
768
|
-
if (meeting_issue) env.WORKFLOW_YT_MEETING_ISSUE = meeting_issue;
|
|
769
|
-
if (vcs_provider) env.WORKFLOW_VCS_PROVIDER = vcs_provider;
|
|
770
|
-
if (vcs_target_branch) env.WORKFLOW_VCS_TARGET_BRANCH = vcs_target_branch;
|
|
771
|
-
const data = initApply({ action, confirmed, env });
|
|
772
|
-
if (data.error) return jsonResult({ error: data.error });
|
|
773
|
-
return jsonResult(data.data);
|
|
774
|
-
},
|
|
775
|
-
);
|
|
776
|
-
|
|
777
|
-
server.registerTool(
|
|
778
|
-
"workflow_youtrack_verify_token",
|
|
779
|
-
{
|
|
780
|
-
description: "Read-only YouTrack token test (GET /api/users/me). No work items created.",
|
|
781
|
-
inputSchema: {},
|
|
782
|
-
},
|
|
783
|
-
async () => {
|
|
784
|
-
const result = verifyYouTrackToken();
|
|
785
|
-
if (result.error) return jsonResult({ error: result.error });
|
|
786
|
-
const data = result.data;
|
|
787
|
-
if (!data.ok) return jsonResult({ error: data.error ?? "token invalid", ...data });
|
|
788
|
-
return jsonResult(data);
|
|
789
|
-
},
|
|
790
|
-
);
|
|
791
|
-
|
|
792
|
-
server.registerTool(
|
|
793
|
-
"workflow_youtrack_parse_issue",
|
|
794
|
-
{
|
|
795
|
-
description: "Parse YouTrack issue URL or bare id (e.g. NSR-40) into issueId",
|
|
796
|
-
inputSchema: {
|
|
797
|
-
issue_ref: z
|
|
798
|
-
.string()
|
|
799
|
-
.describe("YouTrack URL or issue id, e.g. https://…/issue/NSR-40 or NSR-40"),
|
|
800
|
-
},
|
|
801
|
-
},
|
|
802
|
-
async ({ issue_ref }) => {
|
|
803
|
-
const data = youtrackParseIssueRef(issue_ref);
|
|
804
|
-
if (data.error) return jsonResult({ error: data.error });
|
|
805
|
-
return jsonResult(data);
|
|
806
|
-
},
|
|
807
|
-
);
|
|
808
|
-
|
|
809
|
-
server.registerTool(
|
|
810
|
-
"workflow_youtrack_context",
|
|
811
|
-
{
|
|
812
|
-
description: "YouTrack config, greeting, issue resolution (from issue_url/id or meetings)",
|
|
813
|
-
inputSchema: {
|
|
814
|
-
mode: z.enum(["meetings", "task"]).optional(),
|
|
815
|
-
issue_id: z.string().optional(),
|
|
816
|
-
issue_url: z.string().optional(),
|
|
817
|
-
issue_ref: z.string().optional(),
|
|
818
|
-
spec_path: z.string().optional(),
|
|
819
|
-
plan_path: z.string().optional(),
|
|
820
|
-
workspace_root: workspaceRootSchema,
|
|
821
|
-
},
|
|
822
|
-
},
|
|
823
|
-
async ({ mode, issue_id, issue_url, issue_ref, spec_path, plan_path, workspace_root }) => {
|
|
824
|
-
const data = youtrackContext({
|
|
825
|
-
mode,
|
|
826
|
-
issue_id,
|
|
827
|
-
issue_url,
|
|
828
|
-
issue_ref,
|
|
829
|
-
spec_path,
|
|
830
|
-
plan_path,
|
|
831
|
-
workspace_root,
|
|
832
|
-
});
|
|
833
|
-
if (data.error) {
|
|
834
|
-
return jsonResult({
|
|
835
|
-
error: data.error,
|
|
836
|
-
...(data.requiresIssueInput ? { requiresIssueInput: true } : {}),
|
|
837
|
-
});
|
|
838
|
-
}
|
|
839
|
-
return jsonResult(withWorkspace(workspace_root, data));
|
|
840
|
-
},
|
|
841
|
-
);
|
|
842
|
-
|
|
843
|
-
server.registerTool(
|
|
844
|
-
"workflow_youtrack_parse_duration",
|
|
845
|
-
{
|
|
846
|
-
description: "Parse duration text (e.g. 1h 30m) to integer minutes",
|
|
847
|
-
inputSchema: {
|
|
848
|
-
text: z.string(),
|
|
849
|
-
workspace_root: workspaceRootSchema,
|
|
850
|
-
},
|
|
851
|
-
},
|
|
852
|
-
async ({ text, workspace_root }) => {
|
|
853
|
-
const data = youtrackParseDuration(text, workspace_root);
|
|
854
|
-
if (data.error) return jsonResult({ error: data.error });
|
|
855
|
-
return jsonResult(withWorkspace(workspace_root, data));
|
|
856
|
-
},
|
|
857
|
-
);
|
|
858
|
-
|
|
859
|
-
server.registerTool(
|
|
860
|
-
"workflow_youtrack_log_time",
|
|
861
|
-
{
|
|
862
|
-
description: "POST YouTrack work item (time only, no comment)",
|
|
863
|
-
inputSchema: {
|
|
864
|
-
issueId: z.string(),
|
|
865
|
-
minutes: z.number(),
|
|
866
|
-
text: z.string().optional(),
|
|
867
|
-
dateMs: z
|
|
868
|
-
.number()
|
|
869
|
-
.optional()
|
|
870
|
-
.describe("Epoch ms for work item date; omit for today in config timezone"),
|
|
871
|
-
workspace_root: workspaceRootSchema,
|
|
872
|
-
},
|
|
873
|
-
},
|
|
874
|
-
async ({ issueId, minutes, text, dateMs, workspace_root }) => {
|
|
875
|
-
const data = youtrackLogTime({
|
|
876
|
-
issueId,
|
|
877
|
-
minutes,
|
|
878
|
-
text,
|
|
879
|
-
dateMs,
|
|
880
|
-
workspace_root,
|
|
881
|
-
});
|
|
882
|
-
if (data.error) return jsonResult({ error: data.error });
|
|
883
|
-
return jsonResult(withWorkspace(workspace_root, data));
|
|
884
|
-
},
|
|
885
|
-
);
|
|
886
|
-
|
|
887
|
-
server.registerTool(
|
|
888
|
-
"workflow_youtrack_draft",
|
|
889
|
-
{
|
|
890
|
-
description: "Build ES-CL comment markdown without posting (envelope only by default)",
|
|
891
|
-
inputSchema: {
|
|
892
|
-
issueId: z.string(),
|
|
893
|
-
userNotes: z.string(),
|
|
894
|
-
greeting: z.string().optional(),
|
|
895
|
-
projectName: z.string().optional(),
|
|
896
|
-
includeProjectOpener: z.boolean().optional(),
|
|
897
|
-
includeFacts: z.boolean().optional(),
|
|
898
|
-
facts: z.record(z.any()).optional(),
|
|
899
|
-
},
|
|
900
|
-
},
|
|
901
|
-
async (input) => jsonResult(youtrackBuildDraft(input)),
|
|
902
|
-
);
|
|
903
|
-
|
|
904
|
-
server.registerTool(
|
|
905
|
-
"workflow_youtrack_post",
|
|
906
|
-
{
|
|
907
|
-
description: "Post YouTrack comment and optional time — requires confirmed: true",
|
|
908
|
-
inputSchema: {
|
|
909
|
-
confirmed: z.boolean(),
|
|
910
|
-
issueId: z.string(),
|
|
911
|
-
markdown: z.string(),
|
|
912
|
-
minutes: z.number().optional(),
|
|
913
|
-
workspace_root: workspaceRootSchema,
|
|
914
|
-
},
|
|
915
|
-
},
|
|
916
|
-
async ({ confirmed, issueId, markdown, minutes, workspace_root }) => {
|
|
917
|
-
const data = youtrackPostUpdate({
|
|
918
|
-
confirmed,
|
|
919
|
-
issueId,
|
|
920
|
-
markdown,
|
|
921
|
-
minutes,
|
|
922
|
-
workspace_root,
|
|
923
|
-
});
|
|
924
|
-
if (data.error && !data.partial) return jsonResult({ error: data.error });
|
|
925
|
-
return jsonResult(withWorkspace(workspace_root, data));
|
|
926
|
-
},
|
|
927
|
-
);
|
|
928
|
-
|
|
929
|
-
server.registerTool(
|
|
930
|
-
"workflow_present_ascii",
|
|
931
|
-
{
|
|
932
|
-
description: "Render deterministic ASCII UI wireframe from JSON spec",
|
|
933
|
-
inputSchema: {
|
|
934
|
-
title: z.string().optional(),
|
|
935
|
-
width: z.number().optional(),
|
|
936
|
-
rows: z.array(z.record(z.any())),
|
|
937
|
-
},
|
|
938
|
-
},
|
|
939
|
-
async (spec) => {
|
|
940
|
-
const data = asciiWireframe(spec);
|
|
941
|
-
if (data.error) return jsonResult({ error: data.error });
|
|
942
|
-
return jsonResult(data.data);
|
|
943
|
-
},
|
|
944
|
-
);
|
|
945
|
-
|
|
946
|
-
server.registerTool(
|
|
947
|
-
"workflow_present_flow",
|
|
948
|
-
{
|
|
949
|
-
description: "Render mermaid flowchart from JSON nodes/edges",
|
|
950
|
-
inputSchema: {
|
|
951
|
-
title: z.string().optional(),
|
|
952
|
-
direction: z.enum(["TD", "LR", "BT", "RL"]).optional(),
|
|
953
|
-
nodes: z.array(
|
|
954
|
-
z.object({
|
|
955
|
-
id: z.string(),
|
|
956
|
-
label: z.string(),
|
|
957
|
-
shape: z.string().optional(),
|
|
958
|
-
}),
|
|
959
|
-
),
|
|
960
|
-
edges: z.array(
|
|
961
|
-
z.object({
|
|
962
|
-
from: z.string(),
|
|
963
|
-
to: z.string(),
|
|
964
|
-
label: z.string().optional(),
|
|
965
|
-
}),
|
|
966
|
-
),
|
|
967
|
-
},
|
|
968
|
-
},
|
|
969
|
-
async (spec) => {
|
|
970
|
-
const data = flowDiagram(spec);
|
|
971
|
-
if (data.error) return jsonResult({ error: data.error });
|
|
972
|
-
return jsonResult(data.data);
|
|
973
|
-
},
|
|
974
|
-
);
|
|
975
|
-
|
|
976
|
-
server.registerTool(
|
|
977
|
-
"workflow_flow_status",
|
|
978
|
-
{
|
|
979
|
-
description: "Read the spec/plan approval flow state for a workflow",
|
|
980
|
-
inputSchema: {
|
|
981
|
-
plan_path: z.string().optional(),
|
|
982
|
-
spec_path: z.string().optional(),
|
|
983
|
-
workspace_root: workspaceRootSchema,
|
|
984
|
-
},
|
|
985
|
-
},
|
|
986
|
-
async ({ plan_path, spec_path, workspace_root }) => {
|
|
987
|
-
const root = workspace_root ?? process.cwd();
|
|
988
|
-
const slug = slugFromPath(plan_path ?? spec_path ?? "");
|
|
989
|
-
if (!slug) return jsonResult({ error: "plan_path or spec_path required" });
|
|
990
|
-
const state = readFlowState(root, slug);
|
|
991
|
-
return jsonResult({
|
|
992
|
-
slug,
|
|
993
|
-
spec: state.spec,
|
|
994
|
-
plan: state.plan,
|
|
995
|
-
menu: state.menu,
|
|
996
|
-
flow_path: `docs/${slug}/sdd/flow.json`,
|
|
997
|
-
});
|
|
998
|
-
},
|
|
999
|
-
);
|
|
1000
|
-
|
|
1001
|
-
server.registerTool(
|
|
1002
|
-
"workflow_spec_approve",
|
|
1003
|
-
{
|
|
1004
|
-
description:
|
|
1005
|
-
"Advance spec status: first call self_reviewed, second call approved (after user approval)",
|
|
1006
|
-
inputSchema: {
|
|
1007
|
-
confirmed: z.boolean(),
|
|
1008
|
-
spec_path: z.string(),
|
|
1009
|
-
workspace_root: workspaceRootSchema,
|
|
1010
|
-
},
|
|
1011
|
-
},
|
|
1012
|
-
async ({ confirmed, spec_path, workspace_root }) => {
|
|
1013
|
-
const root = workspace_root ?? process.cwd();
|
|
1014
|
-
const slug = slugFromPath(spec_path);
|
|
1015
|
-
const result = transitionSpec(root, slug, spec_path, confirmed);
|
|
1016
|
-
if (result.ok === false) return jsonResult({ error: result.error });
|
|
1017
|
-
return jsonResult({ spec: spec_path, status: readFlowState(root, slug).spec.status });
|
|
1018
|
-
},
|
|
1019
|
-
);
|
|
1020
|
-
|
|
1021
|
-
server.registerTool(
|
|
1022
|
-
"workflow_plan_approve",
|
|
1023
|
-
{
|
|
1024
|
-
description:
|
|
1025
|
-
"Advance plan status: first call self_reviewed, second call approved. Requires approved spec.",
|
|
1026
|
-
inputSchema: {
|
|
1027
|
-
confirmed: z.boolean(),
|
|
1028
|
-
plan_path: z.string(),
|
|
1029
|
-
workspace_root: workspaceRootSchema,
|
|
1030
|
-
},
|
|
1031
|
-
},
|
|
1032
|
-
async ({ confirmed, plan_path, workspace_root }) => {
|
|
1033
|
-
const root = workspace_root ?? process.cwd();
|
|
1034
|
-
const slug = slugFromPath(plan_path);
|
|
1035
|
-
const result = transitionPlan(root, slug, plan_path, confirmed);
|
|
1036
|
-
if (result.ok === false) return jsonResult({ error: result.error });
|
|
1037
|
-
return jsonResult({ plan: plan_path, status: readFlowState(root, slug).plan.status });
|
|
1038
|
-
},
|
|
1039
|
-
);
|
|
1040
|
-
|
|
1041
|
-
server.registerTool(
|
|
1042
|
-
"workflow_plan_menu",
|
|
1043
|
-
{
|
|
1044
|
-
description: "Record the answered post-plan choice menu (called after native question)",
|
|
1045
|
-
inputSchema: {
|
|
1046
|
-
confirmed: z.boolean(),
|
|
1047
|
-
plan_path: z.string(),
|
|
1048
|
-
choice: z.enum(["subagent-driven", "inline", "handoff", "review-spec", "review-plan"]),
|
|
1049
|
-
workspace_root: workspaceRootSchema,
|
|
1050
|
-
},
|
|
1051
|
-
},
|
|
1052
|
-
async ({ confirmed, plan_path, choice, workspace_root }) => {
|
|
1053
|
-
const root = workspace_root ?? process.cwd();
|
|
1054
|
-
const slug = slugFromPath(plan_path);
|
|
1055
|
-
const result = recordMenuChoice(root, slug, plan_path, choice, confirmed);
|
|
1056
|
-
if (result.ok === false) return jsonResult({ error: result.error });
|
|
1057
|
-
return jsonResult({ menu: { presented: true, chosen: choice } });
|
|
1058
|
-
},
|
|
1059
|
-
);
|
|
1060
|
-
|
|
1061
|
-
server.registerTool(
|
|
1062
|
-
"workflow_docs_repo_link",
|
|
1063
|
-
{
|
|
1064
|
-
description: "Link the component docs repo in the toolkit config",
|
|
1065
|
-
inputSchema: {
|
|
1066
|
-
path: z.string(),
|
|
1067
|
-
confirmed: z.boolean(),
|
|
1068
|
-
},
|
|
1069
|
-
},
|
|
1070
|
-
async ({ path: docsPath, confirmed }) => {
|
|
1071
|
-
const result = linkDocsRepo(docsPath, confirmed);
|
|
1072
|
-
if (!result.ok) return jsonResult({ error: result.error });
|
|
1073
|
-
return jsonResult({ path: result.path });
|
|
1074
|
-
},
|
|
1075
|
-
);
|
|
1076
|
-
|
|
1077
|
-
server.registerTool(
|
|
1078
|
-
"workflow_docs_list",
|
|
1079
|
-
{
|
|
1080
|
-
description: "List local specs with docs-repo promotion status",
|
|
1081
|
-
inputSchema: { workspace_root: workspaceRootSchema },
|
|
1082
|
-
},
|
|
1083
|
-
async ({ workspace_root }) => jsonResult(listSpecs(workspace_root ?? process.cwd())),
|
|
1084
|
-
);
|
|
1085
|
-
|
|
1086
|
-
server.registerTool(
|
|
1087
|
-
"workflow_docs_promote",
|
|
1088
|
-
{
|
|
1089
|
-
description: "Promote a spec (+plan) to the linked docs repo with quality gate",
|
|
1090
|
-
inputSchema: {
|
|
1091
|
-
slug: z.string(),
|
|
1092
|
-
confirmed: z.boolean(),
|
|
1093
|
-
force: z.boolean().optional(),
|
|
1094
|
-
workspace_root: workspaceRootSchema,
|
|
1095
|
-
},
|
|
1096
|
-
},
|
|
1097
|
-
async ({ slug, confirmed, force, workspace_root }) => {
|
|
1098
|
-
const result = promoteSpec(workspace_root ?? process.cwd(), slug, { confirmed, force });
|
|
1099
|
-
if (!result.ok) return jsonResult({ error: result.error, findings: result.findings ?? [] });
|
|
1100
|
-
return jsonResult({
|
|
1101
|
-
target_dir: result.target_dir,
|
|
1102
|
-
files: result.files,
|
|
1103
|
-
index_updated: result.index_updated,
|
|
1104
|
-
});
|
|
1105
|
-
},
|
|
1106
|
-
);
|
|
1107
|
-
|
|
1108
|
-
server.registerTool(
|
|
1109
|
-
"workflow_template_list",
|
|
1110
|
-
{
|
|
1111
|
-
description: "List editable templates with their source",
|
|
1112
|
-
inputSchema: { workspace_root: workspaceRootSchema },
|
|
1113
|
-
},
|
|
1114
|
-
async () => jsonResult({ templates: listTemplates() }),
|
|
1115
|
-
);
|
|
1116
|
-
|
|
1117
|
-
server.registerTool(
|
|
1118
|
-
"workflow_template_edit",
|
|
1119
|
-
{
|
|
1120
|
-
description: "Write an edited template to the toolkit config dir",
|
|
1121
|
-
inputSchema: {
|
|
1122
|
-
name: z.enum(["issue-update", "greeting", "headers"]),
|
|
1123
|
-
content: z.string(),
|
|
1124
|
-
confirmed: z.boolean(),
|
|
1125
|
-
},
|
|
1126
|
-
},
|
|
1127
|
-
async ({ name, content, confirmed }) => {
|
|
1128
|
-
const result = writeTemplate(name, content, confirmed);
|
|
1129
|
-
if (!result.ok) return jsonResult({ error: result.error });
|
|
1130
|
-
return jsonResult({ path: result.path });
|
|
1131
|
-
},
|
|
1132
|
-
);
|
|
1133
|
-
|
|
1134
|
-
server.registerTool(
|
|
1135
|
-
"workflow_rule_list",
|
|
1136
|
-
{
|
|
1137
|
-
description: "List canonical rules (config) with platforms",
|
|
1138
|
-
inputSchema: { workspace_root: workspaceRootSchema },
|
|
1139
|
-
},
|
|
1140
|
-
async () => jsonResult({ rules: listRules() }),
|
|
1141
|
-
);
|
|
1142
|
-
|
|
1143
|
-
server.registerTool(
|
|
1144
|
-
"workflow_rule_edit",
|
|
1145
|
-
{
|
|
1146
|
-
description: "Write a canonical rule to the toolkit config dir",
|
|
1147
|
-
inputSchema: {
|
|
1148
|
-
name: z.string(),
|
|
1149
|
-
description: z.string(),
|
|
1150
|
-
platforms: z.array(z.enum(["cursor", "opencode"])),
|
|
1151
|
-
body: z.string(),
|
|
1152
|
-
confirmed: z.boolean(),
|
|
1153
|
-
},
|
|
1154
|
-
},
|
|
1155
|
-
async ({ name, description, platforms, body, confirmed }) => {
|
|
1156
|
-
const result = writeRule({ name, description, platforms, body }, confirmed);
|
|
1157
|
-
if (!result.ok) return jsonResult({ error: result.error });
|
|
1158
|
-
return jsonResult({ path: result.path });
|
|
1159
|
-
},
|
|
1160
|
-
);
|
|
1161
|
-
|
|
1162
|
-
const transport = new StdioServerTransport();
|
|
1163
|
-
await server.connect(transport);
|