pantheon-opencode 1.3.5 → 1.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +33 -1
  2. package/package.json +3 -3
  3. package/scripts/_pantheon_paths.py +0 -1
  4. package/scripts/build-agents-md.mjs +4 -2
  5. package/scripts/code_mode_server.py +307 -21
  6. package/scripts/doctor.mjs +26 -13
  7. package/scripts/hooks/format-multi-language.sh +6 -3
  8. package/scripts/hooks/log-session-start.sh +6 -0
  9. package/scripts/hooks/on-subagent-delegation-start.sh +22 -6
  10. package/scripts/hooks/validate-post-conditions.sh +1 -1
  11. package/scripts/hooks/validate-tool-safety.sh +5 -2
  12. package/scripts/install/agents-md.mjs +3 -1
  13. package/scripts/install/cli-ui.mjs +24 -4
  14. package/scripts/install/interactive.mjs +18 -7
  15. package/scripts/install/migrate-config.mjs +13 -19
  16. package/scripts/install/migrate.mjs +8 -5
  17. package/scripts/install/model-picker.mjs +4 -1
  18. package/scripts/install/opencode.mjs +2 -0
  19. package/scripts/install/shared.mjs +6 -4
  20. package/scripts/install/state.mjs +9 -6
  21. package/scripts/mcp_persistence_server.py +23 -22
  22. package/scripts/mcp_resources_server.py +60 -1
  23. package/scripts/memory_mcp_server.py +80 -4
  24. package/scripts/postinstall.mjs +27 -25
  25. package/scripts/prune.mjs +1 -1
  26. package/scripts/redaction-gate.mjs +3 -1
  27. package/scripts/sync-tui.mjs +148 -0
  28. package/scripts/uninstall.mjs +0 -2
  29. package/scripts/validation-policy.mjs +6 -1
  30. package/scripts/version-check.mjs +9 -4
  31. package/src/agents/aphrodite.md +1 -1
  32. package/src/agents/demeter.md +1 -1
  33. package/src/agents/hephaestus.md +1 -1
  34. package/src/agents/hermes.md +1 -1
  35. package/src/agents/themis.md +4 -0
  36. package/src/mcp/code_mode_server.py +304 -18
  37. package/src/mcp/eval_store.py +233 -0
  38. package/src/mcp/mcp_persistence_server.py +23 -22
  39. package/src/mcp/mcp_resources_server.py +58 -0
  40. package/src/mcp/memory_mcp_server.py +80 -3
  41. package/src/pantheon/auto-wake.ts +2 -4
  42. package/src/pantheon/background-job-board.ts +17 -4
  43. package/src/pantheon/comment-checker.ts +48 -0
  44. package/src/pantheon/delegation-classifier.ts +307 -0
  45. package/src/pantheon/delegation-enforce.ts +21 -21
  46. package/src/pantheon/delegation-finalize.ts +44 -4
  47. package/src/pantheon/delegation-notify.ts +97 -0
  48. package/src/pantheon/delegation.ts +413 -23
  49. package/src/pantheon/dispatch-guard.ts +2 -0
  50. package/src/pantheon/file-persistence.ts +7 -3
  51. package/src/pantheon/permission-eval.ts +284 -0
  52. package/src/pantheon/permission-globs.ts +86 -0
  53. package/src/pantheon/presets.d.mts +41 -0
  54. package/src/pantheon/presets.mjs +134 -0
  55. package/src/pantheon/retry-policy.ts +212 -0
  56. package/src/pantheon/session-guard.ts +1 -3
  57. package/src/pantheon/step-cap.ts +110 -0
  58. package/src/pantheon/tui-version-check.ts +72 -0
  59. package/src/pantheon/vision.ts +7 -5
  60. package/src/pantheon/zeus-delegate-with-retry.ts +81 -12
  61. package/src/plugin.ts +57 -3
  62. package/src/plugins/hook-runner.ts +1 -1
  63. package/src/plugins/pantheon-hooks.ts +1 -1
  64. package/src/plugins/tui/dist/tui.d.ts +1 -1
  65. package/src/plugins/tui/dist/tui.d.ts.map +1 -1
  66. package/src/plugins/tui/dist/tui.js +19 -14
  67. package/src/plugins/tui/dist/tui.tsx +52 -18
  68. package/src/plugins/tui/package.json +1 -1
  69. package/src/plugins/tui/src/index.tsx +106 -24
  70. package/src/plugins/tui/tsdown.config.ts +1 -1
  71. package/src/routing.yml +42 -0
@@ -1,4 +1,3 @@
1
- # Auto-generated: resolved symlink from ../src/mcp/memory_mcp_server.py
2
1
  #!/usr/bin/env python3
3
2
  """Pantheon Memory MCP Server — lightweight, zero heavy deps.
4
3
 
@@ -26,6 +25,7 @@ import json
26
25
  import os
27
26
  import sqlite3
28
27
  import time
28
+ from datetime import datetime, timezone
29
29
  from pathlib import Path
30
30
  from typing import Any
31
31
 
@@ -187,10 +187,55 @@ def _parse_metadata(row: dict[str, Any]) -> dict[str, Any]:
187
187
  _RRF_CONST = 60
188
188
 
189
189
 
190
+ def _parse_iso_ts(value: str) -> float:
191
+ """Parse an ISO 8601 UTC timestamp to epoch seconds.
192
+
193
+ Handles both aware ISO strings (``2026-08-21T10:00:00Z`` /
194
+ ``...+00:00``) and legacy naive rows from ``DEFAULT (datetime('now'))``
195
+ (``YYYY-MM-DD HH:MM:SS``, space separator, no TZ). Naive timestamps are
196
+ assumed UTC — SQLite's ``datetime('now')`` is UTC, so interpreting them
197
+ in the local timezone would skew freshness decay.
198
+ """
199
+ dt = datetime.fromisoformat(value.replace("Z", "+00:00"))
200
+ if dt.tzinfo is None:
201
+ dt = dt.replace(tzinfo=timezone.utc)
202
+ return dt.timestamp()
203
+
204
+
205
+ def _fetch_created_at_map(
206
+ db: sqlite3.Connection, ids: list[int]
207
+ ) -> dict[int, str]:
208
+ """Fetch created_at timestamps for the given memory IDs.
209
+
210
+ Args:
211
+ db: Active SQLite connection.
212
+ ids: Memory row IDs to look up.
213
+
214
+ Returns:
215
+ Mapping of id -> created_at ISO string. Empty on failure.
216
+ """
217
+ created_at_map: dict[int, str] = {}
218
+ if not ids:
219
+ return created_at_map
220
+ try:
221
+ placeholders = ",".join("?" * len(ids))
222
+ rows = db.execute(
223
+ f"SELECT id, created_at FROM memories "
224
+ f"WHERE id IN ({placeholders})",
225
+ ids,
226
+ ).fetchall()
227
+ created_at_map = {r["id"]: r["created_at"] for r in rows}
228
+ except Exception:
229
+ created_at_map = {}
230
+ return created_at_map
231
+
232
+
190
233
  def _rrf_fuse(
191
234
  vec_ids: list[int],
192
235
  fts_ids: list[int],
193
236
  top_k: int,
237
+ created_at_map: dict[int, str] | None = None,
238
+ decay_days: float | None = None,
194
239
  ) -> list[tuple[int, float]]:
195
240
  """Reciprocal Rank Fusion of vector and FTS5 result ID lists.
196
241
 
@@ -198,6 +243,12 @@ def _rrf_fuse(
198
243
  vec_ids: Ordered list of IDs from vector search (most relevant first).
199
244
  fts_ids: Ordered list of IDs from FTS5 search (most relevant first).
200
245
  top_k: Maximum number of fused results to return.
246
+ created_at_map: Optional mapping of doc_id -> created_at ISO string,
247
+ used to compute freshness when ``decay_days`` is set.
248
+ decay_days: Optional freshness half-life in days. When set, each fused
249
+ score is multiplied by ``freshness = 2^(-days/decay_days)`` so
250
+ older entries rank lower. Default None = no decay (backward
251
+ compatible).
201
252
 
202
253
  Returns:
203
254
  List of (id, rrf_score) tuples sorted by descending score.
@@ -207,6 +258,19 @@ def _rrf_fuse(
207
258
  scores[doc_id] = 1.0 / (_RRF_CONST + rank)
208
259
  for rank, doc_id in enumerate(fts_ids, start=1):
209
260
  scores[doc_id] = scores.get(doc_id, 0.0) + 1.0 / (_RRF_CONST + rank)
261
+
262
+ if decay_days is not None and decay_days > 0 and created_at_map:
263
+ now = time.time()
264
+ for doc_id in list(scores):
265
+ created = created_at_map.get(doc_id)
266
+ if not created:
267
+ continue
268
+ try:
269
+ days = max(0.0, (now - _parse_iso_ts(created)) / 86400.0)
270
+ except (ValueError, TypeError):
271
+ continue
272
+ scores[doc_id] *= 2.0 ** (-days / decay_days)
273
+
210
274
  ranked = sorted(scores.items(), key=lambda x: (-x[1], x[0]))
211
275
  return ranked[:top_k]
212
276
 
@@ -283,12 +347,15 @@ def memory_store(
283
347
  @mcp.tool(
284
348
  description="Hybrid semantic search across memories. "
285
349
  "Combines vector cosine similarity and FTS5 BM25 keyword search "
286
- "via Reciprocal Rank Fusion (RRF).",
350
+ "via Reciprocal Rank Fusion (RRF). Optional decay_days applies a "
351
+ "freshness half-life (2^(-days/decay_days)) so recent entries rank "
352
+ "higher; default None keeps the original rank-only behavior.",
287
353
  )
288
354
  def memory_search( # noqa: PLR0912
289
355
  query: str,
290
356
  namespace: str | None = None,
291
357
  top_k: int = 5,
358
+ decay_days: float | None = None,
292
359
  ) -> list[dict[str, Any]]:
293
360
  """Hybrid search: vector + keyword fused via RRF.
294
361
 
@@ -300,6 +367,9 @@ def memory_search( # noqa: PLR0912
300
367
  query: Search query text.
301
368
  namespace: Optional namespace filter.
302
369
  top_k: Maximum results (default 5, max 50).
370
+ decay_days: Optional freshness half-life in days. When set, fused
371
+ scores are multiplied by ``2^(-days_since_created/decay_days)``
372
+ so older entries rank lower. Default None = no decay.
303
373
 
304
374
  Returns:
305
375
  List of memory entries with score and metadata.
@@ -365,8 +435,14 @@ def memory_search( # noqa: PLR0912
365
435
  except Exception:
366
436
  fts_ids = []
367
437
 
368
- # 4. RRF fusion
369
- fused = _rrf_fuse(vec_ids, fts_ids, top_k)
438
+ # 4. RRF fusion (optionally freshness-decayed)
439
+ candidate_ids = list(dict.fromkeys([*vec_ids, *fts_ids]))
440
+ created_at_map = (
441
+ _fetch_created_at_map(db, candidate_ids)
442
+ if decay_days is not None and decay_days > 0
443
+ else {}
444
+ )
445
+ fused = _rrf_fuse(vec_ids, fts_ids, top_k, created_at_map, decay_days)
370
446
 
371
447
  if not fused:
372
448
  return []
@@ -5,43 +5,45 @@
5
5
  * Runs after `npm install` completes. Validates environment and prints
6
6
  * setup instructions. Does NOT install Pantheon platform components.
7
7
  */
8
- import { existsSync } from 'node:fs';
9
- import { join } from 'node:path';
10
- import { fileURLToPath } from 'node:url';
8
+ import { existsSync } from 'node:fs'
9
+ import { join } from 'node:path'
10
+ import { fileURLToPath } from 'node:url'
11
11
 
12
- const __dirname = fileURLToPath(new URL('.', import.meta.url));
13
- const ROOT = join(__dirname, '..');
12
+ const __dirname = fileURLToPath(new URL('.', import.meta.url))
13
+ const _ROOT = join(__dirname, '..')
14
14
 
15
- const REQUIRED_NODE_MAJOR = 18;
16
- const nodeMajor = parseInt(process.versions.node.split('.')[0], 10);
15
+ const REQUIRED_NODE_MAJOR = 18
16
+ const nodeMajor = parseInt(process.versions.node.split('.')[0], 10)
17
17
 
18
18
  try {
19
19
  // Validate Node.js version
20
20
  if (nodeMajor < REQUIRED_NODE_MAJOR) {
21
- console.error(`❌ Node.js >= ${REQUIRED_NODE_MAJOR} required (current: ${process.versions.node})`);
22
- process.exit(1);
21
+ console.error(
22
+ `❌ Node.js >= ${REQUIRED_NODE_MAJOR} required (current: ${process.versions.node})`,
23
+ )
24
+ process.exit(1)
23
25
  }
24
26
 
25
27
  // Validate js-yaml is available
26
28
  try {
27
- await import('js-yaml');
29
+ await import('js-yaml')
28
30
  } catch {
29
- console.warn('⚠️ js-yaml not found — run `npm install` to install dependencies');
31
+ console.warn('⚠️ js-yaml not found — run `npm install` to install dependencies')
30
32
  }
31
33
 
32
- console.log('');
33
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
34
- console.log(' Pantheon OpenCode — npm dependencies installed ✅');
35
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
36
- console.log('');
37
- console.log(' Next steps:');
38
- console.log(' 1. Configure Pantheon:');
39
- console.log(' npm run setup');
40
- console.log(' # or: npx pantheon-opencode init');
41
- console.log(' 2. Verify installation:');
42
- console.log(' npm run doctor');
43
- console.log('');
34
+ console.log('')
35
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
36
+ console.log(' Pantheon OpenCode — npm dependencies installed ✅')
37
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
38
+ console.log('')
39
+ console.log(' Next steps:')
40
+ console.log(' 1. Configure Pantheon:')
41
+ console.log(' npm run setup')
42
+ console.log(' # or: npx pantheon-opencode init')
43
+ console.log(' 2. Verify installation:')
44
+ console.log(' npm run doctor')
45
+ console.log('')
44
46
  } catch (err) {
45
- console.error(`❌ Post-install check failed: ${err.message}`);
46
- process.exit(1);
47
+ console.error(`❌ Post-install check failed: ${err.message}`)
48
+ process.exit(1)
47
49
  }
package/scripts/prune.mjs CHANGED
@@ -34,7 +34,7 @@ import { dirname, join, resolve } from 'node:path'
34
34
  import { fileURLToPath } from 'node:url'
35
35
 
36
36
  const __dirname = dirname(fileURLToPath(import.meta.url))
37
- const ROOT = join(__dirname, '..')
37
+ const _ROOT = join(__dirname, '..')
38
38
  const DEFAULT_BACKUP_AGE_DAYS = 30
39
39
 
40
40
  // ---------------------------------------------------------------------------
@@ -30,7 +30,9 @@ function scanFile(path) {
30
30
  }
31
31
 
32
32
  function scanTarball(path) {
33
- const entries = execFileSync('tar', ['-tzf', path], { encoding: 'utf8' }).split('\n').filter(Boolean)
33
+ const entries = execFileSync('tar', ['-tzf', path], { encoding: 'utf8' })
34
+ .split('\n')
35
+ .filter(Boolean)
34
36
  const result = []
35
37
  for (const entry of entries) {
36
38
  const content = execFileSync('tar', ['-xOzf', path, entry])
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * sync-tui.mjs — Postinstall hook: sync TUI plugin to user's OpenCode config
4
+ *
5
+ * Runs automatically after `npm install pantheon-opencode@latest`. Detects the
6
+ * user's OpenCode config directory and, if the TUI plugin was previously
7
+ * installed (via `npx pantheon-opencode init`), copies fresh files from the
8
+ * installed package and refreshes dependencies.
9
+ *
10
+ * If the user hasn't initialized yet → do nothing (silent exit).
11
+ */
12
+ import { execSync } from 'node:child_process'
13
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'
14
+ import { homedir } from 'node:os'
15
+ import { join } from 'node:path'
16
+ import { fileURLToPath } from 'node:url'
17
+
18
+ const __dirname = fileURLToPath(new URL('.', import.meta.url))
19
+ const ROOT = join(__dirname, '..')
20
+
21
+ // ── Helpers ────────────────────────────────────────────────────────────
22
+
23
+ /**
24
+ * Write content to filePath only if it differs from what's on disk.
25
+ * @returns {boolean} true if file was written (changed), false if skipped
26
+ */
27
+ function writeIfChanged(filePath, content) {
28
+ const existing = existsSync(filePath) ? readFileSync(filePath, 'utf8') : null
29
+ if (existing === content) return false
30
+ writeFileSync(filePath, content, 'utf8')
31
+ return true
32
+ }
33
+
34
+ /**
35
+ * Read the `version` field from a package.json, or null on failure.
36
+ */
37
+ function readVersion(pkgPath) {
38
+ try {
39
+ return JSON.parse(readFileSync(pkgPath, 'utf8')).version || null
40
+ } catch {
41
+ return null
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Resolve the user's OpenCode config directory.
47
+ * Priority: $XDG_CONFIG_HOME/opencode → ~/.opencode → null (not initialized)
48
+ */
49
+ function resolveConfigDir() {
50
+ const xdgConfig = process.env.XDG_CONFIG_HOME || join(homedir(), '.config')
51
+ const xdgDir = join(xdgConfig, 'opencode')
52
+ if (existsSync(xdgDir)) return xdgDir
53
+
54
+ const homeDir = join(homedir(), '.opencode')
55
+ if (existsSync(homeDir)) return homeDir
56
+
57
+ return null
58
+ }
59
+
60
+ /**
61
+ * Copy plugin runtime files (dist/*, package.json, src/index.tsx) from srcDir
62
+ * to dstDir. Skips files that are byte-identical.
63
+ * @returns {{ created: number, skipped: number }}
64
+ */
65
+ function copyPluginFiles(srcDir, dstDir) {
66
+ const result = { created: 0, skipped: 0 }
67
+
68
+ mkdirSync(dstDir, { recursive: true })
69
+ mkdirSync(join(dstDir, 'dist'), { recursive: true })
70
+
71
+ // src/index.tsx → index.tsx
72
+ const srcIdx = join(srcDir, 'src', 'index.tsx')
73
+ if (existsSync(srcIdx)) {
74
+ const content = readFileSync(srcIdx, 'utf8')
75
+ if (writeIfChanged(join(dstDir, 'index.tsx'), content)) result.created++
76
+ else result.skipped++
77
+ }
78
+
79
+ // dist/*
80
+ const distSrc = join(srcDir, 'dist')
81
+ if (existsSync(distSrc)) {
82
+ for (const f of readdirSync(distSrc)) {
83
+ const content = readFileSync(join(distSrc, f), 'utf8')
84
+ if (writeIfChanged(join(dstDir, 'dist', f), content)) result.created++
85
+ else result.skipped++
86
+ }
87
+ }
88
+
89
+ // package.json
90
+ const pkgSrc = join(srcDir, 'package.json')
91
+ if (existsSync(pkgSrc)) {
92
+ const content = readFileSync(pkgSrc, 'utf8')
93
+ if (writeIfChanged(join(dstDir, 'package.json'), content)) result.created++
94
+ else result.skipped++
95
+ }
96
+
97
+ return result
98
+ }
99
+
100
+ // ── Main ───────────────────────────────────────────────────────────────
101
+
102
+ try {
103
+ const configDir = resolveConfigDir()
104
+ if (!configDir) {
105
+ // User hasn't run init yet — silent exit, no error
106
+ process.exit(0)
107
+ }
108
+
109
+ const tuiCopyDir = join(configDir, 'plugins', 'pantheon-tui')
110
+ if (!existsSync(tuiCopyDir)) {
111
+ // TUI plugin not installed — silent exit
112
+ process.exit(0)
113
+ }
114
+
115
+ // Source: TUI plugin inside the installed package
116
+ const tuiSrcDir = join(ROOT, 'src', 'plugins', 'tui')
117
+ if (!existsSync(tuiSrcDir)) {
118
+ // Source not found (shouldn't happen in a proper install)
119
+ process.exit(0)
120
+ }
121
+
122
+ // Compare versions before copy
123
+ const installedVersion = readVersion(join(tuiCopyDir, 'package.json'))
124
+ const sourceVersion = readVersion(join(tuiSrcDir, 'package.json'))
125
+
126
+ // Copy fresh files
127
+ const { created, skipped } = copyPluginFiles(tuiSrcDir, tuiCopyDir)
128
+
129
+ // Refresh dependencies in the copy
130
+ try {
131
+ execSync('npm install --omit=dev --no-audit', { cwd: tuiCopyDir, stdio: 'pipe' })
132
+ } catch {
133
+ // Non-fatal — deps may already be current
134
+ }
135
+
136
+ // Log result
137
+ if (installedVersion && sourceVersion && installedVersion === sourceVersion && created === 0) {
138
+ console.log(` TUI plugin already up to date (v${sourceVersion})`)
139
+ } else {
140
+ console.log(
141
+ ` TUI plugin updated to v${sourceVersion || 'latest'}${installedVersion ? ` (was v${installedVersion})` : ''}`,
142
+ )
143
+ }
144
+ } catch (err) {
145
+ // Postinstall scripts must never break npm install
146
+ console.warn(`⚠️ TUI sync skipped: ${err.message}`)
147
+ process.exit(0)
148
+ }
@@ -37,13 +37,11 @@ const PANTHEON_AGENT_NAMES = [
37
37
  'prometheus',
38
38
  'hephaestus',
39
39
 
40
-
41
40
  'nyx',
42
41
  'gaia',
43
42
  'iris',
44
43
  'mnemosyne',
45
44
  'talos',
46
-
47
45
  ]
48
46
 
49
47
  const PLATFORM_LABELS = {
@@ -1,5 +1,10 @@
1
1
  /** Status/exit policy shared by installation validation and its tests. */
2
- export const VALIDATION_STATUS = Object.freeze({ PASS: 'PASS', WARN: 'WARN', ERROR: 'ERROR', SKIP: 'SKIP' })
2
+ export const VALIDATION_STATUS = Object.freeze({
3
+ PASS: 'PASS',
4
+ WARN: 'WARN',
5
+ ERROR: 'ERROR',
6
+ SKIP: 'SKIP',
7
+ })
3
8
 
4
9
  export function classifyDoctorExit(exitStatus, output = '') {
5
10
  if (exitStatus === 0) return VALIDATION_STATUS.PASS
@@ -26,7 +26,11 @@ import { fileURLToPath, pathToFileURL } from 'node:url'
26
26
  export const MANIFESTS = [
27
27
  { name: 'pyproject.toml', file: 'pyproject.toml', kind: 'toml' },
28
28
  { name: 'plugin.json', file: 'plugin.json', kind: 'json' },
29
- { name: 'src/plugins/tui/package.json', file: join('src', 'plugins', 'tui', 'package.json'), kind: 'json' },
29
+ {
30
+ name: 'src/plugins/tui/package.json',
31
+ file: join('src', 'plugins', 'tui', 'package.json'),
32
+ kind: 'json',
33
+ },
30
34
  ]
31
35
 
32
36
  function readTomlVersion(filePath) {
@@ -109,8 +113,7 @@ function printDivergenceTable(result) {
109
113
  console.log('')
110
114
  }
111
115
 
112
- const isDirectRun =
113
- process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href
116
+ const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href
114
117
 
115
118
  if (isDirectRun) {
116
119
  // npm scripts run from the package root, so the working directory is the
@@ -138,7 +141,9 @@ if (isDirectRun) {
138
141
  console.log(`✓ Synced ${changed} manifest(s) to v${after.source}`)
139
142
  process.exit(0)
140
143
  }
141
- console.error(`version-check: sync incomplete — ${after.manifests.filter((m) => !m.ok).length} manifest(s) still divergent`)
144
+ console.error(
145
+ `version-check: sync incomplete — ${after.manifests.filter((m) => !m.ok).length} manifest(s) still divergent`,
146
+ )
142
147
  process.exit(1)
143
148
  }
144
149
  console.error('version-check: manifests out of sync (run with --fix to sync)')
@@ -14,7 +14,7 @@ permission:
14
14
  task:
15
15
  "*": deny
16
16
  temperature: 0.3
17
- steps: 50
17
+ steps: 70
18
18
  skills:
19
19
  - tdd-with-agents
20
20
  - visual-review-pipeline
@@ -12,7 +12,7 @@ permission:
12
12
  task:
13
13
  "*": deny
14
14
  temperature: 0.2
15
- steps: 40
15
+ steps: 60
16
16
  skills:
17
17
  - tdd-with-agents
18
18
  - incremental-implementation
@@ -12,7 +12,7 @@ permission:
12
12
  task:
13
13
  "*": deny
14
14
  temperature: 0.3
15
- steps: 40
15
+ steps: 60
16
16
  skills:
17
17
  - tdd-with-agents
18
18
  - auto-continue
@@ -11,7 +11,7 @@ mcp_tools:
11
11
  pantheon-memory: [memory_search]
12
12
  pantheon-code-mode: [execute_code_script]
13
13
  temperature: 0.3
14
- steps: 50
14
+ steps: 70
15
15
  skills:
16
16
  - tdd-with-agents
17
17
  - file-prompts
@@ -97,6 +97,10 @@ Verificacoes rapidas de estilo e sanidade:
97
97
  - [ ] Formatacao consistente (mesmo style guide)
98
98
  - [ ] Nomes de variaveis/funcoes descritivos
99
99
  - [ ] Comentarios TODO ou FIXME sem resolucao
100
+ - [ ] Comment Checker: codigo gerado indistinguivel de humano
101
+ - Flag comentarios obvios/excessivos (ex: `// increment i`, `# set x to 5`)
102
+ - Flag codigo morto comentado (blocos inteiros desativados)
103
+ - Flag cabecalhos boilerplate (autor/data/licenca repetitivos)
100
104
 
101
105
  Tempo estimado: ~10% do esforco de review
102
106