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.
Files changed (128) hide show
  1. package/AGENTS.md +37 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1013 -0
  4. package/bin/pantheon-init.mjs +183 -0
  5. package/commands/pantheon-audit.md +25 -0
  6. package/commands/pantheon-bg.md +10 -0
  7. package/commands/pantheon-consolidate.md +11 -0
  8. package/commands/pantheon-deepwork.md +128 -0
  9. package/commands/pantheon-doc.md +10 -0
  10. package/commands/pantheon-focus.md +9 -0
  11. package/commands/pantheon-forget.md +58 -0
  12. package/commands/pantheon-hash.md +11 -0
  13. package/commands/pantheon-optimize.md +79 -0
  14. package/commands/pantheon-remember.md +44 -0
  15. package/commands/pantheon-search.md +48 -0
  16. package/commands/pantheon-status.md +71 -0
  17. package/commands/pantheon-todo.md +11 -0
  18. package/commands/pantheon.md +49 -0
  19. package/docs/AGENT-MCP.md +194 -0
  20. package/docs/ARCHITECTURE.md +384 -0
  21. package/docs/BRANCH-PROTECTION.md +142 -0
  22. package/docs/INDEX.md +81 -0
  23. package/docs/INSTALLATION.md +217 -0
  24. package/docs/MCP.md +238 -0
  25. package/docs/MEMORY.md +471 -0
  26. package/docs/MIGRATION-MEMORY-BANK.md +139 -0
  27. package/docs/PLATFORMS.md +5 -0
  28. package/docs/QUICKSTART.md +49 -0
  29. package/docs/README.md +18 -0
  30. package/docs/RELEASING.md +256 -0
  31. package/docs/SETUP.md +5 -0
  32. package/docs/UPGRADING.md +41 -0
  33. package/docs/mcp-recommendations.md +439 -0
  34. package/docs/mcp-tools.md +156 -0
  35. package/docs/mcp-user-guide.md +204 -0
  36. package/docs/persistence-mcp.md +111 -0
  37. package/package.json +72 -0
  38. package/pantheon.schema.json +158 -0
  39. package/scripts/__init__.py +0 -0
  40. package/scripts/_pantheon_paths.py +68 -0
  41. package/scripts/check-version-consistency.sh +23 -0
  42. package/scripts/code_mode_server.py +202 -0
  43. package/scripts/doctor.mjs +763 -0
  44. package/scripts/generate-prompts.sh +222 -0
  45. package/scripts/generate-routing-docs.mjs +104 -0
  46. package/scripts/hash_verify.py +192 -0
  47. package/scripts/init-pantheon-mcp.sh +118 -0
  48. package/scripts/install/agents-md.mjs +214 -0
  49. package/scripts/install/health-check.mjs +196 -0
  50. package/scripts/install/migrate.mjs +209 -0
  51. package/scripts/install/opencode.mjs +645 -0
  52. package/scripts/install/shared.mjs +655 -0
  53. package/scripts/install/venv.mjs +116 -0
  54. package/scripts/install-mcp.mjs +885 -0
  55. package/scripts/install.mjs +26 -0
  56. package/scripts/manifest.mjs +622 -0
  57. package/scripts/mcp_persistence_server.py +459 -0
  58. package/scripts/mcp_resources_server.py +206 -0
  59. package/scripts/memory_cache.py +78 -0
  60. package/scripts/memory_mcp_server.py +605 -0
  61. package/scripts/paths.py +64 -0
  62. package/scripts/prune_context.py +72 -0
  63. package/scripts/release-bundle.mjs +109 -0
  64. package/scripts/scrub-secrets.py +282 -0
  65. package/scripts/scrub_secrets.py +281 -0
  66. package/scripts/test-context-compression.sh +166 -0
  67. package/scripts/themis_heuristic_scan.py +287 -0
  68. package/scripts/todo_enforcer.py +242 -0
  69. package/scripts/uninstall.mjs +1057 -0
  70. package/scripts/validate-routing.mjs +160 -0
  71. package/scripts/validate_agent_frontmatter.py +226 -0
  72. package/scripts/versioning.mjs +254 -0
  73. package/skills-lock.json +16 -0
  74. package/src/agents/aphrodite.md +162 -0
  75. package/src/agents/apollo.md +109 -0
  76. package/src/agents/athena.md +226 -0
  77. package/src/agents/demeter.md +146 -0
  78. package/src/agents/gaia.md +82 -0
  79. package/src/agents/hephaestus.md +105 -0
  80. package/src/agents/hermes.md +302 -0
  81. package/src/agents/iris.md +99 -0
  82. package/src/agents/mnemosyne.md +226 -0
  83. package/src/agents/nyx.md +87 -0
  84. package/src/agents/prometheus.md +199 -0
  85. package/src/agents/talos.md +93 -0
  86. package/src/agents/themis.md +187 -0
  87. package/src/agents/zeus.md +209 -0
  88. package/src/instructions/agent-return-format.instructions.md +26 -0
  89. package/src/instructions/backend-standards.instructions.md +45 -0
  90. package/src/instructions/documentation-standards.instructions.md +53 -0
  91. package/src/instructions/frontend-standards.instructions.md +46 -0
  92. package/src/instructions/memory-protocol.instructions.md +67 -0
  93. package/src/instructions/yagni.instructions.md +21 -0
  94. package/src/instructions/zeus-anti-stall.instructions.md +72 -0
  95. package/src/instructions/zeus-communication-rules.instructions.md +15 -0
  96. package/src/instructions/zeus-council-synthesis.instructions.md +105 -0
  97. package/src/instructions/zeus-timeout-retry.instructions.md +127 -0
  98. package/src/mcp/_pantheon_paths.py +67 -0
  99. package/src/mcp/code_mode_server.py +202 -0
  100. package/src/mcp/init-pantheon-mcp.sh +118 -0
  101. package/src/mcp/install-mcp.mjs +885 -0
  102. package/src/mcp/mcp_persistence_server.py +458 -0
  103. package/src/mcp/mcp_resources_server.py +205 -0
  104. package/src/mcp/memory_mcp_server.py +604 -0
  105. package/src/mcp/requirements-mcp-core.txt +5 -0
  106. package/src/mcp/requirements-mcp.txt +5 -0
  107. package/src/plugin.ts +19 -0
  108. package/src/plugins/tui/dist/tui.js +143 -0
  109. package/src/plugins/tui/dist/tui.tsx +144 -0
  110. package/src/plugins/tui/package.json +32 -0
  111. package/src/plugins/tui/src/index.tsx +144 -0
  112. package/src/plugins/tui/tsdown.config.ts +22 -0
  113. package/src/routing.yml +499 -0
  114. package/src/skills/README.md +230 -0
  115. package/src/skills/agent-coordination/SKILL.md +95 -0
  116. package/src/skills/artifact-management/SKILL.md +118 -0
  117. package/src/skills/auto-continue/SKILL.md +280 -0
  118. package/src/skills/code-review-checklist/SKILL.md +139 -0
  119. package/src/skills/context-compression/SKILL.md +861 -0
  120. package/src/skills/git-workflow-and-versioning/SKILL.md +32 -0
  121. package/src/skills/incremental-implementation/SKILL.md +27 -0
  122. package/src/skills/memory-bank/SKILL.md +165 -0
  123. package/src/skills/orchestration-workflow/SKILL.md +311 -0
  124. package/src/skills/security-hardening/SKILL.md +36 -0
  125. package/src/skills/session-goal/SKILL.md +138 -0
  126. package/src/skills/spec-driven-development/SKILL.md +23 -0
  127. package/src/skills/tdd-with-agents/SKILL.md +170 -0
  128. 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."