@open-agent-toolkit/cli 0.3.16 → 0.3.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/assets/agents/oat-phase-implementer.md +26 -6
  2. package/assets/agents/oat-reviewer.md +51 -1
  3. package/assets/bundle-metadata.json +1 -1
  4. package/assets/docs/contributing/documentation.md +4 -0
  5. package/assets/docs/contributing/skills.md +3 -1
  6. package/assets/docs/getting-started/bootstrap.md +1 -1
  7. package/assets/docs/getting-started/index.md +0 -4
  8. package/assets/docs/getting-started/tool-packs.md +0 -7
  9. package/assets/docs/reference/cli-reference.md +42 -1
  10. package/assets/docs/reference/config-and-local-state.md +11 -2
  11. package/assets/docs/reference/project-artifacts.md +43 -8
  12. package/assets/docs/skills/repo-improve.md +9 -7
  13. package/assets/docs/workflows/advanced/autonomy.md +40 -0
  14. package/assets/docs/workflows/backlog-and-planning/backlog-lifecycle.md +25 -24
  15. package/assets/docs/workflows/backlog-and-planning/remote-project-management.md +0 -7
  16. package/assets/docs/workflows/ideas/lifecycle.md +2 -7
  17. package/assets/docs/workflows/projects/execution/project-log.md +22 -0
  18. package/assets/docs/workflows/projects/reviews/review-flavors.md +8 -0
  19. package/assets/public-package-versions.json +4 -4
  20. package/assets/skills/oat-agent-instructions-apply/SKILL.md +14 -4
  21. package/assets/skills/oat-brainstorm/SKILL.md +17 -13
  22. package/assets/skills/oat-docs-apply/SKILL.md +13 -3
  23. package/assets/skills/oat-doctor/SKILL.md +11 -1
  24. package/assets/skills/oat-explainer-kit/SKILL.md +1 -1
  25. package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +6 -3
  26. package/assets/skills/oat-pjm-review-backlog/SKILL.md +2 -2
  27. package/assets/skills/oat-pjm-update-repo-reference/SKILL.md +28 -3
  28. package/assets/skills/oat-project-autonomous/SKILL.md +60 -5
  29. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +44 -6
  30. package/assets/skills/oat-project-capture/SKILL.md +7 -3
  31. package/assets/skills/oat-project-complete/SKILL.md +39 -34
  32. package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +106 -28
  33. package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +30 -3
  34. package/assets/skills/oat-project-design/SKILL.md +12 -9
  35. package/assets/skills/oat-project-discover/SKILL.md +8 -4
  36. package/assets/skills/oat-project-document/SKILL.md +14 -5
  37. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +44 -6
  38. package/assets/skills/oat-project-implement/SKILL.md +1 -1
  39. package/assets/skills/oat-project-implement/references/completion-and-closeout.md +6 -6
  40. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +44 -6
  41. package/assets/skills/oat-project-implement/references/phase-execution.md +20 -6
  42. package/assets/skills/oat-project-import-plan/SKILL.md +20 -6
  43. package/assets/skills/oat-project-lite/SKILL.md +7 -5
  44. package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +44 -6
  45. package/assets/skills/oat-project-new/SKILL.md +6 -2
  46. package/assets/skills/oat-project-plan/SKILL.md +14 -6
  47. package/assets/skills/oat-project-pr-final/SKILL.md +1 -1
  48. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +44 -6
  49. package/assets/skills/oat-project-progress/SKILL.md +13 -1
  50. package/assets/skills/oat-project-promote-spec-driven/SKILL.md +7 -3
  51. package/assets/skills/oat-project-quick-start/SKILL.md +17 -12
  52. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +44 -6
  53. package/assets/skills/oat-project-reconcile/SKILL.md +8 -4
  54. package/assets/skills/oat-project-retro/SKILL.md +1 -1
  55. package/assets/skills/oat-project-retro/references/apply-procedure.md +6 -6
  56. package/assets/skills/oat-project-retro-file/SKILL.md +7 -3
  57. package/assets/skills/oat-project-review-provide/SKILL.md +23 -4
  58. package/assets/skills/oat-project-review-provide-remote/SKILL.md +19 -1
  59. package/assets/skills/oat-project-review-receive/SKILL.md +37 -12
  60. package/assets/skills/oat-project-review-receive-remote/SKILL.md +7 -3
  61. package/assets/skills/oat-project-revise/SKILL.md +13 -5
  62. package/assets/skills/oat-project-spec/SKILL.md +8 -4
  63. package/assets/skills/oat-project-summary/SKILL.md +11 -7
  64. package/assets/skills/oat-repo-knowledge-index/SKILL.md +54 -20
  65. package/assets/skills/oat-repo-knowledge-index/scripts/refresh-owned.mjs +118 -0
  66. package/assets/skills/oat-review-provide/SKILL.md +7 -3
  67. package/assets/skills/oat-review-receive-remote/SKILL.md +14 -4
  68. package/assets/skills/oat-wave-execute/SKILL.md +24 -11
  69. package/assets/skills/oat-wave-execute/assets/wrapper-plan-template.md +16 -2
  70. package/assets/skills/oat-worktree-bootstrap-auto/SKILL.md +45 -17
  71. package/assets/templates/pjm-agents.md +38 -8
  72. package/assets/templates/repo-agents.md +5 -0
  73. package/assets/templates/repo-readme.md +5 -0
  74. package/dist/commands/backlog/archive.d.ts +3 -0
  75. package/dist/commands/backlog/archive.d.ts.map +1 -1
  76. package/dist/commands/backlog/archive.js +63 -26
  77. package/dist/commands/backlog/index.d.ts.map +1 -1
  78. package/dist/commands/backlog/index.js +3 -0
  79. package/dist/commands/backlog/rewrite-references.d.ts +5 -0
  80. package/dist/commands/backlog/rewrite-references.d.ts.map +1 -1
  81. package/dist/commands/backlog/rewrite-references.js +39 -1
  82. package/dist/commands/init/index.d.ts.map +1 -1
  83. package/dist/commands/init/index.js +1 -0
  84. package/dist/commands/init/tools/shared/skill-script-references.d.ts.map +1 -1
  85. package/dist/commands/init/tools/shared/skill-script-references.js +8 -1
  86. package/dist/commands/internal/commit-paths.d.ts +3 -0
  87. package/dist/commands/internal/commit-paths.d.ts.map +1 -0
  88. package/dist/commands/internal/commit-paths.js +31 -0
  89. package/dist/commands/internal/index.d.ts.map +1 -1
  90. package/dist/commands/internal/index.js +2 -0
  91. package/dist/commands/pjm/init.d.ts.map +1 -1
  92. package/dist/commands/pjm/init.js +37 -8
  93. package/dist/commands/project/archive/archive-utils.d.ts +6 -3
  94. package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
  95. package/dist/commands/project/archive/archive-utils.js +510 -104
  96. package/dist/commands/project/archive/push-runner.d.ts.map +1 -1
  97. package/dist/commands/project/archive/push-runner.js +1 -0
  98. package/dist/commands/project/log/append.d.ts +2 -3
  99. package/dist/commands/project/log/append.d.ts.map +1 -1
  100. package/dist/commands/project/log/append.js +125 -111
  101. package/dist/commands/project/migrate/index.d.ts +1 -2
  102. package/dist/commands/project/migrate/index.d.ts.map +1 -1
  103. package/dist/commands/project/migrate/index.js +8 -2
  104. package/dist/commands/project/new/scaffold.d.ts.map +1 -1
  105. package/dist/commands/project/new/scaffold.js +4 -4
  106. package/dist/commands/project/promote/promote.d.ts +8 -0
  107. package/dist/commands/project/promote/promote.d.ts.map +1 -1
  108. package/dist/commands/project/promote/promote.js +37 -6
  109. package/dist/commands/project/status.d.ts.map +1 -1
  110. package/dist/commands/project/status.js +3 -0
  111. package/dist/commands/project/sync/ref-sync.d.ts +12 -1
  112. package/dist/commands/project/sync/ref-sync.d.ts.map +1 -1
  113. package/dist/commands/project/sync/ref-sync.js +347 -23
  114. package/dist/commands/shared/agents-md-lock.d.ts +3 -0
  115. package/dist/commands/shared/agents-md-lock.d.ts.map +1 -0
  116. package/dist/commands/shared/agents-md-lock.js +130 -0
  117. package/dist/commands/shared/agents-md.d.ts.map +1 -1
  118. package/dist/commands/shared/agents-md.js +26 -8
  119. package/dist/commands/shared/exact-path-commit.d.ts +32 -0
  120. package/dist/commands/shared/exact-path-commit.d.ts.map +1 -0
  121. package/dist/commands/shared/exact-path-commit.js +691 -0
  122. package/dist/validation/skills.d.ts.map +1 -1
  123. package/dist/validation/skills.js +138 -11
  124. package/package.json +3 -2
@@ -1 +1 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/validation/skills.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,uCAAuC;IACtD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sCAAsC;IACrD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,KAChD,OAAO,CAAC,cAAc,CAAC,CAAC;AAE7B,UAAU,6BAA6B;IACrC,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAu1CD,wBAAsB,gCAAgC,CACpD,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,uCAAuC,EAChD,YAAY,GAAE,6BAAkC,GAC/C,OAAO,CAAC,sCAAsC,CAAC,CAsBjD;AAsGD,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,wBAA6B,EACtC,YAAY,GAAE,6BAAkC,GAC/C,OAAO,CAAC,uBAAuB,CAAC,CAoKlC"}
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/validation/skills.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,uCAAuC;IACtD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sCAAsC;IACrD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,KAChD,OAAO,CAAC,cAAc,CAAC,CAAC;AAE7B,UAAU,6BAA6B;IACrC,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAqgDD,wBAAsB,gCAAgC,CACpD,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,uCAAuC,EAChD,YAAY,GAAE,6BAAkC,GAC/C,OAAO,CAAC,sCAAsC,CAAC,CAsBjD;AAsGD,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,wBAA6B,EACtC,YAAY,GAAE,6BAAkC,GAC/C,OAAO,CAAC,uBAAuB,CAAC,CAoKlC"}
@@ -1,6 +1,6 @@
1
1
  import { execFile as execFileCallback } from 'node:child_process';
2
- import { readdir, readFile, stat } from 'node:fs/promises';
3
- import { join } from 'node:path';
2
+ import { readdir, readFile, realpath, stat } from 'node:fs/promises';
3
+ import { isAbsolute, join, relative, sep } from 'node:path';
4
4
  import { promisify } from 'node:util';
5
5
  import { findSkillFrontmatterTypeProblems, getFrontmatterBlock, parseSkillFrontmatter, resolveSkillVersion, } from '../commands/shared/frontmatter.js';
6
6
  const execFileAsync = promisify(execFileCallback);
@@ -82,7 +82,7 @@ function projectArtifactArrays(content) {
82
82
  function executesCommand(line, command) {
83
83
  const prefix = '(?:(?:&&|\\|\\||;|\\bthen\\b|\\belse\\b|\\bdo\\b|\\bif\\b)\\s+)';
84
84
  const modifiers = '(?:!\\s+)?(?:command\\s+)?(?:env\\s+)?(?:[A-Z][A-Z0-9_]*=\\S+\\s+)*';
85
- return new RegExp(`^\\s*(?:(?:[A-Z][A-Z0-9_]*)=\\$\\()?${command}\\b|${prefix}${modifiers}${command}\\b`).test(line);
85
+ return new RegExp(`^\\s*(?:(?:[A-Z][A-Z0-9_]*)=\\$\\()?${modifiers}${command}\\b|${prefix}${modifiers}${command}\\b`).test(line);
86
86
  }
87
87
  function executesGitCommand(line) {
88
88
  return executesCommand(line, 'git');
@@ -103,6 +103,15 @@ function logicalLines(content) {
103
103
  combined += continuation.trimStart();
104
104
  offset += continuation.length + 1;
105
105
  }
106
+ // Quoted helper messages may span lines; include their ownership tail.
107
+ while (/\boat\s+internal\s+commit-paths\b/.test(combined) &&
108
+ (combined.match(/(?<!\\)"/g)?.length ?? 0) % 2 === 1 &&
109
+ index + 1 < physicalLines.length) {
110
+ index += 1;
111
+ const continuation = physicalLines[index] ?? '';
112
+ combined += `\n${continuation}`;
113
+ offset += continuation.length + 1;
114
+ }
106
115
  logical.push({
107
116
  content: combined,
108
117
  endLine: index,
@@ -116,7 +125,7 @@ function isProjectArtifactWriterLine(line, artifactArrays = new Set()) {
116
125
  const trimmed = line.trim();
117
126
  const executesGitOrOat = executesGitCommand(trimmed) || executesCommand(trimmed, 'oat');
118
127
  const writesProjectArtifact = executesGitOrOat &&
119
- /\bgit\s+(?:add|commit)\b|\boat\s+project\s+push\b/.test(trimmed) &&
128
+ /\bgit\s+(?:add|commit)\b|\boat\s+(?:project\s+push|internal\s+commit-paths)\b/.test(trimmed) &&
120
129
  referencesProjectArtifactVariable(line, artifactArrays);
121
130
  const writesActiveProject = executesGitOrOat && /\boat\s+config\s+set\s+activeProject\b/.test(trimmed);
122
131
  return writesProjectArtifact || writesActiveProject;
@@ -180,8 +189,11 @@ function projectArtifactWriterSites(content) {
180
189
  const sites = new Map();
181
190
  const artifactArrays = projectArtifactArrays(content);
182
191
  for (const line of logicalLines(content)) {
183
- if (isProjectArtifactWriterLine(line.content, artifactArrays)) {
184
- const site = contentSiteAt(content, line.offset, fencedSites);
192
+ const site = contentSiteAt(content, line.offset, fencedSites);
193
+ const writerArrays = /\boat\s+internal\s+commit-paths\b/.test(line.content)
194
+ ? projectArtifactArrays(site.content)
195
+ : artifactArrays;
196
+ if (isProjectArtifactWriterLine(line.content, writerArrays)) {
185
197
  sites.set(`${site.start}:${site.end}`, site);
186
198
  }
187
199
  }
@@ -196,11 +208,12 @@ function collectSyncedContentFindings(file, content, findings) {
196
208
  });
197
209
  }
198
210
  const logical = logicalLines(content);
211
+ const fencedSites = fencedContentSites(content);
199
212
  const artifactArrays = projectArtifactArrays(content);
200
213
  for (const [logicalIndex, site] of logical.entries()) {
201
214
  const line = site.content;
202
215
  const lineNumber = site.startLine + 1;
203
- if (/\bgit\s+add\b[^\n]*\.oat\/projects\/synced(?:\/|\b)/.test(line)) {
216
+ if (/\b(?:git\s+add|oat\s+internal\s+commit-paths)\b[^\n]*\.oat\/projects\/synced(?:\/|\b)/.test(line)) {
204
217
  findings.push({
205
218
  file,
206
219
  message: `Line ${lineNumber}: Never stage a path under .oat/projects/synced/; use oat project push`,
@@ -231,6 +244,60 @@ function collectSyncedContentFindings(file, content, findings) {
231
244
  message: `Line ${lineNumber}: Project-artifact staging must name exact files, not the project directory or a glob`,
232
245
  });
233
246
  }
247
+ const isExactCommit = executesCommand(line, 'oat') &&
248
+ /\boat\s+internal\s+commit-paths\b/.test(line) &&
249
+ !/\bcommit-paths\s+--help(?:\s|$)/.test(line);
250
+ if (isExactCommit && isLifecycleSafetyFile(file)) {
251
+ const ownedTail = /\s--\s+([\s\S]+?)(?:\s+\|\||\s+&&|$)/.exec(line)?.[1];
252
+ if (!/--identity\s+\S/.test(line) ||
253
+ !/--message\s+\S/.test(line) ||
254
+ !ownedTail?.trim()) {
255
+ findings.push({
256
+ file,
257
+ message: `Line ${lineNumber}: Exact-path commits require a stable identity, message, and explicit owned file list after --`,
258
+ });
259
+ }
260
+ if (ownedTail) {
261
+ const arrays = new Set([...ownedTail.matchAll(/\$\{([A-Z][A-Z0-9_]*)\[@\]\}/g)].map((match) => match[1] ?? ''));
262
+ let ownedValues = ownedTail;
263
+ for (let changed = true; changed;) {
264
+ changed = false;
265
+ for (const candidate of logicalLines(contentSiteAt(content, site.offset, fencedSites).content)) {
266
+ const assignment = /^\s*([A-Z][A-Z0-9_]*)(?:\+)?=\((.*)\)\s*$/.exec(candidate.content);
267
+ if (!assignment?.[1] ||
268
+ !arrays.has(assignment[1]) ||
269
+ !assignment[2])
270
+ continue;
271
+ const value = assignment[2];
272
+ ownedValues += ` ${value}`;
273
+ for (const match of value.matchAll(/\$\{([A-Z][A-Z0-9_]*)\[@\]\}/g)) {
274
+ if (match[1] && !arrays.has(match[1])) {
275
+ arrays.add(match[1]);
276
+ changed = true;
277
+ }
278
+ }
279
+ }
280
+ }
281
+ if (/(?:\$PROJECT_PATH|\$\{PROJECT_PATH\}|\{PROJECT_PATH\})(?:\/(?:reviews|references|pr))?\/?(?:["'\s]|$)|[^\s"']*\*[^\s"']*|[^\s"']\/["']/.test(ownedValues)) {
282
+ findings.push({
283
+ file,
284
+ message: `Line ${lineNumber}: Exact-path commits must name concrete owned files, not a directory or glob`,
285
+ });
286
+ }
287
+ if (/\.oat\/projects\/synced(?:\/|\b)/.test(ownedValues)) {
288
+ findings.push({
289
+ file,
290
+ message: `Line ${lineNumber}: Never commit a path under .oat/projects/synced/; use oat project push`,
291
+ });
292
+ }
293
+ }
294
+ if (!/\|\|\s*(?:\{|exit\b)/.test(line)) {
295
+ findings.push({
296
+ file,
297
+ message: `Line ${lineNumber}: Exact-path commit must handle a nonzero exit explicitly and stop bookkeeping`,
298
+ });
299
+ }
300
+ }
234
301
  const pushReceipt = /^\s*([A-Z][A-Z0-9_]*)=\$\(oat project push\b[^\n]*--json\b/.exec(line);
235
302
  if (pushReceipt) {
236
303
  const outputVariable = pushReceipt[1] ?? '';
@@ -281,9 +348,11 @@ function collectSyncedContentFindings(file, content, findings) {
281
348
  }
282
349
  const lines = content.split('\n');
283
350
  const logicalByStart = new Map(logical.map((line) => [line.startLine, line.content]));
351
+ const logicalOffsetByStart = new Map(logical.map((line) => [line.startLine, line.offset]));
284
352
  const continuedLines = new Set(logical.flatMap((line) => Array.from({ length: line.endLine - line.startLine }, (_, offset) => line.startLine + offset + 1)));
285
353
  let fenceMarker = null;
286
354
  let scopeGuardSeen = false;
355
+ let fencedArtifactArrays = artifactArrays;
287
356
  for (const [index, line] of lines.entries()) {
288
357
  const lineNumber = index + 1;
289
358
  const fenceMatch = line.match(/^\s*(`{3,}|~{3,})(?:[^`~]*)$/);
@@ -292,6 +361,7 @@ function collectSyncedContentFindings(file, content, findings) {
292
361
  if (fenceMarker === null) {
293
362
  fenceMarker = marker;
294
363
  scopeGuardSeen = false;
364
+ fencedArtifactArrays = projectArtifactArrays(contentSiteAt(content, logicalOffsetByStart.get(index) ?? 0, fencedSites).content);
295
365
  }
296
366
  else if (marker[0] === fenceMarker[0] &&
297
367
  marker.length >= fenceMarker.length &&
@@ -315,8 +385,10 @@ function collectSyncedContentFindings(file, content, findings) {
315
385
  /\bsynced\b/.test(inspectedLine)) {
316
386
  scopeGuardSeen = true;
317
387
  }
318
- if (/\bgit\s+(?:add|commit)\b/.test(inspectedLine) &&
319
- referencesProjectArtifactVariable(inspectedLine, artifactArrays) &&
388
+ if (/\bgit\s+(?:add|commit)\b|\boat\s+internal\s+commit-paths\b/.test(inspectedLine) &&
389
+ referencesProjectArtifactVariable(inspectedLine, /\boat\s+internal\s+commit-paths\b/.test(inspectedLine)
390
+ ? fencedArtifactArrays
391
+ : artifactArrays) &&
320
392
  !scopeGuardSeen) {
321
393
  findings.push({
322
394
  file,
@@ -766,9 +838,63 @@ async function resolveOwningVersionedFiles(repoRoot, changedPath) {
766
838
  }
767
839
  return [];
768
840
  }
841
+ /** Follow vendored file and directory links without escaping the repo or cycles. */
842
+ async function sharedDocConsumers(repoRoot, changedPaths) {
843
+ const consumers = new Map();
844
+ const sharedPaths = changedPaths.filter((path) => path.startsWith('.agents/docs/'));
845
+ if (sharedPaths.length === 0)
846
+ return consumers;
847
+ const root = await realpath(repoRoot);
848
+ const withinRepo = (path) => {
849
+ const local = relative(root, path);
850
+ return (local !== '..' && !local.startsWith(`..${sep}`) && !isAbsolute(local));
851
+ };
852
+ const targets = new Map();
853
+ for (const path of sharedPaths) {
854
+ const target = await realpath(join(root, path)).catch(() => null);
855
+ if (target === null || !withinRepo(target))
856
+ continue;
857
+ targets.set(target, [...(targets.get(target) ?? []), path]);
858
+ }
859
+ const skillsRoot = join(root, '.agents/skills');
860
+ const skills = await readdir(skillsRoot, { withFileTypes: true }).catch(() => []);
861
+ for (const skill of skills) {
862
+ if (!skill.isDirectory())
863
+ continue;
864
+ const owner = `${SKILLS_PATH_PREFIX}${skill.name}/SKILL.md`;
865
+ if (!(await isFile(join(root, owner))))
866
+ continue;
867
+ const skillRoot = join(skillsRoot, skill.name);
868
+ const owned = new Set();
869
+ async function visit(path, ancestors) {
870
+ // realpath rejects missing targets and symlink loops. An ancestor set also
871
+ // catches directory links back into an already visited directory.
872
+ const target = await realpath(path).catch(() => null);
873
+ if (target === null || !withinRepo(target) || ancestors.has(target))
874
+ return;
875
+ const info = await stat(target);
876
+ if (info.isFile()) {
877
+ for (const changed of targets.get(target) ?? [])
878
+ owned.add(changed);
879
+ }
880
+ else if (info.isDirectory()) {
881
+ const next = new Set([...ancestors, target]);
882
+ for (const entry of await readdir(path, { withFileTypes: true })) {
883
+ // Test fixtures are not shipped skill content, including linked docs.
884
+ if (path !== skillRoot || entry.name !== 'tests')
885
+ await visit(join(path, entry.name), next);
886
+ }
887
+ }
888
+ }
889
+ await visit(skillRoot, new Set());
890
+ if (owned.size)
891
+ consumers.set(owner, [...owned]);
892
+ }
893
+ return consumers;
894
+ }
769
895
  /**
770
896
  * List the canonical files whose version must move, given everything that
771
- * changed under `.agents/skills` and `.agents/agents`.
897
+ * changed under `.agents/skills`, `.agents/agents`, and vendored `.agents/docs`.
772
898
  *
773
899
  * The pathspec is deliberately wider than the files that carry a version: a
774
900
  * skill's `scripts/` and `references/` ship to every `oat tools install`
@@ -794,6 +920,7 @@ async function listChangedVersionedFiles(repoRoot, baseRef, dependencies) {
794
920
  '--',
795
921
  '.agents/skills',
796
922
  '.agents/agents/*.md',
923
+ '.agents/docs',
797
924
  ], {
798
925
  cwd: repoRoot,
799
926
  env: dependencies.env ?? process.env,
@@ -802,7 +929,7 @@ async function listChangedVersionedFiles(repoRoot, baseRef, dependencies) {
802
929
  // legitimately begin or end with a space. The trailing empty entry after the
803
930
  // final NUL is what the length filter removes.
804
931
  const changedPaths = stdout.split('\0').filter((entry) => entry.length > 0);
805
- const grouped = new Map();
932
+ const grouped = await sharedDocConsumers(repoRoot, changedPaths);
806
933
  for (const changedPath of changedPaths) {
807
934
  const owningFiles = await resolveOwningVersionedFiles(repoRoot, changedPath);
808
935
  for (const owningFile of owningFiles) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-agent-toolkit/cli",
3
- "version": "0.3.16",
3
+ "version": "0.3.18",
4
4
  "private": false,
5
5
  "description": "Open Agent Toolkit CLI",
6
6
  "homepage": "https://github.com/voxmedia/open-agent-toolkit/tree/main/packages/cli",
@@ -30,11 +30,12 @@
30
30
  "@inquirer/prompts": "^8.2.0",
31
31
  "chalk": "^5.6.2",
32
32
  "commander": "^12.1.0",
33
+ "entities": "6.0.1",
33
34
  "jsonc-parser": "3.2.1",
34
35
  "ora": "^9.0.0",
35
36
  "yaml": "2.8.2",
36
37
  "zod": "^3.25.76",
37
- "@open-agent-toolkit/control-plane": "0.3.16"
38
+ "@open-agent-toolkit/control-plane": "0.3.18"
38
39
  },
39
40
  "devDependencies": {
40
41
  "@types/node": "^22.10.0",