@open-agent-toolkit/cli 0.2.62 → 0.2.64

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 (145) hide show
  1. package/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/cli-utilities/config-and-local-state.md +1 -0
  3. package/assets/docs/cli-utilities/configuration.md +10 -8
  4. package/assets/docs/cli-utilities/project-log.md +16 -0
  5. package/assets/docs/cli-utilities/tool-packs.md +83 -0
  6. package/assets/docs/cli-utilities/workflow-gates.md +124 -16
  7. package/assets/docs/contributing/skills.md +42 -7
  8. package/assets/docs/provider-sync/commands.md +3 -0
  9. package/assets/docs/provider-sync/instruction-sync.md +107 -0
  10. package/assets/docs/provider-sync/manifest-and-drift.md +105 -0
  11. package/assets/docs/reference/cli-reference.md +16 -6
  12. package/assets/docs/reference/oat-directory-structure.md +32 -31
  13. package/assets/docs/workflows/projects/lifecycle.md +45 -0
  14. package/assets/docs/workflows/projects/picking-up-projects.md +14 -0
  15. package/assets/docs/workflows/skills/explainer-kit.md +10 -4
  16. package/assets/docs/workflows/skills/repo-improve.md +15 -1
  17. package/assets/public-package-versions.json +4 -4
  18. package/assets/skills/create-agnostic-skill/SKILL.md +9 -7
  19. package/assets/skills/create-agnostic-skill/references/skill-template.md +9 -7
  20. package/assets/skills/oat-explainer-kit/SKILL.md +14 -1
  21. package/assets/skills/oat-explainer-kit/references/config-contract.md +12 -0
  22. package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +57 -10
  23. package/assets/skills/oat-explainer-kit/scripts/check-terminal-outcome.mjs +28 -5
  24. package/assets/skills/oat-explainer-kit/scripts/probe-recap-seams.mjs +371 -0
  25. package/assets/skills/oat-explainer-kit/scripts/resolve-intent.mjs +123 -1
  26. package/assets/skills/oat-project-autonomous/SKILL.md +10 -1
  27. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +122 -121
  28. package/assets/skills/oat-project-complete/SKILL.md +134 -4
  29. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +122 -121
  30. package/assets/skills/oat-project-implement/SKILL.md +1 -1
  31. package/assets/skills/oat-project-implement/references/completion-and-closeout.md +38 -6
  32. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +122 -121
  33. package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +122 -121
  34. package/assets/skills/oat-project-next/SKILL.md +62 -19
  35. package/assets/skills/oat-project-plan/SKILL.md +31 -5
  36. package/assets/skills/oat-project-pr-final/SKILL.md +281 -6
  37. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +122 -121
  38. package/assets/skills/oat-project-progress/SKILL.md +12 -3
  39. package/assets/skills/oat-project-quick-start/SKILL.md +264 -4
  40. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +122 -121
  41. package/assets/skills/oat-project-summary/SKILL.md +34 -23
  42. package/assets/skills/oat-repo-improve/SKILL.md +40 -5
  43. package/assets/skills/oat-repo-improve/references/plan-template.md +84 -7
  44. package/dist/agents/canonical/resolve.d.ts.map +1 -1
  45. package/dist/agents/canonical/resolve.js +25 -4
  46. package/dist/commands/config/index.d.ts +1 -0
  47. package/dist/commands/config/index.d.ts.map +1 -1
  48. package/dist/commands/config/index.js +401 -18
  49. package/dist/commands/doctor/index.d.ts +7 -0
  50. package/dist/commands/doctor/index.d.ts.map +1 -1
  51. package/dist/commands/doctor/index.js +10 -2
  52. package/dist/commands/gate/index.d.ts +3 -1
  53. package/dist/commands/gate/index.d.ts.map +1 -1
  54. package/dist/commands/gate/index.js +699 -211
  55. package/dist/commands/init/tools/index.d.ts.map +1 -1
  56. package/dist/commands/init/tools/index.js +68 -36
  57. package/dist/commands/init/tools/shared/skill-script-references.d.ts +126 -0
  58. package/dist/commands/init/tools/shared/skill-script-references.d.ts.map +1 -0
  59. package/dist/commands/init/tools/shared/skill-script-references.js +311 -0
  60. package/dist/commands/instructions/instructions.types.d.ts +63 -0
  61. package/dist/commands/instructions/instructions.types.d.ts.map +1 -1
  62. package/dist/commands/instructions/instructions.utils.d.ts +51 -2
  63. package/dist/commands/instructions/instructions.utils.d.ts.map +1 -1
  64. package/dist/commands/instructions/instructions.utils.js +201 -4
  65. package/dist/commands/instructions/sync/sync.d.ts.map +1 -1
  66. package/dist/commands/instructions/sync/sync.js +12 -1
  67. package/dist/commands/instructions/validate/validate.d.ts.map +1 -1
  68. package/dist/commands/instructions/validate/validate.js +12 -1
  69. package/dist/commands/project/log/append.d.ts +195 -0
  70. package/dist/commands/project/log/append.d.ts.map +1 -1
  71. package/dist/commands/project/log/append.js +1034 -11
  72. package/dist/commands/shared/frontmatter.d.ts +51 -0
  73. package/dist/commands/shared/frontmatter.d.ts.map +1 -1
  74. package/dist/commands/shared/frontmatter.js +118 -6
  75. package/dist/commands/status/index.d.ts.map +1 -1
  76. package/dist/commands/status/index.js +5 -3
  77. package/dist/commands/sync/index.d.ts +14 -0
  78. package/dist/commands/sync/index.d.ts.map +1 -1
  79. package/dist/commands/sync/index.js +21 -0
  80. package/dist/commands/tools/info/index.d.ts.map +1 -1
  81. package/dist/commands/tools/info/index.js +14 -1
  82. package/dist/commands/tools/info/info-tool.d.ts +12 -0
  83. package/dist/commands/tools/info/info-tool.d.ts.map +1 -1
  84. package/dist/commands/tools/info/info-tool.js +54 -6
  85. package/dist/commands/tools/info/skill-views.d.ts +86 -0
  86. package/dist/commands/tools/info/skill-views.d.ts.map +1 -0
  87. package/dist/commands/tools/info/skill-views.js +383 -0
  88. package/dist/commands/tools/install/index.d.ts.map +1 -1
  89. package/dist/commands/tools/install/index.js +3 -25
  90. package/dist/commands/tools/list/list-tools.d.ts +2 -0
  91. package/dist/commands/tools/list/list-tools.d.ts.map +1 -1
  92. package/dist/commands/tools/list/list-tools.js +23 -4
  93. package/dist/commands/tools/remove/index.d.ts.map +1 -1
  94. package/dist/commands/tools/remove/index.js +35 -42
  95. package/dist/commands/tools/remove/remove-tools.d.ts +3 -2
  96. package/dist/commands/tools/remove/remove-tools.d.ts.map +1 -1
  97. package/dist/commands/tools/remove/remove-tools.js +14 -4
  98. package/dist/commands/tools/shared/auto-sync.d.ts +12 -1
  99. package/dist/commands/tools/shared/auto-sync.d.ts.map +1 -1
  100. package/dist/commands/tools/shared/auto-sync.js +20 -4
  101. package/dist/commands/tools/shared/format-pack-inventory.d.ts +12 -1
  102. package/dist/commands/tools/shared/format-pack-inventory.d.ts.map +1 -1
  103. package/dist/commands/tools/shared/format-pack-inventory.js +48 -6
  104. package/dist/commands/tools/shared/in-process-sync.d.ts +11 -0
  105. package/dist/commands/tools/shared/in-process-sync.d.ts.map +1 -0
  106. package/dist/commands/tools/shared/in-process-sync.js +79 -0
  107. package/dist/commands/tools/shared/pack-evidence.d.ts +78 -2
  108. package/dist/commands/tools/shared/pack-evidence.d.ts.map +1 -1
  109. package/dist/commands/tools/shared/pack-evidence.js +96 -2
  110. package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts +11 -0
  111. package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts.map +1 -1
  112. package/dist/commands/tools/shared/pack-lifecycle-outcome.js +24 -3
  113. package/dist/commands/tools/shared/pack-provider-evidence.d.ts +73 -0
  114. package/dist/commands/tools/shared/pack-provider-evidence.d.ts.map +1 -0
  115. package/dist/commands/tools/shared/pack-provider-evidence.js +161 -0
  116. package/dist/commands/tools/shared/provider-context.d.ts +88 -0
  117. package/dist/commands/tools/shared/provider-context.d.ts.map +1 -0
  118. package/dist/commands/tools/shared/provider-context.js +77 -0
  119. package/dist/commands/tools/shared/provider-reachability.d.ts +93 -0
  120. package/dist/commands/tools/shared/provider-reachability.d.ts.map +1 -0
  121. package/dist/commands/tools/shared/provider-reachability.js +358 -0
  122. package/dist/commands/tools/shared/sync-evidence.d.ts +57 -0
  123. package/dist/commands/tools/shared/sync-evidence.d.ts.map +1 -0
  124. package/dist/commands/tools/shared/sync-evidence.js +147 -0
  125. package/dist/commands/tools/update/index.d.ts +0 -4
  126. package/dist/commands/tools/update/index.d.ts.map +1 -1
  127. package/dist/commands/tools/update/index.js +22 -40
  128. package/dist/commands/tools/update/update-tools.d.ts.map +1 -1
  129. package/dist/commands/tools/update/update-tools.js +23 -8
  130. package/dist/config/json.d.ts +36 -0
  131. package/dist/config/json.d.ts.map +1 -1
  132. package/dist/config/json.js +135 -3
  133. package/dist/config/oat-config.d.ts +41 -0
  134. package/dist/config/oat-config.d.ts.map +1 -1
  135. package/dist/config/oat-config.js +111 -0
  136. package/dist/drift/index.d.ts +2 -0
  137. package/dist/drift/index.d.ts.map +1 -1
  138. package/dist/drift/index.js +1 -0
  139. package/dist/drift/skill-view-diagnostic.d.ts +197 -0
  140. package/dist/drift/skill-view-diagnostic.d.ts.map +1 -0
  141. package/dist/drift/skill-view-diagnostic.js +343 -0
  142. package/dist/validation/skills.d.ts +1 -0
  143. package/dist/validation/skills.d.ts.map +1 -1
  144. package/dist/validation/skills.js +181 -19
  145. package/package.json +2 -2
@@ -35,10 +35,67 @@ export interface InstructionsJsonPayload {
35
35
  summary: InstructionsSummary;
36
36
  entries: InstructionEntry[];
37
37
  actions: InstructionActionRecord[];
38
+ /**
39
+ * The normalized repo-relative exclusions this scan was *configured* with, in
40
+ * resolution order. An entry here is a statement of intent, not a guarantee:
41
+ * it may name a directory that does not exist, differ in case from the real
42
+ * path on a case-insensitive filesystem, or be shadowed by the `.oat/repo`
43
+ * carve-in.
44
+ *
45
+ * Read `effectiveExcludedPaths` to learn what was actually protected.
46
+ *
47
+ * Additive and omitted entirely when nothing was configured, so payloads from
48
+ * repositories without a documentation root are byte-identical to the
49
+ * pre-exclusion shape.
50
+ */
51
+ excludedPaths?: string[];
52
+ /**
53
+ * The subset of `excludedPaths` that names a real, case-exact directory the
54
+ * scan can actually prune. This is the field a consumer should trust when
55
+ * deciding whether a tree is protected; every configured entry missing from
56
+ * it was also reported as a warning on stderr.
57
+ *
58
+ * Present whenever `excludedPaths` is, including as an empty array when every
59
+ * configured entry turned out to be inert; omitted only when nothing was
60
+ * configured at all.
61
+ */
62
+ effectiveExcludedPaths?: string[];
63
+ /**
64
+ * One message per configured exclusion that will not protect anything, the
65
+ * same text written to stderr in human mode.
66
+ *
67
+ * This field is why the JSON surface is complete. `logger.warn` is suppressed
68
+ * under `--json`, and an entry rejected during normalization (an absolute
69
+ * path, or one escaping the repository) never reaches `excludedPaths` either
70
+ * — so without this a `--json` consumer would see no trace at all of an
71
+ * exclusion that silently does nothing. Omitted when empty.
72
+ */
73
+ exclusionWarnings?: string[];
74
+ }
75
+ /**
76
+ * The resolved exclusion set, split so that intent and effect cannot be
77
+ * confused. A configured entry that protects nothing is the failure mode this
78
+ * split exists to surface: silently reporting it as applied would let an
79
+ * operator believe a documentation tree is safe while pointers are written
80
+ * into it.
81
+ */
82
+ export interface InstructionPointerExclusions {
83
+ /** Normalized entries exactly as configured, in resolution order. */
84
+ configured: string[];
85
+ /** The subset that names a real, case-exact, prunable directory. */
86
+ effective: string[];
87
+ /** One human-readable warning per configured entry that will do nothing. */
88
+ warnings: string[];
38
89
  }
39
90
  export interface InstructionsScanOptions {
40
91
  strategy?: InstructionSyncStrategy;
41
92
  debug?: (message: string) => void;
93
+ /**
94
+ * Repo-root-relative directories whose subtrees are left out of the scan.
95
+ * Applied after the excluded-root carve-in, so excluding a directory can
96
+ * never strand a subtree the carve-in re-entered (`.oat/repo/**`).
97
+ */
98
+ excludedPaths?: string[];
42
99
  }
43
100
  export interface InstructionsScanDependencies {
44
101
  readdir: (path: string, options: {
@@ -53,6 +110,12 @@ export interface InstructionsScanDependencies {
53
110
  export interface InstructionsValidateCommandDependencies {
54
111
  buildCommandContext: (options: GlobalOptions) => CommandContext;
55
112
  resolveProjectRoot: (cwd: string) => Promise<string>;
113
+ /**
114
+ * The single exclusion path both commands resolve through. Sync inherits it
115
+ * from this interface rather than resolving its own, so validate can never
116
+ * report drift that sync would refuse to fix.
117
+ */
118
+ resolveInstructionPointerExcludes: (repoRoot: string) => Promise<InstructionPointerExclusions>;
56
119
  scanInstructionFiles: (repoRoot: string, options?: InstructionsScanOptions, overrides?: Partial<InstructionsScanDependencies>) => Promise<InstructionEntry[]>;
57
120
  }
58
121
  export interface InstructionsSyncCommandDependencies extends InstructionsValidateCommandDependencies {
@@ -1 +1 @@
1
- {"version":3,"file":"instructions.types.d.ts","sourceRoot":"","sources":["../../../src/commands/instructions/instructions.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAE7C,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1E,eAAO,MAAM,2BAA2B,YACtC,SAAS,EACT,SAAS,EACT,MAAM,CACE,CAAC;AAEX,MAAM,MAAM,uBAAuB,GACjC,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/C,MAAM,MAAM,iBAAiB,GAAG,IAAI,GAAG,SAAS,GAAG,kBAAkB,GAAG,OAAO,CAAC;AAEhF,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG,OAAO,CAAC;AAEhD,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEjE,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAExE,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,qBAAqB,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,uBAAuB,CAAC;CACjC;AAED,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC;AAEhE,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,OAAO,EAAE,uBAAuB,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,EAAE,uBAAuB,CAAC;IACnC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,4BAA4B;IAC3C,OAAO,EAAE,CACP,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;QAAE,aAAa,EAAE,IAAI,CAAA;KAAE,KAC7B,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACvB,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;IACxC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9D,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,uCAAuC;IACtD,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,oBAAoB,EAAE,CACpB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,uBAAuB,EACjC,SAAS,CAAC,EAAE,OAAO,CAAC,4BAA4B,CAAC,KAC9C,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,mCAAoC,SAAQ,uCAAuC;IAClG,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;IACxC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9D,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/E"}
1
+ {"version":3,"file":"instructions.types.d.ts","sourceRoot":"","sources":["../../../src/commands/instructions/instructions.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAE7C,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1E,eAAO,MAAM,2BAA2B,YACtC,SAAS,EACT,SAAS,EACT,MAAM,CACE,CAAC;AAEX,MAAM,MAAM,uBAAuB,GACjC,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/C,MAAM,MAAM,iBAAiB,GAAG,IAAI,GAAG,SAAS,GAAG,kBAAkB,GAAG,OAAO,CAAC;AAEhF,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG,OAAO,CAAC;AAEhD,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEjE,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAExE,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,qBAAqB,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,uBAAuB,CAAC;CACjC;AAED,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC;AAEhE,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,OAAO,EAAE,uBAAuB,EAAE,CAAC;IACnC;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB;;;;;;;;;OASG;IACH,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;IAClC;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,4BAA4B;IAC3C,qEAAqE;IACrE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,oEAAoE;IACpE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,EAAE,uBAAuB,CAAC;IACnC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,4BAA4B;IAC3C,OAAO,EAAE,CACP,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;QAAE,aAAa,EAAE,IAAI,CAAA;KAAE,KAC7B,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACvB,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;IACxC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9D,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,uCAAuC;IACtD,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD;;;;OAIG;IACH,iCAAiC,EAAE,CACjC,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,4BAA4B,CAAC,CAAC;IAC3C,oBAAoB,EAAE,CACpB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,uBAAuB,EACjC,SAAS,CAAC,EAAE,OAAO,CAAC,4BAA4B,CAAC,KAC9C,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,mCAAoC,SAAQ,uCAAuC;IAClG,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;IACxC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9D,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/E"}
@@ -1,15 +1,64 @@
1
- import type { InstructionSyncStrategy, InstructionActionRecord, InstructionEntry, InstructionsJsonPayload, InstructionsMode, InstructionsScanOptions, InstructionsScanDependencies, InstructionsSummary } from './instructions.types.js';
1
+ import type { InstructionSyncStrategy, InstructionActionRecord, InstructionEntry, InstructionsJsonPayload, InstructionsMode, InstructionPointerExclusions, InstructionsScanOptions, InstructionsScanDependencies, InstructionsSummary } from './instructions.types.js';
2
2
  export declare const EXPECTED_CLAUDE_CONTENT = "@AGENTS.md\n";
3
3
  export declare const DEFAULT_INSTRUCTION_SYNC_STRATEGY: InstructionSyncStrategy;
4
4
  interface BuildInstructionsPayloadArgs {
5
5
  mode: InstructionsMode;
6
6
  entries: InstructionEntry[];
7
7
  actions: InstructionActionRecord[];
8
+ excludedPaths?: string[];
9
+ effectiveExcludedPaths?: string[];
10
+ exclusionWarnings?: string[];
8
11
  }
9
12
  export declare function resolveInstructionSyncStrategy(strategy?: InstructionSyncStrategy): InstructionSyncStrategy;
13
+ /**
14
+ * Canonicalize repo-relative exclusion entries into the exact form the scan
15
+ * compares against: POSIX separators, collapsed `.` and `..` segments and
16
+ * duplicate slashes, no trailing slash, de-duplicated, order-preserving.
17
+ *
18
+ * Full normalization is what makes the comparison trustworthy. The scan tests
19
+ * an exclusion against `relative(repoRoot, entryPath)`, which is always
20
+ * already-normalized, so an un-normalized entry like `apps/./docs` or
21
+ * `apps//docs` would silently never match and quietly fail open.
22
+ *
23
+ * Two classes of entry are dropped rather than normalized:
24
+ *
25
+ * - anything resolving to the repository root (`.`, `` , `./`), because
26
+ * excluding the root would silence the entire scan — including the
27
+ * `.oat/repo` carve-in — from one stray config value; and
28
+ * - absolute paths and entries escaping the repository (`..`, `../x`), which
29
+ * can never name a directory inside the tree being scanned. Dropping them
30
+ * keeps a malformed value from masquerading as a matchable exclusion.
31
+ */
32
+ export declare function normalizeExcludedPaths(excludedPaths?: readonly string[]): string[];
33
+ /**
34
+ * The directories `oat instructions sync` and `oat instructions validate` skip:
35
+ * the derived documentation content root first, then the explicit
36
+ * `documentation.instructionPointerExcludes` opt-outs.
37
+ *
38
+ * Both commands resolve exclusions through this one function. That is the
39
+ * property that matters: if they computed exclusions separately, validate
40
+ * could report drift in a directory sync refuses to touch, and the repository
41
+ * would have no clean state to reach.
42
+ *
43
+ * Every configured entry is classified rather than trusted. An entry that is
44
+ * dropped during normalization, names an unexcludable carve-in root, or does
45
+ * not resolve to a real case-exact directory is reported as a warning and kept
46
+ * out of `effective`, so no caller can mistake a typo for protection.
47
+ *
48
+ * `effective` answers "is this tree protected from pointer writes?", not "did
49
+ * the exclusion predicate fire for it". An entry that names an already-skipped
50
+ * built-in root (`.git`, `node_modules`) or sits beneath another exclusion is
51
+ * still effective, because the operator's intent holds; warning about those
52
+ * would be a false alarm about exclusions that do work.
53
+ *
54
+ * Nothing here deletes or rewrites an existing pointer. Excluding a directory
55
+ * only stops it being reported and written; a `CLAUDE.md` already inside an
56
+ * excluded tree is left exactly as it is.
57
+ */
58
+ export declare function resolveInstructionPointerExcludes(repoRoot: string, overrides?: Partial<InstructionsScanDependencies>): Promise<InstructionPointerExclusions>;
10
59
  export declare function scanInstructionFiles(repoRoot: string, options?: InstructionsScanOptions, overrides?: Partial<InstructionsScanDependencies>): Promise<InstructionEntry[]>;
11
60
  export declare function buildInstructionsSummary(entries: InstructionEntry[], actions: InstructionActionRecord[]): InstructionsSummary;
12
- export declare function buildInstructionsPayload({ mode, entries, actions, }: BuildInstructionsPayloadArgs): InstructionsJsonPayload;
61
+ export declare function buildInstructionsPayload({ mode, entries, actions, excludedPaths, effectiveExcludedPaths, exclusionWarnings, }: BuildInstructionsPayloadArgs): InstructionsJsonPayload;
13
62
  export declare function formatInstructionsReport(payload: InstructionsJsonPayload, repoRoot?: string): string;
14
63
  export {};
15
64
  //# sourceMappingURL=instructions.utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"instructions.utils.d.ts","sourceRoot":"","sources":["../../../src/commands/instructions/instructions.utils.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,uBAAuB,EACvB,uBAAuB,EACvB,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,EAChB,uBAAuB,EACvB,4BAA4B,EAE5B,mBAAmB,EACpB,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AACtD,eAAO,MAAM,iCAAiC,EAAE,uBACrC,CAAC;AAcZ,UAAU,4BAA4B;IACpC,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,OAAO,EAAE,uBAAuB,EAAE,CAAC;CACpC;AAiCD,wBAAgB,8BAA8B,CAC5C,QAAQ,CAAC,EAAE,uBAAuB,GACjC,uBAAuB,CAEzB;AA2LD,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,uBAA4B,EACrC,SAAS,GAAE,OAAO,CAAC,4BAA4B,CAAM,GACpD,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAiO7B;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,gBAAgB,EAAE,EAC3B,OAAO,EAAE,uBAAuB,EAAE,GACjC,mBAAmB,CAoBrB;AAgBD,wBAAgB,wBAAwB,CAAC,EACvC,IAAI,EACJ,OAAO,EACP,OAAO,GACR,EAAE,4BAA4B,GAAG,uBAAuB,CAWxD;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,uBAAuB,EAChC,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,CAmCR"}
1
+ {"version":3,"file":"instructions.utils.d.ts","sourceRoot":"","sources":["../../../src/commands/instructions/instructions.utils.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,uBAAuB,EACvB,uBAAuB,EACvB,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,EAChB,4BAA4B,EAC5B,uBAAuB,EACvB,4BAA4B,EAE5B,mBAAmB,EACpB,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AACtD,eAAO,MAAM,iCAAiC,EAAE,uBACrC,CAAC;AAcZ,UAAU,4BAA4B;IACpC,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,OAAO,EAAE,uBAAuB,EAAE,CAAC;IACnC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;IAClC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAiCD,wBAAgB,8BAA8B,CAC5C,QAAQ,CAAC,EAAE,uBAAuB,GACjC,uBAAuB,CAEzB;AA2ED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CACpC,aAAa,GAAE,SAAS,MAAM,EAAO,GACpC,MAAM,EAAE,CAyBV;AAsDD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,OAAO,CAAC,4BAA4B,CAAM,GACpD,OAAO,CAAC,4BAA4B,CAAC,CAuEvC;AAgID,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,uBAA4B,EACrC,SAAS,GAAE,OAAO,CAAC,4BAA4B,CAAM,GACpD,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAkO7B;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,gBAAgB,EAAE,EAC3B,OAAO,EAAE,uBAAuB,EAAE,GACjC,mBAAmB,CAoBrB;AAgBD,wBAAgB,wBAAwB,CAAC,EACvC,IAAI,EACJ,OAAO,EACP,OAAO,EACP,aAAa,EACb,sBAAsB,EACtB,iBAAiB,GAClB,EAAE,4BAA4B,GAAG,uBAAuB,CAgCxD;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,uBAAuB,EAChC,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,CAmCR"}
@@ -1,5 +1,6 @@
1
1
  import { lstat, readdir, readFile, readlink, realpath, stat, } from 'node:fs/promises';
2
- import { dirname, join, relative, resolve } from 'node:path';
2
+ import { dirname, join, posix, relative, resolve } from 'node:path';
3
+ import { readOatConfig, resolveDocumentationContentRoot, } from '../../config/oat-config.js';
3
4
  export const EXPECTED_CLAUDE_CONTENT = '@AGENTS.md\n';
4
5
  export const DEFAULT_INSTRUCTION_SYNC_STRATEGY = 'pointer';
5
6
  const ROOT_EXCLUDED_DIRECTORIES = new Set(['.git', '.oat', '.worktrees']);
@@ -87,7 +88,171 @@ function recordInstructionFile(directoryEntries, directoryPath, entryName, entry
87
88
  }
88
89
  directoryEntries.set(directoryPath, current);
89
90
  }
90
- async function scanInstructionDirectories(repoRoot, dependencies, debug) {
91
+ /**
92
+ * Canonicalize repo-relative exclusion entries into the exact form the scan
93
+ * compares against: POSIX separators, collapsed `.` and `..` segments and
94
+ * duplicate slashes, no trailing slash, de-duplicated, order-preserving.
95
+ *
96
+ * Full normalization is what makes the comparison trustworthy. The scan tests
97
+ * an exclusion against `relative(repoRoot, entryPath)`, which is always
98
+ * already-normalized, so an un-normalized entry like `apps/./docs` or
99
+ * `apps//docs` would silently never match and quietly fail open.
100
+ *
101
+ * Two classes of entry are dropped rather than normalized:
102
+ *
103
+ * - anything resolving to the repository root (`.`, `` , `./`), because
104
+ * excluding the root would silence the entire scan — including the
105
+ * `.oat/repo` carve-in — from one stray config value; and
106
+ * - absolute paths and entries escaping the repository (`..`, `../x`), which
107
+ * can never name a directory inside the tree being scanned. Dropping them
108
+ * keeps a malformed value from masquerading as a matchable exclusion.
109
+ */
110
+ export function normalizeExcludedPaths(excludedPaths = []) {
111
+ const normalized = [];
112
+ for (const candidate of excludedPaths) {
113
+ const trimmed = toPosixPath(candidate.trim());
114
+ if (!trimmed || posix.isAbsolute(trimmed)) {
115
+ continue;
116
+ }
117
+ const posixPath = posix.normalize(trimmed).replace(/\/+$/, '');
118
+ if (!posixPath ||
119
+ posixPath === '.' ||
120
+ posixPath === '..' ||
121
+ posixPath.startsWith('../')) {
122
+ continue;
123
+ }
124
+ if (!normalized.includes(posixPath)) {
125
+ normalized.push(posixPath);
126
+ }
127
+ }
128
+ return normalized;
129
+ }
130
+ /**
131
+ * Paths the carve-in queues directly, which therefore never reach the exclusion
132
+ * predicate and can never be excluded however the config asks. An opt-out
133
+ * naming one of these is inert and must be reported as such rather than
134
+ * silently listed as protection.
135
+ *
136
+ * Matched exactly, never by prefix: only the carve-in root itself bypasses the
137
+ * predicate. Its *descendants* are reached by ordinary traversal out of the
138
+ * queued directory, so `.oat/repo/pjm` is excludable even though `.oat/repo`
139
+ * is not, and treating it as inert would be a false warning about an exclusion
140
+ * that really does protect its tree.
141
+ */
142
+ const UNEXCLUDABLE_PATHS = new Set([...ROOT_EXCLUDED_DIRECTORY_CARVE_INS].map(([directory, carveIn]) => `${directory}/${carveIn}`));
143
+ /**
144
+ * Whether `relativePath` names a real directory whose on-disk spelling matches
145
+ * exactly.
146
+ *
147
+ * The case check is the point. On a case-insensitive filesystem (APFS, NTFS) a
148
+ * plain existence test accepts `Apps/Docsapp/docs` for a directory really named
149
+ * `apps/docsapp/docs`, but the scan compares against `relative(repoRoot, ...)`,
150
+ * which always yields the true on-disk case. The exclusion would then be
151
+ * reported as applied while matching nothing — issue #238 recurring silently on
152
+ * the default developer platform. Resolving through `realpath` and comparing
153
+ * the result to the requested spelling catches that, and returns false for a
154
+ * path that does not exist at all.
155
+ *
156
+ * `repoRoot` is realpath'd too, so a symlinked checkout (`/tmp` on macOS) does
157
+ * not make every entry look like a mismatch.
158
+ */
159
+ async function isCaseExactDirectory(dependencies, repoRoot, relativePath) {
160
+ try {
161
+ const realRoot = await dependencies.realpath(repoRoot);
162
+ const candidate = join(realRoot, relativePath);
163
+ if (!(await directoryExists(dependencies, candidate))) {
164
+ return false;
165
+ }
166
+ const realCandidate = await dependencies.realpath(candidate);
167
+ return toPosixPath(relative(realRoot, realCandidate)) === relativePath;
168
+ }
169
+ catch {
170
+ return false;
171
+ }
172
+ }
173
+ /**
174
+ * The directories `oat instructions sync` and `oat instructions validate` skip:
175
+ * the derived documentation content root first, then the explicit
176
+ * `documentation.instructionPointerExcludes` opt-outs.
177
+ *
178
+ * Both commands resolve exclusions through this one function. That is the
179
+ * property that matters: if they computed exclusions separately, validate
180
+ * could report drift in a directory sync refuses to touch, and the repository
181
+ * would have no clean state to reach.
182
+ *
183
+ * Every configured entry is classified rather than trusted. An entry that is
184
+ * dropped during normalization, names an unexcludable carve-in root, or does
185
+ * not resolve to a real case-exact directory is reported as a warning and kept
186
+ * out of `effective`, so no caller can mistake a typo for protection.
187
+ *
188
+ * `effective` answers "is this tree protected from pointer writes?", not "did
189
+ * the exclusion predicate fire for it". An entry that names an already-skipped
190
+ * built-in root (`.git`, `node_modules`) or sits beneath another exclusion is
191
+ * still effective, because the operator's intent holds; warning about those
192
+ * would be a false alarm about exclusions that do work.
193
+ *
194
+ * Nothing here deletes or rewrites an existing pointer. Excluding a directory
195
+ * only stops it being reported and written; a `CLAUDE.md` already inside an
196
+ * excluded tree is left exactly as it is.
197
+ */
198
+ export async function resolveInstructionPointerExcludes(repoRoot, overrides = {}) {
199
+ const dependencies = {
200
+ lstat,
201
+ realpath,
202
+ readdir,
203
+ readFile,
204
+ readlink,
205
+ stat,
206
+ ...overrides,
207
+ };
208
+ const config = await readOatConfig(repoRoot);
209
+ const contentRoot = await resolveDocumentationContentRoot(repoRoot, config, {
210
+ // Probe through the injected `stat` so a simulated filesystem (tests) and
211
+ // the real one agree on whether `<root>/docs` exists.
212
+ dirExists: async (path) => {
213
+ try {
214
+ return (await dependencies.stat(path)).isDirectory();
215
+ }
216
+ catch {
217
+ return false;
218
+ }
219
+ },
220
+ });
221
+ const requested = [
222
+ ...(contentRoot === null
223
+ ? []
224
+ : [{ raw: contentRoot, source: 'documentation.root' }]),
225
+ ...(config.documentation?.instructionPointerExcludes ?? []).map((raw) => ({
226
+ raw,
227
+ source: 'documentation.instructionPointerExcludes',
228
+ })),
229
+ ];
230
+ const configured = [];
231
+ const effective = [];
232
+ const warnings = [];
233
+ for (const { raw, source } of requested) {
234
+ const [normalizedPath] = normalizeExcludedPaths([raw]);
235
+ if (!normalizedPath) {
236
+ warnings.push(`Ignoring ${source} entry ${JSON.stringify(raw)}: exclusions must be repository-relative paths inside the repository.`);
237
+ continue;
238
+ }
239
+ if (configured.includes(normalizedPath)) {
240
+ continue;
241
+ }
242
+ configured.push(normalizedPath);
243
+ if (UNEXCLUDABLE_PATHS.has(normalizedPath)) {
244
+ warnings.push(`${source} entry ${JSON.stringify(normalizedPath)} has no effect: ${normalizedPath} is always scanned.`);
245
+ continue;
246
+ }
247
+ if (!(await isCaseExactDirectory(dependencies, repoRoot, normalizedPath))) {
248
+ warnings.push(`${source} entry ${JSON.stringify(normalizedPath)} matches no directory in this repository (matching is case-sensitive), so it excludes nothing.`);
249
+ continue;
250
+ }
251
+ effective.push(normalizedPath);
252
+ }
253
+ return { configured, effective, warnings };
254
+ }
255
+ async function scanInstructionDirectories(repoRoot, dependencies, debug, excludedPaths = new Set()) {
91
256
  const queue = [repoRoot];
92
257
  const directoryEntries = new Map();
93
258
  while (queue.length > 0) {
@@ -123,6 +288,19 @@ async function scanInstructionDirectories(repoRoot, dependencies, debug) {
123
288
  }
124
289
  continue;
125
290
  }
291
+ // Deliberately after the carve-in above, never before it: the carve-in
292
+ // path is already queued by the time a configured exclusion is tested,
293
+ // so excluding `.oat` (or any documentation root that happens to
294
+ // contain one) still leaves `.oat/repo/**` scanned. Skipping the
295
+ // directory here also skips its whole subtree, because a directory
296
+ // that is never queued is never read.
297
+ if (excludedPaths.size > 0) {
298
+ const relativePath = toPosixPath(relative(repoRoot, entryPath));
299
+ if (excludedPaths.has(relativePath)) {
300
+ debug?.(`Skipping excluded directory ${relativePath}`);
301
+ continue;
302
+ }
303
+ }
126
304
  queue.push(entryPath);
127
305
  continue;
128
306
  }
@@ -181,7 +359,7 @@ export async function scanInstructionFiles(repoRoot, options = {}, overrides = {
181
359
  stat,
182
360
  ...overrides,
183
361
  };
184
- const instructionDirectories = await scanInstructionDirectories(repoRoot, dependencies, options.debug);
362
+ const instructionDirectories = await scanInstructionDirectories(repoRoot, dependencies, options.debug, new Set(normalizeExcludedPaths(options.excludedPaths)));
185
363
  const entries = [];
186
364
  for (const [directoryPath, directoryEntry] of instructionDirectories) {
187
365
  const agentsPath = directoryEntry.agentsPath ?? null;
@@ -402,15 +580,34 @@ function deriveInstructionsStatus(entries, actions) {
402
580
  }
403
581
  return 'ok';
404
582
  }
405
- export function buildInstructionsPayload({ mode, entries, actions, }) {
583
+ export function buildInstructionsPayload({ mode, entries, actions, excludedPaths, effectiveExcludedPaths, exclusionWarnings, }) {
406
584
  const normalizedEntries = normalizeEntries(entries);
407
585
  const normalizedActions = normalizeActions(actions);
586
+ const normalizedExcludedPaths = normalizeExcludedPaths(excludedPaths);
587
+ const normalizedEffectivePaths = normalizeExcludedPaths(effectiveExcludedPaths);
408
588
  return {
409
589
  mode,
410
590
  status: deriveInstructionsStatus(normalizedEntries, normalizedActions),
411
591
  summary: buildInstructionsSummary(normalizedEntries, normalizedActions),
412
592
  entries: normalizedEntries,
413
593
  actions: normalizedActions,
594
+ // Both omitted when nothing is configured, so a repository with no
595
+ // documentation root keeps its existing payload shape exactly. Once
596
+ // `excludedPaths` is present its effective counterpart always is too, even
597
+ // when empty: "configured three, protected none" is precisely the state a
598
+ // consumer must be able to see, and omitting the field would hide it.
599
+ ...(normalizedExcludedPaths.length > 0
600
+ ? {
601
+ excludedPaths: normalizedExcludedPaths,
602
+ effectiveExcludedPaths: normalizedEffectivePaths,
603
+ }
604
+ : {}),
605
+ // Independent of the two fields above: an entry rejected during
606
+ // normalization produces a warning but no configured path, and that is
607
+ // exactly the case a JSON consumer must still be able to see.
608
+ ...(exclusionWarnings !== undefined && exclusionWarnings.length > 0
609
+ ? { exclusionWarnings }
610
+ : {}),
414
611
  };
415
612
  }
416
613
  export function formatInstructionsReport(payload, repoRoot) {
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../../src/commands/instructions/sync/sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,EAAE,EAAsB,MAAM,kBAAkB,CAAC;AAI3E,OAAO,EAKL,KAAK,mCAAmC,EACzC,MAAM,2CAA2C,CAAC;AAYnD,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAQ5C,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,OAAO,EAAO,GACrB,OAAO,CAAC,IAAI,CAAC,CAEf;AA+SD,wBAAgB,6BAA6B,CAC3C,SAAS,GAAE,OAAO,CAAC,mCAAmC,CAAM,GAC3D,OAAO,CA6FT"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../../src/commands/instructions/sync/sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,EAAE,EAAsB,MAAM,kBAAkB,CAAC;AAI3E,OAAO,EAKL,KAAK,mCAAmC,EACzC,MAAM,2CAA2C,CAAC;AAanD,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAQ5C,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,OAAO,EAAO,GACrB,OAAO,CAAC,IAAI,CAAC,CAEf;AAgTD,wBAAgB,6BAA6B,CAC3C,SAAS,GAAE,OAAO,CAAC,mCAAmC,CAAM,GAC3D,OAAO,CAwGT"}
@@ -2,7 +2,7 @@ import { lstat, readFile, rm, symlink, writeFile } from 'node:fs/promises';
2
2
  import { dirname, join, relative } from 'node:path';
3
3
  import { buildCommandContext } from '../../../app/command-context.js';
4
4
  import { INSTRUCTION_SYNC_STRATEGIES, } from '../../instructions/instructions.types.js';
5
- import { buildInstructionsPayload, DEFAULT_INSTRUCTION_SYNC_STRATEGY, EXPECTED_CLAUDE_CONTENT, formatInstructionsReport, resolveInstructionSyncStrategy, scanInstructionFiles, } from '../../instructions/instructions.utils.js';
5
+ import { buildInstructionsPayload, DEFAULT_INSTRUCTION_SYNC_STRATEGY, EXPECTED_CLAUDE_CONTENT, formatInstructionsReport, resolveInstructionPointerExcludes, resolveInstructionSyncStrategy, scanInstructionFiles, } from '../../instructions/instructions.utils.js';
6
6
  import { readGlobalOptions } from '../../shared/shared.utils.js';
7
7
  import { CliError } from '../../../errors/cli-error.js';
8
8
  import { resolveProjectRoot } from '../../../fs/paths.js';
@@ -16,6 +16,7 @@ function defaultDependencies() {
16
16
  lstat,
17
17
  readFile,
18
18
  removeFile: removeInstructionFile,
19
+ resolveInstructionPointerExcludes,
19
20
  resolveProjectRoot,
20
21
  scanInstructionFiles,
21
22
  symlinkFile: async (target, path) => {
@@ -249,7 +250,14 @@ export function createInstructionsSyncCommand(overrides = {}) {
249
250
  try {
250
251
  const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
251
252
  const strategy = resolveInstructionSyncStrategy(options.strategy);
253
+ const exclusions = await dependencies.resolveInstructionPointerExcludes(repoRoot);
254
+ // Warned before any work: an operator whose opt-out silently matches
255
+ // nothing must hear about it even when the sync then succeeds.
256
+ for (const warning of exclusions.warnings) {
257
+ context.logger.warn(warning);
258
+ }
252
259
  const entries = await dependencies.scanInstructionFiles(repoRoot, {
260
+ excludedPaths: exclusions.configured,
253
261
  strategy,
254
262
  });
255
263
  const plannedActions = planSyncActions({
@@ -267,6 +275,9 @@ export function createInstructionsSyncCommand(overrides = {}) {
267
275
  ? entries
268
276
  : getPostSyncEntries(entries, actions, strategy),
269
277
  actions,
278
+ excludedPaths: exclusions.configured,
279
+ effectiveExcludedPaths: exclusions.effective,
280
+ exclusionWarnings: exclusions.warnings,
270
281
  });
271
282
  if (context.json) {
272
283
  context.logger.json(payload);
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../../src/commands/instructions/validate/validate.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,uCAAuC,EAC7C,MAAM,2CAA2C,CAAC;AAWnD,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAU5C,wBAAgB,iCAAiC,CAC/C,SAAS,GAAE,OAAO,CAAC,uCAAuC,CAAM,GAC/D,OAAO,CA2DT"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../../src/commands/instructions/validate/validate.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,uCAAuC,EAC7C,MAAM,2CAA2C,CAAC;AAYnD,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAW5C,wBAAgB,iCAAiC,CAC/C,SAAS,GAAE,OAAO,CAAC,uCAAuC,CAAM,GAC/D,OAAO,CAsET"}
@@ -1,6 +1,6 @@
1
1
  import { buildCommandContext } from '../../../app/command-context.js';
2
2
  import { INSTRUCTION_SYNC_STRATEGIES, } from '../../instructions/instructions.types.js';
3
- import { buildInstructionsPayload, DEFAULT_INSTRUCTION_SYNC_STRATEGY, formatInstructionsReport, resolveInstructionSyncStrategy, scanInstructionFiles, } from '../../instructions/instructions.utils.js';
3
+ import { buildInstructionsPayload, DEFAULT_INSTRUCTION_SYNC_STRATEGY, formatInstructionsReport, resolveInstructionPointerExcludes, resolveInstructionSyncStrategy, scanInstructionFiles, } from '../../instructions/instructions.utils.js';
4
4
  import { readGlobalOptions } from '../../shared/shared.utils.js';
5
5
  import { CliError } from '../../../errors/cli-error.js';
6
6
  import { resolveProjectRoot } from '../../../fs/paths.js';
@@ -8,6 +8,7 @@ import { Command, Option } from 'commander';
8
8
  function defaultDependencies() {
9
9
  return {
10
10
  buildCommandContext,
11
+ resolveInstructionPointerExcludes,
11
12
  resolveProjectRoot,
12
13
  scanInstructionFiles,
13
14
  };
@@ -27,13 +28,23 @@ export function createInstructionsValidateCommand(overrides = {}) {
27
28
  const strategy = resolveInstructionSyncStrategy(options.strategy);
28
29
  try {
29
30
  const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
31
+ const exclusions = await dependencies.resolveInstructionPointerExcludes(repoRoot);
32
+ // Warned before any work: an operator whose opt-out silently matches
33
+ // nothing must hear about it even when validate then reports `ok`.
34
+ for (const warning of exclusions.warnings) {
35
+ context.logger.warn(warning);
36
+ }
30
37
  const entries = await dependencies.scanInstructionFiles(repoRoot, {
38
+ excludedPaths: exclusions.configured,
31
39
  strategy,
32
40
  });
33
41
  const payload = buildInstructionsPayload({
34
42
  mode: 'validate',
35
43
  entries,
36
44
  actions: [],
45
+ excludedPaths: exclusions.configured,
46
+ effectiveExcludedPaths: exclusions.effective,
47
+ exclusionWarnings: exclusions.warnings,
37
48
  });
38
49
  if (context.json) {
39
50
  context.logger.json(payload);