@projitive/mcp 2.0.3 → 2.0.4

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 (40) hide show
  1. package/output/package.json +8 -2
  2. package/output/source/common/artifacts.js +1 -1
  3. package/output/source/common/artifacts.test.js +11 -11
  4. package/output/source/common/errors.js +19 -19
  5. package/output/source/common/files.js +11 -11
  6. package/output/source/common/files.test.js +14 -14
  7. package/output/source/common/index.js +10 -10
  8. package/output/source/common/linter.js +27 -27
  9. package/output/source/common/linter.test.js +9 -9
  10. package/output/source/common/markdown.js +3 -3
  11. package/output/source/common/markdown.test.js +15 -15
  12. package/output/source/common/response.js +74 -74
  13. package/output/source/common/response.test.js +30 -30
  14. package/output/source/common/store.js +40 -40
  15. package/output/source/common/store.test.js +72 -72
  16. package/output/source/common/types.js +3 -3
  17. package/output/source/common/utils.js +8 -8
  18. package/output/source/index.js +16 -16
  19. package/output/source/index.test.js +64 -64
  20. package/output/source/prompts/index.js +3 -3
  21. package/output/source/prompts/quickStart.js +96 -96
  22. package/output/source/prompts/taskDiscovery.js +184 -184
  23. package/output/source/prompts/taskExecution.js +148 -148
  24. package/output/source/resources/designs.js +26 -26
  25. package/output/source/resources/designs.test.js +88 -88
  26. package/output/source/resources/governance.js +19 -19
  27. package/output/source/resources/index.js +2 -2
  28. package/output/source/resources/readme.js +7 -7
  29. package/output/source/resources/readme.test.js +113 -113
  30. package/output/source/resources/reports.js +10 -10
  31. package/output/source/resources/reports.test.js +83 -83
  32. package/output/source/tools/index.js +3 -3
  33. package/output/source/tools/project.js +191 -191
  34. package/output/source/tools/project.test.js +174 -173
  35. package/output/source/tools/roadmap.js +110 -95
  36. package/output/source/tools/roadmap.test.js +54 -46
  37. package/output/source/tools/task.js +305 -277
  38. package/output/source/tools/task.test.js +117 -110
  39. package/output/source/types.js +22 -22
  40. package/package.json +8 -2
@@ -1,169 +1,176 @@
1
- import { describe, expect, it } from "vitest";
2
- import { collectTaskLintSuggestions, isValidTaskId, normalizeTask, rankActionableTaskCandidates, resolveNoTaskDiscoveryGuidance, renderTaskSeedTemplate, renderTasksMarkdown, loadTasksDocument, saveTasks, taskPriority, toTaskUpdatedAtMs, validateTransition, } from "./task.js";
3
- import fs from "node:fs/promises";
4
- import os from "node:os";
5
- import path from "node:path";
1
+ import { describe, expect, it } from 'vitest';
2
+ import { collectTaskLintSuggestions, isValidTaskId, normalizeTask, rankActionableTaskCandidates, resolveNoTaskDiscoveryGuidance, renderTaskSeedTemplate, renderTasksMarkdown, loadTasksDocument, saveTasks, taskPriority, toTaskUpdatedAtMs, validateTransition, } from './task.js';
3
+ import fs from 'node:fs/promises';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
6
  function buildCandidate(partial) {
7
7
  const task = normalizeTask({
8
8
  id: partial.id,
9
9
  title: partial.title,
10
10
  status: partial.status,
11
- updatedAt: partial.task?.updatedAt ?? "2026-01-01T00:00:00.000Z",
11
+ updatedAt: partial.task?.updatedAt ?? '2026-01-01T00:00:00.000Z',
12
12
  });
13
13
  return {
14
- governanceDir: partial.governanceDir ?? "/workspace/a",
14
+ governanceDir: partial.governanceDir ?? '/workspace/a',
15
15
  task,
16
16
  projectScore: partial.projectScore ?? 1,
17
- projectLatestUpdatedAt: partial.projectLatestUpdatedAt ?? "2026-01-01T00:00:00.000Z",
17
+ projectLatestUpdatedAt: partial.projectLatestUpdatedAt ?? '2026-01-01T00:00:00.000Z',
18
18
  taskUpdatedAtMs: partial.taskUpdatedAtMs ?? toTaskUpdatedAtMs(task.updatedAt),
19
19
  taskPriority: partial.taskPriority ?? taskPriority(task.status),
20
20
  };
21
21
  }
22
- describe("tasks module", () => {
23
- it("validates task IDs", () => {
24
- expect(isValidTaskId("TASK-0001")).toBe(true);
25
- expect(isValidTaskId("TASK-001")).toBe(false);
26
- });
27
- it("allows and rejects expected transitions", () => {
28
- expect(validateTransition("TODO", "IN_PROGRESS")).toBe(true);
29
- expect(validateTransition("IN_PROGRESS", "DONE")).toBe(true);
30
- expect(validateTransition("DONE", "IN_PROGRESS")).toBe(false);
31
- });
32
- it("assigns priority for actionable statuses", () => {
33
- expect(taskPriority("IN_PROGRESS")).toBe(2);
34
- expect(taskPriority("TODO")).toBe(1);
35
- expect(taskPriority("BLOCKED")).toBe(0);
36
- });
37
- it("returns zero timestamp for invalid date", () => {
38
- expect(toTaskUpdatedAtMs("invalid")).toBe(0);
39
- });
40
- it("ranks by project score, then task priority, then recency", () => {
22
+ describe('tasks module', () => {
23
+ it('validates task IDs', () => {
24
+ expect(isValidTaskId('TASK-0001')).toBe(true);
25
+ expect(isValidTaskId('TASK-1')).toBe(true);
26
+ expect(isValidTaskId('TASK-12345')).toBe(true);
27
+ expect(isValidTaskId('TASK-ABCD')).toBe(false);
28
+ });
29
+ it('allows and rejects expected transitions', () => {
30
+ expect(validateTransition('TODO', 'IN_PROGRESS')).toBe(true);
31
+ expect(validateTransition('IN_PROGRESS', 'DONE')).toBe(true);
32
+ expect(validateTransition('DONE', 'IN_PROGRESS')).toBe(false);
33
+ });
34
+ it('assigns priority for actionable statuses', () => {
35
+ expect(taskPriority('IN_PROGRESS')).toBe(2);
36
+ expect(taskPriority('TODO')).toBe(1);
37
+ expect(taskPriority('BLOCKED')).toBe(0);
38
+ });
39
+ it('returns zero timestamp for invalid date', () => {
40
+ expect(toTaskUpdatedAtMs('invalid')).toBe(0);
41
+ });
42
+ it('ranks by project score, then task priority, then recency', () => {
41
43
  const candidates = [
42
- buildCandidate({ id: "TASK-0001", title: "A", status: "TODO", projectScore: 2 }),
43
- buildCandidate({ id: "TASK-0002", title: "B", status: "IN_PROGRESS", projectScore: 2 }),
44
- buildCandidate({ id: "TASK-0003", title: "C", status: "IN_PROGRESS", projectScore: 3 }),
44
+ buildCandidate({ id: 'TASK-0001', title: 'A', status: 'TODO', projectScore: 2 }),
45
+ buildCandidate({ id: 'TASK-0002', title: 'B', status: 'IN_PROGRESS', projectScore: 2 }),
46
+ buildCandidate({ id: 'TASK-0003', title: 'C', status: 'IN_PROGRESS', projectScore: 3 }),
45
47
  ];
46
48
  const ranked = rankActionableTaskCandidates(candidates);
47
- expect(ranked[0].task.id).toBe("TASK-0003");
48
- expect(ranked[1].task.id).toBe("TASK-0002");
49
- expect(ranked[2].task.id).toBe("TASK-0001");
49
+ expect(ranked[0].task.id).toBe('TASK-0003');
50
+ expect(ranked[1].task.id).toBe('TASK-0002');
51
+ expect(ranked[2].task.id).toBe('TASK-0001');
50
52
  });
51
- it("renders task markdown without legacy markers", () => {
52
- const task = normalizeTask({ id: "TASK-0002", title: "render", status: "IN_PROGRESS" });
53
+ it('renders task markdown without legacy markers', () => {
54
+ const task = normalizeTask({ id: 'TASK-0002', title: 'render', status: 'IN_PROGRESS' });
53
55
  const markdown = renderTasksMarkdown([task]);
54
- expect(markdown.includes("PROJITIVE:TASKS:START")).toBe(false);
55
- expect(markdown.includes("PROJITIVE:TASKS:END")).toBe(false);
56
- expect(markdown.includes("## TASK-0002 | IN_PROGRESS | render")).toBe(true);
57
- });
58
- it("renders tasks with subState and blocker metadata", () => {
56
+ expect(markdown.includes('PROJITIVE:TASKS:START')).toBe(false);
57
+ expect(markdown.includes('PROJITIVE:TASKS:END')).toBe(false);
58
+ expect(markdown.includes('## TASK-0002 | IN_PROGRESS | render')).toBe(true);
59
+ expect(markdown).toContain('Author: yinxulai');
60
+ expect(markdown).toContain('Repository: https://github.com/yinxulai/projitive');
61
+ expect(markdown).toContain('Do not edit this file manually.');
62
+ });
63
+ it('renders tasks with subState and blocker metadata', () => {
59
64
  const task1 = normalizeTask({
60
- id: "TASK-0001",
61
- title: "In Progress Task",
62
- status: "IN_PROGRESS",
65
+ id: 'TASK-0001',
66
+ title: 'In Progress Task',
67
+ status: 'IN_PROGRESS',
63
68
  subState: {
64
- phase: "implementation",
69
+ phase: 'implementation',
65
70
  confidence: 0.85,
66
71
  },
67
72
  });
68
73
  const task2 = normalizeTask({
69
- id: "TASK-0002",
70
- title: "Blocked Task",
71
- status: "BLOCKED",
74
+ id: 'TASK-0002',
75
+ title: 'Blocked Task',
76
+ status: 'BLOCKED',
72
77
  blocker: {
73
- type: "external_dependency",
74
- description: "Waiting for API",
78
+ type: 'external_dependency',
79
+ description: 'Waiting for API',
75
80
  },
76
81
  });
77
82
  const markdown = renderTasksMarkdown([task1, task2]);
78
- expect(markdown).toContain("phase: implementation");
79
- expect(markdown).toContain("confidence: 0.85");
80
- expect(markdown).toContain("type: external_dependency");
81
- expect(markdown).toContain("description: Waiting for API");
83
+ expect(markdown).toContain('phase: implementation');
84
+ expect(markdown).toContain('confidence: 0.85');
85
+ expect(markdown).toContain('type: external_dependency');
86
+ expect(markdown).toContain('description: Waiting for API');
82
87
  });
83
- it("collects lint lines with stable code prefix", () => {
88
+ it('collects lint lines with stable code prefix', () => {
84
89
  const task = normalizeTask({
85
- id: "TASK-0001",
86
- title: "lint",
87
- status: "IN_PROGRESS",
88
- owner: "",
90
+ id: 'TASK-0001',
91
+ title: 'lint',
92
+ status: 'IN_PROGRESS',
93
+ owner: '',
89
94
  roadmapRefs: [],
90
95
  });
91
96
  const lint = collectTaskLintSuggestions([task]);
92
- expect(lint.some((line) => line.startsWith("- [TASK_IN_PROGRESS_OWNER_EMPTY]"))).toBe(true);
93
- expect(lint.some((line) => line.startsWith("- [TASK_ROADMAP_REFS_EMPTY]"))).toBe(true);
97
+ expect(lint.some((line) => line.startsWith('- [TASK_IN_PROGRESS_OWNER_EMPTY]'))).toBe(true);
98
+ expect(lint.some((line) => line.startsWith('- [TASK_ROADMAP_REFS_EMPTY]'))).toBe(true);
94
99
  });
95
- it("collects blocker/substate lint rules", () => {
100
+ it('collects blocker/substate lint rules', () => {
96
101
  const blocked = normalizeTask({
97
- id: "TASK-0001",
98
- title: "Blocked",
99
- status: "BLOCKED",
100
- summary: "blocked reason",
101
- roadmapRefs: ["ROADMAP-0001"],
102
+ id: 'TASK-0001',
103
+ title: 'Blocked',
104
+ status: 'BLOCKED',
105
+ summary: 'blocked reason',
106
+ roadmapRefs: ['ROADMAP-0001'],
102
107
  });
103
108
  const inProgress = normalizeTask({
104
- id: "TASK-0002",
105
- title: "In Progress",
106
- status: "IN_PROGRESS",
107
- owner: "ai-copilot",
108
- roadmapRefs: ["ROADMAP-0001"],
109
+ id: 'TASK-0002',
110
+ title: 'In Progress',
111
+ status: 'IN_PROGRESS',
112
+ owner: 'ai-copilot',
113
+ roadmapRefs: ['ROADMAP-0001'],
109
114
  });
110
115
  const lint = collectTaskLintSuggestions([blocked, inProgress]);
111
- expect(lint.some((line) => line.includes("BLOCKED_WITHOUT_BLOCKER"))).toBe(true);
112
- expect(lint.some((line) => line.includes("IN_PROGRESS_WITHOUT_SUBSTATE"))).toBe(true);
116
+ expect(lint.some((line) => line.includes('BLOCKED_WITHOUT_BLOCKER'))).toBe(true);
117
+ expect(lint.some((line) => line.includes('IN_PROGRESS_WITHOUT_SUBSTATE'))).toBe(true);
113
118
  });
114
- it("normalizes links to project-root-relative format without leading slash", () => {
119
+ it('normalizes links to project-root-relative format without leading slash', () => {
115
120
  const task = normalizeTask({
116
- id: "TASK-0003",
117
- title: "link normalize",
118
- status: "TODO",
119
- links: ["/reports/a.md", "./designs/b.md", "reports/c.md", "https://example.com/evidence"],
121
+ id: 'TASK-0003',
122
+ title: 'link normalize',
123
+ status: 'TODO',
124
+ links: ['/reports/a.md', './designs/b.md', 'reports/c.md', 'https://example.com/evidence'],
120
125
  });
121
- expect(task.links).toContain("reports/a.md");
122
- expect(task.links).toContain("designs/b.md");
123
- expect(task.links).toContain("reports/c.md");
124
- expect(task.links).toContain("https://example.com/evidence");
125
- expect(task.links.some((item) => item.startsWith("/"))).toBe(false);
126
+ expect(task.links).toContain('reports/a.md');
127
+ expect(task.links).toContain('designs/b.md');
128
+ expect(task.links).toContain('reports/c.md');
129
+ expect(task.links).toContain('https://example.com/evidence');
130
+ expect(task.links.some((item) => item.startsWith('/'))).toBe(false);
126
131
  });
127
- it("lints invalid links path format", () => {
132
+ it('lints invalid links path format', () => {
128
133
  const task = normalizeTask({
129
- id: "TASK-0004",
130
- title: "invalid link",
131
- status: "TODO",
132
- links: ["../outside.md"],
133
- roadmapRefs: ["ROADMAP-0001"],
134
+ id: 'TASK-0004',
135
+ title: 'invalid link',
136
+ status: 'TODO',
137
+ links: ['../outside.md'],
138
+ roadmapRefs: ['ROADMAP-0001'],
134
139
  });
135
140
  const lint = collectTaskLintSuggestions([task]);
136
- expect(lint.some((line) => line.includes("TASK_LINK_PATH_FORMAT_INVALID"))).toBe(true);
141
+ expect(lint.some((line) => line.includes('TASK_LINK_PATH_FORMAT_INVALID'))).toBe(true);
137
142
  });
138
- it("renders seed task template with provided roadmap ref", () => {
139
- const lines = renderTaskSeedTemplate("ROADMAP-0099");
140
- const markdown = lines.join("\n");
141
- expect(markdown).toContain("- roadmapRefs: ROADMAP-0099");
143
+ it('renders seed task template with provided roadmap ref', () => {
144
+ const lines = renderTaskSeedTemplate('ROADMAP-0099');
145
+ const markdown = lines.join('\n');
146
+ expect(markdown).toContain('- roadmapRefs: ROADMAP-0099');
142
147
  });
143
- it("uses default no-task guidance when hook file is absent", async () => {
144
- const guidance = await resolveNoTaskDiscoveryGuidance("/path/that/does/not/exist");
148
+ it('uses default no-task guidance when hook file is absent', async () => {
149
+ const guidance = await resolveNoTaskDiscoveryGuidance('/path/that/does/not/exist');
145
150
  expect(guidance.length).toBeGreaterThan(3);
146
151
  });
147
- it("returns same default no-task guidance regardless of path", async () => {
148
- const guidanceA = await resolveNoTaskDiscoveryGuidance("/path/that/does/not/exist");
149
- const guidanceB = await resolveNoTaskDiscoveryGuidance("/another/path");
152
+ it('returns same default no-task guidance regardless of path', async () => {
153
+ const guidanceA = await resolveNoTaskDiscoveryGuidance('/path/that/does/not/exist');
154
+ const guidanceB = await resolveNoTaskDiscoveryGuidance('/another/path');
150
155
  expect(guidanceA).toEqual(guidanceB);
151
156
  });
152
- it("loads and saves tasks from governance store and keeps newest-first order", async () => {
153
- const root = await fs.mkdtemp(path.join(os.tmpdir(), "projitive-mcp-task-"));
154
- const governanceDir = path.join(root, ".projitive");
157
+ it('loads and saves tasks from governance store and keeps newest-first order', async () => {
158
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'projitive-mcp-task-'));
159
+ const governanceDir = path.join(root, '.projitive');
155
160
  await fs.mkdir(governanceDir, { recursive: true });
156
- await fs.writeFile(path.join(governanceDir, ".projitive"), "", "utf-8");
157
- const tasksPath = path.join(governanceDir, ".projitive");
161
+ await fs.writeFile(path.join(governanceDir, '.projitive'), '', 'utf-8');
162
+ const tasksPath = path.join(governanceDir, '.projitive');
158
163
  await saveTasks(tasksPath, [
159
- normalizeTask({ id: "TASK-0001", title: "older", status: "TODO", updatedAt: "2026-01-01T00:00:00.000Z" }),
160
- normalizeTask({ id: "TASK-0002", title: "newer", status: "TODO", updatedAt: "2026-02-01T00:00:00.000Z" }),
164
+ normalizeTask({ id: 'TASK-0001', title: 'older', status: 'TODO', updatedAt: '2026-01-01T00:00:00.000Z' }),
165
+ normalizeTask({ id: 'TASK-0002', title: 'newer', status: 'TODO', updatedAt: '2026-02-01T00:00:00.000Z' }),
161
166
  ]);
162
167
  const loaded = await loadTasksDocument(governanceDir);
163
- expect(loaded.tasks[0].id).toBe("TASK-0002");
164
- expect(loaded.tasks[1].id).toBe("TASK-0001");
165
- const markdown = await fs.readFile(path.join(governanceDir, "tasks.md"), "utf-8");
166
- expect(markdown).toContain("generated from .projitive governance store");
168
+ expect(loaded.tasks[0].id).toBe('TASK-0002');
169
+ expect(loaded.tasks[1].id).toBe('TASK-0001');
170
+ const markdown = await fs.readFile(path.join(governanceDir, 'tasks.md'), 'utf-8');
171
+ expect(markdown).toContain('generated from .projitive governance store');
172
+ expect(markdown).toContain('Author: yinxulai');
173
+ expect(markdown).toContain('Repository: https://github.com/yinxulai/projitive');
167
174
  await fs.rm(root, { recursive: true, force: true });
168
175
  });
169
176
  });
@@ -5,7 +5,7 @@
5
5
  // ============================================================================
6
6
  // Task State Machine
7
7
  // ============================================================================
8
- export const ALLOWED_STATUS = ["TODO", "IN_PROGRESS", "BLOCKED", "DONE"];
8
+ export const ALLOWED_STATUS = ['TODO', 'IN_PROGRESS', 'BLOCKED', 'DONE'];
9
9
  // ============================================================================
10
10
  // Sub-state Metadata (Spec v1.1.0)
11
11
  // ============================================================================
@@ -16,33 +16,33 @@ export const ALLOWED_STATUS = ["TODO", "IN_PROGRESS", "BLOCKED", "DONE"];
16
16
  * - implementation: Writing and testing code
17
17
  * - testing: Validation and verification
18
18
  */
19
- export const SUB_STATE_PHASES = ["discovery", "design", "implementation", "testing"];
19
+ export const SUB_STATE_PHASES = ['discovery', 'design', 'implementation', 'testing'];
20
20
  /**
21
21
  * Blocker categorization for BLOCKED state (Spec v1.1.0)
22
22
  */
23
23
  export const BLOCKER_TYPES = [
24
- "internal_dependency",
25
- "external_dependency",
26
- "resource",
27
- "approval"
24
+ 'internal_dependency',
25
+ 'external_dependency',
26
+ 'resource',
27
+ 'approval'
28
28
  ];
29
29
  export const TASK_LINT_CODES = {
30
- DUPLICATE_ID: "TASK_DUPLICATE_ID",
31
- IN_PROGRESS_OWNER_EMPTY: "TASK_IN_PROGRESS_OWNER_EMPTY",
32
- DONE_LINKS_MISSING: "TASK_DONE_LINKS_MISSING",
33
- BLOCKED_SUMMARY_EMPTY: "TASK_BLOCKED_SUMMARY_EMPTY",
34
- UPDATED_AT_INVALID: "TASK_UPDATED_AT_INVALID",
35
- ROADMAP_REFS_EMPTY: "TASK_ROADMAP_REFS_EMPTY",
36
- OUTSIDE_MARKER: "TASK_OUTSIDE_MARKER",
37
- FILTER_EMPTY: "TASK_FILTER_EMPTY",
38
- LINK_TARGET_MISSING: "TASK_LINK_TARGET_MISSING",
39
- LINK_PATH_FORMAT_INVALID: "TASK_LINK_PATH_FORMAT_INVALID",
30
+ DUPLICATE_ID: 'TASK_DUPLICATE_ID',
31
+ IN_PROGRESS_OWNER_EMPTY: 'TASK_IN_PROGRESS_OWNER_EMPTY',
32
+ DONE_LINKS_MISSING: 'TASK_DONE_LINKS_MISSING',
33
+ BLOCKED_SUMMARY_EMPTY: 'TASK_BLOCKED_SUMMARY_EMPTY',
34
+ UPDATED_AT_INVALID: 'TASK_UPDATED_AT_INVALID',
35
+ ROADMAP_REFS_EMPTY: 'TASK_ROADMAP_REFS_EMPTY',
36
+ OUTSIDE_MARKER: 'TASK_OUTSIDE_MARKER',
37
+ FILTER_EMPTY: 'TASK_FILTER_EMPTY',
38
+ LINK_TARGET_MISSING: 'TASK_LINK_TARGET_MISSING',
39
+ LINK_PATH_FORMAT_INVALID: 'TASK_LINK_PATH_FORMAT_INVALID',
40
40
  // Spec v1.1.0 - Blocker Categorization
41
- BLOCKED_WITHOUT_BLOCKER: "TASK_BLOCKED_WITHOUT_BLOCKER",
42
- BLOCKER_TYPE_INVALID: "TASK_BLOCKER_TYPE_INVALID",
43
- BLOCKER_DESCRIPTION_EMPTY: "TASK_BLOCKER_DESCRIPTION_EMPTY",
41
+ BLOCKED_WITHOUT_BLOCKER: 'TASK_BLOCKED_WITHOUT_BLOCKER',
42
+ BLOCKER_TYPE_INVALID: 'TASK_BLOCKER_TYPE_INVALID',
43
+ BLOCKER_DESCRIPTION_EMPTY: 'TASK_BLOCKER_DESCRIPTION_EMPTY',
44
44
  // Spec v1.1.0 - Sub-state Metadata
45
- IN_PROGRESS_WITHOUT_SUBSTATE: "TASK_IN_PROGRESS_WITHOUT_SUBSTATE",
46
- SUBSTATE_PHASE_INVALID: "TASK_SUBSTATE_PHASE_INVALID",
47
- SUBSTATE_CONFIDENCE_INVALID: "TASK_SUBSTATE_CONFIDENCE_INVALID",
45
+ IN_PROGRESS_WITHOUT_SUBSTATE: 'TASK_IN_PROGRESS_WITHOUT_SUBSTATE',
46
+ SUBSTATE_PHASE_INVALID: 'TASK_SUBSTATE_PHASE_INVALID',
47
+ SUBSTATE_CONFIDENCE_INVALID: 'TASK_SUBSTATE_CONFIDENCE_INVALID',
48
48
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projitive/mcp",
3
- "version": "2.0.3",
3
+ "version": "2.0.4",
4
4
  "description": "Projitive MCP Server for project and task discovery/update",
5
5
  "license": "ISC",
6
6
  "author": "",
@@ -16,7 +16,9 @@
16
16
  "test": "vitest run",
17
17
  "test:coverage": "vitest run --coverage",
18
18
  "benchmark": "vitest bench --run",
19
- "lint": "tsc -p tsconfig.json --noEmit",
19
+ "lint": "npm run lint:types && npm run lint:eslint",
20
+ "lint:types": "tsc -p tsconfig.json --noEmit",
21
+ "lint:eslint": "eslint .",
20
22
  "build": "rm -rf output && tsc -p tsconfig.json",
21
23
  "prepublishOnly": "npm run build",
22
24
  "dev": "tsc -p tsconfig.json --watch"
@@ -31,8 +33,12 @@
31
33
  "devDependencies": {
32
34
  "@types/node": "^24.3.0",
33
35
  "@vitest/coverage-v8": "^3.2.4",
36
+ "@eslint/js": "^9.37.0",
37
+ "eslint": "^9.37.0",
38
+ "globals": "^16.4.0",
34
39
  "tsx": "^4.20.5",
35
40
  "typescript": "^5.9.2",
41
+ "typescript-eslint": "^8.45.0",
36
42
  "vitest": "^3.2.4"
37
43
  }
38
44
  }