@hecer/yoke 1.6.2 → 1.8.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 (51) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +42 -0
  4. package/README.md +61 -22
  5. package/canon/manifest.yaml +1 -1
  6. package/canon/tools/gemini-rtk-hook.mjs +25 -0
  7. package/dist/agents/contracts.js +2 -0
  8. package/dist/agents/process-streams.js +18 -4
  9. package/dist/agents/providers.js +32 -4
  10. package/dist/agents/telemetry.js +58 -10
  11. package/dist/check/command.js +114 -0
  12. package/dist/cli.js +124 -5
  13. package/dist/context/packet.js +32 -0
  14. package/dist/dashboard/analytics.js +123 -0
  15. package/dist/dashboard/page.js +34 -0
  16. package/dist/dashboard/panels.js +19 -0
  17. package/dist/dashboard/registry.js +68 -0
  18. package/dist/dashboard/server.js +177 -0
  19. package/dist/estimation/durations.js +20 -0
  20. package/dist/estimation/schedule.js +41 -0
  21. package/dist/execution/actions.js +24 -0
  22. package/dist/goals/command.js +191 -0
  23. package/dist/loop/dispatcher.js +19 -5
  24. package/dist/loop/git.js +5 -4
  25. package/dist/loop/loop.js +23 -3
  26. package/dist/loop/parallel-adapters.js +3 -2
  27. package/dist/loop/parallel-command.js +52 -4
  28. package/dist/loop/prd.js +3 -0
  29. package/dist/loop/recovery.js +51 -0
  30. package/dist/loop/reporter.js +105 -8
  31. package/dist/loop/run-command.js +62 -13
  32. package/dist/loop/runner.js +20 -16
  33. package/dist/loop/scheduler.js +38 -1
  34. package/dist/observability/events.js +72 -0
  35. package/dist/observability/history.js +80 -0
  36. package/dist/quality/candidate-comparison.js +1 -1
  37. package/dist/quality/command.js +16 -3
  38. package/dist/retrofit/config.js +11 -0
  39. package/dist/retrofit/gitignore.js +6 -0
  40. package/dist/retrofit/planners/gemini.js +11 -3
  41. package/dist/routing/router.js +50 -10
  42. package/dist/setup/command.js +3 -2
  43. package/dist/workspace/fingerprint.js +66 -0
  44. package/dist/workspace/state.js +20 -0
  45. package/docs/PRODUCT-DIRECTION-2026-09-05.md +199 -0
  46. package/docs/VERIFIED-PROJECTS-VALIDATION.md +29 -0
  47. package/docs/VERIFIED-PROJECTS.md +167 -0
  48. package/docs/superpowers/plans/2026-09-05-verified-projects.md +83 -0
  49. package/gemini-extension.json +1 -1
  50. package/hooks/bounded-gemini.mjs +70 -0
  51. package/package.json +1 -1
@@ -0,0 +1,70 @@
1
+ import { spawn } from 'node:child_process'
2
+ import { readFileSync, statSync, mkdtempSync, writeFileSync, unlinkSync, rmdirSync } from 'node:fs'
3
+ import { tmpdir } from 'node:os'
4
+ import { dirname, join } from 'node:path'
5
+ import { pathToFileURL } from 'node:url'
6
+
7
+ // Match Gemini's JSON-with-comments reader without interpreting executable content.
8
+ export function parseSettings(source) {
9
+ let clean = '', quoted = false, escaped = false, line = false, block = false
10
+ for (let i = 0; i < source.length; i++) {
11
+ const c = source[i], next = source[i + 1]
12
+ if (line) { if (c === '\n' || c === '\r') { line = false; clean += c } else clean += ' '; continue }
13
+ if (block) { if (c === '*' && next === '/') { block = false; clean += ' '; i++ } else clean += c === '\n' ? '\n' : ' '; continue }
14
+ if (quoted) { clean += c; if (escaped) escaped = false; else if (c === '\\') escaped = true; else if (c === '"') quoted = false; continue }
15
+ if (c === '"') quoted = true
16
+ else if (c === '/' && next === '/') { line = true; clean += ' '; i++; continue }
17
+ else if (c === '/' && next === '*') { block = true; clean += ' '; i++; continue }
18
+ clean += c
19
+ }
20
+ if (block) throw Error('Invalid settings comment')
21
+ const value = JSON.parse(clean)
22
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw Error('Settings must be an object')
23
+ return value
24
+ }
25
+
26
+ export function prepareGeminiEnvironment(environment = process.env, platform = process.platform) {
27
+ const original = environment.GEMINI_CLI_SYSTEM_SETTINGS_PATH ?? (platform === 'win32' ? 'C:\\ProgramData\\gemini-cli\\settings.json' : platform === 'darwin' ? '/Library/Application Support/GeminiCli/settings.json' : '/etc/gemini-cli/settings.json')
28
+ let settings = {}
29
+ try {
30
+ if (statSync(original).size > 1048576) throw Error('Settings too large')
31
+ settings = parseSettings(readFileSync(original, 'utf8'))
32
+ } catch (error) {
33
+ if (error.code !== 'ENOENT') throw Error('Cannot safely preserve existing Gemini system settings')
34
+ }
35
+ if (settings.experimental !== undefined && (!settings.experimental || typeof settings.experimental !== 'object' || Array.isArray(settings.experimental))) throw Error('Invalid Gemini experimental settings')
36
+ if (settings.agents !== undefined && (!settings.agents || typeof settings.agents !== 'object' || Array.isArray(settings.agents))) throw Error('Invalid Gemini agent settings')
37
+ if (settings.agents?.overrides !== undefined && (!settings.agents.overrides || typeof settings.agents.overrides !== 'object' || Array.isArray(settings.agents.overrides))) throw Error('Invalid Gemini agent overrides')
38
+ const dir = mkdtempSync(join(tmpdir(), 'yoke-gemini-'))
39
+ const file = join(dir, 'settings.json')
40
+ const cleanup = () => { try { unlinkSync(file) } catch {} try { rmdirSync(dir) } catch {} }
41
+ const overrides = { ...settings.agents?.overrides }
42
+ // Gemini exposes these built-ins even when experimental agents are disabled.
43
+ for (const name of ['codebase_investigator', 'cli_help']) overrides[name] = { ...overrides[name], enabled: false }
44
+ try { writeFileSync(file, JSON.stringify({ ...settings, experimental: { ...settings.experimental, enableAgents: false }, agents: { ...settings.agents, overrides } }), { mode: 0o600, flag: 'wx' }) }
45
+ catch (error) { cleanup(); throw error }
46
+ return {
47
+ env: { ...environment, GEMINI_CLI_SYSTEM_SETTINGS_PATH: file,
48
+ GEMINI_CLI_SYSTEM_DEFAULTS_PATH: environment.GEMINI_CLI_SYSTEM_DEFAULTS_PATH ?? join(dirname(original), 'system-defaults.json') },
49
+ cleanup,
50
+ }
51
+ }
52
+
53
+ if (process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url) {
54
+ let prepared
55
+ try {
56
+ prepared = prepareGeminiEnvironment()
57
+ // Keep Windows shim arguments shell-inert. Prompts use inherited stdin only.
58
+ const args = process.argv.slice(2)
59
+ const windows = process.platform === 'win32'
60
+ if (windows && args.some(arg => !/^[A-Za-z0-9_./:=+-]+$/u.test(arg))) throw Error('Unsafe Gemini argument')
61
+ const child = spawn(windows ? 'cmd.exe' : 'gemini', windows ? ['/d', '/s', '/c', ['gemini', ...args].join(' ')] : args, { stdio: 'inherit', shell: false, env: prepared.env })
62
+ process.once('exit', prepared.cleanup)
63
+ child.once('error', () => { process.stderr.write('Could not start the Gemini CLI\n'); process.exitCode = 127; prepared.cleanup() })
64
+ child.once('close', code => { process.exitCode ??= code === null || code < 0 ? 1 : code; prepared.cleanup() })
65
+ } catch {
66
+ prepared?.cleanup()
67
+ process.stderr.write('Could not establish bounded Gemini execution while preserving system settings\n')
68
+ process.exitCode = 1
69
+ }
70
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hecer/yoke",
3
- "version": "1.6.2",
3
+ "version": "1.8.0",
4
4
  "description": "One harness, three agents, zero trust in \"done\" — cross-agent coding harness for Claude Code, Codex CLI, and Gemini CLI: one skill canon, mechanical safety gates, an autonomous loop with screenshot/video proofs.",
5
5
  "type": "module",
6
6
  "bin": {