@gethmy/mcp 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +547 -58
  3. package/dist/index.js +3 -3
  4. package/dist/lib/__tests__/active-learning.test.js +386 -0
  5. package/dist/lib/__tests__/agent-performance-profiles.test.js +325 -0
  6. package/dist/lib/__tests__/auto-session.test.js +661 -0
  7. package/dist/lib/__tests__/context-assembly.test.js +362 -0
  8. package/dist/lib/__tests__/graph-expansion.test.js +150 -0
  9. package/dist/lib/__tests__/integration-memory-crud.test.js +797 -0
  10. package/dist/lib/__tests__/integration-memory-system.test.js +281 -0
  11. package/dist/lib/__tests__/lifecycle-maintenance.test.js +207 -0
  12. package/dist/lib/__tests__/pattern-detection.test.js +295 -0
  13. package/dist/lib/__tests__/prompt-builder.test.js +418 -0
  14. package/dist/lib/active-learning.js +878 -0
  15. package/dist/lib/api-client.js +548 -0
  16. package/dist/lib/auto-session.js +173 -0
  17. package/dist/lib/cli.js +127 -0
  18. package/dist/lib/config.js +205 -0
  19. package/dist/lib/consolidation.js +243 -0
  20. package/dist/lib/context-assembly.js +606 -0
  21. package/dist/lib/graph-expansion.js +163 -0
  22. package/dist/lib/http.js +174 -0
  23. package/dist/lib/index.js +7 -0
  24. package/dist/lib/lifecycle-maintenance.js +88 -0
  25. package/dist/lib/prompt-builder.js +483 -0
  26. package/dist/lib/remote.js +166 -0
  27. package/dist/lib/server.js +3132 -0
  28. package/dist/lib/tui/agents.js +116 -0
  29. package/dist/lib/tui/docs.js +558 -0
  30. package/dist/lib/tui/setup.js +1068 -0
  31. package/dist/lib/tui/theme.js +95 -0
  32. package/dist/lib/tui/writer.js +200 -0
  33. package/package.json +11 -3
  34. package/src/__tests__/active-learning.test.ts +483 -0
  35. package/src/__tests__/agent-performance-profiles.test.ts +468 -0
  36. package/src/__tests__/auto-session.test.ts +912 -0
  37. package/src/__tests__/context-assembly.test.ts +506 -0
  38. package/src/__tests__/graph-expansion.test.ts +285 -0
  39. package/src/__tests__/integration-memory-crud.test.ts +948 -0
  40. package/src/__tests__/integration-memory-system.test.ts +321 -0
  41. package/src/__tests__/lifecycle-maintenance.test.ts +238 -0
  42. package/src/__tests__/pattern-detection.test.ts +438 -0
  43. package/src/__tests__/prompt-builder.test.ts +505 -0
  44. package/src/active-learning.ts +1227 -0
  45. package/src/api-client.ts +963 -0
  46. package/src/auto-session.ts +218 -0
  47. package/src/cli.ts +166 -0
  48. package/src/config.ts +285 -0
  49. package/src/consolidation.ts +314 -0
  50. package/src/context-assembly.ts +842 -0
  51. package/src/graph-expansion.ts +234 -0
  52. package/src/http.ts +265 -0
  53. package/src/index.ts +8 -0
  54. package/src/lifecycle-maintenance.ts +120 -0
  55. package/src/prompt-builder.ts +681 -0
  56. package/src/remote.ts +227 -0
  57. package/src/server.ts +3858 -0
  58. package/src/tui/agents.ts +154 -0
  59. package/src/tui/docs.ts +650 -0
  60. package/src/tui/setup.ts +1281 -0
  61. package/src/tui/theme.ts +114 -0
  62. package/src/tui/writer.ts +260 -0
package/dist/cli.js CHANGED
@@ -8828,7 +8828,7 @@ function hasLocalConfig(cwd) {
8828
8828
  function getApiKey() {
8829
8829
  const config = loadConfig();
8830
8830
  if (!config.apiKey) {
8831
- throw new Error(`Not configured. Run "harmony-mcp configure" to set your API key.
8831
+ throw new Error(`Not configured. Run "npx @gethmy/mcp setup" to set your API key.
8832
8832
  ` + "You can generate an API key at https://gethmy.com → Settings → API Keys.");
8833
8833
  }
8834
8834
  return config.apiKey;
@@ -29409,7 +29409,7 @@ function registerHandlers(server, deps) {
29409
29409
  async function handleToolCall(name, args, deps) {
29410
29410
  const unauthenticatedTools = ["harmony_signup", "harmony_onboard"];
29411
29411
  if (!unauthenticatedTools.includes(name) && !deps.isConfigured()) {
29412
- throw new Error(`Not configured. Run "harmony-mcp configure" to set your API key.
29412
+ throw new Error(`Not configured. Run "npx @gethmy/mcp setup" to set your API key.
29413
29413
  ` + `You can generate an API key at https://gethmy.com → Settings → API Keys.
29414
29414
  ` + 'Or use "harmony_onboard" to create an account and configure automatically.');
29415
29415
  }
@@ -30704,7 +30704,7 @@ function createConfigDeps() {
30704
30704
  class HarmonyMCPServer {
30705
30705
  server;
30706
30706
  constructor() {
30707
- this.server = new Server({ name: "harmony-mcp", version: "1.0.0" }, { capabilities: { tools: {}, resources: {} } });
30707
+ this.server = new Server({ name: "@gethmy/mcp", version: "2.0.0" }, { capabilities: { tools: {}, resources: {} } });
30708
30708
  registerHandlers(this.server, createConfigDeps());
30709
30709
  }
30710
30710
  async run() {
@@ -30745,14 +30745,14 @@ class HarmonyMCPServer {
30745
30745
 
30746
30746
  // src/tui/setup.ts
30747
30747
  import {
30748
- existsSync as existsSync5,
30748
+ existsSync as existsSync6,
30749
30749
  lstatSync,
30750
30750
  mkdirSync as mkdirSync4,
30751
30751
  symlinkSync,
30752
30752
  unlinkSync
30753
30753
  } from "node:fs";
30754
30754
  import { homedir as homedir4 } from "node:os";
30755
- import { dirname as dirname2, join as join4 } from "node:path";
30755
+ import { dirname as dirname2, join as join5 } from "node:path";
30756
30756
 
30757
30757
  // ../../node_modules/@clack/core/dist/index.mjs
30758
30758
  var import_sisteransi = __toESM(require_src(), 1);
@@ -31519,6 +31519,15 @@ function detectAgents(cwd = process.cwd()) {
31519
31519
  });
31520
31520
  }
31521
31521
 
31522
+ // src/tui/docs.ts
31523
+ import {
31524
+ existsSync as existsSync4,
31525
+ readFileSync as readFileSync3,
31526
+ readdirSync as readdirSync2,
31527
+ statSync
31528
+ } from "node:fs";
31529
+ import { join as join4 } from "node:path";
31530
+
31522
31531
  // src/tui/theme.ts
31523
31532
  var pc = __toESM(require_picocolors(), 1);
31524
31533
  var symbols = {
@@ -31583,17 +31592,488 @@ function formatPath(path, homeDir) {
31583
31592
  return path;
31584
31593
  }
31585
31594
 
31595
+ // src/tui/docs.ts
31596
+ var IGNORED_DIRS = new Set([
31597
+ "node_modules",
31598
+ ".git",
31599
+ "dist",
31600
+ "build",
31601
+ ".next",
31602
+ ".nuxt",
31603
+ ".output",
31604
+ ".vercel",
31605
+ ".turbo",
31606
+ ".cache",
31607
+ "coverage",
31608
+ ".harmony-worktrees",
31609
+ "__pycache__",
31610
+ "target",
31611
+ "vendor"
31612
+ ]);
31613
+ function readJson(filePath) {
31614
+ try {
31615
+ return JSON.parse(readFileSync3(filePath, "utf-8"));
31616
+ } catch {
31617
+ return null;
31618
+ }
31619
+ }
31620
+ function readText(filePath) {
31621
+ try {
31622
+ return readFileSync3(filePath, "utf-8");
31623
+ } catch {
31624
+ return null;
31625
+ }
31626
+ }
31627
+ function listDirs(dirPath) {
31628
+ try {
31629
+ return readdirSync2(dirPath).filter((entry) => {
31630
+ if (IGNORED_DIRS.has(entry) || entry.startsWith("."))
31631
+ return false;
31632
+ try {
31633
+ return statSync(join4(dirPath, entry)).isDirectory();
31634
+ } catch {
31635
+ return false;
31636
+ }
31637
+ });
31638
+ } catch {
31639
+ return [];
31640
+ }
31641
+ }
31642
+ var DIR_DESCRIPTIONS = {
31643
+ components: "UI components",
31644
+ pages: "Route-level pages",
31645
+ routes: "Route-level pages",
31646
+ views: "Route-level pages",
31647
+ hooks: "Custom hooks",
31648
+ lib: "Utilities",
31649
+ utils: "Utilities",
31650
+ api: "API / server code",
31651
+ server: "API / server code",
31652
+ contexts: "State management",
31653
+ store: "State management",
31654
+ stores: "State management",
31655
+ types: "Type definitions",
31656
+ styles: "Stylesheets",
31657
+ public: "Static assets",
31658
+ static: "Static assets",
31659
+ assets: "Static assets",
31660
+ supabase: "Supabase backend",
31661
+ functions: "Edge functions",
31662
+ packages: "Monorepo packages",
31663
+ apps: "Monorepo applications",
31664
+ src: "Source code",
31665
+ test: "Tests",
31666
+ tests: "Tests",
31667
+ __tests__: "Tests",
31668
+ scripts: "Build / utility scripts",
31669
+ config: "Configuration",
31670
+ docs: "Documentation",
31671
+ migrations: "Database migrations",
31672
+ prisma: "Prisma schema & migrations",
31673
+ e2e: "End-to-end tests",
31674
+ cypress: "Cypress tests"
31675
+ };
31676
+ function describeDir(name) {
31677
+ return DIR_DESCRIPTIONS[name.toLowerCase()] ?? name;
31678
+ }
31679
+ function scanProject(cwd) {
31680
+ let packageManager = null;
31681
+ if (existsSync4(join4(cwd, "bun.lock")) || existsSync4(join4(cwd, "bun.lockb"))) {
31682
+ packageManager = "bun";
31683
+ } else if (existsSync4(join4(cwd, "pnpm-lock.yaml"))) {
31684
+ packageManager = "pnpm";
31685
+ } else if (existsSync4(join4(cwd, "yarn.lock"))) {
31686
+ packageManager = "yarn";
31687
+ } else if (existsSync4(join4(cwd, "package.json"))) {
31688
+ packageManager = "npm";
31689
+ }
31690
+ const pkg = readJson(join4(cwd, "package.json"));
31691
+ const scripts = pkg && typeof pkg.scripts === "object" && pkg.scripts !== null ? pkg.scripts : {};
31692
+ let language = "unknown";
31693
+ if (existsSync4(join4(cwd, "tsconfig.json"))) {
31694
+ language = "typescript";
31695
+ } else if (existsSync4(join4(cwd, "go.mod"))) {
31696
+ language = "go";
31697
+ } else if (existsSync4(join4(cwd, "Cargo.toml"))) {
31698
+ language = "rust";
31699
+ } else if (existsSync4(join4(cwd, "setup.py")) || existsSync4(join4(cwd, "pyproject.toml"))) {
31700
+ language = "python";
31701
+ } else if (pkg) {
31702
+ language = "javascript";
31703
+ }
31704
+ let framework = null;
31705
+ if (pkg) {
31706
+ const deps = {
31707
+ ...typeof pkg.dependencies === "object" ? pkg.dependencies : {},
31708
+ ...typeof pkg.devDependencies === "object" ? pkg.devDependencies : {}
31709
+ };
31710
+ if (deps.next) {
31711
+ framework = "next";
31712
+ } else if (deps.react && deps.vite) {
31713
+ framework = "react+vite";
31714
+ } else if (deps.react) {
31715
+ framework = "react";
31716
+ } else if (deps.vue && deps.vite) {
31717
+ framework = "vue+vite";
31718
+ } else if (deps.vue && deps.nuxt) {
31719
+ framework = "nuxt";
31720
+ } else if (deps.vue) {
31721
+ framework = "vue";
31722
+ } else if (deps.astro) {
31723
+ framework = "astro";
31724
+ } else if (deps.svelte) {
31725
+ framework = "svelte";
31726
+ } else if (deps.express) {
31727
+ framework = "express";
31728
+ } else if (deps.fastify) {
31729
+ framework = "fastify";
31730
+ } else if (deps.hono) {
31731
+ framework = "hono";
31732
+ }
31733
+ }
31734
+ let linter = null;
31735
+ if (existsSync4(join4(cwd, "biome.json")) || existsSync4(join4(cwd, "biome.jsonc"))) {
31736
+ linter = "biome";
31737
+ } else {
31738
+ const eslintFiles = [
31739
+ ".eslintrc",
31740
+ ".eslintrc.js",
31741
+ ".eslintrc.cjs",
31742
+ ".eslintrc.json",
31743
+ ".eslintrc.yml",
31744
+ ".eslintrc.yaml",
31745
+ "eslint.config.js",
31746
+ "eslint.config.mjs",
31747
+ "eslint.config.cjs",
31748
+ "eslint.config.ts"
31749
+ ];
31750
+ if (eslintFiles.some((f) => existsSync4(join4(cwd, f)))) {
31751
+ linter = "eslint";
31752
+ } else {
31753
+ const prettierFiles = [
31754
+ ".prettierrc",
31755
+ ".prettierrc.js",
31756
+ ".prettierrc.json",
31757
+ ".prettierrc.yml",
31758
+ ".prettierrc.yaml",
31759
+ "prettier.config.js",
31760
+ "prettier.config.mjs"
31761
+ ];
31762
+ if (prettierFiles.some((f) => existsSync4(join4(cwd, f)))) {
31763
+ linter = "prettier";
31764
+ }
31765
+ }
31766
+ }
31767
+ let indentStyle = null;
31768
+ const biome = readJson(join4(cwd, "biome.json")) ?? readJson(join4(cwd, "biome.jsonc"));
31769
+ if (biome) {
31770
+ const formatter = biome.formatter;
31771
+ if (formatter) {
31772
+ const type = formatter.indentStyle === "tab" ? "tab" : "space";
31773
+ const width = typeof formatter.indentWidth === "number" ? formatter.indentWidth : 2;
31774
+ indentStyle = { type, width };
31775
+ }
31776
+ }
31777
+ if (!indentStyle) {
31778
+ const editorConfig = readText(join4(cwd, ".editorconfig"));
31779
+ if (editorConfig) {
31780
+ const styleMatch = editorConfig.match(/indent_style\s*=\s*(space|tab)/);
31781
+ const sizeMatch = editorConfig.match(/indent_size\s*=\s*(\d+)/);
31782
+ if (styleMatch) {
31783
+ indentStyle = {
31784
+ type: styleMatch[1],
31785
+ width: sizeMatch ? Number.parseInt(sizeMatch[1], 10) : 2
31786
+ };
31787
+ }
31788
+ }
31789
+ }
31790
+ const dirs = listDirs(cwd);
31791
+ const srcDirs = existsSync4(join4(cwd, "src")) ? listDirs(join4(cwd, "src")) : [];
31792
+ const monorepo = existsSync4(join4(cwd, "packages")) || existsSync4(join4(cwd, "apps"));
31793
+ const existingDocs = {
31794
+ agentsMd: existsSync4(join4(cwd, "AGENTS.md")),
31795
+ claudeMd: existsSync4(join4(cwd, "CLAUDE.md")),
31796
+ docsDir: existsSync4(join4(cwd, "docs")),
31797
+ architectureMd: existsSync4(join4(cwd, "docs", "architecture.md"))
31798
+ };
31799
+ return {
31800
+ packageManager,
31801
+ scripts,
31802
+ language,
31803
+ framework,
31804
+ linter,
31805
+ indentStyle,
31806
+ dirs,
31807
+ srcDirs,
31808
+ monorepo,
31809
+ existingDocs
31810
+ };
31811
+ }
31812
+ function runCmd(pm) {
31813
+ if (pm === "bun")
31814
+ return "bun run";
31815
+ if (pm === "pnpm")
31816
+ return "pnpm run";
31817
+ if (pm === "yarn")
31818
+ return "yarn";
31819
+ return "npm run";
31820
+ }
31821
+ function describeScript(name) {
31822
+ const map3 = {
31823
+ dev: "Dev server",
31824
+ start: "Start server",
31825
+ build: "Production build",
31826
+ lint: "Lint",
31827
+ "lint:fix": "Lint + autofix",
31828
+ format: "Format code",
31829
+ test: "Run tests",
31830
+ "test:watch": "Run tests (watch)",
31831
+ "test:e2e": "End-to-end tests",
31832
+ typecheck: "Type-check",
31833
+ "type-check": "Type-check",
31834
+ preview: "Preview production build",
31835
+ deploy: "Deploy",
31836
+ generate: "Code generation",
31837
+ migrate: "Run migrations",
31838
+ seed: "Seed database",
31839
+ clean: "Clean build artifacts",
31840
+ prepare: "Prepare (husky, etc.)"
31841
+ };
31842
+ return map3[name] ?? "";
31843
+ }
31844
+ function generateAgentsMd(info, _cwd) {
31845
+ const lang = info.language === "typescript" ? "TypeScript" : info.language === "javascript" ? "JavaScript" : info.language;
31846
+ const frameworkLabel = info.framework ? `${info.framework} ` : "";
31847
+ const monoLabel = info.monorepo ? " (monorepo)" : "";
31848
+ const lines = [];
31849
+ lines.push("# AGENTS.md");
31850
+ lines.push("");
31851
+ lines.push(`${frameworkLabel}${lang} project${monoLabel}.`);
31852
+ lines.push("");
31853
+ const scriptEntries = Object.entries(info.scripts);
31854
+ if (scriptEntries.length > 0 && info.packageManager) {
31855
+ const prefix = runCmd(info.packageManager);
31856
+ lines.push("## Commands");
31857
+ lines.push("");
31858
+ lines.push("```bash");
31859
+ const commands = scriptEntries.map(([name]) => `${prefix} ${name}`);
31860
+ const maxLen = Math.max(...commands.map((c) => c.length));
31861
+ for (let i = 0;i < scriptEntries.length; i++) {
31862
+ const [name] = scriptEntries[i];
31863
+ const cmd = commands[i];
31864
+ const desc = describeScript(name);
31865
+ if (desc) {
31866
+ lines.push(`${cmd}${" ".repeat(maxLen - cmd.length + 4)}# ${desc}`);
31867
+ } else {
31868
+ lines.push(cmd);
31869
+ }
31870
+ }
31871
+ lines.push("```");
31872
+ lines.push("");
31873
+ }
31874
+ lines.push("## Code Standards");
31875
+ lines.push("");
31876
+ const langLabel = info.language === "typescript" ? "TypeScript" : "JavaScript";
31877
+ if (info.language === "typescript" || info.language === "javascript") {
31878
+ lines.push(`- ${langLabel} with ES modules`);
31879
+ }
31880
+ if (info.indentStyle) {
31881
+ const unit = info.indentStyle.type === "tab" ? "tab" : "space";
31882
+ lines.push(`- ${info.indentStyle.width}-${unit} indentation`);
31883
+ }
31884
+ if (info.linter) {
31885
+ lines.push(`- Linted with ${info.linter}`);
31886
+ }
31887
+ lines.push("");
31888
+ lines.push("## Architecture");
31889
+ lines.push("");
31890
+ for (const dir of info.dirs) {
31891
+ lines.push(`- \`${dir}/\` — ${describeDir(dir)}`);
31892
+ }
31893
+ if (info.srcDirs.length > 0) {
31894
+ for (const sub of info.srcDirs) {
31895
+ lines.push(` - \`src/${sub}/\` — ${describeDir(sub)}`);
31896
+ }
31897
+ }
31898
+ lines.push("");
31899
+ return lines.join(`
31900
+ `);
31901
+ }
31902
+ function generateClaudeMd(info) {
31903
+ const lines = [];
31904
+ lines.push("# CLAUDE.md");
31905
+ lines.push("");
31906
+ lines.push("@AGENTS.md");
31907
+ if (info.existingDocs.architectureMd || info.dirs.includes("docs")) {
31908
+ lines.push("@docs/architecture.md");
31909
+ }
31910
+ lines.push("");
31911
+ return lines.join(`
31912
+ `);
31913
+ }
31914
+ function generateArchitectureMd(info, _cwd) {
31915
+ const lines = [];
31916
+ lines.push("# Architecture");
31917
+ lines.push("");
31918
+ lines.push("## Directory Structure");
31919
+ lines.push("");
31920
+ for (const dir of info.dirs) {
31921
+ lines.push(`- \`${dir}/\` — ${describeDir(dir)}`);
31922
+ }
31923
+ if (info.srcDirs.length > 0) {
31924
+ lines.push("");
31925
+ lines.push("### `src/`");
31926
+ lines.push("");
31927
+ for (const sub of info.srcDirs) {
31928
+ lines.push(`- \`src/${sub}/\` — ${describeDir(sub)}`);
31929
+ }
31930
+ }
31931
+ lines.push("");
31932
+ return lines.join(`
31933
+ `);
31934
+ }
31935
+ function verifyDocs(cwd) {
31936
+ const issues = [];
31937
+ const claudeMd = readText(join4(cwd, "CLAUDE.md"));
31938
+ if (claudeMd) {
31939
+ for (const line of claudeMd.split(`
31940
+ `)) {
31941
+ const match = line.match(/^@(.+)$/);
31942
+ if (match) {
31943
+ const refPath = match[1].trim();
31944
+ if (!existsSync4(join4(cwd, refPath))) {
31945
+ issues.push({
31946
+ severity: "error",
31947
+ file: "CLAUDE.md",
31948
+ message: `Referenced file does not exist: ${refPath}`,
31949
+ fix: `Remove the @${refPath} line or create the file`
31950
+ });
31951
+ }
31952
+ }
31953
+ }
31954
+ }
31955
+ const agentsMd = readText(join4(cwd, "AGENTS.md"));
31956
+ const pkg = readJson(join4(cwd, "package.json"));
31957
+ const pkgScripts = pkg && typeof pkg.scripts === "object" && pkg.scripts !== null ? pkg.scripts : {};
31958
+ if (agentsMd) {
31959
+ const codeBlockRe = /```[\s\S]*?```/g;
31960
+ let blockMatch;
31961
+ while ((blockMatch = codeBlockRe.exec(agentsMd)) !== null) {
31962
+ const block = blockMatch[0];
31963
+ const cmdRe = /(?:bun|npm|pnpm|yarn)\s+(?:run\s+)?(\S+)/g;
31964
+ let cmdMatch;
31965
+ while ((cmdMatch = cmdRe.exec(block)) !== null) {
31966
+ const scriptName = cmdMatch[1];
31967
+ const builtins = new Set(["install", "init", "create", "exec", "dlx", "x", "test", "start"]);
31968
+ if (builtins.has(scriptName))
31969
+ continue;
31970
+ if (Object.keys(pkgScripts).length > 0 && !(scriptName in pkgScripts)) {
31971
+ issues.push({
31972
+ severity: "warning",
31973
+ file: "AGENTS.md",
31974
+ message: `Command references script "${scriptName}" which is not in package.json`,
31975
+ fix: `Update the command or add "${scriptName}" to package.json scripts`
31976
+ });
31977
+ }
31978
+ }
31979
+ }
31980
+ checkBacktickPaths(agentsMd, "AGENTS.md", cwd, issues);
31981
+ }
31982
+ const archMd = readText(join4(cwd, "docs", "architecture.md"));
31983
+ if (archMd) {
31984
+ checkBacktickPaths(archMd, "docs/architecture.md", cwd, issues);
31985
+ }
31986
+ return issues;
31987
+ }
31988
+ function checkBacktickPaths(content, file2, cwd, issues) {
31989
+ const pathRe = /`((?:src\/|packages\/|apps\/|supabase\/|docs\/)[^`]+)`/g;
31990
+ let match;
31991
+ const checked = new Set;
31992
+ while ((match = pathRe.exec(content)) !== null) {
31993
+ const refPath = match[1].replace(/\/$/, "");
31994
+ if (checked.has(refPath))
31995
+ continue;
31996
+ checked.add(refPath);
31997
+ if (!existsSync4(join4(cwd, refPath))) {
31998
+ issues.push({
31999
+ severity: "warning",
32000
+ file: file2,
32001
+ message: `Referenced path does not exist: ${refPath}`,
32002
+ fix: `Update or remove the \`${refPath}\` reference`
32003
+ });
32004
+ }
32005
+ }
32006
+ }
32007
+ async function runDocsStep(cwd) {
32008
+ const info = scanProject(cwd);
32009
+ const hasDocs = info.existingDocs.agentsMd || info.existingDocs.claudeMd;
32010
+ if (!hasDocs) {
32011
+ const shouldGenerate = await ye({
32012
+ message: "No project docs found. Generate AGENTS.md and CLAUDE.md?",
32013
+ initialValue: true
32014
+ });
32015
+ if (pD(shouldGenerate) || !shouldGenerate) {
32016
+ return { files: [], issues: [], skipped: true };
32017
+ }
32018
+ const files = [];
32019
+ files.push({
32020
+ path: join4(cwd, "AGENTS.md"),
32021
+ content: generateAgentsMd(info, cwd),
32022
+ type: "text"
32023
+ });
32024
+ files.push({
32025
+ path: join4(cwd, "CLAUDE.md"),
32026
+ content: generateClaudeMd(info),
32027
+ type: "text"
32028
+ });
32029
+ if (info.dirs.includes("docs") || info.srcDirs.length > 0) {
32030
+ files.push({
32031
+ path: join4(cwd, "docs", "architecture.md"),
32032
+ content: generateArchitectureMd(info, cwd),
32033
+ type: "text"
32034
+ });
32035
+ }
32036
+ M2.success(`Generated ${files.length} doc file(s): ${files.map((f) => f.path.replace(cwd + "/", "")).join(", ")}`);
32037
+ return { files, issues: [], skipped: false };
32038
+ }
32039
+ const shouldVerify = await ye({
32040
+ message: "Project docs found. Verify for issues?",
32041
+ initialValue: false
32042
+ });
32043
+ if (pD(shouldVerify) || !shouldVerify) {
32044
+ return { files: [], issues: [], skipped: true };
32045
+ }
32046
+ const issues = verifyDocs(cwd);
32047
+ if (issues.length === 0) {
32048
+ M2.success("No issues found in project docs.");
32049
+ } else {
32050
+ for (const issue2 of issues) {
32051
+ const prefix = `${colors.bold(issue2.file)}:`;
32052
+ if (issue2.severity === "error") {
32053
+ M2.error(`${prefix} ${issue2.message}`);
32054
+ } else {
32055
+ M2.warning(`${prefix} ${issue2.message}`);
32056
+ }
32057
+ if (issue2.fix) {
32058
+ M2.message(` ${symbols.arrow} ${colors.dim(issue2.fix)}`);
32059
+ }
32060
+ }
32061
+ M2.info(`Found ${issues.length} issue(s) (${issues.filter((i) => i.severity === "error").length} errors, ${issues.filter((i) => i.severity === "warning").length} warnings)`);
32062
+ }
32063
+ return { files: [], issues, skipped: false };
32064
+ }
32065
+
31586
32066
  // src/tui/writer.ts
31587
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
32067
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
31588
32068
  import { homedir as homedir3 } from "node:os";
31589
32069
  import { dirname } from "node:path";
31590
32070
  function ensureDir(dirPath) {
31591
- if (!existsSync4(dirPath)) {
32071
+ if (!existsSync5(dirPath)) {
31592
32072
  mkdirSync3(dirPath, { recursive: true, mode: 493 });
31593
32073
  }
31594
32074
  }
31595
32075
  function writeFile(filePath, content, options = {}) {
31596
- const exists = existsSync4(filePath);
32076
+ const exists = existsSync5(filePath);
31597
32077
  if (exists && !options.force) {
31598
32078
  return { path: filePath, action: "skip" };
31599
32079
  }
@@ -31611,7 +32091,7 @@ function writeFile(filePath, content, options = {}) {
31611
32091
  }
31612
32092
  }
31613
32093
  function mergeJsonFile(filePath, updates, options = {}) {
31614
- const exists = existsSync4(filePath);
32094
+ const exists = existsSync5(filePath);
31615
32095
  if (!exists) {
31616
32096
  try {
31617
32097
  ensureDir(dirname(filePath));
@@ -31628,7 +32108,7 @@ function mergeJsonFile(filePath, updates, options = {}) {
31628
32108
  }
31629
32109
  }
31630
32110
  try {
31631
- const existing = JSON.parse(readFileSync3(filePath, "utf-8"));
32111
+ const existing = JSON.parse(readFileSync4(filePath, "utf-8"));
31632
32112
  if (updates.mcpServers && existing.mcpServers) {
31633
32113
  const existingServers = existing.mcpServers;
31634
32114
  const updateServers = updates.mcpServers;
@@ -31661,7 +32141,7 @@ function mergeJsonFile(filePath, updates, options = {}) {
31661
32141
  }
31662
32142
  }
31663
32143
  function appendToToml(filePath, section, content, options = {}) {
31664
- const exists = existsSync4(filePath);
32144
+ const exists = existsSync5(filePath);
31665
32145
  if (!exists) {
31666
32146
  try {
31667
32147
  ensureDir(dirname(filePath));
@@ -31676,7 +32156,7 @@ function appendToToml(filePath, section, content, options = {}) {
31676
32156
  }
31677
32157
  }
31678
32158
  try {
31679
- const existing = readFileSync3(filePath, "utf-8");
32159
+ const existing = readFileSync4(filePath, "utf-8");
31680
32160
  if (existing.includes(section)) {
31681
32161
  if (options.force) {
31682
32162
  const updated = existing.replace(new RegExp(`\\[${section.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\][\\s\\S]*?(?=\\[|$)`), content.trim() + `
@@ -31737,7 +32217,7 @@ function getWriteSummary(files, options = {}) {
31737
32217
  const home = homedir3();
31738
32218
  for (const file2 of files) {
31739
32219
  const displayPath = formatPath(file2.path, home);
31740
- const exists = existsSync4(file2.path);
32220
+ const exists = existsSync5(file2.path);
31741
32221
  if (exists && !options.force) {
31742
32222
  toSkip.push(displayPath);
31743
32223
  } else if (exists) {
@@ -31750,7 +32230,7 @@ function getWriteSummary(files, options = {}) {
31750
32230
  }
31751
32231
 
31752
32232
  // src/tui/setup.ts
31753
- var GLOBAL_SKILLS_DIR = join4(homedir4(), ".agents", "skills");
32233
+ var GLOBAL_SKILLS_DIR = join5(homedir4(), ".agents", "skills");
31754
32234
  var API_URL = "https://gethmy.com/api";
31755
32235
  var HARMONY_WORKFLOW_PROMPT = `# Harmony Card Workflow
31756
32236
 
@@ -32043,7 +32523,7 @@ Report the result:
32043
32523
  async function registerMcpServer() {
32044
32524
  try {
32045
32525
  const { execSync } = await import("node:child_process");
32046
- execSync("claude mcp add --transport stdio harmony -- npx -y harmony-mcp@latest serve", {
32526
+ execSync("claude mcp add --transport stdio harmony -- npx -y @gethmy/mcp@latest serve", {
32047
32527
  stdio: "pipe"
32048
32528
  });
32049
32529
  return true;
@@ -32052,22 +32532,22 @@ async function registerMcpServer() {
32052
32532
  }
32053
32533
  }
32054
32534
  async function writeMcpConfigFallback(home) {
32055
- const { readFileSync: readFileSync4, writeFileSync: writeFileSync4, mkdirSync: mkdirSync5, existsSync: existsSync6 } = await import("node:fs");
32056
- const settingsPath = join4(home, ".claude", "settings.json");
32535
+ const { readFileSync: readFileSync5, writeFileSync: writeFileSync4, mkdirSync: mkdirSync5, existsSync: existsSync7 } = await import("node:fs");
32536
+ const settingsPath = join5(home, ".claude", "settings.json");
32057
32537
  const settingsDir = dirname2(settingsPath);
32058
- if (!existsSync6(settingsDir)) {
32538
+ if (!existsSync7(settingsDir)) {
32059
32539
  mkdirSync5(settingsDir, { recursive: true });
32060
32540
  }
32061
32541
  let settings = {};
32062
- if (existsSync6(settingsPath)) {
32542
+ if (existsSync7(settingsPath)) {
32063
32543
  try {
32064
- settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
32544
+ settings = JSON.parse(readFileSync5(settingsPath, "utf-8"));
32065
32545
  } catch {}
32066
32546
  }
32067
32547
  const mcpServers = settings.mcpServers || {};
32068
32548
  mcpServers.harmony = {
32069
32549
  command: "npx",
32070
- args: ["-y", "harmony-mcp@latest", "serve"]
32550
+ args: ["-y", "@gethmy/mcp@latest", "serve"]
32071
32551
  };
32072
32552
  settings.mcpServers = mcpServers;
32073
32553
  writeFileSync4(settingsPath, JSON.stringify(settings, null, 2));
@@ -32159,31 +32639,31 @@ ${HARMONY_PLAN_PROMPT.replace("$ARGUMENTS", "$ARGUMENTS")}
32159
32639
  `;
32160
32640
  if (installMode === "global") {
32161
32641
  files.push({
32162
- path: join4(GLOBAL_SKILLS_DIR, "hmy", "SKILL.md"),
32642
+ path: join5(GLOBAL_SKILLS_DIR, "hmy", "SKILL.md"),
32163
32643
  content: skillContent,
32164
32644
  type: "text"
32165
32645
  });
32166
32646
  symlinks.push({
32167
- target: join4(GLOBAL_SKILLS_DIR, "hmy"),
32168
- link: join4(home, ".claude", "skills", "hmy")
32647
+ target: join5(GLOBAL_SKILLS_DIR, "hmy"),
32648
+ link: join5(home, ".claude", "skills", "hmy")
32169
32649
  });
32170
32650
  files.push({
32171
- path: join4(GLOBAL_SKILLS_DIR, "hmy-plan", "SKILL.md"),
32651
+ path: join5(GLOBAL_SKILLS_DIR, "hmy-plan", "SKILL.md"),
32172
32652
  content: planSkillContent,
32173
32653
  type: "text"
32174
32654
  });
32175
32655
  symlinks.push({
32176
- target: join4(GLOBAL_SKILLS_DIR, "hmy-plan"),
32177
- link: join4(home, ".claude", "skills", "hmy-plan")
32656
+ target: join5(GLOBAL_SKILLS_DIR, "hmy-plan"),
32657
+ link: join5(home, ".claude", "skills", "hmy-plan")
32178
32658
  });
32179
32659
  } else {
32180
32660
  files.push({
32181
- path: join4(cwd, ".claude", "skills", "hmy", "SKILL.md"),
32661
+ path: join5(cwd, ".claude", "skills", "hmy", "SKILL.md"),
32182
32662
  content: skillContent,
32183
32663
  type: "text"
32184
32664
  });
32185
32665
  files.push({
32186
- path: join4(cwd, ".claude", "skills", "hmy-plan", "SKILL.md"),
32666
+ path: join5(cwd, ".claude", "skills", "hmy-plan", "SKILL.md"),
32187
32667
  content: planSkillContent,
32188
32668
  type: "text"
32189
32669
  });
@@ -32218,7 +32698,7 @@ When given a card reference (e.g., #42 or a card name), follow this workflow:
32218
32698
  - \`harmony_generate_prompt\` - Get role-based guidance and focus areas for the card
32219
32699
  `;
32220
32700
  files.push({
32221
- path: join4(cwd, "AGENTS.md"),
32701
+ path: join5(cwd, "AGENTS.md"),
32222
32702
  content: agentsContent,
32223
32703
  type: "text"
32224
32704
  });
@@ -32235,17 +32715,17 @@ ${HARMONY_WORKFLOW_PROMPT.replace("$ARGUMENTS", "{{card}}").replace("Your agent
32235
32715
  `;
32236
32716
  if (installMode === "global") {
32237
32717
  files.push({
32238
- path: join4(GLOBAL_SKILLS_DIR, "codex", "hmy.md"),
32718
+ path: join5(GLOBAL_SKILLS_DIR, "codex", "hmy.md"),
32239
32719
  content: promptContent,
32240
32720
  type: "text"
32241
32721
  });
32242
32722
  symlinks.push({
32243
- target: join4(GLOBAL_SKILLS_DIR, "codex", "hmy.md"),
32244
- link: join4(home, ".codex", "prompts", "hmy.md")
32723
+ target: join5(GLOBAL_SKILLS_DIR, "codex", "hmy.md"),
32724
+ link: join5(home, ".codex", "prompts", "hmy.md")
32245
32725
  });
32246
32726
  } else {
32247
32727
  files.push({
32248
- path: join4(home, ".codex", "prompts", "hmy.md"),
32728
+ path: join5(home, ".codex", "prompts", "hmy.md"),
32249
32729
  content: promptContent,
32250
32730
  type: "text"
32251
32731
  });
@@ -32254,10 +32734,10 @@ ${HARMONY_WORKFLOW_PROMPT.replace("$ARGUMENTS", "{{card}}").replace("Your agent
32254
32734
  # Harmony MCP Server
32255
32735
  [mcp_servers.harmony]
32256
32736
  command = "npx"
32257
- args = ["-y", "harmony-mcp@latest", "serve"]
32737
+ args = ["-y", "@gethmy/mcp@latest", "serve"]
32258
32738
  `;
32259
32739
  files.push({
32260
- path: join4(home, ".codex", "config.toml"),
32740
+ path: join5(home, ".codex", "config.toml"),
32261
32741
  content: tomlContent,
32262
32742
  type: "toml",
32263
32743
  tomlSection: "mcp_servers.harmony"
@@ -32266,12 +32746,12 @@ args = ["-y", "harmony-mcp@latest", "serve"]
32266
32746
  }
32267
32747
  case "cursor": {
32268
32748
  files.push({
32269
- path: join4(cwd, ".cursor", "mcp.json"),
32749
+ path: join5(cwd, ".cursor", "mcp.json"),
32270
32750
  content: JSON.stringify({
32271
32751
  mcpServers: {
32272
32752
  harmony: {
32273
32753
  command: "npx",
32274
- args: ["-y", "harmony-mcp@latest", "serve"]
32754
+ args: ["-y", "@gethmy/mcp@latest", "serve"]
32275
32755
  }
32276
32756
  }
32277
32757
  }, null, 2),
@@ -32292,17 +32772,17 @@ ${HARMONY_WORKFLOW_PROMPT.replace("$ARGUMENTS", "the card reference").replace("Y
32292
32772
  `;
32293
32773
  if (installMode === "global") {
32294
32774
  files.push({
32295
- path: join4(GLOBAL_SKILLS_DIR, "cursor", "harmony.mdc"),
32775
+ path: join5(GLOBAL_SKILLS_DIR, "cursor", "harmony.mdc"),
32296
32776
  content: ruleContent,
32297
32777
  type: "text"
32298
32778
  });
32299
32779
  symlinks.push({
32300
- target: join4(GLOBAL_SKILLS_DIR, "cursor", "harmony.mdc"),
32301
- link: join4(home, ".cursor", "rules", "harmony.mdc")
32780
+ target: join5(GLOBAL_SKILLS_DIR, "cursor", "harmony.mdc"),
32781
+ link: join5(home, ".cursor", "rules", "harmony.mdc")
32302
32782
  });
32303
32783
  } else {
32304
32784
  files.push({
32305
- path: join4(cwd, ".cursor", "rules", "harmony.mdc"),
32785
+ path: join5(cwd, ".cursor", "rules", "harmony.mdc"),
32306
32786
  content: ruleContent,
32307
32787
  type: "text"
32308
32788
  });
@@ -32311,12 +32791,12 @@ ${HARMONY_WORKFLOW_PROMPT.replace("$ARGUMENTS", "the card reference").replace("Y
32311
32791
  }
32312
32792
  case "windsurf": {
32313
32793
  files.push({
32314
- path: join4(home, ".codeium", "windsurf", "mcp_config.json"),
32794
+ path: join5(home, ".codeium", "windsurf", "mcp_config.json"),
32315
32795
  content: JSON.stringify({
32316
32796
  mcpServers: {
32317
32797
  harmony: {
32318
32798
  command: "npx",
32319
- args: ["-y", "harmony-mcp@latest", "serve"],
32799
+ args: ["-y", "@gethmy/mcp@latest", "serve"],
32320
32800
  disabled: false,
32321
32801
  alwaysAllow: []
32322
32802
  }
@@ -32337,17 +32817,17 @@ ${HARMONY_WORKFLOW_PROMPT.replace("$ARGUMENTS", "the card reference").replace("Y
32337
32817
  `;
32338
32818
  if (installMode === "global") {
32339
32819
  files.push({
32340
- path: join4(GLOBAL_SKILLS_DIR, "windsurf", "harmony.md"),
32820
+ path: join5(GLOBAL_SKILLS_DIR, "windsurf", "harmony.md"),
32341
32821
  content: ruleContent,
32342
32822
  type: "text"
32343
32823
  });
32344
32824
  symlinks.push({
32345
- target: join4(GLOBAL_SKILLS_DIR, "windsurf", "harmony.md"),
32346
- link: join4(home, ".codeium", "windsurf", "rules", "harmony.md")
32825
+ target: join5(GLOBAL_SKILLS_DIR, "windsurf", "harmony.md"),
32826
+ link: join5(home, ".codeium", "windsurf", "rules", "harmony.md")
32347
32827
  });
32348
32828
  } else {
32349
32829
  files.push({
32350
- path: join4(cwd, ".windsurf", "rules", "harmony.md"),
32830
+ path: join5(cwd, ".windsurf", "rules", "harmony.md"),
32351
32831
  content: ruleContent,
32352
32832
  type: "text"
32353
32833
  });
@@ -32548,6 +33028,14 @@ async function runSetup(options = {}) {
32548
33028
  type: "text"
32549
33029
  });
32550
33030
  }
33031
+ if (!options.skipDocs) {
33032
+ const docsResult = await runDocsStep(cwd);
33033
+ if (!docsResult.skipped) {
33034
+ for (const file2 of docsResult.files) {
33035
+ allFiles.push(file2);
33036
+ }
33037
+ }
33038
+ }
32551
33039
  if (needsSkills && selectedAgents.length > 0) {
32552
33040
  for (const agentId of selectedAgents) {
32553
33041
  const { files, symlinks } = getAgentFiles(agentId, cwd, installMode);
@@ -32623,7 +33111,7 @@ async function runSetup(options = {}) {
32623
33111
  for (const symlink of allSymlinks) {
32624
33112
  try {
32625
33113
  const linkDir = dirname2(symlink.link);
32626
- if (!existsSync5(linkDir)) {
33114
+ if (!existsSync6(linkDir)) {
32627
33115
  mkdirSync4(linkDir, { recursive: true });
32628
33116
  }
32629
33117
  let linkExists = false;
@@ -32652,9 +33140,9 @@ async function runSetup(options = {}) {
32652
33140
  } else {
32653
33141
  try {
32654
33142
  await writeMcpConfigFallback(home);
32655
- console.log(` ${colors.success("✓")} ${colors.dim(formatPath(join4(home, ".claude", "settings.json"), home))} ${colors.dim("(updated)")}`);
33143
+ console.log(` ${colors.success("✓")} ${colors.dim(formatPath(join5(home, ".claude", "settings.json"), home))} ${colors.dim("(updated)")}`);
32656
33144
  } catch {
32657
- M2.warning("Could not register MCP server. Run manually: claude mcp add --transport stdio harmony -- npx -y harmony-mcp@latest serve");
33145
+ M2.warning("Could not register MCP server. Run manually: claude mcp add --transport stdio harmony -- npx -y @gethmy/mcp@latest serve");
32658
33146
  }
32659
33147
  }
32660
33148
  }
@@ -32690,7 +33178,7 @@ async function runSetup(options = {}) {
32690
33178
  console.log(` ${colors.brand("Cursor/Windsurf:")} MCP tools available automatically`);
32691
33179
  }
32692
33180
  console.log("");
32693
- console.log(` ${colors.dim("Add to new project: npx harmony-mcp setup")}`);
33181
+ console.log(` ${colors.dim("Add to new project: npx @gethmy/mcp setup")}`);
32694
33182
  console.log(` ${colors.dim("Need help? Visit https://gethmy.com/docs/mcp")}`);
32695
33183
  console.log("");
32696
33184
  }
@@ -32698,7 +33186,7 @@ async function runSetup(options = {}) {
32698
33186
  // src/cli.ts
32699
33187
  var require2 = createRequire2(import.meta.url);
32700
33188
  var { version: version2 } = require2("../package.json");
32701
- program.name("harmony-mcp").description("MCP server for Harmony Kanban board").version(version2);
33189
+ program.name("@gethmy/mcp").description("MCP server for Harmony Kanban board").version(version2);
32702
33190
  program.command("serve").description("Start the MCP server (stdio transport)").action(async () => {
32703
33191
  const server = new HarmonyMCPServer;
32704
33192
  await server.run();
@@ -32730,7 +33218,7 @@ Skills:`);
32730
33218
  }
32731
33219
  } else {
32732
33220
  console.log(" Installed: No");
32733
- console.log(" Run: npx harmony-mcp setup");
33221
+ console.log(" Run: npx @gethmy/mcp setup");
32734
33222
  }
32735
33223
  console.log(`
32736
33224
  Context:`);
@@ -32753,7 +33241,7 @@ Context:`);
32753
33241
  } else {
32754
33242
  console.log(`Status: Not configured
32755
33243
  `);
32756
- console.log("Run: npx harmony-mcp setup");
33244
+ console.log("Run: npx @gethmy/mcp setup");
32757
33245
  console.log("Get an API key at: https://gethmy.com/user/keys");
32758
33246
  }
32759
33247
  });
@@ -32766,9 +33254,9 @@ program.command("reset").description("Remove stored configuration").action(() =>
32766
33254
  });
32767
33255
  console.log("Configuration reset successfully");
32768
33256
  console.log(`
32769
- To reconfigure, run: npx harmony-mcp setup`);
33257
+ To reconfigure, run: npx @gethmy/mcp setup`);
32770
33258
  });
32771
- program.command("setup").description("Smart setup wizard for Harmony MCP (recommended)").option("-f, --force", "Overwrite existing configuration files").option("-k, --api-key <key>", "API key (skips prompt)").option("-e, --email <email>", "Your email for auto-assignment").option("-a, --agents <agents...>", "Agents to configure: claude, codex, cursor, windsurf").option("-l, --local", "Install skills locally in project directory").option("-g, --global", "Install skills globally (recommended)").option("-w, --workspace <id>", "Set workspace context").option("-p, --project <id>", "Set project context").option("--skip-context", "Skip workspace/project selection").action(async (options) => {
33259
+ program.command("setup").description("Smart setup wizard for Harmony MCP (recommended)").option("-f, --force", "Overwrite existing configuration files").option("-k, --api-key <key>", "API key (skips prompt)").option("-e, --email <email>", "Your email for auto-assignment").option("-a, --agents <agents...>", "Agents to configure: claude, codex, cursor, windsurf").option("-l, --local", "Install skills locally in project directory").option("-g, --global", "Install skills globally (recommended)").option("-w, --workspace <id>", "Set workspace context").option("-p, --project <id>", "Set project context").option("--skip-context", "Skip workspace/project selection").option("--skip-docs", "Skip project docs scaffold/verification").action(async (options) => {
32772
33260
  await runSetup({
32773
33261
  force: options.force,
32774
33262
  apiKey: options.apiKey,
@@ -32777,7 +33265,8 @@ program.command("setup").description("Smart setup wizard for Harmony MCP (recomm
32777
33265
  installMode: options.global ? "global" : options.local ? "local" : undefined,
32778
33266
  workspaceId: options.workspace,
32779
33267
  projectId: options.project,
32780
- skipContext: options.skipContext
33268
+ skipContext: options.skipContext,
33269
+ skipDocs: options.skipDocs
32781
33270
  });
32782
33271
  });
32783
33272
  program.parse();