@open-agent-toolkit/cli 0.3.16 → 0.3.18

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 (124) hide show
  1. package/assets/agents/oat-phase-implementer.md +26 -6
  2. package/assets/agents/oat-reviewer.md +51 -1
  3. package/assets/bundle-metadata.json +1 -1
  4. package/assets/docs/contributing/documentation.md +4 -0
  5. package/assets/docs/contributing/skills.md +3 -1
  6. package/assets/docs/getting-started/bootstrap.md +1 -1
  7. package/assets/docs/getting-started/index.md +0 -4
  8. package/assets/docs/getting-started/tool-packs.md +0 -7
  9. package/assets/docs/reference/cli-reference.md +42 -1
  10. package/assets/docs/reference/config-and-local-state.md +11 -2
  11. package/assets/docs/reference/project-artifacts.md +43 -8
  12. package/assets/docs/skills/repo-improve.md +9 -7
  13. package/assets/docs/workflows/advanced/autonomy.md +40 -0
  14. package/assets/docs/workflows/backlog-and-planning/backlog-lifecycle.md +25 -24
  15. package/assets/docs/workflows/backlog-and-planning/remote-project-management.md +0 -7
  16. package/assets/docs/workflows/ideas/lifecycle.md +2 -7
  17. package/assets/docs/workflows/projects/execution/project-log.md +22 -0
  18. package/assets/docs/workflows/projects/reviews/review-flavors.md +8 -0
  19. package/assets/public-package-versions.json +4 -4
  20. package/assets/skills/oat-agent-instructions-apply/SKILL.md +14 -4
  21. package/assets/skills/oat-brainstorm/SKILL.md +17 -13
  22. package/assets/skills/oat-docs-apply/SKILL.md +13 -3
  23. package/assets/skills/oat-doctor/SKILL.md +11 -1
  24. package/assets/skills/oat-explainer-kit/SKILL.md +1 -1
  25. package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +6 -3
  26. package/assets/skills/oat-pjm-review-backlog/SKILL.md +2 -2
  27. package/assets/skills/oat-pjm-update-repo-reference/SKILL.md +28 -3
  28. package/assets/skills/oat-project-autonomous/SKILL.md +60 -5
  29. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +44 -6
  30. package/assets/skills/oat-project-capture/SKILL.md +7 -3
  31. package/assets/skills/oat-project-complete/SKILL.md +39 -34
  32. package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +106 -28
  33. package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +30 -3
  34. package/assets/skills/oat-project-design/SKILL.md +12 -9
  35. package/assets/skills/oat-project-discover/SKILL.md +8 -4
  36. package/assets/skills/oat-project-document/SKILL.md +14 -5
  37. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +44 -6
  38. package/assets/skills/oat-project-implement/SKILL.md +1 -1
  39. package/assets/skills/oat-project-implement/references/completion-and-closeout.md +6 -6
  40. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +44 -6
  41. package/assets/skills/oat-project-implement/references/phase-execution.md +20 -6
  42. package/assets/skills/oat-project-import-plan/SKILL.md +20 -6
  43. package/assets/skills/oat-project-lite/SKILL.md +7 -5
  44. package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +44 -6
  45. package/assets/skills/oat-project-new/SKILL.md +6 -2
  46. package/assets/skills/oat-project-plan/SKILL.md +14 -6
  47. package/assets/skills/oat-project-pr-final/SKILL.md +1 -1
  48. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +44 -6
  49. package/assets/skills/oat-project-progress/SKILL.md +13 -1
  50. package/assets/skills/oat-project-promote-spec-driven/SKILL.md +7 -3
  51. package/assets/skills/oat-project-quick-start/SKILL.md +17 -12
  52. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +44 -6
  53. package/assets/skills/oat-project-reconcile/SKILL.md +8 -4
  54. package/assets/skills/oat-project-retro/SKILL.md +1 -1
  55. package/assets/skills/oat-project-retro/references/apply-procedure.md +6 -6
  56. package/assets/skills/oat-project-retro-file/SKILL.md +7 -3
  57. package/assets/skills/oat-project-review-provide/SKILL.md +23 -4
  58. package/assets/skills/oat-project-review-provide-remote/SKILL.md +19 -1
  59. package/assets/skills/oat-project-review-receive/SKILL.md +37 -12
  60. package/assets/skills/oat-project-review-receive-remote/SKILL.md +7 -3
  61. package/assets/skills/oat-project-revise/SKILL.md +13 -5
  62. package/assets/skills/oat-project-spec/SKILL.md +8 -4
  63. package/assets/skills/oat-project-summary/SKILL.md +11 -7
  64. package/assets/skills/oat-repo-knowledge-index/SKILL.md +54 -20
  65. package/assets/skills/oat-repo-knowledge-index/scripts/refresh-owned.mjs +118 -0
  66. package/assets/skills/oat-review-provide/SKILL.md +7 -3
  67. package/assets/skills/oat-review-receive-remote/SKILL.md +14 -4
  68. package/assets/skills/oat-wave-execute/SKILL.md +24 -11
  69. package/assets/skills/oat-wave-execute/assets/wrapper-plan-template.md +16 -2
  70. package/assets/skills/oat-worktree-bootstrap-auto/SKILL.md +45 -17
  71. package/assets/templates/pjm-agents.md +38 -8
  72. package/assets/templates/repo-agents.md +5 -0
  73. package/assets/templates/repo-readme.md +5 -0
  74. package/dist/commands/backlog/archive.d.ts +3 -0
  75. package/dist/commands/backlog/archive.d.ts.map +1 -1
  76. package/dist/commands/backlog/archive.js +63 -26
  77. package/dist/commands/backlog/index.d.ts.map +1 -1
  78. package/dist/commands/backlog/index.js +3 -0
  79. package/dist/commands/backlog/rewrite-references.d.ts +5 -0
  80. package/dist/commands/backlog/rewrite-references.d.ts.map +1 -1
  81. package/dist/commands/backlog/rewrite-references.js +39 -1
  82. package/dist/commands/init/index.d.ts.map +1 -1
  83. package/dist/commands/init/index.js +1 -0
  84. package/dist/commands/init/tools/shared/skill-script-references.d.ts.map +1 -1
  85. package/dist/commands/init/tools/shared/skill-script-references.js +8 -1
  86. package/dist/commands/internal/commit-paths.d.ts +3 -0
  87. package/dist/commands/internal/commit-paths.d.ts.map +1 -0
  88. package/dist/commands/internal/commit-paths.js +31 -0
  89. package/dist/commands/internal/index.d.ts.map +1 -1
  90. package/dist/commands/internal/index.js +2 -0
  91. package/dist/commands/pjm/init.d.ts.map +1 -1
  92. package/dist/commands/pjm/init.js +37 -8
  93. package/dist/commands/project/archive/archive-utils.d.ts +6 -3
  94. package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
  95. package/dist/commands/project/archive/archive-utils.js +510 -104
  96. package/dist/commands/project/archive/push-runner.d.ts.map +1 -1
  97. package/dist/commands/project/archive/push-runner.js +1 -0
  98. package/dist/commands/project/log/append.d.ts +2 -3
  99. package/dist/commands/project/log/append.d.ts.map +1 -1
  100. package/dist/commands/project/log/append.js +125 -111
  101. package/dist/commands/project/migrate/index.d.ts +1 -2
  102. package/dist/commands/project/migrate/index.d.ts.map +1 -1
  103. package/dist/commands/project/migrate/index.js +8 -2
  104. package/dist/commands/project/new/scaffold.d.ts.map +1 -1
  105. package/dist/commands/project/new/scaffold.js +4 -4
  106. package/dist/commands/project/promote/promote.d.ts +8 -0
  107. package/dist/commands/project/promote/promote.d.ts.map +1 -1
  108. package/dist/commands/project/promote/promote.js +37 -6
  109. package/dist/commands/project/status.d.ts.map +1 -1
  110. package/dist/commands/project/status.js +3 -0
  111. package/dist/commands/project/sync/ref-sync.d.ts +12 -1
  112. package/dist/commands/project/sync/ref-sync.d.ts.map +1 -1
  113. package/dist/commands/project/sync/ref-sync.js +347 -23
  114. package/dist/commands/shared/agents-md-lock.d.ts +3 -0
  115. package/dist/commands/shared/agents-md-lock.d.ts.map +1 -0
  116. package/dist/commands/shared/agents-md-lock.js +130 -0
  117. package/dist/commands/shared/agents-md.d.ts.map +1 -1
  118. package/dist/commands/shared/agents-md.js +26 -8
  119. package/dist/commands/shared/exact-path-commit.d.ts +32 -0
  120. package/dist/commands/shared/exact-path-commit.d.ts.map +1 -0
  121. package/dist/commands/shared/exact-path-commit.js +691 -0
  122. package/dist/validation/skills.d.ts.map +1 -1
  123. package/dist/validation/skills.js +138 -11
  124. package/package.json +3 -2
@@ -5,13 +5,17 @@ disable-model-invocation: false
5
5
  user-invocable: true
6
6
  allowed-tools: Read, Write, Bash(git:*), Bash(oat:*), Glob, Grep, AskUserQuestion, Task
7
7
  metadata:
8
- version: 1.6.8
8
+ version: 1.6.9
9
9
  ---
10
10
 
11
11
  # Receive Review
12
12
 
13
13
  Process review findings into actionable plan tasks and guide gap closure execution.
14
14
 
15
+ ## Hook-safe exact-path commits
16
+
17
+ Before a parent-branch commit, verify `oat internal commit-paths --help` succeeds. If unavailable, stop and update the OAT CLI; never fall back to a staged-index or pathspec-only commit. Set `COMMIT_IDENTITY` to a unique operation/artifact identity before the first attempt and retain it for every retry; use a new identity for a new operation. Pass only the exact produced file list, including tracked removals and both names of a rename. A blocked/failed result stops the workflow; retain its receipt and follow its diagnostics. Hooks remain enabled. Synced project artifacts continue through `oat project push`.
18
+
15
19
  ## Purpose
16
20
 
17
21
  Turn review output into plan changes and a clear next action. This closes the feedback loop between reviewing and fixing.
@@ -412,6 +416,11 @@ Derive `TASK_PREFIX` from scope:
412
416
 
413
417
  ### Step 5: Convert Findings to Tasks
414
418
 
419
+ Each generated fix task initializes `TASK_OWNED_FILES=()` before its first
420
+ edit and appends the exact files at their producers, including removals and
421
+ both rename sides. Carry that task-owned list and stable request/task identity
422
+ into its Commit step; never infer ownership from the staged index.
423
+
415
424
  **For each Critical, High, and Medium finding (default):**
416
425
 
417
426
  Create a plan task entry:
@@ -440,8 +449,8 @@ Expected: {expected outcome}
440
449
  **Step 4: Commit**
441
450
 
442
451
  ```bash
443
- git add {files}
444
- git commit -m "fix({task_id}): {description}"
452
+ OWNED_COMMIT_PATHS=("${TASK_OWNED_FILES[@]}")
453
+ oat internal commit-paths --identity "${COMMIT_IDENTITY:?set once and retain for retries}:oat-project-review-receive:{task_id}" --message "fix({task_id}): {description}" -- "${OWNED_COMMIT_PATHS[@]}" || exit 1
445
454
  ```
446
455
 
447
456
  Fix tasks that edit synced artifacts use `oat project push` under the scope
@@ -560,8 +569,17 @@ After the fix tasks are complete:
560
569
  After plan/implementation/state updates reference the archived location, move the processed review artifact into the local-only history directory:
561
570
 
562
571
  ```bash
572
+ REVIEW_ARCHIVE_PATHS=()
573
+ # Record a tracked source deletion before the move; an untracked source has no
574
+ # deletion to commit. The destination is included only when it is not ignored.
575
+ if git --literal-pathspecs ls-files --error-unmatch -- "$REVIEW_PATH" >/dev/null 2>&1; then
576
+ REVIEW_ARCHIVE_PATHS+=("$REVIEW_PATH")
577
+ fi
563
578
  mkdir -p "$ARCHIVED_REVIEW_DIR"
564
- mv "$REVIEW_PATH" "$ARCHIVED_REVIEW_PATH"
579
+ mv "$REVIEW_PATH" "$ARCHIVED_REVIEW_PATH" || exit 1
580
+ if ! git check-ignore -q -- "$ARCHIVED_REVIEW_PATH"; then
581
+ REVIEW_ARCHIVE_PATHS+=("$ARCHIVED_REVIEW_PATH")
582
+ fi
565
583
  ```
566
584
 
567
585
  Rules:
@@ -582,12 +600,10 @@ PROJECT_SCOPE=$(oat project scope "$PROJECT_PATH" --format value) || { echo "oat
582
600
  if [ "$PROJECT_SCOPE" = "synced" ]; then
583
601
  oat project push "$PROJECT_PATH" --message "chore(oat): record review findings and add fix tasks ({scope})" || { echo "oat: project push failed; run oat project pull, resolve the reported state, and retry" >&2; exit 1; }
584
602
  else
585
- git add "$PROJECT_PATH/plan.md" "$PROJECT_PATH/implementation.md" "$PROJECT_PATH/state.md"
586
- # Capture the Step 7.5 archive move: stages both the deletion of the original
587
- # review path and the new archived location. Scope to the project's reviews/
588
- # directory — never use repo-wide `git add -A`.
589
- git add "$PROJECT_PATH/reviews/"
590
- git diff --cached --quiet || git commit -m "chore(oat): record review findings and add fix tasks ({scope})"
603
+ OWNED_COMMIT_PATHS=("$PROJECT_PATH/plan.md" "$PROJECT_PATH/implementation.md" "$PROJECT_PATH/state.md")
604
+ # Consume the exact producer list retained from the Step 7.5 archive move.
605
+ OWNED_COMMIT_PATHS+=("${REVIEW_ARCHIVE_PATHS[@]}")
606
+ oat internal commit-paths --identity "${COMMIT_IDENTITY:?set once and retain for retries}:oat-project-review-receive:2" --message "chore(oat): record review findings and add fix tasks ({scope})" -- "${OWNED_COMMIT_PATHS[@]}" || exit 1
591
607
  fi
592
608
  ```
593
609
 
@@ -595,7 +611,7 @@ Do not use `git add -A` or glob patterns that reach outside `"$PROJECT_PATH/revi
595
611
 
596
612
  If the project itself is still untracked because earlier lifecycle steps never committed the initial artifact set, widen this bookkeeping commit to include the untracked core project artifacts (`discovery.md`, `spec.md`, `design.md`, `plan.md`, `implementation.md`, `state.md`). Do not stage `.oat/state.md`; it is generated dashboard state and normally gitignored. Do not leave the project tree partially tracked after receive-review finishes.
597
613
 
598
- **Note on archived review paths:** When `reviews/archived/` matches a `localPaths` pattern (the default setup), the archived file is gitignored and `git add "$PROJECT_PATH/reviews/"` will only stage the deletion of the original (now-moved) top-level review file. When `reviews/archived/` is tracked, both the deletion and the new archived location are staged. Both cases are safe — the command handles them uniformly.
614
+ **Note on archived review paths:** Retain `REVIEW_ARCHIVE_PATHS` across a retry. When `reviews/archived/` matches a `localPaths` pattern, include the old tracked review path only; when the destination is tracked, include both rename sides. Do not pass the reviews directory or ignored destination to the helper. A resumed operation uses the retained producer list and identity so the helper can verify a previously committed removal.
599
615
 
600
616
  **Worktree handling:** If the project was resolved via a worktree in Step 0, run the git commands scoped to the worktree (`git -C "$WORKTREE_PATH" ...`) so the commit lands on the worktree branch.
601
617
 
@@ -765,8 +781,17 @@ For `artifact` reviews, do not route to implementation tasks. After user-approve
765
781
  **Archive the consumed review artifact (same as Step 7.5):**
766
782
 
767
783
  ```bash
784
+ REVIEW_ARCHIVE_PATHS=()
785
+ # Record a tracked source deletion before the move; an untracked source has no
786
+ # deletion to commit. The destination is included only when it is not ignored.
787
+ if git --literal-pathspecs ls-files --error-unmatch -- "$REVIEW_PATH" >/dev/null 2>&1; then
788
+ REVIEW_ARCHIVE_PATHS+=("$REVIEW_PATH")
789
+ fi
768
790
  mkdir -p "$ARCHIVED_REVIEW_DIR"
769
- mv "$REVIEW_PATH" "$ARCHIVED_REVIEW_PATH"
791
+ mv "$REVIEW_PATH" "$ARCHIVED_REVIEW_PATH" || exit 1
792
+ if ! git check-ignore -q -- "$ARCHIVED_REVIEW_PATH"; then
793
+ REVIEW_ARCHIVE_PATHS+=("$ARCHIVED_REVIEW_PATH")
794
+ fi
770
795
  ```
771
796
 
772
797
  - Before the move, use the Step 1 `REVIEW_FILENAME` for every artifact-review reference or event identity written by the approved edits.
@@ -5,13 +5,17 @@ disable-model-invocation: true
5
5
  user-invocable: true
6
6
  allowed-tools: Read, Write, Bash, Glob, Grep, AskUserQuestion
7
7
  metadata:
8
- version: 1.5.3
8
+ version: 1.5.4
9
9
  ---
10
10
 
11
11
  # Project Remote Review Receive
12
12
 
13
13
  Fetch unresolved GitHub PR feedback and convert it into review-fix tasks inside the active OAT project.
14
14
 
15
+ ## Hook-safe exact-path commits
16
+
17
+ Before a parent-branch commit, verify `oat internal commit-paths --help` succeeds. If unavailable, stop and update the OAT CLI; never fall back to a staged-index or pathspec-only commit. Set `COMMIT_IDENTITY` to a unique operation/artifact identity before the first attempt and retain it for every retry; use a new identity for a new operation. Pass only the exact produced file list, including tracked removals and both names of a rename. A blocked/failed result stops the workflow; retain its receipt and follow its diagnostics. Hooks remain enabled. Synced project artifacts continue through `oat project push`.
18
+
15
19
  ## Prerequisites
16
20
 
17
21
  - Active OAT project with `plan.md` and `implementation.md`.
@@ -273,8 +277,8 @@ PROJECT_SCOPE=$(oat project scope "$PROJECT_PATH" --format value) || { echo "oat
273
277
  if [ "$PROJECT_SCOPE" = "synced" ]; then
274
278
  oat project push "$PROJECT_PATH" --message "chore(oat): record remote review findings and add fix tasks (pr-#$PR_NUMBER)" || { echo "oat: project push failed; run oat project pull, resolve the reported state, and retry" >&2; exit 1; }
275
279
  else
276
- git add "$PROJECT_PATH/plan.md" "$PROJECT_PATH/implementation.md" "$PROJECT_PATH/state.md" "$REMOTE_REVIEW_PATH"
277
- git diff --cached --quiet || git commit -m "chore(oat): record remote review findings and add fix tasks (pr-#$PR_NUMBER)"
280
+ OWNED_COMMIT_PATHS=("$PROJECT_PATH/plan.md" "$PROJECT_PATH/implementation.md" "$PROJECT_PATH/state.md" "$REMOTE_REVIEW_PATH")
281
+ oat internal commit-paths --identity "${COMMIT_IDENTITY:?set once and retain for retries}:oat-project-review-receive-remote:1" --message "chore(oat): record remote review findings and add fix tasks (pr-#$PR_NUMBER)" -- "${OWNED_COMMIT_PATHS[@]}" || exit 1
278
282
  fi
279
283
  ```
280
284
 
@@ -5,13 +5,17 @@ disable-model-invocation: true
5
5
  user-invocable: true
6
6
  allowed-tools: Read, Write, Bash(git:*), Glob, Grep, AskUserQuestion
7
7
  metadata:
8
- version: 1.0.4
8
+ version: 1.0.5
9
9
  ---
10
10
 
11
11
  # Project Revise
12
12
 
13
13
  Accept post-PR human feedback and create revision tasks without starting a new project.
14
14
 
15
+ ## Hook-safe exact-path commits
16
+
17
+ Before a parent-branch commit, verify `oat internal commit-paths --help` succeeds. If unavailable, stop and update the OAT CLI; never fall back to a staged-index or pathspec-only commit. Set `COMMIT_IDENTITY` to a unique operation/artifact identity before the first attempt and retain it for every retry; use a new identity for a new operation. Pass only the exact produced file list, including tracked removals and both names of a rename. A blocked/failed result stops the workflow; retain its receipt and follow its diagnostics. Hooks remain enabled. Synced project artifacts continue through `oat project push`.
18
+
15
19
  ## Purpose
16
20
 
17
21
  Provide a clean re-entry point for post-PR feedback. Routes inline feedback, GitHub PR comments, or review artifacts into revision tasks, manages state transitions (`pr_open` ↔ `in_progress`), and ensures agents understand the project is in revision mode — not done.
@@ -174,6 +178,10 @@ Source: inline feedback ({today})
174
178
 
175
179
  - Modify: `{file}`
176
180
 
181
+ Before the first edit, initialize `TASK_OWNED_FILES=()` and append each exact
182
+ created, modified or removed file at its producer, including both rename sides.
183
+ Retain the task list and its unique task identity for commit/retry.
184
+
177
185
  **Step 1:** {What to change}
178
186
 
179
187
  **Step 2: Verify**
@@ -183,8 +191,8 @@ Expected: {expected outcome}
183
191
  **Step 3: Commit**
184
192
 
185
193
  ```bash
186
- git add {files}
187
- git commit -m "fix(prev{N}-t01): {description}"
194
+ OWNED_COMMIT_PATHS=("${TASK_OWNED_FILES[@]}")
195
+ oat internal commit-paths --identity "${COMMIT_IDENTITY:?set once and retain for retries}:oat-project-revise:prev{N}-t01" --message "fix(prev{N}-t01): {description}" -- "${OWNED_COMMIT_PATHS[@]}" || exit 1
188
196
  ```
189
197
 
190
198
  Fix tasks that edit synced artifacts use `oat project push` under the scope
@@ -277,8 +285,8 @@ PROJECT_SCOPE=$(oat project scope "$PROJECT_PATH" --format value) || { echo "oat
277
285
  if [ "$PROJECT_SCOPE" = "synced" ]; then
278
286
  oat project push "$PROJECT_PATH" --message "chore(oat): create revision tasks for {project-name}" || { echo "oat: project push failed; run oat project pull, resolve the reported state, and retry" >&2; exit 1; }
279
287
  else
280
- git add "$PROJECT_PATH/plan.md" "$PROJECT_PATH/implementation.md" "$PROJECT_PATH/state.md"
281
- git diff --cached --quiet || git commit -m "chore(oat): create revision tasks for {project-name}"
288
+ OWNED_COMMIT_PATHS=("$PROJECT_PATH/plan.md" "$PROJECT_PATH/implementation.md" "$PROJECT_PATH/state.md")
289
+ oat internal commit-paths --identity "${COMMIT_IDENTITY:?set once and retain for retries}:oat-project-revise:2" --message "chore(oat): create revision tasks for {project-name}" -- "${OWNED_COMMIT_PATHS[@]}" || exit 1
282
290
  fi
283
291
  ```
284
292
 
@@ -5,13 +5,17 @@ disable-model-invocation: true
5
5
  user-invocable: true
6
6
  allowed-tools: Read, Write, Bash(git:*), Bash(oat template:*), Glob, Grep, AskUserQuestion
7
7
  metadata:
8
- version: 2.0.4
8
+ version: 2.0.5
9
9
  ---
10
10
 
11
11
  # Specification Phase
12
12
 
13
13
  Transform discovery insights into a formal specification with detailed requirements and acceptance criteria.
14
14
 
15
+ ## Hook-safe exact-path commits
16
+
17
+ Before a parent-branch commit, verify `oat internal commit-paths --help` succeeds. If unavailable, stop and update the OAT CLI; never fall back to a staged-index or pathspec-only commit. Set `COMMIT_IDENTITY` to a unique operation/artifact identity before the first attempt and retain it for every retry; use a new identity for a new operation. Pass only the exact produced file list, including tracked removals and both names of a rename. A blocked/failed result stops the workflow; retain its receipt and follow its diagnostics. Hooks remain enabled. Synced project artifacts continue through `oat project push`.
18
+
15
19
  ## Prerequisites
16
20
 
17
21
  **Required:** Complete discovery document. If missing, run the `oat-project-discover` skill first.
@@ -450,14 +454,14 @@ Requirements:
450
454
  Ready for design phase" || { echo "oat: project push failed; run oat project pull, resolve the reported state, and retry" >&2; exit 1; }
451
455
  else
452
456
  PROJECT_OUTPUT_PATHS=("$PROJECT_PATH/spec.md" "$PROJECT_PATH/state.md")
453
- git add -- "${PROJECT_OUTPUT_PATHS[@]}"
454
- git commit -m "docs: complete specification for {project-name}
457
+ OWNED_COMMIT_PATHS=("${PROJECT_OUTPUT_PATHS[@]}")
458
+ oat internal commit-paths --identity "${COMMIT_IDENTITY:?set once and retain for retries}:oat-project-spec:1" --message "docs: complete specification for {project-name}
455
459
 
456
460
  Requirements:
457
461
  - {N} functional requirements (P0: {n}, P1: {n}, P2: {n})
458
462
  - {N} non-functional requirements (P0: {n}, P1: {n}, P2: {n})
459
463
 
460
- Ready for design phase"
464
+ Ready for design phase" -- "${OWNED_COMMIT_PATHS[@]}" || exit 1
461
465
  fi
462
466
  ```
463
467
 
@@ -5,13 +5,17 @@ disable-model-invocation: false
5
5
  user-invocable: true
6
6
  allowed-tools: Read, Write, Bash(git:*), Bash(jq:*), Bash(oat config:*), Bash(oat decision:*), Bash(oat pjm:*), Bash(oat project log:*), Bash(oat project push:*), Bash(oat project scope:*), Bash(oat template:*), Bash(oat tools:*), Glob, Grep, AskUserQuestion
7
7
  metadata:
8
- version: 1.5.7
8
+ version: 1.5.8
9
9
  ---
10
10
 
11
11
  # Project Summary
12
12
 
13
13
  Generate a durable project summary artifact from project lifecycle artifacts.
14
14
 
15
+ ## Hook-safe exact-path commits
16
+
17
+ Before a parent-branch commit, verify `oat internal commit-paths --help` succeeds. If unavailable, stop and update the OAT CLI; never fall back to a staged-index or pathspec-only commit. Set `COMMIT_IDENTITY` to a unique operation/artifact identity before the first attempt and retain it for every retry; use a new identity for a new operation. Pass only the exact produced file list, including tracked removals and both names of a rename. A blocked/failed result stops the workflow; retain its receipt and follow its diagnostics. Hooks remain enabled. Synced project artifacts continue through `oat project push`.
18
+
15
19
  ## Purpose
16
20
 
17
21
  Produce a `summary.md` that serves as institutional memory — capturing what was built, why decisions were made, what tradeoffs occurred, and what follow-up work was identified. This artifact is distinct from the PR description: summary.md is reflective and thorough; PR descriptions are reviewer-oriented and actionable.
@@ -566,7 +570,7 @@ fi
566
570
 
567
571
  PARENT_DURABILITY_COMMIT=""
568
572
  if [ "$PROJECT_SCOPE" = "synced" ] && [ "${#PARENT_OUTPUT_PATHS[@]}" -gt 0 ]; then
569
- git add -- "${PARENT_OUTPUT_PATHS[@]}"
573
+ OWNED_COMMIT_PATHS=("${PARENT_OUTPUT_PATHS[@]}")
570
574
  if [ "$PROJECT_LOG_LEDGER_APPENDED" = "true" ] && [ "$DECISION_INDEX_CHANGED" = "true" ]; then
571
575
  PARENT_COMMIT_MESSAGE="docs: update project log ledger and decisions for {project-name}"
572
576
  elif [ "$PROJECT_LOG_LEDGER_APPENDED" = "true" ]; then
@@ -574,7 +578,7 @@ if [ "$PROJECT_SCOPE" = "synced" ] && [ "${#PARENT_OUTPUT_PATHS[@]}" -gt 0 ]; th
574
578
  else
575
579
  PARENT_COMMIT_MESSAGE="docs: promote summary decisions for {project-name}"
576
580
  fi
577
- git commit --only -m "$PARENT_COMMIT_MESSAGE" -- "${PARENT_OUTPUT_PATHS[@]}"
581
+ oat internal commit-paths --identity "${COMMIT_IDENTITY:?set once and retain for retries}:oat-project-summary:1" --message "$PARENT_COMMIT_MESSAGE" -- "${OWNED_COMMIT_PATHS[@]}" || exit 1
578
582
  PARENT_DURABILITY_COMMIT=$(git rev-parse HEAD)
579
583
  PARENT_COMMIT_PATHS=$(git diff-tree --no-commit-id --name-only -r "$PARENT_DURABILITY_COMMIT")
580
584
  # Inspect the complete output and require exact set equality with the
@@ -609,8 +613,8 @@ else
609
613
  if [ "${#PARENT_OUTPUT_PATHS[@]}" -gt 0 ]; then
610
614
  PROJECT_OUTPUT_PATHS+=("${PARENT_OUTPUT_PATHS[@]}")
611
615
  fi
612
- git add -- "${PROJECT_OUTPUT_PATHS[@]}"
613
- git commit --only -m "docs: generate summary for {project-name}" -- "${PROJECT_OUTPUT_PATHS[@]}"
616
+ OWNED_COMMIT_PATHS=("${PROJECT_OUTPUT_PATHS[@]}")
617
+ oat internal commit-paths --identity "${COMMIT_IDENTITY:?set once and retain for retries}:oat-project-summary:2" --message "docs: generate summary for {project-name}" -- "${OWNED_COMMIT_PATHS[@]}" || exit 1
614
618
  SUMMARY_COMMIT_SHA=$(git rev-parse HEAD)
615
619
  fi
616
620
 
@@ -639,8 +643,8 @@ if [ "$PROJECT_SCOPE" = "synced" ]; then
639
643
  SUMMARY_PUSH=$(oat project push "$PROJECT_PATH" --message "docs: update summary for {project-name}" --json) || { echo "oat: project push failed; run oat project pull, resolve the reported state, and retry" >&2; exit 1; }
640
644
  SUMMARY_COMMIT_SHA=$(parse_synced_push_receipt "$SUMMARY_PUSH") || { printf '%s\n' "$SUMMARY_PUSH" >&2; echo "Recovery: run oat project pull \"$PROJECT_PATH\", resolve conflicts, then retry this push." >&2; exit 1; }
641
645
  else
642
- git add -- "${PROJECT_OUTPUT_PATHS[@]}"
643
- git commit --only -m "docs: update summary for {project-name}" -- "${PROJECT_OUTPUT_PATHS[@]}"
646
+ OWNED_COMMIT_PATHS=("${PROJECT_OUTPUT_PATHS[@]}")
647
+ oat internal commit-paths --identity "${COMMIT_IDENTITY:?set once and retain for retries}:oat-project-summary:3" --message "docs: update summary for {project-name}" -- "${OWNED_COMMIT_PATHS[@]}" || exit 1
644
648
  SUMMARY_COMMIT_SHA=$(git rev-parse HEAD)
645
649
  fi
646
650
  ```
@@ -5,7 +5,7 @@ disable-model-invocation: true
5
5
  user-invocable: true
6
6
  allowed-tools: Read, Write, Bash(git:*), Glob, Grep, AskUserQuestion, Task
7
7
  metadata:
8
- version: 1.3.2
8
+ version: 1.3.3
9
9
  ---
10
10
 
11
11
  # Knowledge Base Generation
@@ -42,20 +42,39 @@ EXISTING_MD=$(find .oat/repo/knowledge -name "*.md" -type f 2>/dev/null | head -
42
42
  **If `$EXISTING_MD` is non-empty (actual content exists):**
43
43
 
44
44
  - List current files: `ls -la .oat/repo/knowledge/*.md 2>/dev/null`
45
- - Ask: "Refresh (delete + regenerate) or Skip?"
46
- - If Refresh: `rm -rf .oat/repo/knowledge/*.md && mkdir -p .oat/repo/knowledge`
45
+ - Ask: "Refresh generated knowledge or Skip? Hand-written files will be kept; unmarked output-name collisions stop refresh."
46
+ - If Refresh: Continue to the owned-output preflight in Step 2; do not delete files by a wildcard.
47
47
  - If Skip: Exit
48
48
 
49
49
  **If `$EXISTING_MD` is empty (no content or only .gitkeep):**
50
50
 
51
51
  - Continue to Step 2
52
52
 
53
- ### Step 2: Create Knowledge Directory
53
+ ### Step 2: Prepare Only Owned Generated Outputs
54
+
55
+ Set `SKILL_DIR` to the absolute directory containing this loaded `SKILL.md`.
56
+ Resolve the repository root, then run the deterministic ownership preflight
57
+ before thin-index generation or mapper writes. It checks every expected
58
+ output name before deleting anything: an unmarked file, symlink or directory
59
+ collision stops the run and remains intact. Only regular Markdown files with
60
+ frontmatter `oat_generated: true` are removed; other files and manual notes
61
+ survive. Symlinked directories in `.oat/repo/knowledge` are refused.
54
62
 
55
63
  ```bash
56
- mkdir -p .oat/repo/knowledge
64
+ REPO_ROOT=$(git rev-parse --show-toplevel) || exit 1
65
+ node "$SKILL_DIR/scripts/refresh-owned.mjs" --prepare "$REPO_ROOT" || exit 1
57
66
  ```
58
67
 
68
+ Retain the printed JSON report's exact `affectedPaths` (removed generated paths
69
+ plus the eight expected outputs) through generation and commit, including
70
+ across separate shell calls. The report is operation evidence, not a persistent
71
+ ownership manifest; no temporary report file is created. Do not broaden
72
+ ownership to the whole directory. Expected outputs are `project-index.md`,
73
+ `stack.md`, `architecture.md`, `structure.md`, `integrations.md`, `testing.md`,
74
+ `conventions.md` and `concerns.md`; mappers must write only their declared
75
+ outputs, retaining the generated frontmatter. Relocate an authored collision
76
+ only with the operator's direction, then rerun the preflight.
77
+
59
78
  ### Step 3: Get Git SHAs for Frontmatter
60
79
 
61
80
  ```bash
@@ -655,23 +674,38 @@ cat .oat/repo/knowledge/project-index.md | head -50
655
674
 
656
675
  Expected: Complete overview with frontmatter and links
657
676
 
658
- ### Step 10: Commit Knowledge Base
677
+ ### Step 10: Commit Only Generated Knowledge Paths
678
+
679
+ Verify all eight generated outputs, then format their existing supported text
680
+ using the repository's documented write command. Retain removed tracked
681
+ generated paths from the preparation report, including filenames outside the
682
+ current eight-output set. Resolve the repository root again with
683
+ `git rev-parse --show-toplevel`, then convert the report's absolute paths to
684
+ literal repository-relative files using that root, deduplicate them, and populate
685
+ the next shell call's positional arguments; the snippet below initializes
686
+ `KNOWLEDGE_OWNED_FILES` from that retained exact array. Resolve `SKILL_DIR` again
687
+ in each shell call rather than relying on prior shell variables. Omit a removed
688
+ path only if Git proves it was never tracked; never omit a tracked generated
689
+ deletion. Do not add manual files,
690
+ collisions or the tracking/dashboard files below to this commit.
659
691
 
660
692
  ```bash
661
- git add .oat/repo/knowledge/
662
- git commit -m "docs: generate knowledge base
663
-
664
- - project-index.md - High-level codebase overview
665
- - stack.md - Technologies and dependencies
666
- - architecture.md - System design and patterns
667
- - structure.md - Directory layout
668
- - conventions.md - Code style and patterns
669
- - testing.md - Test structure and practices
670
- - integrations.md - External services and APIs
671
- - concerns.md - Technical debt and issues
672
-
673
- Generated from commit: {MERGE_BASE_SHA}"
674
- ```
693
+ REPO_ROOT=$(git rev-parse --show-toplevel) || exit 1
694
+ KNOWLEDGE_OWNED_FILES=("$@")
695
+ [ "${#KNOWLEDGE_OWNED_FILES[@]}" -gt 0 ] || { echo "Missing retained knowledge affectedPaths; refusing to commit" >&2; exit 1; }
696
+ node "$SKILL_DIR/scripts/refresh-owned.mjs" --verify "$REPO_ROOT" || exit 1
697
+ oat internal commit-paths --help || exit 1
698
+ # KNOWLEDGE_OWNED_FILES is the exact retained affectedPaths list after verification.
699
+ # Format existing generated text members using the repository write command.
700
+ oat internal commit-paths --identity "${COMMIT_IDENTITY:?set once and retain for retries}:knowledge-index" --message "docs: generate knowledge base from $MERGE_BASE_SHA" -- "${KNOWLEDGE_OWNED_FILES[@]}" || exit 1
701
+ ```
702
+
703
+ Set a unique `COMMIT_IDENTITY` before the first commit attempt and retain the
704
+ identity, message and literal file list for retries. If the shared command is
705
+ unavailable, stop and update the CLI; a blocked/failed result stops this run
706
+ and retains its receipt for the reported recovery. Hooks stay enabled;
707
+ unrelated staged entries and unstaged bytes remain preserved. Verify the
708
+ owned paths match the committed hook-final content before proceeding.
675
709
 
676
710
  ### Step 10b: Update Tracking Manifest
677
711
 
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ lstat,
4
+ mkdir,
5
+ readdir,
6
+ readFile,
7
+ realpath,
8
+ unlink,
9
+ } from 'node:fs/promises';
10
+ import { join, resolve } from 'node:path';
11
+ import { fileURLToPath } from 'node:url';
12
+
13
+ export const GENERATED_OUTPUTS = Object.freeze([
14
+ 'project-index.md',
15
+ 'stack.md',
16
+ 'architecture.md',
17
+ 'structure.md',
18
+ 'integrations.md',
19
+ 'testing.md',
20
+ 'conventions.md',
21
+ 'concerns.md',
22
+ ]);
23
+
24
+ function generated(content) {
25
+ const frontmatter = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/u.exec(content);
26
+ const markers = frontmatter?.[1].match(/^oat_generated:.*$/gmu) ?? [];
27
+ return (
28
+ markers.length === 1 &&
29
+ /^oat_generated:[ \t]*true[ \t]*(?:#[^\r\n]*)?\r?$/u.test(markers[0])
30
+ );
31
+ }
32
+
33
+ async function knowledgeDirectory(repoRoot, create) {
34
+ const root = await realpath(repoRoot);
35
+ let path = root;
36
+ // Only this repo's known knowledge directory is owned. Never follow a
37
+ // symlink in the .oat/repo/knowledge directory chain.
38
+ for (const component of ['.oat', 'repo', 'knowledge']) {
39
+ path = join(path, component);
40
+ if (create) {
41
+ await mkdir(path).catch((error) => {
42
+ if (error.code !== 'EEXIST') throw error;
43
+ });
44
+ }
45
+ const identity = await lstat(path);
46
+ if (!identity.isDirectory() || identity.isSymbolicLink()) {
47
+ throw new Error(`Knowledge path is not an owned directory: ${path}`);
48
+ }
49
+ }
50
+ return path;
51
+ }
52
+
53
+ /** Validate every collision before deleting only explicitly generated files. */
54
+ export async function prepareOwnedRefresh(repoRoot) {
55
+ const directory = await knowledgeDirectory(repoRoot, true);
56
+ const entries = await readdir(directory, { withFileTypes: true });
57
+ const owned = [];
58
+ for (const entry of entries) {
59
+ const expected = GENERATED_OUTPUTS.includes(entry.name);
60
+ if (!expected && (!entry.isFile() || !entry.name.endsWith('.md'))) continue;
61
+ const path = join(directory, entry.name);
62
+ const marked = entry.isFile() && generated(await readFile(path, 'utf8'));
63
+ if (expected && !marked) {
64
+ throw new Error(
65
+ `Unmarked knowledge output collision: ${path}; preserve or relocate the authored file before refreshing.`,
66
+ );
67
+ }
68
+ if (marked) owned.push(path);
69
+ }
70
+ // Preflight above is all-or-nothing: collisions leave every old file intact.
71
+ for (const path of owned.sort()) await unlink(path);
72
+ const outputPaths = GENERATED_OUTPUTS.map((name) => join(directory, name));
73
+ return {
74
+ removedPaths: owned,
75
+ outputPaths,
76
+ affectedPaths: [...new Set([...owned, ...outputPaths])].sort(),
77
+ };
78
+ }
79
+
80
+ /** Run after generation and before formatting/committing the retained list. */
81
+ export async function verifyOwnedOutputs(repoRoot) {
82
+ const directory = await knowledgeDirectory(repoRoot, false);
83
+ const outputPaths = GENERATED_OUTPUTS.map((name) => join(directory, name));
84
+ for (const path of outputPaths) {
85
+ const identity = await lstat(path);
86
+ if (
87
+ !identity.isFile() ||
88
+ identity.isSymbolicLink() ||
89
+ !generated(await readFile(path, 'utf8'))
90
+ ) {
91
+ throw new Error(`Expected marked generated knowledge output: ${path}`);
92
+ }
93
+ }
94
+ return { outputPaths };
95
+ }
96
+
97
+ if (
98
+ process.argv[1] &&
99
+ (await realpath(process.argv[1]).catch(() => resolve(process.argv[1]))) ===
100
+ fileURLToPath(import.meta.url)
101
+ ) {
102
+ try {
103
+ const [mode, root, ...extra] = process.argv.slice(2);
104
+ if (!root || extra.length || !['--prepare', '--verify'].includes(mode)) {
105
+ throw new Error(
106
+ 'Usage: refresh-owned.mjs <--prepare|--verify> <repo-root>',
107
+ );
108
+ }
109
+ const result =
110
+ mode === '--prepare'
111
+ ? await prepareOwnedRefresh(root)
112
+ : await verifyOwnedOutputs(root);
113
+ process.stdout.write(`${JSON.stringify(result)}\n`);
114
+ } catch (error) {
115
+ process.stderr.write(`${error.message}\n`);
116
+ process.exitCode = 1;
117
+ }
118
+ }
@@ -6,13 +6,17 @@ disable-model-invocation: true
6
6
  user-invocable: true
7
7
  allowed-tools: Read, Write, Bash, Glob, Grep, AskUserQuestion
8
8
  metadata:
9
- version: 1.2.5
9
+ version: 1.2.6
10
10
  ---
11
11
 
12
12
  # Ad-Hoc Review
13
13
 
14
14
  Request and execute a code/file review that is not tied to an OAT project lifecycle.
15
15
 
16
+ ## Hook-safe exact-path commits
17
+
18
+ Before a parent-branch commit, verify `oat internal commit-paths --help` succeeds. If unavailable, stop and update the OAT CLI; never fall back to a staged-index or pathspec-only commit. Set `COMMIT_IDENTITY` to a unique operation/artifact identity before the first attempt and retain it for every retry; use a new identity for a new operation. Pass only the exact produced file list, including tracked removals and both names of a rename. A blocked/failed result stops the workflow; retain its receipt and follow its diagnostics. Hooks remain enabled. Synced project artifacts continue through `oat project push`.
19
+
16
20
  ## Prerequisites
17
21
 
18
22
  - Git repository with changes/files to review.
@@ -207,8 +211,8 @@ If artifact is in tracked storage (e.g., `.oat/repo/reviews`), ask whether to co
207
211
  Suggested commit (when approved):
208
212
 
209
213
  ```bash
210
- git add "{artifact-path}"
211
- git commit -m "chore(oat): record ad-hoc review artifact"
214
+ OWNED_COMMIT_PATHS=("{artifact-path}")
215
+ oat internal commit-paths --identity "${COMMIT_IDENTITY:?set once and retain for retries}:oat-review-provide:1" --message "chore(oat): record ad-hoc review artifact" -- "${OWNED_COMMIT_PATHS[@]}" || exit 1
212
216
  ```
213
217
 
214
218
  For local-active or inline modes, do not commit unless user explicitly requests.
@@ -5,13 +5,17 @@ disable-model-invocation: true
5
5
  user-invocable: true
6
6
  allowed-tools: Read, Write, Bash, Glob, Grep, AskUserQuestion
7
7
  metadata:
8
- version: 1.3.2
8
+ version: 1.3.3
9
9
  ---
10
10
 
11
11
  # Remote Review Receive (Ad-hoc GitHub PR)
12
12
 
13
13
  Process unresolved GitHub PR review feedback into normalized findings and standalone tasks without requiring an active OAT project.
14
14
 
15
+ ## Hook-safe exact-path commits
16
+
17
+ Before a parent-branch commit, verify `oat internal commit-paths --help` succeeds. If unavailable, stop and update the OAT CLI; never fall back to a staged-index or pathspec-only commit. Set `COMMIT_IDENTITY` to a unique operation/artifact identity before the first attempt and retain it for every retry; use a new identity for a new operation. Pass only the exact produced file list, including tracked removals and both names of a rename. A blocked/failed result stops the workflow; retain its receipt and follow its diagnostics. Hooks remain enabled. Synced project artifacts continue through `oat project push`.
18
+
15
19
  ## Prerequisites
16
20
 
17
21
  - `npx agent-reviews` is available.
@@ -172,6 +176,12 @@ Also output deferred and dismissed lists with reasons.
172
176
 
173
177
  ### Step 6: Optional Fix Implementation
174
178
 
179
+ Before the first approved write, initialize `OWNED_OUTPUT_FILES=()`. Append
180
+ each exact successfully created, modified or removed path at that write,
181
+ including both names of a rename and any generated navigation/index file this
182
+ step actually changes. Retain the complete list through verification and commit
183
+ retries; do not substitute a directory or staged-index diff.
184
+
175
185
  After the task list is confirmed, ask:
176
186
 
177
187
  `Would you like me to address these fixes, commit, and push the changes? [yes/no]`
@@ -183,10 +193,10 @@ If yes:
183
193
  3. Stage and commit with a descriptive message referencing the PR:
184
194
 
185
195
  ```bash
186
- git add {changed-files}
187
- git commit -m "fix: address PR #<N> review feedback
196
+ OWNED_COMMIT_PATHS=("${OWNED_OUTPUT_FILES[@]}")
197
+ oat internal commit-paths --identity "${COMMIT_IDENTITY:?set once and retain for retries}:oat-review-receive-remote:1" --message "fix: address PR #<N> review feedback
188
198
 
189
- Resolved findings: {list of finding IDs and titles}"
199
+ Resolved findings: {list of finding IDs and titles}" -- "${OWNED_COMMIT_PATHS[@]}" || exit 1
190
200
  ```
191
201
 
192
202
  4. Push to the current branch: