@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,109 @@
1
+ /**
2
+ * Git Context Tools
3
+ * MCP tools for git repository detection and project context management
4
+ *
5
+ * Project-First Hierarchy: Projects are the primary container.
6
+ * Use these tools to auto-detect which Zephly project you're working on.
7
+ */
8
+
9
+ export const GIT_CONTEXT_TOOLS = [
10
+ {
11
+ name: 'detect_git_repository',
12
+ description: 'Detect if current directory is a git repository and match it ' +
13
+ 'against accessible ezmodo projects by git URL. Returns matching ' +
14
+ 'projects with confidence scores, and for each match the git ' +
15
+ 'repositories linked to that project — including the `repoId` that ' +
16
+ 'manage_pull_request requires. Use this to auto-suggest which ezmodo ' +
17
+ 'project corresponds to a git repo, and to get from a checkout to a ' +
18
+ 'repoId in one call.',
19
+ inputSchema: {
20
+ type: 'object',
21
+ properties: {
22
+ workingDirectory: {
23
+ type: 'string',
24
+ description: 'Directory to check for git repository (defaults to current directory)',
25
+ },
26
+ },
27
+ },
28
+ },
29
+ {
30
+ name: 'get_current_project_context',
31
+ description: 'Detect and read existing .ezmodo/config.json (or legacy ' +
32
+ '.zephly/config.json) from current directory or parent directories. ' +
33
+ 'Returns project context if found, ' +
34
+ 'or null if not configured. Use this BEFORE other MCP tools to ' +
35
+ 'auto-detect which project you\'re working on. Project-First: ' +
36
+ 'Returns projectId, components, and tags that can be used ' +
37
+ 'directly with task/epic creation tools. Caches tags and ' +
38
+ 'components locally with 1-day TTL for fast access. ' +
39
+ 'Also returns `projectType` and `terminology` — the words this project ' +
40
+ 'uses for epics, tasks and components and for their statuses (E-107). ' +
41
+ 'Write anything a person reads in those words: a marketing project calls ' +
42
+ 'an epic a "Campaign" and a component a "Channel". Keep the API field ' +
43
+ 'names (epicId, taskId) as they are. A null `terminology` means plain ' +
44
+ 'English, not an error.',
45
+ inputSchema: {
46
+ type: 'object',
47
+ properties: {
48
+ workingDirectory: {
49
+ type: 'string',
50
+ description: 'Starting directory to search from (defaults to current directory)',
51
+ },
52
+ },
53
+ },
54
+ },
55
+ {
56
+ name: 'initialize_project_context',
57
+ description: 'Create .ezmodo/config.json for project context. Helps AI ' +
58
+ 'agents understand which ezmodo project they\'re working on. ' +
59
+ 'Supports single projects and monorepos. Usage: (1) Single ' +
60
+ 'project: provide projectId + organizationId. (2) Monorepo: ' +
61
+ 'provide organizationId + monorepoProjects array (no projectId ' +
62
+ 'needed). (3) Interactive: omit projectId to get available ' +
63
+ 'projects list.',
64
+ inputSchema: {
65
+ type: 'object',
66
+ properties: {
67
+ projectId: {
68
+ type: 'string',
69
+ description: 'Project ID for single-project setup. Not needed ' +
70
+ 'for monorepo setup (use monorepoProjects instead).',
71
+ },
72
+ organizationId: {
73
+ type: 'string',
74
+ description: 'Organization ID (required for both single project and monorepo setup)',
75
+ },
76
+ workingDirectory: {
77
+ type: 'string',
78
+ description: 'Directory to create .ezmodo/config.json in (defaults to current directory). If a legacy .zephly/ directory already exists in the repo, the existing location is reused for writes.',
79
+ },
80
+ addToGitignore: {
81
+ type: 'boolean',
82
+ description: 'Whether to add the config directory (.ezmodo/ or legacy .zephly/) to .gitignore (default: true)',
83
+ default: true,
84
+ },
85
+ addClaudeMd: {
86
+ type: 'boolean',
87
+ description: 'Whether to generate/update CLAUDE.md with work tracking instructions (default: true)',
88
+ default: true,
89
+ },
90
+ monorepoProjects: {
91
+ type: 'array',
92
+ description: 'For monorepos: Array of {projectId, name, path} ' +
93
+ 'objects mapping subdirectories to projects. When provided ' +
94
+ 'with organizationId (without projectId), writes monorepo ' +
95
+ 'config directly.',
96
+ items: {
97
+ type: 'object',
98
+ properties: {
99
+ projectId: { type: 'string' },
100
+ name: { type: 'string' },
101
+ path: { type: 'string' },
102
+ },
103
+ required: ['projectId', 'name', 'path'],
104
+ },
105
+ },
106
+ },
107
+ },
108
+ },
109
+ ];
@@ -0,0 +1,172 @@
1
+ /**
2
+ * GitHub Tools
3
+ * MCP tools for managing GitHub pull requests and integration
4
+ *
5
+ * Consolidated: manage_pull_request (create/update/merge/close/request_review)
6
+ */
7
+
8
+ export const GITHUB_TOOLS = [
9
+ {
10
+ name: 'list_repositories',
11
+ description: 'List the git repositories linked to a project, each with the ' +
12
+ '`repoId` that every manage_pull_request action requires. Call this first ' +
13
+ 'when you need a repoId — it is the only way to get one. The id is ' +
14
+ '"installationId:owner/repo" and the installation id is not derivable ' +
15
+ 'from anywhere else: no other ezmodo tool exposes it, and GitHub only ' +
16
+ 'answers for it with an app JWT or an app-authorized token. ' +
17
+ '`installationStatus` tells a live link from one whose GitHub App was ' +
18
+ 'uninstalled or suspended; a repoId from a non-active installation will ' +
19
+ 'parse and then fail at the GitHub call.',
20
+ inputSchema: {
21
+ type: 'object',
22
+ properties: {
23
+ projectId: {
24
+ type: 'string',
25
+ description: 'Project whose linked repositories to list (required)',
26
+ },
27
+ },
28
+ required: ['projectId'],
29
+ },
30
+ },
31
+ {
32
+ name: 'manage_pull_request',
33
+ description: 'Create, update, merge, close, or request review on GitHub pull requests. ' +
34
+ 'Also suggests reviewers ("suggest_reviewers") ranked from CODEOWNERS and file history — ' +
35
+ 'that action only SUGGESTS; use "request_review" to actually request them.',
36
+ inputSchema: {
37
+ type: 'object',
38
+ properties: {
39
+ action: {
40
+ type: 'string',
41
+ enum: ['create', 'update', 'merge', 'close', 'request_review', 'suggest_reviewers'],
42
+ description: 'Action to perform',
43
+ },
44
+ // --- Identifiers (used by all actions) ---
45
+ repoId: {
46
+ type: 'string',
47
+ description: 'Repository ID in format "installationId:owner/repo" (e.g., "12345678:acme-corp/backend"). ' +
48
+ 'Get it from `list_repositories` (or from the `repositories` on a `detect_git_repository` match) — ' +
49
+ 'the installation id is not derivable from the repo, and GitHub will not tell you it.',
50
+ },
51
+ number: {
52
+ type: 'number',
53
+ description: 'PR number (required for update, merge, close, request_review)',
54
+ },
55
+ // --- Create fields ---
56
+ title: {
57
+ type: 'string',
58
+ description: 'PR title (required for create, optional for update)',
59
+ },
60
+ body: {
61
+ type: 'string',
62
+ description: 'PR description in markdown format. Used by create and update.',
63
+ },
64
+ headBranch: {
65
+ type: 'string',
66
+ description: 'Source branch name without refs/heads/ (required for create)',
67
+ },
68
+ baseBranch: {
69
+ type: 'string',
70
+ description: 'Target branch name e.g. "main" (required for create, optional for update)',
71
+ },
72
+ draft: {
73
+ type: 'boolean',
74
+ description: 'Create as draft PR (create only, default: false)',
75
+ },
76
+ maintainerCanModify: {
77
+ type: 'boolean',
78
+ description: 'Allow maintainers to modify the PR. Used by create and update (default: true).',
79
+ },
80
+ assignees: {
81
+ type: 'array',
82
+ items: { type: 'string' },
83
+ description: 'GitHub usernames to assign to the PR (create only)',
84
+ },
85
+ reviewers: {
86
+ type: 'array',
87
+ items: { type: 'string' },
88
+ description: 'GitHub usernames to request review from. Used by create and request_review.',
89
+ },
90
+ teamReviewers: {
91
+ type: 'array',
92
+ items: { type: 'string' },
93
+ description: 'GitHub team slugs to request review from (e.g., "backend-team"). Used by create and request_review.',
94
+ },
95
+ labels: {
96
+ type: 'array',
97
+ items: { type: 'string' },
98
+ description: 'Labels to add to the PR (create only)',
99
+ },
100
+ milestoneNumber: {
101
+ type: 'number',
102
+ description: 'Milestone number to associate with the PR (create only)',
103
+ },
104
+ // --- Work linkage (E-233) ---
105
+ taskId: {
106
+ type: 'string',
107
+ description: 'ezmodo task this PR implements. Used by create and update. ' +
108
+ 'You already know which task you are on, so passing it here records the ' +
109
+ 'strongest possible link — better than relying on the resolver to infer ' +
110
+ 'one from the branch name or the PR body afterwards.',
111
+ },
112
+ epicId: {
113
+ type: 'string',
114
+ description: 'ezmodo epic this PR advances. Used by create and update. ' +
115
+ 'Usually unnecessary when taskId is set and the task already belongs to the epic.',
116
+ },
117
+ // --- Update-only fields ---
118
+ state: {
119
+ type: 'string',
120
+ enum: ['open', 'closed'],
121
+ description: 'Update PR state (update only)',
122
+ },
123
+ // --- Merge fields ---
124
+ mergeMethod: {
125
+ type: 'string',
126
+ enum: ['merge', 'squash', 'rebase'],
127
+ description: 'Merge method to use (merge only, default: "merge")',
128
+ },
129
+ commitTitle: {
130
+ type: 'string',
131
+ description: 'Custom merge commit title for squash/merge (merge only)',
132
+ },
133
+ commitMessage: {
134
+ type: 'string',
135
+ description: 'Custom merge commit message body (merge only)',
136
+ },
137
+ sha: {
138
+ type: 'string',
139
+ description: 'Head SHA to validate before merge — prevents race conditions (merge only)',
140
+ },
141
+ // --- suggest_reviewers fields ---
142
+ projectId: {
143
+ type: 'string',
144
+ description: 'Project ID — required by every action. It scopes access, and on create/update it is ' +
145
+ 'what the PR is stored and linked against.',
146
+ },
147
+ changedPaths: {
148
+ type: 'array',
149
+ items: { type: 'string' },
150
+ description: 'Repo-relative paths this change touches (REQUIRED for suggest_reviewers). ' +
151
+ 'Without them there is nothing to reason from; guessing "the whole repo" would just ' +
152
+ 'suggest whoever commits most. Capped at 50 — file history is one API call per path.',
153
+ },
154
+ excludeLogins: {
155
+ type: 'array',
156
+ items: { type: 'string' },
157
+ description: 'Logins to never suggest (suggest_reviewers only). Passing `number` also ' +
158
+ 'excludes the PR author automatically.',
159
+ },
160
+ limit: {
161
+ type: 'number',
162
+ description: 'Maximum suggestions to return (suggest_reviewers only, default 5, max 25). ' +
163
+ 'The response sets `truncated` when the list was capped.',
164
+ },
165
+ },
166
+ // Every action's server handler rejects a missing projectId, so the schema
167
+ // says so too. It used to be documented as "required for suggest_reviewers",
168
+ // which read as optional everywhere else and was not.
169
+ required: ['action', 'repoId', 'projectId'],
170
+ },
171
+ },
172
+ ];
package/tools/graph.js ADDED
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Graph Tools
3
+ *
4
+ * The living navigation graph (E-209): a browsable, cross-entity projection
5
+ * over the entity_links link graph (relates_to / blocked_by). One read lets an
6
+ * agent walk "what connects to this" from any node — features, epics, tasks,
7
+ * goals, milestones, components, designs, decisions, documents, test suites,
8
+ * feature flags — instead of issuing N separate list_links calls and stitching
9
+ * the results together.
10
+ *
11
+ * It is a READ VIEW, not a hierarchy: features/goals stay org-level; nothing is
12
+ * re-parented. Backed by the graph projection endpoint (proxies GET-equivalent
13
+ * server-side traversal, org-scoped + RLS).
14
+ */
15
+
16
+ import { LINKABLE_TYPES } from './linkable-types.js';
17
+
18
+ export const GRAPH_TOOLS = [
19
+ {
20
+ name: 'get_graph',
21
+ description:
22
+ 'Walk the compendium navigation graph outward from a root node and return ' +
23
+ 'the hydrated neighborhood as nodes + edges. Nodes carry lightweight ' +
24
+ 'display fields (type, id, title, status, projectId, kind, number, depth); ' +
25
+ 'edges are the entity_links relations (relates_to / blocked_by) between ' +
26
+ 'them, direction preserved. Use it to answer "what connects to this?" in ' +
27
+ 'one call. Traversal is a breadth-first walk bounded by depth and a node ' +
28
+ 'cap (truncated:true in the response means the cap was hit). A root ' +
29
+ '(rootType + rootId) is REQUIRED — the graph is always explored from a ' +
30
+ 'node, not listed wholesale.',
31
+ inputSchema: {
32
+ type: 'object',
33
+ properties: {
34
+ projectId: {
35
+ type: 'string',
36
+ description:
37
+ 'Project that scopes access (authorizes the caller and sets the org ' +
38
+ 'scope). Required. The graph itself can still reach org-level nodes ' +
39
+ '(features/goals) under scope "org".',
40
+ },
41
+ rootType: {
42
+ type: 'string',
43
+ enum: LINKABLE_TYPES,
44
+ description: 'Type of the node to explore from (required).',
45
+ },
46
+ rootId: {
47
+ type: 'string',
48
+ description: 'ID of the root node to explore from (required).',
49
+ },
50
+ scope: {
51
+ type: 'string',
52
+ enum: ['org', 'project'],
53
+ description:
54
+ 'org (default): keep every reachable in-org node. project: drop nodes ' +
55
+ 'that belong to a different project than the root (org-level nodes and ' +
56
+ 'the root are always kept).',
57
+ },
58
+ depth: {
59
+ type: 'number',
60
+ description: 'Hops from the root to traverse. Default 2, max 5.',
61
+ },
62
+ maxNodes: {
63
+ type: 'number',
64
+ description: 'Cap on the number of nodes returned. Default 150, max 500.',
65
+ },
66
+ },
67
+ required: ['projectId', 'rootType', 'rootId'],
68
+ },
69
+ },
70
+ ];
package/tools/index.js ADDED
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Tool Definitions Index
3
+ * Aggregates all MCP tool definitions
4
+ *
5
+ * Consolidated tool set (~40 tools, down from 116):
6
+ * - One "manage_*" tool per entity (mutations with action param)
7
+ * - One "get_*" or "list_*" tool per entity (reads)
8
+ * - One unified "get_context" tool (replaces 8 context/graph tools)
9
+ */
10
+
11
+ import { ORGANIZATION_TOOLS } from './organizations.js';
12
+ import { PROJECT_TOOLS } from './projects.js';
13
+ import { COMPONENT_TOOLS } from './components.js';
14
+ import { EPIC_TOOLS } from './epics.js';
15
+ import { MILESTONE_TOOLS } from './milestones.js';
16
+ import { FEATURE_TOOLS } from './features.js';
17
+ import { DECISION_TOOLS } from './decisions.js';
18
+ import { DESIGN_TOOLS } from './designs.js';
19
+ import { CATALOG_TOOLS } from './catalogs.js';
20
+ import { FEATURE_FLAG_TOOLS } from './feature-flags.js';
21
+ import { TASK_TOOLS } from './tasks.js';
22
+ import { DOCUMENT_TOOLS } from './documents.js';
23
+ import { FOLDER_TOOLS } from './folders.js';
24
+ import { ATTACHMENT_TOOLS } from './attachments.js';
25
+ import { ENTITY_TOOLS } from './entities.js';
26
+ import { LINK_TOOLS } from './links.js';
27
+ import { ACCESS_TOOLS } from './access.js';
28
+ import { GRAPH_TOOLS } from './graph.js';
29
+ import { TAG_TOOLS } from './tags.js';
30
+ import { WATCHER_TOOLS } from './watchers.js';
31
+ import { AI_INTELLIGENCE_TOOLS } from './ai-intelligence.js';
32
+ import { GIT_CONTEXT_TOOLS } from './git-context.js';
33
+ import { WORKTREE_TOOLS } from '../lib/worktree-tools.js';
34
+ import { GITHUB_TOOLS } from './github.js';
35
+ import { TODO_TOOLS } from './todos.js';
36
+ import { TESTING_TOOLS } from './testing.js';
37
+ import { CONTEXT_MANIFEST_TOOLS } from './context-manifest.js';
38
+ import { ACTIVITY_TOOLS } from './activity.js';
39
+ import { FACT_TOOLS } from './facts.js';
40
+ import { AGENT_TOOLS } from './agents.js';
41
+ import { RECURRING_TASK_TOOLS } from './recurring-tasks.js';
42
+ import { WORK_TEMPLATE_TOOLS } from './work-templates.js';
43
+
44
+ export const TOOLS = [
45
+ ...ORGANIZATION_TOOLS,
46
+ ...PROJECT_TOOLS,
47
+ ...COMPONENT_TOOLS,
48
+ ...EPIC_TOOLS,
49
+ ...MILESTONE_TOOLS,
50
+ ...FEATURE_TOOLS,
51
+ ...DECISION_TOOLS,
52
+ ...DESIGN_TOOLS,
53
+ ...CATALOG_TOOLS,
54
+ ...FEATURE_FLAG_TOOLS,
55
+ ...TASK_TOOLS,
56
+ ...RECURRING_TASK_TOOLS,
57
+ ...WORK_TEMPLATE_TOOLS,
58
+ ...DOCUMENT_TOOLS,
59
+ ...FOLDER_TOOLS,
60
+ ...ATTACHMENT_TOOLS,
61
+ ...ENTITY_TOOLS,
62
+ ...LINK_TOOLS,
63
+ ...ACCESS_TOOLS,
64
+ ...GRAPH_TOOLS,
65
+ ...TAG_TOOLS,
66
+ ...WATCHER_TOOLS,
67
+ ...AI_INTELLIGENCE_TOOLS,
68
+ ...GIT_CONTEXT_TOOLS,
69
+ ...WORKTREE_TOOLS,
70
+ ...GITHUB_TOOLS,
71
+ ...TODO_TOOLS,
72
+ ...TESTING_TOOLS,
73
+ ...CONTEXT_MANIFEST_TOOLS,
74
+ ...ACTIVITY_TOOLS,
75
+ ...FACT_TOOLS,
76
+ ...AGENT_TOOLS,
77
+ ];
@@ -0,0 +1,93 @@
1
+ /**
2
+ * The reusable link-shaped tool parameters, declared once.
3
+ *
4
+ * Two problems these exist to fix (E-225):
5
+ *
6
+ * 1. No creation tool could attach a link at CREATE time. An agent had to
7
+ * create the entity, then remember a second `manage_link` call — which it
8
+ * usually didn't, so work landed unlinked from the feature/flag/goal it
9
+ * belonged to. `LINKS_ARRAY_SCHEMA` gives every `create` action a `links`
10
+ * param so the graph edge is born with the entity.
11
+ *
12
+ * 2. Six tool files inlined a stale 4-type `['task','epic','project','document']`
13
+ * enum on addRelatedItem/removeRelatedItem that predated LINKABLE_TYPES, so
14
+ * a task could not be given a related design or decision except through
15
+ * `manage_link`. `RELATED_ITEM_SCHEMA` is the drop-in replacement.
16
+ *
17
+ * Everything here reuses the shared `LINKABLE_TYPES` array BY REFERENCE — the
18
+ * `link-params` test asserts identity, not equality, so a copy that happens to
19
+ * agree today cannot silently drift tomorrow.
20
+ */
21
+
22
+ import { LINKABLE_TYPES } from './linkable-types.js';
23
+
24
+ /** Link semantics an agent may request. Mirrors links.LinkType in the Go API. */
25
+ export const LINK_TYPES = ['relates_to', 'blocked_by'];
26
+
27
+ /**
28
+ * One link to attach, as used inside `links: [...]`. Mirrors the node shape
29
+ * already carried by manage_feature's apply_init (`links:[{targetType,targetId}]`).
30
+ */
31
+ export const LINK_ITEM_SCHEMA = {
32
+ type: 'object',
33
+ properties: {
34
+ targetType: {
35
+ type: 'string',
36
+ enum: LINKABLE_TYPES,
37
+ description: 'Type of the entity to link to (required)',
38
+ },
39
+ targetId: {
40
+ type: 'string',
41
+ description: 'ID of the entity to link to (required)',
42
+ },
43
+ linkType: {
44
+ type: 'string',
45
+ enum: LINK_TYPES,
46
+ default: 'relates_to',
47
+ description:
48
+ 'relates_to: soft cross-entity link (any combo, the default). ' +
49
+ 'blocked_by: source is blocked by target (only task→task or epic→epic).',
50
+ },
51
+ },
52
+ required: ['targetType', 'targetId'],
53
+ };
54
+
55
+ /**
56
+ * The `links` array parameter. Attach on create so the entity is born
57
+ * connected — no follow-up manage_link round trip, and no orphan work.
58
+ *
59
+ * Best-effort by contract: a link that fails is reported back in
60
+ * `links.failed` on the response, it never fails the entity create.
61
+ */
62
+ export const LINKS_ARRAY_SCHEMA = {
63
+ type: 'array',
64
+ items: LINK_ITEM_SCHEMA,
65
+ description:
66
+ 'Links to attach at creation time, e.g. the feature this work advances, ' +
67
+ 'the goal it serves, or the document it implements: ' +
68
+ '[{ targetType: "feature", targetId: "abc" }]. Prefer this over a ' +
69
+ 'follow-up manage_link call — work that is not linked when it is created ' +
70
+ 'usually never gets linked. Applied best-effort: failures are returned in ' +
71
+ 'the response\'s `links.failed` and never fail the create.',
72
+ };
73
+
74
+ /**
75
+ * A single cross-entity related item — the shape used by the legacy
76
+ * addRelatedItem / removeRelatedItem update params. Kept working, but
77
+ * deprecated in favour of `addLinks` / `removeLinks`.
78
+ */
79
+ export const RELATED_ITEM_SCHEMA = {
80
+ type: 'object',
81
+ properties: {
82
+ type: {
83
+ type: 'string',
84
+ enum: LINKABLE_TYPES,
85
+ description: 'Type of the related entity',
86
+ },
87
+ id: {
88
+ type: 'string',
89
+ description: 'ID of the related entity',
90
+ },
91
+ },
92
+ required: ['type', 'id'],
93
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The entity types a link can point at, declared once.
3
+ *
4
+ * This mirrors `links.allEntityTypes` in the Go API (api/internal/core/links/
5
+ * service.go), which is the authority — an enum here that the API rejects just
6
+ * turns a clear tool-schema error into a confusing 400, and an enum here that
7
+ * is MISSING a type the API accepts hides a capability from agents entirely.
8
+ *
9
+ * Before this, six tool files each spelled the list out inline and every one of
10
+ * them was frozen at the set of types that existed the day it was written:
11
+ * decisions and designs stopped at `project`, features knew about design and
12
+ * test_suite but not feature_flag or catalog, links.js knew feature_flag but
13
+ * not milestone or goal. Adding a linkable type meant editing six lists and
14
+ * forgetting one was invisible until an agent tried it.
15
+ *
16
+ * Adding a type: add it in the Go `allEntityTypes` list, then here.
17
+ */
18
+ export const LINKABLE_TYPES = [
19
+ 'task',
20
+ 'epic',
21
+ 'project',
22
+ 'document',
23
+ 'component',
24
+ 'feature',
25
+ 'decision',
26
+ 'design',
27
+ 'test_suite',
28
+ 'feature_flag',
29
+ 'catalog',
30
+ 'catalog_item',
31
+ 'milestone',
32
+ 'goal',
33
+ // One row of git_pull_requests (E-233). A PR is linked to the work it
34
+ // implements, rather than the PR table carrying task/epic columns.
35
+ 'pull_request',
36
+ ];