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,222 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# generate-prompts.sh — Generate dynamic prompt templates for Pantheon
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
PANTHEON_DIR="$(dirname "$SCRIPT_DIR")"
|
|
7
|
+
|
|
8
|
+
# ── Read active plan ─────────────────────────────────────────────────────────
|
|
9
|
+
PLAN_FILE="$PANTHEON_DIR/platform/plans/plan-active.json"
|
|
10
|
+
if [[ -f "$PLAN_FILE" ]]; then
|
|
11
|
+
PLAN=$(jq -r '.plan // "unknown"' "$PLAN_FILE")
|
|
12
|
+
SERVICE=$(jq -r '.service // "unknown"' "$PLAN_FILE")
|
|
13
|
+
TIER=$(jq -r '.tier // "unknown"' "$PLAN_FILE")
|
|
14
|
+
PRICE=$(jq -r '.price // "unknown"' "$PLAN_FILE")
|
|
15
|
+
MONTHLY_REQUESTS=$(jq -r '.limits.monthlyRequests // "unlimited"' "$PLAN_FILE")
|
|
16
|
+
|
|
17
|
+
# Build tier-to-model map
|
|
18
|
+
TIER_MAP=$(jq -r '
|
|
19
|
+
to_entries[]
|
|
20
|
+
| select(.key | IN("free","fast","default","premium"))
|
|
21
|
+
| " \(.key): \(.value)"
|
|
22
|
+
' "$PLAN_FILE" 2>/dev/null || echo " (unable to parse tiers)")
|
|
23
|
+
|
|
24
|
+
# Build agent overrides map
|
|
25
|
+
AGENT_OVERRIDES=$(jq -r '
|
|
26
|
+
.agent_overrides // {}
|
|
27
|
+
| to_entries[]
|
|
28
|
+
| " \(.key): \(.value)"
|
|
29
|
+
' "$PLAN_FILE" 2>/dev/null || true)
|
|
30
|
+
else
|
|
31
|
+
PLAN="default"
|
|
32
|
+
SERVICE="opencode"
|
|
33
|
+
TIER="default"
|
|
34
|
+
PRICE="unknown"
|
|
35
|
+
MONTHLY_REQUESTS="unlimited"
|
|
36
|
+
TIER_MAP=" default: unknown"
|
|
37
|
+
AGENT_OVERRIDES=""
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# ── List agents ──────────────────────────────────────────────────────────────
|
|
41
|
+
AGENTS_DIR="$PANTHEON_DIR/platform/opencode/agents"
|
|
42
|
+
AGENTS_LIST=""
|
|
43
|
+
if [[ -d "$AGENTS_DIR" ]]; then
|
|
44
|
+
AGENTS_LIST=$(ls "$AGENTS_DIR"/*.md 2>/dev/null | xargs -n1 basename | sed 's/\.md$//' | sort | paste -sd ', ' -)
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# ── Create dynamic prompts directory ─────────────────────────────────────────
|
|
48
|
+
DYNAMIC_DIR="$PANTHEON_DIR/prompts/dynamic"
|
|
49
|
+
mkdir -p "$DYNAMIC_DIR"
|
|
50
|
+
|
|
51
|
+
# ── Helper: extract description from agent frontmatter ───────────────────────
|
|
52
|
+
extract_description() {
|
|
53
|
+
local file="$1"
|
|
54
|
+
# Grab the first 'description:' line inside the first YAML frontmatter block
|
|
55
|
+
sed -n '/^---$/,/^---$/p' "$file" 2>/dev/null \
|
|
56
|
+
| grep -m1 '^description:' \
|
|
57
|
+
| sed 's/^description:[[:space:]]*//; s/^"//; s/"$//; s/^'"'"'//; s/'"'"'$//' \
|
|
58
|
+
|| echo "No description available"
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# ── Generate Agora Prompt ─────────────────────────────────────────────────────
|
|
62
|
+
AGORA_FILE="$DYNAMIC_DIR/agora-generated.txt"
|
|
63
|
+
cat > "$AGORA_FILE" << EOF
|
|
64
|
+
# =============================================================================
|
|
65
|
+
# Dynamic Agora Prompt
|
|
66
|
+
# Generated: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
67
|
+
# Plan: $PLAN
|
|
68
|
+
# Service: $SERVICE
|
|
69
|
+
# Tier: $TIER
|
|
70
|
+
# Price: $PRICE
|
|
71
|
+
# Monthly Requests: $MONTHLY_REQUESTS
|
|
72
|
+
# =============================================================================
|
|
73
|
+
|
|
74
|
+
You are convening an agora with access to these specialists: $AGENTS_LIST
|
|
75
|
+
|
|
76
|
+
## Platform Capabilities
|
|
77
|
+
EOF
|
|
78
|
+
|
|
79
|
+
if [[ -n "$TIER_MAP" ]]; then
|
|
80
|
+
echo "Model Tiers:" >> "$AGORA_FILE"
|
|
81
|
+
echo "$TIER_MAP" >> "$AGORA_FILE"
|
|
82
|
+
echo "" >> "$AGORA_FILE"
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
if [[ -n "$AGENT_OVERRIDES" ]]; then
|
|
86
|
+
echo "Agent Overrides:" >> "$AGORA_FILE"
|
|
87
|
+
echo "$AGENT_OVERRIDES" >> "$AGORA_FILE"
|
|
88
|
+
echo "" >> "$AGORA_FILE"
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
cat >> "$AGORA_FILE" << EOF
|
|
92
|
+
## Instructions
|
|
93
|
+
1. Select 3-5 relevant agents based on the question domain
|
|
94
|
+
2. Dispatch them in parallel with the same question
|
|
95
|
+
3. Synthesize their perspectives into a single recommendation
|
|
96
|
+
4. Include confidence level and next steps
|
|
97
|
+
|
|
98
|
+
## Available Specialists
|
|
99
|
+
EOF
|
|
100
|
+
|
|
101
|
+
for agent_file in "$AGENTS_DIR"/*.md; do
|
|
102
|
+
[[ -f "$agent_file" ]] || continue
|
|
103
|
+
agent_name=$(basename "$agent_file" .md)
|
|
104
|
+
description=$(extract_description "$agent_file")
|
|
105
|
+
printf -- "- @%s: %s\n" "$agent_name" "$description" >> "$AGORA_FILE"
|
|
106
|
+
done
|
|
107
|
+
|
|
108
|
+
# ── Generate Orchestration Prompt ────────────────────────────────────────────
|
|
109
|
+
ORCH_FILE="$DYNAMIC_DIR/orchestrate-generated.txt"
|
|
110
|
+
cat > "$ORCH_FILE" << EOF
|
|
111
|
+
# =============================================================================
|
|
112
|
+
# Dynamic Orchestration Prompt
|
|
113
|
+
# Generated: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
114
|
+
# Plan: $PLAN
|
|
115
|
+
# Service: $SERVICE
|
|
116
|
+
# Tier: $TIER
|
|
117
|
+
# Price: $PRICE
|
|
118
|
+
# Monthly Requests: $MONTHLY_REQUESTS
|
|
119
|
+
# =============================================================================
|
|
120
|
+
|
|
121
|
+
You are Zeus, the central orchestrator for the Pantheon multi-agent system.
|
|
122
|
+
|
|
123
|
+
## Active Configuration
|
|
124
|
+
- Plan: $PLAN
|
|
125
|
+
- Service: $SERVICE
|
|
126
|
+
- Tier: $TIER
|
|
127
|
+
- Price: $PRICE
|
|
128
|
+
- Monthly Request Limit: $MONTHLY_REQUESTS
|
|
129
|
+
|
|
130
|
+
## Model Tiers
|
|
131
|
+
EOF
|
|
132
|
+
|
|
133
|
+
if [[ -n "$TIER_MAP" ]]; then
|
|
134
|
+
echo "$TIER_MAP" >> "$ORCH_FILE"
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
cat >> "$ORCH_FILE" << EOF
|
|
138
|
+
|
|
139
|
+
## Available Agents
|
|
140
|
+
EOF
|
|
141
|
+
|
|
142
|
+
for agent_file in "$AGENTS_DIR"/*.md; do
|
|
143
|
+
[[ -f "$agent_file" ]] || continue
|
|
144
|
+
agent_name=$(basename "$agent_file" .md)
|
|
145
|
+
description=$(extract_description "$agent_file")
|
|
146
|
+
printf -- "- @%s: %s\n" "$agent_name" "$description" >> "$ORCH_FILE"
|
|
147
|
+
done
|
|
148
|
+
|
|
149
|
+
cat >> "$ORCH_FILE" << EOF
|
|
150
|
+
|
|
151
|
+
## Orchestration Rules
|
|
152
|
+
1. **Planning**: Delegate to @athena for architecture decisions
|
|
153
|
+
2. **Discovery**: Delegate to @apollo for codebase investigation
|
|
154
|
+
3. **Implementation**: Dispatch @hermes (backend), @aphrodite (frontend), @demeter (database) in parallel when scopes are independent
|
|
155
|
+
4. **Review**: Always route completed work to @themis before merge
|
|
156
|
+
5. **Deploy**: Route infrastructure changes to @prometheus
|
|
157
|
+
6. **Document**: Route memory tasks to @mnemosyne
|
|
158
|
+
7. **Hotfix**: Route small fixes to @talos (bypass orchestration)
|
|
159
|
+
|
|
160
|
+
## Cost-Conscious Routing
|
|
161
|
+
- Use \`fast\` tier agents for discovery, documentation, and simple queries
|
|
162
|
+
- Use \`default\` tier agents for standard implementation work
|
|
163
|
+
- Use \`premium\` tier agents for planning, review, and complex orchestration
|
|
164
|
+
- Respect monthly request limits: $MONTHLY_REQUESTS
|
|
165
|
+
EOF
|
|
166
|
+
|
|
167
|
+
# ── Generate Agent Handoff Prompt ────────────────────────────────────────────
|
|
168
|
+
HANDOFF_FILE="$DYNAMIC_DIR/handoff-generated.txt"
|
|
169
|
+
cat > "$HANDOFF_FILE" << EOF
|
|
170
|
+
# =============================================================================
|
|
171
|
+
# Dynamic Agent Handoff Prompt
|
|
172
|
+
# Generated: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
173
|
+
# Plan: $PLAN
|
|
174
|
+
# Service: $SERVICE
|
|
175
|
+
# Tier: $TIER
|
|
176
|
+
# =============================================================================
|
|
177
|
+
|
|
178
|
+
## Handoff Tier Assignments
|
|
179
|
+
EOF
|
|
180
|
+
|
|
181
|
+
if [[ -n "$TIER_MAP" ]]; then
|
|
182
|
+
echo "$TIER_MAP" >> "$HANDOFF_FILE"
|
|
183
|
+
echo "" >> "$HANDOFF_FILE"
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
cat >> "$HANDOFF_FILE" << EOF
|
|
187
|
+
## Agent Directory
|
|
188
|
+
EOF
|
|
189
|
+
|
|
190
|
+
for agent_file in "$AGENTS_DIR"/*.md; do
|
|
191
|
+
[[ -f "$agent_file" ]] || continue
|
|
192
|
+
agent_name=$(basename "$agent_file" .md)
|
|
193
|
+
description=$(extract_description "$agent_file")
|
|
194
|
+
printf -- "- @%s: %s\n" "$agent_name" "$description" >> "$HANDOFF_FILE"
|
|
195
|
+
done
|
|
196
|
+
|
|
197
|
+
cat >> "$HANDOFF_FILE" << EOF
|
|
198
|
+
|
|
199
|
+
## Handoff Rules
|
|
200
|
+
| Direction | Tier | Reason |
|
|
201
|
+
|---|---|---|
|
|
202
|
+
| Any → Themis | premium | Critical quality + security gate |
|
|
203
|
+
| Any → Zeus | premium | Complex orchestration decisions |
|
|
204
|
+
| Athena → Zeus | premium | Plan handoff needs careful execution |
|
|
205
|
+
| Any → Mnemosyne | fast | Simple documentation writes |
|
|
206
|
+
| Hephaestus → Prometheus | default | Infrastructure config generation |
|
|
207
|
+
| Hephaestus | fast | Conversational AI dispatch |
|
|
208
|
+
|
|
209
|
+
## Cost Cap Rule
|
|
210
|
+
The requested model tier cannot exceed the cost tier of the main conversation model.
|
|
211
|
+
EOF
|
|
212
|
+
|
|
213
|
+
# ── Summary ────────────────────────────────────────────────────────────────────
|
|
214
|
+
echo "✅ Generated dynamic prompts for plan '$PLAN'"
|
|
215
|
+
echo " Service: $SERVICE"
|
|
216
|
+
echo " Tier: $TIER"
|
|
217
|
+
echo " Agents: $AGENTS_LIST"
|
|
218
|
+
echo ""
|
|
219
|
+
echo " Output files:"
|
|
220
|
+
echo " - $AGORA_FILE"
|
|
221
|
+
echo " - $ORCH_FILE"
|
|
222
|
+
echo " - $HANDOFF_FILE"
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* generate-routing-docs.mjs — Generate routing docs from routing.yml
|
|
4
|
+
*
|
|
5
|
+
* Reads the canonical routing.yml and outputs markdown documentation
|
|
6
|
+
* to stdout. Pipe to a file to save: node generate-routing-docs.mjs > ROUTING.md
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
10
|
+
import { dirname, join } from 'node:path'
|
|
11
|
+
import { fileURLToPath } from 'node:url'
|
|
12
|
+
import yaml from 'js-yaml'
|
|
13
|
+
|
|
14
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
15
|
+
const ROOT = join(__dirname, '..')
|
|
16
|
+
|
|
17
|
+
const routingPath = join(ROOT, 'routing.yml')
|
|
18
|
+
if (!existsSync(routingPath)) {
|
|
19
|
+
console.error('❌ routing.yml not found')
|
|
20
|
+
process.exit(1)
|
|
21
|
+
}
|
|
22
|
+
const routing = yaml.load(readFileSync(routingPath, 'utf8'))
|
|
23
|
+
|
|
24
|
+
const agents = routing.agents || {}
|
|
25
|
+
const matrix = routing.routing_matrix || []
|
|
26
|
+
const handoffs = routing.handoffs || {}
|
|
27
|
+
|
|
28
|
+
let doc = ''
|
|
29
|
+
|
|
30
|
+
// Title
|
|
31
|
+
doc += `# Pantheon Routing Reference\n\n`
|
|
32
|
+
doc += `Auto-generated from \`routing.yml\` (v${routing.version || '?'})\n\n`
|
|
33
|
+
doc += `${Object.keys(agents).length} agents · ${matrix.length} routing rules · ${Object.keys(handoffs).length} agent handoff groups\n\n`
|
|
34
|
+
|
|
35
|
+
// Agent Registry
|
|
36
|
+
doc += `---\n\n## Agent Registry\n\n`
|
|
37
|
+
doc += `All ${Object.keys(agents).length} Pantheon agents with roles, model tiers, and capabilities.\n\n`
|
|
38
|
+
doc += `| Agent | Role | Model Tier | User-Invocable | Delegates To | Skills |\n`
|
|
39
|
+
doc += `|-------|------|-----------|----------------|-------------|--------|\n`
|
|
40
|
+
|
|
41
|
+
for (const [name, info] of Object.entries(agents)) {
|
|
42
|
+
const role = info.role || '—'
|
|
43
|
+
const tier = info.model_tier || '—'
|
|
44
|
+
const invocable = info.user_invocable ? '✅ Yes' : '❌ No'
|
|
45
|
+
const delegates = (info.subagent_can_delegate_to || []).join(', ') || '—'
|
|
46
|
+
const agentSkills = (info.skills || []).map((s) => `\`${s}\``).join(', ') || '—'
|
|
47
|
+
doc += `| **@${name}** | ${role} | ${tier} | ${invocable} | ${delegates} | ${agentSkills} |\n`
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Routing Matrix
|
|
51
|
+
doc += `\n---\n\n## Routing Matrix\n\n`
|
|
52
|
+
doc += `Task category → primary agent mapping. Use this to decide which agent to invoke.\n\n`
|
|
53
|
+
doc += `| Category | Primary Agent | Model Tier | Can Run Parallel With |\n`
|
|
54
|
+
doc += `|----------|--------------|-----------|----------------------|\n`
|
|
55
|
+
|
|
56
|
+
for (const entry of matrix) {
|
|
57
|
+
const primary = `@${entry.primary_agent}`
|
|
58
|
+
const tier = entry.model_tier || '—'
|
|
59
|
+
const parallel = entry.parallel_with ? entry.parallel_with.map((a) => `@${a}`).join(', ') : '—'
|
|
60
|
+
doc += `| ${entry.category} | ${primary} | ${tier} | ${parallel} |\n`
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Handoff Reference
|
|
64
|
+
doc += `\n---\n\n## Handoff Reference\n\n`
|
|
65
|
+
doc += `Structured delegation contracts between agents.\n\n`
|
|
66
|
+
|
|
67
|
+
for (const [agentName, agentHandoffs] of Object.entries(handoffs)) {
|
|
68
|
+
doc += `### @${agentName}\n\n`
|
|
69
|
+
doc += `| Handoff | Target Agent | Model Tier | Description |\n`
|
|
70
|
+
doc += `|---------|-------------|-----------|-------------|\n`
|
|
71
|
+
|
|
72
|
+
for (const [key, handoff] of Object.entries(agentHandoffs)) {
|
|
73
|
+
if (typeof handoff !== 'object') continue
|
|
74
|
+
const target = `@${handoff.agent}`
|
|
75
|
+
const tier = handoff.model_tier || '—'
|
|
76
|
+
const desc = handoff.description || handoff.prompt?.substring(0, 80) || '—'
|
|
77
|
+
doc += `| **${key}** | ${target} | ${tier} | ${desc} |\n`
|
|
78
|
+
}
|
|
79
|
+
doc += `\n`
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Deployment platforms
|
|
83
|
+
doc += `---\n\n## Deployment Platforms\n\n`
|
|
84
|
+
doc += `Supported platforms and their configuration details.\n\n`
|
|
85
|
+
doc += `| Platform | Directory | Format | Handoff Strategy |\n`
|
|
86
|
+
doc += `|----------|-----------|--------|-----------------|\n`
|
|
87
|
+
|
|
88
|
+
const PLATFORM_INFO = {
|
|
89
|
+
opencode: { dir: '.opencode/agents/', format: '.md with YAML', handoff: 'native (YAML)' },
|
|
90
|
+
claude: { dir: '.claude/agents/', format: '.md with YAML', handoff: 'embed (body)' },
|
|
91
|
+
cursor: { dir: '.cursor/rules/', format: '.mdc rule files', handoff: 'embed (body)' },
|
|
92
|
+
windsurf: { dir: '.windsurf/rules/', format: '.md rule files', handoff: 'embed (body)' },
|
|
93
|
+
cline: { dir: '.clinerules/', format: 'plain text', handoff: 'embed (body)' },
|
|
94
|
+
copilot: { dir: '.github/agents/', format: '.agent.md with YAML', handoff: 'native (YAML)' },
|
|
95
|
+
continue: { dir: '.continue/rules/', format: '.md rules', handoff: 'embed (body)' },
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for (const [platform, info] of Object.entries(PLATFORM_INFO)) {
|
|
99
|
+
doc += `| **${platform}** | ${info.dir} | ${info.format} | ${info.handoff} |\n`
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
doc += `\n---\n\n*Generated on ${new Date().toISOString().split('T')[0]} from \`routing.yml\`*\n`
|
|
103
|
+
|
|
104
|
+
console.log(doc)
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Hash-Anchored Edit Verification — standalone script.
|
|
3
|
+
|
|
4
|
+
Verifies that file edits actually changed content by comparing SHA-256
|
|
5
|
+
hashes before and after an edit. Detects failed/no-op edits.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python3 scripts/hash_verify.py <file> [--before-hash=<sha256>] [--diff-min-lines=10]
|
|
9
|
+
python3 scripts/hash_verify.py --staged # Check all staged files
|
|
10
|
+
python3 scripts/hash_verify.py --batch <file>... # Check multiple files
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import hashlib
|
|
15
|
+
import subprocess
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
EXIT_SUCCESS = 0
|
|
20
|
+
EXIT_FAILURE = 1
|
|
21
|
+
EXIT_WARNING = 2
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def sha256_of(file: Path) -> str:
|
|
25
|
+
"""Return hex SHA-256 of file content."""
|
|
26
|
+
return hashlib.sha256(file.read_bytes()).hexdigest()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def compute_diff_size(file: Path) -> int:
|
|
30
|
+
"""Return number of changed lines in working-tree diff."""
|
|
31
|
+
try:
|
|
32
|
+
result = subprocess.run(
|
|
33
|
+
["git", "diff", "--", str(file)],
|
|
34
|
+
capture_output=True,
|
|
35
|
+
text=True,
|
|
36
|
+
timeout=5,
|
|
37
|
+
check=False,
|
|
38
|
+
)
|
|
39
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
40
|
+
return 0
|
|
41
|
+
|
|
42
|
+
diff = result.stdout
|
|
43
|
+
added = len(
|
|
44
|
+
[
|
|
45
|
+
line
|
|
46
|
+
for line in diff.split("\n")
|
|
47
|
+
if line.startswith("+") and not line.startswith("+++")
|
|
48
|
+
]
|
|
49
|
+
)
|
|
50
|
+
removed = len(
|
|
51
|
+
[
|
|
52
|
+
line
|
|
53
|
+
for line in diff.split("\n")
|
|
54
|
+
if line.startswith("-") and not line.startswith("---")
|
|
55
|
+
]
|
|
56
|
+
)
|
|
57
|
+
return added + removed
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def verify_file(file: Path, before_hash: str | None, diff_min_lines: int) -> dict:
|
|
61
|
+
"""Verify a single file edit."""
|
|
62
|
+
if not file.exists():
|
|
63
|
+
return {"file": str(file), "status": "error", "message": "File does not exist"}
|
|
64
|
+
|
|
65
|
+
after_hash = sha256_of(file)
|
|
66
|
+
|
|
67
|
+
if before_hash:
|
|
68
|
+
# Compare explicit before/after hashes
|
|
69
|
+
if after_hash == before_hash:
|
|
70
|
+
return {
|
|
71
|
+
"file": str(file),
|
|
72
|
+
"status": "failed",
|
|
73
|
+
"hash_before": before_hash,
|
|
74
|
+
"hash_after": after_hash,
|
|
75
|
+
"message": "Hash unchanged — edit did not modify file content",
|
|
76
|
+
}
|
|
77
|
+
else:
|
|
78
|
+
# Auto-detect: check git diff
|
|
79
|
+
diff_changes = compute_diff_size(file)
|
|
80
|
+
if diff_changes == 0:
|
|
81
|
+
return {
|
|
82
|
+
"file": str(file),
|
|
83
|
+
"status": "failed",
|
|
84
|
+
"hash_after": after_hash,
|
|
85
|
+
"message": "No git diff detected — edit did not modify file",
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if diff_changes < diff_min_lines:
|
|
89
|
+
return {
|
|
90
|
+
"file": str(file),
|
|
91
|
+
"status": "warning",
|
|
92
|
+
"hash_after": after_hash,
|
|
93
|
+
"diff_lines": diff_changes,
|
|
94
|
+
"message": f"Edit detected but diff is small ({diff_changes} lines vs min {diff_min_lines})",
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
"file": str(file),
|
|
99
|
+
"status": "verified",
|
|
100
|
+
"hash_after": after_hash,
|
|
101
|
+
"message": "Edit verified — file content changed",
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def check_staged_files(diff_min_lines: int) -> list[dict]:
|
|
106
|
+
"""Verify all staged (git-add'ed) files."""
|
|
107
|
+
try:
|
|
108
|
+
result = subprocess.run(
|
|
109
|
+
["git", "diff", "--cached", "--name-only"],
|
|
110
|
+
capture_output=True,
|
|
111
|
+
text=True,
|
|
112
|
+
timeout=5,
|
|
113
|
+
check=False,
|
|
114
|
+
)
|
|
115
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
116
|
+
return [{"status": "error", "message": "Git not available"}]
|
|
117
|
+
|
|
118
|
+
staged = [f.strip() for f in result.stdout.split("\n") if f.strip()]
|
|
119
|
+
results = []
|
|
120
|
+
for fname in staged[:20]: # limit to 20 files
|
|
121
|
+
fpath = Path(fname)
|
|
122
|
+
if fpath.exists():
|
|
123
|
+
results.append(verify_file(fpath, None, diff_min_lines))
|
|
124
|
+
return results
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def main() -> None:
|
|
128
|
+
parser = argparse.ArgumentParser(
|
|
129
|
+
description="Hash-anchored edit verification — ensures file edits actually changed content."
|
|
130
|
+
)
|
|
131
|
+
parser.add_argument("files", nargs="*", help="File(s) to verify")
|
|
132
|
+
parser.add_argument(
|
|
133
|
+
"--before-hash", help="Expected SHA-256 hash before edit (for single-file mode)"
|
|
134
|
+
)
|
|
135
|
+
parser.add_argument(
|
|
136
|
+
"--diff-min-lines",
|
|
137
|
+
type=int,
|
|
138
|
+
default=3,
|
|
139
|
+
help="Minimum diff lines to consider edit meaningful (default: 3)",
|
|
140
|
+
)
|
|
141
|
+
parser.add_argument(
|
|
142
|
+
"--staged", action="store_true", help="Verify all staged files instead"
|
|
143
|
+
)
|
|
144
|
+
parser.add_argument(
|
|
145
|
+
"--batch", action="store_true", help="Treat positional args as batch file list"
|
|
146
|
+
)
|
|
147
|
+
args = parser.parse_args()
|
|
148
|
+
|
|
149
|
+
if args.staged:
|
|
150
|
+
results = check_staged_files(args.diff_min_lines)
|
|
151
|
+
elif args.batch or len(args.files) > 1:
|
|
152
|
+
results = [verify_file(Path(f), None, args.diff_min_lines) for f in args.files]
|
|
153
|
+
elif args.files:
|
|
154
|
+
results = [
|
|
155
|
+
verify_file(Path(args.files[0]), args.before_hash, args.diff_min_lines)
|
|
156
|
+
]
|
|
157
|
+
else:
|
|
158
|
+
print("Usage: python3 scripts/hash_verify.py <file> [--before-hash=<sha256>]")
|
|
159
|
+
print(" python3 scripts/hash_verify.py --staged")
|
|
160
|
+
print(" python3 scripts/hash_verify.py --batch <file>...")
|
|
161
|
+
sys.exit(EXIT_FAILURE)
|
|
162
|
+
|
|
163
|
+
# Print results
|
|
164
|
+
any_failed = False
|
|
165
|
+
any_warning = False
|
|
166
|
+
|
|
167
|
+
for r in results:
|
|
168
|
+
status_tag = {
|
|
169
|
+
"verified": "✅",
|
|
170
|
+
"warning": "⚠️",
|
|
171
|
+
"failed": "❌",
|
|
172
|
+
"error": "❌",
|
|
173
|
+
}.get(r["status"], "❓")
|
|
174
|
+
print(f"{status_tag} {r['file']}: {r['message']}")
|
|
175
|
+
if r["status"] == "failed" or r["status"] == "error":
|
|
176
|
+
any_failed = True
|
|
177
|
+
elif r["status"] == "warning":
|
|
178
|
+
any_warning = True
|
|
179
|
+
|
|
180
|
+
if any_failed:
|
|
181
|
+
print("\n❌ HASH VERIFY FAILED — some edits did not modify files")
|
|
182
|
+
sys.exit(EXIT_FAILURE)
|
|
183
|
+
elif any_warning:
|
|
184
|
+
print("\n⚠️ HASH VERIFY PASSED WITH WARNINGS — small diffs detected")
|
|
185
|
+
sys.exit(EXIT_WARNING)
|
|
186
|
+
else:
|
|
187
|
+
print("\n✅ HASH VERIFY: all edits verified")
|
|
188
|
+
sys.exit(EXIT_SUCCESS)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
if __name__ == "__main__":
|
|
192
|
+
main()
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Pantheon MCP Init — configura os 3 MCP servers em qualquer plataforma
|
|
3
|
+
# Uso: ./scripts/init-pantheon-mcp.sh /caminho/do/projeto [plataforma]
|
|
4
|
+
# plataforma: opencode (padrão), claude, cursor, windsurf, cline, continue, copilot
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
SOURCE_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
9
|
+
TARGET_DIR="${1:-.}"
|
|
10
|
+
PLATFORM="${2:-opencode}"
|
|
11
|
+
|
|
12
|
+
echo "🚀 Pantheon MCP Init — $PLATFORM"
|
|
13
|
+
echo " Origem: $SOURCE_DIR"
|
|
14
|
+
echo " Destino: $TARGET_DIR"
|
|
15
|
+
echo ""
|
|
16
|
+
|
|
17
|
+
# 1. Copiar scripts
|
|
18
|
+
echo "📁 Copiando scripts MCP..."
|
|
19
|
+
mkdir -p "$TARGET_DIR/scripts" "$TARGET_DIR/.pantheon/code-mode"
|
|
20
|
+
cp "$SOURCE_DIR/scripts/mcp_resources_server.py" "$TARGET_DIR/scripts/"
|
|
21
|
+
cp "$SOURCE_DIR/scripts/code_mode_server.py" "$TARGET_DIR/scripts/"
|
|
22
|
+
cp "$SOURCE_DIR/scripts/memory_mcp_server.py" "$TARGET_DIR/scripts/"
|
|
23
|
+
cp "$SOURCE_DIR/.pantheon/code-mode/example-sync.sh" "$TARGET_DIR/.pantheon/code-mode/"
|
|
24
|
+
chmod +x "$TARGET_DIR/.pantheon/code-mode/example-sync.sh"
|
|
25
|
+
echo " ✅ Scripts copiados"
|
|
26
|
+
|
|
27
|
+
# 2. Virtualenv + dependências
|
|
28
|
+
echo "🐍 Configurando virtualenv..."
|
|
29
|
+
cd "$TARGET_DIR"
|
|
30
|
+
if [ ! -d ".venv" ]; then
|
|
31
|
+
python3 -m venv .venv
|
|
32
|
+
fi
|
|
33
|
+
source .venv/bin/activate
|
|
34
|
+
pip install -q chromadb>=0.6.0 sentence-transformers fastmcp>=3.4.0 pyyaml 2>/dev/null || \
|
|
35
|
+
pip install chromadb sentence-transformers fastmcp pyyaml 2>/dev/null
|
|
36
|
+
echo " ✅ Dependências instaladas"
|
|
37
|
+
|
|
38
|
+
# 3. Config da plataforma
|
|
39
|
+
echo "📝 Configurando $PLATFORM..."
|
|
40
|
+
|
|
41
|
+
case "$PLATFORM" in
|
|
42
|
+
opencode)
|
|
43
|
+
mkdir -p "$(dirname "$TARGET_DIR/opencode.json")"
|
|
44
|
+
if [ ! -f "$TARGET_DIR/opencode.json" ]; then
|
|
45
|
+
cp "$SOURCE_DIR/platform/opencode/opencode.json" "$TARGET_DIR/"
|
|
46
|
+
echo " ✅ opencode.json criado"
|
|
47
|
+
else
|
|
48
|
+
echo " ⚠️ opencode.json já existe — adicione os MCPs manualmente"
|
|
49
|
+
fi
|
|
50
|
+
;;
|
|
51
|
+
claude)
|
|
52
|
+
if [ ! -f "$TARGET_DIR/.mcp.json" ]; then
|
|
53
|
+
cp "$SOURCE_DIR/platform/claude/mcp-template.json" "$TARGET_DIR/.mcp.json"
|
|
54
|
+
echo " ✅ .mcp.json criado"
|
|
55
|
+
else
|
|
56
|
+
echo " ⚠️ .mcp.json já existe — faça merge manual dos mcpServers"
|
|
57
|
+
fi
|
|
58
|
+
;;
|
|
59
|
+
cursor)
|
|
60
|
+
mkdir -p "$TARGET_DIR/.cursor"
|
|
61
|
+
if [ ! -f "$TARGET_DIR/.cursor/mcp.json" ]; then
|
|
62
|
+
cp "$SOURCE_DIR/platform/cursor/mcp-template.json" "$TARGET_DIR/.cursor/mcp.json"
|
|
63
|
+
echo " ✅ .cursor/mcp.json criado"
|
|
64
|
+
else
|
|
65
|
+
echo " ⚠️ .cursor/mcp.json já existe — faça merge manual"
|
|
66
|
+
fi
|
|
67
|
+
;;
|
|
68
|
+
windsurf)
|
|
69
|
+
mkdir -p "$HOME/.codeium/windsurf"
|
|
70
|
+
if [ ! -f "$HOME/.codeium/windsurf/mcp_config.json" ]; then
|
|
71
|
+
cp "$SOURCE_DIR/platform/windsurf/mcp-template.json" "$HOME/.codeium/windsurf/mcp_config.json"
|
|
72
|
+
echo " ✅ mcp_config.json criado (global)"
|
|
73
|
+
else
|
|
74
|
+
echo " ⚠️ mcp_config.json já existe — faça merge manual"
|
|
75
|
+
fi
|
|
76
|
+
;;
|
|
77
|
+
cline)
|
|
78
|
+
echo " ⚠️ Cline usa settings do VS Code. Copie manualmente:"
|
|
79
|
+
echo " $SOURCE_DIR/platform/cline/mcp-template.json"
|
|
80
|
+
echo " Para: ~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json"
|
|
81
|
+
;;
|
|
82
|
+
continue)
|
|
83
|
+
echo " ⚠️ Continue.dev usa ~/.continue/config.json. Copie manualmente:"
|
|
84
|
+
echo " $SOURCE_DIR/platform/continue/mcp-template.json"
|
|
85
|
+
echo " (mcpServers é ARRAY — faça merge no seu config.json)"
|
|
86
|
+
;;
|
|
87
|
+
copilot)
|
|
88
|
+
mkdir -p "$TARGET_DIR/.vscode"
|
|
89
|
+
if [ ! -f "$TARGET_DIR/.vscode/mcp.json" ]; then
|
|
90
|
+
cp "$SOURCE_DIR/platform/copilot/mcp-template.json" "$TARGET_DIR/.vscode/mcp.json"
|
|
91
|
+
echo " ✅ .vscode/mcp.json criado"
|
|
92
|
+
else
|
|
93
|
+
echo " ⚠️ .vscode/mcp.json já existe — faça merge manual"
|
|
94
|
+
fi
|
|
95
|
+
;;
|
|
96
|
+
*)
|
|
97
|
+
echo " ❌ Plataforma desconhecida: $PLATFORM"
|
|
98
|
+
echo " Use: opencode, claude, cursor, windsurf, cline, continue, copilot"
|
|
99
|
+
exit 1
|
|
100
|
+
;;
|
|
101
|
+
esac
|
|
102
|
+
|
|
103
|
+
# 4. Verificação
|
|
104
|
+
echo ""
|
|
105
|
+
echo "🔍 Verificando instalação..."
|
|
106
|
+
python3 -c "from scripts.mcp_resources_server import mcp; print(' ✅ resources OK')" 2>/dev/null || echo " ⚠️ Verifique o Python path"
|
|
107
|
+
python3 -c "from scripts.code_mode_server import mcp; print(' ✅ code-mode OK')" 2>/dev/null || echo " ⚠️ Verifique o Python path"
|
|
108
|
+
.venv/bin/python3 -c "from scripts.memory_mcp_server import mcp; print(' ✅ memory OK')" 2>/dev/null || echo " ⚠️ Verifique o .venv"
|
|
109
|
+
|
|
110
|
+
echo ""
|
|
111
|
+
echo "🎉 Pantheon MCP Init concluído para $PLATFORM!"
|
|
112
|
+
echo ""
|
|
113
|
+
echo "Comandos uteis:"
|
|
114
|
+
echo " Testar: cd $TARGET_DIR && .venv/bin/python3 -m pytest tests/ -v"
|
|
115
|
+
echo " Usar: abra o projeto no $PLATFORM e os MCPs estarão disponíveis"
|
|
116
|
+
echo ""
|
|
117
|
+
echo "Nota: o modelo all-MiniLM-L6-v2 (~80MB) baixa automaticamente"
|
|
118
|
+
echo "no primeiro uso do pantheon-memory."
|