@gethmy/mcp 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/cli.js +1977 -1573
- package/dist/index.js +16 -0
- package/dist/lib/api-client.js +2 -0
- package/dist/lib/cli.js +2 -0
- package/dist/lib/context-assembly.js +21 -0
- package/dist/lib/skills.js +569 -0
- package/dist/lib/tui/docs.js +207 -21
- package/dist/lib/tui/setup.js +4 -308
- package/package.json +5 -4
- package/src/api-client.ts +6 -0
- package/src/cli.ts +2 -0
- package/src/context-assembly.ts +21 -0
- package/src/skills.ts +607 -0
- package/src/tui/docs.ts +241 -28
- package/src/tui/setup.ts +4 -311
package/dist/lib/tui/docs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { existsSync,
|
|
2
|
-
import { join } from "node:path";
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, join, resolve, sep } from "node:path";
|
|
3
3
|
import * as p from "@clack/prompts";
|
|
4
4
|
import { colors, symbols } from "./theme.js";
|
|
5
5
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
@@ -141,8 +141,12 @@ export function scanProject(cwd) {
|
|
|
141
141
|
let framework = null;
|
|
142
142
|
if (pkg) {
|
|
143
143
|
const deps = {
|
|
144
|
-
...(typeof pkg.dependencies === "object"
|
|
145
|
-
|
|
144
|
+
...(typeof pkg.dependencies === "object"
|
|
145
|
+
? pkg.dependencies
|
|
146
|
+
: {}),
|
|
147
|
+
...(typeof pkg.devDependencies === "object"
|
|
148
|
+
? pkg.devDependencies
|
|
149
|
+
: {}),
|
|
146
150
|
};
|
|
147
151
|
if (deps.next) {
|
|
148
152
|
framework = "next";
|
|
@@ -305,7 +309,11 @@ function describeScript(name) {
|
|
|
305
309
|
* Generate a scaffold AGENTS.md from project metadata.
|
|
306
310
|
*/
|
|
307
311
|
export function generateAgentsMd(info, _cwd) {
|
|
308
|
-
const lang = info.language === "typescript"
|
|
312
|
+
const lang = info.language === "typescript"
|
|
313
|
+
? "TypeScript"
|
|
314
|
+
: info.language === "javascript"
|
|
315
|
+
? "JavaScript"
|
|
316
|
+
: info.language;
|
|
309
317
|
const frameworkLabel = info.framework ? `${info.framework} ` : "";
|
|
310
318
|
const monoLabel = info.monorepo ? " (monorepo)" : "";
|
|
311
319
|
const lines = [];
|
|
@@ -404,19 +412,63 @@ export function generateArchitectureMd(info, _cwd) {
|
|
|
404
412
|
return lines.join("\n");
|
|
405
413
|
}
|
|
406
414
|
// ── Verification ────────────────────────────────────────────────────────────
|
|
415
|
+
/** Vague phrases that provide no actionable guidance to agents. */
|
|
416
|
+
const VAGUE_STANDARDS = [
|
|
417
|
+
"follow best practices",
|
|
418
|
+
"use best practices",
|
|
419
|
+
"keep it clean",
|
|
420
|
+
"write clean code",
|
|
421
|
+
"maintain code quality",
|
|
422
|
+
"ensure quality",
|
|
423
|
+
"use proper naming",
|
|
424
|
+
"follow conventions",
|
|
425
|
+
"be consistent",
|
|
426
|
+
];
|
|
407
427
|
/**
|
|
408
|
-
* Verify existing docs for broken references, stale commands,
|
|
428
|
+
* Verify existing docs for broken references, stale commands, dead paths,
|
|
429
|
+
* and structural quality issues from the setup-agent-docs quality checks.
|
|
409
430
|
*/
|
|
410
431
|
export function verifyDocs(cwd) {
|
|
411
432
|
const issues = [];
|
|
412
|
-
// 1. CLAUDE.md — check @-references
|
|
413
433
|
const claudeMd = readText(join(cwd, "CLAUDE.md"));
|
|
434
|
+
const agentsMd = readText(join(cwd, "AGENTS.md"));
|
|
435
|
+
const pkg = readJson(join(cwd, "package.json"));
|
|
436
|
+
const pkgScripts = pkg && typeof pkg.scripts === "object" && pkg.scripts !== null
|
|
437
|
+
? pkg.scripts
|
|
438
|
+
: {};
|
|
439
|
+
// ── CLAUDE.md checks ──────────────────────────────────────────────────
|
|
440
|
+
const projectRoot = resolve(cwd);
|
|
414
441
|
if (claudeMd) {
|
|
442
|
+
// Check @-references exist on disk (with path traversal protection)
|
|
443
|
+
const importedFiles = [];
|
|
415
444
|
for (const line of claudeMd.split("\n")) {
|
|
416
445
|
const match = line.match(/^@(.+)$/);
|
|
417
446
|
if (match) {
|
|
418
447
|
const refPath = match[1].trim();
|
|
419
|
-
|
|
448
|
+
// Reject absolute paths
|
|
449
|
+
if (isAbsolute(refPath)) {
|
|
450
|
+
issues.push({
|
|
451
|
+
severity: "error",
|
|
452
|
+
file: "CLAUDE.md",
|
|
453
|
+
message: `@ reference uses an absolute path: ${refPath}`,
|
|
454
|
+
fix: "Use a project-relative path under the repository root",
|
|
455
|
+
});
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
// Resolve and ensure the path stays inside the project root
|
|
459
|
+
const resolvedPath = resolve(projectRoot, refPath);
|
|
460
|
+
if (resolvedPath !== projectRoot &&
|
|
461
|
+
!resolvedPath.startsWith(projectRoot + sep)) {
|
|
462
|
+
issues.push({
|
|
463
|
+
severity: "error",
|
|
464
|
+
file: "CLAUDE.md",
|
|
465
|
+
message: `@ reference escapes project root: ${refPath}`,
|
|
466
|
+
fix: "Remove traversal segments (../) and keep references inside the repo",
|
|
467
|
+
});
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
importedFiles.push({ ref: refPath, resolved: resolvedPath });
|
|
471
|
+
if (!existsSync(resolvedPath)) {
|
|
420
472
|
issues.push({
|
|
421
473
|
severity: "error",
|
|
422
474
|
file: "CLAUDE.md",
|
|
@@ -426,26 +478,96 @@ export function verifyDocs(cwd) {
|
|
|
426
478
|
}
|
|
427
479
|
}
|
|
428
480
|
}
|
|
481
|
+
// Line count — CLAUDE.md should be under 100 lines (lean, @imports only)
|
|
482
|
+
const claudeLines = claudeMd.split("\n").length;
|
|
483
|
+
if (claudeLines > 100) {
|
|
484
|
+
issues.push({
|
|
485
|
+
severity: "warning",
|
|
486
|
+
file: "CLAUDE.md",
|
|
487
|
+
message: `CLAUDE.md is ${claudeLines} lines (recommended: under 100)`,
|
|
488
|
+
fix: "Move detailed content to AGENTS.md or docs/ files and use @imports",
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
// Duplication — check if CLAUDE.md duplicates content from imported files
|
|
492
|
+
if (importedFiles.length > 0) {
|
|
493
|
+
const claudeHeadings = extractHeadings(claudeMd);
|
|
494
|
+
for (const { ref: refPath, resolved: resolvedPath } of importedFiles) {
|
|
495
|
+
const refContent = readText(resolvedPath);
|
|
496
|
+
if (!refContent)
|
|
497
|
+
continue;
|
|
498
|
+
const refHeadings = extractHeadings(refContent);
|
|
499
|
+
// Flag if CLAUDE.md repeats section headings from imported files
|
|
500
|
+
for (const heading of claudeHeadings) {
|
|
501
|
+
if (refHeadings.has(heading)) {
|
|
502
|
+
issues.push({
|
|
503
|
+
severity: "warning",
|
|
504
|
+
file: "CLAUDE.md",
|
|
505
|
+
message: `Section "${heading}" duplicates content from @${refPath}`,
|
|
506
|
+
fix: `Remove the "${heading}" section — it's already included via @import`,
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
429
512
|
}
|
|
430
|
-
//
|
|
431
|
-
const agentsMd = readText(join(cwd, "AGENTS.md"));
|
|
432
|
-
const pkg = readJson(join(cwd, "package.json"));
|
|
433
|
-
const pkgScripts = pkg && typeof pkg.scripts === "object" && pkg.scripts !== null
|
|
434
|
-
? pkg.scripts
|
|
435
|
-
: {};
|
|
513
|
+
// ── AGENTS.md checks ──────────────────────────────────────────────────
|
|
436
514
|
if (agentsMd) {
|
|
437
|
-
//
|
|
515
|
+
// Project Context — first non-heading, non-blank line should be exactly one line
|
|
516
|
+
const agentsLines = agentsMd.split("\n");
|
|
517
|
+
const contextLines = [];
|
|
518
|
+
let pastFirstHeading = false;
|
|
519
|
+
let hitNextSection = false;
|
|
520
|
+
for (const line of agentsLines) {
|
|
521
|
+
if (!pastFirstHeading) {
|
|
522
|
+
if (line.startsWith("# ")) {
|
|
523
|
+
pastFirstHeading = true;
|
|
524
|
+
}
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
// Stop at next ## heading
|
|
528
|
+
if (line.startsWith("## ")) {
|
|
529
|
+
hitNextSection = true;
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
532
|
+
const trimmed = line.trim();
|
|
533
|
+
if (trimmed)
|
|
534
|
+
contextLines.push(trimmed);
|
|
535
|
+
}
|
|
536
|
+
if (pastFirstHeading && !hitNextSection && contextLines.length === 0) {
|
|
537
|
+
issues.push({
|
|
538
|
+
severity: "warning",
|
|
539
|
+
file: "AGENTS.md",
|
|
540
|
+
message: "Missing project context line after the title heading",
|
|
541
|
+
fix: "Add a single-line description: stack + what the project does",
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
else if (contextLines.length > 1) {
|
|
545
|
+
issues.push({
|
|
546
|
+
severity: "warning",
|
|
547
|
+
file: "AGENTS.md",
|
|
548
|
+
message: `Project context should be exactly 1 line, found ${contextLines.length}`,
|
|
549
|
+
fix: "Condense to a single line: stack + what the project does",
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
// Commands — check against package.json scripts
|
|
438
553
|
const codeBlockRe = /```[\s\S]*?```/g;
|
|
439
554
|
let blockMatch;
|
|
440
555
|
while ((blockMatch = codeBlockRe.exec(agentsMd)) !== null) {
|
|
441
556
|
const block = blockMatch[0];
|
|
442
|
-
// Match lines that look like package manager commands
|
|
443
557
|
const cmdRe = /(?:bun|npm|pnpm|yarn)\s+(?:run\s+)?(\S+)/g;
|
|
444
558
|
let cmdMatch;
|
|
445
559
|
while ((cmdMatch = cmdRe.exec(block)) !== null) {
|
|
446
560
|
const scriptName = cmdMatch[1];
|
|
447
|
-
|
|
448
|
-
|
|
561
|
+
const builtins = new Set([
|
|
562
|
+
"install",
|
|
563
|
+
"init",
|
|
564
|
+
"create",
|
|
565
|
+
"exec",
|
|
566
|
+
"dlx",
|
|
567
|
+
"x",
|
|
568
|
+
"test",
|
|
569
|
+
"start",
|
|
570
|
+
]);
|
|
449
571
|
if (builtins.has(scriptName))
|
|
450
572
|
continue;
|
|
451
573
|
if (Object.keys(pkgScripts).length > 0 && !(scriptName in pkgScripts)) {
|
|
@@ -458,27 +580,91 @@ export function verifyDocs(cwd) {
|
|
|
458
580
|
}
|
|
459
581
|
}
|
|
460
582
|
}
|
|
461
|
-
//
|
|
583
|
+
// Code Standards — flag vague, non-actionable phrases
|
|
584
|
+
const standardsSection = extractSection(agentsMd, "Code Standards");
|
|
585
|
+
if (standardsSection) {
|
|
586
|
+
const lower = standardsSection.toLowerCase();
|
|
587
|
+
for (const phrase of VAGUE_STANDARDS) {
|
|
588
|
+
if (lower.includes(phrase)) {
|
|
589
|
+
issues.push({
|
|
590
|
+
severity: "warning",
|
|
591
|
+
file: "AGENTS.md",
|
|
592
|
+
message: `Code Standards contains vague phrase: "${phrase}"`,
|
|
593
|
+
fix: "Replace with specific, verifiable conventions derived from config files",
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
// Missing test command — if no test script exists, AGENTS.md should say so
|
|
599
|
+
if (Object.keys(pkgScripts).length > 0) {
|
|
600
|
+
const hasTestScript = Object.keys(pkgScripts).some((k) => k === "test" || k.startsWith("test:"));
|
|
601
|
+
if (!hasTestScript) {
|
|
602
|
+
const mentionsNoTest = agentsMd.toLowerCase().includes("no test");
|
|
603
|
+
if (!mentionsNoTest) {
|
|
604
|
+
issues.push({
|
|
605
|
+
severity: "warning",
|
|
606
|
+
file: "AGENTS.md",
|
|
607
|
+
message: "No test script in package.json and AGENTS.md doesn't mention it",
|
|
608
|
+
fix: 'Add a note like "No test framework. Verify changes with `bun run build`."',
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
// Check backtick-quoted paths
|
|
462
614
|
checkBacktickPaths(agentsMd, "AGENTS.md", cwd, issues);
|
|
463
615
|
}
|
|
464
|
-
//
|
|
616
|
+
// ── docs/architecture.md checks ───────────────────────────────────────
|
|
465
617
|
const archMd = readText(join(cwd, "docs", "architecture.md"));
|
|
466
618
|
if (archMd) {
|
|
467
619
|
checkBacktickPaths(archMd, "docs/architecture.md", cwd, issues);
|
|
468
620
|
}
|
|
469
621
|
return issues;
|
|
470
622
|
}
|
|
623
|
+
/** Extract ## headings from markdown content. */
|
|
624
|
+
function extractHeadings(content) {
|
|
625
|
+
const headings = new Set();
|
|
626
|
+
for (const line of content.split("\n")) {
|
|
627
|
+
const match = line.match(/^#{2,3}\s+(.+)$/);
|
|
628
|
+
if (match) {
|
|
629
|
+
headings.add(match[1].trim());
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return headings;
|
|
633
|
+
}
|
|
634
|
+
/** Extract content under a specific ## section heading. */
|
|
635
|
+
function extractSection(content, heading) {
|
|
636
|
+
const lines = content.split("\n");
|
|
637
|
+
let capturing = false;
|
|
638
|
+
const result = [];
|
|
639
|
+
for (const line of lines) {
|
|
640
|
+
if (capturing) {
|
|
641
|
+
// Stop at next ## heading
|
|
642
|
+
if (line.match(/^#{1,2}\s/))
|
|
643
|
+
break;
|
|
644
|
+
result.push(line);
|
|
645
|
+
}
|
|
646
|
+
else if (line.match(new RegExp(`^##\\s+${heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "i"))) {
|
|
647
|
+
capturing = true;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return result.length > 0 ? result.join("\n") : null;
|
|
651
|
+
}
|
|
471
652
|
/** Scan markdown for backtick-quoted paths and check they exist. */
|
|
472
653
|
function checkBacktickPaths(content, file, cwd, issues) {
|
|
473
654
|
const pathRe = /`((?:src\/|packages\/|apps\/|supabase\/|docs\/)[^`]+)`/g;
|
|
474
655
|
let match;
|
|
475
656
|
const checked = new Set();
|
|
657
|
+
const root = resolve(cwd);
|
|
476
658
|
while ((match = pathRe.exec(content)) !== null) {
|
|
477
659
|
const refPath = match[1].replace(/\/$/, ""); // strip trailing slash
|
|
478
660
|
if (checked.has(refPath))
|
|
479
661
|
continue;
|
|
480
662
|
checked.add(refPath);
|
|
481
|
-
|
|
663
|
+
// Skip paths that escape the project root (e.g. src/../../etc/hosts)
|
|
664
|
+
const resolvedRef = resolve(root, refPath);
|
|
665
|
+
if (resolvedRef !== root && !resolvedRef.startsWith(root + sep))
|
|
666
|
+
continue;
|
|
667
|
+
if (!existsSync(resolvedRef)) {
|
|
482
668
|
issues.push({
|
|
483
669
|
severity: "warning",
|
|
484
670
|
file,
|
package/dist/lib/tui/setup.js
CHANGED
|
@@ -3,6 +3,7 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import * as p from "@clack/prompts";
|
|
5
5
|
import { areSkillsInstalled, getConfigPath, getLocalConfigPath, hasProjectContext, isConfigured, loadConfig, saveLocalConfig, } from "../config.js";
|
|
6
|
+
import { buildSkillFile, HARMONY_WORKFLOW_PROMPT } from "../skills.js";
|
|
6
7
|
import { detectAgents } from "./agents.js";
|
|
7
8
|
import { runDocsStep } from "./docs.js";
|
|
8
9
|
import { colors, formatPath, messages } from "./theme.js";
|
|
@@ -11,296 +12,6 @@ import { getWriteSummary, writeFilesWithProgress } from "./writer.js";
|
|
|
11
12
|
const GLOBAL_SKILLS_DIR = join(homedir(), ".agents", "skills");
|
|
12
13
|
// API base URL
|
|
13
14
|
const API_URL = "https://gethmy.com/api";
|
|
14
|
-
// Harmony workflow prompt - shared across agents
|
|
15
|
-
const HARMONY_WORKFLOW_PROMPT = `# Harmony Card Workflow
|
|
16
|
-
|
|
17
|
-
Start work on a Harmony card. Card reference: $ARGUMENTS
|
|
18
|
-
|
|
19
|
-
## 1. Find & Fetch Card
|
|
20
|
-
|
|
21
|
-
Parse the reference and fetch the card:
|
|
22
|
-
- \`#42\` or \`42\` → \`harmony_get_card_by_short_id\` with \`shortId: 42\`
|
|
23
|
-
- UUID → \`harmony_get_card\` with \`cardId\`
|
|
24
|
-
- Name/text → \`harmony_search_cards\` with \`query\`
|
|
25
|
-
|
|
26
|
-
## 2. Get Board State
|
|
27
|
-
|
|
28
|
-
Call \`harmony_get_board\` to get columns and labels. From the response:
|
|
29
|
-
- Find the "In Progress" (or "Progress") column ID
|
|
30
|
-
- Find the "agent" label ID
|
|
31
|
-
|
|
32
|
-
## 3. Setup Card for Work
|
|
33
|
-
|
|
34
|
-
Execute these in sequence:
|
|
35
|
-
1. \`harmony_move_card\` → Move to "In Progress" column
|
|
36
|
-
2. \`harmony_add_label_to_card\` → Add "agent" label
|
|
37
|
-
3. \`harmony_start_agent_session\`:
|
|
38
|
-
- \`cardId\`: Card UUID
|
|
39
|
-
- \`agentIdentifier\`: Your agent identifier
|
|
40
|
-
- \`agentName\`: Your agent name
|
|
41
|
-
- \`currentTask\`: "Analyzing card requirements"
|
|
42
|
-
|
|
43
|
-
## 4. Generate Work Prompt
|
|
44
|
-
|
|
45
|
-
Call \`harmony_generate_prompt\` with:
|
|
46
|
-
- \`cardId\` or \`shortId\` (+ \`projectId\` if using shortId)
|
|
47
|
-
- \`variant\`: Select based on task:
|
|
48
|
-
- \`"execute"\` (default) → Clear tasks, bug fixes, well-defined work
|
|
49
|
-
- \`"analysis"\` → Complex features, unclear requirements
|
|
50
|
-
- \`"draft"\` → Medium complexity, want feedback first
|
|
51
|
-
|
|
52
|
-
The generated prompt provides role framing, focus areas, subtasks, linked cards, and suggested outputs.
|
|
53
|
-
|
|
54
|
-
## 5. Display Card Summary
|
|
55
|
-
|
|
56
|
-
Show the user: Card title, short ID, role, priority, labels, due date, description, and subtasks.
|
|
57
|
-
|
|
58
|
-
## 6. Implement Solution
|
|
59
|
-
|
|
60
|
-
Work on the card following the generated prompt's guidance. Update progress at milestones:
|
|
61
|
-
- \`harmony_update_agent_progress\` with \`progressPercent\` (0-100), \`currentTask\`, \`status\`, \`blockers\`
|
|
62
|
-
|
|
63
|
-
**Progress checkpoints:** 20% (exploration), 50% (implementation), 80% (testing), 100% (done)
|
|
64
|
-
|
|
65
|
-
## 7. Complete Work
|
|
66
|
-
|
|
67
|
-
When finished:
|
|
68
|
-
1. \`harmony_end_agent_session\` with \`status: "completed"\`, \`progressPercent: 100\`
|
|
69
|
-
2. \`harmony_move_card\` to "Review" column
|
|
70
|
-
3. Summarize accomplishments
|
|
71
|
-
|
|
72
|
-
If pausing: \`harmony_end_agent_session\` with \`status: "paused"\`
|
|
73
|
-
|
|
74
|
-
## Key Tools Reference
|
|
75
|
-
|
|
76
|
-
**Cards:** \`harmony_get_card\`, \`harmony_get_card_by_short_id\`, \`harmony_search_cards\`, \`harmony_create_card\`, \`harmony_update_card\`, \`harmony_move_card\`, \`harmony_delete_card\`, \`harmony_assign_card\`
|
|
77
|
-
|
|
78
|
-
**Subtasks:** \`harmony_create_subtask\`, \`harmony_toggle_subtask\`, \`harmony_delete_subtask\`
|
|
79
|
-
|
|
80
|
-
**Labels:** \`harmony_add_label_to_card\`, \`harmony_remove_label_from_card\`, \`harmony_create_label\`
|
|
81
|
-
|
|
82
|
-
**Links:** \`harmony_add_link_to_card\`, \`harmony_remove_link_from_card\`, \`harmony_get_card_links\`
|
|
83
|
-
|
|
84
|
-
**Board:** \`harmony_get_board\`, \`harmony_list_projects\`, \`harmony_get_context\`, \`harmony_set_project_context\`
|
|
85
|
-
|
|
86
|
-
**Sessions:** \`harmony_start_agent_session\`, \`harmony_update_agent_progress\`, \`harmony_end_agent_session\`, \`harmony_get_agent_session\`
|
|
87
|
-
|
|
88
|
-
**AI:** \`harmony_generate_prompt\`, \`harmony_process_command\`
|
|
89
|
-
`;
|
|
90
|
-
// Harmony plan prompt - unified plan creation and execution workflow
|
|
91
|
-
const HARMONY_PLAN_PROMPT = `# Harmony Plan Workflow
|
|
92
|
-
|
|
93
|
-
Create a new plan or work on an existing one. Argument: $ARGUMENTS
|
|
94
|
-
|
|
95
|
-
## Step 1 — Detect Intent
|
|
96
|
-
|
|
97
|
-
Parse \`$ARGUMENTS\` to determine the workflow:
|
|
98
|
-
|
|
99
|
-
- **UUID** (contains dashes, 36 chars) → call \`harmony_get_plan\` with \`planId\` directly → **Step 2A**
|
|
100
|
-
- **\`#N\`** (short ID) → call \`harmony_get_card_by_short_id\` to get the card, then \`harmony_get_plan\` with \`cardId\` → **Step 2A**
|
|
101
|
-
- **Text** (anything else) → call \`harmony_list_plans\` with \`search\` set to the text
|
|
102
|
-
- If **one match** → use it → **Step 2A**
|
|
103
|
-
- If **multiple matches** → list them with title, status, phase, and updated date. Ask the user to pick one using \`AskUserQuestion\` → **Step 2A**
|
|
104
|
-
- If **no matches** → ask user: "No existing plans found for '$ARGUMENTS'. Would you like to create a new plan on this topic?" → **Step 2B**
|
|
105
|
-
- **Empty / vague topic** → **Step 2B** (create new plan)
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
## Step 2A — Work on Existing Plan
|
|
110
|
-
|
|
111
|
-
### 2A.1 — Analyze & Display
|
|
112
|
-
|
|
113
|
-
Once you have the plan ID, call \`harmony_get_plan\` to fetch the full plan with tasks. Show a structured summary:
|
|
114
|
-
|
|
115
|
-
\\\`\\\`\\\`
|
|
116
|
-
## [Plan Title]
|
|
117
|
-
**Status:** draft/active/archived | **Phase:** plan/execute/verify/done
|
|
118
|
-
**Tasks:** N total (X pending, Y in_progress, Z completed)
|
|
119
|
-
**URL:** https://gethmy.com/plans/{id}
|
|
120
|
-
\\\`\\\`\\\`
|
|
121
|
-
|
|
122
|
-
If plan is in execute phase and tasks already have linked cards, note which tasks have cards and which don't.
|
|
123
|
-
|
|
124
|
-
### 2A.2 — Recall Context
|
|
125
|
-
|
|
126
|
-
Call \`harmony_memory_search\` with the plan title to find related knowledge, patterns, or decisions that may inform execution.
|
|
127
|
-
|
|
128
|
-
### 2A.3 — Present Options
|
|
129
|
-
|
|
130
|
-
Use \`AskUserQuestion\` to offer execution choices. Adapt options based on plan state:
|
|
131
|
-
|
|
132
|
-
#### Default options (plan in \`plan\` phase):
|
|
133
|
-
|
|
134
|
-
**(A) Single card** — Create one card with plan summary + link to plan. Best for simple plans.
|
|
135
|
-
**(B) Multiple cards** — Create one card per task via \`harmony_advance_plan\`. Best for complex plans with independent tasks.
|
|
136
|
-
**(C) Analyze only** — Review the plan and design an implementation approach. Create cards later.
|
|
137
|
-
**(D) Skip** — Do nothing.
|
|
138
|
-
|
|
139
|
-
#### If plan has no tasks:
|
|
140
|
-
Only offer **(A) Single card**, **(C) Analyze only**, or **(D) Skip**.
|
|
141
|
-
|
|
142
|
-
#### If plan is already in \`execute\` phase with existing cards:
|
|
143
|
-
**(A) Work on existing cards** — List cards with short IDs, suggest \`/hmy #N\` to start
|
|
144
|
-
**(B) Create cards for remaining tasks** — Only create cards for tasks without linked cards
|
|
145
|
-
**(C) Analyze progress** — Review what's done vs remaining
|
|
146
|
-
**(D) Skip**
|
|
147
|
-
|
|
148
|
-
### 2A.4 — Execute Choice
|
|
149
|
-
|
|
150
|
-
#### Option A — Single card
|
|
151
|
-
1. Call \`harmony_create_card\` with:
|
|
152
|
-
- \`title\`: Plan title
|
|
153
|
-
- \`description\`: Brief 2-3 sentence summary of the plan + \`\\n\\n[View plan](https://gethmy.com/plans/{planId})\`
|
|
154
|
-
- \`priority\`: based on plan task priorities (use highest)
|
|
155
|
-
2. Call \`harmony_update_plan\` to set \`status: "active"\`, \`workflowPhase: "execute"\`
|
|
156
|
-
|
|
157
|
-
#### Option B — Multiple cards (advance plan)
|
|
158
|
-
1. Call \`harmony_advance_plan\` with:
|
|
159
|
-
- \`planId\`: the plan ID
|
|
160
|
-
- \`phase\`: \`"execute"\`
|
|
161
|
-
- \`summary\`: Brief summary of the plan scope
|
|
162
|
-
2. Display the created cards with their short IDs
|
|
163
|
-
|
|
164
|
-
#### Option C — Analyze only
|
|
165
|
-
1. Present a structured implementation analysis:
|
|
166
|
-
- Suggested order of tasks
|
|
167
|
-
- Dependencies between tasks
|
|
168
|
-
- Key technical considerations from memory search
|
|
169
|
-
- Estimated complexity
|
|
170
|
-
2. Suggest creating cards when ready
|
|
171
|
-
|
|
172
|
-
#### Option D — Skip
|
|
173
|
-
Acknowledge and stop.
|
|
174
|
-
|
|
175
|
-
### 2A.5 — Summary
|
|
176
|
-
|
|
177
|
-
After execution, show:
|
|
178
|
-
- Created card(s) with short IDs
|
|
179
|
-
- Current plan phase
|
|
180
|
-
- Suggest next step: \`/hmy #N\` to start working on a card
|
|
181
|
-
|
|
182
|
-
---
|
|
183
|
-
|
|
184
|
-
## Step 2B — Create New Plan
|
|
185
|
-
|
|
186
|
-
### 2B.1 — Context Gathering
|
|
187
|
-
|
|
188
|
-
Before interviewing, gather existing context:
|
|
189
|
-
|
|
190
|
-
1. Call \`harmony_get_board\` for board state (columns, cards, labels)
|
|
191
|
-
2. Call \`harmony_recall\` with relevant tags to find existing knowledge on the topic
|
|
192
|
-
3. Call \`harmony_memory_search\` with the topic to find related patterns/decisions/lessons
|
|
193
|
-
|
|
194
|
-
Note what you learn — reference it during the interview to ask smarter questions.
|
|
195
|
-
|
|
196
|
-
### 2B.2 — Structured Interview (3-5 questions)
|
|
197
|
-
|
|
198
|
-
Interview the user with **3-5 focused questions** using AskUserQuestion. Adapt based on complexity.
|
|
199
|
-
|
|
200
|
-
#### Core Questions
|
|
201
|
-
1. **Problem & Audience**: What problem are we solving? Who benefits?
|
|
202
|
-
2. **Scope**: What's in v1, what's deferred?
|
|
203
|
-
3. **Technical Constraints**: Any technical preferences, constraints, or existing patterns to follow?
|
|
204
|
-
|
|
205
|
-
#### Adaptive Follow-ups (if needed)
|
|
206
|
-
- Key user flows or interactions?
|
|
207
|
-
- Integration points with existing systems?
|
|
208
|
-
- Performance, security, or accessibility requirements?
|
|
209
|
-
|
|
210
|
-
#### Interview Rules
|
|
211
|
-
- Reference what you found in Step 2B.1 (board state, memories) to ask informed questions
|
|
212
|
-
- Skip questions that aren't relevant — simple features need fewer questions
|
|
213
|
-
- Tell the user when you have enough information to draft
|
|
214
|
-
|
|
215
|
-
### 2B.3 — Plan Document
|
|
216
|
-
|
|
217
|
-
Create a structured markdown document:
|
|
218
|
-
|
|
219
|
-
\\\`\\\`\\\`markdown
|
|
220
|
-
# [Title]
|
|
221
|
-
|
|
222
|
-
## Problem
|
|
223
|
-
[What problem we're solving and why]
|
|
224
|
-
|
|
225
|
-
## Scope
|
|
226
|
-
|
|
227
|
-
### In Scope
|
|
228
|
-
- [Feature/capability 1]
|
|
229
|
-
- [Feature/capability 2]
|
|
230
|
-
|
|
231
|
-
### Out of Scope
|
|
232
|
-
- [Deferred items]
|
|
233
|
-
|
|
234
|
-
## Approach
|
|
235
|
-
[Technical design, architecture decisions, key patterns]
|
|
236
|
-
|
|
237
|
-
### Key Decisions
|
|
238
|
-
| Decision | Choice | Rationale |
|
|
239
|
-
|----------|--------|-----------|
|
|
240
|
-
| ... | ... | ... |
|
|
241
|
-
|
|
242
|
-
## Tasks
|
|
243
|
-
1. **[Task title]** — priority: high
|
|
244
|
-
[Brief description]
|
|
245
|
-
|
|
246
|
-
2. **[Task title]** — priority: medium
|
|
247
|
-
[Brief description]
|
|
248
|
-
|
|
249
|
-
[Continue for all tasks...]
|
|
250
|
-
|
|
251
|
-
## Risks & Mitigations
|
|
252
|
-
| Risk | Mitigation |
|
|
253
|
-
|------|------------|
|
|
254
|
-
| ... | ... |
|
|
255
|
-
|
|
256
|
-
## Success Criteria
|
|
257
|
-
- [ ] [Measurable criterion]
|
|
258
|
-
\\\`\\\`\\\`
|
|
259
|
-
|
|
260
|
-
### 2B.4 — Create Plan
|
|
261
|
-
|
|
262
|
-
Call \`harmony_create_plan\` with:
|
|
263
|
-
- \`title\`: The plan title
|
|
264
|
-
- \`content\`: Full markdown document from Step 2B.3
|
|
265
|
-
- \`source\`: \`"agent"\`
|
|
266
|
-
- \`workflowPhase\`: \`"plan"\`
|
|
267
|
-
- \`tasks\`: Array of tasks from the Tasks section, each with:
|
|
268
|
-
- \`content\`: Task title + brief description
|
|
269
|
-
- \`priority\`: \`"high"\`, \`"medium"\`, or \`"low"\`
|
|
270
|
-
- \`status\`: \`"pending"\`
|
|
271
|
-
|
|
272
|
-
### 2B.5 — User Approval
|
|
273
|
-
|
|
274
|
-
Show the user:
|
|
275
|
-
- Plan URL: \`https://gethmy.com/plans/{id}\`
|
|
276
|
-
- Number of tasks created
|
|
277
|
-
- Brief summary
|
|
278
|
-
|
|
279
|
-
Then ask: **"Ready to execute? I'll create board cards from these tasks and start the execution phase."**
|
|
280
|
-
|
|
281
|
-
Options:
|
|
282
|
-
1. **Yes, advance to Execute** — proceed to Step 2B.6
|
|
283
|
-
2. **Let me review first** — end here, they can advance later from the UI
|
|
284
|
-
3. **Make changes** — iterate on the plan
|
|
285
|
-
|
|
286
|
-
### 2B.6 — Advance to Execute
|
|
287
|
-
|
|
288
|
-
Call \`harmony_advance_plan\` with:
|
|
289
|
-
- \`planId\`: The plan ID from Step 2B.4
|
|
290
|
-
- \`phase\`: \`"execute"\`
|
|
291
|
-
- \`summary\`: A 2-3 sentence summary of the key decisions made during planning
|
|
292
|
-
|
|
293
|
-
Report the result:
|
|
294
|
-
- "Created N cards in 'To Do'. Use \`/hmy #<id>\` to start working on any card."
|
|
295
|
-
- List the created cards with their short IDs
|
|
296
|
-
|
|
297
|
-
## Key Tools Reference
|
|
298
|
-
|
|
299
|
-
**Discovery:** \`harmony_list_plans\`, \`harmony_get_plan\`, \`harmony_get_card_by_short_id\`
|
|
300
|
-
**Context:** \`harmony_get_board\`, \`harmony_recall\`, \`harmony_memory_search\`
|
|
301
|
-
**Planning:** \`harmony_create_plan\`, \`harmony_advance_plan\`, \`harmony_update_plan\`
|
|
302
|
-
**Execution:** \`harmony_create_card\`, \`harmony_update_card\`
|
|
303
|
-
`;
|
|
304
15
|
/**
|
|
305
16
|
* Register MCP server using Claude CLI
|
|
306
17
|
* Returns true if successful, false if CLI unavailable or failed
|
|
@@ -432,24 +143,9 @@ function getAgentFiles(agentId, cwd, installMode = "global") {
|
|
|
432
143
|
const symlinks = [];
|
|
433
144
|
switch (agentId) {
|
|
434
145
|
case "claude": {
|
|
435
|
-
// Claude Code skill
|
|
436
|
-
const skillContent =
|
|
437
|
-
|
|
438
|
-
description: Start working on a Harmony card. Use when given a card reference like #42, UUID, or card name to implement.
|
|
439
|
-
argument-hint: <card-reference>
|
|
440
|
-
---
|
|
441
|
-
|
|
442
|
-
${HARMONY_WORKFLOW_PROMPT.replace("Your agent identifier", "claude-code").replace("Your agent name", "Claude Code")}
|
|
443
|
-
`;
|
|
444
|
-
// hmy-plan skill file (unified plan creation + execution)
|
|
445
|
-
const planSkillContent = `---
|
|
446
|
-
name: hmy-plan
|
|
447
|
-
description: Create a new plan or work on an existing one. Use when asked to plan a feature, execute a plan, review a plan, or given a plan reference.
|
|
448
|
-
argument-hint: [plan name, ID, or topic to plan]
|
|
449
|
-
---
|
|
450
|
-
|
|
451
|
-
${HARMONY_PLAN_PROMPT.replace("$ARGUMENTS", "$ARGUMENTS")}
|
|
452
|
-
`;
|
|
146
|
+
// Claude Code skill files built from the central skill registry
|
|
147
|
+
const skillContent = buildSkillFile("hmy", "claude");
|
|
148
|
+
const planSkillContent = buildSkillFile("hmy-plan", "claude");
|
|
453
149
|
if (installMode === "global") {
|
|
454
150
|
// Global mode: Write skills to ~/.agents/skills/, symlink directories to ~/.claude/skills/
|
|
455
151
|
files.push({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gethmy/mcp",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "MCP server for Harmony Kanban board - enables AI coding agents to manage your boards",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
"coding-assistant"
|
|
47
47
|
],
|
|
48
48
|
"engines": {
|
|
49
|
-
"node": ">=
|
|
49
|
+
"node": ">=20.0.0",
|
|
50
|
+
"bun": ">=1.0.0"
|
|
50
51
|
},
|
|
51
52
|
"scripts": {
|
|
52
53
|
"build": "bun build src/index.ts src/cli.ts --outdir dist --target node && tsc --outDir dist/lib --declaration false --skipLibCheck --noCheck",
|
|
@@ -68,7 +69,7 @@
|
|
|
68
69
|
"zod": "^4.3.6"
|
|
69
70
|
},
|
|
70
71
|
"devDependencies": {
|
|
71
|
-
"@types/node": "^25.
|
|
72
|
-
"typescript": "^
|
|
72
|
+
"@types/node": "^25.5.0",
|
|
73
|
+
"typescript": "^6.0.1"
|
|
73
74
|
}
|
|
74
75
|
}
|