delimit-cli 3.15.6 → 3.15.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.
@@ -3631,6 +3631,255 @@ STANDARD_WORKFLOWS = [
3631
3631
  ]
3632
3632
 
3633
3633
 
3634
+ @mcp.tool()
3635
+ def delimit_swarm(action: str = "status", venture: str = "",
3636
+ agent_id: str = "", repo_path: str = "",
3637
+ deploy_target: str = "", target_path: str = "",
3638
+ access_action: str = "read") -> Dict[str, Any]:
3639
+ """Manage the agent swarm — ventures, personas, namespace isolation.
3640
+
3641
+ Each venture gets 5 AI agent roles (Architect, Senior Dev, Reviewer, QA, Ops)
3642
+ with namespace isolation and model binding per Agent Swarm Standard v1.2.
3643
+
3644
+ Actions:
3645
+ status: Full swarm overview (ventures, agents, health)
3646
+ register: Register a new venture with agent team
3647
+ venture: Get venture details + its agents
3648
+ agent: Get agent details
3649
+ check: Check namespace access for an agent
3650
+ approve: Check approval tier for an action
3651
+ guide: Get usage documentation
3652
+ rules: Get escalation rules
3653
+
3654
+ Args:
3655
+ action: "status", "register", "venture", "agent", "check", "approve", "guide", or "rules".
3656
+ venture: Venture name (for register/venture).
3657
+ agent_id: Agent ID like "delimit-architect-01" (for agent/check).
3658
+ repo_path: Repo path for venture registration.
3659
+ deploy_target: Deploy target for venture registration.
3660
+ target_path: File path to check access for (check action).
3661
+ access_action: Action name — for check: "read"/"write"/"deploy". For approve: "deploy_production"/"deploy_staging"/"social_post" etc.
3662
+ """
3663
+ from ai.swarm import (register_venture, get_venture, get_agent,
3664
+ check_namespace_access, get_swarm_status,
3665
+ check_approval, get_escalation_rules, get_usage_guide)
3666
+
3667
+ if action == "register":
3668
+ return _with_next_steps("swarm", _safe_call(
3669
+ register_venture, name=venture, repo_path=repo_path, deploy_target=deploy_target,
3670
+ ))
3671
+ if action == "venture":
3672
+ return _with_next_steps("swarm", _safe_call(get_venture, name=venture))
3673
+ if action == "agent":
3674
+ return _with_next_steps("swarm", _safe_call(get_agent, agent_id=agent_id))
3675
+ if action == "approve":
3676
+ return _with_next_steps("swarm", _safe_call(
3677
+ check_approval, action=access_action, venture=venture, agent_id=agent_id,
3678
+ ))
3679
+ if action == "guide":
3680
+ return _with_next_steps("swarm", _safe_call(get_usage_guide))
3681
+ if action == "rules":
3682
+ return _with_next_steps("swarm", _safe_call(get_escalation_rules))
3683
+ if action == "check":
3684
+ return _with_next_steps("swarm", _safe_call(
3685
+ check_namespace_access, agent_id=agent_id, target_path=target_path, action=access_action,
3686
+ ))
3687
+ return _with_next_steps("swarm", _safe_call(get_swarm_status))
3688
+
3689
+
3690
+ @mcp.tool()
3691
+ def delimit_review(diff: str = "", file_path: str = "",
3692
+ context: str = "", pr_url: str = "") -> Dict[str, Any]:
3693
+ """Run a multi-model code review on a diff or file.
3694
+
3695
+ Sends the code change to multiple AI models and consolidates their
3696
+ feedback into a single structured review. The output can be posted
3697
+ as a GitHub PR comment.
3698
+
3699
+ Provide either a diff string or a file path to review.
3700
+
3701
+ Args:
3702
+ diff: Git diff or code to review. Takes priority over file_path.
3703
+ file_path: Path to file to review (reads current content).
3704
+ context: Additional context about the change (what it does, why).
3705
+ pr_url: GitHub PR URL for linking the review.
3706
+ """
3707
+ from ai.multi_review import generate_review_prompt, consolidate_reviews, save_review
3708
+
3709
+ # Get the diff content
3710
+ if not diff and file_path:
3711
+ try:
3712
+ from subprocess import run as _run
3713
+ result = _run(
3714
+ ["git", "diff", "HEAD", "--", file_path],
3715
+ capture_output=True, text=True, timeout=10,
3716
+ )
3717
+ diff = result.stdout or Path(file_path).read_text()[:5000]
3718
+ except Exception:
3719
+ try:
3720
+ diff = Path(file_path).read_text()[:5000]
3721
+ except Exception as e:
3722
+ return {"error": f"Could not read {file_path}: {e}"}
3723
+
3724
+ if not diff:
3725
+ return {"error": "Provide either diff text or file_path to review"}
3726
+
3727
+ prompt = generate_review_prompt(diff, context)
3728
+
3729
+ # Run through deliberation engine for multi-model feedback
3730
+ try:
3731
+ from ai.deliberation import get_models_config
3732
+ config = get_models_config()
3733
+ enabled = {k: v for k, v in config.items() if v.get("enabled")}
3734
+
3735
+ if len(enabled) < 2:
3736
+ return {
3737
+ "error": "Need at least 2 AI models for multi-model review",
3738
+ "tip": "Configure models in ~/.delimit/models.json",
3739
+ }
3740
+
3741
+ reviews = []
3742
+ from ai.deliberation import _call_model
3743
+ import time as _time
3744
+
3745
+ for model_id, model_config in list(enabled.items())[:3]:
3746
+ start = _time.time()
3747
+ try:
3748
+ response = _call_model(model_id, model_config, prompt,
3749
+ system_prompt="You are a senior code reviewer. Be concise and actionable.")
3750
+ duration = int((_time.time() - start) * 1000)
3751
+ reviews.append({
3752
+ "model": model_config.get("name", model_id),
3753
+ "content": response,
3754
+ "duration_ms": duration,
3755
+ })
3756
+ except Exception as e:
3757
+ reviews.append({
3758
+ "model": model_config.get("name", model_id),
3759
+ "content": f"Review failed: {e}",
3760
+ "duration_ms": 0,
3761
+ })
3762
+
3763
+ report = consolidate_reviews(reviews)
3764
+ result = save_review(diff, report, pr_url)
3765
+
3766
+ return _with_next_steps("review", {
3767
+ "status": "complete",
3768
+ "models_used": report["models_used"],
3769
+ "review_count": len(reviews),
3770
+ "pr_comment": result["pr_comment"],
3771
+ "review_id": result["review_id"],
3772
+ })
3773
+
3774
+ except ImportError:
3775
+ return {"error": "Deliberation engine required for multi-model review"}
3776
+
3777
+
3778
+ @mcp.tool()
3779
+ def delimit_redact(action: str = "scan", text: str = "",
3780
+ categories: str = "") -> Dict[str, Any]:
3781
+ """Scan or redact sensitive data (API keys, secrets, PII) from text.
3782
+
3783
+ Use before sending prompts to external LLMs to prevent data leakage.
3784
+ Detects: API keys (OpenAI, xAI, Google, GitHub, npm), passwords,
3785
+ bearer tokens, emails, phone numbers, SSNs, credit cards, IPs, DB URLs.
3786
+
3787
+ Actions:
3788
+ scan: Preview what would be redacted (non-destructive)
3789
+ redact: Replace sensitive data with [REDACTED_TYPE_N] tokens
3790
+
3791
+ Args:
3792
+ action: "scan" or "redact".
3793
+ text: Text to scan/redact.
3794
+ categories: Comma-separated categories (api_key, secret, pii, infra). Empty = all.
3795
+ """
3796
+ from ai.pii_redact import scan as pii_scan, redact as pii_redact
3797
+
3798
+ cat_list = [c.strip() for c in categories.split(",") if c.strip()] if categories else None
3799
+
3800
+ if action == "redact":
3801
+ result = pii_redact(text, categories=cat_list)
3802
+ # Never expose token_map through MCP — keep it local
3803
+ return _with_next_steps("redact", {
3804
+ "redacted": result["redacted"],
3805
+ "findings": result["findings"],
3806
+ "token_count": result["token_count"],
3807
+ })
3808
+
3809
+ return _with_next_steps("redact", _safe_call(pii_scan, text=text))
3810
+
3811
+
3812
+ @mcp.tool()
3813
+ def delimit_prompt_drift(action: str = "check", prompt: str = "",
3814
+ model: str = "", result_summary: str = "",
3815
+ success: str = "true", task_type: str = "") -> Dict[str, Any]:
3816
+ """Detect prompt drift — when the same task behaves differently across models.
3817
+
3818
+ Track how prompts perform across Claude, Codex, and Gemini.
3819
+ Find which model is best for each task type on YOUR codebase.
3820
+
3821
+ Actions:
3822
+ record: Log a prompt result (model, success, duration)
3823
+ check: Detect drift across models for a prompt or task type
3824
+ rank: Rank models by success rate and speed
3825
+
3826
+ Args:
3827
+ action: "record", "check", or "rank".
3828
+ prompt: The prompt text (for record/check).
3829
+ model: AI model name (for record).
3830
+ result_summary: Brief description of the result (for record).
3831
+ success: Whether the result was good ("true"/"false").
3832
+ task_type: Task category (refactoring/testing/debugging/docs).
3833
+ """
3834
+ from ai.prompt_drift import record_result, check_drift, get_model_rankings
3835
+
3836
+ if action == "record":
3837
+ return _with_next_steps("prompt_drift", _safe_call(
3838
+ record_result, prompt=prompt, model=model,
3839
+ result_summary=result_summary,
3840
+ success=success.lower().strip() in ("true", "1", "yes"),
3841
+ task_type=task_type,
3842
+ ))
3843
+ if action == "rank":
3844
+ return _with_next_steps("prompt_drift", _safe_call(
3845
+ get_model_rankings, task_type=task_type,
3846
+ ))
3847
+ return _with_next_steps("prompt_drift", _safe_call(
3848
+ check_drift, prompt=prompt, task_type=task_type,
3849
+ ))
3850
+
3851
+
3852
+ @mcp.tool()
3853
+ def delimit_collision_check(action: str = "check", file_path: str = "",
3854
+ model: str = "", task_id: str = "") -> Dict[str, Any]:
3855
+ """Detect and prevent two AI models from editing the same file.
3856
+
3857
+ Call before editing a file to check if another model is already working on it.
3858
+
3859
+ Actions:
3860
+ check: Show all active file locks and hotspots
3861
+ claim: Claim a file before editing (returns collision if held)
3862
+ release: Release a file lock after done editing
3863
+
3864
+ Args:
3865
+ action: "check", "claim", or "release".
3866
+ file_path: File to claim/release (required for claim/release).
3867
+ model: AI model name (claude/codex/gemini).
3868
+ task_id: Optional task ID for tracking.
3869
+ """
3870
+ from ai.collision_detect import claim_file, release_file, check_collisions
3871
+
3872
+ if action == "claim":
3873
+ return _with_next_steps("collision", _safe_call(
3874
+ claim_file, file_path=file_path, model=model, task_id=task_id,
3875
+ ))
3876
+ if action == "release":
3877
+ return _with_next_steps("collision", _safe_call(
3878
+ release_file, file_path=file_path, model=model,
3879
+ ))
3880
+ return _with_next_steps("collision", _safe_call(check_collisions, model=model))
3881
+
3882
+
3634
3883
  @mcp.tool()
3635
3884
  def delimit_project_config(action: str = "load", project_path: str = ".",
3636
3885
  mode: str = "advisory", preset: str = "default",
@@ -0,0 +1,397 @@
1
+ """Agent Swarm — persona registry, namespace isolation, and venture management.
2
+
3
+ Implements Agent Swarm Standard v1.2 (4-party consent achieved 2026-03-30).
4
+ Each venture gets 5 agent roles bound to AI models with namespace isolation.
5
+
6
+ Config: ~/.delimit/swarm/config.yml
7
+ """
8
+
9
+ import json
10
+ import time
11
+ from pathlib import Path
12
+ from typing import Any, Dict, List, Optional
13
+
14
+ SWARM_DIR = Path.home() / ".delimit" / "swarm"
15
+ REGISTRY_FILE = SWARM_DIR / "agent_registry.json"
16
+ VENTURES_FILE = SWARM_DIR / "ventures.json"
17
+ SWARM_LOG = SWARM_DIR / "swarm_log.jsonl"
18
+
19
+ # Default roster from Agent Swarm Standard v1.2
20
+ DEFAULT_ROSTER = {
21
+ "architect": {
22
+ "role": "System Design, Architecture, Complex Problem Solving",
23
+ "default_model": "claude-opus-4.6",
24
+ "fallback_model": "grok-4",
25
+ },
26
+ "senior_dev": {
27
+ "role": "Implementation, Code Generation, Feature Building",
28
+ "default_model": "claude-opus-4.6",
29
+ "fallback_model": "codex-gpt-5.4",
30
+ },
31
+ "reviewer": {
32
+ "role": "Code Review, PR Analysis, Bug Detection",
33
+ "default_model": "gemini-3.1-pro-preview",
34
+ "fallback_model": "grok-4",
35
+ },
36
+ "qa": {
37
+ "role": "Quality Assurance, Testing, CI Verification",
38
+ "default_model": "gemini-3.1-pro-preview",
39
+ "fallback_model": "codex-gpt-5.4",
40
+ },
41
+ "ops": {
42
+ "role": "Strategy, Deliberation, Outreach, Competitive Intel",
43
+ "default_model": "grok-4",
44
+ "fallback_model": "gemini-3.1-pro-preview",
45
+ },
46
+ }
47
+
48
+
49
+ def _ensure_dir():
50
+ SWARM_DIR.mkdir(parents=True, exist_ok=True)
51
+
52
+
53
+ def _load_registry() -> Dict[str, Any]:
54
+ if not REGISTRY_FILE.exists():
55
+ return {"agents": {}, "version": "1.2"}
56
+ try:
57
+ return json.loads(REGISTRY_FILE.read_text())
58
+ except (json.JSONDecodeError, OSError):
59
+ return {"agents": {}, "version": "1.2"}
60
+
61
+
62
+ def _save_registry(registry: Dict[str, Any]):
63
+ _ensure_dir()
64
+ REGISTRY_FILE.write_text(json.dumps(registry, indent=2))
65
+
66
+
67
+ def _load_ventures() -> Dict[str, Any]:
68
+ if not VENTURES_FILE.exists():
69
+ return {}
70
+ try:
71
+ return json.loads(VENTURES_FILE.read_text())
72
+ except (json.JSONDecodeError, OSError):
73
+ return {}
74
+
75
+
76
+ def _save_ventures(ventures: Dict[str, Any]):
77
+ _ensure_dir()
78
+ VENTURES_FILE.write_text(json.dumps(ventures, indent=2))
79
+
80
+
81
+ def _log(entry: Dict[str, Any]):
82
+ _ensure_dir()
83
+ entry["ts"] = time.strftime("%Y-%m-%dT%H:%M:%SZ")
84
+ with open(SWARM_LOG, "a") as f:
85
+ f.write(json.dumps(entry) + "\n")
86
+
87
+
88
+ def register_venture(
89
+ name: str,
90
+ namespace: str = "",
91
+ repo_path: str = "",
92
+ deploy_target: str = "",
93
+ custom_tools: Optional[List[str]] = None,
94
+ special_rules: Optional[List[str]] = None,
95
+ ) -> Dict[str, Any]:
96
+ """Register a venture in the swarm with its own namespace and agent team."""
97
+ if not name:
98
+ return {"error": "name is required"}
99
+
100
+ name = name.strip().lower()
101
+ namespace = namespace or name.replace(" ", "_").replace("-", "_")
102
+
103
+ ventures = _load_ventures()
104
+ ventures[name] = {
105
+ "name": name,
106
+ "namespace": namespace,
107
+ "repo_path": repo_path,
108
+ "deploy_target": deploy_target,
109
+ "custom_tools": custom_tools or [],
110
+ "special_rules": special_rules or [],
111
+ "created_at": ventures.get(name, {}).get("created_at", time.strftime("%Y-%m-%dT%H:%M:%SZ")),
112
+ "updated_at": time.strftime("%Y-%m-%dT%H:%M:%SZ"),
113
+ }
114
+ _save_ventures(ventures)
115
+
116
+ # Auto-create agents for this venture
117
+ registry = _load_registry()
118
+ agents_created = 0
119
+ for role_key, role_config in DEFAULT_ROSTER.items():
120
+ agent_id = f"{namespace}-{role_key}-01"
121
+ if agent_id not in registry["agents"]:
122
+ registry["agents"][agent_id] = {
123
+ "id": agent_id,
124
+ "venture": name,
125
+ "namespace": namespace,
126
+ "role": role_key,
127
+ "role_description": role_config["role"],
128
+ "model": role_config["default_model"],
129
+ "fallback_model": role_config["fallback_model"],
130
+ "permissions": {
131
+ "read": f"{namespace}/*",
132
+ "write": f"{namespace}/src/*",
133
+ "deploy": False,
134
+ },
135
+ "status": "active",
136
+ "created_at": time.strftime("%Y-%m-%dT%H:%M:%SZ"),
137
+ }
138
+ agents_created += 1
139
+
140
+ _save_registry(registry)
141
+ _log({"action": "register_venture", "venture": name, "agents_created": agents_created})
142
+
143
+ return {
144
+ "status": "registered",
145
+ "venture": name,
146
+ "namespace": namespace,
147
+ "agents_created": agents_created,
148
+ "total_agents": len([a for a in registry["agents"].values() if a["venture"] == name]),
149
+ "message": f"Venture '{name}' registered with {agents_created} new agent(s)",
150
+ }
151
+
152
+
153
+ def get_venture(name: str = "") -> Dict[str, Any]:
154
+ """Get venture details, or list all ventures."""
155
+ ventures = _load_ventures()
156
+
157
+ if not name:
158
+ return {
159
+ "status": "ok",
160
+ "ventures": list(ventures.values()),
161
+ "total": len(ventures),
162
+ }
163
+
164
+ name = name.strip().lower()
165
+ if name not in ventures:
166
+ return {"error": f"Venture '{name}' not found"}
167
+
168
+ venture = ventures[name]
169
+ registry = _load_registry()
170
+ agents = [a for a in registry["agents"].values() if a["venture"] == name]
171
+
172
+ return {
173
+ "status": "ok",
174
+ "venture": venture,
175
+ "agents": agents,
176
+ "agent_count": len(agents),
177
+ }
178
+
179
+
180
+ def get_agent(agent_id: str = "") -> Dict[str, Any]:
181
+ """Get agent details, or list all agents across ventures."""
182
+ registry = _load_registry()
183
+
184
+ if not agent_id:
185
+ agents = list(registry["agents"].values())
186
+ by_venture = {}
187
+ for a in agents:
188
+ by_venture.setdefault(a["venture"], []).append({
189
+ "id": a["id"],
190
+ "role": a["role"],
191
+ "model": a["model"],
192
+ "status": a["status"],
193
+ })
194
+ return {
195
+ "status": "ok",
196
+ "total_agents": len(agents),
197
+ "by_venture": by_venture,
198
+ }
199
+
200
+ agent = registry["agents"].get(agent_id)
201
+ if not agent:
202
+ return {"error": f"Agent '{agent_id}' not found"}
203
+ return {"status": "ok", "agent": agent}
204
+
205
+
206
+ def check_namespace_access(
207
+ agent_id: str,
208
+ target_path: str,
209
+ action: str = "read",
210
+ ) -> Dict[str, Any]:
211
+ """Check if an agent has access to a path within namespace isolation rules."""
212
+ registry = _load_registry()
213
+ agent = registry["agents"].get(agent_id)
214
+
215
+ if not agent:
216
+ return {"allowed": False, "reason": f"Agent '{agent_id}' not found"}
217
+
218
+ namespace = agent["namespace"]
219
+ ventures = _load_ventures()
220
+ venture = ventures.get(agent["venture"], {})
221
+ repo_path = venture.get("repo_path", "")
222
+
223
+ # Check if target is within the venture's namespace
224
+ if repo_path and target_path.startswith(repo_path):
225
+ if action == "read":
226
+ return {"allowed": True, "agent": agent_id, "reason": "Within venture namespace"}
227
+ if action == "write":
228
+ return {"allowed": True, "agent": agent_id, "reason": "Write within namespace"}
229
+ if action == "deploy":
230
+ if agent["permissions"].get("deploy", False):
231
+ return {"allowed": True, "agent": agent_id, "reason": "Deploy permitted"}
232
+ return {"allowed": False, "agent": agent_id, "reason": "Deploy requires founder approval"}
233
+
234
+ # Cross-venture access blocked by default
235
+ return {
236
+ "allowed": False,
237
+ "agent": agent_id,
238
+ "target": target_path,
239
+ "namespace": namespace,
240
+ "reason": f"Cross-venture access blocked. Agent '{agent_id}' can only access {namespace}/* paths.",
241
+ }
242
+
243
+
244
+ def get_swarm_status() -> Dict[str, Any]:
245
+ """Get the full swarm status — ventures, agents, health."""
246
+ ventures = _load_ventures()
247
+ registry = _load_registry()
248
+ agents = list(registry["agents"].values())
249
+
250
+ return {
251
+ "status": "ok",
252
+ "version": registry.get("version", "1.2"),
253
+ "ventures": len(ventures),
254
+ "total_agents": len(agents),
255
+ "active_agents": len([a for a in agents if a["status"] == "active"]),
256
+ "by_venture": {
257
+ v: {
258
+ "agents": len([a for a in agents if a["venture"] == v]),
259
+ "namespace": ventures[v].get("namespace", v),
260
+ "repo": ventures[v].get("repo_path", ""),
261
+ }
262
+ for v in ventures
263
+ },
264
+ "roster": list(DEFAULT_ROSTER.keys()),
265
+ }
266
+
267
+
268
+ # ═══════════════════════════════════════════════════════════════════════
269
+ # LED-276: Central Governor — tiered approvals + auto-escalation
270
+ # ═══════════════════════════════════════════════════════════════════════
271
+
272
+ APPROVAL_TIERS = {
273
+ "deploy_production": "founder_required",
274
+ "deploy_staging": "auto_approved",
275
+ "social_post": "founder_email",
276
+ "social_low_risk": "auto_after_consensus",
277
+ "outreach_issue": "founder_email",
278
+ "ledger_update": "auto_approved",
279
+ "code_commit": "auto_approved",
280
+ "security_audit": "auto_approved",
281
+ }
282
+
283
+ ESCALATION_RULES = [
284
+ {"trigger": "collision_detected", "action": "halt_and_notify", "severity": "high"},
285
+ {"trigger": "prompt_drift_exceeded", "action": "pause_agent", "severity": "medium"},
286
+ {"trigger": "unauthorized_deploy", "action": "block_and_alert", "severity": "critical"},
287
+ {"trigger": "pii_detected_outbound", "action": "redact_and_log", "severity": "high"},
288
+ {"trigger": "xai_credits_exhausted", "action": "pause_posting", "severity": "medium"},
289
+ {"trigger": "model_error_rate_high", "action": "switch_to_fallback", "severity": "medium"},
290
+ ]
291
+
292
+
293
+ def check_approval(action: str, venture: str = "", agent_id: str = "") -> Dict[str, Any]:
294
+ """Check if an action requires approval or is auto-approved.
295
+
296
+ Tiered approval system:
297
+ - deploy_production: always requires founder approval
298
+ - deploy_staging: auto-approved
299
+ - social_post: founder email approval
300
+ - social_low_risk: auto after multi-model consensus
301
+ - code_commit, ledger_update: auto-approved
302
+ """
303
+ tier = APPROVAL_TIERS.get(action, "founder_required")
304
+
305
+ result = {
306
+ "action": action,
307
+ "tier": tier,
308
+ "venture": venture,
309
+ "agent_id": agent_id,
310
+ }
311
+
312
+ if tier == "auto_approved":
313
+ result["approved"] = True
314
+ result["message"] = f"'{action}' is auto-approved"
315
+ elif tier == "auto_after_consensus":
316
+ result["approved"] = False
317
+ result["message"] = f"'{action}' requires multi-model consensus before auto-approval"
318
+ result["next_step"] = "Run delimit_deliberate on the proposed action"
319
+ elif tier == "founder_email":
320
+ result["approved"] = False
321
+ result["message"] = f"'{action}' requires founder email approval"
322
+ result["next_step"] = "Send via delimit_notify for founder review"
323
+ else:
324
+ result["approved"] = False
325
+ result["message"] = f"'{action}' requires founder approval"
326
+ result["next_step"] = "Submit for founder review via dashboard or email"
327
+
328
+ _log({"action": "approval_check", "requested": action, "result": tier, "venture": venture, "agent": agent_id})
329
+ return result
330
+
331
+
332
+ def get_escalation_rules() -> Dict[str, Any]:
333
+ """Get the current escalation rules for the central governor."""
334
+ return {
335
+ "status": "ok",
336
+ "rules": ESCALATION_RULES,
337
+ "approval_tiers": APPROVAL_TIERS,
338
+ }
339
+
340
+
341
+ # ═══════════════════════════════════════════════════════════════════════
342
+ # Usage Guide
343
+ # ═══════════════════════════════════════════════════════════════════════
344
+
345
+ USAGE_GUIDE = """
346
+ # Delimit Agent Swarm — Usage Guide
347
+
348
+ ## Quick Start
349
+
350
+ 1. Register your ventures:
351
+ delimit_swarm(action="register", venture="my-project", repo_path="/path/to/repo")
352
+
353
+ 2. View your swarm:
354
+ delimit_swarm(action="status")
355
+
356
+ 3. Check agent permissions:
357
+ delimit_swarm(action="check", agent_id="my_project-architect-01", target_path="/path/to/file")
358
+
359
+ 4. Check approval tier:
360
+ delimit_swarm(action="approve", action_name="deploy_production")
361
+
362
+ ## Agent Roles
363
+
364
+ Each venture gets 5 agent roles:
365
+ - architect: System design (default: Claude Opus)
366
+ - senior_dev: Implementation (default: Claude Opus)
367
+ - reviewer: Code review (default: Gemini)
368
+ - qa: Testing (default: Gemini)
369
+ - ops: Strategy & outreach (default: Grok)
370
+
371
+ ## Namespace Isolation
372
+
373
+ - Agents can only access files within their venture's repo path
374
+ - Cross-venture access is blocked by default
375
+ - Deploy requires founder approval (auto for staging)
376
+ - All actions are logged to ~/.delimit/swarm/swarm_log.jsonl
377
+
378
+ ## Approval Tiers
379
+
380
+ | Action | Tier |
381
+ |--------|------|
382
+ | deploy_production | Founder approval required |
383
+ | deploy_staging | Auto-approved |
384
+ | social_post | Founder email approval |
385
+ | code_commit | Auto-approved |
386
+ | ledger_update | Auto-approved |
387
+
388
+ ## Escalation
389
+
390
+ Critical alerts auto-escalate: collision, unauthorized deploy, PII detected.
391
+ Medium alerts: prompt drift, model errors, credit exhaustion.
392
+ """
393
+
394
+
395
+ def get_usage_guide() -> Dict[str, Any]:
396
+ """Get the swarm usage guide."""
397
+ return {"guide": USAGE_GUIDE, "version": "1.2"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "delimit-cli",
3
3
  "mcpName": "io.github.delimit-ai/delimit-mcp-server",
4
- "version": "3.15.6",
4
+ "version": "3.15.7",
5
5
  "description": "Unify Claude Code, Codex, Cursor, and Gemini CLI with persistent context, governance, and multi-model debate.",
6
6
  "main": "index.js",
7
7
  "files": [