opencode-codeops 2.1.1 → 2.2.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.
@@ -17,9 +17,21 @@ from dataclasses import dataclass
17
17
  from pathlib import Path
18
18
 
19
19
  try:
20
- from scripts.codeops_plan import BLOCKED_REASON_RE, IMPLEMENTS_RE, parse_implements, parse_tasks
20
+ from scripts.codeops_plan import (
21
+ BLOCKED_REASON_RE,
22
+ IMPLEMENTS_RE,
23
+ has_checkbox_lines,
24
+ parse_implements,
25
+ parse_tasks,
26
+ )
21
27
  except ModuleNotFoundError: # Direct execution adds scripts/, not the repository root, to sys.path.
22
- from codeops_plan import BLOCKED_REASON_RE, IMPLEMENTS_RE, parse_implements, parse_tasks
28
+ from codeops_plan import (
29
+ BLOCKED_REASON_RE,
30
+ IMPLEMENTS_RE,
31
+ has_checkbox_lines,
32
+ parse_implements,
33
+ parse_tasks,
34
+ )
23
35
 
24
36
 
25
37
  TARGET_ID_RE = re.compile(
@@ -231,7 +243,9 @@ def inspect_migration(codeops_root: Path) -> Migration:
231
243
  continue
232
244
  execution_text = execution.read_text(encoding="utf-8")
233
245
  tasks = parse_tasks(execution_text)
234
- if not tasks:
246
+ # Presence stays checkbox-based (id-agnostic) so legacy id-less
247
+ # checklists keep migrating; only real tasks are checked further.
248
+ if not has_checkbox_lines(execution_text):
235
249
  problems.append(f"{execution}: contains no execution tasks")
236
250
  for task in tasks:
237
251
  if task.marker == "!" and not BLOCKED_REASON_RE.search(task.text):
@@ -172,6 +172,11 @@ For each task, in order:
172
172
  the Master Progress Checklist — see the protocol's dual-format detection) and bump the Progress
173
173
  counter / Last Updated stamp as soon as implementation finishes (crash-safe), promote it to
174
174
  `[x]` only after its verification passes. A task never shows `[x]` with a failing verify.
175
+
176
+ - **Report progress** — when the `codeops_progress` tool is available — at the transition
177
+ points in [execution-protocol.md](execution-protocol.md) §Progress Reporting; a failed or
178
+ missing report never blocks any step.
179
+
175
180
  3. **Verify** — run your project's verify command (from the project's AGENTS.md, or detected
176
181
  project conventions), output captured per the protocol's **Verify-output capture rule**
177
182
  (PASS one-liner; on failure the last 50 log lines + log path). Pass → promote `[~]` → `[x]`;
@@ -444,6 +444,35 @@ otherwise still `[~]` — with the progress counter and Last Updated stamp curre
444
444
 
445
445
  ---
446
446
 
447
+ ## Progress Reporting (fail-soft)
448
+
449
+ While a plan executes, the agent may report live progress through the `codeops_progress` tool so
450
+ the sidebar shows the current run. Reporting is an observability aid only: the Markdown execution
451
+ plan remains the single progress source of truth, and a missing tool or a failed call never
452
+ blocks, delays, or changes any verify, commit, or plan update — when the tool is unavailable,
453
+ execution proceeds silently.
454
+
455
+ Report at these moments; `plan` is the plan folder name, and every call carries `plan` and
456
+ `activity` (absent optional fields are carried by the run state):
457
+
458
+ | Moment | Report payload |
459
+ | ------ | -------------- |
460
+ | Run start (start point determined) | `{ plan, phase, task, activity: "implementing" }` |
461
+ | Phase start (subsequent phases) | `{ plan, phase, activity: "starting" }` |
462
+ | Task implemented (after the `[~]` mark) | `{ plan, task, activity: "implementing" }` |
463
+ | Verification (before running verify) | `{ plan, activity: "verifying" }` |
464
+ | Task verified (after `[x]` + the progress-bar display) | `{ plan, verified, total, activity: "implementing" }` — counts from that display |
465
+ | Blocker (after the `[!]` mark) | `{ plan, activity: "blocked", detail: "<short reason>" }` |
466
+ | Delegation (around an executor/reviewer dispatch) | `{ plan, activity: "delegating", detail: "<role>" }` |
467
+ | Review step (post-phase quality loop) | `{ plan, activity: "reviewing" }` |
468
+ | Waiting (before pausing for an explicit user decision) | `{ plan, activity: "waiting" }` |
469
+ | Run done (all tasks complete) | `{ plan, activity: "done" }` |
470
+
471
+ One call per listed moment, no other calls (noise control). A reporting failure or a host without
472
+ the tool never blocks, delays, or changes any step.
473
+
474
+ ---
475
+
447
476
  ## Step 3: Session Wrap-Up
448
477
 
449
478
  1. Complete the current task before stopping.
@@ -1,55 +0,0 @@
1
- /**
2
- * Specification tests for the scratch-directory lifecycle.
3
- *
4
- * The plugin exports one scratch directory (`$CODEOPS_TMPDIR`) per plugin
5
- * runtime, and every session served by that runtime shares it. Deleting the
6
- * directory when a plugin instance unloads destroys in-flight scratch files of
7
- * sessions that are still running, so the contract is: the plugin never
8
- * deletes the shared directory; the age-based sweep reclaims abandoned
9
- * directories instead.
10
- *
11
- * This is the regression oracle for that contract: a failure here means the
12
- * repository reintroduced early deletion, never that the test is wrong.
13
- *
14
- * @module temp-lifecycle.spec.test
15
- */
16
-
17
- import assert from "node:assert/strict"
18
- import { readFileSync } from "node:fs"
19
- import { dirname, join, resolve } from "node:path"
20
- import { describe, it } from "node:test"
21
- import { fileURLToPath } from "node:url"
22
-
23
- const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..")
24
-
25
- /**
26
- * Read a repository file as UTF-8 text.
27
- * @param {string} relativePath - Path relative to the repository root.
28
- * @returns {string} The file contents.
29
- */
30
- function read(relativePath) {
31
- return readFileSync(join(ROOT, relativePath), "utf8")
32
- }
33
-
34
- describe("scratch-directory lifecycle", () => {
35
- it("keeps the plugin free of scratch-directory deletion", () => {
36
- const plugin = read("plugin/index.ts")
37
- assert.doesNotMatch(plugin, /removeSessionTmpDir/)
38
- assert.match(plugin, /cleanStaleTmpDirs\(\)/)
39
- assert.match(plugin, /controller\.abort\(\)/)
40
- })
41
-
42
- it("keeps the temp hygiene module free of exact-directory deletion", () => {
43
- assert.doesNotMatch(read("bin/lib/tmp-hygiene.mjs"), /removeSessionTmpDir/)
44
- assert.doesNotMatch(read("bin/lib/tmp-hygiene.d.mts"), /removeSessionTmpDir/)
45
- })
46
-
47
- it("documents the age-based sweep as the automatic cleanup", () => {
48
- const hygiene = read("_shared/workspace-hygiene.md")
49
- assert.doesNotMatch(hygiene, /removes that runtime's\s+directory when the plugin unloads/i)
50
- assert.match(hygiene, /seven days/i)
51
- const readme = read("README.md")
52
- assert.doesNotMatch(readme, /removes the\s+directory when it unloads/i)
53
- assert.match(readme, /sweeps\s+directories abandoned/i)
54
- })
55
- })