@planu/cli 4.3.20 → 4.3.22
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.
- package/CHANGELOG.md +12 -0
- package/dist/engine/spec-format/lean-technical-generator.js +5 -7
- package/dist/engine/spec-format/unified-spec-builder.js +1 -1
- package/dist/engine/spec-generator/fallback-generator.js +4 -30
- package/dist/engine/test-generators/contract-test-generator.d.ts +3 -2
- package/dist/engine/test-generators/contract-test-generator.js +8 -43
- package/dist/engine/test-generators/mock-generator.d.ts +1 -1
- package/dist/engine/test-generators/mock-generator.js +13 -31
- package/dist/tools/bump-spec-version.js +2 -1
- package/dist/tools/create-spec.js +30 -10
- package/dist/tools/generate-batch-script.js +2 -1
- package/dist/tools/generate-proposal.js +2 -1
- package/dist/tools/generate-tests/adapters/contract-testing-adapter.js +9 -3
- package/dist/tools/heal-spec-docs.js +96 -40
- package/dist/tools/migrate-tech/core-handlers.js +24 -72
- package/dist/tools/register-agent-squad-tools.js +2 -1
- package/dist/tools/register-platform-tools/design-stack-tools.js +10 -10
- package/dist/tools/register-platform-tools/lifecycle-infra-tools.js +8 -8
- package/dist/tools/register-spec-tools/analysis-tools.js +7 -7
- package/dist/tools/register-spec-tools/core-spec-tools.js +6 -6
- package/dist/tools/render-spec-for-provider.js +2 -1
- package/dist/tools/schemas/github.js +4 -3
- package/dist/tools/schemas/index.d.ts +1 -0
- package/dist/tools/schemas/index.js +1 -0
- package/dist/tools/schemas/spec-id.d.ts +7 -0
- package/dist/tools/schemas/spec-id.js +17 -0
- package/dist/tools/spec-prompt-handler.js +2 -1
- package/dist/tools/tool-registry/core-tools.js +3 -2
- package/dist/tools/tool-registry/group-infra.js +3 -3
- package/dist/tools/tool-registry/group-quality-compliance.js +11 -14
- package/package.json +9 -9
- package/planu-native.json +1 -1
- package/planu-plugin.json +1 -1
|
@@ -1,41 +1,20 @@
|
|
|
1
1
|
// tools/migrate-tech/core-handlers.ts — Handlers: analyze | map | plan | strategy | validate
|
|
2
2
|
import { t } from '../../i18n/index.js';
|
|
3
|
-
import { buildStackAnalysis,
|
|
3
|
+
import { buildStackAnalysis, buildEquivalenceMap, generateMigrationPlan, recommendMigrationStrategy, generateParityValidation, } from '../../engine/migration/index.js';
|
|
4
4
|
export function handleAnalyze(args) {
|
|
5
|
-
const components = scoreMigratableComponents([
|
|
6
|
-
{
|
|
7
|
-
id: 'app-core',
|
|
8
|
-
name: 'Application Core',
|
|
9
|
-
path: args.projectPath ?? './src',
|
|
10
|
-
type: 'module',
|
|
11
|
-
cyclomaticComplexity: 8,
|
|
12
|
-
couplingScore: 5,
|
|
13
|
-
dependsOn: [],
|
|
14
|
-
dependents: ['api-handler'],
|
|
15
|
-
linesOfCode: 400,
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
id: 'api-handler',
|
|
19
|
-
name: 'API Handler',
|
|
20
|
-
path: args.projectPath ? `${args.projectPath}/api` : './src/api',
|
|
21
|
-
type: 'handler',
|
|
22
|
-
cyclomaticComplexity: 12,
|
|
23
|
-
couplingScore: 6,
|
|
24
|
-
dependsOn: ['app-core'],
|
|
25
|
-
dependents: [],
|
|
26
|
-
linesOfCode: 250,
|
|
27
|
-
},
|
|
28
|
-
]);
|
|
29
5
|
const data = buildStackAnalysis({
|
|
30
6
|
language: args.sourceStack ?? 'unknown',
|
|
31
7
|
framework: args.sourceStack ?? 'unknown',
|
|
32
8
|
components: [],
|
|
33
|
-
entryPoints: [
|
|
9
|
+
entryPoints: [],
|
|
34
10
|
externalIntegrations: [],
|
|
35
|
-
migratableComponents:
|
|
11
|
+
migratableComponents: [],
|
|
36
12
|
hasTests: false,
|
|
37
13
|
});
|
|
38
|
-
const warnings =
|
|
14
|
+
const warnings = [
|
|
15
|
+
...data.riskFlags,
|
|
16
|
+
'No project-derived migration inventory was provided; analysis does not fabricate components or entry points.',
|
|
17
|
+
];
|
|
39
18
|
return {
|
|
40
19
|
action: 'analyze',
|
|
41
20
|
data,
|
|
@@ -60,48 +39,15 @@ export function handleMap(args) {
|
|
|
60
39
|
};
|
|
61
40
|
}
|
|
62
41
|
export function handlePlan(args) {
|
|
63
|
-
const components = scoreMigratableComponents([
|
|
64
|
-
{
|
|
65
|
-
id: 'utils',
|
|
66
|
-
name: 'Utils',
|
|
67
|
-
path: './src/utils',
|
|
68
|
-
type: 'util',
|
|
69
|
-
cyclomaticComplexity: 3,
|
|
70
|
-
couplingScore: 1,
|
|
71
|
-
dependsOn: [],
|
|
72
|
-
dependents: ['service'],
|
|
73
|
-
linesOfCode: 100,
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
id: 'service',
|
|
77
|
-
name: 'Service',
|
|
78
|
-
path: './src/service',
|
|
79
|
-
type: 'service',
|
|
80
|
-
cyclomaticComplexity: 10,
|
|
81
|
-
couplingScore: 4,
|
|
82
|
-
dependsOn: ['utils'],
|
|
83
|
-
dependents: ['handler'],
|
|
84
|
-
linesOfCode: 300,
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
id: 'handler',
|
|
88
|
-
name: 'Handler',
|
|
89
|
-
path: './src/handler',
|
|
90
|
-
type: 'handler',
|
|
91
|
-
cyclomaticComplexity: 6,
|
|
92
|
-
couplingScore: 3,
|
|
93
|
-
dependsOn: ['service'],
|
|
94
|
-
dependents: [],
|
|
95
|
-
linesOfCode: 150,
|
|
96
|
-
},
|
|
97
|
-
]);
|
|
98
42
|
const data = generateMigrationPlan({
|
|
99
43
|
sourceStack: args.sourceStack ?? 'unknown',
|
|
100
44
|
targetStack: args.targetStack ?? 'unknown',
|
|
101
|
-
components,
|
|
45
|
+
components: [],
|
|
102
46
|
hasTests: false,
|
|
103
47
|
});
|
|
104
|
-
const warnings = [
|
|
48
|
+
const warnings = [
|
|
49
|
+
'No project-derived migration components were provided; plan does not fabricate modules.',
|
|
50
|
+
];
|
|
105
51
|
if (data.circularDependencies.length > 0) {
|
|
106
52
|
warnings.push(`${data.circularDependencies.length} circular dependency cycles detected — resolve before migrating`);
|
|
107
53
|
}
|
|
@@ -114,32 +60,38 @@ export function handlePlan(args) {
|
|
|
114
60
|
}
|
|
115
61
|
export function handleStrategy(_args) {
|
|
116
62
|
const data = recommendMigrationStrategy({
|
|
117
|
-
componentCount:
|
|
118
|
-
hasActiveUsers:
|
|
63
|
+
componentCount: 0,
|
|
64
|
+
hasActiveUsers: false,
|
|
119
65
|
hasHighSla: false,
|
|
120
66
|
hasCriticalData: false,
|
|
121
67
|
migratableComponents: [],
|
|
122
|
-
entryPoints: [
|
|
68
|
+
entryPoints: [],
|
|
123
69
|
});
|
|
124
70
|
return {
|
|
125
71
|
action: 'strategy',
|
|
126
72
|
data,
|
|
127
73
|
message: t('tools.migrate_tech.strategySuccess'),
|
|
128
|
-
warnings: [
|
|
74
|
+
warnings: [
|
|
75
|
+
'No project-derived strategy inputs were provided; recommendation uses neutral empty evidence.',
|
|
76
|
+
],
|
|
129
77
|
};
|
|
130
78
|
}
|
|
131
79
|
export function handleValidate(args) {
|
|
132
|
-
const component = args.component ?? '
|
|
80
|
+
const component = args.component?.trim() ?? '';
|
|
133
81
|
const data = generateParityValidation({
|
|
134
82
|
component,
|
|
135
|
-
endpoints: [
|
|
83
|
+
endpoints: [],
|
|
136
84
|
observedDifferences: [],
|
|
137
85
|
});
|
|
138
86
|
return {
|
|
139
87
|
action: 'validate',
|
|
140
88
|
data,
|
|
141
89
|
message: t('tools.migrate_tech.validateSuccess'),
|
|
142
|
-
warnings:
|
|
90
|
+
warnings: args.component === undefined
|
|
91
|
+
? [
|
|
92
|
+
'No component was provided; validation checklist is unscoped and no contract tests were generated.',
|
|
93
|
+
]
|
|
94
|
+
: ['No endpoints were provided; contract tests were not generated.'],
|
|
143
95
|
};
|
|
144
96
|
}
|
|
145
97
|
//# sourceMappingURL=core-handlers.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { safeLicensed, safeTracked } from './safe-handler.js';
|
|
3
|
+
import { SpecIdSchema } from './schemas/index.js';
|
|
3
4
|
import { onAutopilotEvent } from '../engine/autopilot/event-bus.js';
|
|
4
5
|
import { dispatchSquadForPhase } from '../engine/agent-squad/dispatcher.js';
|
|
5
6
|
import { getSquadConfig, setAgentEnabled, getSpecRunHistory, getSquadRuns, } from '../storage/agent-squad-store.js';
|
|
@@ -143,7 +144,7 @@ export function registerAgentSquadTools(server) {
|
|
|
143
144
|
'which agents ran, their verdict, and when.',
|
|
144
145
|
inputSchema: {
|
|
145
146
|
projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root.'),
|
|
146
|
-
specId:
|
|
147
|
+
specId: SpecIdSchema.describe('Spec ID to query (e.g. SPEC-550).'),
|
|
147
148
|
},
|
|
148
149
|
}, safeTracked('agent_run_history', async (args) => {
|
|
149
150
|
const { projectPath, specId } = args;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { t } from '../../i18n/index.js';
|
|
3
3
|
import { safeTracked, safeLicensed } from '../safe-handler.js';
|
|
4
|
-
import { AgentPlatformEnum, SubAgentFrameworkEnum, ChallengeSpecFocusEnum, } from '../schemas/index.js';
|
|
4
|
+
import { AgentPlatformEnum, SubAgentFrameworkEnum, ChallengeSpecFocusEnum, SpecIdSchema, } from '../schemas/index.js';
|
|
5
5
|
import { handleGenerateADR } from '../generate-adr.js';
|
|
6
6
|
import { handleDesignSchema } from '../design-schema.js';
|
|
7
7
|
import { handleDefineUIContract } from '../define-ui-contract.js';
|
|
@@ -25,7 +25,7 @@ export function registerDesignStackTools(server) {
|
|
|
25
25
|
description: t('tools.generate_adr.description'),
|
|
26
26
|
annotations: { readOnlyHint: true },
|
|
27
27
|
inputSchema: {
|
|
28
|
-
specId:
|
|
28
|
+
specId: SpecIdSchema.describe('Spec ID'),
|
|
29
29
|
projectId: z.string().max(500).describe('Project ID'),
|
|
30
30
|
decisions: z
|
|
31
31
|
.array(z.string().max(10_000))
|
|
@@ -39,7 +39,7 @@ export function registerDesignStackTools(server) {
|
|
|
39
39
|
description: t('tools.design_schema.description'),
|
|
40
40
|
annotations: { readOnlyHint: true },
|
|
41
41
|
inputSchema: {
|
|
42
|
-
specId:
|
|
42
|
+
specId: SpecIdSchema.describe('Spec ID'),
|
|
43
43
|
projectId: z.string().max(500).describe('Project ID'),
|
|
44
44
|
description: z
|
|
45
45
|
.string()
|
|
@@ -53,7 +53,7 @@ export function registerDesignStackTools(server) {
|
|
|
53
53
|
description: t('tools.define_ui_contract.description'),
|
|
54
54
|
annotations: { readOnlyHint: true },
|
|
55
55
|
inputSchema: {
|
|
56
|
-
specId:
|
|
56
|
+
specId: SpecIdSchema.describe('Spec ID'),
|
|
57
57
|
projectId: z.string().max(500).describe('Project ID'),
|
|
58
58
|
screens: z
|
|
59
59
|
.array(z.string().max(500))
|
|
@@ -67,7 +67,7 @@ export function registerDesignStackTools(server) {
|
|
|
67
67
|
description: t('tools.challenge_spec.description'),
|
|
68
68
|
annotations: { readOnlyHint: true },
|
|
69
69
|
inputSchema: {
|
|
70
|
-
specId:
|
|
70
|
+
specId: SpecIdSchema.describe('Spec ID to challenge'),
|
|
71
71
|
projectId: z
|
|
72
72
|
.string()
|
|
73
73
|
.max(500)
|
|
@@ -90,7 +90,7 @@ export function registerDesignStackTools(server) {
|
|
|
90
90
|
description: t('tools.generate_execution_plan.description'),
|
|
91
91
|
annotations: { readOnlyHint: true },
|
|
92
92
|
inputSchema: {
|
|
93
|
-
specId:
|
|
93
|
+
specId: SpecIdSchema.describe('Spec ID'),
|
|
94
94
|
projectId: z.string().max(500).describe('Project ID'),
|
|
95
95
|
},
|
|
96
96
|
}, safeTracked('generate_execution_plan', async (args) => handleGenerateExecutionPlan(args)));
|
|
@@ -145,7 +145,7 @@ export function registerDesignStackTools(server) {
|
|
|
145
145
|
.describe('Skill type: project-rules | domain-skill | workflow | convention'),
|
|
146
146
|
name: z.string().max(500).describe('Skill name'),
|
|
147
147
|
description: z.string().max(10_000).describe('Skill description'),
|
|
148
|
-
specId:
|
|
148
|
+
specId: SpecIdSchema.optional().describe('Spec ID for context'),
|
|
149
149
|
},
|
|
150
150
|
}, safeLicensed('generate_skill', async (args) => handleGenerateSkill(args)));
|
|
151
151
|
// 28. detect_agent
|
|
@@ -208,10 +208,10 @@ export function registerDesignStackTools(server) {
|
|
|
208
208
|
annotations: { readOnlyHint: true },
|
|
209
209
|
inputSchema: {
|
|
210
210
|
specIds: z
|
|
211
|
-
.array(
|
|
211
|
+
.array(SpecIdSchema)
|
|
212
212
|
.min(1)
|
|
213
213
|
.max(100)
|
|
214
|
-
.describe('List of spec IDs to run in parallel (e.g. ["SPEC-013", "SPEC-
|
|
214
|
+
.describe('List of canonical spec IDs to run in parallel (e.g. ["SPEC-013", "SPEC-014"])'),
|
|
215
215
|
projectPath: z.string().max(4096).describe('Absolute path to the project root'),
|
|
216
216
|
mainBranch: z.string().max(500).optional().describe('Main branch name (default: main)'),
|
|
217
217
|
baseBranch: z
|
|
@@ -226,7 +226,7 @@ export function registerDesignStackTools(server) {
|
|
|
226
226
|
description: 'Adversarial review — argues against the spec from first principles before approval. Runs pre-mortem, surfaces hidden assumptions, proposes alternatives.',
|
|
227
227
|
annotations: { readOnlyHint: true },
|
|
228
228
|
inputSchema: {
|
|
229
|
-
specId:
|
|
229
|
+
specId: SpecIdSchema.describe('Spec ID to red-team'),
|
|
230
230
|
projectId: z.string().max(500).describe('Project ID'),
|
|
231
231
|
intensity: z
|
|
232
232
|
.enum(['light', 'full'])
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { t } from '../../i18n/index.js';
|
|
3
3
|
import { safeTracked, safeLicensed } from '../safe-handler.js';
|
|
4
|
-
import { DocumentationTypeEnum, GitActionEnum, DevLifecycleCategoryEnum, PMPlatformEnum, PMActionEnum, OrchestrateActionEnum, ManageContextActionEnum, ContextBudgetActionEnum, } from '../schemas/index.js';
|
|
4
|
+
import { DocumentationTypeEnum, GitActionEnum, DevLifecycleCategoryEnum, PMPlatformEnum, PMActionEnum, OrchestrateActionEnum, ManageContextActionEnum, ContextBudgetActionEnum, SpecIdSchema, } from '../schemas/index.js';
|
|
5
5
|
import { handleSuggestTooling } from '../suggest-tooling.js';
|
|
6
6
|
import { handleGenerateTests } from '../generate-tests.js';
|
|
7
7
|
import { handleGenerateDocs } from '../generate-docs.js';
|
|
@@ -21,7 +21,7 @@ export function registerLifecycleInfraTools(server) {
|
|
|
21
21
|
annotations: { readOnlyHint: true },
|
|
22
22
|
inputSchema: {
|
|
23
23
|
projectId: z.string().max(500).describe('Project ID'),
|
|
24
|
-
specId:
|
|
24
|
+
specId: SpecIdSchema.optional().describe('Spec ID for context'),
|
|
25
25
|
categories: z
|
|
26
26
|
.array(DevLifecycleCategoryEnum)
|
|
27
27
|
.optional()
|
|
@@ -33,7 +33,7 @@ export function registerLifecycleInfraTools(server) {
|
|
|
33
33
|
description: t('tools.generate_tests.description'),
|
|
34
34
|
annotations: { readOnlyHint: true },
|
|
35
35
|
inputSchema: {
|
|
36
|
-
specId:
|
|
36
|
+
specId: SpecIdSchema.describe('Spec ID'),
|
|
37
37
|
projectId: z.string().max(500).describe('Project ID'),
|
|
38
38
|
autoGenerate: z.boolean().optional().describe('Auto-generate test files (default: false)'),
|
|
39
39
|
},
|
|
@@ -49,7 +49,7 @@ export function registerLifecycleInfraTools(server) {
|
|
|
49
49
|
.max(100)
|
|
50
50
|
.describe('Types of documentation to generate'),
|
|
51
51
|
docType: DocumentationTypeEnum.optional().describe('Single documentation type for routing — shorthand for types: [docType]'),
|
|
52
|
-
specId:
|
|
52
|
+
specId: SpecIdSchema.optional().describe('Spec ID for context'),
|
|
53
53
|
audience: z
|
|
54
54
|
.enum(['end-user', 'developer', 'stakeholder', 'ops'])
|
|
55
55
|
.optional()
|
|
@@ -88,7 +88,7 @@ export function registerLifecycleInfraTools(server) {
|
|
|
88
88
|
inputSchema: {
|
|
89
89
|
projectId: z.string().max(500).describe('Project ID'),
|
|
90
90
|
action: GitActionEnum.describe('Git action to perform'),
|
|
91
|
-
specId:
|
|
91
|
+
specId: SpecIdSchema.optional().describe('Spec ID for context'),
|
|
92
92
|
config: z
|
|
93
93
|
.object({
|
|
94
94
|
branchPrefix: z.record(z.string().max(500), z.string().max(500)).optional(),
|
|
@@ -113,10 +113,10 @@ export function registerLifecycleInfraTools(server) {
|
|
|
113
113
|
projectId: z.string().max(500).describe('Project ID'),
|
|
114
114
|
action: OrchestrateActionEnum.describe('Orchestration action'),
|
|
115
115
|
sessionId: z.string().max(500).optional().describe('Agent session ID'),
|
|
116
|
-
specId:
|
|
116
|
+
specId: SpecIdSchema.optional().describe('Spec ID for context'),
|
|
117
117
|
resourcePath: z.string().max(4096).optional().describe('Resource path to lock/unlock'),
|
|
118
118
|
specIds: z
|
|
119
|
-
.array(
|
|
119
|
+
.array(SpecIdSchema)
|
|
120
120
|
.max(100)
|
|
121
121
|
.optional()
|
|
122
122
|
.describe('List of spec IDs for pre-check'),
|
|
@@ -163,7 +163,7 @@ export function registerLifecycleInfraTools(server) {
|
|
|
163
163
|
projectId: z.string().max(500).describe('Project ID'),
|
|
164
164
|
platform: PMPlatformEnum.describe('PM platform'),
|
|
165
165
|
action: PMActionEnum.describe('Integration action'),
|
|
166
|
-
specId:
|
|
166
|
+
specId: SpecIdSchema.optional().describe('Spec ID'),
|
|
167
167
|
config: z
|
|
168
168
|
.record(z.string().max(500), z.string().max(10_000))
|
|
169
169
|
.optional()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { t } from '../../i18n/index.js';
|
|
3
3
|
import { safeTracked, safeLicensed } from '../safe-handler.js';
|
|
4
|
-
import { ChecklistFocusEnum, AuditCategoryEnum } from '../schemas/index.js';
|
|
4
|
+
import { ChecklistFocusEnum, AuditCategoryEnum, SpecIdSchema } from '../schemas/index.js';
|
|
5
5
|
import { resolveProjectId, missingProjectIdError } from '../resolve-project-id.js';
|
|
6
6
|
import { handleDetectDrift } from '../detect-drift.js';
|
|
7
7
|
import { handleSummarizeSpec } from '../summarize-spec.js';
|
|
@@ -27,7 +27,7 @@ export function registerAnalysisTools(server) {
|
|
|
27
27
|
server.registerTool('detect_drift', {
|
|
28
28
|
description: t('tools.detect_drift.description'),
|
|
29
29
|
inputSchema: {
|
|
30
|
-
specId:
|
|
30
|
+
specId: SpecIdSchema.describe('Spec ID to check'),
|
|
31
31
|
projectId: z.string().max(500).optional().describe('Project ID hash. Prefer projectPath.'),
|
|
32
32
|
projectPath: z
|
|
33
33
|
.string()
|
|
@@ -56,7 +56,7 @@ export function registerAnalysisTools(server) {
|
|
|
56
56
|
description: t('tools.summarize_spec.description'),
|
|
57
57
|
annotations: { readOnlyHint: true },
|
|
58
58
|
inputSchema: {
|
|
59
|
-
specId:
|
|
59
|
+
specId: SpecIdSchema.describe('Spec ID to summarize'),
|
|
60
60
|
projectId: z.string().max(500).optional().describe('Project ID hash. Prefer projectPath.'),
|
|
61
61
|
projectPath: z
|
|
62
62
|
.string()
|
|
@@ -79,7 +79,7 @@ export function registerAnalysisTools(server) {
|
|
|
79
79
|
server.registerTool('generate_checklist', {
|
|
80
80
|
description: t('tools.generate_checklist.description'),
|
|
81
81
|
inputSchema: {
|
|
82
|
-
specId:
|
|
82
|
+
specId: SpecIdSchema.describe('Spec ID'),
|
|
83
83
|
projectId: z.string().max(500).optional().describe('Project ID hash. Prefer projectPath.'),
|
|
84
84
|
projectPath: z
|
|
85
85
|
.string()
|
|
@@ -103,7 +103,7 @@ export function registerAnalysisTools(server) {
|
|
|
103
103
|
server.registerTool('reconcile_spec', {
|
|
104
104
|
description: t('tools.reconcile_spec.description'),
|
|
105
105
|
inputSchema: {
|
|
106
|
-
specId:
|
|
106
|
+
specId: SpecIdSchema.describe('Spec ID to reconcile'),
|
|
107
107
|
projectId: z.string().max(500).optional().describe('Project ID hash. Prefer projectPath.'),
|
|
108
108
|
projectPath: z
|
|
109
109
|
.string()
|
|
@@ -176,7 +176,7 @@ export function registerAnalysisTools(server) {
|
|
|
176
176
|
.max(100)
|
|
177
177
|
.optional()
|
|
178
178
|
.describe('Audit categories to check'),
|
|
179
|
-
specId:
|
|
179
|
+
specId: SpecIdSchema.optional().describe('Spec ID to audit against'),
|
|
180
180
|
},
|
|
181
181
|
}, safeLicensed('audit', async (args) => {
|
|
182
182
|
const pid = resolveProjectId(args);
|
|
@@ -231,7 +231,7 @@ export function registerAnalysisTools(server) {
|
|
|
231
231
|
.max(4096)
|
|
232
232
|
.optional()
|
|
233
233
|
.describe('Absolute path to project root. Derives projectId automatically.'),
|
|
234
|
-
specId:
|
|
234
|
+
specId: SpecIdSchema.optional().describe('Spec ID for context-specific suggestions'),
|
|
235
235
|
},
|
|
236
236
|
}, safeLicensed('suggest_mcps', async (args) => {
|
|
237
237
|
const pid = resolveProjectId(args);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { t } from '../../i18n/index.js';
|
|
3
3
|
import { safeTracked, safeLicensed } from '../safe-handler.js';
|
|
4
|
-
import { SupportedLocaleEnum, SpecStatusEnum, SpecTypeEnum, SpecScopeEnum, SpecTargetEnum, ExperienceLevelEnum, WorkModeEnum, ListSpecsOutputSchema, EstimateOutputSchema, ValidateOutputSchema, } from '../schemas/index.js';
|
|
4
|
+
import { SupportedLocaleEnum, SpecStatusEnum, SpecTypeEnum, SpecScopeEnum, SpecTargetEnum, ExperienceLevelEnum, WorkModeEnum, ListSpecsOutputSchema, EstimateOutputSchema, ValidateOutputSchema, SpecIdSchema, } from '../schemas/index.js';
|
|
5
5
|
import { handleSetLocale } from '../set-locale.js';
|
|
6
6
|
import { handleInitProject } from '../init-project.js';
|
|
7
7
|
import { handleSetWorkMode } from '../set-work-mode-handler.js';
|
|
@@ -274,7 +274,7 @@ export function registerCoreSpecTools(server) {
|
|
|
274
274
|
server.registerTool('update_status', {
|
|
275
275
|
description: t('tools.update_status.description'),
|
|
276
276
|
inputSchema: {
|
|
277
|
-
specId:
|
|
277
|
+
specId: SpecIdSchema.describe('Spec ID to update'),
|
|
278
278
|
projectId: z
|
|
279
279
|
.string()
|
|
280
280
|
.max(500)
|
|
@@ -379,7 +379,7 @@ export function registerCoreSpecTools(server) {
|
|
|
379
379
|
server.registerTool('update_status_batch', {
|
|
380
380
|
description: 'Batch update many specs to the same status in one MCP execution. Uses the same transition rules as update_status and reports updated/skipped/failed per spec.',
|
|
381
381
|
inputSchema: {
|
|
382
|
-
specIds: z.array(
|
|
382
|
+
specIds: z.array(SpecIdSchema).min(1).describe('Spec IDs to update'),
|
|
383
383
|
projectId: z.string().max(500).optional().describe('Project ID, if known'),
|
|
384
384
|
projectPath: z
|
|
385
385
|
.string()
|
|
@@ -399,7 +399,7 @@ export function registerCoreSpecTools(server) {
|
|
|
399
399
|
server.registerTool('estimate', {
|
|
400
400
|
description: t('tools.estimate.description'),
|
|
401
401
|
inputSchema: {
|
|
402
|
-
specId:
|
|
402
|
+
specId: SpecIdSchema.describe('Spec ID to estimate'),
|
|
403
403
|
projectId: z.string().max(500).describe('Project ID'),
|
|
404
404
|
},
|
|
405
405
|
outputSchema: EstimateOutputSchema,
|
|
@@ -420,7 +420,7 @@ export function registerCoreSpecTools(server) {
|
|
|
420
420
|
server.registerTool('validate', {
|
|
421
421
|
description: t('tools.validate.description'),
|
|
422
422
|
inputSchema: {
|
|
423
|
-
specId:
|
|
423
|
+
specId: SpecIdSchema.describe('Spec ID to validate'),
|
|
424
424
|
projectId: z
|
|
425
425
|
.string()
|
|
426
426
|
.max(500)
|
|
@@ -441,7 +441,7 @@ export function registerCoreSpecTools(server) {
|
|
|
441
441
|
'and missing parameter type annotations. Returns a health score (0-100) and actionable suggestions.',
|
|
442
442
|
inputSchema: {
|
|
443
443
|
projectPath: z.string().max(4096).describe('Absolute path to the project root'),
|
|
444
|
-
specId:
|
|
444
|
+
specId: SpecIdSchema.describe('Spec ID being validated'),
|
|
445
445
|
waiver: z
|
|
446
446
|
.array(z.string().max(4096))
|
|
447
447
|
.max(100)
|
|
@@ -8,9 +8,10 @@ import { detectLLMClient } from '../engine/client-detection.js';
|
|
|
8
8
|
import { getRegisteredProviders, getRenderer, llmClientToProvider, } from '../engine/provider-adapters/registry.js';
|
|
9
9
|
import { loadSpecForRendering } from '../engine/provider-adapters/shared/spec-loader.js';
|
|
10
10
|
import { compactError, compactResult, formatKeyValue } from './output-formatter.js';
|
|
11
|
+
import { SpecIdSchema } from './schemas/index.js';
|
|
11
12
|
const PROVIDER_VALUES = ['claude', 'gpt4', 'gemini', 'markdown'];
|
|
12
13
|
export const RenderSpecForProviderInputSchema = z.object({
|
|
13
|
-
specId:
|
|
14
|
+
specId: SpecIdSchema.describe('Spec identifier, e.g. "SPEC-670"'),
|
|
14
15
|
projectPath: z.string().describe('Absolute path to the project root'),
|
|
15
16
|
provider: z
|
|
16
17
|
.enum(PROVIDER_VALUES)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// schemas/github.ts — Zod schemas for GitHub integration tools (SPEC-086).
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
+
import { SpecIdSchema } from './spec-id.js';
|
|
3
4
|
export const CreatePRFromSpecSchema = z.object({
|
|
4
|
-
specId:
|
|
5
|
+
specId: SpecIdSchema.describe('ID of the spec (e.g., SPEC-086)'),
|
|
5
6
|
baseBranch: z
|
|
6
7
|
.string()
|
|
7
8
|
.max(500)
|
|
@@ -11,7 +12,7 @@ export const CreatePRFromSpecSchema = z.object({
|
|
|
11
12
|
});
|
|
12
13
|
export const PRStatusSchema = z.object({
|
|
13
14
|
prNumber: z.number().optional().describe('Specific PR number to query'),
|
|
14
|
-
specId:
|
|
15
|
+
specId: SpecIdSchema.optional().describe('Spec ID to find related PRs (e.g., SPEC-086)'),
|
|
15
16
|
view: z.enum(['prs', 'milestones']).default('prs').describe('View mode: prs | milestones'),
|
|
16
17
|
});
|
|
17
18
|
export const ReviewPRSchema = z.object({
|
|
@@ -55,7 +56,7 @@ export const GenerateChangelogSchema = z.object({
|
|
|
55
56
|
.describe('Write the generated changelog to CHANGELOG.md (prepends to existing file). Default: false.'),
|
|
56
57
|
});
|
|
57
58
|
export const CreateIssueFromSpecSchema = z.object({
|
|
58
|
-
specId:
|
|
59
|
+
specId: SpecIdSchema.describe('ID of the spec (e.g., SPEC-086)'),
|
|
59
60
|
criteriaIds: z
|
|
60
61
|
.array(z.string().max(500))
|
|
61
62
|
.max(1000)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { SupportedLocaleEnum, SpecStatusEnum, SpecTypeEnum, SpecScopeEnum, SpecTargetEnum, ExperienceLevelEnum, WorkModeEnum, } from './spec.js';
|
|
2
|
+
export { SPEC_ID_PATTERN, SPEC_DIR_NAME_PATTERN, SpecIdSchema, SpecDirNameSchema, isCanonicalSpecId, } from './spec-id.js';
|
|
2
3
|
export { AuditCategoryEnum, ChecklistFocusEnum, ChallengeSpecFocusEnum } from './analysis.js';
|
|
3
4
|
export { AgentPlatformEnum, SubAgentFrameworkEnum } from './agents.js';
|
|
4
5
|
export { DocumentationTypeEnum, GitActionEnum, DevLifecycleCategoryEnum, PMPlatformEnum, PMActionEnum, } from './lifecycle.js';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// schemas/index.ts — Barrel re-export for all Zod enum schemas.
|
|
2
2
|
export { SupportedLocaleEnum, SpecStatusEnum, SpecTypeEnum, SpecScopeEnum, SpecTargetEnum, ExperienceLevelEnum, WorkModeEnum, } from './spec.js';
|
|
3
|
+
export { SPEC_ID_PATTERN, SPEC_DIR_NAME_PATTERN, SpecIdSchema, SpecDirNameSchema, isCanonicalSpecId, } from './spec-id.js';
|
|
3
4
|
export { AuditCategoryEnum, ChecklistFocusEnum, ChallengeSpecFocusEnum } from './analysis.js';
|
|
4
5
|
export { AgentPlatformEnum, SubAgentFrameworkEnum } from './agents.js';
|
|
5
6
|
export { DocumentationTypeEnum, GitActionEnum, DevLifecycleCategoryEnum, PMPlatformEnum, PMActionEnum, } from './lifecycle.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const SPEC_ID_PATTERN: RegExp;
|
|
3
|
+
export declare const SPEC_DIR_NAME_PATTERN: RegExp;
|
|
4
|
+
export declare const SpecIdSchema: z.ZodString;
|
|
5
|
+
export declare const SpecDirNameSchema: z.ZodString;
|
|
6
|
+
export declare function isCanonicalSpecId(value: string): boolean;
|
|
7
|
+
//# sourceMappingURL=spec-id.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const SPEC_ID_PATTERN = /^SPEC-\d+$/;
|
|
3
|
+
export const SPEC_DIR_NAME_PATTERN = /^SPEC-\d+(?:-[a-z0-9]+(?:-[a-z0-9]+)*)?$/;
|
|
4
|
+
export const SpecIdSchema = z
|
|
5
|
+
.string()
|
|
6
|
+
.min(1)
|
|
7
|
+
.max(50)
|
|
8
|
+
.regex(SPEC_ID_PATTERN, 'Spec ID must use canonical numeric format like SPEC-042.');
|
|
9
|
+
export const SpecDirNameSchema = z
|
|
10
|
+
.string()
|
|
11
|
+
.min(1)
|
|
12
|
+
.max(200)
|
|
13
|
+
.regex(SPEC_DIR_NAME_PATTERN, 'Spec directory must start with a canonical numeric ID like SPEC-042 or SPEC-042-my-feature.');
|
|
14
|
+
export function isCanonicalSpecId(value) {
|
|
15
|
+
return SPEC_ID_PATTERN.test(value);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=spec-id.js.map
|
|
@@ -4,6 +4,7 @@ import { getSpec } from '../storage/spec-store.js';
|
|
|
4
4
|
import { hashProjectPath } from '../storage/base-store.js';
|
|
5
5
|
import { generatePromptFromSpec } from '../engine/spec-prompt-generator.js';
|
|
6
6
|
import { compactResult } from './output-formatter.js';
|
|
7
|
+
import { SpecIdSchema } from './schemas/index.js';
|
|
7
8
|
// ---------------------------------------------------------------------------
|
|
8
9
|
// In-memory registry of registered prompts for this session
|
|
9
10
|
// ---------------------------------------------------------------------------
|
|
@@ -12,7 +13,7 @@ export const registeredPrompts = new Map();
|
|
|
12
13
|
// Zod schemas
|
|
13
14
|
// ---------------------------------------------------------------------------
|
|
14
15
|
const ExposeSpecAsPromptInputSchema = {
|
|
15
|
-
specId:
|
|
16
|
+
specId: SpecIdSchema.describe('The spec ID to expose as an MCP prompt (e.g. "SPEC-042")'),
|
|
16
17
|
projectPath: z.string().describe('Absolute path to the project root where the spec is stored'),
|
|
17
18
|
promptName: z
|
|
18
19
|
.string()
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// To add a tool: import its handler + schema, add an entry to the array.
|
|
11
11
|
// To read them: import coreToolsRegistry from this file.
|
|
12
12
|
import { z } from 'zod';
|
|
13
|
+
import { SpecIdSchema } from '../schemas/index.js';
|
|
13
14
|
// ── Handlers ────────────────────────────────────────────────────────────────
|
|
14
15
|
import { GenerateBatchScriptInputSchema, handleGenerateBatchScript, } from '../generate-batch-script.js';
|
|
15
16
|
import { GenerateAutomationGuideInputSchema, handleGenerateAutomationGuide, } from '../generate-automation-guide.js';
|
|
@@ -373,7 +374,7 @@ const coreToolsRegistry = [
|
|
|
373
374
|
'(structured JSON optimized for autonomous coding agents: Devin, Kiro, SWE-agent, generic). ' +
|
|
374
375
|
'Writes files to .specify/specs/{slug}/ by default.',
|
|
375
376
|
schema: {
|
|
376
|
-
specId:
|
|
377
|
+
specId: SpecIdSchema.describe('Spec ID to export (e.g. "SPEC-001")'),
|
|
377
378
|
projectPath: z.string().min(1).max(2000).describe('Absolute path to the project root'),
|
|
378
379
|
format: z
|
|
379
380
|
.enum(['spec-kit', 'agent-ready'])
|
|
@@ -687,7 +688,7 @@ const coreToolsRegistry = [
|
|
|
687
688
|
'detects drift, and updates the ## Progress section of spec.md with real coverage data. ' +
|
|
688
689
|
'Works entirely offline — pure local file analysis.',
|
|
689
690
|
schema: {
|
|
690
|
-
specId:
|
|
691
|
+
specId: SpecIdSchema.describe('Spec ID, e.g. SPEC-042'),
|
|
691
692
|
projectPath: z.string().min(1).max(4096).describe('Absolute path to project root'),
|
|
692
693
|
},
|
|
693
694
|
handler: async (args) => handleReconcileSpecLiving({
|
|
@@ -13,7 +13,7 @@ import { safe, safeLicensed, safeTracked } from '../safe-handler.js';
|
|
|
13
13
|
// ── License tools (register-license-tools.ts) ───────────────────────────────
|
|
14
14
|
import { handleActivateLicense, handleDeactivateLicense } from '../activate-license.js';
|
|
15
15
|
import { handleLicenseStatus } from '../license-status.js';
|
|
16
|
-
import { LicenseStatusOutputSchema } from '../schemas/index.js';
|
|
16
|
+
import { LicenseStatusOutputSchema, SpecIdSchema } from '../schemas/index.js';
|
|
17
17
|
// ── OAuth tools (register-oauth-tools.ts / register-configure-oauth-tool.ts) ─
|
|
18
18
|
import { handleStartOAuthFlow, handleOAuthStatus, StartOAuthFlowInputSchema, OAuthStatusInputSchema, } from '../oauth-handler.js';
|
|
19
19
|
import { handleConfigureOAuth } from '../configure-oauth-handler.js';
|
|
@@ -386,7 +386,7 @@ export function registerInfraGroupTools(server) {
|
|
|
386
386
|
inputSchema: {
|
|
387
387
|
projectPath: z.string().min(1).describe('Absolute path to the project root'),
|
|
388
388
|
provider: deployProviderEnum,
|
|
389
|
-
specId:
|
|
389
|
+
specId: SpecIdSchema.optional().describe('Optional spec ID for traceability'),
|
|
390
390
|
environment: z
|
|
391
391
|
.enum(['production', 'preview'])
|
|
392
392
|
.optional()
|
|
@@ -484,7 +484,7 @@ export function registerInfraGroupTools(server) {
|
|
|
484
484
|
.string()
|
|
485
485
|
.min(1)
|
|
486
486
|
.describe('Absolute path to the project root (e.g. "/home/user/my-app").'),
|
|
487
|
-
specId:
|
|
487
|
+
specId: SpecIdSchema.describe('Spec ID to validate (e.g. "SPEC-042").'),
|
|
488
488
|
baseUrl: z
|
|
489
489
|
.string()
|
|
490
490
|
.min(1)
|