@planu/cli 1.11.0 → 1.12.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 (179) hide show
  1. package/dist/config/license-plans.json +27 -2
  2. package/dist/engine/api-compat/compatibility-checker.d.ts +4 -0
  3. package/dist/engine/api-compat/compatibility-checker.d.ts.map +1 -0
  4. package/dist/engine/api-compat/compatibility-checker.js +118 -0
  5. package/dist/engine/api-compat/compatibility-checker.js.map +1 -0
  6. package/dist/engine/checkpoint/checkpoint-manager.d.ts +22 -0
  7. package/dist/engine/checkpoint/checkpoint-manager.d.ts.map +1 -0
  8. package/dist/engine/checkpoint/checkpoint-manager.js +76 -0
  9. package/dist/engine/checkpoint/checkpoint-manager.js.map +1 -0
  10. package/dist/engine/checkpoint/policy-engine.d.ts +10 -0
  11. package/dist/engine/checkpoint/policy-engine.d.ts.map +1 -0
  12. package/dist/engine/checkpoint/policy-engine.js +87 -0
  13. package/dist/engine/checkpoint/policy-engine.js.map +1 -0
  14. package/dist/engine/compliance/auto-remediator.d.ts +9 -0
  15. package/dist/engine/compliance/auto-remediator.d.ts.map +1 -0
  16. package/dist/engine/compliance/auto-remediator.js +118 -0
  17. package/dist/engine/compliance/auto-remediator.js.map +1 -0
  18. package/dist/engine/context-profile/profile-catalog.d.ts +5 -0
  19. package/dist/engine/context-profile/profile-catalog.d.ts.map +1 -0
  20. package/dist/engine/context-profile/profile-catalog.js +145 -0
  21. package/dist/engine/context-profile/profile-catalog.js.map +1 -0
  22. package/dist/engine/critical-path/path-analyzer.d.ts +3 -0
  23. package/dist/engine/critical-path/path-analyzer.d.ts.map +1 -0
  24. package/dist/engine/critical-path/path-analyzer.js +145 -0
  25. package/dist/engine/critical-path/path-analyzer.js.map +1 -0
  26. package/dist/engine/drift/violation-resolver.d.ts +9 -0
  27. package/dist/engine/drift/violation-resolver.d.ts.map +1 -0
  28. package/dist/engine/drift/violation-resolver.js +128 -0
  29. package/dist/engine/drift/violation-resolver.js.map +1 -0
  30. package/dist/engine/ears/criterion-scorer.d.ts +7 -0
  31. package/dist/engine/ears/criterion-scorer.d.ts.map +1 -0
  32. package/dist/engine/ears/criterion-scorer.js +87 -0
  33. package/dist/engine/ears/criterion-scorer.js.map +1 -0
  34. package/dist/engine/ears/pattern-matcher.d.ts +5 -0
  35. package/dist/engine/ears/pattern-matcher.d.ts.map +1 -0
  36. package/dist/engine/ears/pattern-matcher.js +48 -0
  37. package/dist/engine/ears/pattern-matcher.js.map +1 -0
  38. package/dist/engine/ears/rewriter.d.ts +7 -0
  39. package/dist/engine/ears/rewriter.d.ts.map +1 -0
  40. package/dist/engine/ears/rewriter.js +45 -0
  41. package/dist/engine/ears/rewriter.js.map +1 -0
  42. package/dist/engine/ears/spec-linter.d.ts +7 -0
  43. package/dist/engine/ears/spec-linter.d.ts.map +1 -0
  44. package/dist/engine/ears/spec-linter.js +127 -0
  45. package/dist/engine/ears/spec-linter.js.map +1 -0
  46. package/dist/engine/health/auto-fixer.d.ts +7 -0
  47. package/dist/engine/health/auto-fixer.d.ts.map +1 -0
  48. package/dist/engine/health/auto-fixer.js +130 -0
  49. package/dist/engine/health/auto-fixer.js.map +1 -0
  50. package/dist/engine/mcp-catalog/catalog-advisor.d.ts +3 -0
  51. package/dist/engine/mcp-catalog/catalog-advisor.d.ts.map +1 -0
  52. package/dist/engine/mcp-catalog/catalog-advisor.js +180 -0
  53. package/dist/engine/mcp-catalog/catalog-advisor.js.map +1 -0
  54. package/dist/engine/similar-problems/similarity-finder.d.ts +3 -0
  55. package/dist/engine/similar-problems/similarity-finder.d.ts.map +1 -0
  56. package/dist/engine/similar-problems/similarity-finder.js +144 -0
  57. package/dist/engine/similar-problems/similarity-finder.js.map +1 -0
  58. package/dist/engine/sync/asana-puller.d.ts +9 -0
  59. package/dist/engine/sync/asana-puller.d.ts.map +1 -0
  60. package/dist/engine/sync/asana-puller.js +91 -0
  61. package/dist/engine/sync/asana-puller.js.map +1 -0
  62. package/dist/engine/sync/conflict-resolver.d.ts +17 -0
  63. package/dist/engine/sync/conflict-resolver.d.ts.map +1 -0
  64. package/dist/engine/sync/conflict-resolver.js +58 -0
  65. package/dist/engine/sync/conflict-resolver.js.map +1 -0
  66. package/dist/engine/sync/monday-puller.d.ts +9 -0
  67. package/dist/engine/sync/monday-puller.d.ts.map +1 -0
  68. package/dist/engine/sync/monday-puller.js +110 -0
  69. package/dist/engine/sync/monday-puller.js.map +1 -0
  70. package/dist/engine/sync/notion-puller.d.ts +15 -0
  71. package/dist/engine/sync/notion-puller.d.ts.map +1 -0
  72. package/dist/engine/sync/notion-puller.js +101 -0
  73. package/dist/engine/sync/notion-puller.js.map +1 -0
  74. package/dist/engine/verifier/code-scanner.d.ts +8 -0
  75. package/dist/engine/verifier/code-scanner.d.ts.map +1 -0
  76. package/dist/engine/verifier/code-scanner.js +73 -0
  77. package/dist/engine/verifier/code-scanner.js.map +1 -0
  78. package/dist/engine/verifier/compliance-scorer.d.ts +17 -0
  79. package/dist/engine/verifier/compliance-scorer.d.ts.map +1 -0
  80. package/dist/engine/verifier/compliance-scorer.js +131 -0
  81. package/dist/engine/verifier/compliance-scorer.js.map +1 -0
  82. package/dist/engine/verifier/criterion-matcher.d.ts +15 -0
  83. package/dist/engine/verifier/criterion-matcher.d.ts.map +1 -0
  84. package/dist/engine/verifier/criterion-matcher.js +210 -0
  85. package/dist/engine/verifier/criterion-matcher.js.map +1 -0
  86. package/dist/index.js +14 -0
  87. package/dist/index.js.map +1 -1
  88. package/dist/storage/compliance-score-store.d.ts +16 -0
  89. package/dist/storage/compliance-score-store.d.ts.map +1 -0
  90. package/dist/storage/compliance-score-store.js +30 -0
  91. package/dist/storage/compliance-score-store.js.map +1 -0
  92. package/dist/storage/context-profile-store.d.ts +14 -0
  93. package/dist/storage/context-profile-store.d.ts.map +1 -0
  94. package/dist/storage/context-profile-store.js +34 -0
  95. package/dist/storage/context-profile-store.js.map +1 -0
  96. package/dist/storage/workflow-checkpoint-store.d.ts +16 -0
  97. package/dist/storage/workflow-checkpoint-store.d.ts.map +1 -0
  98. package/dist/storage/workflow-checkpoint-store.js +71 -0
  99. package/dist/storage/workflow-checkpoint-store.js.map +1 -0
  100. package/dist/tools/checkpoint/approve-checkpoint-handler.d.ts +3 -0
  101. package/dist/tools/checkpoint/approve-checkpoint-handler.d.ts.map +1 -0
  102. package/dist/tools/checkpoint/approve-checkpoint-handler.js +32 -0
  103. package/dist/tools/checkpoint/approve-checkpoint-handler.js.map +1 -0
  104. package/dist/tools/checkpoint/configure-policy-handler.d.ts +3 -0
  105. package/dist/tools/checkpoint/configure-policy-handler.d.ts.map +1 -0
  106. package/dist/tools/checkpoint/configure-policy-handler.js +60 -0
  107. package/dist/tools/checkpoint/configure-policy-handler.js.map +1 -0
  108. package/dist/tools/checkpoint/list-checkpoints-handler.d.ts +3 -0
  109. package/dist/tools/checkpoint/list-checkpoints-handler.d.ts.map +1 -0
  110. package/dist/tools/checkpoint/list-checkpoints-handler.js +25 -0
  111. package/dist/tools/checkpoint/list-checkpoints-handler.js.map +1 -0
  112. package/dist/tools/checkpoint/reject-checkpoint-handler.d.ts +3 -0
  113. package/dist/tools/checkpoint/reject-checkpoint-handler.d.ts.map +1 -0
  114. package/dist/tools/checkpoint/reject-checkpoint-handler.js +32 -0
  115. package/dist/tools/checkpoint/reject-checkpoint-handler.js.map +1 -0
  116. package/dist/tools/checkpoint/require-checkpoint-handler.d.ts +3 -0
  117. package/dist/tools/checkpoint/require-checkpoint-handler.d.ts.map +1 -0
  118. package/dist/tools/checkpoint/require-checkpoint-handler.js +44 -0
  119. package/dist/tools/checkpoint/require-checkpoint-handler.js.map +1 -0
  120. package/dist/tools/pull-sync-handler.d.ts +25 -0
  121. package/dist/tools/pull-sync-handler.d.ts.map +1 -0
  122. package/dist/tools/pull-sync-handler.js +161 -0
  123. package/dist/tools/pull-sync-handler.js.map +1 -0
  124. package/dist/tools/register-auto-remediation.d.ts +3 -0
  125. package/dist/tools/register-auto-remediation.d.ts.map +1 -0
  126. package/dist/tools/register-auto-remediation.js +174 -0
  127. package/dist/tools/register-auto-remediation.js.map +1 -0
  128. package/dist/tools/register-checkpoints.d.ts +3 -0
  129. package/dist/tools/register-checkpoints.d.ts.map +1 -0
  130. package/dist/tools/register-checkpoints.js +134 -0
  131. package/dist/tools/register-checkpoints.js.map +1 -0
  132. package/dist/tools/register-context-profile.d.ts +3 -0
  133. package/dist/tools/register-context-profile.d.ts.map +1 -0
  134. package/dist/tools/register-context-profile.js +106 -0
  135. package/dist/tools/register-context-profile.js.map +1 -0
  136. package/dist/tools/register-ears.d.ts +3 -0
  137. package/dist/tools/register-ears.d.ts.map +1 -0
  138. package/dist/tools/register-ears.js +148 -0
  139. package/dist/tools/register-ears.js.map +1 -0
  140. package/dist/tools/register-enterprise-compliance.js +1 -1
  141. package/dist/tools/register-enterprise-compliance.js.map +1 -1
  142. package/dist/tools/register-pull-sync.d.ts +3 -0
  143. package/dist/tools/register-pull-sync.d.ts.map +1 -0
  144. package/dist/tools/register-pull-sync.js +71 -0
  145. package/dist/tools/register-pull-sync.js.map +1 -0
  146. package/dist/tools/register-spec405-tools.d.ts +7 -0
  147. package/dist/tools/register-spec405-tools.d.ts.map +1 -0
  148. package/dist/tools/register-spec405-tools.js +194 -0
  149. package/dist/tools/register-spec405-tools.js.map +1 -0
  150. package/dist/tools/register-verifier.d.ts +3 -0
  151. package/dist/tools/register-verifier.d.ts.map +1 -0
  152. package/dist/tools/register-verifier.js +141 -0
  153. package/dist/tools/register-verifier.js.map +1 -0
  154. package/dist/types/analysis.d.ts +98 -0
  155. package/dist/types/analysis.d.ts.map +1 -1
  156. package/dist/types/context-profile.d.ts +22 -0
  157. package/dist/types/context-profile.d.ts.map +1 -0
  158. package/dist/types/context-profile.js +2 -0
  159. package/dist/types/context-profile.js.map +1 -0
  160. package/dist/types/ears.d.ts +34 -0
  161. package/dist/types/ears.d.ts.map +1 -0
  162. package/dist/types/ears.js +3 -0
  163. package/dist/types/ears.js.map +1 -0
  164. package/dist/types/health.d.ts +40 -0
  165. package/dist/types/health.d.ts.map +1 -0
  166. package/dist/types/health.js +3 -0
  167. package/dist/types/health.js.map +1 -0
  168. package/dist/types/index.d.ts +4 -0
  169. package/dist/types/index.d.ts.map +1 -1
  170. package/dist/types/index.js +4 -0
  171. package/dist/types/index.js.map +1 -1
  172. package/dist/types/notion-asana-monday.d.ts +38 -0
  173. package/dist/types/notion-asana-monday.d.ts.map +1 -1
  174. package/dist/types/workflow-checkpoint.d.ts +66 -0
  175. package/dist/types/workflow-checkpoint.d.ts.map +1 -0
  176. package/dist/types/workflow-checkpoint.js +4 -0
  177. package/dist/types/workflow-checkpoint.js.map +1 -0
  178. package/package.json +1 -1
  179. package/src/config/license-plans.json +27 -2
@@ -0,0 +1,148 @@
1
+ // tools/register-ears.ts — EARS criteria quality tools (SPEC-410)
2
+ import { z } from 'zod';
3
+ import { safeTracked } from './safe-handler.js';
4
+ import { lintSpec, lintAllSpecs } from '../engine/ears/spec-linter.js';
5
+ import { scoreCriterion } from '../engine/ears/criterion-scorer.js';
6
+ import { generateRewrites } from '../engine/ears/rewriter.js';
7
+ import { detectEarsPattern } from '../engine/ears/pattern-matcher.js';
8
+ export function registerEarsTools(server) {
9
+ // -------------------------------------------------------------------------
10
+ // validate_criteria_quality — score AC quality for a spec or raw criteria
11
+ // -------------------------------------------------------------------------
12
+ server.registerTool('validate_criteria_quality', {
13
+ description: 'Score acceptance criteria quality using EARS (Easy Approach to Requirements Syntax). ' +
14
+ 'Detects vague language, scores testability and specificity (0-10 each), ' +
15
+ 'identifies the EARS pattern, and suggests rewrites for low-scoring criteria. ' +
16
+ 'Provide either specId (reads from spec file) or criteria (raw text array). ' +
17
+ 'Returns per-criterion scores, overall grade (A-F), and rewrite suggestions.',
18
+ inputSchema: {
19
+ projectPath: z
20
+ .string()
21
+ .min(1)
22
+ .max(4096)
23
+ .describe('Absolute path to the project root where planu/ directory lives.'),
24
+ specId: z
25
+ .string()
26
+ .min(1)
27
+ .max(500)
28
+ .optional()
29
+ .describe('Spec ID to validate (e.g. SPEC-042). Reads criteria from the spec file.'),
30
+ criteria: z
31
+ .array(z.string().min(1).max(2000))
32
+ .optional()
33
+ .describe('Raw acceptance criteria text array to score. Used when specId is not provided.'),
34
+ },
35
+ annotations: { title: 'Validate Criteria Quality', readOnlyHint: true },
36
+ }, safeTracked('validate_criteria_quality', async (args) => {
37
+ const { projectPath, specId, criteria } = args;
38
+ if (specId !== undefined) {
39
+ const report = await lintSpec(specId, projectPath);
40
+ return {
41
+ content: [{ type: 'text', text: JSON.stringify(report, null, 2) }],
42
+ };
43
+ }
44
+ if (criteria !== undefined && criteria.length > 0) {
45
+ const scored = criteria.map(scoreCriterion);
46
+ const overallScore = scored.length > 0
47
+ ? Math.round((scored.reduce((sum, c) => sum + c.overallScore, 0) / scored.length) * 10) / 10
48
+ : 0;
49
+ return {
50
+ content: [
51
+ {
52
+ type: 'text',
53
+ text: JSON.stringify({ criteria: scored, overallScore }, null, 2),
54
+ },
55
+ ],
56
+ };
57
+ }
58
+ return {
59
+ content: [
60
+ {
61
+ type: 'text',
62
+ text: 'Provide either specId or criteria array.',
63
+ },
64
+ ],
65
+ isError: true,
66
+ };
67
+ }));
68
+ // -------------------------------------------------------------------------
69
+ // rewrite_criteria_ears — score a single criterion and get EARS rewrites
70
+ // -------------------------------------------------------------------------
71
+ server.registerTool('rewrite_criteria_ears', {
72
+ description: 'Score a single acceptance criterion and generate 2 EARS-format rewrite suggestions. ' +
73
+ 'Returns current testability and specificity scores, detected vagueness flags, ' +
74
+ 'EARS pattern classification, and concrete rewrite alternatives. ' +
75
+ 'No LLM call — rewrites are template-driven for consistency and speed.',
76
+ inputSchema: {
77
+ projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root.'),
78
+ criterion: z
79
+ .string()
80
+ .min(1)
81
+ .max(2000)
82
+ .describe('The acceptance criterion text to score and rewrite.'),
83
+ },
84
+ annotations: { title: 'Rewrite Criteria as EARS', readOnlyHint: true },
85
+ }, safeTracked('rewrite_criteria_ears', (args) => {
86
+ const { criterion } = args;
87
+ const scored = scoreCriterion(criterion);
88
+ const pattern = detectEarsPattern(criterion);
89
+ const rewrites = generateRewrites(criterion, pattern);
90
+ return Promise.resolve({
91
+ content: [
92
+ {
93
+ type: 'text',
94
+ text: JSON.stringify({ ...scored, rewrites }, null, 2),
95
+ },
96
+ ],
97
+ });
98
+ }));
99
+ // -------------------------------------------------------------------------
100
+ // ears_lint — lint all specs in the project and report worst criteria
101
+ // -------------------------------------------------------------------------
102
+ server.registerTool('ears_lint', {
103
+ description: 'Lint all specs in the project for EARS compliance and criteria quality. ' +
104
+ 'Returns project-wide average score, letter grade, and top 10 worst criteria ranked by score. ' +
105
+ 'Use minScore to filter and only report criteria below a threshold. ' +
106
+ 'Helpful for identifying which specs need the most improvement.',
107
+ inputSchema: {
108
+ projectPath: z
109
+ .string()
110
+ .min(1)
111
+ .max(4096)
112
+ .describe('Absolute path to the project root where planu/specs/ directory lives.'),
113
+ minScore: z
114
+ .number()
115
+ .min(0)
116
+ .max(10)
117
+ .optional()
118
+ .describe('Optional score threshold (0-10). Only report criteria with overallScore below this value. ' +
119
+ 'Defaults to 10 (report all criteria regardless of score).'),
120
+ },
121
+ annotations: { title: 'EARS Lint All Specs', readOnlyHint: true },
122
+ }, safeTracked('ears_lint', async (args) => {
123
+ const { projectPath, minScore } = args;
124
+ const report = await lintAllSpecs(projectPath);
125
+ const threshold = minScore ?? 10;
126
+ const filteredWorst = report.topWorstCriteria.filter((c) => c.score < threshold);
127
+ const summary = {
128
+ projectPath: report.projectPath,
129
+ totalSpecs: report.totalSpecs,
130
+ averageScore: report.averageScore,
131
+ grade: report.averageScore >= 9
132
+ ? 'A'
133
+ : report.averageScore >= 7
134
+ ? 'B'
135
+ : report.averageScore >= 5
136
+ ? 'C'
137
+ : report.averageScore >= 3
138
+ ? 'D'
139
+ : 'F',
140
+ topWorstCriteria: filteredWorst,
141
+ generatedAt: report.generatedAt,
142
+ };
143
+ return {
144
+ content: [{ type: 'text', text: JSON.stringify(summary, null, 2) }],
145
+ };
146
+ }));
147
+ }
148
+ //# sourceMappingURL=register-ears.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-ears.js","sourceRoot":"","sources":["../../src/tools/register-ears.ts"],"names":[],"mappings":"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"}
@@ -16,7 +16,7 @@ export function registerEnterpriseComplianceTools(s) {
16
16
  const projectId = hashProjectPath(projectPath);
17
17
  return handleComplianceGapAnalysis(projectId, framework);
18
18
  });
19
- s.registerTool('generate_compliance_report', {
19
+ s.registerTool('generate_compliance_evidence', {
20
20
  description: 'Generates a compliance evidence package for a framework — includes control matrix, change management evidence from audit trail, and gap analysis.',
21
21
  inputSchema: {
22
22
  projectPath: z.string().describe('Absolute path to the project root'),
@@ -1 +1 @@
1
- {"version":3,"file":"register-enterprise-compliance.js","sourceRoot":"","sources":["../../src/tools/register-enterprise-compliance.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAG7D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,GAClC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE3D,MAAM,aAAa,GAAG,CAAC;KACpB,IAAI,CAAC,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;KAC5D,QAAQ,CAAC,sEAAsE,CAAC,CAAC;AAEpF,MAAM,UAAU,iCAAiC,CAAC,CAAY;IAC5D,CAAC,CAAC,YAAY,CACZ,yBAAyB,EACzB;QACE,WAAW,EACT,4KAA4K;QAC9K,WAAW,EAAE;YACX,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;YACrE,SAAS,EAAE,aAAa;SACzB;KACF,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,EAAE;QACnC,MAAM,SAAS,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;QAC/C,OAAO,2BAA2B,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAC3D,CAAC,CACF,CAAC;IAEF,CAAC,CAAC,YAAY,CACZ,4BAA4B,EAC5B;QACE,WAAW,EACT,mJAAmJ;QACrJ,WAAW,EAAE;YACX,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;YACrE,SAAS,EAAE,aAAa;SACzB;KACF,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,EAAE;QACnC,MAAM,SAAS,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;QAC/C,OAAO,iCAAiC,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACjE,CAAC,CACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"register-enterprise-compliance.js","sourceRoot":"","sources":["../../src/tools/register-enterprise-compliance.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAG7D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,GAClC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE3D,MAAM,aAAa,GAAG,CAAC;KACpB,IAAI,CAAC,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;KAC5D,QAAQ,CAAC,sEAAsE,CAAC,CAAC;AAEpF,MAAM,UAAU,iCAAiC,CAAC,CAAY;IAC5D,CAAC,CAAC,YAAY,CACZ,yBAAyB,EACzB;QACE,WAAW,EACT,4KAA4K;QAC9K,WAAW,EAAE;YACX,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;YACrE,SAAS,EAAE,aAAa;SACzB;KACF,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,EAAE;QACnC,MAAM,SAAS,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;QAC/C,OAAO,2BAA2B,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAC3D,CAAC,CACF,CAAC;IAEF,CAAC,CAAC,YAAY,CACZ,8BAA8B,EAC9B;QACE,WAAW,EACT,mJAAmJ;QACrJ,WAAW,EAAE;YACX,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;YACrE,SAAS,EAAE,aAAa;SACzB;KACF,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,EAAE;QACnC,MAAM,SAAS,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;QAC/C,OAAO,iCAAiC,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACjE,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerPullSyncTools(server: McpServer): void;
3
+ //# sourceMappingURL=register-pull-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-pull-sync.d.ts","sourceRoot":"","sources":["../../src/tools/register-pull-sync.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAuCzE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAyE7D"}
@@ -0,0 +1,71 @@
1
+ import { z } from 'zod';
2
+ import { safeLicensed } from './safe-handler.js';
3
+ import { handlePullFromNotion, handlePullFromAsana, handlePullFromMonday, handleSyncAllIntegrations, } from './pull-sync-handler.js';
4
+ const projectPathSchema = z
5
+ .string()
6
+ .min(1)
7
+ .max(4096)
8
+ .describe('Absolute path to the project root where Planu stores its data.');
9
+ const conflictStrategySchema = z
10
+ .enum(['spec-wins', 'external-wins', 'newest-wins', 'manual'])
11
+ .describe('Conflict resolution strategy when a field differs between Planu and the external PM tool. ' +
12
+ '"spec-wins" keeps the Planu value (default). ' +
13
+ '"external-wins" applies the external value to Planu. ' +
14
+ '"newest-wins" picks whichever side was updated most recently. ' +
15
+ '"manual" flags the conflict for human review without resolving it.')
16
+ .default('spec-wins');
17
+ const createMissingSchema = z
18
+ .boolean()
19
+ .optional()
20
+ .describe('When true, creates new Planu specs for external items that have no matching spec. ' +
21
+ 'Default: false.');
22
+ export function registerPullSyncTools(server) {
23
+ server.registerTool('pull_from_notion', {
24
+ description: 'Pull updates from Notion into Planu. ' +
25
+ 'Compares Notion page status against the last-synced state and applies changes according ' +
26
+ 'to the conflict resolution strategy. ' +
27
+ 'Run configure_notion and sync_to_notion first to establish the sync mapping.',
28
+ inputSchema: {
29
+ projectPath: projectPathSchema,
30
+ conflictStrategy: conflictStrategySchema,
31
+ createMissing: createMissingSchema,
32
+ },
33
+ annotations: { title: 'Pull from Notion' },
34
+ }, safeLicensed('pull_from_notion', (args) => handlePullFromNotion(args)));
35
+ server.registerTool('pull_from_asana', {
36
+ description: 'Pull updates from Asana into Planu. ' +
37
+ 'Compares Asana task completion state against the last-synced state and applies changes ' +
38
+ 'according to the conflict resolution strategy. ' +
39
+ 'Run configure_asana and sync_to_asana first to establish the sync mapping.',
40
+ inputSchema: {
41
+ projectPath: projectPathSchema,
42
+ conflictStrategy: conflictStrategySchema,
43
+ createMissing: createMissingSchema,
44
+ },
45
+ annotations: { title: 'Pull from Asana' },
46
+ }, safeLicensed('pull_from_asana', (args) => handlePullFromAsana(args)));
47
+ server.registerTool('pull_from_monday', {
48
+ description: 'Pull updates from Monday.com into Planu. ' +
49
+ 'Compares Monday.com item status against the last-synced state and applies changes ' +
50
+ 'according to the conflict resolution strategy. ' +
51
+ 'Run configure_monday and sync_to_monday first to establish the sync mapping.',
52
+ inputSchema: {
53
+ projectPath: projectPathSchema,
54
+ conflictStrategy: conflictStrategySchema,
55
+ createMissing: createMissingSchema,
56
+ },
57
+ annotations: { title: 'Pull from Monday.com' },
58
+ }, safeLicensed('pull_from_monday', (args) => handlePullFromMonday(args)));
59
+ server.registerTool('sync_all_integrations', {
60
+ description: 'Run pull sync for all configured PM integrations (Notion, Asana, Monday.com) in one call. ' +
61
+ 'Idempotent — safe to run repeatedly. ' +
62
+ 'Only syncs integrations that have been configured with their respective configure_* tools. ' +
63
+ 'Returns a unified summary of updates pulled and conflicts resolved across all integrations.',
64
+ inputSchema: {
65
+ projectPath: projectPathSchema,
66
+ conflictStrategy: conflictStrategySchema,
67
+ },
68
+ annotations: { title: 'Sync All Integrations' },
69
+ }, safeLicensed('sync_all_integrations', (args) => handleSyncAllIntegrations(args)));
70
+ }
71
+ //# sourceMappingURL=register-pull-sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-pull-sync.js","sourceRoot":"","sources":["../../src/tools/register-pull-sync.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,yBAAyB,GAK1B,MAAM,wBAAwB,CAAC;AAEhC,MAAM,iBAAiB,GAAG,CAAC;KACxB,MAAM,EAAE;KACR,GAAG,CAAC,CAAC,CAAC;KACN,GAAG,CAAC,IAAI,CAAC;KACT,QAAQ,CAAC,gEAAgE,CAAC,CAAC;AAE9E,MAAM,sBAAsB,GAAG,CAAC;KAC7B,IAAI,CAAC,CAAC,WAAW,EAAE,eAAe,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;KAC7D,QAAQ,CACP,4FAA4F;IAC1F,+CAA+C;IAC/C,uDAAuD;IACvD,gEAAgE;IAChE,oEAAoE,CACvE;KACA,OAAO,CAAC,WAAW,CAAC,CAAC;AAExB,MAAM,mBAAmB,GAAG,CAAC;KAC1B,OAAO,EAAE;KACT,QAAQ,EAAE;KACV,QAAQ,CACP,oFAAoF;IAClF,iBAAiB,CACpB,CAAC;AAEJ,MAAM,UAAU,qBAAqB,CAAC,MAAiB;IACrD,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;QACE,WAAW,EACT,uCAAuC;YACvC,0FAA0F;YAC1F,uCAAuC;YACvC,8EAA8E;QAChF,WAAW,EAAE;YACX,WAAW,EAAE,iBAAiB;YAC9B,gBAAgB,EAAE,sBAAsB;YACxC,aAAa,EAAE,mBAAmB;SACnC;QACD,WAAW,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE;KAC3C,EACD,YAAY,CAAC,kBAAkB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAA2B,CAAC,CAAC,CAC9F,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;QACE,WAAW,EACT,sCAAsC;YACtC,yFAAyF;YACzF,iDAAiD;YACjD,4EAA4E;QAC9E,WAAW,EAAE;YACX,WAAW,EAAE,iBAAiB;YAC9B,gBAAgB,EAAE,sBAAsB;YACxC,aAAa,EAAE,mBAAmB;SACnC;QACD,WAAW,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE;KAC1C,EACD,YAAY,CAAC,iBAAiB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,mBAAmB,CAAC,IAA0B,CAAC,CAAC,CAC3F,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;QACE,WAAW,EACT,2CAA2C;YAC3C,oFAAoF;YACpF,iDAAiD;YACjD,8EAA8E;QAChF,WAAW,EAAE;YACX,WAAW,EAAE,iBAAiB;YAC9B,gBAAgB,EAAE,sBAAsB;YACxC,aAAa,EAAE,mBAAmB;SACnC;QACD,WAAW,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE;KAC/C,EACD,YAAY,CAAC,kBAAkB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAA2B,CAAC,CAAC,CAC9F,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,uBAAuB,EACvB;QACE,WAAW,EACT,4FAA4F;YAC5F,uCAAuC;YACvC,6FAA6F;YAC7F,6FAA6F;QAC/F,WAAW,EAAE;YACX,WAAW,EAAE,iBAAiB;YAC9B,gBAAgB,EAAE,sBAAsB;SACzC;QACD,WAAW,EAAE,EAAE,KAAK,EAAE,uBAAuB,EAAE;KAChD,EACD,YAAY,CAAC,uBAAuB,EAAE,CAAC,IAAI,EAAE,EAAE,CAC7C,yBAAyB,CAAC,IAAgC,CAAC,CAC5D,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ /**
3
+ * Register SPEC-405 tools on the MCP server.
4
+ * Call this from src/index.ts after all other tool registrations.
5
+ */
6
+ export declare function registerSpec405Tools(server: McpServer): void;
7
+ //# sourceMappingURL=register-spec405-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-spec405-tools.d.ts","sourceRoot":"","sources":["../../src/tools/register-spec405-tools.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AASzE;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA+P5D"}
@@ -0,0 +1,194 @@
1
+ // tools/register-spec405-tools.ts — SPEC-405
2
+ // Registers four pro tools with real engine logic:
3
+ // 1. check_api_compatibility_v2 — compare API surfaces of two specs
4
+ // 2. critical_path_analyzer_v2 — DAG-based critical path with frontmatter deps
5
+ // 3. similar_problems_finder_v2 — cross-project similarity search
6
+ // 4. suggest_mcp_catalog — recommend external MCP servers from catalog
7
+ //
8
+ // AUTO-TRIGGER NOTE (suggest_mcp_catalog):
9
+ // This tool should be auto-triggered from create-spec.ts after a spec is
10
+ // created, if the description contains any of the catalog keywords
11
+ // (database, payment, github, email, slack, search, etc.).
12
+ // Integration point: after `specStore.create(...)` resolves, call
13
+ // `suggestMcpServers(args.description)` and append suggestions to the
14
+ // response text when results.suggestions.length > 0.
15
+ import { z } from 'zod';
16
+ import { safeTracked, safeLicensed } from './safe-handler.js';
17
+ import { formatSuccess, formatError, toolResult } from './response-helpers.js';
18
+ import { checkApiCompatibility } from '../engine/api-compat/compatibility-checker.js';
19
+ import { analyzeCriticalPath } from '../engine/critical-path/path-analyzer.js';
20
+ import { findSimilarProblems } from '../engine/similar-problems/similarity-finder.js';
21
+ import { suggestMcpServers } from '../engine/mcp-catalog/catalog-advisor.js';
22
+ /**
23
+ * Register SPEC-405 tools on the MCP server.
24
+ * Call this from src/index.ts after all other tool registrations.
25
+ */
26
+ export function registerSpec405Tools(server) {
27
+ // ---------------------------------------------------------------------------
28
+ // 1. check_api_compatibility_v2
29
+ // ---------------------------------------------------------------------------
30
+ server.registerTool('check_api_compatibility_v2', {
31
+ description: 'Compare the API surfaces of two specs by scanning their spec.md files for REST endpoints, GraphQL types, and events. Returns breaking changes, additive changes, and a compatibility score 0-100.',
32
+ annotations: { readOnlyHint: true },
33
+ inputSchema: {
34
+ specId1: z
35
+ .string()
36
+ .min(1)
37
+ .max(500)
38
+ .describe('First spec ID (e.g. SPEC-001). Will read planu/specs/SPEC-001-*/spec.md'),
39
+ specId2: z
40
+ .string()
41
+ .min(1)
42
+ .max(500)
43
+ .describe('Second spec ID (e.g. SPEC-002). Will read planu/specs/SPEC-002-*/spec.md'),
44
+ projectPath: z
45
+ .string()
46
+ .min(1)
47
+ .max(4096)
48
+ .describe('Absolute path to the project root where planu/specs/ lives'),
49
+ },
50
+ }, safeLicensed('check_api_compatibility_v2', async (args) => {
51
+ const { specId1, specId2, projectPath } = args;
52
+ if (!projectPath.trim()) {
53
+ return toolResult(formatError('projectPath is required'), undefined, true);
54
+ }
55
+ if (!specId1.trim() || !specId2.trim()) {
56
+ return toolResult(formatError('specId1 and specId2 are required'), undefined, true);
57
+ }
58
+ const result = await checkApiCompatibility(specId1, specId2, projectPath);
59
+ const lines = [
60
+ `Compatibility: ${result.compatible ? 'COMPATIBLE' : 'BREAKING CHANGES DETECTED'}`,
61
+ `Score: ${String(result.score)}/100`,
62
+ `Breaking changes: ${String(result.breakingChanges.length)}`,
63
+ `Additive changes: ${String(result.additiveChanges.length)}`,
64
+ ];
65
+ if (result.breakingChanges.length > 0) {
66
+ lines.push('', 'Breaking:');
67
+ for (const c of result.breakingChanges) {
68
+ lines.push(` - [${c.type}] ${c.item}`);
69
+ }
70
+ }
71
+ if (result.additiveChanges.length > 0) {
72
+ lines.push('', 'Additive:');
73
+ for (const c of result.additiveChanges) {
74
+ lines.push(` - [${c.type}] ${c.item}`);
75
+ }
76
+ }
77
+ return toolResult(formatSuccess('API Compatibility Check', lines.join('\n')), result);
78
+ }));
79
+ // ---------------------------------------------------------------------------
80
+ // 2. critical_path_analyzer_v2
81
+ // ---------------------------------------------------------------------------
82
+ server.registerTool('critical_path_analyzer_v2', {
83
+ description: 'Analyze all specs in a project using frontmatter dependencies and estimatedHours to compute the true critical path, parallelizable specs, and scheduling bottlenecks.',
84
+ annotations: { readOnlyHint: true },
85
+ inputSchema: {
86
+ projectPath: z
87
+ .string()
88
+ .min(1)
89
+ .max(4096)
90
+ .describe('Absolute path to the project root where planu/specs/ lives'),
91
+ },
92
+ }, safeLicensed('critical_path_analyzer_v2', async (args) => {
93
+ const { projectPath } = args;
94
+ if (!projectPath.trim()) {
95
+ return toolResult(formatError('projectPath is required'), undefined, true);
96
+ }
97
+ const result = await analyzeCriticalPath(projectPath);
98
+ const lines = [
99
+ `Total hours: ${result.totalHours.toFixed(1)}h`,
100
+ `Critical path (${String(result.criticalPath.length)} specs): ${result.criticalPath.join(' → ') || '(none)'}`,
101
+ `Parallelizable specs: ${String(result.parallelizableSpecs.length)}`,
102
+ `Bottlenecks: ${result.bottlenecks.join(', ') || '(none)'}`,
103
+ ];
104
+ return toolResult(formatSuccess('Critical Path Analysis', lines.join('\n')), result);
105
+ }));
106
+ // ---------------------------------------------------------------------------
107
+ // 3. similar_problems_finder_v2
108
+ // ---------------------------------------------------------------------------
109
+ server.registerTool('similar_problems_finder_v2', {
110
+ description: 'Find specs with similar problems across all registered projects using Jaccard similarity on titles and tags. Returns top 5 matches with similarity score and matching keywords.',
111
+ annotations: { readOnlyHint: true },
112
+ inputSchema: {
113
+ projectPath: z
114
+ .string()
115
+ .min(1)
116
+ .max(4096)
117
+ .describe('Absolute path to the project root (used as the primary project)'),
118
+ description: z
119
+ .string()
120
+ .min(1)
121
+ .max(2000)
122
+ .describe('Free-text description of the problem to search for (e.g. "implement OAuth login with Google")'),
123
+ specId: z
124
+ .string()
125
+ .max(500)
126
+ .optional()
127
+ .describe('Optional spec ID to use as additional context for similarity matching'),
128
+ },
129
+ }, safeTracked('similar_problems_finder_v2', async (args) => {
130
+ const { projectPath, description, specId } = args;
131
+ if (!projectPath.trim()) {
132
+ return toolResult(formatError('projectPath is required'), undefined, true);
133
+ }
134
+ if (!description.trim()) {
135
+ return toolResult(formatError('description is required'), undefined, true);
136
+ }
137
+ const result = await findSimilarProblems(description, projectPath, specId);
138
+ const lines = [
139
+ `Query: "${result.query}"`,
140
+ `Searched: ${String(result.totalSearched)} specs across all registered projects`,
141
+ `Found: ${String(result.results.length)} similar spec(s)`,
142
+ '',
143
+ ];
144
+ for (const r of result.results) {
145
+ lines.push(` [${(r.similarityScore * 100).toFixed(0)}%] ${r.specId}: ${r.title}`, ` Project: ${r.projectPath}`, ` Matches: ${r.keyMatches.join(', ')}`);
146
+ }
147
+ if (result.results.length === 0) {
148
+ lines.push(' No similar specs found.');
149
+ }
150
+ return toolResult(formatSuccess('Similar Problems Finder', lines.join('\n')), result);
151
+ }));
152
+ // ---------------------------------------------------------------------------
153
+ // 4. suggest_mcp_catalog
154
+ // ---------------------------------------------------------------------------
155
+ server.registerTool('suggest_mcp_catalog', {
156
+ description: 'Suggest external MCP servers from a curated catalog based on your project description or tech stack. Returns top 5 matches with install commands and rationale.',
157
+ annotations: { readOnlyHint: true },
158
+ inputSchema: {
159
+ description: z
160
+ .string()
161
+ .min(1)
162
+ .max(2000)
163
+ .describe('Description of what you need (e.g. "send transactional emails and store user data in postgres")'),
164
+ stack: z
165
+ .string()
166
+ .max(500)
167
+ .optional()
168
+ .describe('Optional tech stack hint (e.g. "nextjs stripe supabase")'),
169
+ projectPath: z
170
+ .string()
171
+ .max(4096)
172
+ .optional()
173
+ .describe('Project path (unused for catalog lookup, kept for consistency)'),
174
+ },
175
+ }, safeTracked('suggest_mcp_catalog', (args) => {
176
+ const { description, stack } = args;
177
+ if (!description.trim()) {
178
+ return Promise.resolve(toolResult(formatError('description is required'), undefined, true));
179
+ }
180
+ const result = suggestMcpServers(description, stack);
181
+ if (result.suggestions.length === 0) {
182
+ return Promise.resolve(toolResult(formatSuccess('MCP Catalog Suggestions', 'No matching MCP servers found for your description. Try using more specific keywords (e.g. database, payment, email, github, slack).'), result));
183
+ }
184
+ const lines = [
185
+ `Found ${String(result.suggestions.length)} MCP server suggestion(s) for: "${result.query}"`,
186
+ '',
187
+ ];
188
+ for (const s of result.suggestions) {
189
+ lines.push(`**${s.name}** — ${s.description}`, ` Install: \`${s.installCommand}\``, ` Rationale: ${s.rationale}`, ` Use cases: ${s.useCases.join(', ')}`, '');
190
+ }
191
+ return Promise.resolve(toolResult(formatSuccess('MCP Catalog Suggestions', lines.join('\n')), result));
192
+ }));
193
+ }
194
+ //# sourceMappingURL=register-spec405-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-spec405-tools.js","sourceRoot":"","sources":["../../src/tools/register-spec405-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerVerifierTools(server: McpServer): void;
3
+ //# sourceMappingURL=register-verifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-verifier.d.ts","sourceRoot":"","sources":["../../src/tools/register-verifier.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA0EzE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA6F7D"}
@@ -0,0 +1,141 @@
1
+ // tools/register-verifier.ts — Verifier Agent tools (SPEC-406)
2
+ // AUTO-TRIGGER: verify_spec_compliance should be called automatically
3
+ // when update_status(done) is invoked. Integration point in src/tools/update-status/index.ts
4
+ // The coordinator will wire this after SPEC-406 is merged.
5
+ import { z } from 'zod';
6
+ import { safeLicensed } from './safe-handler.js';
7
+ import { scoreCompliance } from '../engine/verifier/compliance-scorer.js';
8
+ import { saveComplianceScore, getAllComplianceScores } from '../storage/compliance-score-store.js';
9
+ import { hashProjectPath } from '../storage/base-store.js';
10
+ // ---------------------------------------------------------------------------
11
+ // Formatters
12
+ // ---------------------------------------------------------------------------
13
+ function formatScore(score) {
14
+ const levelEmoji = score.level === 'compliant' ? '✅' : score.level === 'partial' ? '⚠️' : '❌';
15
+ const lines = [
16
+ `## Compliance Report — ${score.specId}`,
17
+ '',
18
+ `**Score**: ${score.score}/100 ${levelEmoji} (${score.level})`,
19
+ `**Checked at**: ${score.checkedAt}`,
20
+ '',
21
+ `| Criteria | Count |`,
22
+ `|----------|-------|`,
23
+ `| Total | ${score.totalCriteria} |`,
24
+ `| Verified | ${score.verifiedCriteria} |`,
25
+ `| Partial | ${score.partialCriteria} |`,
26
+ `| Unverified | ${score.unverifiedCriteria} |`,
27
+ ];
28
+ if (score.criteriaDetails.length > 0) {
29
+ lines.push('', '### Criteria Details', '');
30
+ for (const detail of score.criteriaDetails) {
31
+ const icon = detail.status === 'verified' ? '✅' : detail.status === 'partial' ? '⚠️' : '❌';
32
+ lines.push(`${icon} **${detail.status.toUpperCase()}**: ${detail.criterion}`);
33
+ if (detail.evidence.length > 0) {
34
+ lines.push(` Evidence: ${detail.evidence.slice(0, 3).join(', ')}`);
35
+ }
36
+ }
37
+ }
38
+ return lines.join('\n');
39
+ }
40
+ function formatReport(report) {
41
+ const lines = [
42
+ `## Compliance Score Report`,
43
+ '',
44
+ `**Project**: ${report.projectPath}`,
45
+ `**Generated**: ${report.generatedAt}`,
46
+ `**Overall Average**: ${report.overallAverage.toFixed(1)}/100`,
47
+ '',
48
+ `| Status | Count |`,
49
+ `|--------|-------|`,
50
+ `| Total Specs | ${report.totalSpecs} |`,
51
+ `| Compliant (≥80) | ${report.compliantCount} |`,
52
+ `| Partial (60-79) | ${report.partialCount} |`,
53
+ `| Non-compliant (<60) | ${report.nonCompliantCount} |`,
54
+ ];
55
+ if (report.scores.length > 0) {
56
+ lines.push('', '### Per-Spec Scores', '');
57
+ lines.push('| Spec ID | Score | Level |');
58
+ lines.push('|---------|-------|-------|');
59
+ for (const s of report.scores) {
60
+ lines.push(`| ${s.specId} | ${s.score} | ${s.level} |`);
61
+ }
62
+ }
63
+ return lines.join('\n');
64
+ }
65
+ // ---------------------------------------------------------------------------
66
+ // Tool registration
67
+ // ---------------------------------------------------------------------------
68
+ export function registerVerifierTools(server) {
69
+ // Tool 1: verify_spec_compliance
70
+ server.registerTool('verify_spec_compliance', {
71
+ description: 'Verifier Agent: checks whether implemented code matches an approved spec. ' +
72
+ 'Reads acceptance criteria from spec.md, searches the codebase for evidence, ' +
73
+ 'and computes a compliance score (0-100). ' +
74
+ 'Score ≥80 = compliant, 60-79 = partial, <60 = non-compliant. ' +
75
+ 'Results are persisted and retrievable via compliance_score_report.',
76
+ annotations: { readOnlyHint: false },
77
+ inputSchema: {
78
+ specId: z
79
+ .string()
80
+ .max(500)
81
+ .describe('Spec ID to verify (e.g. SPEC-042). Must match the directory name in planu/specs/.'),
82
+ projectPath: z
83
+ .string()
84
+ .max(4096)
85
+ .describe('Absolute path to the project root containing planu/specs/ and source code.'),
86
+ },
87
+ }, safeLicensed('verify_spec_compliance', async (args) => {
88
+ const { specId, projectPath } = args;
89
+ const score = await scoreCompliance(specId, projectPath);
90
+ const projectId = hashProjectPath(projectPath);
91
+ await saveComplianceScore(projectId, score);
92
+ return {
93
+ content: [{ type: 'text', text: formatScore(score) }],
94
+ };
95
+ }));
96
+ // Tool 2: compliance_score_report
97
+ server.registerTool('compliance_score_report', {
98
+ description: 'Returns a project-wide compliance summary showing all previously computed spec compliance scores. ' +
99
+ 'Run verify_spec_compliance for each spec first to populate the report. ' +
100
+ 'Optionally filter by minimum score to surface only specs below a threshold.',
101
+ annotations: { readOnlyHint: true },
102
+ inputSchema: {
103
+ projectPath: z.string().max(4096).describe('Absolute path to the project root.'),
104
+ minScore: z
105
+ .number()
106
+ .min(0)
107
+ .max(100)
108
+ .optional()
109
+ .describe('Only include specs with score below this value (e.g. 80 to show non-compliant only). Default: show all.'),
110
+ },
111
+ }, safeLicensed('compliance_score_report', async (args) => {
112
+ const { projectPath, minScore } = args;
113
+ const projectId = hashProjectPath(projectPath);
114
+ const allScores = await getAllComplianceScores(projectId);
115
+ let scores = Object.values(allScores);
116
+ if (minScore !== undefined) {
117
+ scores = scores.filter((s) => s.score < minScore);
118
+ }
119
+ // Sort by score ascending (worst first)
120
+ scores.sort((a, b) => a.score - b.score);
121
+ const totalSpecs = scores.length;
122
+ const compliantCount = scores.filter((s) => s.level === 'compliant').length;
123
+ const partialCount = scores.filter((s) => s.level === 'partial').length;
124
+ const nonCompliantCount = scores.filter((s) => s.level === 'non-compliant').length;
125
+ const overallAverage = totalSpecs > 0 ? scores.reduce((sum, s) => sum + s.score, 0) / totalSpecs : 0;
126
+ const report = {
127
+ projectPath,
128
+ overallAverage: Math.round(overallAverage * 10) / 10,
129
+ totalSpecs,
130
+ compliantCount,
131
+ partialCount,
132
+ nonCompliantCount,
133
+ scores,
134
+ generatedAt: new Date().toISOString(),
135
+ };
136
+ return {
137
+ content: [{ type: 'text', text: formatReport(report) }],
138
+ };
139
+ }));
140
+ }
141
+ //# sourceMappingURL=register-verifier.js.map