@kylecheng3146/agent-ops 0.1.13 → 0.1.15

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.
@@ -0,0 +1,65 @@
1
+ import { join } from "node:path";
2
+ import { AgentOpsError } from "../fs/paths.js";
3
+ import { readPrivateFile, writePrivateFile } from "../security/permissions.js";
4
+ const FINGERPRINT_PATTERN = /^[a-f0-9]{64}$/u;
5
+ const TASK_ID_PATTERN = /^[A-Za-z0-9._-]{1,128}$/u;
6
+ export const REVIEW_ATTESTATION_DIRECTORY = ".agent-ops/reviews";
7
+ function attestationPath(root, fingerprint) {
8
+ return join(root, ...REVIEW_ATTESTATION_DIRECTORY.split("/"), `${fingerprint}.json`);
9
+ }
10
+ function parseAttestation(value) {
11
+ if (typeof value !== "object" || value === null) {
12
+ return null;
13
+ }
14
+ const record = value;
15
+ if (record.schemaVersion !== 1 ||
16
+ record.status !== "PASS" ||
17
+ (record.taskId !== undefined &&
18
+ (typeof record.taskId !== "string" ||
19
+ !TASK_ID_PATTERN.test(record.taskId))) ||
20
+ typeof record.harness !== "string" ||
21
+ !TASK_ID_PATTERN.test(record.harness) ||
22
+ typeof record.sourceFingerprint !== "string" ||
23
+ !FINGERPRINT_PATTERN.test(record.sourceFingerprint) ||
24
+ typeof record.createdAt !== "string" ||
25
+ !Number.isFinite(Date.parse(record.createdAt))) {
26
+ return null;
27
+ }
28
+ return {
29
+ schemaVersion: 1,
30
+ ...(record.taskId === undefined ? {} : { taskId: record.taskId }),
31
+ harness: record.harness,
32
+ status: "PASS",
33
+ sourceFingerprint: record.sourceFingerprint,
34
+ createdAt: record.createdAt
35
+ };
36
+ }
37
+ export async function saveReviewAttestation(root, attestation) {
38
+ const validated = parseAttestation(attestation);
39
+ if (validated === null) {
40
+ throw new AgentOpsError("REVIEW_ATTESTATION_INVALID", "Review attestation is invalid.");
41
+ }
42
+ const relativePath = `${REVIEW_ATTESTATION_DIRECTORY}/${validated.sourceFingerprint}.json`;
43
+ await writePrivateFile(attestationPath(root, validated.sourceFingerprint), `${JSON.stringify(validated, null, 2)}\n`, root);
44
+ return relativePath;
45
+ }
46
+ /**
47
+ * Returns the attestation recorded for this exact source state, or null. A
48
+ * malformed file reads as absent: a gate must fail closed on garbage, never
49
+ * treat it as a passing review.
50
+ */
51
+ export async function findReviewAttestation(root, sourceFingerprint) {
52
+ if (!FINGERPRINT_PATTERN.test(sourceFingerprint)) {
53
+ return null;
54
+ }
55
+ const source = await readPrivateFile(attestationPath(root, sourceFingerprint), root);
56
+ if (source === null) {
57
+ return null;
58
+ }
59
+ try {
60
+ return parseAttestation(JSON.parse(source));
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ }
@@ -140,6 +140,9 @@ export function createReviewExecutor(options) {
140
140
  if (spawned.stdoutTruncated || spawned.stderrTruncated) {
141
141
  return { status: "NOT_RUN", reason: "output-too-large", harness: target };
142
142
  }
143
+ if (spawned.failureClass === "nonzero-exit") {
144
+ return { status: "NOT_RUN", reason: "login-required", harness: target };
145
+ }
143
146
  const payload = extractReviewObject(target, spawned.stdout);
144
147
  const parsed = payload === undefined
145
148
  ? undefined
@@ -47,9 +47,10 @@ Enable it during `agent-ops init`, or by hand:
47
47
  ```
48
48
 
49
49
  `targets` is an **ordered fallback chain**. Every review locks to the
50
- staged/unstaged/untracked surface (or a clean `--base <ref>...HEAD` range),
51
- requires fresh PASS evidence for required checks, and prints a complete native
52
- schema report without persisting it.
50
+ staged/unstaged/untracked surface (or a clean `--base <ref>...HEAD` range).
51
+ A bare review uses the built-in `change-quality` criterion; `--task` uses the
52
+ task criteria and requires fresh PASS evidence for required checks. The full
53
+ report is printed, and PASS persists only a source-fingerprint attestation.
53
54
 
54
55
  Every attempt starts from a fresh temporary cwd with a narrow environment. The
55
56
  following target identities may be configured; only Claude currently meets the
@@ -102,20 +103,25 @@ interactive OAuth, so there is no `--fix`. Run `<target> login` yourself.
102
103
  ### Project-local loop profile
103
104
 
104
105
  `--profile loop` is an opt-in project-scope profile. Select `codex`, `claude`,
105
- or both (for example, `--harness codex,claude`); it requires a
106
- POSIX-compatible `bash` and does not support Windows launchers yet. Start with
107
- a dry run:
106
+ or both (for example, `--harness codex,claude`). Claude Code supports native
107
+ Windows through a generated PowerShell launcher; Codex's loop launcher still
108
+ requires POSIX-compatible `bash`. Start with a dry run:
108
109
 
109
110
  ```bash
110
111
  agent-ops init --dry-run --scope project --harness codex,claude --profile loop --json
111
112
  agent-ops init --scope project --harness codex,claude --profile loop --yes
112
113
  ```
113
114
 
114
- For each selected supported harness, agent-ops owns exactly one small launcher:
115
- `.codex/hooks/agent-ops-loop.sh` or `.claude/hooks/agent-ops-loop.sh`. Both
116
- launchers delegate to the same installed Node runtime, so they do not copy a
117
- project-specific loop script. Codex also gets `.codex/config.toml` only when it
118
- is absent. First installation seeds, without replacing existing content,
115
+ On native Windows, select `--harness claude` unless Codex is running in a
116
+ POSIX environment; the Codex loop still invokes `bash`.
117
+
118
+ For each selected supported harness, agent-ops owns the minimal native
119
+ launchers: `.codex/hooks/agent-ops-loop.sh` for Codex, and
120
+ `.claude/hooks/agent-ops-loop.sh` plus `.claude/hooks/agent-ops-loop.ps1` for
121
+ Claude Code. Both Claude launchers delegate to the same installed Node
122
+ runtime, so they do not copy a project-specific loop script. On Windows,
123
+ Claude's generated settings select the PowerShell launcher. Codex also gets
124
+ `.codex/config.toml` only when it is absent. First installation seeds, without replacing existing content,
119
125
  `loop-goal.md`, `loop-state.md`, and `loop-telemetry.jsonl` under the selected
120
126
  harness directory. A hash-commented `.gitignore` block ignores those local
121
127
  files.
@@ -194,7 +200,6 @@ Changing this feature changes native registration. Run:
194
200
 
195
201
  ```bash
196
202
  agent-ops update
197
- agent-ops trust grant
198
203
  ```
199
204
 
200
205
  Without `update`, doctor can report `UPDATE_REQUIRED` for registration drift.
@@ -202,14 +207,14 @@ Separately, after a toolkit upgrade or effective profile or capability change
202
207
  alters an intact path-independent managed rules artifact,
203
208
  `artifact-staleness` reports `DEGRADED` with `UPDATE_REQUIRED`. `agent-ops
204
209
  update` regenerates the artifact and clears that result; a missing or
205
- hash-mismatched artifact remains an `artifacts` `FAIL`. Without the new trust
206
- grant, trust-gated hooks remain stale.
210
+ hash-mismatched artifact remains an `artifacts` `FAIL`. Confirmed project
211
+ updates automatically replace the stale trust binding when verifiers exist.
207
212
 
208
213
  Doctor never writes, and some findings have no fix: a surface outside the
209
214
  installation root, or a capability a harness only partially supports by
210
215
  descriptor declaration (opencode's `lifecycle-summary`, for example), report
211
216
  `UNKNOWN` or `DEGRADED` permanently and exit 0. CI that wants automatic
212
- repair calls `agent-ops update` / `agent-ops trust grant` directly rather
217
+ repair calls `agent-ops update` or the manual `agent-ops trust grant` directly rather
213
218
  than parsing doctor's output.
214
219
 
215
220
  Stop is report-only: it continues the
@@ -7,7 +7,8 @@ integration is a generated local plugin; it does not manage `opencode.json`.
7
7
 
8
8
  Configuration is versioned independently from the manifest. Config v1 migrates
9
9
  to config v2 with Stop verification disabled; changing the capability requires
10
- `agent-ops update` followed by `agent-ops trust grant`. Stop verification is
10
+ a confirmed project `agent-ops update`, which also refreshes trust when
11
+ verifiers exist. Stop verification is
11
12
  explicit, trusted, report-only, and never completes a task. Dry-run plans keep
12
13
  foreign settings opaque, and the routing migration is one-way once applied.
13
14
 
@@ -85,7 +85,8 @@ policy into project-specific scripts or alter an ordinary permission request.
85
85
 
86
86
  - Trigger: A project selects `loop` with Codex, Claude Code, or both.
87
87
  - Action: Generate only the selected `.codex/hooks/agent-ops-loop.sh` and/or
88
- `.claude/hooks/agent-ops-loop.sh` launchers, register the documented loop
88
+ Claude's `.claude/hooks/agent-ops-loop.sh` plus
89
+ `.claude/hooks/agent-ops-loop.ps1` launchers, register the documented loop
89
90
  lifecycle events except `Stop`, and preserve foreign hook groups. Block only
90
91
  high-confidence literal credentials at `UserPromptSubmit` or Bash
91
92
  `PreToolUse`, and dangerous Bash commands at `PreToolUse`, using the documented native denial shape. Emit no
@@ -42,8 +42,9 @@ Claude 與 Codex lifecycle support 為 `supported`,OpenCode 從 app initializa
42
42
  ```
43
43
 
44
44
  `targets` 是**有序的後備鏈**。每次 review 都鎖定 staged/unstaged/untracked
45
- 變更(或乾淨的 `--base <ref>...HEAD`),並要求必要驗證的最新 PASS evidence;
46
- 完整原生 schema report 會顯示給人看,但不會持久化。
45
+ 變更(或乾淨的 `--base <ref>...HEAD`)。裸跑使用內建 `change-quality` 準則;
46
+ `--task` 使用 task criteria,並要求必要驗證的最新 PASS evidence。完整 report
47
+ 會顯示給人看,PASS 後只持久化 source-fingerprint attestation。
47
48
 
48
49
  每次嘗試都從新的暫存 cwd 與最小環境啟動。目前只有 Claude 具備完整的
49
50
  context-isolation 合約,能自動執行:
@@ -90,18 +91,24 @@ agent-ops doctor --check-auth # 每個目標一次真實 print 呼叫
90
91
  ### Project-local loop profile
91
92
 
92
93
  `--profile loop` 是明確 opt-in 的 project-scope profile。請選擇 `codex`、
93
- `claude` 或兩者(例如 `--harness codex,claude`);它需要 POSIX-compatible
94
- `bash`,目前尚未支援 Windows launcher。建議先 dry run:
94
+ `claude` 或兩者(例如 `--harness codex,claude`)。Claude Code 已支援原生
95
+ Windows,會使用產生的 PowerShell launcher;Codex loop launcher 仍需要
96
+ POSIX-compatible `bash`。建議先 dry run:
95
97
 
96
98
  ```bash
97
99
  agent-ops init --dry-run --scope project --harness codex,claude --profile loop --json
98
100
  agent-ops init --scope project --harness codex,claude --profile loop --yes
99
101
  ```
100
102
 
101
- 對每個選定且支援的 harness,agent-ops 只擁有一個小型 launcher:
102
- `.codex/hooks/agent-ops-loop.sh` `.claude/hooks/agent-ops-loop.sh`。兩個
103
- launcher 都委派給同一個已安裝的 Node runtime,因此不會複製 project-specific
104
- loop script。Codex 只會在 `.codex/config.toml` 不存在時建立它。首次安裝會在不
103
+ 在原生 Windows 上,除非 Codex 是在 POSIX environment 執行,請選擇
104
+ `--harness claude`;Codex loop 仍會呼叫 `bash`。
105
+
106
+ 對每個選定且支援的 harness,agent-ops 擁有最小的 native launcher:Codex 是
107
+ `.codex/hooks/agent-ops-loop.sh`;Claude Code 是
108
+ `.claude/hooks/agent-ops-loop.sh` 與 `.claude/hooks/agent-ops-loop.ps1`。兩個
109
+ Claude launcher 都委派給同一個已安裝的 Node runtime,因此不會複製
110
+ project-specific loop script。Windows 產生的 settings 會選用 PowerShell
111
+ launcher。Codex 只會在 `.codex/config.toml` 不存在時建立它。首次安裝會在不
105
112
  覆寫既有內容的前提下,於選定 harness directory 建立 `loop-goal.md`、
106
113
  `loop-state.md` 與 `loop-telemetry.jsonl`;並以 hash-commented `.gitignore`
107
114
  block 忽略這些 local file。
@@ -175,7 +182,6 @@ config v2 feature,必須明確啟用且至少提供一個已確認的 command
175
182
 
176
183
  ```bash
177
184
  agent-ops update
178
- agent-ops trust grant
179
185
  ```
180
186
 
181
187
  未執行 `update` 時,doctor 可因 registration drift 回報 `UPDATE_REQUIRED`。另
@@ -183,12 +189,12 @@ agent-ops trust grant
183
189
  path-independent managed rules artifact 改變時,`artifact-staleness` 會回報帶有
184
190
  `UPDATE_REQUIRED` 的 `DEGRADED`。`agent-ops update` 會重新產生 artifact 並清除
185
191
  這個結果;artifact 缺失或 hash 不符時,`artifacts` check 仍為 `FAIL`。未重新
186
- grant trust 時,trust-gated hook 仍會是 stale。
192
+ project update 經確認後,若有 verifier 會自動更新 stale trust binding
187
193
 
188
194
  doctor 從不寫入檔案,部分結果本來就無法修復:安裝根目錄以外的 surface,
189
195
  或 harness 依 descriptor 宣告只部分支援的 capability(例如 opencode 的
190
196
  `lifecycle-summary`),會永久回報 `UNKNOWN` 或 `DEGRADED` 且 exit 0。若 CI
191
- 需要自動修復,請直接呼叫 `agent-ops update` / `agent-ops trust grant`,
197
+ 需要自動修復,請直接呼叫 `agent-ops update`;也可手動呼叫 `agent-ops trust grant`,
192
198
  不要解析 doctor 的輸出。
193
199
 
194
200
  Stop 是 report-only:`PASS`、`FAIL`
@@ -6,8 +6,8 @@ Harness adapter 規則涵蓋 Codex、Claude Code 與 opencode。opencode 整合
6
6
  產生的 local plugin,不管理 `opencode.json`。
7
7
 
8
8
  Configuration 與 manifest 分開版本化。Config v1 會遷移為預設 disabled Stop
9
- verification 的 config v2;變更 capability 後必須先執行 `agent-ops update`,
10
- 再執行 `agent-ops trust grant`。Stop verification 必須明確啟用、具備 trust、
9
+ verification 的 config v2;變更 capability 後必須執行經確認的 project
10
+ `agent-ops update`,有 verifier 時會一併更新 trustStop verification 必須明確啟用、具備 trust、
11
11
  為 report-only,且永遠不會完成 task。Dry-run plan 會隱藏 foreign settings
12
12
  內容;routing migration 一旦套用即為單向。
13
13
 
@@ -72,8 +72,8 @@ Claude Code launcher 後使用同一個 shared runtime。它 MUST NOT 將 policy
72
72
  project-specific script,也不得改變一般 permission request。
73
73
 
74
74
  - Trigger: Project 以 Codex、Claude Code 或兩者選擇 `loop`。
75
- - Action: 只產生選定的 `.codex/hooks/agent-ops-loop.sh` 與/或
76
- `.claude/hooks/agent-ops-loop.sh` launcher,註冊文件化的 loop lifecycle event
75
+ - Action: 只產生選定的 `.codex/hooks/agent-ops-loop.sh` 與/或 Claude 的
76
+ `.claude/hooks/agent-ops-loop.sh`、`.claude/hooks/agent-ops-loop.ps1` launcher,註冊文件化的 loop lifecycle event
77
77
  (不含 `Stop`),並保留 foreign hook group。只在 `UserPromptSubmit` 或 Bash
78
78
  `PreToolUse` 的 high-confidence literal credential,以及 `PreToolUse` 的危險 Bash command 時,使用
79
79
  文件化的 native denial shape 進行 blocking。對 `PermissionRequest`(包括
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kylecheng3146/agent-ops",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "Evidence-driven development loops for Codex, Claude Code, and opencode",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,4 +1,6 @@
1
1
  ## Loop Engineering
2
2
 
3
- Load `.agent-ops/CLAUDE.md` as the agent-ops managed baseline.
3
+ @.agent-ops/CLAUDE.md
4
+
5
+ The file above is the agent-ops managed baseline.
4
6
  Project-specific instructions in this file remain authoritative.