pantheon-opencode 1.0.0
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/AGENTS.md +37 -0
- package/LICENSE +21 -0
- package/README.md +1013 -0
- package/bin/pantheon-init.mjs +183 -0
- package/commands/pantheon-audit.md +25 -0
- package/commands/pantheon-bg.md +10 -0
- package/commands/pantheon-consolidate.md +11 -0
- package/commands/pantheon-deepwork.md +128 -0
- package/commands/pantheon-doc.md +10 -0
- package/commands/pantheon-focus.md +9 -0
- package/commands/pantheon-forget.md +58 -0
- package/commands/pantheon-hash.md +11 -0
- package/commands/pantheon-optimize.md +79 -0
- package/commands/pantheon-remember.md +44 -0
- package/commands/pantheon-search.md +48 -0
- package/commands/pantheon-status.md +71 -0
- package/commands/pantheon-todo.md +11 -0
- package/commands/pantheon.md +49 -0
- package/docs/AGENT-MCP.md +194 -0
- package/docs/ARCHITECTURE.md +384 -0
- package/docs/BRANCH-PROTECTION.md +142 -0
- package/docs/INDEX.md +81 -0
- package/docs/INSTALLATION.md +217 -0
- package/docs/MCP.md +238 -0
- package/docs/MEMORY.md +471 -0
- package/docs/MIGRATION-MEMORY-BANK.md +139 -0
- package/docs/PLATFORMS.md +5 -0
- package/docs/QUICKSTART.md +49 -0
- package/docs/README.md +18 -0
- package/docs/RELEASING.md +256 -0
- package/docs/SETUP.md +5 -0
- package/docs/UPGRADING.md +41 -0
- package/docs/mcp-recommendations.md +439 -0
- package/docs/mcp-tools.md +156 -0
- package/docs/mcp-user-guide.md +204 -0
- package/docs/persistence-mcp.md +111 -0
- package/package.json +72 -0
- package/pantheon.schema.json +158 -0
- package/scripts/__init__.py +0 -0
- package/scripts/_pantheon_paths.py +68 -0
- package/scripts/check-version-consistency.sh +23 -0
- package/scripts/code_mode_server.py +202 -0
- package/scripts/doctor.mjs +763 -0
- package/scripts/generate-prompts.sh +222 -0
- package/scripts/generate-routing-docs.mjs +104 -0
- package/scripts/hash_verify.py +192 -0
- package/scripts/init-pantheon-mcp.sh +118 -0
- package/scripts/install/agents-md.mjs +214 -0
- package/scripts/install/health-check.mjs +196 -0
- package/scripts/install/migrate.mjs +209 -0
- package/scripts/install/opencode.mjs +645 -0
- package/scripts/install/shared.mjs +655 -0
- package/scripts/install/venv.mjs +116 -0
- package/scripts/install-mcp.mjs +885 -0
- package/scripts/install.mjs +26 -0
- package/scripts/manifest.mjs +622 -0
- package/scripts/mcp_persistence_server.py +459 -0
- package/scripts/mcp_resources_server.py +206 -0
- package/scripts/memory_cache.py +78 -0
- package/scripts/memory_mcp_server.py +605 -0
- package/scripts/paths.py +64 -0
- package/scripts/prune_context.py +72 -0
- package/scripts/release-bundle.mjs +109 -0
- package/scripts/scrub-secrets.py +282 -0
- package/scripts/scrub_secrets.py +281 -0
- package/scripts/test-context-compression.sh +166 -0
- package/scripts/themis_heuristic_scan.py +287 -0
- package/scripts/todo_enforcer.py +242 -0
- package/scripts/uninstall.mjs +1057 -0
- package/scripts/validate-routing.mjs +160 -0
- package/scripts/validate_agent_frontmatter.py +226 -0
- package/scripts/versioning.mjs +254 -0
- package/skills-lock.json +16 -0
- package/src/agents/aphrodite.md +162 -0
- package/src/agents/apollo.md +109 -0
- package/src/agents/athena.md +226 -0
- package/src/agents/demeter.md +146 -0
- package/src/agents/gaia.md +82 -0
- package/src/agents/hephaestus.md +105 -0
- package/src/agents/hermes.md +302 -0
- package/src/agents/iris.md +99 -0
- package/src/agents/mnemosyne.md +226 -0
- package/src/agents/nyx.md +87 -0
- package/src/agents/prometheus.md +199 -0
- package/src/agents/talos.md +93 -0
- package/src/agents/themis.md +187 -0
- package/src/agents/zeus.md +209 -0
- package/src/instructions/agent-return-format.instructions.md +26 -0
- package/src/instructions/backend-standards.instructions.md +45 -0
- package/src/instructions/documentation-standards.instructions.md +53 -0
- package/src/instructions/frontend-standards.instructions.md +46 -0
- package/src/instructions/memory-protocol.instructions.md +67 -0
- package/src/instructions/yagni.instructions.md +21 -0
- package/src/instructions/zeus-anti-stall.instructions.md +72 -0
- package/src/instructions/zeus-communication-rules.instructions.md +15 -0
- package/src/instructions/zeus-council-synthesis.instructions.md +105 -0
- package/src/instructions/zeus-timeout-retry.instructions.md +127 -0
- package/src/mcp/_pantheon_paths.py +67 -0
- package/src/mcp/code_mode_server.py +202 -0
- package/src/mcp/init-pantheon-mcp.sh +118 -0
- package/src/mcp/install-mcp.mjs +885 -0
- package/src/mcp/mcp_persistence_server.py +458 -0
- package/src/mcp/mcp_resources_server.py +205 -0
- package/src/mcp/memory_mcp_server.py +604 -0
- package/src/mcp/requirements-mcp-core.txt +5 -0
- package/src/mcp/requirements-mcp.txt +5 -0
- package/src/plugin.ts +19 -0
- package/src/plugins/tui/dist/tui.js +143 -0
- package/src/plugins/tui/dist/tui.tsx +144 -0
- package/src/plugins/tui/package.json +32 -0
- package/src/plugins/tui/src/index.tsx +144 -0
- package/src/plugins/tui/tsdown.config.ts +22 -0
- package/src/routing.yml +499 -0
- package/src/skills/README.md +230 -0
- package/src/skills/agent-coordination/SKILL.md +95 -0
- package/src/skills/artifact-management/SKILL.md +118 -0
- package/src/skills/auto-continue/SKILL.md +280 -0
- package/src/skills/code-review-checklist/SKILL.md +139 -0
- package/src/skills/context-compression/SKILL.md +861 -0
- package/src/skills/git-workflow-and-versioning/SKILL.md +32 -0
- package/src/skills/incremental-implementation/SKILL.md +27 -0
- package/src/skills/memory-bank/SKILL.md +165 -0
- package/src/skills/orchestration-workflow/SKILL.md +311 -0
- package/src/skills/security-hardening/SKILL.md +36 -0
- package/src/skills/session-goal/SKILL.md +138 -0
- package/src/skills/spec-driven-development/SKILL.md +23 -0
- package/src/skills/tdd-with-agents/SKILL.md +170 -0
- package/src/skills/visual-review-pipeline/SKILL.md +200 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Stall detection, phase reminders, delegate retry, and progress checkpoints for Zeus"
|
|
3
|
+
name: "Zeus Anti-Stall"
|
|
4
|
+
applyTo: "agents/zeus.agent.md"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 🛑 ANTI-STALL & STALL DETECTION
|
|
8
|
+
|
|
9
|
+
You MUST proactively detect and recover from stalled states. Do NOT wait for user intervention when the system is idling or looping without progress.
|
|
10
|
+
|
|
11
|
+
## Stall Detection Protocol
|
|
12
|
+
|
|
13
|
+
You MUST self-monitor for these stall conditions:
|
|
14
|
+
|
|
15
|
+
| Symptom | Detection Rule | Recovery Action |
|
|
16
|
+
|---------|---------------|-----------------|
|
|
17
|
+
| Silent loop | 3+ consecutive turns with no tool call AND no visible progress | Output `[STALL_DETECTED]` and re-read your task definition. If still stuck, escalate to user with: "I appear to be stuck on [task]. Options: (1) retry with different approach, (2) delegate to specialist, (3) simplify scope." |
|
|
18
|
+
| Delegation black hole | Agent dispatched but no response after 2x the timeout from routing.yml | Log the hang, cancel via `cancel_task`, dispatch to fallback agent, report to user |
|
|
19
|
+
| Circular delegation | Same specialist re-dispatched for same task 2+ times without progress | Break cycle: dispatch to different specialist OR escalate to user |
|
|
20
|
+
| Idle after completion | All background tasks completed but no synthesis/next step for 2+ turns | Force synthesis: summarize all completed results and propose next action |
|
|
21
|
+
| Context thrash | Re-reading same files repeatedly without new action | Stop re-reading. State: "Already have context on [file]. Proceeding with [action]." |
|
|
22
|
+
|
|
23
|
+
## Phase Reminder
|
|
24
|
+
|
|
25
|
+
After dispatching background specialists, you MUST:
|
|
26
|
+
1. DO NOT poll running jobs or consume their partial output
|
|
27
|
+
2. DO NOT advance dependent work until terminal results arrive
|
|
28
|
+
3. Continue orchestration ONLY on non-overlapping independent work
|
|
29
|
+
4. If nothing independent remains, briefly report what was launched and WAIT
|
|
30
|
+
|
|
31
|
+
Self-check every 3 turns: "Am I waiting on a delegate? Have I polled without need? Is there independent work I can do?"
|
|
32
|
+
|
|
33
|
+
## Delegate Retry Enhancement
|
|
34
|
+
|
|
35
|
+
When a delegation fails (timeout, empty response, error):
|
|
36
|
+
|
|
37
|
+
1. **FIRST:** Check if the error is a known pattern:
|
|
38
|
+
- "Agent not responding" → verify agent name matches routing.yml
|
|
39
|
+
- "Context exceeded" → reduce scope, split into smaller tasks
|
|
40
|
+
- "Permission denied" → verify agent has correct tools/permissions
|
|
41
|
+
|
|
42
|
+
2. **Retry ONCE** with rephrased prompt — add: "Previous attempt failed with: [error]. Adjusted approach: [what changed]."
|
|
43
|
+
|
|
44
|
+
3. **If retry also fails** → DO NOT retry a third time blindly. Instead:
|
|
45
|
+
- Dispatch to fallback agent (from routing.yml)
|
|
46
|
+
- If no fallback, escalate to user with: "Task [X] failed after retry. Options: (a) simplify, (b) different agent, (c) manual intervention."
|
|
47
|
+
|
|
48
|
+
## Progress Checkpoint
|
|
49
|
+
|
|
50
|
+
On tasks expected to run > 5 turns:
|
|
51
|
+
- After turn 5: output `[CHECKPOINT] Completed so far: [summary]. Remaining: [list].`
|
|
52
|
+
- After turn 10: re-evaluate. If < 50% done, consider splitting or escalating.
|
|
53
|
+
- If 3 consecutive turns produce no tool calls: trigger Stall Detection Protocol (see above).
|
|
54
|
+
|
|
55
|
+
## Heartbeat & Checkpoint Integration
|
|
56
|
+
|
|
57
|
+
### Heartbeat Check
|
|
58
|
+
- If `.pantheon/deepwork/<slug>/heartbeat.json` exists and `last_action` is older than 300s, log a stall warning and resume
|
|
59
|
+
- Write heartbeat after every anti-stall recovery action
|
|
60
|
+
|
|
61
|
+
### Checkpoint Auto-Save
|
|
62
|
+
Before ANY delegate dispatch, save a checkpoint:
|
|
63
|
+
```bash
|
|
64
|
+
python .pantheon/code-mode/checkpoint_session.py save <slug>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Long-Session Progress
|
|
68
|
+
Every 5 turns during a long session, update STATUS.md with:
|
|
69
|
+
- Current phase
|
|
70
|
+
- Completed tasks
|
|
71
|
+
- Pending tasks
|
|
72
|
+
- Any blockers
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Communication rules for Zeus and all Pantheon agents — clarity, conciseness, and directness"
|
|
3
|
+
name: "Zeus Communication Rules"
|
|
4
|
+
applyTo: "agents/zeus.agent.md"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 🗣️ COMMUNICATION RULES
|
|
8
|
+
|
|
9
|
+
These rules apply to Zeus and all agents in the system:
|
|
10
|
+
|
|
11
|
+
- **No Flattery**: Never start a response with compliments or affirmations ("Great question!", "Absolutely!", "Sure!"). Begin directly with the answer or action.
|
|
12
|
+
- **Honest Pushback**: If a request is technically unsound or will cause problems, say so clearly and explain why. Offer a better approach. Agreeing to avoid friction is worse than a useful correction.
|
|
13
|
+
- **Concise Execution**: Skip lengthy preambles. State what you're doing in one line, do it, report the outcome. Avoid verbose commentary around trivial steps.
|
|
14
|
+
- **No Padding**: Don't add filler sentences, summaries of what was just said, or redundant confirmations. Every sentence must carry information.
|
|
15
|
+
- **Uncertainty = Ask**: If requirements are ambiguous, ask one targeted question. Don't guess and implement the wrong thing.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Council synthesis — dispatch 2-4 specialists inline for multi-perspective decisions"
|
|
3
|
+
name: "Zeus Council Synthesis"
|
|
4
|
+
applyTo: "agents/zeus.agent.md"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 🏛️ INLINE COUNCIL SYNTHESIS — /pantheon
|
|
8
|
+
|
|
9
|
+
When a question requires multiple expert perspectives on a trade-off or architecture decision, **dispatch specialists inline** (visible to user) instead of delegating to a hidden subagent.
|
|
10
|
+
|
|
11
|
+
## Trigger Patterns (detect ANY)
|
|
12
|
+
- Trade-off questions: "which is better?", "should we use X or Y?", "compare A and B"
|
|
13
|
+
- Architecture decisions with long-term impact
|
|
14
|
+
- Security/compliance choices
|
|
15
|
+
- Technology selection (databases, frameworks, providers, libraries)
|
|
16
|
+
- "Is this safe?", "trade-offs of...", "what are the risks?"
|
|
17
|
+
- Cost vs quality decisions
|
|
18
|
+
- Multi-stakeholder concerns (frontend + backend + infra)
|
|
19
|
+
|
|
20
|
+
```mermaid
|
|
21
|
+
---
|
|
22
|
+
config:
|
|
23
|
+
look: classic
|
|
24
|
+
theme: dark
|
|
25
|
+
---
|
|
26
|
+
sequenceDiagram
|
|
27
|
+
participant U as User
|
|
28
|
+
participant Z as Zeus
|
|
29
|
+
participant S1 as Specialist 1
|
|
30
|
+
participant S2 as Specialist 2
|
|
31
|
+
participant S3 as Specialist 3
|
|
32
|
+
|
|
33
|
+
U->>Z: /pantheon question
|
|
34
|
+
Note over Z: Detect multi-perspective trigger
|
|
35
|
+
Z->>Z: Select 2-4 specialists
|
|
36
|
+
par Dispatch all specialists
|
|
37
|
+
Z->>S1: Domain-specific query
|
|
38
|
+
Z->>S2: Domain-specific query
|
|
39
|
+
Z->>S3: Domain-specific query
|
|
40
|
+
end
|
|
41
|
+
par Collect responses
|
|
42
|
+
S1-->>Z: Position + trade-offs
|
|
43
|
+
S2-->>Z: Position + trade-offs
|
|
44
|
+
S3-->>Z: Position + trade-offs
|
|
45
|
+
end
|
|
46
|
+
Note over Z: Synthesize agreements + divergences + recommendation
|
|
47
|
+
Z->>U: 🏛️ Council Synthesis
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Dispatch with Timeout
|
|
51
|
+
|
|
52
|
+
Send ALL `task()` calls in a single message. Each specialist must respond concisely (2-4 sentences).
|
|
53
|
+
|
|
54
|
+
⚠️ **TIMEOUT RULE:** If a specialist does not respond after other specialists have responded, proceed with partial results. Do NOT wait indefinitely.
|
|
55
|
+
|
|
56
|
+
1. Detect multi-perspective trigger pattern
|
|
57
|
+
2. Select 2-4 specialists based on domain (see tables below)
|
|
58
|
+
3. Dispatch ALL `task()` calls in ONE message
|
|
59
|
+
4. Wait for responses — note any specialists that don't respond as TIMEOUT
|
|
60
|
+
5. Synthesize: include "X of Y specialists responded" in output
|
|
61
|
+
6. Adjust confidence: down if key specialists timed out
|
|
62
|
+
|
|
63
|
+
## Domain-to-Specialist Mapping
|
|
64
|
+
|
|
65
|
+
| Domain | Specialists |
|
|
66
|
+
|--------|-------------|
|
|
67
|
+
| Architecture | hermes, demeter, themis, athena |
|
|
68
|
+
| Security | themis, hermes, prometheus, nyx |
|
|
69
|
+
| Database | demeter, hermes, prometheus |
|
|
70
|
+
| AI/RAG | hephaestus, nyx |
|
|
71
|
+
| Infrastructure | prometheus, hermes, themis |
|
|
72
|
+
| Frontend/UX | aphrodite, themis, hermes |
|
|
73
|
+
| Observability | nyx, hermes |
|
|
74
|
+
| General | athena, themis, hermes |
|
|
75
|
+
|
|
76
|
+
## Synthesis Output Template
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
## 🏛️ Council Synthesis
|
|
80
|
+
|
|
81
|
+
**Question:** <original question>
|
|
82
|
+
**Date:** <date>
|
|
83
|
+
**Response rate:** X of Y specialists responded
|
|
84
|
+
**Timed out:** @agent1, @agent2 (if any)
|
|
85
|
+
|
|
86
|
+
### Specialist Perspectives
|
|
87
|
+
| Agent | Position | Trade-offs | Confidence |
|
|
88
|
+
|-------|----------|------------|------------|
|
|
89
|
+
| @agent1 | ... | ... | High/Med/Low |
|
|
90
|
+
|
|
91
|
+
### Agreements
|
|
92
|
+
- <what 2+ specialists agree on>
|
|
93
|
+
|
|
94
|
+
### Divergences
|
|
95
|
+
| Issue | Side A | Side B | Resolution |
|
|
96
|
+
|-------|--------|--------|------------|
|
|
97
|
+
|
|
98
|
+
### Recommendation
|
|
99
|
+
<decisive conclusion>
|
|
100
|
+
|
|
101
|
+
### Decision Gate
|
|
102
|
+
**Confidence:** High/Medium/Low (adjusted for response rate)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
> **Note**: The user can explicitly invoke this via `/pantheon <question>`. The command dispatches to Zeus who runs the council inline.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Timeout enforcement, retry policies, subtask dispatch, and timeout tracking for Zeus"
|
|
3
|
+
name: "Zeus Timeout & Retry"
|
|
4
|
+
applyTo: "agents/zeus.agent.md"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# ⏱️ TIMEOUT & RETRY ENFORCEMENT
|
|
8
|
+
|
|
9
|
+
When a delegated agent does not respond in time, enforce the timeout policy from `routing.yml`.
|
|
10
|
+
|
|
11
|
+
## Timeout Behavior by Agent Role
|
|
12
|
+
|
|
13
|
+
| Agent Role | Timeout | Retry Policy | Fallback | Partial Results OK? | Reasoning Effort |
|
|
14
|
+
|------------|---------|-------------|----------|---------------------|------------------|
|
|
15
|
+
| Explorer (@apollo) | 60s | 2 retries, exponential backoff | @athena | ✅ Yes | low |
|
|
16
|
+
| Implementer (@hermes, @aphrodite, @demeter) | 180s | 3 retries, exponential backoff | @talos | ❌ No | medium |
|
|
17
|
+
| Reviewer (@themis) | 120s | 2 retries, exponential backoff | @zeus | ❌ No | high |
|
|
18
|
+
| Infrastructure (@prometheus) | 300s | 2 retries, exponential backoff | @hermes | ❌ No | medium |
|
|
19
|
+
| Hotfix (@talos) | 30s | 1 retry, no backoff | @hermes | ✅ Yes | low |
|
|
20
|
+
| Remote Sensing (@gaia) | 120s | 2 retries, exponential backoff | @hermes | ✅ Yes | high |
|
|
21
|
+
|
|
22
|
+
## Retry Flow
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Task dispatch → timeout elapsed → log timeout
|
|
26
|
+
├─ retry_count > 0 → retry with backoff → decrement retry_count
|
|
27
|
+
├─ fallback_agent exists → dispatch to fallback
|
|
28
|
+
└─ no retries + no fallback → return TIMEOUT error to user
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Session Reuse Check
|
|
32
|
+
Before dispatching a task, check if a reusable session exists:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
@hermes — continuing from previous session.
|
|
36
|
+
Files already explored: backend/routers/auth.py, backend/services/auth_service.py.
|
|
37
|
+
New task: add refresh token rotation.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Use `session_max` from routing.yml to determine how many sessions to keep per agent.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
# 📦 SUBTASK DISPATCH (Lightweight Delegation)
|
|
45
|
+
|
|
46
|
+
Subtask is a bounded, low-risk delegation mode that **skips** the standard artifact lifecycle. Use it for focused work that doesn't need Themis review.
|
|
47
|
+
|
|
48
|
+
## When to Use Subtask vs Full Task
|
|
49
|
+
|
|
50
|
+
> **REGRA DE OURO:** Quando em dúvida, use full task. Subtask é para o que você tem 100% de certeza que é seguro pular revisão.
|
|
51
|
+
|
|
52
|
+
### Subtask Decision Tree (run BEFORE every delegation)
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
□ Scope: ≤2 files AND ≤10 lines changed? [YES→continue | NO→full task]
|
|
56
|
+
□ Risk: No schema change, no security impact? [YES→continue | NO→full task]
|
|
57
|
+
□ Auth: No authentication/authorization logic? [YES→continue | NO→full task]
|
|
58
|
+
□ Data: No data loss risk, no migration? [YES→continue | NO→full task]
|
|
59
|
+
□ Review: Output does NOT feed into Themis review? [YES→continue | NO→full task]
|
|
60
|
+
|
|
61
|
+
ALL YES → subtask (skip artifact + Themis)
|
|
62
|
+
ANY NO → full task (IMPL artifact + Themis review mandatory)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Comparison Table
|
|
66
|
+
|
|
67
|
+
| Aspect | Subtask | Full Task |
|
|
68
|
+
|--------|---------|-----------|
|
|
69
|
+
| Scope | Single file, <10 lines, read-only | Feature, multi-file, schema change |
|
|
70
|
+
| Risk | Low (no security/data implications) | Any risk level |
|
|
71
|
+
| Artifact | ❌ No IMPL artifact | ✅ IMPL artifact required |
|
|
72
|
+
| Themis review | ❌ None | ✅ Mandatory |
|
|
73
|
+
| Use case | Apollo discovery, Talos hotfix, bounded fix | Feature implementation, migration, API change |
|
|
74
|
+
|
|
75
|
+
### Concrete Examples
|
|
76
|
+
|
|
77
|
+
| Task | Scope | Risk | Subtask? | Why |
|
|
78
|
+
|------|-------|------|----------|-----|
|
|
79
|
+
| Fix typo in CSS class | 1 file, 1 line | None | ✅ | Bounded, no security impact |
|
|
80
|
+
| Add error handling to existing endpoint | 1 file, 5 lines | Low | ✅ | No schema change |
|
|
81
|
+
| Implement login endpoint | 2+ files, 50+ lines | High (auth) | ❌ | Security-critical, needs Themis |
|
|
82
|
+
| Database migration | 1 file, 15 lines | High (data) | ❌ | Data loss risk, needs rollback |
|
|
83
|
+
| Apollo codebase search | 0 files | None | ✅ | Read-only investigation |
|
|
84
|
+
| Update README | 1 file, 3 lines | None | ✅ | Documentation only |
|
|
85
|
+
|
|
86
|
+
### Safety Rules
|
|
87
|
+
1. **Bounded scope** — single file or read-only investigation
|
|
88
|
+
2. **Low risk** — no security implications, no data loss, no breaking changes
|
|
89
|
+
3. **No Themis dependency** — output doesn't feed into a phase that requires review
|
|
90
|
+
|
|
91
|
+
## Subtask Return Format
|
|
92
|
+
Expect a `subtask_summary` response with:
|
|
93
|
+
```
|
|
94
|
+
## subtask_summary
|
|
95
|
+
**files_changed:** [paths]
|
|
96
|
+
**summary:** What was done
|
|
97
|
+
**tests:** ✅ or N/A
|
|
98
|
+
**status:** complete | partial | escalated
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Timeout Parcial (Partial Results)
|
|
102
|
+
|
|
103
|
+
Timeout parcial is ONLY for read-only, independent agents:
|
|
104
|
+
- ✅ @apollo — can return partial file list ("found 7 of 12 files before timeout")
|
|
105
|
+
- ✅ @gaia — can return partial literature findings
|
|
106
|
+
|
|
107
|
+
- ✅ @talos — can confirm progress if hotfix times out
|
|
108
|
+
- ❌ Never for implementers or reviewers — must complete or fail
|
|
109
|
+
|
|
110
|
+
When dispatching with partial-OK, set expectation:
|
|
111
|
+
```
|
|
112
|
+
@apollo Search for auth files. Timeout parcial OK — return whatever you have.
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
# 📊 TIMEOUT TRACKING
|
|
118
|
+
|
|
119
|
+
Maintain awareness of in-flight delegations:
|
|
120
|
+
|
|
121
|
+
| Agent | Timeout | Status | Partial OK? |
|
|
122
|
+
|-------|---------|--------|-------------|
|
|
123
|
+
| @apollo | 60s | ✅ complete | ✅ |
|
|
124
|
+
| @hermes | 180s | ⏳ in progress | ❌ |
|
|
125
|
+
| @themis | 120s | ⏳ in progress | ❌ |
|
|
126
|
+
|
|
127
|
+
Log timeouts to `/memories/session/timeout-log.md` for later analysis.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Pantheon path resolution — global install + project root detection.
|
|
3
|
+
|
|
4
|
+
Provides two functions used by all MCP servers and utility scripts:
|
|
5
|
+
|
|
6
|
+
pantheon_home() -> Path
|
|
7
|
+
Pantheon global installation directory.
|
|
8
|
+
Priority: $PANTHEON_HOME → $XDG_CONFIG_HOME/opencode → ~/.config/opencode
|
|
9
|
+
|
|
10
|
+
pantheon_project() -> Path | None
|
|
11
|
+
Current Pantheon project root.
|
|
12
|
+
Priority: $PANTHEON_PROJECT → os.getcwd() → None (resources disabled)
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
from _pantheon_paths import pantheon_home, pantheon_project
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import os
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def pantheon_home() -> Path:
|
|
25
|
+
"""Return the Pantheon global installation directory.
|
|
26
|
+
|
|
27
|
+
Resolution priority:
|
|
28
|
+
1. $PANTHEON_HOME env var (explicit user override)
|
|
29
|
+
2. $XDG_CONFIG_HOME/opencode (XDG Base Directory spec)
|
|
30
|
+
3. ~/.config/opencode (POSIX default)
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Absolute Path to the Pantheon global config directory.
|
|
34
|
+
"""
|
|
35
|
+
env = os.environ.get("PANTHEON_HOME")
|
|
36
|
+
if env:
|
|
37
|
+
return Path(env).expanduser().resolve()
|
|
38
|
+
|
|
39
|
+
xdg = os.environ.get("XDG_CONFIG_HOME")
|
|
40
|
+
if xdg:
|
|
41
|
+
return Path(xdg).expanduser().resolve() / "opencode"
|
|
42
|
+
|
|
43
|
+
return Path.home() / ".config" / "opencode"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def pantheon_project() -> Path | None:
|
|
47
|
+
"""Return the Pantheon project root directory.
|
|
48
|
+
|
|
49
|
+
Resolution priority:
|
|
50
|
+
1. $PANTHEON_PROJECT env var (explicit override)
|
|
51
|
+
2. Current working directory (set by MCP client's cwd in opencode.json)
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Absolute Path to the project root, or None if neither is available
|
|
55
|
+
(project-scoped resources like deepwork/memory-bank are unavailable).
|
|
56
|
+
"""
|
|
57
|
+
env = os.environ.get("PANTHEON_PROJECT")
|
|
58
|
+
if env:
|
|
59
|
+
return Path(env).expanduser().resolve()
|
|
60
|
+
|
|
61
|
+
cwd = os.getcwd()
|
|
62
|
+
if cwd:
|
|
63
|
+
return Path(cwd).resolve()
|
|
64
|
+
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Pantheon Code Mode MCP Server.
|
|
3
|
+
|
|
4
|
+
Provides a confined execution environment for orchestration scripts
|
|
5
|
+
via MCP tools and resources.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python scripts/code_mode_server.py
|
|
9
|
+
|
|
10
|
+
Or via MCP client (stdio transport):
|
|
11
|
+
pantheon-code-mode:
|
|
12
|
+
command: python
|
|
13
|
+
args: ["scripts/code_mode_server.py"]
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import asyncio
|
|
19
|
+
import os
|
|
20
|
+
import stat
|
|
21
|
+
from contextlib import suppress
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
from _pantheon_paths import pantheon_home, pantheon_project
|
|
25
|
+
from mcp.server.fastmcp import FastMCP
|
|
26
|
+
|
|
27
|
+
# ── Constants ─────────────────────────────────────────────────────────────────
|
|
28
|
+
ALLOWED_EXTENSIONS: frozenset[str] = frozenset({".sh", ".py"})
|
|
29
|
+
SCRIPT_TIMEOUT: int = 30
|
|
30
|
+
|
|
31
|
+
# ── Scripts Directory Resolution ─────────────────────────────────────────────
|
|
32
|
+
# Priority:
|
|
33
|
+
# 1. /.opencode/.pantheon/code-mode/ (project install)
|
|
34
|
+
# 2. /.pantheon/code-mode/ (legacy fallback)
|
|
35
|
+
# 3. /.pantheon/code-mode/ (global fallback)
|
|
36
|
+
_PANTHEON_HOME: Path = pantheon_home()
|
|
37
|
+
_SCRIPTS_DIR_CANDIDATES: list[Path] = []
|
|
38
|
+
_proj = pantheon_project()
|
|
39
|
+
if _proj is not None:
|
|
40
|
+
_SCRIPTS_DIR_CANDIDATES.append(_proj / ".opencode" / ".pantheon" / "code-mode")
|
|
41
|
+
_SCRIPTS_DIR_CANDIDATES.append(_proj / ".pantheon" / "code-mode")
|
|
42
|
+
_SCRIPTS_DIR_CANDIDATES.append(_PANTHEON_HOME / ".pantheon" / "code-mode")
|
|
43
|
+
|
|
44
|
+
SCRIPTS_DIR: Path = _PANTHEON_HOME / ".pantheon" / "code-mode" # default
|
|
45
|
+
for _candidate in _SCRIPTS_DIR_CANDIDATES:
|
|
46
|
+
if _candidate.is_dir():
|
|
47
|
+
SCRIPTS_DIR = _candidate
|
|
48
|
+
break
|
|
49
|
+
|
|
50
|
+
# ── FastMCP App ───────────────────────────────────────────────────────────────
|
|
51
|
+
mcp = FastMCP(
|
|
52
|
+
"Pantheon Code Mode",
|
|
53
|
+
instructions="Confined script execution for Pantheon orchestration. "
|
|
54
|
+
"Scripts live in .pantheon/code-mode/ and must be .sh or .py files.",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# ── Helpers ───────────────────────────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _validate_script_name(script_name: str) -> Path:
|
|
61
|
+
"""Validate a script name and return its resolved path.
|
|
62
|
+
|
|
63
|
+
Raises ValueError if the name is invalid, traverses paths, or
|
|
64
|
+
has a disallowed extension.
|
|
65
|
+
"""
|
|
66
|
+
if not script_name:
|
|
67
|
+
raise ValueError("Script name cannot be empty")
|
|
68
|
+
|
|
69
|
+
name = script_name.strip()
|
|
70
|
+
if name.startswith("."):
|
|
71
|
+
raise ValueError(f"Invalid script name: '{script_name}'")
|
|
72
|
+
if "/" in name or "\\" in name:
|
|
73
|
+
raise ValueError(f"Invalid script name: '{script_name}'")
|
|
74
|
+
|
|
75
|
+
ext = Path(name).suffix.lower()
|
|
76
|
+
if ext not in ALLOWED_EXTENSIONS:
|
|
77
|
+
allowed = ", ".join(sorted(ALLOWED_EXTENSIONS))
|
|
78
|
+
raise ValueError(f"Extension '{ext}' not allowed. Allowed: {allowed}")
|
|
79
|
+
|
|
80
|
+
script_path = (SCRIPTS_DIR / name).resolve()
|
|
81
|
+
if not str(script_path).startswith(str(SCRIPTS_DIR.resolve())):
|
|
82
|
+
raise ValueError(f"Invalid script name: '{script_name}'")
|
|
83
|
+
if not script_path.exists():
|
|
84
|
+
raise ValueError(f"Script '{script_name}' not found")
|
|
85
|
+
|
|
86
|
+
return script_path
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _format_output(
|
|
90
|
+
stdout: str, stderr: str, exit_code: int, timed_out: bool = False
|
|
91
|
+
) -> str:
|
|
92
|
+
"""Format script execution output into a readable string."""
|
|
93
|
+
parts: list[str] = []
|
|
94
|
+
if timed_out:
|
|
95
|
+
parts.append(f"[TIMEOUT] Script exceeded {SCRIPT_TIMEOUT}s limit")
|
|
96
|
+
if stdout:
|
|
97
|
+
parts.append(stdout)
|
|
98
|
+
if stderr:
|
|
99
|
+
parts.append(f"[stderr]\n{stderr}")
|
|
100
|
+
parts.append(f"--- exit code: {exit_code}")
|
|
101
|
+
return "\n".join(parts)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# ── Static Resources ──────────────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@mcp.resource(
|
|
108
|
+
"pantheon://code-mode/scripts",
|
|
109
|
+
description="List of available code-mode scripts",
|
|
110
|
+
)
|
|
111
|
+
async def list_code_mode_scripts() -> str:
|
|
112
|
+
"""Return a list of available scripts in the code-mode directory."""
|
|
113
|
+
if not SCRIPTS_DIR.is_dir():
|
|
114
|
+
return "Code mode directory not found."
|
|
115
|
+
|
|
116
|
+
scripts: list[str] = []
|
|
117
|
+
for f in sorted(SCRIPTS_DIR.iterdir()):
|
|
118
|
+
if f.is_file() and f.suffix.lower() in ALLOWED_EXTENSIONS:
|
|
119
|
+
scripts.append(f"- {f.name}")
|
|
120
|
+
|
|
121
|
+
return "\n".join(scripts) if scripts else "No scripts found."
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# ── Template Resources ────────────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@mcp.resource(
|
|
128
|
+
"pantheon://code-mode/scripts/{script_name}",
|
|
129
|
+
description="Content of a code-mode script by name",
|
|
130
|
+
)
|
|
131
|
+
async def get_code_mode_script(script_name: str) -> str:
|
|
132
|
+
"""Return the source content of a code-mode script."""
|
|
133
|
+
try:
|
|
134
|
+
script_path = _validate_script_name(script_name)
|
|
135
|
+
return script_path.read_text(encoding="utf-8")
|
|
136
|
+
except ValueError as e:
|
|
137
|
+
return str(e)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# ── Tools ─────────────────────────────────────────────────────────────────────
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@mcp.tool(
|
|
144
|
+
name="execute_code_script",
|
|
145
|
+
description="Run a .sh/.py script from .pantheon/code-mode/ "
|
|
146
|
+
"with optional args. 30s timeout.",
|
|
147
|
+
)
|
|
148
|
+
async def execute_code_script(script_name: str, args: list[str] | None = None) -> str:
|
|
149
|
+
"""Execute a code-mode script with confinement and timeout.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
script_name: Name of the script in the code-mode directory.
|
|
153
|
+
args: Optional CLI arguments forwarded to the subprocess (e.g.
|
|
154
|
+
``["compress", "--text", "..."]``). Defaults to no args.
|
|
155
|
+
"""
|
|
156
|
+
args = args or []
|
|
157
|
+
try:
|
|
158
|
+
script_path = _validate_script_name(script_name)
|
|
159
|
+
except ValueError as e:
|
|
160
|
+
return str(e)
|
|
161
|
+
|
|
162
|
+
# Ensure script is executable
|
|
163
|
+
if not os.access(script_path, os.X_OK):
|
|
164
|
+
with suppress(OSError):
|
|
165
|
+
mode = script_path.stat().st_mode
|
|
166
|
+
script_path.chmod(mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
|
167
|
+
|
|
168
|
+
script_dir = script_path.parent
|
|
169
|
+
try:
|
|
170
|
+
proc = await asyncio.create_subprocess_exec(
|
|
171
|
+
str(script_path),
|
|
172
|
+
*args,
|
|
173
|
+
cwd=str(script_dir),
|
|
174
|
+
stdout=asyncio.subprocess.PIPE,
|
|
175
|
+
stderr=asyncio.subprocess.PIPE,
|
|
176
|
+
)
|
|
177
|
+
try:
|
|
178
|
+
stdout, stderr = await asyncio.wait_for(
|
|
179
|
+
proc.communicate(), timeout=SCRIPT_TIMEOUT
|
|
180
|
+
)
|
|
181
|
+
return _format_output(
|
|
182
|
+
stdout.decode("utf-8", errors="replace"),
|
|
183
|
+
stderr.decode("utf-8", errors="replace"),
|
|
184
|
+
proc.returncode or 0,
|
|
185
|
+
)
|
|
186
|
+
except TimeoutError:
|
|
187
|
+
proc.kill()
|
|
188
|
+
await proc.wait()
|
|
189
|
+
return _format_output("", "", -1, timed_out=True)
|
|
190
|
+
except FileNotFoundError:
|
|
191
|
+
return (
|
|
192
|
+
f"Script '{script_name}' not found "
|
|
193
|
+
f"or interpreter missing.\n--- exit code: -1"
|
|
194
|
+
)
|
|
195
|
+
except OSError as e:
|
|
196
|
+
return f"Failed to execute script: {e}\n--- exit code: -1"
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
# ── Main Entrypoint ───────────────────────────────────────────────────────────
|
|
200
|
+
|
|
201
|
+
if __name__ == "__main__":
|
|
202
|
+
mcp.run()
|