opencode-overclock 0.3.0 → 0.5.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 (82) hide show
  1. package/README.md +252 -111
  2. package/package.json +6 -4
  3. package/skills/codebase-design/DEEPENING.md +35 -0
  4. package/skills/codebase-design/DESIGN-IT-TWICE.md +34 -0
  5. package/skills/codebase-design/SKILL.md +93 -0
  6. package/skills/diagnosing-bugs/SKILL.md +123 -0
  7. package/skills/domain-modeling/ADR-FORMAT.md +55 -0
  8. package/skills/domain-modeling/CONTEXT-FORMAT.md +32 -0
  9. package/skills/domain-modeling/SKILL.md +102 -0
  10. package/skills/doubt/SKILL.md +80 -0
  11. package/skills/grilling/SKILL.md +96 -0
  12. package/skills/source-discipline/SKILL.md +78 -0
  13. package/skills/tdd/SKILL.md +87 -0
  14. package/skills/to-spec/SKILL.md +69 -0
  15. package/skills/to-spec/SPEC-TEMPLATE.md +50 -0
  16. package/skills/to-tickets/SKILL.md +74 -0
  17. package/skills/to-tickets/TICKET-TEMPLATE.md +41 -0
  18. package/src/bridge.ts +1 -0
  19. package/src/buddy/companion.ts +104 -5
  20. package/src/buddy/sprites.ts +4 -4
  21. package/src/buddy/tui.ts +175 -65
  22. package/src/core/bridge.ts +34 -0
  23. package/src/core/lifecycle.ts +67 -0
  24. package/src/core/policy.ts +128 -0
  25. package/src/core/summary.ts +33 -0
  26. package/src/core/types.ts +193 -0
  27. package/src/features/buddy.ts +1 -2
  28. package/src/features/guard.ts +421 -37
  29. package/src/features/index.ts +18 -4
  30. package/src/features/recovery.ts +153 -0
  31. package/src/features/safety.ts +147 -0
  32. package/src/features/sched.ts +183 -89
  33. package/src/features/tasks.ts +134 -33
  34. package/src/features/truncator.ts +116 -0
  35. package/src/features/usage.ts +46 -65
  36. package/src/features/workflow.ts +256 -0
  37. package/src/index.ts +96 -67
  38. package/src/lib/busy.ts +1 -25
  39. package/src/lib/exec.ts +13 -0
  40. package/src/lib/inject.ts +10 -56
  41. package/src/lib/mirror.ts +13 -0
  42. package/src/lib/probe.ts +1 -15
  43. package/src/lib/state.ts +10 -39
  44. package/src/lib/tmux.ts +1 -0
  45. package/src/lib/ui.ts +208 -0
  46. package/src/merge.ts +2 -66
  47. package/src/platform/probe.ts +25 -0
  48. package/src/platform/process/exec.ts +317 -0
  49. package/src/platform/process/tmux.ts +60 -0
  50. package/src/platform/session/busy.ts +33 -0
  51. package/src/platform/session/inject.ts +89 -0
  52. package/src/platform/session/notify.ts +20 -0
  53. package/src/platform/storage/state.ts +99 -0
  54. package/src/platform/storage/store.ts +61 -0
  55. package/src/summary.ts +1 -0
  56. package/src/tools.ts +8 -244
  57. package/src/tui.ts +57 -186
  58. package/src/types.ts +1 -73
  59. package/src/v2/context.ts +470 -0
  60. package/src/v2/host.ts +120 -0
  61. package/src/v2/loader.ts +150 -0
  62. package/src/workflow/agents/codebase-researcher.ts +27 -0
  63. package/src/workflow/agents/design-explorer.ts +33 -0
  64. package/src/workflow/agents/doubt-reviewer.ts +26 -0
  65. package/src/workflow/agents/engineering-coach.ts +23 -0
  66. package/src/workflow/agents/performance-auditor.ts +29 -0
  67. package/src/workflow/agents/security-auditor.ts +23 -0
  68. package/src/workflow/agents/spec-reviewer.ts +15 -0
  69. package/src/workflow/agents/standards-reviewer.ts +24 -0
  70. package/src/workflow/agents/test-engineer.ts +28 -0
  71. package/src/workflow/catalog.ts +210 -0
  72. package/src/workflow/templates/build.ts +47 -0
  73. package/src/workflow/templates/define.ts +45 -0
  74. package/src/workflow/templates/diagnose.ts +58 -0
  75. package/src/workflow/templates/plan.ts +52 -0
  76. package/src/workflow/templates/ship.ts +64 -0
  77. package/src/buddy/reactions.ts +0 -41
  78. package/src/buddy/types.ts +0 -30
  79. package/src/config.ts +0 -19
  80. package/src/features/checkpoints.ts +0 -128
  81. package/src/features/sandbox.ts +0 -104
  82. package/src/validate.ts +0 -197
package/src/validate.ts DELETED
@@ -1,197 +0,0 @@
1
- import type { ConfigIssue, FeatureModule, OptionType } from "./types.ts"
2
- import type { ToolPolicy } from "./tools.ts"
3
-
4
- export type { ConfigIssue }
5
-
6
- /** Levenshtein, capped -- only used to turn a typo into a "did you mean". */
7
- function distance(a: string, b: string): number {
8
- const prev = Array.from({ length: b.length + 1 }, (_, i) => i)
9
- const cur = new Array<number>(b.length + 1)
10
- for (let i = 1; i <= a.length; i++) {
11
- cur[0] = i
12
- for (let j = 1; j <= b.length; j++) {
13
- cur[j] = Math.min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1))
14
- }
15
- prev.splice(0, prev.length, ...cur)
16
- }
17
- return prev[b.length]
18
- }
19
-
20
- /** Closest known name within edit distance 2, else undefined. */
21
- function nearest(input: string, known: readonly string[]): string | undefined {
22
- let best: string | undefined
23
- let bestD = 3
24
- for (const k of known) {
25
- const d = distance(input.toLowerCase(), k.toLowerCase())
26
- if (d < bestD) {
27
- bestD = d
28
- best = k
29
- }
30
- }
31
- return best
32
- }
33
-
34
- function unknownKey(input: string, known: readonly string[], what: string): string {
35
- const guess = nearest(input, known)
36
- if (guess) return `unknown ${what} "${input}" -- did you mean "${guess}"?`
37
- return `unknown ${what} "${input}". Known: ${known.join(", ")}`
38
- }
39
-
40
- function typeOf(v: unknown): OptionType | "null" {
41
- if (v === null) return "null"
42
- if (Array.isArray(v)) return "array"
43
- const t = typeof v
44
- if (t === "boolean" || t === "number" || t === "string" || t === "object") return t
45
- return "object"
46
- }
47
-
48
- function isPlainObject(v: unknown): v is Record<string, unknown> {
49
- return typeof v === "object" && v !== null && !Array.isArray(v)
50
- }
51
-
52
- /**
53
- * Check the `toolNames` remap. A rename that silently does nothing is the worst outcome
54
- * here: the proxy keeps rejecting the tool and the config looks correct. So an unknown
55
- * source name is an issue, and two sources aiming at one target is an issue -- the merge
56
- * would keep only the last.
57
- */
58
- function validateToolNames(toolNames: unknown, features: readonly FeatureModule[]): ConfigIssue[] {
59
- if (toolNames === undefined) return []
60
- if (!isPlainObject(toolNames)) {
61
- return [{ path: "toolNames", message: `"toolNames" must be an object, got ${typeOf(toolNames)}` }]
62
- }
63
-
64
- const issues: ConfigIssue[] = []
65
- const declared = features.flatMap((f) => f.tools ?? [])
66
- const targets = new Map<string, string>()
67
-
68
- for (const [from, to] of Object.entries(toolNames)) {
69
- if (!declared.includes(from)) {
70
- issues.push({ path: `toolNames.${from}`, message: unknownKey(from, declared, "tool") })
71
- continue
72
- }
73
- if (typeof to !== "string" || to.trim() === "") {
74
- issues.push({
75
- path: `toolNames.${from}`,
76
- message: `must be a non-empty string, got ${typeOf(to)}`,
77
- })
78
- continue
79
- }
80
- const prior = targets.get(to)
81
- if (prior) {
82
- issues.push({
83
- path: `toolNames.${from}`,
84
- message: `"${to}" is already the target of "${prior}" -- only one would survive the merge`,
85
- })
86
- continue
87
- }
88
- targets.set(to, from)
89
- }
90
-
91
- return issues
92
- }
93
-
94
- /**
95
- * Check overclock.json against the feature registry.
96
- *
97
- * Exists because an unrecognised key is otherwise a silent no-op: `killOnExist: true`
98
- * reads as "option not set", the feature runs with defaults, and nothing complains.
99
- * Returns every issue found -- callers warn, never throw. A bad config degrades to
100
- * defaults rather than taking the plugin down.
101
- */
102
- export function validateConfig(config: unknown, features: readonly FeatureModule[]): ConfigIssue[] {
103
- const issues: ConfigIssue[] = []
104
- if (!isPlainObject(config)) {
105
- return [{ path: "", message: `config must be an object, got ${typeOf(config)}` }]
106
- }
107
-
108
- const TOP = ["features", "toolNames", "toolAllowlist"]
109
- for (const key of Object.keys(config)) {
110
- if (!TOP.includes(key)) issues.push({ path: key, message: unknownKey(key, TOP, "top-level key") })
111
- }
112
-
113
- issues.push(...validateToolNames(config.toolNames, features))
114
-
115
- const { features: featuresCfg } = config
116
- if (featuresCfg === undefined) return issues
117
- if (!isPlainObject(featuresCfg)) {
118
- issues.push({
119
- path: "features",
120
- message: `"features" must be an object, got ${typeOf(featuresCfg)}`,
121
- })
122
- return issues
123
- }
124
-
125
- const names = features.map((f) => f.name)
126
- for (const [name, setting] of Object.entries(featuresCfg)) {
127
- const feature = features.find((f) => f.name === name)
128
- if (!feature) {
129
- issues.push({ path: `features.${name}`, message: unknownKey(name, names, "feature") })
130
- continue
131
- }
132
- if (typeof setting === "boolean") continue
133
- if (!isPlainObject(setting)) {
134
- issues.push({
135
- path: `features.${name}`,
136
- message: `must be true, false, or an options object -- got ${typeOf(setting)}`,
137
- })
138
- continue
139
- }
140
-
141
- const schema = feature.options ?? {}
142
- const optionNames = Object.keys(schema)
143
- for (const [key, value] of Object.entries(setting)) {
144
- const expected = schema[key]
145
- if (!expected) {
146
- issues.push({
147
- path: `features.${name}.${key}`,
148
- message: optionNames.length
149
- ? unknownKey(key, optionNames, "option")
150
- : `"${name}" takes no options, got "${key}"`,
151
- })
152
- continue
153
- }
154
- const actual = typeOf(value)
155
- if (actual !== expected) {
156
- issues.push({
157
- path: `features.${name}.${key}`,
158
- message: `expected ${expected}, got ${actual}`,
159
- })
160
- }
161
- }
162
- }
163
-
164
- return issues
165
- }
166
-
167
- /**
168
- * One-line inventory of what this plugin just added to the session.
169
- *
170
- * Not about context cost -- the full tool surface is only ~800 tokens. It is about
171
- * capability: installing overclock hands the agent background shell execution and
172
- * recurring scheduling, and that should not be something a user discovers by accident.
173
- */
174
- export function summarise(
175
- enabled: readonly FeatureModule[],
176
- skipped: readonly string[],
177
- policy: ToolPolicy = { rename: {}, withheld: new Set() },
178
- ): string {
179
- const { rename, withheld } = policy
180
- const offered = enabled.flatMap((f) => (f.tools ?? []).filter((t) => !withheld.has(t)))
181
- // Report the name the model is actually offered, not the declared one -- under a remap the
182
- // declared name appears nowhere on the wire, so listing it would misdescribe the session.
183
- const parts = enabled.map((f) => {
184
- const names = (f.tools ?? []).filter((t) => !withheld.has(t)).map((t) => rename[t] ?? t)
185
- return `${f.name}${names.length ? ` (${names.join(", ")})` : ""}`
186
- })
187
- const plural = (n: number, word: string) => `${n} ${word}${n === 1 ? "" : "s"}`
188
- let line = `${plural(enabled.length, "module")}, ${plural(offered.length, "tool")}: ${parts.join(" · ")}`
189
- const applied = offered.filter((t) => rename[t] && rename[t] !== t).map((t) => `${t}->${rename[t]}`)
190
- if (applied.length) line += ` | renamed: ${applied.join(", ")}`
191
- // Withheld tools are the one case where the session is quietly less capable than the config
192
- // implies, so they are named here rather than left to the issue log alone.
193
- const held = enabled.flatMap((f) => (f.tools ?? []).filter((t) => withheld.has(t)))
194
- if (held.length) line += ` | withheld: ${held.join(", ")}`
195
- if (skipped.length) line += ` | skipped: ${skipped.join(", ")}`
196
- return line
197
- }