opencode-codeops 1.8.1 → 1.10.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/CHANGELOG.md +31 -0
- package/README.md +18 -0
- package/_shared/quality-profile.md +9 -0
- package/_shared/reasoning-effort.md +135 -0
- package/_shared/workspace-hygiene.md +53 -0
- package/agent-templates/codebase-scout.md +6 -0
- package/agent-templates/concurrency-auditor.md +7 -0
- package/agent-templates/design-challenger.md +6 -0
- package/agent-templates/domain-specialist-executor.md +6 -0
- package/agent-templates/domain-specialist-reviewer.md +6 -0
- package/agent-templates/financial-integrity-auditor.md +7 -0
- package/agent-templates/perf-auditor.md +6 -0
- package/agent-templates/phase-reviewer.md +6 -0
- package/agent-templates/plan-task-executor-opus.md +6 -0
- package/agent-templates/plan-task-executor.md +6 -0
- package/agent-templates/preflight-auditor.md +6 -0
- package/agent-templates/security-auditor.md +6 -0
- package/agent-templates/semantics-reviewer.md +7 -0
- package/agent-templates/spec-test-author.md +6 -0
- package/agents/concurrency-auditor.md +7 -0
- package/agents/correctness-reviewer.md +6 -0
- package/agents/demanding-executor.md +6 -0
- package/agents/design-challenger.md +6 -0
- package/agents/executor.md +6 -0
- package/agents/explorer.md +6 -0
- package/agents/financial-integrity-auditor.md +7 -0
- package/agents/performance-auditor.md +6 -0
- package/agents/preflight-auditor.md +6 -0
- package/agents/security-auditor.md +6 -0
- package/agents/semantics-reviewer.md +7 -0
- package/agents/spec-test-author.md +6 -0
- package/bin/lib/reasoning-effort.d.mts +85 -0
- package/bin/lib/reasoning-effort.mjs +331 -0
- package/bin/lib/tmp-hygiene.d.mts +38 -0
- package/bin/lib/tmp-hygiene.mjs +180 -0
- package/package.json +1 -1
- package/plugin/index.ts +146 -3
- package/scripts/__pycache__/install_agents.cpython-312.pyc +0 -0
- package/scripts/codeops_effort.py +216 -0
- package/scripts/fixtures/catalog-executor.golden.md +6 -0
- package/skills/analyze-project/SKILL.md +8 -0
- package/skills/clean-comments/SKILL.md +8 -0
- package/skills/exec-plan/SKILL.md +21 -0
- package/skills/exec-plan/execution-protocol.md +52 -7
- package/skills/git-commit/SKILL.md +11 -2
- package/skills/github-issues/SKILL.md +8 -0
- package/skills/grill-me/SKILL.md +18 -0
- package/skills/make-plan/SKILL.md +25 -0
- package/skills/make-plan/templates.md +2 -0
- package/skills/make-requirements/SKILL.md +18 -0
- package/skills/outcome-review/SKILL.md +8 -0
- package/skills/preflight/SKILL.md +18 -0
- package/skills/retro-requirements/SKILL.md +18 -0
- package/skills/roadmap/SKILL.md +8 -0
- package/skills/setup-codeops/SKILL.md +8 -0
- package/skills/setup-routing/SKILL.md +10 -0
- package/skills/setup-routing/routing.md +16 -1
- package/skills/techdocs/SKILL.md +8 -0
- package/skills/upgrade-plan/SKILL.md +18 -0
- package/standards/coding-standards-full.md +19 -0
- package/standards/coding-standards.md +6 -0
package/plugin/index.ts
CHANGED
|
@@ -4,6 +4,20 @@ import { homedir } from "node:os"
|
|
|
4
4
|
import { join, dirname } from "node:path"
|
|
5
5
|
import { fileURLToPath } from "node:url"
|
|
6
6
|
|
|
7
|
+
import {
|
|
8
|
+
cleanStaleTmpDirs,
|
|
9
|
+
ensureSessionTmpDir,
|
|
10
|
+
removeSessionTmpDir,
|
|
11
|
+
} from "../bin/lib/tmp-hygiene.mjs"
|
|
12
|
+
import {
|
|
13
|
+
applyEffort,
|
|
14
|
+
findEffortMarker,
|
|
15
|
+
readRoutingReasoning,
|
|
16
|
+
readSessionEffort,
|
|
17
|
+
resolveEffort,
|
|
18
|
+
} from "../bin/lib/reasoning-effort.mjs"
|
|
19
|
+
import type { EffortLevel } from "../bin/lib/reasoning-effort.mjs"
|
|
20
|
+
|
|
7
21
|
// ---------------------------------------------------------------------------
|
|
8
22
|
// Package root — resolved at module load time so it is always the plugin's
|
|
9
23
|
// installed directory, regardless of the working directory at event time.
|
|
@@ -43,6 +57,28 @@ const outputStyle = readFileSync(
|
|
|
43
57
|
)
|
|
44
58
|
const standardsText = `${codingStandards}\n\n${outputStyle}`
|
|
45
59
|
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Workspace hygiene — the plugin owns one temp directory per session (exported
|
|
62
|
+
// as CODEOPS_TMPDIR) and sweeps directories abandoned by earlier interrupted
|
|
63
|
+
// runs. The sweep runs at most once per plugin process, so spawning many child
|
|
64
|
+
// sessions does not repeat the directory walk.
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
let sweptStaleTmpDirs = false
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Sweep temp directories abandoned by earlier interrupted runs, once per
|
|
70
|
+
* process. Cleanup is best effort: a failure must never block a session.
|
|
71
|
+
*/
|
|
72
|
+
function sweepStaleTmpDirsOnce(): void {
|
|
73
|
+
if (sweptStaleTmpDirs) return
|
|
74
|
+
sweptStaleTmpDirs = true
|
|
75
|
+
try {
|
|
76
|
+
cleanStaleTmpDirs()
|
|
77
|
+
} catch {
|
|
78
|
+
// Best effort only.
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
46
82
|
// ---------------------------------------------------------------------------
|
|
47
83
|
// Helper — inject standards into a session without triggering an AI reply.
|
|
48
84
|
// Uses client.session.prompt with noReply: true (confirmed from OpenCode SDK).
|
|
@@ -109,23 +145,69 @@ async function warnOnVersionSkew(
|
|
|
109
145
|
}
|
|
110
146
|
}
|
|
111
147
|
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// Helper — log one content-free warning. Logging is best effort: a failed log
|
|
150
|
+
// must never break a request.
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
async function warnContentFree(
|
|
153
|
+
client: Parameters<Plugin>[0]["client"],
|
|
154
|
+
message: string
|
|
155
|
+
): Promise<void> {
|
|
156
|
+
try {
|
|
157
|
+
await client.app.log({ body: { service: "codeops", level: "warn", message } })
|
|
158
|
+
} catch {
|
|
159
|
+
// Best effort only.
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
// Helper — read the project routing config fresh on every request, so an edit
|
|
165
|
+
// applies without restarting the session. Any failure means "no routing".
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
function readRoutingConfig(directory: string): unknown {
|
|
168
|
+
try {
|
|
169
|
+
return JSON.parse(readFileSync(join(directory, "codeops", "codeops.json"), "utf8"))
|
|
170
|
+
} catch {
|
|
171
|
+
return {}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
112
175
|
// ---------------------------------------------------------------------------
|
|
113
176
|
// CodeOps plugin for OpenCode
|
|
114
177
|
// Replaces: hooks/hooks.json + hook_session_context.sh + hook_marker_guard.sh
|
|
115
178
|
// ---------------------------------------------------------------------------
|
|
116
179
|
export const CodeOpsPlugin: Plugin = async ({ client, directory }) => {
|
|
180
|
+
// Reasoning-effort state lives for the lifetime of this plugin instance:
|
|
181
|
+
// one entry per user message that carried a dispatch marker, plus a
|
|
182
|
+
// deduplication set for unsupported-level warnings.
|
|
183
|
+
const effortMarkers = new Map<string, { sessionID: string; level: EffortLevel }>()
|
|
184
|
+
const warnedEffortLevels = new Set<string>()
|
|
185
|
+
|
|
117
186
|
return {
|
|
118
187
|
// -----------------------------------------------------------------------
|
|
119
188
|
// Hook 1 & 2: inject standards on session.created and session.compacted.
|
|
120
189
|
// Both are dispatched via the generic event hook.
|
|
121
190
|
// session.created → new session (Codex: startup)
|
|
122
191
|
// session.compacted → after compact (Codex: resume|compact)
|
|
192
|
+
// Also the hygiene lifecycle: a new top-level session sweeps abandoned
|
|
193
|
+
// temp directories once, and a deleted session's temp directory is removed.
|
|
123
194
|
// -----------------------------------------------------------------------
|
|
124
195
|
event: async ({ event }) => {
|
|
125
196
|
if (event.type === "session.created") {
|
|
126
|
-
const
|
|
127
|
-
await injectStandards(client,
|
|
197
|
+
const info = (event.properties as { info: { id: string; parentID?: string } }).info
|
|
198
|
+
await injectStandards(client, info.id)
|
|
128
199
|
await warnOnVersionSkew(client, directory)
|
|
200
|
+
if (!info.parentID) sweepStaleTmpDirsOnce()
|
|
201
|
+
} else if (event.type === "session.deleted") {
|
|
202
|
+
const info = (event.properties as { info: { id: string } }).info
|
|
203
|
+
removeSessionTmpDir(info.id)
|
|
204
|
+
try {
|
|
205
|
+
for (const [messageID, entry] of effortMarkers) {
|
|
206
|
+
if (entry.sessionID === info.id) effortMarkers.delete(messageID)
|
|
207
|
+
}
|
|
208
|
+
} catch {
|
|
209
|
+
await warnContentFree(client, "Could not clear captured reasoning-effort markers.")
|
|
210
|
+
}
|
|
129
211
|
} else if (event.type === "session.compacted") {
|
|
130
212
|
const sessionId: string = (event.properties as { sessionID: string }).sessionID
|
|
131
213
|
await injectStandards(client, sessionId)
|
|
@@ -148,9 +230,16 @@ export const CodeOpsPlugin: Plugin = async ({ client, directory }) => {
|
|
|
148
230
|
// scripts as: python3 "${CODEOPS_PLUGIN_ROOT}/scripts/codeops_plan.py"
|
|
149
231
|
// The value is the package root (parent of this plugin/ directory), which
|
|
150
232
|
// is where skills/, scripts/, and the other shipped assets live.
|
|
233
|
+
// CODEOPS_TMPDIR is the session's directory under the CodeOps temp root;
|
|
234
|
+
// skills and agents put every scratch file there and delete it when done.
|
|
151
235
|
// -----------------------------------------------------------------------
|
|
152
|
-
"shell.env": async (
|
|
236
|
+
"shell.env": async (input, output) => {
|
|
153
237
|
output.env.CODEOPS_PLUGIN_ROOT = PACKAGE_ROOT
|
|
238
|
+
try {
|
|
239
|
+
output.env.CODEOPS_TMPDIR = ensureSessionTmpDir(input.sessionID)
|
|
240
|
+
} catch {
|
|
241
|
+
// Best effort: a temp-directory failure must never block a shell.
|
|
242
|
+
}
|
|
154
243
|
},
|
|
155
244
|
|
|
156
245
|
// -----------------------------------------------------------------------
|
|
@@ -177,5 +266,59 @@ export const CodeOpsPlugin: Plugin = async ({ client, directory }) => {
|
|
|
177
266
|
)
|
|
178
267
|
}
|
|
179
268
|
},
|
|
269
|
+
|
|
270
|
+
// -----------------------------------------------------------------------
|
|
271
|
+
// Hook 6: capture a dispatch marker from an incoming user message. The
|
|
272
|
+
// marker travels in the dispatch packet text; storing it by message id
|
|
273
|
+
// lets the later chat.params hook apply it to the same request.
|
|
274
|
+
// -----------------------------------------------------------------------
|
|
275
|
+
"chat.message": async (input, output) => {
|
|
276
|
+
try {
|
|
277
|
+
const texts = output.parts.map((part) =>
|
|
278
|
+
part?.type === "text" ? part.text : undefined
|
|
279
|
+
)
|
|
280
|
+
const level = findEffortMarker(texts)
|
|
281
|
+
if (level !== undefined) {
|
|
282
|
+
effortMarkers.set(output.message.id, { sessionID: input.sessionID, level })
|
|
283
|
+
}
|
|
284
|
+
} catch {
|
|
285
|
+
await warnContentFree(client, "Could not scan a message for a reasoning-effort marker.")
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
// -----------------------------------------------------------------------
|
|
290
|
+
// Hook 7: resolve the request's reasoning level (dispatch marker, then
|
|
291
|
+
// session flag, then routing default) and merge the model's own variant
|
|
292
|
+
// options. Any failure leaves the request unchanged.
|
|
293
|
+
// -----------------------------------------------------------------------
|
|
294
|
+
"chat.params": async (input, output) => {
|
|
295
|
+
try {
|
|
296
|
+
const stored = effortMarkers.get(input.message.id)
|
|
297
|
+
const marker = stored && stored.sessionID === input.sessionID ? stored.level : undefined
|
|
298
|
+
const session = readSessionEffort(input.sessionID)
|
|
299
|
+
const routing = readRoutingReasoning(readRoutingConfig(directory), input.agent)
|
|
300
|
+
const level = resolveEffort({ marker, session, routing })
|
|
301
|
+
if (level === undefined) return
|
|
302
|
+
|
|
303
|
+
const applied = applyEffort(output.options, level, input.model)
|
|
304
|
+
if (applied === output.options) {
|
|
305
|
+
if (marker !== undefined) {
|
|
306
|
+
const warningKey = `${input.sessionID}:${marker}`
|
|
307
|
+
if (!warnedEffortLevels.has(warningKey)) {
|
|
308
|
+
warnedEffortLevels.add(warningKey)
|
|
309
|
+
await warnContentFree(
|
|
310
|
+
client,
|
|
311
|
+
`Reasoning effort ${marker} is not available for agent ${input.agent}; ` +
|
|
312
|
+
"request left unchanged."
|
|
313
|
+
)
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return
|
|
317
|
+
}
|
|
318
|
+
output.options = applied
|
|
319
|
+
} catch {
|
|
320
|
+
await warnContentFree(client, "Could not apply a reasoning-effort level to a request.")
|
|
321
|
+
}
|
|
322
|
+
},
|
|
180
323
|
}
|
|
181
324
|
}
|
|
Binary file
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Record and clear the opt-in reasoning-effort level for a CodeOps session run.
|
|
3
|
+
|
|
4
|
+
The skills call this helper when a user passes `--auto-effort`. It writes one
|
|
5
|
+
small JSON file inside the session's CodeOps temp directory; the plugin reads
|
|
6
|
+
that file on every request and applies the level. The command fails closed:
|
|
7
|
+
an invalid level or a directory outside the CodeOps temp root exits with
|
|
8
|
+
status 2 and writes nothing.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import json
|
|
15
|
+
import os
|
|
16
|
+
import sys
|
|
17
|
+
import tempfile
|
|
18
|
+
from datetime import datetime, timezone
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
#: The only levels a session flag may record.
|
|
23
|
+
EFFORT_LEVELS = ("low", "medium", "high", "max")
|
|
24
|
+
|
|
25
|
+
#: Name of the state file the plugin reads.
|
|
26
|
+
STATE_FILE_NAME = "reasoning-effort.json"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def codeops_tmp_root() -> Path:
|
|
30
|
+
"""Return the CodeOps-owned temp root for the active temp directory.
|
|
31
|
+
|
|
32
|
+
The root mirrors the layout `bin/lib/tmp-hygiene.mjs` owns, so the helper
|
|
33
|
+
and the plugin resolve the same per-session directory.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Resolved path of the CodeOps temp root.
|
|
37
|
+
"""
|
|
38
|
+
return Path(tempfile.gettempdir()).resolve() / "opencode" / "codeops"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def resolve_session_dir(raw_dir: str) -> Path | None:
|
|
42
|
+
"""Validate a session directory argument against the CodeOps temp root.
|
|
43
|
+
|
|
44
|
+
A directory is accepted only when it exists, resolves strictly inside the
|
|
45
|
+
CodeOps temp root, and is not the root itself. Symlinks are resolved, so a
|
|
46
|
+
link that points outside the root is rejected.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
raw_dir: The `--dir` argument as provided by the caller.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
The resolved session directory, or None when it is not acceptable.
|
|
53
|
+
"""
|
|
54
|
+
try:
|
|
55
|
+
resolved = Path(raw_dir).resolve(strict=True)
|
|
56
|
+
except (OSError, RuntimeError):
|
|
57
|
+
# RuntimeError covers a symlink loop on Python 3.12, which the
|
|
58
|
+
# filesystem reports as ELOOP rather than a plain OSError.
|
|
59
|
+
return None
|
|
60
|
+
root = codeops_tmp_root()
|
|
61
|
+
if resolved == root or root not in resolved.parents:
|
|
62
|
+
return None
|
|
63
|
+
if not resolved.is_dir():
|
|
64
|
+
return None
|
|
65
|
+
return resolved
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def state_path(session_dir: Path) -> Path:
|
|
69
|
+
"""Return the state-file path inside a validated session directory.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
session_dir: A directory accepted by `resolve_session_dir`.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Path of the reasoning-effort state file.
|
|
76
|
+
"""
|
|
77
|
+
return session_dir / STATE_FILE_NAME
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def set_level(session_dir: Path, level: str) -> int:
|
|
81
|
+
"""Write the session level atomically and report it.
|
|
82
|
+
|
|
83
|
+
The payload is written to a temporary file in the same directory and then
|
|
84
|
+
moved over the final name with `os.replace`, so a reader never observes a
|
|
85
|
+
partially written file.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
session_dir: A validated session directory.
|
|
89
|
+
level: One of `EFFORT_LEVELS`.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Process exit code 0.
|
|
93
|
+
"""
|
|
94
|
+
payload = {
|
|
95
|
+
"schema": 1,
|
|
96
|
+
"reasoning": level,
|
|
97
|
+
"setAt": datetime.now(timezone.utc).isoformat(timespec="seconds"),
|
|
98
|
+
}
|
|
99
|
+
path = state_path(session_dir)
|
|
100
|
+
descriptor, temporary_name = tempfile.mkstemp(
|
|
101
|
+
prefix=f"{STATE_FILE_NAME}.", suffix=".tmp", dir=session_dir
|
|
102
|
+
)
|
|
103
|
+
try:
|
|
104
|
+
with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
|
|
105
|
+
json.dump(payload, handle, separators=(",", ":"), sort_keys=True)
|
|
106
|
+
handle.write("\n")
|
|
107
|
+
os.replace(temporary_name, path)
|
|
108
|
+
except OSError:
|
|
109
|
+
try:
|
|
110
|
+
os.unlink(temporary_name)
|
|
111
|
+
except OSError:
|
|
112
|
+
pass
|
|
113
|
+
print("Error: could not write the reasoning-effort state file.", file=sys.stderr)
|
|
114
|
+
return 2
|
|
115
|
+
print(f"Reasoning effort set: {level} for this session run.")
|
|
116
|
+
return 0
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def clear_level(session_dir: Path) -> int:
|
|
120
|
+
"""Remove the session level when present and report the cleanup.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
session_dir: A validated session directory.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
Process exit code 0.
|
|
127
|
+
"""
|
|
128
|
+
try:
|
|
129
|
+
state_path(session_dir).unlink()
|
|
130
|
+
except FileNotFoundError:
|
|
131
|
+
pass
|
|
132
|
+
except OSError:
|
|
133
|
+
print("Error: could not remove the reasoning-effort state file.", file=sys.stderr)
|
|
134
|
+
return 2
|
|
135
|
+
print("Reasoning effort cleared.")
|
|
136
|
+
return 0
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def show_status(session_dir: Path) -> int:
|
|
140
|
+
"""Print the stored session level, or the empty-state message.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
session_dir: A validated session directory.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Process exit code 0.
|
|
147
|
+
"""
|
|
148
|
+
level = None
|
|
149
|
+
try:
|
|
150
|
+
payload = json.loads(state_path(session_dir).read_text(encoding="utf-8"))
|
|
151
|
+
if (
|
|
152
|
+
isinstance(payload, dict)
|
|
153
|
+
and payload.get("schema") == 1
|
|
154
|
+
and payload.get("reasoning") in EFFORT_LEVELS
|
|
155
|
+
):
|
|
156
|
+
level = payload["reasoning"]
|
|
157
|
+
except (OSError, json.JSONDecodeError):
|
|
158
|
+
level = None
|
|
159
|
+
if level is None:
|
|
160
|
+
print("No session reasoning effort set.")
|
|
161
|
+
else:
|
|
162
|
+
print(f"Session reasoning effort: {level}")
|
|
163
|
+
return 0
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def parse_args() -> argparse.Namespace:
|
|
167
|
+
"""Parse the command-line arguments.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
The parsed argparse namespace.
|
|
171
|
+
"""
|
|
172
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
173
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
174
|
+
|
|
175
|
+
set_parser = sub.add_parser("set", help="record a session reasoning level")
|
|
176
|
+
set_parser.add_argument("--dir", required=True, help="session temp directory")
|
|
177
|
+
set_parser.add_argument("--reasoning", required=True, help="level to record")
|
|
178
|
+
|
|
179
|
+
clear_parser = sub.add_parser("clear", help="remove the session reasoning level")
|
|
180
|
+
clear_parser.add_argument("--dir", required=True, help="session temp directory")
|
|
181
|
+
|
|
182
|
+
status_parser = sub.add_parser("status", help="print the session reasoning level")
|
|
183
|
+
status_parser.add_argument("--dir", required=True, help="session temp directory")
|
|
184
|
+
|
|
185
|
+
return parser.parse_args()
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def main() -> int:
|
|
189
|
+
"""Run the requested command.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Process exit code: 0 on success, 2 on invalid input.
|
|
193
|
+
"""
|
|
194
|
+
args = parse_args()
|
|
195
|
+
session_dir = resolve_session_dir(args.dir)
|
|
196
|
+
if session_dir is None:
|
|
197
|
+
print(
|
|
198
|
+
"Error: --dir must be an existing session directory inside the CodeOps temp root.",
|
|
199
|
+
file=sys.stderr,
|
|
200
|
+
)
|
|
201
|
+
return 2
|
|
202
|
+
if args.command == "set":
|
|
203
|
+
if args.reasoning not in EFFORT_LEVELS:
|
|
204
|
+
print(
|
|
205
|
+
"Error: --reasoning must be one of: " + ", ".join(EFFORT_LEVELS) + ".",
|
|
206
|
+
file=sys.stderr,
|
|
207
|
+
)
|
|
208
|
+
return 2
|
|
209
|
+
return set_level(session_dir, args.reasoning)
|
|
210
|
+
if args.command == "clear":
|
|
211
|
+
return clear_level(session_dir)
|
|
212
|
+
return show_status(session_dir)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
if __name__ == "__main__":
|
|
216
|
+
raise SystemExit(main())
|
|
@@ -57,3 +57,9 @@ original-goal or smallest-design context blocks execution; report it to the pare
|
|
|
57
57
|
exactly what is missing or ambiguous as a blocker — never guess, and never edit the
|
|
58
58
|
execution plan or roadmap (the parent session owns those and the user conversation).
|
|
59
59
|
- Report per task, 3-4 lines each: what changed, test status, any blocker.
|
|
60
|
+
- **Workspace hygiene (non-negotiable).** Temporary artifacts you create — verify logs, diffs,
|
|
61
|
+
patches, scratch directories — belong under `$CODEOPS_TMPDIR` when it is set (otherwise the OS
|
|
62
|
+
temp directory), never inside the repository. Delete everything you created before reporting
|
|
63
|
+
done. Never delete user files, versioned artifacts, worktrees, or another session's temporary
|
|
64
|
+
files. If an artifact must outlive the task, name it and say why. Full rules:
|
|
65
|
+
`_shared/workspace-hygiene.md`.
|
|
@@ -27,3 +27,11 @@ description: Analyze the current repository and create or refresh concise CodeOp
|
|
|
27
27
|
10. When a specialization signal is strong and no specialist covers it, report the candidate with evidence and recommend the `setup-routing` creation flow ([../../_shared/specialist-agents.md](../../_shared/specialist-agents.md)); never write agent files here. Preserve the `<!-- CODEOPS-SPECIALISTS:START -->` / `<!-- CODEOPS-SPECIALISTS:END -->` block byte-for-byte when refreshing the managed section.
|
|
28
28
|
|
|
29
29
|
Keep `AGENTS.md` small. Operational routing belongs in `codeops/codeops.json` or `opencode.json`, not prose.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
> **Workspace hygiene (non-negotiable):** put every temporary artifact this run creates in
|
|
34
|
+
> `$CODEOPS_TMPDIR` (fallback: the OS temp directory), never in the repository, and delete them
|
|
35
|
+
> all before reporting completion. Never delete user files, versioned artifacts, worktrees, or
|
|
36
|
+
> another session's temporary files. Full rules:
|
|
37
|
+
> [_shared/workspace-hygiene.md](../../_shared/workspace-hygiene.md).
|
|
@@ -20,3 +20,11 @@ This is a comment-only workflow. Never alter executable tokens, declarations, ty
|
|
|
20
20
|
7. Review the diff to prove only comments changed. Run the repository's normal formatter and verification command.
|
|
21
21
|
|
|
22
22
|
Documentation explains the shipped system, not the temporary process that produced it.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
> **Workspace hygiene (non-negotiable):** put every temporary artifact this run creates in
|
|
27
|
+
> `$CODEOPS_TMPDIR` (fallback: the OS temp directory), never in the repository, and delete them
|
|
28
|
+
> all before reporting completion. Never delete user files, versioned artifacts, worktrees, or
|
|
29
|
+
> another session's temporary files. Full rules:
|
|
30
|
+
> [_shared/workspace-hygiene.md](../../_shared/workspace-hygiene.md).
|
|
@@ -36,6 +36,18 @@ do not report or implement optional additions raised during execution or review.
|
|
|
36
36
|
Exploration may create `SE-*` proposals, but only the user may choose `Keep` and authorize a plan
|
|
37
37
|
update.
|
|
38
38
|
|
|
39
|
+
## Auto-effort option
|
|
40
|
+
|
|
41
|
+
If `$ARGUMENTS` contains exactly one exact standalone `--auto-effort` or `--auto-effort=<level>`
|
|
42
|
+
token before the first `--` sentinel, remove it before resolving targets, paths, or modes; zero
|
|
43
|
+
occurrences means each phase's `Reasoning:` level is printed as a suggestion only, more than one
|
|
44
|
+
or an invalid level is an argument error; announce
|
|
45
|
+
`Auto-effort active — reasoning <level> applied for this run`; then read and apply
|
|
46
|
+
[../../_shared/reasoning-effort.md](../../_shared/reasoning-effort.md) §Auto-effort option. A bare
|
|
47
|
+
flag follows each phase's suggestion; `--auto-effort=<level>` forces that level for the whole run,
|
|
48
|
+
including every dispatch marker composed during it. The run clears the level before its final
|
|
49
|
+
summary.
|
|
50
|
+
|
|
39
51
|
Execute the implementation plan at `plans/$ARGUMENTS/99-execution-plan.md`. The first
|
|
40
52
|
argument is the feature name; an optional flag selects the commit mode.
|
|
41
53
|
|
|
@@ -267,3 +279,12 @@ Brief rules for verification failure, plan deviation, and mid-task interruption
|
|
|
267
279
|
files and use only facts you can read — do not invent settings.
|
|
268
280
|
- Commit using the `git-commit` skill (commit only) or the `git-commit` skill in push mode (commit + push), or a normal git commit.
|
|
269
281
|
- Related skills: make-plan (creation), upgrade-plan (outdated plans), preflight, roadmap, techdocs.
|
|
282
|
+
- Cleanup is part of session wrap-up: delete the run's verify logs and scratch per
|
|
283
|
+
[../../_shared/workspace-hygiene.md](../../_shared/workspace-hygiene.md) and report
|
|
284
|
+
`Cleanup: done` in the session summary.
|
|
285
|
+
|
|
286
|
+
> **Workspace hygiene (non-negotiable):** put every temporary artifact this run creates in
|
|
287
|
+
> `$CODEOPS_TMPDIR` (fallback: the OS temp directory), never in the repository, and delete them
|
|
288
|
+
> all before reporting completion. Never delete user files, versioned artifacts, worktrees, or
|
|
289
|
+
> another session's temporary files. Full rules:
|
|
290
|
+
> [_shared/workspace-hygiene.md](../../_shared/workspace-hygiene.md).
|
|
@@ -70,6 +70,10 @@ scope baseline; missing mode context fails closed to strict scope.
|
|
|
70
70
|
When opt-in outcome metrics are enabled, record only a content-free execution-stage event through
|
|
71
71
|
`codeops_outcomes.py`; metrics never gate execution.
|
|
72
72
|
|
|
73
|
+
When the phase header carries `> **Reasoning**: <level> — <reason>`, record it as the phase's
|
|
74
|
+
advisory level for dispatch markers, inline reporting, and applied-level reporting. It is a
|
|
75
|
+
suggestion: no gate reads it, and its absence means "inherit".
|
|
76
|
+
|
|
73
77
|
**Spec-author dispatch (profile-gated).** Tasks marked `[spec-author]` dispatch the
|
|
74
78
|
spec-test-author agent — packet per `_shared/quality-profile.md` — BEFORE any implementation
|
|
75
79
|
task of that phase, and the red phase is confirmed from its report. A spec test that cannot be
|
|
@@ -225,7 +229,8 @@ red-phase, green-phase, session wrap-up — executes with output captured to a t
|
|
|
225
229
|
<verify command> > "$VERIFY_LOG" 2>&1
|
|
226
230
|
```
|
|
227
231
|
|
|
228
|
-
(`$VERIFY_LOG` = a file
|
|
232
|
+
(`$VERIFY_LOG` = a file under `$CODEOPS_TMPDIR` — or the OS temp directory when it is unset —
|
|
233
|
+
e.g. `verify-<task-id>.log`.)
|
|
229
234
|
|
|
230
235
|
- **PASS** → surface ONE line: `VERIFY PASS (task N.N.N)`, plus the test count if it is
|
|
231
236
|
extractable from the log tail at no extra cost.
|
|
@@ -234,10 +239,11 @@ red-phase, green-phase, session wrap-up — executes with output captured to a t
|
|
|
234
239
|
- **Red-phase runs** (spec tests expected to fail) → surface only the failing spec-test
|
|
235
240
|
names/count confirming the red state — never the full dump.
|
|
236
241
|
|
|
237
|
-
The full log
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
242
|
+
The full log remains on disk until the session wrap-up cleanup deletes it (see
|
|
243
|
+
`_shared/workspace-hygiene.md`); nothing in the transcript depends on it afterward. If the log
|
|
244
|
+
location is unwritable, fall back to running verify plainly ONCE and note the fallback in the
|
|
245
|
+
session summary. This rule is about CONTEXT, not rigor: the verify command itself, its scope, and
|
|
246
|
+
pass/fail gating are unchanged — and the temp log is read-only evidence, never executed.
|
|
241
247
|
|
|
242
248
|
### Zero-Ambiguity During Execution
|
|
243
249
|
|
|
@@ -298,12 +304,46 @@ receives nothing else and must not need anything else:
|
|
|
298
304
|
- the scope mode (`strict` or `explore`) and confirmed product scope baseline; missing or invalid
|
|
299
305
|
scope context fails closed to strict mode. Missing or invalid original-goal or smallest-design
|
|
300
306
|
context blocks dispatch;
|
|
301
|
-
- the target file paths and the project's verify command
|
|
307
|
+
- the target file paths and the project's verify command;
|
|
308
|
+
- the phase's reasoning marker line (`[codeops-effort: <level>]`), resolved from the run's forced
|
|
309
|
+
`--auto-effort=<level>` level, else the phase's `Reasoning:` suggestion; omitted when neither
|
|
310
|
+
exists.
|
|
302
311
|
|
|
303
312
|
Excerpting owned content into a packet is the intended retrieval mechanism, not restatement. The
|
|
304
313
|
quoted AR/ST/spec content is context for the executor's *understanding* — it must not surface as a
|
|
305
314
|
citation in shipped code (the executor carries the same doc-standard ban and self-check).
|
|
306
315
|
|
|
316
|
+
**Reasoning marker.** Every dispatched unit — executor, reviewer, auditor, spec-test author,
|
|
317
|
+
specialist, or scout — receives one standalone marker line in its packet:
|
|
318
|
+
|
|
319
|
+
```text
|
|
320
|
+
[codeops-effort: medium]
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Place it immediately after the `[codeops-dispatch …]` header for quality agents, or as the first
|
|
324
|
+
line for packets without a header. When neither the forced run level nor the phase suggestion
|
|
325
|
+
exists, add no marker: routing policy still applies, otherwise the child inherits the parent
|
|
326
|
+
variant. The marker is packet context; it never appears in shipped code comments. The
|
|
327
|
+
complexity-gate design challenger is excluded because its independence contract forbids extra
|
|
328
|
+
packet shaping.
|
|
329
|
+
|
|
330
|
+
**Applied-level reporting.** For every dispatch, report the level and its source in the dispatch
|
|
331
|
+
commentary, for example `Dispatch: executor — reasoning: medium (phase suggestion)`,
|
|
332
|
+
`— reasoning: high (routing default)`, or `— inherited`. Reporting is observational; it never
|
|
333
|
+
gates a dispatch.
|
|
334
|
+
|
|
335
|
+
**Inline phases.** When a phase runs inline (the default):
|
|
336
|
+
|
|
337
|
+
1. Print `Suggested reasoning: <level> — <reason>` before the phase's first task when the phase
|
|
338
|
+
header carries the line.
|
|
339
|
+
2. Without `--auto-effort`, change nothing else — the session keeps its own variant.
|
|
340
|
+
3. With `--auto-effort`, set the session level for the phase through
|
|
341
|
+
`python3 "${CODEOPS_PLUGIN_ROOT}/scripts/codeops_effort.py" set --dir "$CODEOPS_TMPDIR" --reasoning <level>`
|
|
342
|
+
and update it when the next phase's suggestion differs; with `--auto-effort=<level>`, set that
|
|
343
|
+
level once and keep it for the whole run. A phase without a `Reasoning:` line leaves the
|
|
344
|
+
session level unchanged.
|
|
345
|
+
4. When `$CODEOPS_TMPDIR` is empty or the helper fails, print an advise-only note and continue.
|
|
346
|
+
|
|
307
347
|
**Division of labor.** The PARENT — never the executor — updates `99-execution-plan.md`
|
|
308
348
|
(two-stage marks), the Progress header, and the roadmap. The executor implements task-by-task,
|
|
309
349
|
runs verify per the Verify-output capture rule, and reports per task. Mark `[~]` as the executor
|
|
@@ -402,7 +442,12 @@ otherwise still `[~]` — with the progress counter and Last Updated stamp curre
|
|
|
402
442
|
2. **🚨 First: update `99-execution-plan.md`** with ALL completed tasks (before anything else).
|
|
403
443
|
3. Run the verify command (output captured per the Verify-output capture rule).
|
|
404
444
|
4. Handle the commit per the active commit mode (see [commit-modes.md](commit-modes.md)).
|
|
405
|
-
5.
|
|
445
|
+
5. If this run set a session level through `--auto-effort`, clear it before the summary:
|
|
446
|
+
`python3 "${CODEOPS_PLUGIN_ROOT}/scripts/codeops_effort.py" clear --dir "$CODEOPS_TMPDIR"`.
|
|
447
|
+
6. Report the session summary (must include `Execution Plan Updated: ✅`).
|
|
448
|
+
7. **Cleanup:** delete every temporary artifact this session created — verify logs under
|
|
449
|
+
`$CODEOPS_TMPDIR`, scratch directories, temporary diffs — per
|
|
450
|
+
`_shared/workspace-hygiene.md`, and report `Cleanup: done` or name what was kept and why.
|
|
406
451
|
|
|
407
452
|
### Session Summary Template
|
|
408
453
|
|
|
@@ -14,13 +14,14 @@ A request to commit authorizes a local commit only. Push only when the user expl
|
|
|
14
14
|
1. Resolve the repository root and inspect `git status --short`, staged/unstaged diffs, and recent commit style.
|
|
15
15
|
2. If clean, report `nothing to commit` and stop.
|
|
16
16
|
3. Inspect every untracked file. Stop and ask before staging likely secrets, credentials, build output, large binaries, or unrelated scratch files.
|
|
17
|
-
4. Resolve the authoritative verification command from `AGENTS.md`, CodeOps plan Verify lines, or project manifests. Run it with output captured to a
|
|
17
|
+
4. Resolve the authoritative verification command from `AGENTS.md`, CodeOps plan Verify lines, or project manifests. Run it with output captured to a log under `$CODEOPS_TMPDIR` (fallback: the OS temp directory), never in the repository. On failure, report the failure tail and do not stage or commit.
|
|
18
18
|
5. Stage deliberately by explicit paths or coherent groups. Avoid `git add .` when the working directory may be a monorepo subtree or unrelated changes exist.
|
|
19
19
|
6. Review `git diff --cached --check`, the staged stat, and the staged diff. Ensure the commit contains one coherent purpose and preserves unrelated user changes.
|
|
20
|
-
7. Write a Conventional Commit message to a temporary file and commit with `git commit -F`. Never use an inline multiline message.
|
|
20
|
+
7. Write a Conventional Commit message to a temporary file under `$CODEOPS_TMPDIR` (fallback: the OS temp directory) and commit with `git commit -F`. Never use an inline multiline message.
|
|
21
21
|
8. If a commit hook modifies files, inspect and restage only relevant changes, then retry once. Never use `--no-verify` automatically.
|
|
22
22
|
9. For push mode, fetch/rebase only when appropriate for the branch policy. Stop on conflicts; never resolve ambiguous conflicts automatically. Push normally, never with force.
|
|
23
23
|
10. Report the resulting commit, verification, and push state.
|
|
24
|
+
11. Delete the temporary verification log and commit-message file, then report `Cleanup: done` or name what was kept and why.
|
|
24
25
|
|
|
25
26
|
## Message shape
|
|
26
27
|
|
|
@@ -33,3 +34,11 @@ type(scope): imperative summary
|
|
|
33
34
|
```
|
|
34
35
|
|
|
35
36
|
Use `feat`, `fix`, `refactor`, `test`, `docs`, or `chore` unless repository guidance defines another convention.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
> **Workspace hygiene (non-negotiable):** put every temporary artifact this run creates in
|
|
41
|
+
> `$CODEOPS_TMPDIR` (fallback: the OS temp directory), never in the repository, and delete them
|
|
42
|
+
> all before reporting completion. Never delete user files, versioned artifacts, worktrees, or
|
|
43
|
+
> another session's temporary files. Full rules:
|
|
44
|
+
> [_shared/workspace-hygiene.md](../../_shared/workspace-hygiene.md).
|
|
@@ -36,3 +36,11 @@ For each issue:
|
|
|
36
36
|
5. Continue past nonexistent individual issues but record every outcome.
|
|
37
37
|
|
|
38
38
|
Return a per-issue summary. Never create, edit, label, close, reopen, or comment unless the user's request explicitly authorizes that mutation.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
> **Workspace hygiene (non-negotiable):** put every temporary artifact this run creates in
|
|
43
|
+
> `$CODEOPS_TMPDIR` (fallback: the OS temp directory), never in the repository, and delete them
|
|
44
|
+
> all before reporting completion. Never delete user files, versioned artifacts, worktrees, or
|
|
45
|
+
> another session's temporary files. Full rules:
|
|
46
|
+
> [_shared/workspace-hygiene.md](../../_shared/workspace-hygiene.md).
|
package/skills/grill-me/SKILL.md
CHANGED
|
@@ -21,6 +21,16 @@ implementation work begins.
|
|
|
21
21
|
|
|
22
22
|
> **CodeOps Artifact Schema**: 1
|
|
23
23
|
|
|
24
|
+
## Auto-effort option
|
|
25
|
+
|
|
26
|
+
If `$ARGUMENTS` contains exactly one exact standalone `--auto-effort` or `--auto-effort=<level>`
|
|
27
|
+
token before the first `--` sentinel, remove it before resolving targets, paths, or modes; zero
|
|
28
|
+
occurrences means this skill's recommended level (`high`) is printed as a suggestion only, more
|
|
29
|
+
than one or an invalid level is an argument error; announce
|
|
30
|
+
`Auto-effort active — reasoning <level> applied for this run`; then read and apply
|
|
31
|
+
[../../_shared/reasoning-effort.md](../../_shared/reasoning-effort.md) §Auto-effort option. The
|
|
32
|
+
run clears the level before its final summary.
|
|
33
|
+
|
|
24
34
|
## Core Directive
|
|
25
35
|
|
|
26
36
|
> **Interview the user relentlessly about every aspect of the topic until you reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one.**
|
|
@@ -340,3 +350,11 @@ grill-me → identify design tree → walk each branch → resolve decisions →
|
|
|
340
350
|
|
|
341
351
|
**Output:** A shared understanding summary with all decisions, assumptions,
|
|
342
352
|
constraints, and deferrals — ready to feed into any downstream skill.
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
> **Workspace hygiene (non-negotiable):** put every temporary artifact this run creates in
|
|
357
|
+
> `$CODEOPS_TMPDIR` (fallback: the OS temp directory), never in the repository, and delete them
|
|
358
|
+
> all before reporting completion. Never delete user files, versioned artifacts, worktrees, or
|
|
359
|
+
> another session's temporary files. Full rules:
|
|
360
|
+
> [_shared/workspace-hygiene.md](../../_shared/workspace-hygiene.md).
|