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