@ezmodo/mcp-server 0.13.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 (98) hide show
  1. package/README.md +305 -0
  2. package/config/development.js +20 -0
  3. package/config/endpoint-map.js +351 -0
  4. package/config/index.js +34 -0
  5. package/config/production.js +18 -0
  6. package/config/staging.js +18 -0
  7. package/handlers/access.js +141 -0
  8. package/handlers/activity.js +112 -0
  9. package/handlers/agents.js +95 -0
  10. package/handlers/ai-intelligence.js +55 -0
  11. package/handlers/attachments.js +30 -0
  12. package/handlers/catalogs.js +169 -0
  13. package/handlers/components.js +282 -0
  14. package/handlers/context-manifest.js +1150 -0
  15. package/handlers/decisions.js +114 -0
  16. package/handlers/designs.js +118 -0
  17. package/handlers/documents.js +227 -0
  18. package/handlers/entities.js +95 -0
  19. package/handlers/epics.js +190 -0
  20. package/handlers/facts.js +62 -0
  21. package/handlers/feature-flags.js +142 -0
  22. package/handlers/features.js +137 -0
  23. package/handlers/folders.js +127 -0
  24. package/handlers/git-context.js +917 -0
  25. package/handlers/github.js +72 -0
  26. package/handlers/graph.js +23 -0
  27. package/handlers/index.js +205 -0
  28. package/handlers/links.js +156 -0
  29. package/handlers/milestones.js +131 -0
  30. package/handlers/organizations.js +14 -0
  31. package/handlers/projects.js +122 -0
  32. package/handlers/recurring-tasks.js +33 -0
  33. package/handlers/tags.js +124 -0
  34. package/handlers/tasks.js +561 -0
  35. package/handlers/testing.js +116 -0
  36. package/handlers/todos.js +43 -0
  37. package/handlers/watchers.js +54 -0
  38. package/handlers/work-templates.js +32 -0
  39. package/index.js +175 -0
  40. package/lib/active-session.js +86 -0
  41. package/lib/auto-assign.js +93 -0
  42. package/lib/autolink.js +176 -0
  43. package/lib/changed-files.js +22 -0
  44. package/lib/env.js +45 -0
  45. package/lib/git-helpers.js +553 -0
  46. package/lib/git-utils.js +73 -0
  47. package/lib/http-client.js +164 -0
  48. package/lib/links-at-create.js +94 -0
  49. package/lib/local-cache.js +140 -0
  50. package/lib/logger.js +109 -0
  51. package/lib/manifest-loader.js +182 -0
  52. package/lib/manifest-query.js +686 -0
  53. package/lib/repo-config-dir.js +118 -0
  54. package/lib/version.js +10 -0
  55. package/lib/web-url.js +69 -0
  56. package/lib/worktree-tools.js +950 -0
  57. package/package.json +62 -0
  58. package/prompts/ai-workflow-automation.js +96 -0
  59. package/prompts/index.js +39 -0
  60. package/prompts/zephly-usage-guide-content.txt +631 -0
  61. package/prompts/zephly-usage-guide.js +119 -0
  62. package/tools/access-entity-types.js +28 -0
  63. package/tools/access.js +152 -0
  64. package/tools/activity.js +38 -0
  65. package/tools/agents.js +208 -0
  66. package/tools/ai-intelligence.js +111 -0
  67. package/tools/attachments.js +92 -0
  68. package/tools/catalogs.js +341 -0
  69. package/tools/components.js +249 -0
  70. package/tools/context-manifest.js +236 -0
  71. package/tools/decisions.js +168 -0
  72. package/tools/designs.js +222 -0
  73. package/tools/documents.js +287 -0
  74. package/tools/entities.js +223 -0
  75. package/tools/epics.js +267 -0
  76. package/tools/facts.js +70 -0
  77. package/tools/feature-flags.js +300 -0
  78. package/tools/features.js +246 -0
  79. package/tools/folders.js +122 -0
  80. package/tools/git-context.js +109 -0
  81. package/tools/github.js +172 -0
  82. package/tools/graph.js +70 -0
  83. package/tools/index.js +77 -0
  84. package/tools/link-params.js +93 -0
  85. package/tools/linkable-types.js +36 -0
  86. package/tools/links.js +199 -0
  87. package/tools/milestones.js +176 -0
  88. package/tools/organizations.js +23 -0
  89. package/tools/projects.js +172 -0
  90. package/tools/recurring-tasks.js +115 -0
  91. package/tools/tags.js +219 -0
  92. package/tools/task-item-schema.js +57 -0
  93. package/tools/task-type.js +33 -0
  94. package/tools/tasks.js +680 -0
  95. package/tools/testing.js +344 -0
  96. package/tools/todos.js +69 -0
  97. package/tools/watchers.js +81 -0
  98. package/tools/work-templates.js +96 -0
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Helpers for locating an EzModo project's per-repo config directory.
3
+ *
4
+ * After the EzModo rebrand the directory is `.ezmodo/` (was `.zephly/`).
5
+ * Existing checkouts still have `.zephly/` so all readers must dual-check:
6
+ * the new directory wins; the legacy directory is the fallback.
7
+ *
8
+ * New writes always go to `.ezmodo/`. Use `ezmodo migrate-config` to
9
+ * move an existing `.zephly/` directory in place.
10
+ */
11
+
12
+ import { existsSync, statSync } from 'fs';
13
+ import fsp from 'fs/promises';
14
+ import { dirname, join } from 'path';
15
+
16
+ export const CURRENT_REPO_CONFIG_DIR = '.ezmodo';
17
+ export const LEGACY_REPO_CONFIG_DIR = '.zephly';
18
+ export const REPO_CONFIG_FILENAME = 'config.json';
19
+
20
+ const PROBE_ORDER = [CURRENT_REPO_CONFIG_DIR, LEGACY_REPO_CONFIG_DIR];
21
+
22
+ let warned = false;
23
+
24
+ /**
25
+ * Synchronously locate the first existing project config file walking up
26
+ * from `startDir`. Tries `.ezmodo/config.json` first, then `.zephly/`.
27
+ * Returns null if neither is found.
28
+ */
29
+ export function findRepoConfigPathSync(startDir) {
30
+ let dir = startDir;
31
+ while (true) {
32
+ for (const name of PROBE_ORDER) {
33
+ const candidate = join(dir, name, REPO_CONFIG_FILENAME);
34
+ if (existsSync(candidate)) {
35
+ if (name === LEGACY_REPO_CONFIG_DIR) maybeWarnLegacy(candidate);
36
+ return candidate;
37
+ }
38
+ }
39
+ const parent = dirname(dir);
40
+ if (parent === dir) return null;
41
+ dir = parent;
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Async variant of findRepoConfigPathSync — preferred for MCP handlers
47
+ * that already operate on promises.
48
+ */
49
+ export async function findRepoConfigPath(startDir) {
50
+ let dir = startDir;
51
+ while (true) {
52
+ for (const name of PROBE_ORDER) {
53
+ const candidate = join(dir, name, REPO_CONFIG_FILENAME);
54
+ if (await fileExists(candidate)) {
55
+ if (name === LEGACY_REPO_CONFIG_DIR) maybeWarnLegacy(candidate);
56
+ return candidate;
57
+ }
58
+ }
59
+ const parent = dirname(dir);
60
+ if (parent === dir) return null;
61
+ dir = parent;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Returns the existing config directory path (e.g. `/repo/.ezmodo` or
67
+ * `/repo/.zephly`) walking up from `startDir`, or null if neither exists.
68
+ */
69
+ export async function findRepoConfigDir(startDir) {
70
+ const cfg = await findRepoConfigPath(startDir);
71
+ return cfg ? dirname(cfg) : null;
72
+ }
73
+
74
+ /**
75
+ * Returns the directory name new writes should target. Always the current
76
+ * name (`.ezmodo`) — even if a legacy directory still exists in the same
77
+ * repo. Migration to a single directory is handled by `ezmodo migrate-config`.
78
+ */
79
+ export function getWriteRepoConfigDirName() {
80
+ return CURRENT_REPO_CONFIG_DIR;
81
+ }
82
+
83
+ /**
84
+ * Returns true if the given directory holds either a current or legacy
85
+ * config dir. Sync because callers commonly need it during sync init.
86
+ */
87
+ export function hasAnyRepoConfigDirSync(repoRoot) {
88
+ for (const name of PROBE_ORDER) {
89
+ try {
90
+ if (statSync(join(repoRoot, name)).isDirectory()) return true;
91
+ } catch {
92
+ // not a directory — try next
93
+ }
94
+ }
95
+ return false;
96
+ }
97
+
98
+ async function fileExists(p) {
99
+ try {
100
+ await fsp.access(p);
101
+ return true;
102
+ } catch {
103
+ return false;
104
+ }
105
+ }
106
+
107
+ function maybeWarnLegacy(path) {
108
+ if (warned) return;
109
+ warned = true;
110
+ process.stderr.write(
111
+ `⚠️ Using legacy ${LEGACY_REPO_CONFIG_DIR}/ directory at ${path}. ` +
112
+ `Run \`ezmodo migrate-config\` to move it to ${CURRENT_REPO_CONFIG_DIR}/.\n`
113
+ );
114
+ }
115
+
116
+ export function _resetLegacyWarningForTests() {
117
+ warned = false;
118
+ }
package/lib/version.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * MCP Server version — single source of truth.
3
+ *
4
+ * Kept as a plain constant so the CLI binary (compiled with Bun) can
5
+ * bundle it without needing createRequire() to read package.json at runtime,
6
+ * which fails inside the /$bunfs/ virtual filesystem.
7
+ *
8
+ * Update this when bumping the version in package.json.
9
+ */
10
+ export const MCP_VERSION = '0.13.0';
package/lib/web-url.js ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Web URL Builder
3
+ * Constructs web app URLs for entities using cached project context.
4
+ */
5
+
6
+ import { readConfig } from './local-cache.js';
7
+ import { CONFIG } from '../config/index.js';
8
+
9
+ /**
10
+ * Get the base web URL and routing slugs from cached config.
11
+ * Returns null if config is unavailable.
12
+ */
13
+ async function getRoutingContext() {
14
+ const config = await readConfig();
15
+ if (!config?.orgSlug || !config?.projectSlug) return null;
16
+ return {
17
+ baseUrl: CONFIG.webUrl,
18
+ orgSlug: config.orgSlug,
19
+ projectSlug: config.projectSlug,
20
+ };
21
+ }
22
+
23
+ /**
24
+ * Build a web URL for a task.
25
+ * @param {number} taskNumber
26
+ * @returns {Promise<string|null>}
27
+ */
28
+ export async function buildTaskUrl(taskNumber) {
29
+ if (!taskNumber) return null;
30
+ const ctx = await getRoutingContext();
31
+ if (!ctx) return null;
32
+ return `${ctx.baseUrl}/${ctx.orgSlug}/projects/${ctx.projectSlug}/tasks/${taskNumber}`;
33
+ }
34
+
35
+ /**
36
+ * Build a web URL for an epic.
37
+ * @param {number} epicNumber
38
+ * @returns {Promise<string|null>}
39
+ */
40
+ export async function buildEpicUrl(epicNumber) {
41
+ if (!epicNumber) return null;
42
+ const ctx = await getRoutingContext();
43
+ if (!ctx) return null;
44
+ return `${ctx.baseUrl}/${ctx.orgSlug}/projects/${ctx.projectSlug}/epics/${epicNumber}`;
45
+ }
46
+
47
+ /**
48
+ * Build a web URL for a goal.
49
+ * @param {string} goalId
50
+ * @returns {Promise<string|null>}
51
+ */
52
+ export async function buildGoalUrl(goalId) {
53
+ if (!goalId) return null;
54
+ const ctx = await getRoutingContext();
55
+ if (!ctx) return null;
56
+ return `${ctx.baseUrl}/${ctx.orgSlug}/goals/${goalId}`;
57
+ }
58
+
59
+ /**
60
+ * Build a web URL for a milestone.
61
+ * @param {string} milestoneSlug
62
+ * @returns {Promise<string|null>}
63
+ */
64
+ export async function buildMilestoneUrl(milestoneSlug) {
65
+ if (!milestoneSlug) return null;
66
+ const ctx = await getRoutingContext();
67
+ if (!ctx) return null;
68
+ return `${ctx.baseUrl}/${ctx.orgSlug}/projects/${ctx.projectSlug}/milestones/${milestoneSlug}`;
69
+ }