@planu/cli 4.10.6 → 4.10.7

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.
@@ -7,6 +7,7 @@ import { buildSpecEvidenceIndex } from '../../engine/evidence-index/index-builde
7
7
  import { checkDoneDriftContract } from '../../engine/evidence-index/done-drift.js';
8
8
  import { parseTechnicalReferenceGroundingRecords } from '../../engine/spec-grounding/contract.js';
9
9
  import { extractAcceptanceCriteriaTexts } from '../../engine/spec-format/acceptance-criteria.js';
10
+ import { formatKeyValue } from '../output-formatter.js';
10
11
  /** SPEC-1054: BDD/SDD lifecycle evidence gate. */
11
12
  export async function checkLifecycleEvidenceTransitionGate(args) {
12
13
  const [criteriaResult, artifacts] = await Promise.all([
@@ -69,15 +70,16 @@ function evidenceGateError(args) {
69
70
  content: [
70
71
  {
71
72
  type: 'text',
72
- text: JSON.stringify({
73
+ text: formatKeyValue({
73
74
  error: 'lifecycle_evidence_gate_failed',
74
75
  message: `BDD/SDD evidence gate blocked ${args.transition}.`,
75
76
  specId: args.specId,
76
77
  transition: args.transition,
77
- issues: args.issues,
78
- requiredContractKinds: args.requiredContractKinds,
78
+ issues: args.issues.length,
79
+ firstIssue: args.issues[0]?.message,
80
+ requiredContractKinds: args.requiredContractKinds.length,
79
81
  fixHint: 'Add the missing evidence artifact to the external Planu project data handoff store, then retry the transition. Do not create JSON files under planu/specs/<spec>/.',
80
- }, null, 2),
82
+ }),
81
83
  },
82
84
  ],
83
85
  isError: true,
@@ -26,6 +26,7 @@ import { updateFrontmatterField } from '../../engine/frontmatter-parser.js';
26
26
  import { atomicWriteFile } from '../../engine/safety/atomic-write-file.js';
27
27
  import { recordForceUsage } from '../../storage/force-analytics-store.js';
28
28
  import { maybeSafePushOnDone, runCascadeForResponse } from './side-effects.js';
29
+ import { formatKeyValue } from '../output-formatter.js';
29
30
  import { checkCodeReality } from '../../engine/code-scanner/index.js';
30
31
  import { scanCrashRisks } from '../../engine/crash-shield/index.js';
31
32
  import { shouldSkipCrashScan, recordCrashScanRun, } from '../../engine/autopilot/crash-shield-rate-limiter.js';
@@ -341,11 +342,11 @@ export async function handleUpdateStatus(params, server) {
341
342
  content: [
342
343
  {
343
344
  type: 'text',
344
- text: JSON.stringify({
345
+ text: formatKeyValue({
345
346
  error: 'invalid_input',
346
347
  message: reverseValidation.error,
347
348
  fixHint: reverseValidation.fixHint,
348
- }, null, 2),
349
+ }),
349
350
  },
350
351
  ],
351
352
  isError: true,
@@ -8,6 +8,7 @@ import { checkReadinessInternal } from '../../engine/readiness-checker.js';
8
8
  import { validateEnglishOnlySpecText } from '../../engine/spec-language/english-only.js';
9
9
  import { checkGroundedSpecContract } from '../../engine/spec-grounding/contract.js';
10
10
  import { checkGenericSpecOutput } from '../../engine/spec-quality/generic-output-gate.js';
11
+ import { formatKeyValue } from '../output-formatter.js';
11
12
  /**
12
13
  * Valid state transitions for spec lifecycle.
13
14
  * draft -> review -> approved -> implementing -> done
@@ -133,16 +134,29 @@ export function checkDorGate(spec, specId, projectId, newStatus) {
133
134
  content: [
134
135
  {
135
136
  type: 'text',
136
- text: JSON.stringify({
137
- error: 'DoR gate failed',
138
- message: dorResult.humanMessage,
139
- expertSummary: dorResult.expertSummary,
140
- blockingItems: dorResult.blockingItems,
141
- warningItems: dorResult.warningItems,
142
- }, null, 2),
137
+ text: [
138
+ dorResult.humanMessage,
139
+ dorResult.expertSummary,
140
+ ...dorResult.blockingItems.slice(0, 3).map((item) => `Blocker: ${item}`),
141
+ dorResult.blockingItems.length > 3
142
+ ? `More blockers: ${String(dorResult.blockingItems.length - 3)}`
143
+ : undefined,
144
+ dorResult.warningItems.length > 0
145
+ ? `Warnings: ${String(dorResult.warningItems.length)}`
146
+ : undefined,
147
+ ]
148
+ .filter(Boolean)
149
+ .join('\n'),
143
150
  },
144
151
  ],
145
152
  isError: true,
153
+ structuredContent: {
154
+ error: 'DoR gate failed',
155
+ message: dorResult.humanMessage,
156
+ expertSummary: dorResult.expertSummary,
157
+ blockingItems: dorResult.blockingItems,
158
+ warningItems: dorResult.warningItems,
159
+ },
146
160
  };
147
161
  }
148
162
  return null;
@@ -163,16 +177,24 @@ export async function checkAmbiguityGate(spec, newStatus) {
163
177
  content: [
164
178
  {
165
179
  type: 'text',
166
- text: JSON.stringify({
180
+ text: formatKeyValue({
167
181
  error: 'Ambiguity gate failed',
168
182
  message: `Ambiguity score ${String(report.score)}/100 — below 70. Fix vague terms in criteria before approving.`,
169
183
  ambiguityScore: report.score,
170
- violations: report.violations.slice(0, 5),
171
- blockers: report.blockers.slice(0, 5),
172
- }, null, 2),
184
+ violations: report.violations.length,
185
+ blockers: report.blockers.length,
186
+ firstBlocker: report.blockers[0],
187
+ }, 'Ambiguity gate failed'),
173
188
  },
174
189
  ],
175
190
  isError: true,
191
+ structuredContent: {
192
+ error: 'Ambiguity gate failed',
193
+ message: `Ambiguity score ${String(report.score)}/100 — below 70. Fix vague terms in criteria before approving.`,
194
+ ambiguityScore: report.score,
195
+ violations: report.violations.slice(0, 5),
196
+ blockers: report.blockers.slice(0, 5),
197
+ },
176
198
  };
177
199
  }
178
200
  /**
@@ -1,11 +1,33 @@
1
1
  import { specStore } from '../storage/index.js';
2
2
  import { validateTeamResults } from '../engine/team-planner/index.js';
3
3
  import { readFile } from 'node:fs/promises';
4
+ import { formatKeyValue } from './output-formatter.js';
4
5
  // ---------------------------------------------------------------------------
5
6
  // Helpers
6
7
  // ---------------------------------------------------------------------------
7
8
  function text(content) {
8
- return { content: [{ type: 'text', text: JSON.stringify(content, null, 2) }] };
9
+ const conflicts = Array.isArray(content.conflicts) ? content.conflicts.length : undefined;
10
+ const mergeOrder = Array.isArray(content.mergeOrder) ? content.mergeOrder.length : undefined;
11
+ const mergeOrderPreview = Array.isArray(content.mergeOrder)
12
+ ? content.mergeOrder.slice(0, 5).join(', ')
13
+ : undefined;
14
+ const warnings = Array.isArray(content.warnings) ? content.warnings.length : undefined;
15
+ return {
16
+ content: [
17
+ {
18
+ type: 'text',
19
+ text: formatKeyValue({
20
+ passed: content.passed,
21
+ conflicts,
22
+ mergeOrder,
23
+ mergeOrderPreview,
24
+ warnings,
25
+ summary: content.summary,
26
+ }) + '\nVerification commands: pnpm typecheck; pnpm lint; pnpm test',
27
+ },
28
+ ],
29
+ structuredContent: content,
30
+ };
9
31
  }
10
32
  function err(message) {
11
33
  return { content: [{ type: 'text', text: message }], isError: true };
@@ -6,6 +6,7 @@ import { PLANU_VERSION } from '../config/version.js';
6
6
  import { runWithSessionContext } from './session-context.js';
7
7
  import { checkHttpRateLimit } from './http-rate-limiter.js';
8
8
  import { loadOAuthConfig, extractBearerToken, validateToken } from './oauth-validator.js';
9
+ import { compactToolsListMessage, isToolsListResponse, } from '../engine/compact/tool-list-compactor.js';
9
10
  const MAX_BODY_BYTES = 5 * 1024 * 1024; // 5MB
10
11
  const SHUTDOWN_GRACE_MS = 5_000;
11
12
  const sessions = new Map();
@@ -95,6 +96,7 @@ async function handlePost(req, res, sessionId, serverFactory) {
95
96
  const transport = new StreamableHTTPServerTransport({
96
97
  sessionIdGenerator: () => randomUUID(),
97
98
  });
99
+ installToolsListCompaction(transport);
98
100
  transport.onclose = () => {
99
101
  const sid = transport.sessionId;
100
102
  if (sid) {
@@ -177,6 +179,17 @@ async function handleMcpRequest(req, res, serverFactory, corsOrigin) {
177
179
  res.writeHead(405, { 'Content-Type': 'application/json' });
178
180
  res.end(JSON.stringify({ error: 'Method not allowed' }));
179
181
  }
182
+ function installToolsListCompaction(transport) {
183
+ const originalSend = transport.send.bind(transport);
184
+ const compactingSend = (message) => {
185
+ const outgoing = isToolsListResponse(message)
186
+ ? compactToolsListMessage(message)
187
+ : message;
188
+ return originalSend(outgoing);
189
+ };
190
+ const wrappedTransport = transport;
191
+ wrappedTransport.send = compactingSend;
192
+ }
180
193
  export async function createHttpTransport(serverFactory, config) {
181
194
  const httpServer = createServer((req, res) => {
182
195
  void (async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planu/cli",
3
- "version": "4.10.6",
3
+ "version": "4.10.7",
4
4
  "description": "Planu — MCP Server for Spec Driven Development with native Rust acceleration for hot paths. Cross-platform (Linux/macOS/Windows, x64/arm64, glibc/musl).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -34,14 +34,14 @@
34
34
  "packageName": "@planu/core"
35
35
  },
36
36
  "optionalDependencies": {
37
- "@planu/core-darwin-arm64": "4.10.5",
38
- "@planu/core-darwin-x64": "4.10.5",
39
- "@planu/core-linux-arm64-gnu": "4.10.5",
40
- "@planu/core-linux-arm64-musl": "4.10.5",
41
- "@planu/core-linux-x64-gnu": "4.10.5",
42
- "@planu/core-linux-x64-musl": "4.10.5",
43
- "@planu/core-win32-arm64-msvc": "4.10.5",
44
- "@planu/core-win32-x64-msvc": "4.10.5"
37
+ "@planu/core-darwin-arm64": "4.10.7",
38
+ "@planu/core-darwin-x64": "4.10.7",
39
+ "@planu/core-linux-arm64-gnu": "4.10.7",
40
+ "@planu/core-linux-arm64-musl": "4.10.7",
41
+ "@planu/core-linux-x64-gnu": "4.10.7",
42
+ "@planu/core-linux-x64-musl": "4.10.7",
43
+ "@planu/core-win32-arm64-msvc": "4.10.7",
44
+ "@planu/core-win32-x64-msvc": "4.10.7"
45
45
  },
46
46
  "engines": {
47
47
  "node": ">=24.0.0"
@@ -164,7 +164,7 @@
164
164
  "tsc-alias": "^1.9.0",
165
165
  "type-coverage": "^2.29.7",
166
166
  "typescript": "^6.0.3",
167
- "typescript-eslint": "^8.62.1",
167
+ "typescript-eslint": "^8.63.0",
168
168
  "vite": "^8.1.3",
169
169
  "vitest": "^4.1.10",
170
170
  "vue": "^3.5.39"
package/planu-native.json CHANGED
@@ -1,20 +1,26 @@
1
1
  {
2
2
  "name": "dev.planu.native",
3
3
  "displayName": "Planu Native Lightweight Surface",
4
- "version": "4.10.6",
4
+ "version": "4.10.7",
5
5
  "packageName": "@planu/cli",
6
6
  "modes": {
7
7
  "lightweight": {
8
8
  "requiresMcp": false,
9
9
  "requiresDaemon": false,
10
- "hosts": ["codex", "claude-code"],
10
+ "hosts": [
11
+ "codex",
12
+ "claude-code"
13
+ ],
11
14
  "commands": [
12
15
  {
13
16
  "id": "planu.status",
14
17
  "title": "Project status",
15
18
  "description": "Show the compact Planu project snapshot without loading the MCP tool graph.",
16
19
  "invocation": "planu status",
17
- "hosts": ["codex", "claude-code"],
20
+ "hosts": [
21
+ "codex",
22
+ "claude-code"
23
+ ],
18
24
  "requiresMcp": false,
19
25
  "requiresDaemon": false,
20
26
  "mapsTo": "handlePlanStatus"
@@ -24,7 +30,10 @@
24
30
  "title": "Create spec",
25
31
  "description": "Create a new spec through the CLI-backed SDD contract.",
26
32
  "invocation": "planu spec create \"<title>\"",
27
- "hosts": ["codex", "claude-code"],
33
+ "hosts": [
34
+ "codex",
35
+ "claude-code"
36
+ ],
28
37
  "requiresMcp": false,
29
38
  "requiresDaemon": false,
30
39
  "mapsTo": "handleCreateSpec"
@@ -34,7 +43,10 @@
34
43
  "title": "List specs",
35
44
  "description": "List specs in the current project with optional status/type filters.",
36
45
  "invocation": "planu spec list",
37
- "hosts": ["codex", "claude-code"],
46
+ "hosts": [
47
+ "codex",
48
+ "claude-code"
49
+ ],
38
50
  "requiresMcp": false,
39
51
  "requiresDaemon": false,
40
52
  "mapsTo": "handleListSpecs"
@@ -44,7 +56,10 @@
44
56
  "title": "Validate spec",
45
57
  "description": "Validate a spec against the current codebase from the native CLI surface.",
46
58
  "invocation": "planu spec validate SPEC-001",
47
- "hosts": ["codex", "claude-code"],
59
+ "hosts": [
60
+ "codex",
61
+ "claude-code"
62
+ ],
48
63
  "requiresMcp": false,
49
64
  "requiresDaemon": false,
50
65
  "mapsTo": "handleValidate"
@@ -54,7 +69,10 @@
54
69
  "title": "Audit technical debt",
55
70
  "description": "Run the read-only project audit path for lightweight debt checks.",
56
71
  "invocation": "planu audit debt",
57
- "hosts": ["codex", "claude-code"],
72
+ "hosts": [
73
+ "codex",
74
+ "claude-code"
75
+ ],
58
76
  "requiresMcp": false,
59
77
  "requiresDaemon": false,
60
78
  "mapsTo": "handleAudit"
@@ -64,7 +82,10 @@
64
82
  "title": "Check release readiness",
65
83
  "description": "Check local-first release readiness, branch cleanliness, and optional gitflow drift.",
66
84
  "invocation": "planu release check",
67
- "hosts": ["codex", "claude-code"],
85
+ "hosts": [
86
+ "codex",
87
+ "claude-code"
88
+ ],
68
89
  "requiresMcp": false,
69
90
  "requiresDaemon": false,
70
91
  "mapsTo": "releaseCommand"
package/planu-plugin.json CHANGED
@@ -2,9 +2,12 @@
2
2
  "name": "dev.planu.cli",
3
3
  "displayName": "Planu — Spec Driven Development",
4
4
  "description": "Manage software specs, estimations, and autonomous SDD workflows. Language-agnostic MCP server for Claude Code.",
5
- "version": "4.10.6",
5
+ "version": "4.10.7",
6
6
  "icon": "assets/plugin/icon.svg",
7
- "command": ["npx", "@planu/cli@latest"],
7
+ "command": [
8
+ "npx",
9
+ "@planu/cli@latest"
10
+ ],
8
11
  "packageName": "@planu/cli",
9
12
  "capabilities": {
10
13
  "tools": [
@@ -23,17 +26,42 @@
23
26
  "create_skill",
24
27
  "skill_search"
25
28
  ],
26
- "resources": ["planu://specs/list", "planu://specs/{id}", "planu://project/status", "planu://roadmap"],
27
- "prompts": ["create-spec-from-idea", "review-spec-readiness", "generate-implementation-plan"],
28
- "subagents": ["sdd-orchestrator", "spec-challenger", "test-generator"]
29
+ "resources": [
30
+ "planu://specs/list",
31
+ "planu://specs/{id}",
32
+ "planu://project/status",
33
+ "planu://roadmap"
34
+ ],
35
+ "prompts": [
36
+ "create-spec-from-idea",
37
+ "review-spec-readiness",
38
+ "generate-implementation-plan"
39
+ ],
40
+ "subagents": [
41
+ "sdd-orchestrator",
42
+ "spec-challenger",
43
+ "test-generator"
44
+ ]
29
45
  },
30
46
  "compatibility": {
31
47
  "minimumHostVersion": "1.0.0",
32
- "requiredFeatures": ["mcp-tools", "file-editing"]
48
+ "requiredFeatures": [
49
+ "mcp-tools",
50
+ "file-editing"
51
+ ]
33
52
  },
34
53
  "repository": "https://github.com/planu-dev/planu",
35
54
  "author": "Planu",
36
55
  "license": "MIT",
37
56
  "homepage": "https://planu.dev",
38
- "keywords": ["sdd", "spec-driven-development", "mcp", "specs", "planning", "ai", "bdd", "tdd"]
57
+ "keywords": [
58
+ "sdd",
59
+ "spec-driven-development",
60
+ "mcp",
61
+ "specs",
62
+ "planning",
63
+ "ai",
64
+ "bdd",
65
+ "tdd"
66
+ ]
39
67
  }