@open-agent-toolkit/cli 0.2.64 → 0.2.65

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 (79) hide show
  1. package/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/contributing/skills.md +15 -5
  3. package/assets/public-package-versions.json +4 -4
  4. package/assets/skills/analyze/SKILL.md +2 -1
  5. package/assets/skills/authoring-docs/SKILL.md +2 -1
  6. package/assets/skills/compare/SKILL.md +2 -1
  7. package/assets/skills/create-agnostic-skill/SKILL.md +2 -1
  8. package/assets/skills/deep-research/SKILL.md +2 -1
  9. package/assets/skills/explainer-kit/SKILL.md +2 -1
  10. package/assets/skills/oat-agent-instructions-analyze/SKILL.md +2 -1
  11. package/assets/skills/oat-agent-instructions-apply/SKILL.md +2 -1
  12. package/assets/skills/oat-brainstorm/SKILL.md +2 -1
  13. package/assets/skills/oat-cursor-cloud-projects/SKILL.md +2 -1
  14. package/assets/skills/oat-dispatch-subagents/SKILL.md +2 -1
  15. package/assets/skills/oat-docs/SKILL.md +2 -1
  16. package/assets/skills/oat-docs-analyze/SKILL.md +2 -1
  17. package/assets/skills/oat-docs-apply/SKILL.md +2 -1
  18. package/assets/skills/oat-docs-authoring/SKILL.md +2 -1
  19. package/assets/skills/oat-docs-bootstrap/SKILL.md +2 -1
  20. package/assets/skills/oat-doctor/SKILL.md +2 -1
  21. package/assets/skills/oat-explainer-kit/SKILL.md +2 -1
  22. package/assets/skills/oat-explainer-kit/scripts/check-core.mjs +322 -2
  23. package/assets/skills/oat-idea-ideate/SKILL.md +2 -1
  24. package/assets/skills/oat-idea-new/SKILL.md +2 -1
  25. package/assets/skills/oat-idea-scratchpad/SKILL.md +2 -1
  26. package/assets/skills/oat-idea-summarize/SKILL.md +2 -1
  27. package/assets/skills/oat-pjm-add-backlog-item/SKILL.md +2 -1
  28. package/assets/skills/oat-pjm-decision/SKILL.md +2 -1
  29. package/assets/skills/oat-pjm-review-backlog/SKILL.md +2 -1
  30. package/assets/skills/oat-pjm-update-repo-reference/SKILL.md +2 -1
  31. package/assets/skills/oat-project-autonomous/SKILL.md +2 -1
  32. package/assets/skills/oat-project-capture/SKILL.md +2 -1
  33. package/assets/skills/oat-project-clear-active/SKILL.md +2 -1
  34. package/assets/skills/oat-project-complete/SKILL.md +2 -1
  35. package/assets/skills/oat-project-design/SKILL.md +2 -1
  36. package/assets/skills/oat-project-discover/SKILL.md +2 -1
  37. package/assets/skills/oat-project-dispatch-subagents/SKILL.md +2 -1
  38. package/assets/skills/oat-project-document/SKILL.md +2 -1
  39. package/assets/skills/oat-project-implement/SKILL.md +2 -1
  40. package/assets/skills/oat-project-import-plan/SKILL.md +2 -1
  41. package/assets/skills/oat-project-lite/SKILL.md +2 -1
  42. package/assets/skills/oat-project-new/SKILL.md +2 -1
  43. package/assets/skills/oat-project-next/SKILL.md +2 -1
  44. package/assets/skills/oat-project-open/SKILL.md +2 -1
  45. package/assets/skills/oat-project-plan/SKILL.md +2 -1
  46. package/assets/skills/oat-project-plan-writing/SKILL.md +2 -1
  47. package/assets/skills/oat-project-pr-final/SKILL.md +2 -1
  48. package/assets/skills/oat-project-pr-progress/SKILL.md +2 -1
  49. package/assets/skills/oat-project-progress/SKILL.md +2 -1
  50. package/assets/skills/oat-project-promote-spec-driven/SKILL.md +2 -1
  51. package/assets/skills/oat-project-quick-start/SKILL.md +2 -1
  52. package/assets/skills/oat-project-reconcile/SKILL.md +2 -1
  53. package/assets/skills/oat-project-retro/SKILL.md +2 -1
  54. package/assets/skills/oat-project-retro-file/SKILL.md +2 -1
  55. package/assets/skills/oat-project-review-provide/SKILL.md +2 -1
  56. package/assets/skills/oat-project-review-provide-remote/SKILL.md +2 -1
  57. package/assets/skills/oat-project-review-receive/SKILL.md +2 -1
  58. package/assets/skills/oat-project-review-receive-remote/SKILL.md +2 -1
  59. package/assets/skills/oat-project-revise/SKILL.md +2 -1
  60. package/assets/skills/oat-project-spec/SKILL.md +2 -1
  61. package/assets/skills/oat-project-split/SKILL.md +2 -1
  62. package/assets/skills/oat-project-summary/SKILL.md +2 -1
  63. package/assets/skills/oat-repo-improve/SKILL.md +1 -1
  64. package/assets/skills/oat-repo-knowledge-index/SKILL.md +2 -1
  65. package/assets/skills/oat-repo-maintainability-review/SKILL.md +2 -1
  66. package/assets/skills/oat-review-provide/SKILL.md +2 -1
  67. package/assets/skills/oat-review-provide-remote/SKILL.md +2 -1
  68. package/assets/skills/oat-review-receive/SKILL.md +2 -1
  69. package/assets/skills/oat-review-receive-remote/SKILL.md +2 -1
  70. package/assets/skills/oat-wave-execute/SKILL.md +2 -1
  71. package/assets/skills/oat-wave-program/SKILL.md +2 -1
  72. package/assets/skills/oat-worktree-bootstrap/SKILL.md +2 -1
  73. package/assets/skills/oat-worktree-bootstrap-auto/SKILL.md +2 -1
  74. package/assets/skills/oat-wrap-up/SKILL.md +2 -1
  75. package/assets/skills/recon/SKILL.md +2 -1
  76. package/assets/skills/skeptic/SKILL.md +2 -1
  77. package/assets/skills/subagent-orchestration/SKILL.md +2 -1
  78. package/assets/skills/synthesize/SKILL.md +2 -1
  79. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "oatVersion": "0.2.64"
3
+ "oatVersion": "0.2.65"
4
4
  }
@@ -204,9 +204,14 @@ resolution. `resolveSkillVersion` in
204
204
  runtime helper and the validators cannot disagree.
205
205
 
206
206
  The top-level `version` field remains supported as a deprecated alias. Every
207
- bundled skill still uses it today, so `pnpm oat:validate-skills` reports each
208
- one as a non-blocking warning; the value is still read, and nothing breaks.
209
- Migrating the bundled skills is tracked separately.
207
+ bundled skill now declares `metadata.version` and carries no top-level
208
+ `version`, so `pnpm oat:validate-skills` reports no alias warnings on the
209
+ bundled tree. The alias is retained for third-party skills installed from
210
+ packs, which may still carry it; it retires on the schedule recorded in
211
+ `DR-260908-bundled-skills-declare` — the warning becomes an error in the first
212
+ release after 0.2.65 that changes the validator, and the resolver's top-level
213
+ read is removed one release after that error has produced no findings on the
214
+ bundled tree.
210
215
 
211
216
  A skill that carries both fields with different values is a conflict. The
212
217
  resolver reports the `metadata.version` value together with the conflict, and
@@ -214,7 +219,12 @@ the callers that must not guess act on it: validation reports an error rather
214
219
  than silently picking one, and canonical role resolution treats the identity as
215
220
  invalid. The runtime version readers still return the `metadata.version` value,
216
221
  so an installed-versus-bundled comparison keeps working while the conflict is
217
- being fixed. Carrying both with the _same_ value is accepted.
222
+ being fixed. Carrying both with the _same_ value resolves without a conflict and
223
+ without an alias warning, because `metadata.version` wins. A bundled skill must
224
+ still carry `metadata.version` alone: the corpus sweep in
225
+ `packages/cli/src/validation/skills.test.ts` fails on any column-0 `version:`
226
+ line under `.agents/skills`, which is the only check that catches the
227
+ same-value case.
218
228
 
219
229
  ### The version is gated
220
230
 
@@ -227,7 +237,7 @@ resolved version, so it enforces the bump whether the skill declares
227
237
  of Done) and in CI; a changed skill whose version matches `origin/main` fails
228
238
  the gate. The deprecation warning above is deliberately not part of that gate:
229
239
  `check:skill-bumps` fails on any finding it receives, so an alias warning
230
- routed through it would fail every changed skill until the migration lands.
240
+ routed through it would fail every changed skill that still carried one.
231
241
 
232
242
  ## Practical Authoring Flow
233
243
 
@@ -1,6 +1,6 @@
1
1
  {
2
- "cli": "0.2.64",
3
- "docs-config": "0.2.64",
4
- "docs-theme": "0.2.64",
5
- "docs-transforms": "0.2.64"
2
+ "cli": "0.2.65",
3
+ "docs-config": "0.2.65",
4
+ "docs-theme": "0.2.65",
5
+ "docs-transforms": "0.2.65"
6
6
  }
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  name: analyze
3
- version: 0.2.0
4
3
  description: Multi-angle analysis of existing artifacts, codebases, documents, or systems. Examines what you have from six analysis angles and produces structured findings with prioritized recommendations.
5
4
  argument-hint: 'target [--context path]'
6
5
  user-invocable: true
7
6
  allowed-tools: Read, Glob, Grep, Bash, WebSearch, WebFetch, AskUserQuestion, Agent, mcp__*
7
+ metadata:
8
+ version: 0.2.1
8
9
  ---
9
10
 
10
11
  # /analyze
@@ -1,11 +1,12 @@
1
1
  ---
2
2
  name: authoring-docs
3
- version: 1.0.0
4
3
  description: Use when creating, restructuring, migrating, auditing, or reviewing technical documentation for software projects. Provides evidence-first, provider-portable Markdown authoring standards.
5
4
  argument-hint: '[docs task or target path]'
6
5
  disable-model-invocation: false
7
6
  allowed-tools: Read, Write, Bash, Glob, Grep, AskUserQuestion
8
7
  user-invocable: true
8
+ metadata:
9
+ version: 1.0.1
9
10
  ---
10
11
 
11
12
  # Authoring Docs
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  name: compare
3
- version: 0.1.1
4
3
  description: Domain-aware comparative analysis with clear recommendations. Compares options across auto-detected or user-specified dimensions and produces a qualitative assessment with a clear winner.
5
4
  argument-hint: 'item1 item2 [item3...] [--save] [--context path] [--dimensions "dim1, dim2, ..."]'
6
5
  user-invocable: true
7
6
  allowed-tools: Read, Glob, Grep, Bash, WebSearch, WebFetch, AskUserQuestion, Agent, mcp__*
7
+ metadata:
8
+ version: 0.1.2
8
9
  ---
9
10
 
10
11
  # /compare
@@ -1,11 +1,12 @@
1
1
  ---
2
2
  name: create-agnostic-skill
3
- version: 1.4.2
4
3
  description: Use when adding a reusable workflow skill for AI coding agents. Scaffolds a new .agents/skills skill using the Agent Skills open standard.
5
4
  argument-hint: '[skill-name]'
6
5
  disable-model-invocation: true
7
6
  allowed-tools: Read, Write, Bash, Glob, Grep, AskUserQuestion
8
7
  user-invocable: true
8
+ metadata:
9
+ version: 1.4.3
9
10
  ---
10
11
 
11
12
  # Create Skill
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  name: deep-research
3
- version: 0.2.0
4
3
  description: Comprehensive research orchestrator that classifies topics, dispatches parallel research-angle workers, and produces structured artifacts using domain-specific schemas.
5
4
  argument-hint: 'topic [--depth surface|standard|exhaustive] [--focus angle] [--context path] [output-path]'
6
5
  user-invocable: true
7
6
  allowed-tools: Read, Glob, Grep, Bash, WebSearch, WebFetch, AskUserQuestion, Agent, mcp__*
7
+ metadata:
8
+ version: 0.2.1
8
9
  ---
9
10
 
10
11
  # /deep-research
@@ -1,9 +1,10 @@
1
1
  ---
2
2
  name: explainer-kit
3
- version: 2.1.0
4
3
  description: Use when building destination-neutral visual explainer artifacts from explicit, versioned inputs.
5
4
  user-invocable: true
6
5
  allowed-tools: Read, Write, Edit, Bash, Grep, Glob, Agent, mcp__*
6
+ metadata:
7
+ version: 2.1.1
7
8
  ---
8
9
 
9
10
  # Explainer Kit
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  name: oat-agent-instructions-analyze
3
- version: 1.12.0
4
3
  description: Run when you need to evaluate agent instruction file coverage, quality, and drift. Produces a severity-rated analysis artifact. Run before oat-agent-instructions-apply to identify what needs improvement.
5
4
  disable-model-invocation: true
6
5
  user-invocable: true
7
6
  allowed-tools: Read, Write, Bash(git:*), Glob, Grep, AskUserQuestion, Task
7
+ metadata:
8
+ version: 1.12.1
8
9
  ---
9
10
 
10
11
  # Agent Instructions Analysis
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  name: oat-agent-instructions-apply
3
- version: 1.7.0
4
3
  description: Run when you have an agent instructions analysis artifact and want to generate or update instruction files. Creates a branch, generates files from templates, and optionally opens a PR.
5
4
  disable-model-invocation: true
6
5
  user-invocable: true
7
6
  allowed-tools: Read, Write, Edit, Bash(git:*), Bash(gh:*), Glob, Grep, AskUserQuestion
7
+ metadata:
8
+ version: 1.7.1
8
9
  ---
9
10
 
10
11
  # Agent Instructions Apply
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  name: oat-brainstorm
3
- version: 1.3.6
4
3
  description: Use when the user explicitly invokes the `brainstorm` verb, including `/oat-brainstorm`, "let's brainstorm", "brainstorm this", "can we brainstorm X", or "help me brainstorm X". For ambiguous exploratory phrasing ("I've been thinking", "what if", "help me think through"), do NOT auto-enter; respond conversationally and offer mode only after ≥2 sustained exploratory turns. Do NOT use for review, debug, PR, status, implementation, or active-workflow questions.
5
4
  disable-model-invocation: false
6
5
  user-invocable: true
7
6
  allowed-tools: Read, Write, Bash, Glob, Grep, AskUserQuestion
7
+ metadata:
8
+ version: 1.3.7
8
9
  ---
9
10
 
10
11
  # Brainstorm
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  name: oat-cursor-cloud-projects
3
- version: 1.1.1
4
3
  description: Use when OAT work is mentioned in a Cursor Cloud environment. Orients agents to cloud detection, repo-rooted project homes, user-first assets, CLI availability, and Cursor dispatch context without owning lifecycle execution.
5
4
  disable-model-invocation: false
6
5
  user-invocable: true
7
6
  allowed-tools: Read, Write, Bash, Glob, Grep
7
+ metadata:
8
+ version: 1.1.2
8
9
  ---
9
10
 
10
11
  # OAT in Cursor Cloud
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  name: oat-dispatch-subagents
3
- version: 1.2.6
4
3
  description: Use when an OAT skill or workflow needs provider-neutral selection, launch, recovery, or evidence for bounded subagent work without project lifecycle policy.
5
4
  disable-model-invocation: true
6
5
  user-invocable: false
7
6
  allowed-tools: Read
7
+ metadata:
8
+ version: 1.2.7
8
9
  ---
9
10
 
10
11
  # Dispatching OAT Subagents
@@ -1,11 +1,12 @@
1
1
  ---
2
2
  name: oat-docs
3
- version: 1.0.2
4
3
  description: Use when a user asks questions about OAT workflows, CLI commands, skill authoring, configuration, or project lifecycle. Answers questions by reading locally-bundled OAT documentation.
5
4
  argument-hint: '[question]'
6
5
  disable-model-invocation: false
7
6
  user-invocable: true
8
7
  allowed-tools: Read, Glob, Grep, AskUserQuestion
8
+ metadata:
9
+ version: 1.0.3
9
10
  ---
10
11
 
11
12
  # OAT Docs
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  name: oat-docs-analyze
3
- version: 1.5.0
4
3
  description: Run when you need to evaluate documentation structure, navigation, and coverage against the OAT docs app contract. Produces a severity-rated analysis artifact for oat-docs-apply.
5
4
  disable-model-invocation: true
6
5
  user-invocable: true
7
6
  allowed-tools: Read, Write, Bash(git:*), Glob, Grep, AskUserQuestion, Task
7
+ metadata:
8
+ version: 1.5.1
8
9
  ---
9
10
 
10
11
  # Docs Analysis
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  name: oat-docs-apply
3
- version: 1.3.0
4
3
  description: Run when you have a docs analysis artifact and want to generate or update documentation structure and content. Creates a branch, applies approved changes, and optionally opens a PR.
5
4
  disable-model-invocation: true
6
5
  user-invocable: true
7
6
  allowed-tools: Read, Write, Edit, Bash(git:*), Bash(gh:*), Glob, Grep, AskUserQuestion
7
+ metadata:
8
+ version: 1.3.1
8
9
  ---
9
10
 
10
11
  # Docs Apply
@@ -1,11 +1,12 @@
1
1
  ---
2
2
  name: oat-docs-authoring
3
- version: 1.0.0
4
3
  description: Use when authoring or restructuring targeted content inside an existing OAT/Fumadocs docs app. Preserves OAT docs navigation, generated indexes, and validation boundaries.
5
4
  argument-hint: '[docs task or target path]'
6
5
  disable-model-invocation: false
7
6
  allowed-tools: Read, Write, Bash, Glob, Grep, AskUserQuestion
8
7
  user-invocable: true
8
+ metadata:
9
+ version: 1.0.1
9
10
  ---
10
11
 
11
12
  # OAT Docs Authoring
@@ -1,11 +1,12 @@
1
1
  ---
2
2
  name: oat-docs-bootstrap
3
- version: 1.2.0
4
3
  description: Use when bootstrapping a new OAT docs app in a repo. Guides the user through preflight detection, richer input gathering than the raw CLI, `oat docs init` invocation with gated post-patches for open CLI gaps, build verification, post-scaffold config inspection, and an educational walkthrough. Supports Fumadocs (full path) and MkDocs (lean path with defined minimum contract).
5
4
  argument-hint: '<optional-target-dir>'
6
5
  disable-model-invocation: true
7
6
  user-invocable: true
8
7
  allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
8
+ metadata:
9
+ version: 1.2.1
9
10
  ---
10
11
 
11
12
  # Docs Bootstrap
@@ -1,11 +1,12 @@
1
1
  ---
2
2
  name: oat-doctor
3
- version: 1.2.1
4
3
  description: Use when you need to diagnose your OAT setup, check for outdated skills, identify misconfigurations, or get a summary of installed tools and config. Runs health checks and recommends corrective actions.
5
4
  argument-hint: '[--summary]'
6
5
  disable-model-invocation: true
7
6
  user-invocable: true
8
7
  allowed-tools: Read, Bash, Glob, Grep, AskUserQuestion
8
+ metadata:
9
+ version: 1.2.2
9
10
  ---
10
11
 
11
12
  # OAT Doctor
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  name: oat-explainer-kit
3
- version: 1.0.7
4
3
  description: Use when building project explainers or recaps from OAT configuration, state, and lifecycle artifacts.
5
4
  disable-model-invocation: false
6
5
  user-invocable: true
7
6
  allowed-tools: Read, Write, Edit, Bash, Grep, Glob, AskUserQuestion
7
+ metadata:
8
+ version: 1.0.8
8
9
  ---
9
10
 
10
11
  # OAT Explainer Kit
@@ -104,11 +104,331 @@ function failure({
104
104
  };
105
105
  }
106
106
 
107
- function readFrontmatterVersion(content) {
108
- const match = content.match(/^version:\s*([^\s#]+)\s*(?:#.*)?$/m);
107
+ /**
108
+ * Read a skill's canonical version from its frontmatter, with
109
+ * `metadata.version` taking precedence over the deprecated top-level `version`
110
+ * alias.
111
+ *
112
+ * ACCEPTED EXCEPTION to the repository rule that only
113
+ * `packages/cli/src/commands/shared/frontmatter.ts` implements this precedence:
114
+ * this script is installed into user projects by pack install, where it can
115
+ * import neither that resolver nor the `yaml` package, so it carries a
116
+ * self-contained reader. The parity contract
117
+ * `tools/smoke/explainer-kit/check-core-version-parity.test.mjs` runs one
118
+ * shared fixture corpus — plus every bundled `SKILL.md` — through this reader
119
+ * and the canonical resolver and requires the same answer; a change to the
120
+ * canonical precedence rule must update those fixtures and this reader
121
+ * together.
122
+ *
123
+ * Anything the canonical resolver cannot resolve cleanly returns `null` so the
124
+ * compatibility check fails closed instead of guessing: a conflict between the
125
+ * two positions, a malformed block, a duplicate key, a tagged, anchored, or
126
+ * aliased scalar, and a plain scalar YAML would read as a number, boolean, or
127
+ * null.
128
+ *
129
+ * Deliberate limits of a parser-free reader, all of which fail closed rather
130
+ * than guess a version: flow collections (`metadata: {version: 1.2.3}`), block
131
+ * scalars, quoted keys, quote escape sequences, multi-line plain scalars, and
132
+ * document markers inside the block all return `null` even where a full YAML
133
+ * parser would resolve them. Nothing in the authoring templates emits those
134
+ * shapes.
135
+ *
136
+ * Every key and value in the block is checked, not just `version`, and the
137
+ * whole block is walked to any depth, so a syntax error anywhere in it fails
138
+ * closed rather than leaving a readable version behind. What remains outside
139
+ * the reader's reach is the content of a shape it refuses outright — the body
140
+ * of a block scalar, or anything inside a flow collection — which it never
141
+ * parses because it never accepts the shape that introduces it.
142
+ */
143
+ export function readFrontmatterVersion(content) {
144
+ const block = frontmatterBlock(content);
145
+ if (block === null) {
146
+ return null;
147
+ }
148
+
149
+ const root = readBlockMap(block.split('\n').map(classifyLine), -1);
150
+ if (root === null || (root.indent !== null && root.indent !== 0)) {
151
+ return null;
152
+ }
153
+
154
+ let topLevel;
155
+ let metadata;
156
+ let unusable = false;
157
+
158
+ for (const child of root.children) {
159
+ if (child.name === 'version') {
160
+ const scalar = declaredScalar(child);
161
+ if (scalar === null) {
162
+ unusable = true;
163
+ } else {
164
+ topLevel = scalar;
165
+ }
166
+ continue;
167
+ }
168
+ if (child.name === 'metadata') {
169
+ // A `metadata:` key with a scalar or flow value is not the block map the
170
+ // canonical resolver requires, and one with no children is a null scalar.
171
+ if (scalarText(child.value) !== '' || child.map.indent === null) {
172
+ return null;
173
+ }
174
+ const nested = child.map.children.find(
175
+ (entry) => entry.name === 'version',
176
+ );
177
+ if (nested !== undefined) {
178
+ const scalar = declaredScalar(nested);
179
+ if (scalar === null) {
180
+ unusable = true;
181
+ } else {
182
+ metadata = scalar;
183
+ }
184
+ }
185
+ }
186
+ }
187
+
188
+ if (unusable) {
189
+ return null;
190
+ }
191
+ if (metadata !== undefined) {
192
+ return topLevel !== undefined && topLevel !== metadata ? null : metadata;
193
+ }
194
+ return topLevel ?? null;
195
+ }
196
+
197
+ function frontmatterBlock(content) {
198
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
109
199
  return match?.[1] ?? null;
110
200
  }
111
201
 
202
+ /**
203
+ * Sort one frontmatter line into the few shapes this reader understands.
204
+ *
205
+ * A key must be followed by `:` and then whitespace or the end of the line,
206
+ * which is what YAML requires of a block mapping: `version:1.2.3` is a plain
207
+ * scalar, not a key, and the canonical resolver reads no version from it. A tab
208
+ * in the indentation is never valid YAML indentation, and a quoted key is a
209
+ * shape this reader refuses rather than compares raw against a plain one.
210
+ */
211
+ function classifyLine(line) {
212
+ if (line.trim() === '') {
213
+ return { kind: 'blank' };
214
+ }
215
+ const indentation = line.match(/^[ \t]*/)[0];
216
+ if (indentation.includes('\t')) {
217
+ return { kind: 'invalid' };
218
+ }
219
+ const indent = indentation.length;
220
+ const rest = line.slice(indent);
221
+ if (rest.startsWith('#')) {
222
+ return { kind: 'comment', indent };
223
+ }
224
+ const key = rest.match(/^([^\s:#]+):(?=[ \t]|$)([ \t].*)?$/);
225
+ // A key is a plain scalar too: a quoted key is a shape this reader refuses
226
+ // rather than compares raw against a plain one, and a key opening with an
227
+ // indicator is not a key at all.
228
+ if (key === null || /^["']/.test(key[1]) || !isPlainScalar(key[1])) {
229
+ return { kind: 'invalid', indent };
230
+ }
231
+ return { kind: 'key', indent, name: key[1], value: key[2] ?? '' };
232
+ }
233
+
234
+ /**
235
+ * Read one block map and everything under it, rejecting every structure this
236
+ * reader cannot validate: an inconsistent indent, a duplicate key at any depth,
237
+ * a value shape it cannot read, or a line it cannot classify. Rejection is
238
+ * always `null`, never a guess.
239
+ */
240
+ function readBlockMap(lines, parentIndent) {
241
+ let indent = null;
242
+ const names = [];
243
+ const children = [];
244
+
245
+ for (let index = 0; index < lines.length; ) {
246
+ const line = lines[index];
247
+ if (line.kind === 'invalid') {
248
+ return null;
249
+ }
250
+ if (line.kind === 'blank' || line.kind === 'comment') {
251
+ index += 1;
252
+ continue;
253
+ }
254
+ if (line.indent <= parentIndent) {
255
+ return null;
256
+ }
257
+ if (indent === null) {
258
+ indent = line.indent;
259
+ }
260
+ if (line.indent !== indent) {
261
+ return null;
262
+ }
263
+ const identity = keyIdentity(line.name);
264
+ if (names.includes(identity) || !isReadableValue(line.value)) {
265
+ return null;
266
+ }
267
+ names.push(identity);
268
+
269
+ let end = index + 1;
270
+ while (
271
+ end < lines.length &&
272
+ (lines[end].kind === 'blank' ||
273
+ lines[end].kind === 'comment' ||
274
+ lines[end].kind === 'invalid' ||
275
+ lines[end].indent > indent)
276
+ ) {
277
+ end += 1;
278
+ }
279
+ const body = lines.slice(index + 1, end);
280
+ const map = body.some((entry) => entry.kind !== 'blank')
281
+ ? readBlockMap(body, indent)
282
+ : { indent: null, children: [] };
283
+ // A key cannot own both a scalar and a nested map. This also refuses a
284
+ // multi-line plain scalar, whose continuation lines arrive here as
285
+ // children: folding one is beyond a parser-free reader, so it fails closed.
286
+ if (
287
+ map === null ||
288
+ (map.indent !== null && scalarText(line.value) !== '')
289
+ ) {
290
+ return null;
291
+ }
292
+ children.push({ ...line, map });
293
+ index = end;
294
+ }
295
+
296
+ return { indent, children };
297
+ }
298
+
299
+ /**
300
+ * The scalar a key declares, or `null` when it declares something this reader
301
+ * refuses to read as a version — including a multi-line scalar or a nested map,
302
+ * both of which arrive here as children.
303
+ */
304
+ function declaredScalar(child) {
305
+ return child.map.indent === null ? scalarValue(child.value) : null;
306
+ }
307
+
308
+ /**
309
+ * Whether a raw value is a shape this reader can read at all. Every key's value
310
+ * is checked, not just `version`, so no syntax error is silently walked past:
311
+ * the canonical resolver rejects the whole document for any of them.
312
+ */
313
+ function isReadableValue(raw) {
314
+ const text = raw.trim();
315
+ if (text === '' || text.startsWith('#')) {
316
+ return true;
317
+ }
318
+ if (/^["']/.test(text)) {
319
+ return quotedScalar(text) !== null;
320
+ }
321
+ return isPlainScalar(scalarText(text));
322
+ }
323
+
324
+ /**
325
+ * Whether a comment-stripped value is a plain scalar YAML reads as a string.
326
+ *
327
+ * Refused: every character YAML forbids at the start of a plain scalar — the
328
+ * flow, block-scalar, anchor, alias, and tag indicators, and the reserved `@`,
329
+ * `` ` ``, and `%` — plus `-`, `?`, and `:` when whitespace or the end of the
330
+ * value follows, which is what makes them indicators rather than the first
331
+ * letter of a word. A `:` anywhere in the value gets the same treatment,
332
+ * because `: ` opens a nested mapping instead of continuing the scalar.
333
+ *
334
+ * Still readable, because none of these is an indicator in block context: a `:`
335
+ * inside a word (`https://example.com`), a `,` after the first character
336
+ * (`allowed-tools: Read, Write`), and a leading `-` or `?` that starts a word
337
+ * (`-word`).
338
+ */
339
+ function isPlainScalar(plain) {
340
+ return (
341
+ !/^[&*!|>[\]{},@`%]/.test(plain) &&
342
+ !/^[-?](?:[ \t]|$)/.test(plain) &&
343
+ !/:(?:[ \t]|$)/.test(plain)
344
+ );
345
+ }
346
+
347
+ /**
348
+ * The identity YAML gives a mapping key, so two spellings of one key are caught
349
+ * as the duplicate they are: `true` and `True` are the same boolean key, and
350
+ * `1` and `01` the same integer key, even though their text differs.
351
+ */
352
+ function keyIdentity(name) {
353
+ if (/^(null|Null|NULL|~)$/.test(name)) {
354
+ return 'null';
355
+ }
356
+ if (/^(true|True|TRUE)$/.test(name)) {
357
+ return 'bool:true';
358
+ }
359
+ if (/^(false|False|FALSE)$/.test(name)) {
360
+ return 'bool:false';
361
+ }
362
+ if (
363
+ /^[-+]?[0-9]+$/.test(name) ||
364
+ /^0x[0-9a-fA-F]+$/.test(name) ||
365
+ /^0o[0-7]+$/.test(name) ||
366
+ /^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$/.test(name)
367
+ ) {
368
+ return `number:${Number(name)}`;
369
+ }
370
+ return `string:${name}`;
371
+ }
372
+
373
+ /**
374
+ * Strip a trailing comment from a raw value. YAML only starts a comment at a
375
+ * `#` preceded by whitespace, so `1.2.3#note` is the string it looks like.
376
+ */
377
+ function scalarText(raw) {
378
+ const text = raw.trim();
379
+ if (text.startsWith('#')) {
380
+ return '';
381
+ }
382
+ const comment = text.search(/[ \t]#/);
383
+ return (comment === -1 ? text : text.slice(0, comment)).trim();
384
+ }
385
+
386
+ function quotedScalar(text) {
387
+ const quoted =
388
+ text.match(/^"([^"\\]*)"(?:[ \t]+#.*)?$/) ??
389
+ text.match(/^'([^']*)'(?:[ \t]+#.*)?$/);
390
+ return quoted?.[1] ?? null;
391
+ }
392
+
393
+ /**
394
+ * Read one plain or quoted string scalar, returning `null` for every value the
395
+ * canonical resolver refuses to treat as a version string.
396
+ */
397
+ function scalarValue(raw) {
398
+ const text = raw.trim();
399
+ if (text === '' || text.startsWith('#')) {
400
+ return null;
401
+ }
402
+
403
+ if (/^["']/.test(text)) {
404
+ const quoted = quotedScalar(text)?.trim();
405
+ return quoted === undefined || quoted === '' ? null : quoted;
406
+ }
407
+
408
+ const plain = scalarText(text);
409
+ if (plain === '') {
410
+ return null;
411
+ }
412
+ if (!isPlainScalar(plain)) {
413
+ return null;
414
+ }
415
+ // Core-schema scalars that resolve to a non-string type: `version: 1.10` is
416
+ // the number 1.1, never the string an author meant to write.
417
+ if (
418
+ /^(null|Null|NULL|~)$/.test(plain) ||
419
+ /^(true|True|TRUE|false|False|FALSE)$/.test(plain) ||
420
+ /^[-+]?[0-9]+$/.test(plain) ||
421
+ /^0o[0-7]+$/.test(plain) ||
422
+ /^0x[0-9a-fA-F]+$/.test(plain) ||
423
+ /^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$/.test(plain) ||
424
+ /^[-+]?\.(inf|Inf|INF)$/.test(plain) ||
425
+ /^\.(nan|NaN|NAN)$/.test(plain)
426
+ ) {
427
+ return null;
428
+ }
429
+ return plain;
430
+ }
431
+
112
432
  function parseVersion(value) {
113
433
  if (typeof value !== 'string') {
114
434
  return null;
@@ -1,11 +1,12 @@
1
1
  ---
2
2
  name: oat-idea-ideate
3
- version: 1.2.2
4
3
  description: Use when continuing an existing tracked idea or expanding an explicit scratchpad seed from {IDEAS_ROOT}/scratchpad.md. Do NOT use to start a brand-new, destinationless brainstorm; use oat-brainstorm for that.
5
4
  argument-hint: '[--global]'
6
5
  disable-model-invocation: true
7
6
  user-invocable: true
8
7
  allowed-tools: Read, Write, Bash, Glob, AskUserQuestion
8
+ metadata:
9
+ version: 1.2.3
9
10
  ---
10
11
 
11
12
  # Ideate