@open-agent-toolkit/cli 0.0.27 → 0.0.29

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/docs/cli-utilities/config-and-local-state.md +2 -1
  2. package/assets/docs/cli-utilities/configuration.md +111 -0
  3. package/assets/docs/cli-utilities/tool-packs.md +1 -1
  4. package/assets/docs/reference/cli-reference.md +25 -0
  5. package/assets/docs/reference/file-locations.md +2 -1
  6. package/assets/docs/reference/oat-directory-structure.md +1 -0
  7. package/assets/docs/workflows/projects/hill-checkpoints.md +17 -0
  8. package/assets/docs/workflows/projects/lifecycle.md +6 -0
  9. package/assets/docs/workflows/projects/reviews.md +24 -0
  10. package/assets/docs/workflows/skills/index.md +2 -0
  11. package/assets/public-package-versions.json +4 -4
  12. package/assets/skills/oat-project-complete/SKILL.md +20 -1
  13. package/assets/skills/oat-project-implement/SKILL.md +85 -4
  14. package/assets/skills/oat-project-next/SKILL.md +2 -2
  15. package/assets/skills/oat-project-review-provide/SKILL.md +12 -2
  16. package/assets/skills/oat-project-review-receive/SKILL.md +23 -1
  17. package/assets/skills/oat-project-review-receive-remote/SKILL.md +17 -1
  18. package/assets/skills/oat-wrap-up/SKILL.md +417 -0
  19. package/assets/skills/oat-wrap-up/references/automation-recipes.md +100 -0
  20. package/assets/skills/oat-wrap-up/references/report-template.md +90 -0
  21. package/dist/commands/config/index.d.ts +5 -1
  22. package/dist/commands/config/index.d.ts.map +1 -1
  23. package/dist/commands/config/index.js +283 -140
  24. package/dist/commands/init/tools/shared/skill-manifest.d.ts +1 -1
  25. package/dist/commands/init/tools/shared/skill-manifest.d.ts.map +1 -1
  26. package/dist/commands/init/tools/shared/skill-manifest.js +1 -0
  27. package/dist/config/oat-config.d.ts +15 -0
  28. package/dist/config/oat-config.d.ts.map +1 -1
  29. package/dist/config/oat-config.js +54 -0
  30. package/dist/config/resolve.d.ts.map +1 -1
  31. package/dist/config/resolve.js +21 -0
  32. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: oat-project-review-receive-remote
3
- version: 1.2.0
3
+ version: 1.3.0
4
4
  description: Use when processing GitHub PR review comments within project context. Fetches PR comments, creates plan tasks, and updates project artifacts.
5
5
  disable-model-invocation: true
6
6
  user-invocable: true
@@ -190,6 +190,21 @@ Update `state.md`:
190
190
  - `oat_current_task: <first-new-task-id|null>`
191
191
  - `oat_project_state_updated: "{ISO 8601 UTC timestamp}"`
192
192
 
193
+ ### Step 6.5: Commit Review Bookkeeping (Required)
194
+
195
+ **CRITICAL — DO NOT SKIP.** This skill modifies `plan.md`, `implementation.md`, and `state.md` when processing GitHub PR comments. When it runs in a separate agent session (subagent, fresh session, or different conversation), uncommitted bookkeeping updates cause state drift for the original agent. The commit below is the safety net.
196
+
197
+ Commit all modified OAT tracking files atomically:
198
+
199
+ ```bash
200
+ git add "$PROJECT_PATH/plan.md" "$PROJECT_PATH/implementation.md" "$PROJECT_PATH/state.md"
201
+ git diff --cached --quiet || git commit -m "chore(oat): record remote review findings and add fix tasks (pr-#$PR_NUMBER)"
202
+ ```
203
+
204
+ Do not use `git add -A` or glob patterns. Do not include unrelated implementation or code files. Do not defer this commit without explicit user approval.
205
+
206
+ **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.
207
+
193
208
  ### Step 7: Enforce Review Cycle Limit and Route Next Action
194
209
 
195
210
  Track review cycles for the same scope.
@@ -232,4 +247,5 @@ At completion, report:
232
247
  - Findings triaged with explicit dispositions.
233
248
  - Plan tasks created with stable IDs when needed.
234
249
  - `plan.md`, `implementation.md`, and `state.md` updated consistently.
250
+ - All artifact updates committed atomically before the skill exits to prevent cross-session drift.
235
251
  - Review cycle guard and next-action routing applied.
@@ -0,0 +1,417 @@
1
+ ---
2
+ name: oat-wrap-up
3
+ version: 1.0.0
4
+ description: Use when preparing a shipping digest or weekly/biweekly wrap-up summarizing OAT projects and merged PRs over a time window. Reads local summary files and GitHub PR metadata; writes a version-controlled markdown report.
5
+ argument-hint: '[--since YYYY-MM-DD] [--until YYYY-MM-DD] [--past-week|--past-2-weeks|--past-month] [--output <path>] [--dry-run]'
6
+ disable-model-invocation: false
7
+ allowed-tools: Read, Write, Bash, Glob, Grep, AskUserQuestion
8
+ user-invocable: true
9
+ ---
10
+
11
+ # Wrap-Up
12
+
13
+ Read local OAT project summaries plus merged PRs over a time window, synthesize a shipping digest (features, bug fixes, new user-facing capabilities), and write a markdown report to a version-controlled directory.
14
+
15
+ ## When to Use
16
+
17
+ Use when:
18
+
19
+ - Preparing a weekly, biweekly, or ad-hoc "what shipped" digest for a team retrospective or stakeholder update.
20
+ - Generating a release-notes seed from OAT-tracked projects and merged PRs over a specific window.
21
+ - Capturing an audit trail of what a team accomplished over a fixed period.
22
+
23
+ ## When NOT to Use
24
+
25
+ Don't use when:
26
+
27
+ - You need a single specific project's summary — use `oat-project-summary` instead.
28
+ - You want an always-current status view without a fixed window — the wrap-up is window-scoped by design.
29
+ - Archived projects from teammates have not been hydrated locally yet — run `oat project archive sync` first so the digest reflects the full team's work. The skill warns if this looks undone but will not auto-run sync.
30
+
31
+ ## Arguments
32
+
33
+ Parse from `$ARGUMENTS`:
34
+
35
+ - `--since YYYY-MM-DD`: (optional) Explicit window start date (inclusive).
36
+ - `--until YYYY-MM-DD`: (optional) Explicit window end date (inclusive). Defaults to today.
37
+ - `--past-week`: (optional) Named shortcut — last 7 days ending today.
38
+ - `--past-2-weeks`: (optional) Named shortcut — last 14 days ending today.
39
+ - `--past-month`: (optional) Named shortcut — last 30 days ending today.
40
+ - `--output <path>`: (optional) Override the report destination. Bypasses the `archive.wrapUpExportPath` config.
41
+ - `--dry-run`: (optional) Print the report to stdout instead of writing a file.
42
+
43
+ Exactly one time-range specifier is required: either a named range (`--past-week` / `--past-2-weeks` / `--past-month`) OR `--since` (with optional `--until`). Named ranges and `--since` are mutually exclusive.
44
+
45
+ ## Prerequisites
46
+
47
+ - Repository is an OAT project (contains `.oat/`).
48
+ - For cross-teammate visibility: `oat project archive sync` has been run recently so teammates' archived projects are hydrated into `.oat/projects/archived/`. The skill warns if `archive.s3Uri` is configured but the local archive has no `.oat-archive-source.json` metadata files.
49
+ - `gh` CLI authenticated for the current repository (needed for merged-PR fetching via `gh api graphql`).
50
+
51
+ ## Mode Assertion
52
+
53
+ **OAT MODE: Wrap-Up**
54
+
55
+ **Purpose:** Produce a date-ranged shipping digest over OAT-tracked projects and merged PRs.
56
+
57
+ **BLOCKED Activities:**
58
+
59
+ - No modifying OAT project summaries, plan files, or other implementation artifacts.
60
+ - No auto-running `oat project archive sync` — it is a user-gated prerequisite, not a side effect of this skill.
61
+ - No network writes of any kind. The GitHub API is read-only.
62
+
63
+ **ALLOWED Activities:**
64
+
65
+ - Reading local OAT summary files from active projects, local archive, and the version-controlled export directory.
66
+ - Fetching merged-PR metadata via `gh api graphql`.
67
+ - Writing a single markdown wrap-up report to the configured destination (or stdout on `--dry-run`).
68
+
69
+ **Self-Correction Protocol:**
70
+
71
+ If you catch yourself:
72
+
73
+ - About to modify a summary file → STOP, the skill is strictly read-only against summaries.
74
+ - About to auto-run `oat project archive sync` → STOP, the skill only warns; the user runs sync themselves.
75
+ - About to string-concatenate summary prose verbatim → STOP, the report is a synthesis, not a concatenation.
76
+ - About to skip the prerequisite warning when the local archive is empty and S3 is configured → STOP, emit the warning first.
77
+
78
+ **Recovery:**
79
+
80
+ 1. Return to read-only discovery.
81
+ 2. Re-derive any partially written output from the summaries and PR data captured so far.
82
+ 3. Restart from the current step.
83
+
84
+ ## Progress Indicators (User-Facing)
85
+
86
+ Print the phase banner once at start:
87
+
88
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
89
+ OAT ▸ WRAP-UP
90
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
91
+
92
+ Print each step indicator at the **start** of that step, not all at once upfront:
93
+
94
+ - `[1/9] Resolving inputs…`
95
+ - `[2/9] Resolving config…`
96
+ - `[3/9] Discovering summary files…`
97
+ - `[4/9] Parsing + filtering summaries…`
98
+ - `[5/9] Deduping by project…`
99
+ - `[6/9] Fetching merged PRs…`
100
+ - `[7/9] Cross-referencing PRs to OAT projects…`
101
+ - `[8/9] Synthesizing report…`
102
+ - `[9/9] Writing report…`
103
+
104
+ After step 9, print the final banner on a single line:
105
+
106
+ `OAT ▸ WRAP-UP ▸ DONE — <N> summaries + <M> PRs → <path>`
107
+
108
+ On `--dry-run`, replace `<path>` with `stdout (dry-run)`.
109
+
110
+ ## Process
111
+
112
+ ### Step 0: Archive-Sync Prerequisite Warning
113
+
114
+ Before any step indicator fires, check whether the local archive likely needs to be hydrated. Resolve the projects root and probe for archived metadata:
115
+
116
+ ```bash
117
+ set -eu
118
+ PROJECTS_ROOT="${OAT_PROJECTS_ROOT:-$(oat config get projects.root 2>/dev/null || echo ".oat/projects/shared")}"
119
+ PROJECTS_ROOT="${PROJECTS_ROOT%/}"
120
+ ARCHIVE_DIR="$(dirname "$PROJECTS_ROOT")/archived"
121
+ S3_URI="$(oat config get archive.s3Uri 2>/dev/null || true)"
122
+
123
+ if [ -n "$S3_URI" ] && [ -d "$ARCHIVE_DIR" ]; then
124
+ if ! find "$ARCHIVE_DIR" -maxdepth 2 -name '.oat-archive-source.json' 2>/dev/null | grep -q . ; then
125
+ printf '⚠️ archive.s3Uri is configured but no archived snapshots are hydrated locally.\n'
126
+ printf ' Run "oat project archive sync" first so teammates archived projects are visible to the wrap-up.\n'
127
+ printf ' (Proceeding with active projects and the version-controlled summaries directory only.)\n'
128
+ fi
129
+ elif [ -n "$S3_URI" ]; then
130
+ printf '⚠️ archive.s3Uri is configured but %s does not exist.\n' "$ARCHIVE_DIR"
131
+ printf ' Run "oat project archive sync" first so archived projects are available.\n'
132
+ printf ' (Proceeding with active projects and the version-controlled summaries directory only.)\n'
133
+ fi
134
+ ```
135
+
136
+ The `[ -d "$ARCHIVE_DIR" ]` guard protects against parent shells inheriting `set -o pipefail`, where `find` on a missing directory could fail the pipeline. The differentiated branch also gives clearer guidance when the directory is missing entirely versus merely empty of metadata.
137
+
138
+ Do NOT block on this warning. Continue to Step 1.
139
+
140
+ ### Step 1: Resolve Inputs
141
+
142
+ Parse `$ARGUMENTS` and resolve the time window into concrete `SINCE` and `UNTIL` dates:
143
+
144
+ - If a named range (`--past-week` / `--past-2-weeks` / `--past-month`) is provided, compute:
145
+ - `UNTIL` = today (ISO `YYYY-MM-DD` in the local timezone)
146
+ - `SINCE` = `UNTIL` minus 7 / 14 / 30 days respectively
147
+ - `WINDOW_LABEL` = the range name (`past-week` / `past-2-weeks` / `past-month`)
148
+ - If `--since YYYY-MM-DD` is provided, use it directly. `--until` defaults to today when omitted. `WINDOW_LABEL` = `custom`.
149
+ - If both named range and `--since` are provided, fail with `Error: --past-week / --past-2-weeks / --past-month are mutually exclusive with --since.`
150
+ - If neither is provided, fail with `Error: specify either --past-week / --past-2-weeks / --past-month or --since YYYY-MM-DD.`
151
+ - Validate that `SINCE` and `UNTIL` parse as `YYYY-MM-DD` and that `SINCE <= UNTIL`.
152
+
153
+ Resolve `--output` if present; otherwise leave it null (the destination will be computed in Step 2 from config).
154
+
155
+ Resolve `--dry-run` to a boolean.
156
+
157
+ Print the resolved window for reproducibility:
158
+
159
+ ```
160
+ Window: SINCE → UNTIL (WINDOW_LABEL)
161
+ ```
162
+
163
+ ### Step 2: Resolve Config
164
+
165
+ Resolve the configuration surface via `oat config get`:
166
+
167
+ ```bash
168
+ PROJECTS_ROOT="${OAT_PROJECTS_ROOT:-$(oat config get projects.root 2>/dev/null || echo ".oat/projects/shared")}"
169
+ PROJECTS_ROOT="${PROJECTS_ROOT%/}"
170
+ SUMMARY_EXPORT_PATH="$(oat config get archive.summaryExportPath 2>/dev/null || true)"
171
+ WRAPUP_EXPORT_PATH="$(oat config get archive.wrapUpExportPath 2>/dev/null || true)"
172
+ if [ -z "$WRAPUP_EXPORT_PATH" ]; then
173
+ WRAPUP_EXPORT_PATH=".oat/repo/reference/wrap-ups"
174
+ fi
175
+ REPO_NAME_WITH_OWNER="$(gh repo view --json nameWithOwner --jq .nameWithOwner)"
176
+ ```
177
+
178
+ - `SUMMARY_EXPORT_PATH` may be empty. When empty, Step 3 skips that discovery location.
179
+ - `WRAPUP_EXPORT_PATH` falls back to `.oat/repo/reference/wrap-ups` because `oat config get` returns empty when unset. This fallback lives in the skill, not in the CLI, to match the sibling `archive.summaryExportPath` behavior.
180
+ - `REPO_NAME_WITH_OWNER` must resolve successfully — fail fast if `gh repo view` errors (auth issue or wrong cwd).
181
+
182
+ If `--output` was set in Step 1, use it as the final destination. Otherwise the destination is:
183
+
184
+ ```
185
+ <repoRoot>/<WRAPUP_EXPORT_PATH>/<UNTIL>-wrap-up-<WINDOW_LABEL>.md
186
+ ```
187
+
188
+ ### Step 3: Discover Summary Files
189
+
190
+ Build a candidate set from three local locations. Use `Glob` for each and collect absolute paths:
191
+
192
+ 1. **Active projects**: `${PROJECTS_ROOT}/*/summary.md` AND multi-scope `.oat/projects/*/*/summary.md` (some repos use scope subdirectories under `.oat/projects/`).
193
+ 2. **Local archive**: `.oat/projects/archived/*/summary.md`. Also include timestamp-suffixed variants like `.oat/projects/archived/*-[0-9]*/summary.md` (created by `resolveUniqueArchivePath` when the same project is archived twice).
194
+ 3. **Version-controlled export**: `${SUMMARY_EXPORT_PATH}/*.md` — only if `SUMMARY_EXPORT_PATH` is non-empty. Files here follow the `{YYYYMMDD}-{project}.md` naming produced by the completion workflow.
195
+
196
+ Collect every candidate path into a single list before filtering in Step 4.
197
+
198
+ ### Step 4: Parse and Filter Summaries
199
+
200
+ For each candidate file:
201
+
202
+ 1. Read the file and parse its YAML frontmatter.
203
+ 2. Verify OAT origin: the file is an OAT summary if either
204
+ - the frontmatter contains `oat_generated: true`, OR
205
+ - the filename matches the `YYYYMMDD-{project}.md` pattern (export directory format).
206
+ Drop any file that matches neither check.
207
+ 3. Extract the summary date using this priority order:
208
+ 1. Frontmatter `oat_last_updated` (YYYY-MM-DD).
209
+ 2. The `{YYYYMMDD}` prefix parsed from the filename or parent directory. Format the prefix as a date.
210
+ 3. File `mtime` (last-resort fallback).
211
+ 4. Extract the project name using this priority order:
212
+ 1. Frontmatter `project_name` or similar explicit field if present.
213
+ 2. The parent directory basename (strip any timestamp suffix like `-20260403120000`).
214
+ 3. The filename-embedded project name (portion after `{YYYYMMDD}-` for the export format).
215
+ 5. Keep the summary only if its resolved date is within `[SINCE, UNTIL]` (inclusive).
216
+
217
+ The result is a list of `{projectName, date, path, source, content}` records where `source` is one of `active`, `archived`, `exported`.
218
+
219
+ ### Step 5: Dedupe by Project
220
+
221
+ The same project can appear in multiple locations (e.g., an active project that was also exported to the version-controlled directory). Dedupe with these rules:
222
+
223
+ 1. Group records by project name.
224
+ 2. Within each group, prefer the record with the latest `oat_last_updated` frontmatter value (or resolved date if frontmatter is missing).
225
+ 3. On ties, prefer `source` in this order: `active` > `archived` > `exported`. Rationale: the active project is the freshest copy when work is still in progress; the local archive is the next most recent; the version-controlled export is a historical snapshot.
226
+
227
+ The result is a list of at most one summary per project, all within the time window.
228
+
229
+ ### Step 6: Fetch Merged PRs
230
+
231
+ Fetch merged PRs in the window via `gh api graphql`. Reuse the search-string pattern from `packages/cli/src/commands/repo/pr-comments/collect/collect-comments.ts:198-205`:
232
+
233
+ ```bash
234
+ SEARCH_QUERY="is:pr is:merged merged:${SINCE}..${UNTIL} repo:${REPO_NAME_WITH_OWNER}"
235
+ ```
236
+
237
+ Paginate using `endCursor` / `hasNextPage` — do not stop at the first page. Capture for each PR: number, title, author login, mergedAt, labels, and body (for cross-reference scanning in Step 7).
238
+
239
+ Example single-page call (the agent should loop until `hasNextPage` is false):
240
+
241
+ ```bash
242
+ gh api graphql \
243
+ -f query='query($searchQuery: String!, $first: Int!, $after: String) {
244
+ search(query: $searchQuery, type: ISSUE, first: $first, after: $after) {
245
+ pageInfo { hasNextPage endCursor }
246
+ nodes {
247
+ ... on PullRequest {
248
+ number
249
+ title
250
+ author { login }
251
+ mergedAt
252
+ labels(first: 20) { nodes { name } }
253
+ body
254
+ }
255
+ }
256
+ }
257
+ }' \
258
+ -f searchQuery="$SEARCH_QUERY" \
259
+ -F first=25
260
+ ```
261
+
262
+ Store the flattened PR list for Step 7.
263
+
264
+ ### Step 7: Cross-Reference PRs to OAT Projects
265
+
266
+ For each merged PR, determine whether it is already "claimed" by an included summary:
267
+
268
+ 1. Collect the set of included summaries from Step 5.
269
+ 2. For each summary, scan the body text for PR references matching `#<number>`, `github.com/.../pull/<number>`, or the bare PR number when adjacent to keywords like "PR", "merged", "ships in".
270
+ 3. Build a map of `pr_number → summary.projectName` for every match.
271
+
272
+ Partition the merged PRs into two groups:
273
+
274
+ - **Shipped via OAT projects**: PRs whose number appears in the cross-reference map.
275
+ - **Other merged PRs**: all other merged PRs in the window.
276
+
277
+ ### Step 8: Synthesize Report
278
+
279
+ Compose the markdown report by **reading** each included summary and **synthesizing** (not concatenating) its Overview and What Was Implemented sections into the report's narrative sections. Apply the summary template's section-omission rule — omit any report section with no content.
280
+
281
+ Use the skeleton at `references/report-template.md` as the scaffold. Key synthesis rules:
282
+
283
+ - **TL;DR**: 2–4 sentences describing the headline of what shipped during the window. This is your summary, written fresh, not a copy of any single project's overview.
284
+ - **Features introduced**: one bullet per new feature. Each bullet is a short paragraph naming the capability, where it shipped, and linking the relevant PR(s).
285
+ - **Bug fixes**: one bullet per bug fix. Be specific about what broke and what now works.
286
+ - **New user-facing capabilities**: for each new capability a user can directly invoke or rely on, describe what it does and how to use it (command, flag, URL, UI affordance). This is where release-notes-level content lives.
287
+ - **Shipped via OAT projects**: a compact table listing project name, window date, linked PR(s), and a one-line outcome per project.
288
+ - **Other merged PRs**: a compact table listing PR number, title, author, and merged date for merged PRs not claimed by any OAT project summary in the window.
289
+ - **Open follow-ups**: aggregate the `Follow-up Items` section across all included summaries. Dedupe similar items.
290
+ - **Included summaries (provenance)**: a simple list of the summary file paths consulted, so readers can audit what the report drew from.
291
+
292
+ If a section would be empty (e.g., no bug fixes shipped during the window), omit it entirely — do not include a "None" placeholder.
293
+
294
+ ### Step 9: Write Report or Print to Stdout
295
+
296
+ Compose the final markdown document with this frontmatter at the top:
297
+
298
+ ```yaml
299
+ ---
300
+ oat_wrap_up: true
301
+ oat_generated: true
302
+ window_since: { SINCE }
303
+ window_until: { UNTIL }
304
+ window_label: { WINDOW_LABEL }
305
+ generated_at: { ISO 8601 UTC timestamp }
306
+ ---
307
+ ```
308
+
309
+ Followed by `# Wrap-up: {SINCE} to {UNTIL}` as the H1 and then the synthesized sections from Step 8.
310
+
311
+ Destination logic:
312
+
313
+ - If `--dry-run` is set, write the report to stdout. Do not create or modify any file.
314
+ - Otherwise, if `--output` was explicitly set in Step 1, write to that path (create parent directories if needed).
315
+ - Otherwise, write to `<repoRoot>/<WRAPUP_EXPORT_PATH>/<UNTIL>-wrap-up-<WINDOW_LABEL>.md` (create parent directories if needed).
316
+
317
+ After the write (or stdout output), print the final banner:
318
+
319
+ ```
320
+ OAT ▸ WRAP-UP ▸ DONE — <N_summaries> summaries + <N_prs> PRs → <path_or_stdout>
321
+ ```
322
+
323
+ Do NOT commit the resulting file. If the user wants it committed, they run `git add` and `git commit` themselves, or use an automation wrapper (see `references/automation-recipes.md`).
324
+
325
+ ## Examples
326
+
327
+ ### Basic Usage
328
+
329
+ ```
330
+ /oat-wrap-up --past-week
331
+ ```
332
+
333
+ ```
334
+ /oat-wrap-up --past-2-weeks --dry-run
335
+ ```
336
+
337
+ ```
338
+ /oat-wrap-up --since 2026-03-20 --until 2026-04-01
339
+ ```
340
+
341
+ ```
342
+ /oat-wrap-up --past-month --output /tmp/march-wrapup.md
343
+ ```
344
+
345
+ ### Conversational
346
+
347
+ ```
348
+ Summarize what shipped this past week across OAT projects and merged PRs.
349
+ ```
350
+
351
+ ```
352
+ Give me a biweekly digest — the last 14 days of shipped work — and write it
353
+ to the usual wrap-ups directory.
354
+ ```
355
+
356
+ ```
357
+ Produce a wrap-up for the window 2026-03-20 to 2026-04-01 and print it to stdout
358
+ so I can review before committing it.
359
+ ```
360
+
361
+ ## Reference
362
+
363
+ - Report skeleton: `references/report-template.md`
364
+ - Automation patterns (Claude Code `CronCreate`, Codex host scheduling, plain cron): `references/automation-recipes.md`
365
+ - Prerequisite command: `oat project archive sync` at `packages/cli/src/commands/project/archive/index.ts:244`
366
+ - Merged-PR query pattern: `packages/cli/src/commands/repo/pr-comments/collect/collect-comments.ts:198-205`
367
+ - Summary frontmatter schema: `.oat/templates/summary.md`
368
+ - Config key: `archive.wrapUpExportPath` (managed via `oat config set archive.wrapUpExportPath <path>`)
369
+
370
+ ## Troubleshooting
371
+
372
+ **`gh repo view` fails with an auth error:**
373
+
374
+ - Run `gh auth status` and authenticate against the current repository's host.
375
+ - Verify the current working directory is inside a git repo with a configured `origin` remote.
376
+
377
+ **Report is empty or "no summaries found":**
378
+
379
+ - The window may genuinely have no activity — double-check `SINCE` and `UNTIL`.
380
+ - Check whether teammates' archived projects are hydrated: `ls .oat/projects/archived/` should list directories. If empty and `archive.s3Uri` is configured, run `oat project archive sync` first.
381
+ - Verify that `.oat/projects/*/*/summary.md` and/or `${SUMMARY_EXPORT_PATH}/*.md` contain files whose `oat_last_updated` falls inside the window.
382
+
383
+ **Report is missing PRs that clearly merged in the window:**
384
+
385
+ - Confirm `gh` is authenticated and scoped to the right repo via `gh repo view --json nameWithOwner`.
386
+ - Verify the GraphQL search query uses `merged:${SINCE}..${UNTIL}` (inclusive on both ends) — some date-boundary surprises come from the `mergedAt` timestamp being in UTC.
387
+ - Check pagination: if the fetch stopped before `hasNextPage: false`, extend the pagination loop.
388
+
389
+ **`archive.wrapUpExportPath` returns empty from `oat config get` but the skill wrote to a different path:**
390
+
391
+ - This is expected. The skill's internal fallback is `.oat/repo/reference/wrap-ups` — the config layer itself returns empty for unset values, consistent with the sibling `archive.summaryExportPath` behavior.
392
+ - To change the destination, either `oat config set archive.wrapUpExportPath <path>` or use `--output <path>` for a one-off override.
393
+
394
+ **"Shipped via OAT projects" is empty or "Other merged PRs" contains PRs that clearly correspond to included summaries (false negative):**
395
+
396
+ - Step 7's cross-reference logic is strict: it only claims a PR for a summary when the summary body literally contains the PR number (`#<n>`, `github.com/.../pull/<n>`, or a bare PR number adjacent to keywords like "PR", "merged", "ships in"), or when a sibling `pr/` directory inside the project references the PR. OAT summaries as authored today (see `.oat/templates/summary.md`) do not inline PR numbers, so in repos that have not adopted the convention of citing PRs in summaries, the cross-reference will often find zero matches and all merged PRs will land in "Other merged PRs".
397
+ - **Workaround options (author-time)**: add a short "Associated PRs" section to project summaries and list PR numbers inline, OR populate `pr/` under each project directory with PR metadata so the sibling scan has data to match against.
398
+ - **Workaround options (report-time)**: manually re-read the generated report; move PRs from "Other merged PRs" into "Shipped via OAT projects" by hand if the correspondence is obvious (matching keywords in the PR title and the summary's What Was Implemented section). The skill is designed to produce an editable draft, not a final release note.
399
+ - This is a v1 limitation. Future versions may add a looser heuristic (e.g., title-keyword matching, commit-to-project mapping via `git log`) to close the gap.
400
+
401
+ **A PR was partitioned into "Shipped via OAT projects" but the narrative does not mention it (false positive):**
402
+
403
+ - The `#<number>` pattern can false-positive when a summary references a heading anchor like `#42`, a footnote marker, or a legacy issue number that happens to match a PR number in the window. Step 7's hits are advisory, not authoritative.
404
+ - If the synthesized report puts a PR in the wrong bucket, the fix is manual: re-read the summary that claims the PR and the PR's actual body. If the connection is incorrect, move the PR row from "Shipped via OAT projects" to "Other merged PRs" by hand and delete the entry from any feature/bug/capability bullets that cite it.
405
+
406
+ ## Success Criteria
407
+
408
+ - ✅ Prerequisite warning fired (if applicable) before step 1 started.
409
+ - ✅ Time window resolved to concrete `SINCE`/`UNTIL` dates and printed for reproducibility.
410
+ - ✅ Config resolution used `archive.wrapUpExportPath` with the documented fallback when unset.
411
+ - ✅ Summary discovery covered all three local locations (active, archived, exported).
412
+ - ✅ Summaries filtered by `oat_last_updated` within the window and deduped by project with the documented precedence.
413
+ - ✅ Merged PRs fetched with pagination and partitioned into "Shipped via OAT projects" vs "Other merged PRs".
414
+ - ✅ Report is a synthesis of summary content, not a verbatim concatenation.
415
+ - ✅ Report lands at the resolved destination (or stdout on `--dry-run`) with correct frontmatter.
416
+ - ✅ Final banner printed with accurate counts and path.
417
+ - ✅ No files were modified except the single output report (and no files at all on `--dry-run`).
@@ -0,0 +1,100 @@
1
+ # Automation Recipes
2
+
3
+ The `oat-wrap-up` skill is manual and model-invocable by design. OAT itself does not ship a scheduler (see `.agents/docs/agent-instruction.md:18` — scheduling is out of scope for OAT). To run the skill on a recurring cadence, configure your host agent or an external cron to invoke it. Three patterns are documented below.
4
+
5
+ ## Pattern 1 — Claude Code `CronCreate` trigger
6
+
7
+ Use Claude Code's scheduled-trigger mechanism to fire the skill on a fixed cadence. Create the trigger from within Claude Code:
8
+
9
+ ```
10
+ /schedule "0 9 * * 1" "/oat-wrap-up --past-week"
11
+ ```
12
+
13
+ Or via the `CronCreate` tool directly (if building from the SDK):
14
+
15
+ ```json
16
+ {
17
+ "name": "oat-wrap-up-weekly",
18
+ "cron": "0 9 * * 1",
19
+ "timezone": "America/Los_Angeles",
20
+ "prompt": "/oat-wrap-up --past-week"
21
+ }
22
+ ```
23
+
24
+ Notes:
25
+
26
+ - `0 9 * * 1` fires every Monday at 09:00 in the configured timezone.
27
+ - The prompt is interpreted as a user message — Claude Code routes it through the normal skill invocation path.
28
+ - Pair this with a wrapper prompt like `After the wrap-up completes, git add the resulting file, commit it with "chore: weekly wrap-up for {date}", and push to a new branch with name weekly-wrap-up/{date}` if you want automated commit + push.
29
+
30
+ ## Pattern 2 — Codex host scheduling
31
+
32
+ Codex exposes scheduled-trigger capabilities through its host runtime. Consult your Codex deployment's scheduling docs for the exact API (it varies by host integration).
33
+
34
+ The generic pattern:
35
+
36
+ 1. Create a scheduled trigger whose prompt is `/oat-wrap-up --past-week` (or a named range that matches your cadence).
37
+ 2. Configure the trigger to run in a workspace where the OAT repository is the current working directory.
38
+ 3. Ensure the workspace has `gh` authenticated and, for cross-teammate visibility, a recent run of `oat project archive sync` (this can be a pre-prompt step in the trigger).
39
+
40
+ If your Codex host does not expose scheduled triggers, fall through to Pattern 3.
41
+
42
+ ## Pattern 3 — Plain cron / systemd timer via headless host CLI
43
+
44
+ When neither Claude Code's `CronCreate` nor Codex host scheduling is available, drive the skill via a headless host CLI from plain cron.
45
+
46
+ ### crontab example (Claude Code headless)
47
+
48
+ ```cron
49
+ # Every Monday at 09:00 local time, generate a past-week wrap-up and commit it.
50
+ 0 9 * * 1 cd /path/to/repo && \
51
+ oat project archive sync --dry-run > /dev/null && \
52
+ claude -p "/oat-wrap-up --past-week" && \
53
+ git add .oat/repo/reference/wrap-ups && \
54
+ git -c user.name="wrapup-bot" -c user.email="wrapup-bot@example.com" commit -m "chore: weekly wrap-up $(date -u +%Y-%m-%d)" >/dev/null 2>&1 || true
55
+ ```
56
+
57
+ Notes:
58
+
59
+ - Replace `claude -p ...` with your host's headless invocation (`codex -p ...`, etc.) if using a different host.
60
+ - The `oat project archive sync --dry-run` call is a preflight check — it surfaces auth or config problems early. Change it to `oat project archive sync` if you want to actively pull teammates' archives before each run.
61
+ - The trailing `|| true` keeps cron quiet when there is nothing new to commit (e.g., the wrap-up matched a previous one).
62
+
63
+ ### systemd timer example
64
+
65
+ `/etc/systemd/system/oat-wrap-up.service`:
66
+
67
+ ```ini
68
+ [Unit]
69
+ Description=Generate weekly OAT wrap-up
70
+
71
+ [Service]
72
+ Type=oneshot
73
+ WorkingDirectory=/path/to/repo
74
+ ExecStart=/bin/sh -c 'oat project archive sync --dry-run && claude -p "/oat-wrap-up --past-week"'
75
+ User=wrapup-bot
76
+ ```
77
+
78
+ `/etc/systemd/system/oat-wrap-up.timer`:
79
+
80
+ ```ini
81
+ [Unit]
82
+ Description=Weekly OAT wrap-up
83
+
84
+ [Timer]
85
+ OnCalendar=Mon 09:00
86
+ Persistent=true
87
+
88
+ [Install]
89
+ WantedBy=timers.target
90
+ ```
91
+
92
+ Enable with `systemctl enable --now oat-wrap-up.timer`.
93
+
94
+ ## Operational guidance
95
+
96
+ - **Timezones**: The skill resolves named ranges against `today` in the local timezone of the host running the skill. Always pin the timezone at the scheduler layer (cron `TZ=`, systemd `OnCalendar=Mon 09:00 America/Los_Angeles`, or the Claude Code trigger's `timezone` field) so reports are reproducible across re-runs.
97
+ - **Cadence vs window length**: A weekly cadence usually pairs with `--past-week`. A biweekly cadence with `--past-2-weeks`. Running a weekly cadence with `--past-2-weeks` overlaps every report — fine if you want a rolling window, confusing if you expect discrete chunks.
98
+ - **Commit + push**: The skill itself never commits. If you want the report to land in the repo automatically, the automation wrapper owns the `git add && git commit && git push` step. Keep that wrapper out of the skill so the skill stays portable.
99
+ - **Failure handling**: If `gh` auth expires or `oat project archive sync` fails, the cron run should log the failure and exit non-zero rather than producing an empty wrap-up. Prefer pinning the shell to `set -euo pipefail` or an equivalent in the wrapper.
100
+ - **Idempotence**: Running the same window twice produces the same report content (modulo `generated_at`). Overwriting is safe; appending duplicate commits is usually not — coalesce with a pre-check (`git diff --quiet` on the wrap-ups directory) if your cadence might cause no-op runs.
@@ -0,0 +1,90 @@
1
+ # Wrap-Up Report Skeleton
2
+
3
+ This is the markdown scaffold the `oat-wrap-up` skill fills in during Step 8 (Synthesize Report) and Step 9 (Write Report). It is a **reference** only — the skill reads this file for structure guidance, then produces the final report from the discovered summaries and merged PRs.
4
+
5
+ ## Structure
6
+
7
+ ```markdown
8
+ ---
9
+ oat_wrap_up: true
10
+ oat_generated: true
11
+ window_since: YYYY-MM-DD
12
+ window_until: YYYY-MM-DD
13
+ window_label: past-week
14
+ generated_at: YYYY-MM-DDTHH:MM:SSZ
15
+ ---
16
+
17
+ # Wrap-up: YYYY-MM-DD to YYYY-MM-DD
18
+
19
+ ## TL;DR
20
+
21
+ {2–4 sentences describing the headline of what shipped during the window. Written fresh by the skill, not copied from any single project's overview. Mention the most important user-facing change first, then the breadth of the shipped work.}
22
+
23
+ ## Features introduced
24
+
25
+ - **{Feature name}** — {1–2 sentences describing what shipped and why it matters to users}. Shipped in {project reference} via {PR links}.
26
+ - **{Another feature}** — {...}.
27
+
28
+ ## Bug fixes
29
+
30
+ - **{Short description of the bug that was fixed}** — {what was broken, what now works}. {PR link}.
31
+ - **{...}**
32
+
33
+ ## New user-facing capabilities
34
+
35
+ - **{Capability name}**: {what it does}. Invoke via `{command / flag / URL}`. {1 sentence on when or how users would reach for this}. {PR link}.
36
+ - **{...}**
37
+
38
+ ## Shipped via OAT projects
39
+
40
+ | Project | Window date | PR | Outcome |
41
+ | ------- | ----------- | ---- | ------------------ |
42
+ | {name} | YYYY-MM-DD | #{n} | {one-line outcome} |
43
+ | {name} | YYYY-MM-DD | #{n} | {one-line outcome} |
44
+
45
+ ## Other merged PRs
46
+
47
+ | PR | Title | Author | Merged |
48
+ | ---- | ------- | ------- | ---------- |
49
+ | #{n} | {title} | {login} | YYYY-MM-DD |
50
+ | #{n} | {title} | {login} | YYYY-MM-DD |
51
+
52
+ ## Open follow-ups
53
+
54
+ - {Aggregated `Follow-up Items` entry from one of the included summaries. Dedupe near-duplicates across summaries.}
55
+ - {Another follow-up}
56
+
57
+ ## Included summaries (provenance)
58
+
59
+ - `.oat/projects/shared/<project-a>/summary.md`
60
+ - `.oat/projects/archived/<project-b>/summary.md`
61
+ - `.oat/repo/reference/project-summaries/20260403-<project-c>.md`
62
+ ```
63
+
64
+ ## Section omission rule
65
+
66
+ Apply the same rule as `.oat/templates/summary.md`: **omit any section with no content**. Do not leave empty sections or "None" placeholders. The minimum viable wrap-up is `# Wrap-up` + `## TL;DR` + one of (`Features introduced` | `Bug fixes` | `Shipped via OAT projects` | `Other merged PRs`) + `Included summaries (provenance)`.
67
+
68
+ If the window is completely empty (no summaries, no merged PRs), still write the file (or stdout) with:
69
+
70
+ ```markdown
71
+ # Wrap-up: YYYY-MM-DD to YYYY-MM-DD
72
+
73
+ ## TL;DR
74
+
75
+ No OAT-tracked projects completed or merged PRs landed in this window.
76
+
77
+ ## Included summaries (provenance)
78
+
79
+ - _(none)_
80
+ ```
81
+
82
+ ## Synthesis guidance
83
+
84
+ **Don't string-concatenate summary prose.** The report is a synthesis. Read each summary's Overview and What Was Implemented sections, and write a fresh narrative that groups related work. If two projects shipped complementary pieces of the same capability, describe them together rather than as separate bullets.
85
+
86
+ **Link everything.** Every feature, bug fix, and capability entry should link to at least one PR (or summary file) so readers can drill in. If a summary is the source of the narrative, link to the summary file in "Included summaries (provenance)" and reference PR numbers from within the narrative sections.
87
+
88
+ **Keep the TL;DR short.** Two to four sentences. If you can't summarize the window in that space, the window is probably too wide for a single report — consider running two reports over narrower windows.
89
+
90
+ **User-facing voice.** The "New user-facing capabilities" section is the most important output for release notes and stakeholder communication. Describe each capability in the voice of "what a user can now do," not "what the team shipped." For each capability, include the exact command, flag, URL, or UI affordance so the entry is directly actionable.
@@ -1,5 +1,6 @@
1
1
  import { buildCommandContext, type CommandContext } from '../../app/command-context.js';
2
- import { type OatConfig, type OatLocalConfig } from '../../config/oat-config.js';
2
+ import { type OatConfig, type OatLocalConfig, type UserConfig } from '../../config/oat-config.js';
3
+ import { type ResolvedConfig } from '../../config/resolve.js';
3
4
  import { Command } from 'commander';
4
5
  interface ConfigCommandDependencies {
5
6
  buildCommandContext: (options: Parameters<typeof buildCommandContext>[0]) => CommandContext;
@@ -8,7 +9,10 @@ interface ConfigCommandDependencies {
8
9
  writeOatConfig: (repoRoot: string, config: OatConfig) => Promise<void>;
9
10
  readOatLocalConfig: (repoRoot: string) => Promise<OatLocalConfig>;
10
11
  writeOatLocalConfig: (repoRoot: string, config: OatLocalConfig) => Promise<void>;
12
+ readUserConfig: (userConfigDir: string) => Promise<UserConfig>;
13
+ writeUserConfig: (userConfigDir: string, config: UserConfig) => Promise<void>;
11
14
  resolveProjectsRoot: (repoRoot: string, env: NodeJS.ProcessEnv) => Promise<string>;
15
+ resolveEffectiveConfig: (repoRoot: string, userConfigDir: string, env: NodeJS.ProcessEnv) => Promise<ResolvedConfig>;
12
16
  processEnv: NodeJS.ProcessEnv;
13
17
  }
14
18
  export declare function createConfigCommand(overrides?: Partial<ConfigCommandDependencies>): Command;