foundry-mcp 0.3.3__py3-none-any.whl → 0.8.10__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/__init__.py +0 -13
- foundry_mcp/cli/commands/plan.py +10 -3
- foundry_mcp/cli/commands/review.py +19 -4
- foundry_mcp/cli/commands/session.py +1 -8
- foundry_mcp/cli/commands/specs.py +38 -208
- foundry_mcp/cli/context.py +39 -0
- foundry_mcp/cli/output.py +3 -3
- foundry_mcp/config.py +615 -11
- foundry_mcp/core/ai_consultation.py +146 -9
- foundry_mcp/core/batch_operations.py +1196 -0
- foundry_mcp/core/discovery.py +7 -7
- foundry_mcp/core/error_store.py +2 -2
- foundry_mcp/core/intake.py +933 -0
- foundry_mcp/core/llm_config.py +28 -2
- foundry_mcp/core/metrics_store.py +2 -2
- foundry_mcp/core/naming.py +25 -2
- foundry_mcp/core/progress.py +70 -0
- foundry_mcp/core/prometheus.py +0 -13
- 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/__init__.py +12 -0
- foundry_mcp/core/providers/base.py +39 -0
- foundry_mcp/core/providers/claude.py +51 -48
- foundry_mcp/core/providers/codex.py +70 -60
- foundry_mcp/core/providers/cursor_agent.py +25 -47
- foundry_mcp/core/providers/detectors.py +34 -7
- foundry_mcp/core/providers/gemini.py +69 -58
- foundry_mcp/core/providers/opencode.py +101 -47
- foundry_mcp/core/providers/package-lock.json +4 -4
- foundry_mcp/core/providers/package.json +1 -1
- foundry_mcp/core/providers/validation.py +128 -0
- foundry_mcp/core/research/__init__.py +68 -0
- foundry_mcp/core/research/memory.py +528 -0
- foundry_mcp/core/research/models.py +1220 -0
- foundry_mcp/core/research/providers/__init__.py +40 -0
- foundry_mcp/core/research/providers/base.py +242 -0
- foundry_mcp/core/research/providers/google.py +507 -0
- foundry_mcp/core/research/providers/perplexity.py +442 -0
- foundry_mcp/core/research/providers/semantic_scholar.py +544 -0
- foundry_mcp/core/research/providers/tavily.py +383 -0
- foundry_mcp/core/research/workflows/__init__.py +25 -0
- foundry_mcp/core/research/workflows/base.py +298 -0
- foundry_mcp/core/research/workflows/chat.py +271 -0
- foundry_mcp/core/research/workflows/consensus.py +539 -0
- foundry_mcp/core/research/workflows/deep_research.py +4020 -0
- foundry_mcp/core/research/workflows/ideate.py +682 -0
- foundry_mcp/core/research/workflows/thinkdeep.py +405 -0
- foundry_mcp/core/responses.py +690 -0
- foundry_mcp/core/spec.py +2439 -236
- foundry_mcp/core/task.py +1205 -31
- foundry_mcp/core/testing.py +512 -123
- foundry_mcp/core/validation.py +319 -43
- 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 +0 -14
- foundry_mcp/tools/unified/__init__.py +39 -18
- foundry_mcp/tools/unified/authoring.py +2371 -248
- foundry_mcp/tools/unified/documentation_helpers.py +69 -6
- foundry_mcp/tools/unified/environment.py +434 -32
- foundry_mcp/tools/unified/error.py +18 -1
- foundry_mcp/tools/unified/lifecycle.py +8 -0
- foundry_mcp/tools/unified/plan.py +133 -2
- foundry_mcp/tools/unified/provider.py +0 -40
- foundry_mcp/tools/unified/research.py +1283 -0
- foundry_mcp/tools/unified/review.py +374 -17
- foundry_mcp/tools/unified/review_helpers.py +16 -1
- foundry_mcp/tools/unified/server.py +9 -24
- foundry_mcp/tools/unified/spec.py +367 -0
- foundry_mcp/tools/unified/task.py +1664 -30
- foundry_mcp/tools/unified/test.py +69 -8
- {foundry_mcp-0.3.3.dist-info → foundry_mcp-0.8.10.dist-info}/METADATA +8 -1
- foundry_mcp-0.8.10.dist-info/RECORD +153 -0
- foundry_mcp/cli/flags.py +0 -266
- foundry_mcp/core/feature_flags.py +0 -592
- foundry_mcp-0.3.3.dist-info/RECORD +0 -135
- {foundry_mcp-0.3.3.dist-info → foundry_mcp-0.8.10.dist-info}/WHEEL +0 -0
- {foundry_mcp-0.3.3.dist-info → foundry_mcp-0.8.10.dist-info}/entry_points.txt +0 -0
- {foundry_mcp-0.3.3.dist-info → foundry_mcp-0.8.10.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/__init__.py
CHANGED
|
@@ -5,13 +5,6 @@ All commands emit structured JSON to stdout for reliable parsing.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from foundry_mcp.cli.config import CLIContext, create_context
|
|
8
|
-
from foundry_mcp.cli.flags import (
|
|
9
|
-
CLIFlagRegistry,
|
|
10
|
-
apply_cli_flag_overrides,
|
|
11
|
-
flags_for_discovery,
|
|
12
|
-
get_cli_flags,
|
|
13
|
-
with_flag_options,
|
|
14
|
-
)
|
|
15
8
|
from foundry_mcp.cli.logging import (
|
|
16
9
|
CLILogContext,
|
|
17
10
|
cli_command,
|
|
@@ -51,12 +44,6 @@ __all__ = [
|
|
|
51
44
|
"emit",
|
|
52
45
|
"emit_error",
|
|
53
46
|
"emit_success",
|
|
54
|
-
# Feature flags
|
|
55
|
-
"CLIFlagRegistry",
|
|
56
|
-
"apply_cli_flag_overrides",
|
|
57
|
-
"flags_for_discovery",
|
|
58
|
-
"get_cli_flags",
|
|
59
|
-
"with_flag_options",
|
|
60
47
|
# Logging
|
|
61
48
|
"CLILogContext",
|
|
62
49
|
"cli_command",
|
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
|
|
@@ -183,12 +183,11 @@ def show_limits_cmd(ctx: click.Context) -> None:
|
|
|
183
183
|
@handle_keyboard_interrupt()
|
|
184
184
|
@with_sync_timeout(FAST_TIMEOUT, "Capabilities lookup timed out")
|
|
185
185
|
def session_capabilities_cmd(ctx: click.Context) -> None:
|
|
186
|
-
"""Show CLI capabilities
|
|
186
|
+
"""Show CLI capabilities.
|
|
187
187
|
|
|
188
188
|
Returns a manifest of available features, commands, and their status
|
|
189
189
|
for AI coding assistants to understand available functionality.
|
|
190
190
|
"""
|
|
191
|
-
from foundry_mcp.cli.flags import flags_for_discovery, get_cli_flags
|
|
192
191
|
from foundry_mcp.cli.main import cli
|
|
193
192
|
|
|
194
193
|
cli_ctx = get_context(ctx)
|
|
@@ -204,15 +203,10 @@ def session_capabilities_cmd(ctx: click.Context) -> None:
|
|
|
204
203
|
else:
|
|
205
204
|
command_groups[name] = {"type": "command"}
|
|
206
205
|
|
|
207
|
-
# Get feature flags
|
|
208
|
-
get_cli_flags()
|
|
209
|
-
flags = flags_for_discovery()
|
|
210
|
-
|
|
211
206
|
# Known CLI capabilities
|
|
212
207
|
capabilities = {
|
|
213
208
|
"json_output": True, # All output is JSON
|
|
214
209
|
"spec_driven": True, # SDD methodology supported
|
|
215
|
-
"feature_flags": True, # Feature flag system available
|
|
216
210
|
"session_tracking": True, # Session/context tracking
|
|
217
211
|
"rate_limiting": True, # Rate limiting built-in
|
|
218
212
|
}
|
|
@@ -222,7 +216,6 @@ def session_capabilities_cmd(ctx: click.Context) -> None:
|
|
|
222
216
|
"version": "0.1.0",
|
|
223
217
|
"name": "foundry-cli",
|
|
224
218
|
"capabilities": capabilities,
|
|
225
|
-
"feature_flags": flags,
|
|
226
219
|
"command_groups": list(command_groups.keys()),
|
|
227
220
|
"command_count": len(cli.commands),
|
|
228
221
|
"specs_dir": str(cli_ctx.specs_dir) if cli_ctx.specs_dir else None,
|
|
@@ -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/context.py
CHANGED
|
@@ -29,6 +29,43 @@ class SessionStats:
|
|
|
29
29
|
last_activity: Optional[str] = None
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
@dataclass
|
|
33
|
+
class AutonomousSession:
|
|
34
|
+
"""
|
|
35
|
+
Ephemeral state for autonomous task execution mode.
|
|
36
|
+
|
|
37
|
+
This tracks whether the agent is running in autonomous mode where
|
|
38
|
+
it continues to the next task without explicit user confirmation.
|
|
39
|
+
|
|
40
|
+
NOTE: This state is EPHEMERAL - it exists only in memory and does
|
|
41
|
+
not persist across CLI restarts. Each new CLI session starts with
|
|
42
|
+
autonomous mode disabled.
|
|
43
|
+
"""
|
|
44
|
+
enabled: bool = False
|
|
45
|
+
tasks_completed: int = 0
|
|
46
|
+
pause_reason: Optional[str] = None # Why auto-mode paused: "limit", "error", "user", "context"
|
|
47
|
+
started_at: Optional[str] = None # ISO timestamp when auto-mode was enabled
|
|
48
|
+
|
|
49
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
50
|
+
"""Convert to dictionary for JSON output."""
|
|
51
|
+
return {
|
|
52
|
+
"enabled": self.enabled,
|
|
53
|
+
"tasks_completed": self.tasks_completed,
|
|
54
|
+
"pause_reason": self.pause_reason,
|
|
55
|
+
"started_at": self.started_at,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def from_dict(cls, data: Dict[str, Any]) -> "AutonomousSession":
|
|
60
|
+
"""Create from dictionary (for in-session use only)."""
|
|
61
|
+
return cls(
|
|
62
|
+
enabled=data.get("enabled", False),
|
|
63
|
+
tasks_completed=data.get("tasks_completed", 0),
|
|
64
|
+
pause_reason=data.get("pause_reason"),
|
|
65
|
+
started_at=data.get("started_at"),
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
32
69
|
@dataclass
|
|
33
70
|
class ContextSession:
|
|
34
71
|
"""
|
|
@@ -44,6 +81,7 @@ class ContextSession:
|
|
|
44
81
|
limits: SessionLimits = field(default_factory=SessionLimits)
|
|
45
82
|
stats: SessionStats = field(default_factory=SessionStats)
|
|
46
83
|
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
84
|
+
autonomous: Optional[AutonomousSession] = None
|
|
47
85
|
|
|
48
86
|
@property
|
|
49
87
|
def consultations_remaining(self) -> int:
|
|
@@ -111,6 +149,7 @@ class ContextSession:
|
|
|
111
149
|
"at_limit": self.at_limit,
|
|
112
150
|
},
|
|
113
151
|
"metadata": self.metadata,
|
|
152
|
+
"autonomous": self.autonomous.to_dict() if self.autonomous else None,
|
|
114
153
|
}
|
|
115
154
|
|
|
116
155
|
|
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
|
|