x-ipe 1.0.17__py3-none-any.whl → 1.0.18__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.
x_ipe/cli/main.py CHANGED
@@ -211,6 +211,9 @@ def init(ctx: click.Context, force: bool, dry_run: bool, no_skills: bool) -> Non
211
211
  # Copy config files (copilot-prompt.json, tools.json, .env.example)
212
212
  scaffold.copy_config_files()
213
213
 
214
+ # Copy planning templates (features.md, task-board.md)
215
+ scaffold.copy_planning_templates()
216
+
214
217
  # Create config file
215
218
  scaffold.create_config_file()
216
219
 
@@ -218,6 +221,39 @@ def init(ctx: click.Context, force: bool, dry_run: bool, no_skills: bool) -> Non
218
221
  if (project_root / ".git").exists():
219
222
  scaffold.update_gitignore()
220
223
 
224
+ # MCP config merge with user confirmation
225
+ mcp_servers = scaffold.get_project_mcp_servers()
226
+ if mcp_servers and not dry_run:
227
+ click.echo("\n" + "-" * 40)
228
+ click.echo("MCP Server Configuration")
229
+ click.echo("-" * 40)
230
+
231
+ # Show available servers
232
+ click.echo(f"\nFound {len(mcp_servers)} MCP server(s) in project config:")
233
+ for name in mcp_servers:
234
+ click.echo(f" • {name}")
235
+
236
+ # Confirm each server
237
+ servers_to_merge = []
238
+ for name in mcp_servers:
239
+ if click.confirm(f"\nAdd '{name}' to global MCP config?", default=True):
240
+ servers_to_merge.append(name)
241
+
242
+ if servers_to_merge:
243
+ # Confirm target path
244
+ default_path = Path.home() / ".copilot" / "mcp-config.json"
245
+ target_path = click.prompt(
246
+ "\nTarget MCP config path",
247
+ default=str(default_path),
248
+ type=click.Path(dir_okay=False, path_type=Path)
249
+ )
250
+
251
+ scaffold.merge_mcp_config(
252
+ servers_to_merge=servers_to_merge,
253
+ target_path=target_path
254
+ )
255
+ click.echo(f"\n✓ Merged {len(servers_to_merge)} MCP server(s) to {target_path}")
256
+
221
257
  # Show summary
222
258
  created, skipped = scaffold.get_summary()
223
259
 
x_ipe/core/scaffold.py CHANGED
@@ -3,6 +3,7 @@ from pathlib import Path
3
3
  from typing import List, Tuple, Optional
4
4
  import shutil
5
5
  import os
6
+ import json
6
7
 
7
8
 
8
9
  class ScaffoldManager:
@@ -143,6 +144,90 @@ class ScaffoldManager:
143
144
  shutil.copy2(source, target)
144
145
  self.created.append(target)
145
146
 
147
+ def get_project_mcp_servers(self) -> dict:
148
+ """Get MCP servers from project's .github/copilot/mcp-config.json.
149
+
150
+ Returns:
151
+ Dict of server_name -> server_config, or empty dict if not found.
152
+ """
153
+ project_mcp = self.project_root / ".github" / "copilot" / "mcp-config.json"
154
+ if not project_mcp.exists():
155
+ return {}
156
+
157
+ try:
158
+ project_config = json.loads(project_mcp.read_text())
159
+ return project_config.get("mcpServers", {})
160
+ except (json.JSONDecodeError, IOError):
161
+ return {}
162
+
163
+ def merge_mcp_config(
164
+ self,
165
+ servers_to_merge: Optional[List[str]] = None,
166
+ target_path: Optional[Path] = None
167
+ ) -> None:
168
+ """Merge project's MCP servers into global config.
169
+
170
+ Args:
171
+ servers_to_merge: List of server names to merge. If None, merges all.
172
+ target_path: Path to target mcp-config.json. Defaults to ~/.copilot/mcp-config.json.
173
+
174
+ This allows project-specific MCP servers to be available globally.
175
+ Deep-merges mcpServers objects, with project servers added to global config.
176
+ Existing global servers are preserved unless --force is used for conflicts.
177
+ """
178
+ project_servers = self.get_project_mcp_servers()
179
+ if not project_servers:
180
+ return
181
+
182
+ # Filter to requested servers if specified
183
+ if servers_to_merge is not None:
184
+ project_servers = {k: v for k, v in project_servers.items() if k in servers_to_merge}
185
+ if not project_servers:
186
+ return
187
+
188
+ # Target: configurable or default to ~/.copilot/mcp-config.json
189
+ if target_path is None:
190
+ global_copilot_dir = Path.home() / ".copilot"
191
+ global_mcp = global_copilot_dir / "mcp-config.json"
192
+ else:
193
+ global_mcp = Path(target_path)
194
+ global_copilot_dir = global_mcp.parent
195
+
196
+ if self.dry_run:
197
+ self.created.append(global_mcp)
198
+ return
199
+
200
+ # Load or create global config
201
+ global_config = {"mcpServers": {}}
202
+ if global_mcp.exists():
203
+ try:
204
+ global_config = json.loads(global_mcp.read_text())
205
+ if "mcpServers" not in global_config:
206
+ global_config["mcpServers"] = {}
207
+ except (json.JSONDecodeError, IOError):
208
+ global_config = {"mcpServers": {}}
209
+
210
+ # Merge: add project servers to global
211
+ merged_count = 0
212
+ skipped_count = 0
213
+ for server_name, server_config in project_servers.items():
214
+ if server_name in global_config["mcpServers"]:
215
+ if self.force:
216
+ global_config["mcpServers"][server_name] = server_config
217
+ merged_count += 1
218
+ else:
219
+ skipped_count += 1
220
+ else:
221
+ global_config["mcpServers"][server_name] = server_config
222
+ merged_count += 1
223
+
224
+ if merged_count > 0:
225
+ global_copilot_dir.mkdir(parents=True, exist_ok=True)
226
+ global_mcp.write_text(json.dumps(global_config, indent=2) + "\n")
227
+ self.created.append(global_mcp)
228
+ elif skipped_count > 0:
229
+ self.skipped.append(global_mcp)
230
+
146
231
  def copy_config_files(self) -> None:
147
232
  """Copy config files (copilot-prompt.json, tools.json, .env.example) to x-ipe-docs/config/."""
148
233
  config_source = self._get_resource_path("config")
@@ -170,6 +255,33 @@ class ScaffoldManager:
170
255
  shutil.copy2(source_file, target_file)
171
256
  self.created.append(target_file)
172
257
 
258
+ def copy_planning_templates(self) -> None:
259
+ """Copy planning templates (features.md, task-board.md) to x-ipe-docs/planning/."""
260
+ planning_source = self._get_resource_path("planning")
261
+ if planning_source is None or not planning_source.exists():
262
+ return
263
+
264
+ target_dir = self.project_root / "x-ipe-docs" / "planning"
265
+
266
+ # Copy each planning file individually (don't overwrite existing)
267
+ planning_files = ["features.md", "task-board.md"]
268
+ for filename in planning_files:
269
+ source_file = planning_source / filename
270
+ target_file = target_dir / filename
271
+
272
+ if not source_file.exists():
273
+ continue
274
+
275
+ if target_file.exists():
276
+ if not self.force:
277
+ self.skipped.append(target_file)
278
+ continue
279
+
280
+ if not self.dry_run:
281
+ target_dir.mkdir(parents=True, exist_ok=True)
282
+ shutil.copy2(source_file, target_file)
283
+ self.created.append(target_file)
284
+
173
285
  def create_config_file(self, config_content: Optional[str] = None) -> None:
174
286
  """Create .x-ipe.yaml with defaults.
175
287
 
@@ -243,8 +355,10 @@ server:
243
355
  self.copy_skills()
244
356
  self.copy_copilot_instructions()
245
357
  self.copy_config_files()
358
+ self.copy_planning_templates()
246
359
  self.create_config_file()
247
360
  self.update_gitignore()
361
+ self.merge_mcp_config()
248
362
  return self.get_summary()
249
363
 
250
364
  def get_summary(self) -> Tuple[List[Path], List[Path]]:
@@ -0,0 +1,53 @@
1
+ # Feature Board
2
+
3
+ > Last Updated: {DATE}
4
+
5
+ ## Overview
6
+
7
+ This board tracks all features across the project lifecycle.
8
+
9
+ **Status Definitions:**
10
+ - **Planned** - Feature identified, awaiting refinement
11
+ - **Refined** - Specification complete, ready for design
12
+ - **Designed** - Technical design complete, ready for implementation
13
+ - **Implemented** - Code complete, ready for testing
14
+ - **Tested** - Tests complete, ready for deployment
15
+ - **Completed** - Feature fully deployed and verified
16
+
17
+ ---
18
+
19
+ ## Feature Tracking
20
+
21
+ | Feature ID | Feature Title | Version | Status | Specification Link | Created | Last Updated |
22
+ |------------|---------------|---------|--------|-------------------|---------|--------------|
23
+ | _No features yet_ | | | | | | |
24
+
25
+ ---
26
+
27
+ ## Status Details
28
+
29
+ ### Planned (0)
30
+ - None
31
+
32
+ ### Refined (0)
33
+ - None
34
+
35
+ ### Designed (0)
36
+ - None
37
+
38
+ ### Implemented (0)
39
+ - None
40
+
41
+ ### Tested (0)
42
+ - None
43
+
44
+ ### Completed (0)
45
+ - None
46
+
47
+ ---
48
+
49
+ ## Feature Details
50
+
51
+ _Features will appear here after creation_
52
+
53
+ ---
@@ -0,0 +1,77 @@
1
+ # Task Board
2
+
3
+ > Task Board Management - Task Tracking
4
+
5
+ ## Global Settings
6
+
7
+ ```yaml
8
+ auto_proceed: false # Set to true for automatic task chaining
9
+ ```
10
+
11
+ ---
12
+
13
+ ## Active Tasks
14
+
15
+ | Task ID | Task Type | Description | Role | Status | Last Updated | Output Links | Next Task |
16
+ |---------|-----------|-------------|------|--------|--------------|--------------|----------|
17
+ | | | | | | | | |
18
+
19
+ ---
20
+
21
+ ## Completed Tasks
22
+
23
+ | Task ID | Task Type | Description | Role | Last Updated | Output Links | Notes |
24
+ |---------|-----------|-------------|------|--------------|--------------|-------|
25
+ | | | | | | | |
26
+
27
+ ---
28
+
29
+ ## Cancelled Tasks
30
+
31
+ | Task ID | Task Type | Description | Reason | Last Updated | Output Links |
32
+ |---------|-----------|-------------|--------|--------------|--------------|
33
+ | | | | | | |
34
+
35
+ ---
36
+
37
+ ## Status Legend
38
+
39
+ | Status | Symbol | Description |
40
+ |--------|--------|-------------|
41
+ | pending | ⏳ | Waiting to start |
42
+ | in_progress | 🔄 | Working |
43
+ | blocked | 🚫 | Waiting for dependency |
44
+ | deferred | ⏸️ | Paused by human |
45
+ | completed | ✅ | Done |
46
+ | cancelled | ❌ | Stopped |
47
+
48
+ ---
49
+
50
+ ## Task Type Quick Reference
51
+
52
+ | Task Type | Skill | Default Next |
53
+ |-----------|-------|--------------|
54
+ | Requirement Gathering | task-type-requirement-gathering | Feature Breakdown |
55
+ | Feature Breakdown | task-type-feature-breakdown | Technical Design |
56
+ | Technical Design | task-type-technical-design | Test Generation |
57
+ | Test Generation | task-type-test-generation | Code Implementation |
58
+ | Code Implementation | task-type-code-implementation | Human Playground |
59
+ | Human Playground | task-type-human-playground | Feature Closing |
60
+ | Feature Closing | task-type-feature-closing | - |
61
+ | Code Refactor | task-type-code-refactor | - |
62
+ | Project Initialization | task-type-project-init | Dev Environment Setup |
63
+ | Dev Environment Setup | task-type-dev-environment | - |
64
+
65
+ ---
66
+
67
+ ## Quick Stats
68
+
69
+ - **Total Active:** 0
70
+ - **In Progress:** 0
71
+ - **Pending:** 0
72
+ - **Pending Review:** 0
73
+ - **Blocked:** 0
74
+ - **Deferred:** 0
75
+ - **Completed:** 0
76
+
77
+ ---