@open-agent-toolkit/cli 0.3.23 → 0.3.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 (29) hide show
  1. package/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/docs-tooling/workflows.md +90 -11
  3. package/assets/docs/reference/cli-reference.md +1 -1
  4. package/assets/docs/reference/configuration.md +6 -2
  5. package/assets/public-package-versions.json +4 -4
  6. package/assets/skills/authoring-docs/SKILL.md +18 -8
  7. package/assets/skills/authoring-docs/references/guidance-sources.md +95 -0
  8. package/assets/skills/authoring-docs/references/information-architecture.md +121 -86
  9. package/assets/skills/authoring-docs/references/page-types.md +35 -7
  10. package/assets/skills/authoring-docs/references/review-rubric.md +12 -12
  11. package/assets/skills/authoring-docs/references/templates.md +54 -0
  12. package/assets/skills/authoring-docs/references/workflow.md +6 -1
  13. package/assets/skills/authoring-docs/references/writing-style.md +1 -1
  14. package/assets/skills/authoring-docs/scripts/resolve-guidance.mjs +53 -0
  15. package/assets/skills/oat-docs-analyze/SKILL.md +140 -7
  16. package/assets/skills/oat-docs-analyze/references/analysis-artifact-template.md +54 -2
  17. package/assets/skills/oat-docs-analyze/scripts/package-drift.mjs +129 -0
  18. package/assets/skills/oat-docs-apply/SKILL.md +148 -11
  19. package/assets/skills/oat-docs-apply/references/apply-plan-template.md +67 -14
  20. package/assets/skills/oat-docs-authoring/SKILL.md +20 -8
  21. package/assets/skills/oat-docs-bootstrap/SKILL.md +74 -15
  22. package/dist/commands/config/index.d.ts.map +1 -1
  23. package/dist/commands/config/index.js +118 -1
  24. package/dist/config/oat-config.d.ts +21 -0
  25. package/dist/config/oat-config.d.ts.map +1 -1
  26. package/dist/config/oat-config.js +96 -4
  27. package/dist/config/resolve.d.ts.map +1 -1
  28. package/dist/config/resolve.js +5 -1
  29. package/package.json +2 -2
@@ -98,6 +98,30 @@ Rules:
98
98
  - Include verification, rollback, and escalation.
99
99
  - Avoid long historical background before recovery steps.
100
100
 
101
+ ## Feature Guide
102
+
103
+ Describe intended user behavior, the problem solved, prerequisites, boundaries
104
+ and linked Engineering/QA counterparts when the IA declares them. Distinguish
105
+ concept names from verified literal screen text; do not infer behavior from a UI.
106
+
107
+ ## Validation Matrix
108
+
109
+ Specify scenarios, preconditions, actions and expected outcomes independently
110
+ of implementation details. Link the canonical feature and only cite test IDs
111
+ that exist. Keep duplicate or contradictory scenarios out of the matrix.
112
+
113
+ ## Solution
114
+
115
+ Explain how supported capabilities combine to solve one end-to-end problem.
116
+ State constraints, tradeoffs, integration steps and verification; link exact
117
+ contracts rather than duplicating reference facts.
118
+
119
+ ## Decision Record
120
+
121
+ Record an accepted choice with context, alternatives, reasoning, consequences
122
+ and status. Link the authoritative decision surface when it lives elsewhere;
123
+ keep planning and execution evidence out of reader docs.
124
+
101
125
  ## Mixed Pages
102
126
 
103
127
  Some pages legitimately mix types. A landing page can include orientation,
@@ -110,10 +134,14 @@ and keep everything else subordinate.
110
134
 
111
135
  ## Smell Test
112
136
 
113
- | If the Reader Asks | They Need | Do Not Give Them |
114
- | ------------------------------- | ------------ | --------------------------- |
115
- | I am new. Walk me through this. | Tutorial | Full API reference |
116
- | How do I do X? | How-to guide | Architecture essay |
117
- | What does this option mean? | Reference | A blog post |
118
- | Why does this behave this way? | Explanation | A command list |
119
- | Production is broken. What now? | Runbook | Historical background first |
137
+ | If the Reader Asks | They Need | Do Not Give Them |
138
+ | ------------------------------------------- | ----------------- | ---------------------------- |
139
+ | I am new. Walk me through this. | Tutorial | Full API reference |
140
+ | How do I do X? | How-to guide | Architecture essay |
141
+ | What does this option mean? | Reference | A blog post |
142
+ | Why does this behave this way? | Explanation | A command list |
143
+ | Production is broken. What now? | Runbook | Historical background first |
144
+ | What behavior should this feature provide? | Feature guide | Implementation internals |
145
+ | How do I verify that behavior? | Validation matrix | Unverified test IDs |
146
+ | How do these capabilities solve my problem? | Solution | Disconnected reference dumps |
147
+ | Why was this choice accepted? | Decision record | A transient project plan |
@@ -53,18 +53,18 @@ Production runbooks should be especially strong on operational safety.
53
53
  Public-facing docs should be especially strong on examples, accuracy, and
54
54
  audience boundary.
55
55
 
56
- | Area | 0 | 1 | 2 | 3 |
57
- | ------------------------ | --------------------------- | ---------------------- | --------------------------- | ---------------------------------------------------------- |
58
- | Purpose | No clear reason page exists | Purpose is implied | Purpose is stated | Purpose, reader, and outcome are clear |
59
- | Accuracy | Incorrect or invented | Partially accurate | Mostly accurate | Grounded and verified |
60
- | Structure | Hard to scan | Some headings | Clear sections | Predictable IA and strong headings |
61
- | Task support | Cannot complete task | Can partially complete | Can complete with some gaps | Can complete safely with verification |
62
- | Reference quality | Missing facts | Facts scattered | Most fields documented | Types, defaults, constraints, examples, errors documented |
63
- | Operational safety | Risky or absent | Mentions risk | Includes basic verification | Includes verification, rollback, escalation, failure modes |
64
- | Agent usefulness | Ambiguous | Some exact data | Mostly explicit | Exact commands, paths, constraints, and uncertainty |
65
- | Maintainability | Duplicated or stale | Hard to update | Mostly maintainable | Clear source of truth and generated markers where needed |
66
- | Public/internal boundary | Leaks or omits key context | Boundary unclear | Mostly appropriate | Correct assumptions and safe content |
67
- | Links and navigation | Broken or absent | Sparse | Useful | Strong cross-links and clear next steps |
56
+ | Area | 0 | 1 | 2 | 3 |
57
+ | ------------------------ | --------------------------- | ---------------------- | --------------------------- | ------------------------------------------------------------------------- |
58
+ | Purpose | No clear reason page exists | Purpose is implied | Purpose is stated | Purpose, reader, and outcome are clear |
59
+ | Accuracy | Incorrect or invented | Partially accurate | Mostly accurate | Grounded and verified |
60
+ | Structure | Hard to scan | Some headings | Clear sections | Reader-first [IA method](information-architecture.md) and strong headings |
61
+ | Task support | Cannot complete task | Can partially complete | Can complete with some gaps | Can complete safely with verification |
62
+ | Reference quality | Missing facts | Facts scattered | Most fields documented | Types, defaults, constraints, examples, errors documented |
63
+ | Operational safety | Risky or absent | Mentions risk | Includes basic verification | Includes verification, rollback, escalation, failure modes |
64
+ | Agent usefulness | Ambiguous | Some exact data | Mostly explicit | Exact commands, paths, constraints, and uncertainty |
65
+ | Maintainability | Duplicated or stale | Hard to update | Mostly maintainable | Clear source of truth and generated markers where needed |
66
+ | Public/internal boundary | Leaks or omits key context | Boundary unclear | Mostly appropriate | Correct assumptions and safe content |
67
+ | Links and navigation | Broken or absent | Sparse | Useful | Strong cross-links and clear next steps |
68
68
 
69
69
  ## Minimum Acceptable Scores
70
70
 
@@ -547,3 +547,57 @@ docs/
547
547
 
548
548
  - <Explicitly out-of-scope docs>
549
549
  ```
550
+
551
+ ## IA Proposal
552
+
553
+ Use for bootstrap or an existing-tree `--ia` analysis. This proposes structure;
554
+ it neither persists an IA decision nor materializes a preset. Feature and QA
555
+ validation page templates are not included; use the page-type guidance to
556
+ plan their content.
557
+
558
+ ````markdown
559
+ # IA Proposal: {surface}
560
+
561
+ ## Evidence and guidance
562
+
563
+ - Resolved sources, in order: {path/URL, provenance: explicit/conversation/shared/user/builtin}
564
+ - Local authority: {AGENTS/contributing paths and relevant rules}
565
+ - Repo signals: {manifests, navigators, bin, routers, IaC, e2e, docs, units}
566
+ - Readers and work contexts: {who must succeed at what}
567
+ - Entry key per track: {task/domain/capability/unit/command/page type}
568
+ - Page purposes: {one primary job per representative page}
569
+ - Verification overlay: {triad/agent-only/none and evidence}
570
+ - Size gate: {page count and amount of structure justified}
571
+ - Canonical facts elsewhere and links to preserve: {paths/URLs}
572
+ - Unresolved conflicts: {ask_user finding with source citations, or none}
573
+
574
+ ## Candidate A: {reader-facing name}
575
+
576
+ Nearest preset: {approved label}. Choose when: {signals and tradeoffs}.
577
+
578
+ ```text
579
+ {track-or-section}/
580
+ {page.md} — {page's one job}
581
+ {page.md} — {page's one job}
582
+ {track-or-section}/
583
+ {page.md} — {page's one job}
584
+ {page.md} — {page's one job}
585
+ {page.md} — {page's one job}
586
+ ```
587
+
588
+ Use a two-level tree and 5–8 representative page placements. For Flat Small,
589
+ first-level labels are grouped headings in one index, not new directories.
590
+
591
+ ## Candidate B: {alternative reader-facing name}
592
+
593
+ Nearest preset: {approved label}. Choose when: {different plausible signals}.
594
+
595
+ {Second two-level tree with 5–8 representative page placements and page jobs.}
596
+
597
+ ## Recommendation and open decisions
598
+
599
+ {Preferred candidate, evidence and tradeoffs. Ask only decision-changing
600
+ questions. Record an explicit choice before replacement or a second app.
601
+ Existing docs default to analyze → apply of approved recommendations.
602
+ No private exemplar text, copied external contents or unapproved moves.}
603
+ ````
@@ -80,7 +80,12 @@ Most docs serve one or more of these readers:
80
80
  - support engineer
81
81
  - AI agent making future changes
82
82
 
83
- Write pages for actual tasks, not org-chart abstractions.
83
+ Map personas to tracks by work context: consumers and CLI/API users usually
84
+ enter through tasks or contracts; maintainers through domains and operations;
85
+ product stakeholders through behavior; independent QA through validation;
86
+ agents through exact task inputs and checks. One person may use several tracks.
87
+ Use the five axes in [Information Architecture](information-architecture.md)
88
+ before naming a preset. Write pages for actual tasks, not org-chart abstractions.
84
89
 
85
90
  ## 5. Decide the Minimum Useful Set
86
91
 
@@ -93,7 +93,7 @@ Use notes for useful context, not ordinary tips.
93
93
  - Use one `#` heading per page.
94
94
  - Do not skip heading levels.
95
95
  - Use fenced code blocks with language identifiers.
96
- - Use `sh` for shell commands.
96
+ - Use `bash` for shell commands unless the example is POSIX-only (`sh`) or local repository conventions require another fence.
97
97
  - Use `txt` for command output.
98
98
  - Do not include shell prompts unless the prompt itself matters.
99
99
  - Use angle brackets for placeholders, such as `<environment>`.
@@ -0,0 +1,53 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { parseArgs } from 'node:util';
3
+
4
+ // Select inputs only. The consuming skill reads Markdown and checks Contents,
5
+ // local authority and conflicts; this helper never fetches or writes a source.
6
+ const { values } = parseArgs({
7
+ options: {
8
+ builtin: { type: 'string' },
9
+ 'ia-guidance': { type: 'string', multiple: true },
10
+ 'conversation-guidance': { type: 'string', multiple: true },
11
+ },
12
+ });
13
+ const explicit = values['ia-guidance'];
14
+ const conversation = values['conversation-guidance'];
15
+ if (explicit && conversation) {
16
+ throw new Error(
17
+ 'Use one explicit source list; reconcile argument/conversation disagreement with the user first.',
18
+ );
19
+ }
20
+ const input = JSON.parse(readFileSync(0, 'utf8'));
21
+ if (input.status !== 'ok' || input.key !== 'documentation.guidance') {
22
+ throw new Error(
23
+ 'Expected successful oat config get documentation.guidance --json output.',
24
+ );
25
+ }
26
+ let locations;
27
+ let source;
28
+ if (explicit || conversation) {
29
+ locations = explicit ?? conversation;
30
+ source = explicit ? 'explicit' : 'conversation';
31
+ } else if (input.value !== null && input.value !== undefined) {
32
+ if (!['shared', 'user'].includes(input.source)) {
33
+ throw new Error(
34
+ `Unsupported documentation.guidance config source: ${input.source}`,
35
+ );
36
+ }
37
+ locations = Array.isArray(input.value) ? input.value : [input.value];
38
+ source = input.source;
39
+ } else {
40
+ locations = [values.builtin];
41
+ source = 'builtin';
42
+ }
43
+ if (
44
+ !locations.length ||
45
+ locations.some((location) => typeof location !== 'string' || !location.trim())
46
+ ) {
47
+ throw new Error(
48
+ 'Guidance requires non-empty path/URL strings (or --builtin for fallback).',
49
+ );
50
+ }
51
+ process.stdout.write(
52
+ `${JSON.stringify({ sources: locations.map((location) => ({ location: location.trim(), source })) }, null, 2)}\n`,
53
+ );
@@ -1,11 +1,12 @@
1
1
  ---
2
2
  name: oat-docs-analyze
3
3
  description: Run when you need to evaluate documentation structure, navigation, and coverage against the OAT documentation contract. Produces a severity-rated analysis artifact for oat-docs-apply.
4
+ argument-hint: '[target] [--ia] [--ia-guidance <path|url>]'
4
5
  disable-model-invocation: true
5
6
  user-invocable: true
6
- allowed-tools: Read, Write, Bash(git:*), Bash(oat config:*), Bash(oat docs nav sync --check:*), Bash(pnpm:*), Bash(bash:*), Bash(jq:*), Bash(mkdir:*), Bash(date:*), Bash(echo:*), Bash(cd:*), Bash(pwd:*), Glob, Grep, AskUserQuestion, Task
7
+ allowed-tools: Read, Write, Bash(git:*), Bash(oat config:*), Bash(oat docs nav sync --check:*), Bash(pnpm:*), Bash(bash:*), Bash(jq:*), Bash(node:*), Bash(mkdir:*), Bash(date:*), Bash(echo:*), Bash(cd:*), Bash(pwd:*), Glob, Grep, AskUserQuestion, Task
7
8
  metadata:
8
- version: '1.6.3'
9
+ version: '1.7.1'
9
10
  ---
10
11
 
11
12
  # Docs Analysis
@@ -85,6 +86,39 @@ When executing this skill, provide lightweight progress feedback so the user can
85
86
 
86
87
  ## Process
87
88
 
89
+ ### Shared authoring method and optional IA proposal
90
+
91
+ Independently probe each exact `authoring-docs/SKILL.md` in order:
92
+ `${SKILL_DIR}/..` from the loaded skill (`SKILL_DIR` is the absolute directory
93
+ containing that loaded `SKILL.md`), `${HOME}/.agents/skills`, then
94
+ `<repo-root>/.agents/skills`. Bind the first matching `authoring-docs` directory
95
+ once as `AUTHORING_DOCS_ROOT`; load `${AUTHORING_DOCS_ROOT}/SKILL.md` and use
96
+ that same root for every authoring-docs reference and script, never ambient
97
+ discovery. If none is found, name `authoring-docs`, stop this handoff, and give
98
+ the intended-scope recovery command: `oat tools install docs --scope <user|project>`
99
+ or `oat tools update --pack docs --scope <user|project>`.
100
+
101
+ Always use its landing-page contract, section entries and one-purpose-per-page
102
+ rule for the quality pass even without `--ia`. For IA work, read
103
+ `${AUTHORING_DOCS_ROOT}/references/guidance-sources.md` and use the source-aware config-get
104
+ interface/helper: explicit `--ia-guidance` or conversation → repo → user →
105
+ built-in. Preserve selected source order and cite locators/provenance in the
106
+ analysis artifact. External Markdown packs require `index.md` and `## Contents`;
107
+ non-repo inputs stay read-only and private contents never enter the artifact.
108
+
109
+ `--ia` is a skill argument, not a CLI docs command. It adds the shared IA Proposal
110
+ to the **same analysis artifact and existing artifact-review loop**. Read manifests,
111
+ UI navigators/journeys, CLI `bin`, routers, IaC, `e2e/`/`.maestro/`, existing docs
112
+ and independent monorepo units. Ask only decision-changing questions about reader
113
+ work contexts, independent verification, canonical facts and link survival.
114
+ Use the five axes before presets and show two plausible two-level trees with
115
+ 5–8 representative page placements each, page jobs, nearest approved presets
116
+ and tradeoffs. A UI alone does not justify Product/Engineering/QA; size chooses
117
+ depth, not audience. For an existing tree respect AGENTS/contributing authority;
118
+ conflicts are `ask_user`, with both source citations. Analyze remains read-only:
119
+ propose `record-ia` or `restructure` recommendations for apply; never persist
120
+ IA or materialize scaffolds from analysis.
121
+
88
122
  ### Step 0: Resolve Docs Target and Analysis Mode
89
123
 
90
124
  Determine the documentation root using explicit docs-app evidence before generic
@@ -267,8 +301,8 @@ For each evaluated page or directory:
267
301
  syntax intentionally shown inside inline code, fenced examples, or template
268
302
  snippets.
269
303
  4. Check Markdown hygiene: opening code fences need language identifiers; shell
270
- examples should follow local fence conventions, defaulting to `sh` unless
271
- local guidance uses `bash` or the block needs Bash-only syntax.
304
+ examples should follow local fence conventions, defaulting to `bash` unless
305
+ the example is POSIX-only (`sh`) or local guidance requires another fence.
272
306
  5. Flag empty headings, multiple document-level H1s outside intentional imported
273
307
  README contexts, overlong frontmatter descriptions when local guidance
274
308
  defines a limit, ellipsis-truncated descriptions, and README-copy metadata
@@ -284,6 +318,37 @@ For each evaluated page or directory:
284
318
  In `delta` mode, always evaluate changed docs files plus the nearest parent `index.md` pages.
285
319
  In `full` mode, evaluate the whole docs surface.
286
320
 
321
+ ### Information Architecture Fit
322
+
323
+ Read the authored IA decision and `## Information architecture` section in the
324
+ docs-app AGENTS.md (Fumadocs/MkDocs) or contributing page (Markdown).
325
+ `documentation.ia` is a repository-only mirror: compare preset, decision path,
326
+ tracks/principles and verification overlay to those authored sources. The
327
+ authored record is authoritative; missing/broken records or contradictory
328
+ mirrors become `ask_user` findings. No recorded IA means `ask_user` to choose
329
+ and record one, not an inferred preset. Keep ordinary contract analysis useful
330
+ while those recommendations are blocked.
331
+
332
+ In the artifact's **Information Architecture Fit** section, cite whether the
333
+ landing page routes each declared audience, all declared sections exist and are
334
+ reachable, page reader/purpose matches placement, root breadth is usable, and an
335
+ app's docs avoid merely mirroring implementation directories. Review coverage
336
+ only for declared readers: Product features/journeys/screens/states; QA scenario
337
+ tables with seeds and automation references; consumer first-success/task guides;
338
+ agent entrypoints, canonical sources and verification commands. Do not demand
339
+ these tracks from every repository or implement the later P24 source checks.
340
+
341
+ Top-level IA misroutes a declared audience: `High` when the reader cannot find
342
+ a normal first-success or required verification path; `Medium` when that path
343
+ is reachable but its placement causes material detours/confusion. Cite the
344
+ declared audience, route and concrete reader impact. This rule overrides Low
345
+ organization polish; do not upgrade purely cosmetic differences.
346
+
347
+ For framework apps read `documentation.navOwner` independently of IA. Missing
348
+ means **undeclared**: issue `ask_user` to choose `nav-sync` or `hand-authored`,
349
+ and run only read-only membership/order comparisons. Never assume nav sync owns
350
+ existing navigation. Markdown ignores navOwner and never runs nav sync.
351
+
287
352
  ### Step 4: Verify Substantive Claims Against Repo Sources
288
353
 
289
354
  Add a dedicated accuracy verification pass between page-quality assessment and nav/drift checks.
@@ -405,8 +470,8 @@ If a generated root index or manifest exists:
405
470
  files, and representative links for each finding.
406
471
  5. Prefer source-of-truth fixes over generated-file edits.
407
472
 
408
- If the app is Fumadocs (a `source.config.ts` beside `docs/`), its sidebar comes from
409
- `docs/**/meta.json` files that `oat docs nav sync` generates from `## Contents`:
473
+ For a Fumadocs app with recorded `navOwner: nav-sync`, compare generated
474
+ `docs/**/meta.json` with authored `## Contents`:
410
475
 
411
476
  1. Flag a docs directory with an `index.md` but no `meta.json` as a missing
412
477
  generated artifact.
@@ -430,6 +495,13 @@ If the app is Fumadocs (a `source.config.ts` beside `docs/`), its sidebar comes
430
495
  1 when either exists. The skill grants only this `--check` form; plain
431
496
  `oat docs nav sync` writes files and is not part of analysis.
432
497
 
498
+ For `hand-authored` or undeclared Fumadocs navigation, compare current meta.json
499
+ membership/order with Contents without assuming strict generator output; do not
500
+ flag rest entries merely because they are hand-authored. Report missing reader
501
+ routes and differences with local ownership evidence; recommend no automatic
502
+ regeneration. For nav-sync-owned MkDocs, use the same explicit-target read-only
503
+ `oat docs nav sync --check --target-dir <docs-app-dir>`.
504
+
433
505
  If `mkdocs.yml` exists:
434
506
 
435
507
  1. Compare nav entries with the docs tree.
@@ -443,6 +515,53 @@ site nav generation or builds. Lack of `mkdocs.yml`, a generated manifest, or an
443
515
  app package is expected, not a migration finding. For other unconfigured surfaces,
444
516
  record whether app migration is relevant only when evidence or user intent supports it.
445
517
 
518
+ ### Offline docs-package drift
519
+
520
+ For framework apps read their package.json and installed docs packages against
521
+ the running CLI's bundled `assets/public-package-versions.json`, without network.
522
+ Run this skill's production helper after resolving this skill's own directory:
523
+
524
+ ```bash
525
+ node "$SKILL_DIR/scripts/package-drift.mjs" --app "<docs-app-dir>" --cli "$(command -v oat)"
526
+ ```
527
+
528
+ If a package-manager wrapper prevents locating the CLI root, discover the bundle
529
+ from that invoked installation offline. Inspect `command -v oat` and, for a shell
530
+ shim, read its `exec` lines or `cmd-shim-target` comment (for example,
531
+ `sed -n '1,160p' "$(command -v oat)"`). Resolve a relative JavaScript target from
532
+ the shim's directory; do not evaluate copied shell text. Locate that target's
533
+ package root containing `package.json` named `@open-agent-toolkit/cli` and
534
+ `assets/public-package-versions.json`. `pnpm root -g` or `npm root -g` can help
535
+ locate it, but newer pnpm installations may nest it in a hashed directory; use
536
+ the actual shim target, never another checkout's bundle. Bind the inspected paths:
537
+
538
+ ```bash
539
+ OAT_INSTALLED_ENTRY="<absolute JavaScript target from the invoked shim>"
540
+ OAT_INSTALLED_ROOT="<that installed @open-agent-toolkit/cli package root>"
541
+ # Both must equal the invoked oat version; stop if either differs.
542
+ test "$(node "$OAT_INSTALLED_ENTRY" --version)" = "$(oat --version)" &&
543
+ test "$(node -p 'JSON.parse(require("node:fs").readFileSync(process.argv[1], "utf8")).version' "$OAT_INSTALLED_ROOT/package.json")" = "$(oat --version)" &&
544
+ node "$SKILL_DIR/scripts/package-drift.mjs" --app "<docs-app-dir>" --versions "$OAT_INSTALLED_ROOT/assets/public-package-versions.json"
545
+ ```
546
+
547
+ Never substitute registry latest or this target repo's package version. Missing
548
+ bundle metadata, missing installed packages, or non-orderable versions (including
549
+ prereleases) mean unverified, never an upgrade. The helper compares stable numeric
550
+ version components: older installed packages are `drift`; equal versions are
551
+ `current`; newer installed packages are `ahead`, with no upgrade command. For
552
+ `ahead`, report that the running CLI is stale and ask the operator to update or
553
+ select the intended CLI installation before re-analysis; do not recommend a docs
554
+ package downgrade. `unverified` also has no package command and needs manual
555
+ verification. Neither status authorizes a tooling update.
556
+
557
+ The helper reports declared and installed versions, target versions and commands
558
+ for orderable targets. If the app uses a different package manager, translate the
559
+ command to its documented equivalent preserving package, exact target and
560
+ dependency section. Cite the manifest, installed package path, bundle path and
561
+ exact command. Report older-package drift as `tooling-update`, `approval: explicit`;
562
+ describe unsupported scaffold repairs separately as `manual`. Markdown has no
563
+ app-package drift check. This helper reads only and never upgrades anything.
564
+
446
565
  ### Step 7: Severity-Rate Findings
447
566
 
448
567
  Use these defaults:
@@ -452,6 +571,19 @@ Use these defaults:
452
571
  - `Medium`: incomplete `## Contents`, `overview.md` still in use, plugin/contributor guidance gaps, moderate duplication
453
572
  - `Low`: polish, wording, or organization improvements
454
573
 
574
+ ### Recommendation handoff
575
+
576
+ Give every recommendation a unique stable ID (`R-001`, etc.) and
577
+ `approval: explicit | default`; preserve IDs through review and never reuse a
578
+ retired ID for a different change. `explicit` covers docs-package updates,
579
+ restructure blocks and anything outside the Markdown scope (including authored
580
+ IA decision/config writes). `default` covers ordinary in-scope content repairs.
581
+ Missing evidence, `omit` and `ask_user` remain independent blockers regardless
582
+ of approval class. Capture exact targets and commands; an explicit class is not
583
+ permission to perform unsupported work. Apply's one input is this artifact plus
584
+ a list of approved recommendation IDs. Blanket or unattended approval never
585
+ covers explicit recommendations; apply reports skipped IDs with reasons.
586
+
455
587
  ### Step 8: Write Analysis Artifact
456
588
 
457
589
  Use `references/analysis-artifact-template.md`.
@@ -473,7 +605,8 @@ Populate the artifact with:
473
605
  - Accuracy verification verdicts for repo-checkable claims
474
606
  - Content opportunities for missing or thin docs coverage
475
607
  - Navigation/drift findings
476
- - Ordered recommendations
608
+ - Information Architecture Fit and offline package drift
609
+ - Ordered recommendations with stable IDs and approval classes
477
610
  - Exact evidence references for each finding and recommendation
478
611
  - Confidence for each recommendation
479
612
  - Progressive disclosure decisions (`inline`, `link_only`, `omit`, `ask_user`)
@@ -15,6 +15,51 @@ oat_analysis_commit: { commitHash }
15
15
  **Surface Type:** {mkdocs-app|oat-fumadocs-app|docs-tree|root-markdown}
16
16
  **Commit:** {short-hash}
17
17
 
18
+ ## IA guidance and proposal
19
+
20
+ - Selected sources in order: {locator + explicit/conversation/shared/user/builtin provenance}
21
+ - Resolved index entries: {entry paths/URLs; external Markdown has index.md + Contents}
22
+ - Local authority: {AGENTS/contributing source citations}
23
+ - Conflicts/unreadable sources: {ask_user with both citations, or none}
24
+ - IA proposal: {when --ia, embed the shared authoring-docs IA Proposal template:
25
+ two two-level trees, 5–8 placements each, page jobs, nearest approved presets,
26
+ evidence and tradeoffs; otherwise not requested}
27
+
28
+ Do not copy private guidance contents or persist an IA decision from this report.
29
+
30
+ ## Information Architecture Fit
31
+
32
+ - Authored decision and Information architecture section: {paths/lines or missing}
33
+ - Config mirror: {preset, decision, tracks/principles, verification; agrees/conflicts}
34
+ - Missing IA / conflicting authority: {ask_user recommendation ID or none}
35
+ - Sidebar owner: {nav-sync / hand-authored / undeclared; Markdown: ignored}
36
+ - Undeclared framework owner: {ask_user recommendation ID or none}
37
+
38
+ | Declared reader/track | Landing route and section reachability | Placement/coverage evidence | Reader impact | Severity |
39
+ | -------------------------------------------------------- | -------------------------------------- | ----------------------------------------- | -------------------------------------------------------------- | --------------------- |
40
+ | {Product / QA / consumer / agent / other declared track} | {exact links} | {sources; page purpose; missing coverage} | {blocked first success/verification, material detour, or none} | {High / Medium / N/A} |
41
+
42
+ Assess root breadth and implementation-directory mirroring using reader evidence.
43
+ Do not invent missing audiences or choose an IA without approval.
44
+
45
+ ## Offline Package Drift
46
+
47
+ - Bundle metadata: {running CLI public-package-versions.json path or unverified}
48
+ - Docs app manifest: {path; Markdown: not applicable}
49
+
50
+ | Package | Declared / installed | Bundled | Status | Exact upgrade command | Recommendation ID |
51
+ | ----------------------------- | ----------------------------------- | --------- | -------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------- |
52
+ | {@open-agent-toolkit/docs-\*} | {range / actual version or missing} | {version} | {current / drift / ahead / unverified} | {drift only: helper upgrade command adapted to actual manager; otherwise none} | {drift only: R-NNN; approval: explicit; otherwise none} |
53
+
54
+ Only older installed packages (`drift`) receive a package-upgrade command and
55
+ stable recommendation ID with `approval: explicit`. `current` needs no tooling
56
+ recommendation, even if the helper emits a reinstall command. For `ahead`, select
57
+ or update the intended CLI installation, then re-analyze; do not recommend a docs
58
+ package downgrade. `unverified` requires manual verification before a package
59
+ recommendation.
60
+
61
+ Unsupported app config/routes/scripts: {manual changes required; never automatic}.
62
+
18
63
  ## Summary
19
64
 
20
65
  - **Files evaluated:** {N}
@@ -258,14 +303,18 @@ canonical docs/config/examples.
258
303
 
259
304
  1. **{Action}** — {rationale}
260
305
  - Target: `{path}`
261
- - Action Type: {create / update / move / scaffold / sync-nav}
306
+ - ID: {R-NNN; unique and stable}
307
+ - approval: {explicit | default}
308
+ - Action Type: {create / update / move / scaffold / sync-nav / record-ia / restructure / tooling-update / manual}
262
309
  - Evidence: {exact refs}
263
310
  - Confidence: {high | medium | low}
264
311
  - Disclosure: {inline | link_only | omit | ask_user}
265
312
  - Link Targets: {path or URL / N/A}
266
313
  2. **{Action}** — {rationale}
267
314
  - Target: `{path}`
268
- - Action Type: {create / update / move / scaffold / sync-nav}
315
+ - ID: {R-NNN; unique and stable}
316
+ - approval: {explicit | default}
317
+ - Action Type: {create / update / move / scaffold / sync-nav / record-ia / restructure / tooling-update / manual}
269
318
  - Evidence: {exact refs}
270
319
  - Confidence: {high | medium | low}
271
320
  - Disclosure: {inline | link_only | omit | ask_user}
@@ -274,6 +323,9 @@ canonical docs/config/examples.
274
323
 
275
324
  ## Apply Contract
276
325
 
326
+ - `oat-docs-apply` input is this artifact path plus approved recommendation IDs.
327
+ - Every recommendation has a unique stable ID and approval: explicit | default.
328
+ - Explicit items need individual approval by ID; blanket/unattended approval skips them and reports the reason.
277
329
  - `oat-docs-apply` may only implement recommendations backed by evidence in this artifact.
278
330
  - Findings based on contradicted claims must be resolved against cited repo sources before `oat-docs-apply` acts on them.
279
331
  - Generated artifacts must not be hand-edited by `oat-docs-apply`. If a
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env node
2
+ // Read-only, offline comparison used by oat-docs-analyze. No package resolution or install.
3
+ import { readFile, realpath } from 'node:fs/promises';
4
+ import { dirname, join, resolve } from 'node:path';
5
+ import { parseArgs } from 'node:util';
6
+
7
+ const shellQuote = (value) =>
8
+ `'${value.replaceAll("'", String.fromCharCode(39, 92, 39, 39))}'`;
9
+
10
+ async function versionsForCli(executable) {
11
+ if (!executable)
12
+ throw new Error(
13
+ 'Provide --cli with the installed oat executable or --versions with its bundled public-package-versions.json.',
14
+ );
15
+ let directory = dirname(await realpath(executable));
16
+ for (;;) {
17
+ try {
18
+ const pkg = JSON.parse(
19
+ await readFile(join(directory, 'package.json'), 'utf8'),
20
+ );
21
+ if (pkg.name === '@open-agent-toolkit/cli')
22
+ return join(directory, 'assets/public-package-versions.json');
23
+ } catch (error) {
24
+ if (error.code !== 'ENOENT') throw error;
25
+ }
26
+ const parent = dirname(directory);
27
+ if (parent === directory)
28
+ throw new Error(
29
+ 'Cannot locate the running CLI bundle through this wrapper; supply --versions from that installed CLI.',
30
+ );
31
+ directory = parent;
32
+ }
33
+ }
34
+
35
+ try {
36
+ const { values } = parseArgs({
37
+ options: {
38
+ app: { type: 'string' },
39
+ versions: { type: 'string' },
40
+ cli: { type: 'string' },
41
+ },
42
+ });
43
+ if (!values.app) throw new Error('Required: --app <docs-app-directory>.');
44
+ const versionsPath = resolve(
45
+ values.versions ?? (await versionsForCli(values.cli)),
46
+ );
47
+ const versions = JSON.parse(await readFile(versionsPath, 'utf8'));
48
+ const appPath = resolve(values.app);
49
+ const manifest = JSON.parse(
50
+ await readFile(join(appPath, 'package.json'), 'utf8'),
51
+ );
52
+ const packages = [];
53
+ for (const section of [
54
+ 'dependencies',
55
+ 'devDependencies',
56
+ 'optionalDependencies',
57
+ ]) {
58
+ for (const [name, declared] of Object.entries(manifest[section] ?? {})) {
59
+ if (
60
+ ![
61
+ '@open-agent-toolkit/docs-config',
62
+ '@open-agent-toolkit/docs-theme',
63
+ '@open-agent-toolkit/docs-transforms',
64
+ ].includes(name)
65
+ )
66
+ continue;
67
+ const bundled = versions[name.slice('@open-agent-toolkit/'.length)];
68
+ if (
69
+ typeof bundled !== 'string' ||
70
+ !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(bundled)
71
+ )
72
+ throw new Error(`Missing/invalid bundled version for ${name}.`);
73
+ // Report installed package bytes when available, not just a declared range.
74
+ let installed = null;
75
+ try {
76
+ const installedManifest = JSON.parse(
77
+ await readFile(
78
+ join(appPath, 'node_modules', name, 'package.json'),
79
+ 'utf8',
80
+ ),
81
+ );
82
+ if (typeof installedManifest.version !== 'string')
83
+ throw new Error(`Invalid installed version for ${name}.`);
84
+ installed = installedManifest.version;
85
+ } catch (error) {
86
+ if (error.code !== 'ENOENT') throw error;
87
+ }
88
+ // Only stable numeric releases are orderable here; prereleases and other
89
+ // package-manager version forms need manual verification, never an upgrade.
90
+ const stable = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
91
+ let status = 'unverified';
92
+ if (
93
+ installed !== null &&
94
+ stable.test(installed) &&
95
+ stable.test(bundled)
96
+ ) {
97
+ const left = installed.split('.').map(BigInt);
98
+ const right = bundled.split('.').map(BigInt);
99
+ const differing = left.findIndex(
100
+ (part, index) => part !== right[index],
101
+ );
102
+ status =
103
+ differing === -1
104
+ ? 'current'
105
+ : left[differing] < right[differing]
106
+ ? 'drift'
107
+ : 'ahead';
108
+ }
109
+ packages.push({
110
+ name,
111
+ section,
112
+ declared,
113
+ installed,
114
+ bundled,
115
+ status,
116
+ upgradeCommand:
117
+ status === 'drift' || status === 'current'
118
+ ? `pnpm --dir ${shellQuote(appPath)} add${section === 'devDependencies' ? ' --save-dev' : section === 'optionalDependencies' ? ' --save-optional' : ''} ${name}@${bundled}`
119
+ : null,
120
+ });
121
+ }
122
+ }
123
+ process.stdout.write(
124
+ `${JSON.stringify({ versionsPath, appPath, packages }, null, 2)}\n`,
125
+ );
126
+ } catch (error) {
127
+ process.stderr.write(`${error.message}\n`);
128
+ process.exitCode = 1;
129
+ }