@open-agent-toolkit/cli 0.2.24 → 0.2.25

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 (32) hide show
  1. package/assets/agents/oat-reviewer.md +12 -1
  2. package/assets/docs/cli-utilities/configuration.md +2 -4
  3. package/assets/docs/reference/cli-reference.md +1 -1
  4. package/assets/docs/workflows/projects/reviews.md +19 -10
  5. package/assets/public-package-versions.json +4 -4
  6. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +3 -3
  7. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +3 -3
  8. package/assets/skills/oat-project-implement/SKILL.md +1 -1
  9. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +3 -3
  10. package/assets/skills/oat-project-implement/references/phase-execution.md +29 -0
  11. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +3 -3
  12. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +3 -3
  13. package/assets/skills/oat-project-review-provide/SKILL.md +124 -26
  14. package/assets/skills/oat-project-review-provide-remote/SKILL.md +64 -15
  15. package/assets/skills/oat-project-review-receive/SKILL.md +21 -1
  16. package/assets/skills/oat-project-review-receive-remote/SKILL.md +39 -2
  17. package/assets/skills/oat-review-provide-remote/SKILL.md +55 -12
  18. package/assets/templates/plan.md +13 -7
  19. package/dist/commands/config/index.js +8 -8
  20. package/dist/commands/review/latest.d.ts.map +1 -1
  21. package/dist/commands/review/latest.js +22 -9
  22. package/dist/config/resolve.js +1 -1
  23. package/dist/review-remote/body-builder.d.ts +12 -3
  24. package/dist/review-remote/body-builder.d.ts.map +1 -1
  25. package/dist/review-remote/body-builder.js +11 -0
  26. package/dist/review-remote/marker-parser.d.ts +8 -3
  27. package/dist/review-remote/marker-parser.d.ts.map +1 -1
  28. package/dist/review-remote/marker-parser.js +13 -2
  29. package/dist/review-remote/narrowing.d.ts +44 -8
  30. package/dist/review-remote/narrowing.d.ts.map +1 -1
  31. package/dist/review-remote/narrowing.js +106 -10
  32. package/package.json +2 -2
@@ -9,16 +9,85 @@
9
9
  * range, so a rebased/force-pushed/shallow prior SHA never produces a
10
10
  * misleading partial range.
11
11
  */
12
- /** Does a prior review match the current `(rail, project, scope)` tuple? */
12
+ const FULL_COMMIT_SHA = /^[0-9a-f]{40}$/i;
13
+ /**
14
+ * Normalize one parsed GitHub marker into a narrowing candidate. Unknown
15
+ * invocation and target-less gate markers preserve their metadata but carry no
16
+ * lineage, so tuple matching rejects them and fails open.
17
+ */
18
+ export function priorReviewFromMarker(marker, submittedAt) {
19
+ const lineage = markerLineage(marker);
20
+ return {
21
+ headSha: marker.oat_review_head_sha,
22
+ scope: marker.oat_review_scope,
23
+ ...(marker.oat_project ? { project: marker.oat_project } : {}),
24
+ ...(marker.oat_review_invocation
25
+ ? { invocation: marker.oat_review_invocation }
26
+ : {}),
27
+ ...(lineage ? { lineage } : {}),
28
+ submittedAt,
29
+ };
30
+ }
31
+ function markerLineage(marker) {
32
+ if (marker.oat_review_invocation === 'manual' ||
33
+ marker.oat_review_invocation === 'auto') {
34
+ return { kind: 'lifecycle' };
35
+ }
36
+ const gateTarget = marker.oat_gate_target?.trim();
37
+ return marker.oat_review_invocation === 'gate' && gateTarget
38
+ ? { kind: 'gate', target: gateTarget }
39
+ : undefined;
40
+ }
41
+ /**
42
+ * Normalize durable ledger provenance into the same candidate shape used by
43
+ * artifact-sourced reviews. Both sources then pass through {@link matchesTuple}
44
+ * and cannot drift into separate lineage predicates.
45
+ */
46
+ export function priorReviewFromLedger(row) {
47
+ const invocation = isReviewInvocationKind(row.invocation)
48
+ ? row.invocation
49
+ : undefined;
50
+ const lineage = ledgerLineage(row);
51
+ return {
52
+ headSha: row.reviewedHead,
53
+ scope: row.scope,
54
+ ...(row.project ? { project: row.project } : {}),
55
+ ...(invocation ? { invocation } : {}),
56
+ ...(lineage ? { lineage } : {}),
57
+ submittedAt: row.submittedAt,
58
+ };
59
+ }
60
+ function isReviewInvocationKind(invocation) {
61
+ return (invocation === 'manual' || invocation === 'auto' || invocation === 'gate');
62
+ }
63
+ function ledgerLineage(row) {
64
+ if (row.invocation === 'manual' || row.invocation === 'auto') {
65
+ return { kind: 'lifecycle' };
66
+ }
67
+ const gateTarget = row.gateTarget?.trim();
68
+ return row.invocation === 'gate' && gateTarget
69
+ ? { kind: 'gate', target: gateTarget }
70
+ : undefined;
71
+ }
72
+ /** Does a prior review match the current rail, project, scope, and lineage? */
13
73
  function matchesTuple(review, input) {
74
+ if (!FULL_COMMIT_SHA.test(review.headSha)) {
75
+ return false;
76
+ }
77
+ const sameLineage = review.lineage !== undefined &&
78
+ review.lineage.kind === input.lineage.kind &&
79
+ (input.lineage.kind === 'lifecycle' ||
80
+ (review.lineage.kind === 'gate' &&
81
+ review.lineage.target === input.lineage.target));
14
82
  if (input.rail === 'ad-hoc') {
15
83
  // Ad-hoc: scope sentinel must match AND there must be no project key.
16
- return review.scope === 'ad-hoc' && review.project === undefined;
84
+ return (review.scope === 'ad-hoc' && review.project === undefined && sameLineage);
17
85
  }
18
86
  // Project rail: same project AND same scope.
19
87
  return (review.project !== undefined &&
20
88
  review.project === input.project &&
21
- review.scope === input.scope);
89
+ review.scope === input.scope &&
90
+ sameLineage);
22
91
  }
23
92
  function mostRecentMatch(input) {
24
93
  const matches = input.reviews.filter((r) => matchesTuple(r, input));
@@ -48,16 +117,47 @@ async function guardPasses(priorSha, input) {
48
117
  }
49
118
  return input.git.isAncestor(priorSha, input.headSha);
50
119
  }
120
+ async function classifyRange(priorSha, input) {
121
+ let files;
122
+ try {
123
+ files = await input.git.changedFiles(priorSha, input.headSha);
124
+ }
125
+ catch {
126
+ // Classification is reporting-only, so enumeration failure must not block
127
+ // dispatch. Treat unknown work as substantive and disclose the fallback.
128
+ return {
129
+ classification: 'substantive',
130
+ classificationReason: 'changed-files-unavailable',
131
+ };
132
+ }
133
+ if (files.length === 0) {
134
+ return { classification: 'empty' };
135
+ }
136
+ const projectPrefix = input.project === null ? null : `${input.project.replace(/\/+$/, '')}/`;
137
+ // Path-only classification is sufficient while it is reporting-only. If it
138
+ // ever authorizes skipping work, strengthen it with lifecycle corroboration.
139
+ return {
140
+ classification: projectPrefix !== null &&
141
+ files.every((file) => file.startsWith(projectPrefix))
142
+ ? 'bookkeeping-only'
143
+ : 'substantive',
144
+ };
145
+ }
51
146
  /**
52
147
  * Pick the narrowing target for the current re-review pass.
53
148
  */
54
149
  export async function pickNarrowingTarget(input) {
150
+ if (input.narrowingPreference === false && !input.forceNarrow) {
151
+ return {
152
+ kind: 'full-scope-fallback',
153
+ reason: 'narrowing-disabled',
154
+ };
155
+ }
55
156
  const prior = mostRecentMatch(input);
56
157
  if (!prior) {
57
158
  return {
58
159
  kind: 'full-scope-fallback',
59
160
  reason: 'no-prior-review',
60
- prompted: false,
61
161
  };
62
162
  }
63
163
  const passed = await guardPasses(prior.headSha, input);
@@ -66,8 +166,7 @@ export async function pickNarrowingTarget(input) {
66
166
  kind: 'narrow-range',
67
167
  priorSha: prior.headSha,
68
168
  headSha: input.headSha,
69
- // Auto-narrow skips the prompt; otherwise the caller still confirms.
70
- prompted: !input.autoNarrow,
169
+ ...(await classifyRange(prior.headSha, input)),
71
170
  };
72
171
  }
73
172
  // Guard failed. The user explicitly asked to narrow → hard error.
@@ -78,13 +177,10 @@ export async function pickNarrowingTarget(input) {
78
177
  priorSha: prior.headSha,
79
178
  };
80
179
  }
81
- // Otherwise fall back to full scope. Auto-narrow surfaces this as the
82
- // auto-fallback notice (no prompt); manual mode does not prompt here either —
83
- // the fallback is informational.
180
+ // Otherwise fail open to full scope and preserve the guard failure reason.
84
181
  return {
85
182
  kind: 'full-scope-fallback',
86
183
  reason: 'stale-sha',
87
184
  priorSha: prior.headSha,
88
- prompted: false,
89
185
  };
90
186
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-agent-toolkit/cli",
3
- "version": "0.2.24",
3
+ "version": "0.2.25",
4
4
  "private": false,
5
5
  "description": "Open Agent Toolkit CLI",
6
6
  "homepage": "https://github.com/voxmedia/open-agent-toolkit/tree/main/packages/cli",
@@ -34,7 +34,7 @@
34
34
  "ora": "^9.0.0",
35
35
  "yaml": "2.8.2",
36
36
  "zod": "^3.25.76",
37
- "@open-agent-toolkit/control-plane": "0.2.24"
37
+ "@open-agent-toolkit/control-plane": "0.2.25"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/node": "^22.10.0",