foundry-mcp 0.3.3__py3-none-any.whl → 0.7.0__py3-none-any.whl
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.
- foundry_mcp/__init__.py +7 -1
- foundry_mcp/cli/commands/plan.py +10 -3
- foundry_mcp/cli/commands/review.py +19 -4
- foundry_mcp/cli/commands/specs.py +38 -208
- foundry_mcp/cli/output.py +3 -3
- foundry_mcp/config.py +235 -5
- foundry_mcp/core/ai_consultation.py +146 -9
- foundry_mcp/core/discovery.py +6 -6
- foundry_mcp/core/error_store.py +2 -2
- foundry_mcp/core/intake.py +933 -0
- foundry_mcp/core/llm_config.py +20 -2
- foundry_mcp/core/metrics_store.py +2 -2
- foundry_mcp/core/progress.py +70 -0
- foundry_mcp/core/prompts/fidelity_review.py +149 -4
- foundry_mcp/core/prompts/markdown_plan_review.py +5 -1
- foundry_mcp/core/prompts/plan_review.py +5 -1
- foundry_mcp/core/providers/claude.py +6 -47
- foundry_mcp/core/providers/codex.py +6 -57
- foundry_mcp/core/providers/cursor_agent.py +3 -44
- foundry_mcp/core/providers/gemini.py +6 -57
- foundry_mcp/core/providers/opencode.py +35 -5
- foundry_mcp/core/research/__init__.py +68 -0
- foundry_mcp/core/research/memory.py +425 -0
- foundry_mcp/core/research/models.py +437 -0
- foundry_mcp/core/research/workflows/__init__.py +22 -0
- foundry_mcp/core/research/workflows/base.py +204 -0
- foundry_mcp/core/research/workflows/chat.py +271 -0
- foundry_mcp/core/research/workflows/consensus.py +396 -0
- foundry_mcp/core/research/workflows/ideate.py +682 -0
- foundry_mcp/core/research/workflows/thinkdeep.py +405 -0
- foundry_mcp/core/responses.py +450 -0
- foundry_mcp/core/spec.py +2438 -236
- foundry_mcp/core/task.py +1064 -19
- foundry_mcp/core/testing.py +512 -123
- foundry_mcp/core/validation.py +313 -42
- foundry_mcp/dashboard/components/charts.py +0 -57
- foundry_mcp/dashboard/launcher.py +11 -0
- foundry_mcp/dashboard/views/metrics.py +25 -35
- foundry_mcp/dashboard/views/overview.py +1 -65
- foundry_mcp/resources/specs.py +25 -25
- foundry_mcp/schemas/intake-schema.json +89 -0
- foundry_mcp/schemas/sdd-spec-schema.json +33 -5
- foundry_mcp/server.py +38 -0
- foundry_mcp/tools/unified/__init__.py +4 -2
- foundry_mcp/tools/unified/authoring.py +2423 -267
- foundry_mcp/tools/unified/documentation_helpers.py +69 -6
- foundry_mcp/tools/unified/environment.py +235 -6
- foundry_mcp/tools/unified/error.py +18 -1
- foundry_mcp/tools/unified/lifecycle.py +8 -0
- foundry_mcp/tools/unified/plan.py +113 -1
- foundry_mcp/tools/unified/research.py +658 -0
- foundry_mcp/tools/unified/review.py +370 -16
- foundry_mcp/tools/unified/spec.py +367 -0
- foundry_mcp/tools/unified/task.py +1163 -48
- foundry_mcp/tools/unified/test.py +69 -8
- {foundry_mcp-0.3.3.dist-info → foundry_mcp-0.7.0.dist-info}/METADATA +7 -1
- {foundry_mcp-0.3.3.dist-info → foundry_mcp-0.7.0.dist-info}/RECORD +60 -48
- {foundry_mcp-0.3.3.dist-info → foundry_mcp-0.7.0.dist-info}/WHEEL +0 -0
- {foundry_mcp-0.3.3.dist-info → foundry_mcp-0.7.0.dist-info}/entry_points.txt +0 -0
- {foundry_mcp-0.3.3.dist-info → foundry_mcp-0.7.0.dist-info}/licenses/LICENSE +0 -0
foundry_mcp/__init__.py
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
"""Foundry MCP - MCP server for SDD toolkit spec management."""
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
__version__ = version("foundry-mcp")
|
|
7
|
+
except PackageNotFoundError:
|
|
8
|
+
# Package not installed (development mode without editable install)
|
|
9
|
+
__version__ = "0.7.0"
|
|
4
10
|
|
|
5
11
|
from foundry_mcp.server import create_server, main
|
|
6
12
|
|
foundry_mcp/cli/commands/plan.py
CHANGED
|
@@ -20,6 +20,7 @@ from foundry_mcp.cli.resilience import (
|
|
|
20
20
|
handle_keyboard_interrupt,
|
|
21
21
|
)
|
|
22
22
|
from foundry_mcp.core.spec import find_specs_directory
|
|
23
|
+
from foundry_mcp.core.llm_config import get_consultation_config
|
|
23
24
|
|
|
24
25
|
logger = get_cli_logger()
|
|
25
26
|
|
|
@@ -135,8 +136,8 @@ def plan_group() -> None:
|
|
|
135
136
|
"--type",
|
|
136
137
|
"review_type",
|
|
137
138
|
type=click.Choice(REVIEW_TYPES),
|
|
138
|
-
default=
|
|
139
|
-
help="Type of review to perform.",
|
|
139
|
+
default=None,
|
|
140
|
+
help="Type of review to perform (defaults to config value, typically 'full').",
|
|
140
141
|
)
|
|
141
142
|
@click.option(
|
|
142
143
|
"--ai-provider",
|
|
@@ -165,7 +166,7 @@ def plan_group() -> None:
|
|
|
165
166
|
def plan_review_cmd(
|
|
166
167
|
ctx: click.Context,
|
|
167
168
|
plan_path: str,
|
|
168
|
-
review_type: str,
|
|
169
|
+
review_type: Optional[str],
|
|
169
170
|
ai_provider: Optional[str],
|
|
170
171
|
ai_timeout: float,
|
|
171
172
|
no_consultation_cache: bool,
|
|
@@ -184,6 +185,12 @@ def plan_review_cmd(
|
|
|
184
185
|
|
|
185
186
|
sdd plan review ./PLAN.md --ai-provider gemini
|
|
186
187
|
"""
|
|
188
|
+
# Get default review_type from config if not provided
|
|
189
|
+
if review_type is None:
|
|
190
|
+
consultation_config = get_consultation_config()
|
|
191
|
+
workflow_config = consultation_config.get_workflow_config("markdown_plan_review")
|
|
192
|
+
review_type = workflow_config.default_review_type
|
|
193
|
+
|
|
187
194
|
start_time = time.perf_counter()
|
|
188
195
|
|
|
189
196
|
llm_status = _get_llm_status()
|
|
@@ -16,6 +16,7 @@ AI-enhanced reviews use:
|
|
|
16
16
|
|
|
17
17
|
import json
|
|
18
18
|
import time
|
|
19
|
+
from pathlib import Path
|
|
19
20
|
from typing import Any, Dict, List, Optional
|
|
20
21
|
|
|
21
22
|
import click
|
|
@@ -42,6 +43,7 @@ from foundry_mcp.tools.unified.review_helpers import (
|
|
|
42
43
|
_run_ai_review,
|
|
43
44
|
_run_quick_review,
|
|
44
45
|
)
|
|
46
|
+
from foundry_mcp.core.llm_config import get_consultation_config
|
|
45
47
|
|
|
46
48
|
logger = get_cli_logger()
|
|
47
49
|
|
|
@@ -121,8 +123,8 @@ def review_group() -> None:
|
|
|
121
123
|
"--type",
|
|
122
124
|
"review_type",
|
|
123
125
|
type=click.Choice(REVIEW_TYPES),
|
|
124
|
-
default=
|
|
125
|
-
help="Type of review to perform.",
|
|
126
|
+
default=None,
|
|
127
|
+
help="Type of review to perform (defaults to config value, typically 'full').",
|
|
126
128
|
)
|
|
127
129
|
@click.option(
|
|
128
130
|
"--tools",
|
|
@@ -159,7 +161,7 @@ def review_group() -> None:
|
|
|
159
161
|
def review_spec_cmd(
|
|
160
162
|
ctx: click.Context,
|
|
161
163
|
spec_id: str,
|
|
162
|
-
review_type: str,
|
|
164
|
+
review_type: Optional[str],
|
|
163
165
|
tools: Optional[str],
|
|
164
166
|
model: Optional[str],
|
|
165
167
|
ai_provider: Optional[str],
|
|
@@ -172,6 +174,12 @@ def review_spec_cmd(
|
|
|
172
174
|
cli_ctx = get_context(ctx)
|
|
173
175
|
specs_dir = cli_ctx.specs_dir
|
|
174
176
|
|
|
177
|
+
# Get default review_type from config if not provided
|
|
178
|
+
if review_type is None:
|
|
179
|
+
consultation_config = get_consultation_config()
|
|
180
|
+
workflow_config = consultation_config.get_workflow_config("plan_review")
|
|
181
|
+
review_type = workflow_config.default_review_type
|
|
182
|
+
|
|
175
183
|
if specs_dir is None:
|
|
176
184
|
emit_error(
|
|
177
185
|
"No specs directory found",
|
|
@@ -538,8 +546,15 @@ def _run_fidelity_review(
|
|
|
538
546
|
spec_requirements = _build_spec_requirements(spec_data, task_id, phase_id)
|
|
539
547
|
|
|
540
548
|
# Build implementation artifacts (file contents, git diff if incremental)
|
|
549
|
+
workspace_root = Path(specs_dir).parent if specs_dir else None
|
|
541
550
|
implementation_artifacts = _build_implementation_artifacts(
|
|
542
|
-
spec_data,
|
|
551
|
+
spec_data,
|
|
552
|
+
task_id,
|
|
553
|
+
phase_id,
|
|
554
|
+
files,
|
|
555
|
+
incremental,
|
|
556
|
+
base_branch,
|
|
557
|
+
workspace_root=workspace_root,
|
|
543
558
|
)
|
|
544
559
|
|
|
545
560
|
# Build test results section
|
|
@@ -27,7 +27,8 @@ from foundry_mcp.core.spec import list_specs as core_list_specs, load_spec
|
|
|
27
27
|
logger = get_cli_logger()
|
|
28
28
|
|
|
29
29
|
# Valid templates and categories
|
|
30
|
-
|
|
30
|
+
# Note: Only 'empty' template is supported. Use phase templates to add structure.
|
|
31
|
+
TEMPLATES = ("empty",)
|
|
31
32
|
CATEGORIES = ("investigation", "implementation", "refactoring", "decision", "research")
|
|
32
33
|
|
|
33
34
|
|
|
@@ -51,20 +52,31 @@ def generate_spec_id(name: str) -> str:
|
|
|
51
52
|
def get_template_structure(template: str, category: str) -> Dict[str, Any]:
|
|
52
53
|
"""Get the hierarchical structure for a spec template.
|
|
53
54
|
|
|
55
|
+
Only 'empty' template is supported. Use phase templates to add structure.
|
|
56
|
+
|
|
54
57
|
Args:
|
|
55
|
-
template: Template type (
|
|
58
|
+
template: Template type (only 'empty' is valid).
|
|
56
59
|
category: Default task category.
|
|
57
60
|
|
|
58
61
|
Returns:
|
|
59
62
|
Hierarchy dict for the spec.
|
|
63
|
+
|
|
64
|
+
Raises:
|
|
65
|
+
ValueError: If template is not 'empty'.
|
|
60
66
|
"""
|
|
61
|
-
|
|
67
|
+
if template != "empty":
|
|
68
|
+
raise ValueError(
|
|
69
|
+
f"Invalid template '{template}'. Only 'empty' template is supported. "
|
|
70
|
+
f"Use phase templates to add structure."
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
return {
|
|
62
74
|
"spec-root": {
|
|
63
75
|
"type": "spec",
|
|
64
76
|
"title": "", # Filled in later
|
|
65
77
|
"status": "pending",
|
|
66
78
|
"parent": None,
|
|
67
|
-
"children": [
|
|
79
|
+
"children": [],
|
|
68
80
|
"total_tasks": 0,
|
|
69
81
|
"completed_tasks": 0,
|
|
70
82
|
"metadata": {
|
|
@@ -77,180 +89,8 @@ def get_template_structure(template: str, category: str) -> Dict[str, Any]:
|
|
|
77
89
|
"depends": [],
|
|
78
90
|
},
|
|
79
91
|
},
|
|
80
|
-
"phase-1": {
|
|
81
|
-
"type": "phase",
|
|
82
|
-
"title": "Planning & Discovery",
|
|
83
|
-
"status": "pending",
|
|
84
|
-
"parent": "spec-root",
|
|
85
|
-
"children": ["task-1-1"],
|
|
86
|
-
"total_tasks": 1,
|
|
87
|
-
"completed_tasks": 0,
|
|
88
|
-
"metadata": {
|
|
89
|
-
"purpose": "Initial planning and requirements gathering",
|
|
90
|
-
"estimated_hours": 2,
|
|
91
|
-
},
|
|
92
|
-
"dependencies": {
|
|
93
|
-
"blocks": [],
|
|
94
|
-
"blocked_by": [],
|
|
95
|
-
"depends": [],
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
"task-1-1": {
|
|
99
|
-
"type": "task",
|
|
100
|
-
"title": "Define requirements",
|
|
101
|
-
"status": "pending",
|
|
102
|
-
"parent": "phase-1",
|
|
103
|
-
"children": [],
|
|
104
|
-
"total_tasks": 1,
|
|
105
|
-
"completed_tasks": 0,
|
|
106
|
-
"metadata": {
|
|
107
|
-
"details": "Document the requirements and acceptance criteria",
|
|
108
|
-
"category": category,
|
|
109
|
-
"estimated_hours": 1,
|
|
110
|
-
},
|
|
111
|
-
"dependencies": {
|
|
112
|
-
"blocks": [],
|
|
113
|
-
"blocked_by": [],
|
|
114
|
-
"depends": [],
|
|
115
|
-
},
|
|
116
|
-
},
|
|
117
92
|
}
|
|
118
93
|
|
|
119
|
-
if template == "simple":
|
|
120
|
-
return base_hierarchy
|
|
121
|
-
|
|
122
|
-
# Medium template adds implementation phase
|
|
123
|
-
if template in ("medium", "complex", "security"):
|
|
124
|
-
base_hierarchy["spec-root"]["children"].append("phase-2")
|
|
125
|
-
base_hierarchy["phase-1"]["dependencies"]["blocks"].append("phase-2")
|
|
126
|
-
base_hierarchy["phase-2"] = {
|
|
127
|
-
"type": "phase",
|
|
128
|
-
"title": "Implementation",
|
|
129
|
-
"status": "pending",
|
|
130
|
-
"parent": "spec-root",
|
|
131
|
-
"children": ["task-2-1"],
|
|
132
|
-
"total_tasks": 1,
|
|
133
|
-
"completed_tasks": 0,
|
|
134
|
-
"metadata": {
|
|
135
|
-
"purpose": "Core implementation work",
|
|
136
|
-
"estimated_hours": 8,
|
|
137
|
-
},
|
|
138
|
-
"dependencies": {
|
|
139
|
-
"blocks": [],
|
|
140
|
-
"blocked_by": ["phase-1"],
|
|
141
|
-
"depends": [],
|
|
142
|
-
},
|
|
143
|
-
}
|
|
144
|
-
base_hierarchy["task-2-1"] = {
|
|
145
|
-
"type": "task",
|
|
146
|
-
"title": "Implement core functionality",
|
|
147
|
-
"status": "pending",
|
|
148
|
-
"parent": "phase-2",
|
|
149
|
-
"children": [],
|
|
150
|
-
"total_tasks": 1,
|
|
151
|
-
"completed_tasks": 0,
|
|
152
|
-
"metadata": {
|
|
153
|
-
"details": "Implement the main features",
|
|
154
|
-
"category": category,
|
|
155
|
-
"estimated_hours": 4,
|
|
156
|
-
},
|
|
157
|
-
"dependencies": {
|
|
158
|
-
"blocks": [],
|
|
159
|
-
"blocked_by": [],
|
|
160
|
-
"depends": [],
|
|
161
|
-
},
|
|
162
|
-
}
|
|
163
|
-
base_hierarchy["spec-root"]["total_tasks"] = 2
|
|
164
|
-
base_hierarchy["phase-1"]["total_tasks"] = 1
|
|
165
|
-
|
|
166
|
-
# Complex template adds verification phase
|
|
167
|
-
if template in ("complex", "security"):
|
|
168
|
-
base_hierarchy["spec-root"]["children"].append("phase-3")
|
|
169
|
-
base_hierarchy["phase-2"]["dependencies"]["blocks"].append("phase-3")
|
|
170
|
-
base_hierarchy["phase-3"] = {
|
|
171
|
-
"type": "phase",
|
|
172
|
-
"title": "Verification & Testing",
|
|
173
|
-
"status": "pending",
|
|
174
|
-
"parent": "spec-root",
|
|
175
|
-
"children": ["verify-3-1"],
|
|
176
|
-
"total_tasks": 1,
|
|
177
|
-
"completed_tasks": 0,
|
|
178
|
-
"metadata": {
|
|
179
|
-
"purpose": "Verify implementation meets requirements",
|
|
180
|
-
"estimated_hours": 4,
|
|
181
|
-
},
|
|
182
|
-
"dependencies": {
|
|
183
|
-
"blocks": [],
|
|
184
|
-
"blocked_by": ["phase-2"],
|
|
185
|
-
"depends": [],
|
|
186
|
-
},
|
|
187
|
-
}
|
|
188
|
-
base_hierarchy["verify-3-1"] = {
|
|
189
|
-
"type": "verify",
|
|
190
|
-
"title": "Run test suite",
|
|
191
|
-
"status": "pending",
|
|
192
|
-
"parent": "phase-3",
|
|
193
|
-
"children": [],
|
|
194
|
-
"total_tasks": 1,
|
|
195
|
-
"completed_tasks": 0,
|
|
196
|
-
"metadata": {
|
|
197
|
-
"verification_type": "auto",
|
|
198
|
-
"command": "pytest",
|
|
199
|
-
"expected": "All tests pass",
|
|
200
|
-
},
|
|
201
|
-
"dependencies": {
|
|
202
|
-
"blocks": [],
|
|
203
|
-
"blocked_by": [],
|
|
204
|
-
"depends": [],
|
|
205
|
-
},
|
|
206
|
-
}
|
|
207
|
-
base_hierarchy["spec-root"]["total_tasks"] = 3
|
|
208
|
-
|
|
209
|
-
# Security template adds security review phase
|
|
210
|
-
if template == "security":
|
|
211
|
-
base_hierarchy["spec-root"]["children"].append("phase-4")
|
|
212
|
-
base_hierarchy["phase-3"]["dependencies"]["blocks"].append("phase-4")
|
|
213
|
-
base_hierarchy["phase-4"] = {
|
|
214
|
-
"type": "phase",
|
|
215
|
-
"title": "Security Review",
|
|
216
|
-
"status": "pending",
|
|
217
|
-
"parent": "spec-root",
|
|
218
|
-
"children": ["task-4-1"],
|
|
219
|
-
"total_tasks": 1,
|
|
220
|
-
"completed_tasks": 0,
|
|
221
|
-
"metadata": {
|
|
222
|
-
"purpose": "Security audit and hardening",
|
|
223
|
-
"estimated_hours": 4,
|
|
224
|
-
},
|
|
225
|
-
"dependencies": {
|
|
226
|
-
"blocks": [],
|
|
227
|
-
"blocked_by": ["phase-3"],
|
|
228
|
-
"depends": [],
|
|
229
|
-
},
|
|
230
|
-
}
|
|
231
|
-
base_hierarchy["task-4-1"] = {
|
|
232
|
-
"type": "task",
|
|
233
|
-
"title": "Security audit",
|
|
234
|
-
"status": "pending",
|
|
235
|
-
"parent": "phase-4",
|
|
236
|
-
"children": [],
|
|
237
|
-
"total_tasks": 1,
|
|
238
|
-
"completed_tasks": 0,
|
|
239
|
-
"metadata": {
|
|
240
|
-
"details": "Review for security vulnerabilities",
|
|
241
|
-
"category": "investigation",
|
|
242
|
-
"estimated_hours": 2,
|
|
243
|
-
},
|
|
244
|
-
"dependencies": {
|
|
245
|
-
"blocks": [],
|
|
246
|
-
"blocked_by": [],
|
|
247
|
-
"depends": [],
|
|
248
|
-
},
|
|
249
|
-
}
|
|
250
|
-
base_hierarchy["spec-root"]["total_tasks"] = 4
|
|
251
|
-
|
|
252
|
-
return base_hierarchy
|
|
253
|
-
|
|
254
94
|
|
|
255
95
|
@click.group("specs")
|
|
256
96
|
def specs() -> None:
|
|
@@ -260,36 +100,18 @@ def specs() -> None:
|
|
|
260
100
|
|
|
261
101
|
# Template definitions for listing/showing
|
|
262
102
|
TEMPLATE_INFO = {
|
|
263
|
-
"
|
|
264
|
-
"name": "
|
|
265
|
-
"description": "
|
|
266
|
-
"phases":
|
|
267
|
-
"tasks":
|
|
268
|
-
"use_cases": ["
|
|
269
|
-
},
|
|
270
|
-
"medium": {
|
|
271
|
-
"name": "medium",
|
|
272
|
-
"description": "Standard spec with planning and implementation phases",
|
|
273
|
-
"phases": 2,
|
|
274
|
-
"tasks": 2,
|
|
275
|
-
"use_cases": ["New features", "Moderate refactoring", "Standard development"],
|
|
276
|
-
},
|
|
277
|
-
"complex": {
|
|
278
|
-
"name": "complex",
|
|
279
|
-
"description": "Full spec with planning, implementation, and verification phases",
|
|
280
|
-
"phases": 3,
|
|
281
|
-
"tasks": 3,
|
|
282
|
-
"use_cases": ["Large features", "Major refactoring", "Critical systems"],
|
|
283
|
-
},
|
|
284
|
-
"security": {
|
|
285
|
-
"name": "security",
|
|
286
|
-
"description": "Complete spec with security review phase",
|
|
287
|
-
"phases": 4,
|
|
288
|
-
"tasks": 4,
|
|
289
|
-
"use_cases": ["Security-sensitive features", "Authentication", "Data handling"],
|
|
103
|
+
"empty": {
|
|
104
|
+
"name": "empty",
|
|
105
|
+
"description": "Blank spec with no phases - use phase templates to add structure",
|
|
106
|
+
"phases": 0,
|
|
107
|
+
"tasks": 0,
|
|
108
|
+
"use_cases": ["All specs - add phases via phase-add-bulk or phase-template apply"],
|
|
290
109
|
},
|
|
291
110
|
}
|
|
292
111
|
|
|
112
|
+
# Phase templates available for adding structure
|
|
113
|
+
PHASE_TEMPLATES = ("planning", "implementation", "testing", "security", "documentation")
|
|
114
|
+
|
|
293
115
|
|
|
294
116
|
@specs.command("template")
|
|
295
117
|
@click.argument("action", type=click.Choice(["list", "show"]))
|
|
@@ -456,8 +278,8 @@ def analyze(ctx: click.Context, directory: Optional[str] = None) -> None:
|
|
|
456
278
|
@click.option(
|
|
457
279
|
"--template",
|
|
458
280
|
type=click.Choice(TEMPLATES),
|
|
459
|
-
default="
|
|
460
|
-
help="Spec template
|
|
281
|
+
default="empty",
|
|
282
|
+
help="Spec template (only 'empty' supported - use phase templates to add structure).",
|
|
461
283
|
)
|
|
462
284
|
@click.option(
|
|
463
285
|
"--category",
|
|
@@ -465,6 +287,12 @@ def analyze(ctx: click.Context, directory: Optional[str] = None) -> None:
|
|
|
465
287
|
default="implementation",
|
|
466
288
|
help="Default task category.",
|
|
467
289
|
)
|
|
290
|
+
@click.option(
|
|
291
|
+
"--mission",
|
|
292
|
+
type=str,
|
|
293
|
+
default="",
|
|
294
|
+
help="Optional mission statement for the spec.",
|
|
295
|
+
)
|
|
468
296
|
@click.pass_context
|
|
469
297
|
@cli_command("create")
|
|
470
298
|
@handle_keyboard_interrupt()
|
|
@@ -474,6 +302,7 @@ def create(
|
|
|
474
302
|
name: str,
|
|
475
303
|
template: str,
|
|
476
304
|
category: str,
|
|
305
|
+
mission: str,
|
|
477
306
|
) -> None:
|
|
478
307
|
"""Create a new specification.
|
|
479
308
|
|
|
@@ -523,8 +352,9 @@ def create(
|
|
|
523
352
|
"last_updated": now,
|
|
524
353
|
"metadata": {
|
|
525
354
|
"description": "",
|
|
355
|
+
"mission": mission.strip(),
|
|
526
356
|
"objectives": [],
|
|
527
|
-
"complexity": "
|
|
357
|
+
"complexity": "low", # Set explicitly via metadata, not template
|
|
528
358
|
"estimated_hours": sum(
|
|
529
359
|
node.get("metadata", {}).get("estimated_hours", 0)
|
|
530
360
|
for node in hierarchy.values()
|
|
@@ -534,13 +364,13 @@ def create(
|
|
|
534
364
|
"status": "pending",
|
|
535
365
|
"owner": "",
|
|
536
366
|
"progress_percentage": 0,
|
|
537
|
-
"current_phase":
|
|
367
|
+
"current_phase": None, # Empty template has no phases
|
|
538
368
|
"category": category,
|
|
539
369
|
"template": template,
|
|
540
370
|
},
|
|
541
371
|
"progress_percentage": 0,
|
|
542
372
|
"status": "pending",
|
|
543
|
-
"current_phase":
|
|
373
|
+
"current_phase": None, # Empty template has no phases
|
|
544
374
|
"hierarchy": hierarchy,
|
|
545
375
|
"journal": [],
|
|
546
376
|
}
|
foundry_mcp/cli/output.py
CHANGED
|
@@ -35,12 +35,12 @@ def emit(data: Any) -> None:
|
|
|
35
35
|
"""Emit JSON to stdout.
|
|
36
36
|
|
|
37
37
|
This is the single output function for all CLI commands.
|
|
38
|
-
Data is serialized
|
|
38
|
+
Data is serialized in minified format for smaller payloads.
|
|
39
39
|
|
|
40
40
|
Args:
|
|
41
41
|
data: Any JSON-serializable data structure.
|
|
42
42
|
"""
|
|
43
|
-
print(json.dumps(data,
|
|
43
|
+
print(json.dumps(data, separators=(",", ":"), default=str))
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
def emit_error(
|
|
@@ -75,7 +75,7 @@ def emit_error(
|
|
|
75
75
|
details=details,
|
|
76
76
|
request_id=_ensure_request_id(),
|
|
77
77
|
)
|
|
78
|
-
print(json.dumps(asdict(response),
|
|
78
|
+
print(json.dumps(asdict(response), separators=(",", ":"), default=str), file=sys.stderr)
|
|
79
79
|
sys.exit(1)
|
|
80
80
|
|
|
81
81
|
|