@cad0p/pi-steering 0.1.0

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 (246) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +29 -0
  3. package/README.md +920 -0
  4. package/dist/__test-helpers__.d.ts +95 -0
  5. package/dist/__test-helpers__.d.ts.map +1 -0
  6. package/dist/__test-helpers__.js +174 -0
  7. package/dist/__test-helpers__.js.map +1 -0
  8. package/dist/bin/pi-steering.d.ts +9 -0
  9. package/dist/bin/pi-steering.d.ts.map +1 -0
  10. package/dist/bin/pi-steering.js +506 -0
  11. package/dist/bin/pi-steering.js.map +1 -0
  12. package/dist/compat.d.ts +50 -0
  13. package/dist/compat.d.ts.map +1 -0
  14. package/dist/compat.js +180 -0
  15. package/dist/compat.js.map +1 -0
  16. package/dist/defaults.d.ts +61 -0
  17. package/dist/defaults.d.ts.map +1 -0
  18. package/dist/defaults.js +106 -0
  19. package/dist/defaults.js.map +1 -0
  20. package/dist/define-config.d.ts +235 -0
  21. package/dist/define-config.d.ts.map +1 -0
  22. package/dist/define-config.js +118 -0
  23. package/dist/define-config.js.map +1 -0
  24. package/dist/define-predicate.d.ts +49 -0
  25. package/dist/define-predicate.d.ts.map +1 -0
  26. package/dist/define-predicate.js +35 -0
  27. package/dist/define-predicate.js.map +1 -0
  28. package/dist/evaluator-internals/context.d.ts +186 -0
  29. package/dist/evaluator-internals/context.d.ts.map +1 -0
  30. package/dist/evaluator-internals/context.js +219 -0
  31. package/dist/evaluator-internals/context.js.map +1 -0
  32. package/dist/evaluator-internals/override.d.ts +19 -0
  33. package/dist/evaluator-internals/override.d.ts.map +1 -0
  34. package/dist/evaluator-internals/override.js +79 -0
  35. package/dist/evaluator-internals/override.js.map +1 -0
  36. package/dist/evaluator-internals/predicates.d.ts +152 -0
  37. package/dist/evaluator-internals/predicates.d.ts.map +1 -0
  38. package/dist/evaluator-internals/predicates.js +849 -0
  39. package/dist/evaluator-internals/predicates.js.map +1 -0
  40. package/dist/evaluator-internals/speculative-synthesis.d.ts +102 -0
  41. package/dist/evaluator-internals/speculative-synthesis.d.ts.map +1 -0
  42. package/dist/evaluator-internals/speculative-synthesis.js +101 -0
  43. package/dist/evaluator-internals/speculative-synthesis.js.map +1 -0
  44. package/dist/evaluator.d.ts +101 -0
  45. package/dist/evaluator.d.ts.map +1 -0
  46. package/dist/evaluator.js +714 -0
  47. package/dist/evaluator.js.map +1 -0
  48. package/dist/helpers/walker-unknown-cwd-reason.d.ts +68 -0
  49. package/dist/helpers/walker-unknown-cwd-reason.d.ts.map +1 -0
  50. package/dist/helpers/walker-unknown-cwd-reason.js +73 -0
  51. package/dist/helpers/walker-unknown-cwd-reason.js.map +1 -0
  52. package/dist/index.d.ts +42 -0
  53. package/dist/index.d.ts.map +1 -0
  54. package/dist/index.js +93 -0
  55. package/dist/index.js.map +1 -0
  56. package/dist/internal/drop-unused-observers.d.ts +43 -0
  57. package/dist/internal/drop-unused-observers.d.ts.map +1 -0
  58. package/dist/internal/drop-unused-observers.js +50 -0
  59. package/dist/internal/drop-unused-observers.js.map +1 -0
  60. package/dist/internal/finalize-plugin-state.d.ts +22 -0
  61. package/dist/internal/finalize-plugin-state.d.ts.map +1 -0
  62. package/dist/internal/finalize-plugin-state.js +14 -0
  63. package/dist/internal/finalize-plugin-state.js.map +1 -0
  64. package/dist/internal/merge-observers.d.ts +30 -0
  65. package/dist/internal/merge-observers.d.ts.map +1 -0
  66. package/dist/internal/merge-observers.js +27 -0
  67. package/dist/internal/merge-observers.js.map +1 -0
  68. package/dist/internal/pattern-utils.d.ts +33 -0
  69. package/dist/internal/pattern-utils.d.ts.map +1 -0
  70. package/dist/internal/pattern-utils.js +20 -0
  71. package/dist/internal/pattern-utils.js.map +1 -0
  72. package/dist/internal/ref-text.d.ts +11 -0
  73. package/dist/internal/ref-text.d.ts.map +1 -0
  74. package/dist/internal/ref-text.js +15 -0
  75. package/dist/internal/ref-text.js.map +1 -0
  76. package/dist/internal/session-runtime.d.ts +44 -0
  77. package/dist/internal/session-runtime.d.ts.map +1 -0
  78. package/dist/internal/session-runtime.js +143 -0
  79. package/dist/internal/session-runtime.js.map +1 -0
  80. package/dist/internal/watch-matcher.d.ts +56 -0
  81. package/dist/internal/watch-matcher.d.ts.map +1 -0
  82. package/dist/internal/watch-matcher.js +179 -0
  83. package/dist/internal/watch-matcher.js.map +1 -0
  84. package/dist/loader.d.ts +97 -0
  85. package/dist/loader.d.ts.map +1 -0
  86. package/dist/loader.js +527 -0
  87. package/dist/loader.js.map +1 -0
  88. package/dist/observer-dispatcher.d.ts +69 -0
  89. package/dist/observer-dispatcher.d.ts.map +1 -0
  90. package/dist/observer-dispatcher.js +184 -0
  91. package/dist/observer-dispatcher.js.map +1 -0
  92. package/dist/plugin-merger.d.ts +175 -0
  93. package/dist/plugin-merger.d.ts.map +1 -0
  94. package/dist/plugin-merger.js +450 -0
  95. package/dist/plugin-merger.js.map +1 -0
  96. package/dist/plugins/git/branch-tracker.d.ts +85 -0
  97. package/dist/plugins/git/branch-tracker.d.ts.map +1 -0
  98. package/dist/plugins/git/branch-tracker.js +181 -0
  99. package/dist/plugins/git/branch-tracker.js.map +1 -0
  100. package/dist/plugins/git/cwd-extensions.d.ts +10 -0
  101. package/dist/plugins/git/cwd-extensions.d.ts.map +1 -0
  102. package/dist/plugins/git/cwd-extensions.js +155 -0
  103. package/dist/plugins/git/cwd-extensions.js.map +1 -0
  104. package/dist/plugins/git/git-ops.d.ts +89 -0
  105. package/dist/plugins/git/git-ops.d.ts.map +1 -0
  106. package/dist/plugins/git/git-ops.js +107 -0
  107. package/dist/plugins/git/git-ops.js.map +1 -0
  108. package/dist/plugins/git/index.d.ts +225 -0
  109. package/dist/plugins/git/index.d.ts.map +1 -0
  110. package/dist/plugins/git/index.js +52 -0
  111. package/dist/plugins/git/index.js.map +1 -0
  112. package/dist/plugins/git/predicates.d.ts +312 -0
  113. package/dist/plugins/git/predicates.d.ts.map +1 -0
  114. package/dist/plugins/git/predicates.js +580 -0
  115. package/dist/plugins/git/predicates.js.map +1 -0
  116. package/dist/plugins/git/rules.d.ts +209 -0
  117. package/dist/plugins/git/rules.d.ts.map +1 -0
  118. package/dist/plugins/git/rules.js +267 -0
  119. package/dist/plugins/git/rules.js.map +1 -0
  120. package/dist/schema.d.ts +1799 -0
  121. package/dist/schema.d.ts.map +1 -0
  122. package/dist/schema.js +4 -0
  123. package/dist/schema.js.map +1 -0
  124. package/dist/testing/index.d.ts +621 -0
  125. package/dist/testing/index.d.ts.map +1 -0
  126. package/dist/testing/index.js +747 -0
  127. package/dist/testing/index.js.map +1 -0
  128. package/examples/README.md +83 -0
  129. package/examples/combined-git-discipline/README.md +52 -0
  130. package/examples/combined-git-discipline/node_modules/.bin/pi-steering +43 -0
  131. package/examples/combined-git-discipline/package.json +18 -0
  132. package/examples/combined-git-discipline/steering.json +27 -0
  133. package/examples/combined-git-discipline/steering.test.ts +42 -0
  134. package/examples/combined-git-discipline/steering.ts +64 -0
  135. package/examples/combined-git-discipline/tsconfig.json +7 -0
  136. package/examples/draft-prs-only/README.md +42 -0
  137. package/examples/draft-prs-only/node_modules/.bin/pi-steering +43 -0
  138. package/examples/draft-prs-only/package.json +18 -0
  139. package/examples/draft-prs-only/steering.json +12 -0
  140. package/examples/draft-prs-only/steering.test.ts +48 -0
  141. package/examples/draft-prs-only/steering.ts +36 -0
  142. package/examples/draft-prs-only/tsconfig.json +7 -0
  143. package/examples/dynamic-reason-runtime-cwd/README.md +148 -0
  144. package/examples/dynamic-reason-runtime-cwd/node_modules/.bin/pi-steering +43 -0
  145. package/examples/dynamic-reason-runtime-cwd/package.json +18 -0
  146. package/examples/dynamic-reason-runtime-cwd/steering.test.ts +130 -0
  147. package/examples/dynamic-reason-runtime-cwd/steering.ts +82 -0
  148. package/examples/dynamic-reason-runtime-cwd/tsconfig.json +7 -0
  149. package/examples/force-push-strict/README.md +75 -0
  150. package/examples/force-push-strict/node_modules/.bin/pi-steering +43 -0
  151. package/examples/force-push-strict/package.json +18 -0
  152. package/examples/force-push-strict/steering.json +12 -0
  153. package/examples/force-push-strict/steering.test.ts +51 -0
  154. package/examples/force-push-strict/steering.ts +54 -0
  155. package/examples/force-push-strict/tsconfig.json +7 -0
  156. package/examples/no-amend/README.md +52 -0
  157. package/examples/no-amend/node_modules/.bin/pi-steering +43 -0
  158. package/examples/no-amend/package.json +18 -0
  159. package/examples/no-amend/steering.cwd-scoped.json +12 -0
  160. package/examples/no-amend/steering.json +11 -0
  161. package/examples/no-amend/steering.test.ts +49 -0
  162. package/examples/no-amend/steering.ts +38 -0
  163. package/examples/no-amend/tsconfig.json +7 -0
  164. package/examples/work-item-plugin/README.md +103 -0
  165. package/examples/work-item-plugin/node_modules/.bin/pi-steering +43 -0
  166. package/examples/work-item-plugin/package.json +19 -0
  167. package/examples/work-item-plugin/src/index.test.ts +168 -0
  168. package/examples/work-item-plugin/src/index.ts +122 -0
  169. package/examples/work-item-plugin/src/observers/npm-test-tracker.test.ts +95 -0
  170. package/examples/work-item-plugin/src/observers/npm-test-tracker.ts +88 -0
  171. package/examples/work-item-plugin/src/observers/retest-required-tracker.test.ts +81 -0
  172. package/examples/work-item-plugin/src/observers/retest-required-tracker.ts +81 -0
  173. package/examples/work-item-plugin/src/predicates/work-item-format.test.ts +157 -0
  174. package/examples/work-item-plugin/src/predicates/work-item-format.ts +99 -0
  175. package/examples/work-item-plugin/src/rules/commit-description-check.test.ts +107 -0
  176. package/examples/work-item-plugin/src/rules/commit-description-check.ts +83 -0
  177. package/examples/work-item-plugin/src/rules/commit-requires-work-item.test.ts +79 -0
  178. package/examples/work-item-plugin/src/rules/commit-requires-work-item.ts +56 -0
  179. package/examples/work-item-plugin/src/rules/push-requires-tests.test.ts +245 -0
  180. package/examples/work-item-plugin/src/rules/push-requires-tests.ts +62 -0
  181. package/examples/work-item-plugin/tsconfig.json +8 -0
  182. package/package.json +88 -0
  183. package/skills/steering-authoring/SKILL.md +102 -0
  184. package/src/INVARIANTS.md +116 -0
  185. package/src/__test-helpers__.ts +243 -0
  186. package/src/bin/pi-steering.test.ts +920 -0
  187. package/src/bin/pi-steering.ts +584 -0
  188. package/src/compat.test.ts +431 -0
  189. package/src/compat.ts +282 -0
  190. package/src/defaults.test.ts +585 -0
  191. package/src/defaults.ts +148 -0
  192. package/src/define-config.test.ts +888 -0
  193. package/src/define-config.ts +375 -0
  194. package/src/define-predicate.test.ts +70 -0
  195. package/src/define-predicate.ts +57 -0
  196. package/src/evaluator-internals/context.ts +290 -0
  197. package/src/evaluator-internals/override.test.ts +99 -0
  198. package/src/evaluator-internals/override.ts +83 -0
  199. package/src/evaluator-internals/predicates.ts +1037 -0
  200. package/src/evaluator-internals/speculative-synthesis.test.ts +411 -0
  201. package/src/evaluator-internals/speculative-synthesis.ts +199 -0
  202. package/src/evaluator.test.ts +6296 -0
  203. package/src/evaluator.ts +1106 -0
  204. package/src/factory-time-load.test.ts +630 -0
  205. package/src/helpers/walker-unknown-cwd-reason.test.ts +73 -0
  206. package/src/helpers/walker-unknown-cwd-reason.ts +83 -0
  207. package/src/index.test.ts +836 -0
  208. package/src/index.ts +229 -0
  209. package/src/internal/drop-unused-observers.test.ts +141 -0
  210. package/src/internal/drop-unused-observers.ts +81 -0
  211. package/src/internal/finalize-plugin-state.ts +43 -0
  212. package/src/internal/merge-observers.ts +51 -0
  213. package/src/internal/pattern-utils.test.ts +52 -0
  214. package/src/internal/pattern-utils.ts +38 -0
  215. package/src/internal/ref-text.test.ts +54 -0
  216. package/src/internal/ref-text.ts +17 -0
  217. package/src/internal/session-runtime.test.ts +629 -0
  218. package/src/internal/session-runtime.ts +201 -0
  219. package/src/internal/watch-matcher.test.ts +326 -0
  220. package/src/internal/watch-matcher.ts +200 -0
  221. package/src/jsdoc-propagation.test.ts +277 -0
  222. package/src/loader.test.ts +1098 -0
  223. package/src/loader.ts +581 -0
  224. package/src/not-block-onunknown.test.ts +554 -0
  225. package/src/observer-dispatcher.test.ts +939 -0
  226. package/src/observer-dispatcher.ts +290 -0
  227. package/src/per-predicate-typing.test.ts +361 -0
  228. package/src/plugin-merger.test.ts +790 -0
  229. package/src/plugin-merger.ts +598 -0
  230. package/src/plugins/git/README.md +485 -0
  231. package/src/plugins/git/branch-tracker.test.ts +281 -0
  232. package/src/plugins/git/branch-tracker.ts +187 -0
  233. package/src/plugins/git/cwd-extensions.test.ts +230 -0
  234. package/src/plugins/git/cwd-extensions.ts +160 -0
  235. package/src/plugins/git/git-ops.test.ts +397 -0
  236. package/src/plugins/git/git-ops.ts +173 -0
  237. package/src/plugins/git/index.ts +253 -0
  238. package/src/plugins/git/integration.test.ts +705 -0
  239. package/src/plugins/git/predicates.test.ts +1401 -0
  240. package/src/plugins/git/predicates.ts +693 -0
  241. package/src/plugins/git/rules.test.ts +1001 -0
  242. package/src/plugins/git/rules.ts +307 -0
  243. package/src/schema.test.ts +259 -0
  244. package/src/schema.ts +1963 -0
  245. package/src/testing/index.test.ts +1696 -0
  246. package/src/testing/index.ts +1472 -0
package/dist/compat.js ADDED
@@ -0,0 +1,180 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Part of pi-steering.
3
+ /**
4
+ * Error thrown when the input JSON uses a feature the v1 → v2 helper
5
+ * can't represent. Carries a `path` pointing at the offending location
6
+ * (e.g. `rules[2].when.branch`) so callers can point the user at the
7
+ * rule to rewrite in TypeScript.
8
+ */
9
+ export class FromJSONError extends Error {
10
+ path;
11
+ constructor(message, path) {
12
+ super(`${message} (at ${path})`);
13
+ this.name = "FromJSONError";
14
+ this.path = path;
15
+ }
16
+ }
17
+ /** Narrow `unknown` to a plain (non-array) object. */
18
+ function isPlainObject(v) {
19
+ return typeof v === "object" && v !== null && !Array.isArray(v);
20
+ }
21
+ /**
22
+ * Convert v1 JSON into a v2 {@link SteeringConfig}.
23
+ *
24
+ * The input is typed `unknown` deliberately — this helper accepts
25
+ * whatever came out of `JSON.parse`, validates the shape, and throws
26
+ * {@link FromJSONError} on anything it can't represent.
27
+ */
28
+ export function fromJSON(json) {
29
+ if (!isPlainObject(json)) {
30
+ throw new FromJSONError("expected a JSON object at the top level", "<root>");
31
+ }
32
+ // Pre-flight: loudly reject v2-only keys that shouldn't appear in
33
+ // v1 JSON. If a user lands here by mistake (e.g. calling fromJSON
34
+ // on an already-v2 object), we'd rather tell them than silently
35
+ // drop their plugins / observers.
36
+ for (const forbidden of [
37
+ "plugins",
38
+ "observers",
39
+ "disablePlugins",
40
+ "disabledPlugins",
41
+ "disabledRules",
42
+ "disableDefaults",
43
+ ]) {
44
+ if (forbidden in json) {
45
+ throw new FromJSONError(`JSON config cannot express \`${forbidden}\`; ` +
46
+ `author that configuration directly in TypeScript instead`, `<root>.${forbidden}`);
47
+ }
48
+ }
49
+ const out = {};
50
+ if ("disable" in json) {
51
+ const disable = json["disable"];
52
+ if (!Array.isArray(disable) ||
53
+ !disable.every((x) => typeof x === "string")) {
54
+ throw new FromJSONError("`disable` must be a string array", "<root>.disable");
55
+ }
56
+ // v1 JSON's `disable` → v2 TS `disabledRules`. The JSON key is
57
+ // preserved for legacy on-disk configs; the TS shape uses the
58
+ // past-participle form.
59
+ out.disabledRules = [...disable];
60
+ }
61
+ if ("defaultNoOverride" in json) {
62
+ const v = json["defaultNoOverride"];
63
+ if (typeof v !== "boolean") {
64
+ throw new FromJSONError("`defaultNoOverride` must be a boolean", "<root>.defaultNoOverride");
65
+ }
66
+ out.defaultNoOverride = v;
67
+ }
68
+ if ("rules" in json) {
69
+ const rules = json["rules"];
70
+ if (!Array.isArray(rules)) {
71
+ throw new FromJSONError("`rules` must be an array", "<root>.rules");
72
+ }
73
+ out.rules = rules.map((rule, i) => convertRule(rule, `<root>.rules[${i}]`));
74
+ }
75
+ return out;
76
+ }
77
+ /** Convert a single v1 JSON rule, scoping errors with `path`. */
78
+ function convertRule(raw, path) {
79
+ if (!isPlainObject(raw)) {
80
+ throw new FromJSONError("rule must be an object", path);
81
+ }
82
+ // Required: name, tool, field, pattern, reason.
83
+ const name = raw["name"];
84
+ if (typeof name !== "string" || name.length === 0) {
85
+ throw new FromJSONError("`name` must be a non-empty string", `${path}.name`);
86
+ }
87
+ const tool = raw["tool"];
88
+ if (tool !== "bash" && tool !== "write" && tool !== "edit") {
89
+ throw new FromJSONError("`tool` must be one of 'bash' | 'write' | 'edit'", `${path}.tool`);
90
+ }
91
+ const field = raw["field"];
92
+ if (field !== "command" && field !== "path" && field !== "content") {
93
+ throw new FromJSONError("`field` must be one of 'command' | 'path' | 'content'", `${path}.field`);
94
+ }
95
+ // Validate the (tool, field) combination per the discriminated
96
+ // Rule union: bash rules test against `command`; write / edit
97
+ // rules test against `path` or `content`.
98
+ if (tool === "bash" && field !== "command") {
99
+ throw new FromJSONError(`bash rules must use \`field: "command"\` (got "${field}")`, `${path}.field`);
100
+ }
101
+ if ((tool === "write" || tool === "edit") && field === "command") {
102
+ throw new FromJSONError(`${tool} rules must use \`field: "path"\` or \`field: "content"\` ` +
103
+ `(got "command")`, `${path}.field`);
104
+ }
105
+ const pattern = raw["pattern"];
106
+ if (typeof pattern !== "string") {
107
+ throw new FromJSONError("JSON rules must use a string `pattern` (v2 accepts RegExp in TS)", `${path}.pattern`);
108
+ }
109
+ const reason = raw["reason"];
110
+ if (typeof reason !== "string") {
111
+ throw new FromJSONError("`reason` must be a string", `${path}.reason`);
112
+ }
113
+ const rule = tool === "bash"
114
+ ? { name, tool, field: "command", pattern, reason }
115
+ : {
116
+ name,
117
+ tool,
118
+ // Narrowed by the (tool, field) check above.
119
+ field: field,
120
+ pattern,
121
+ reason,
122
+ };
123
+ // Optional: requires / unless (string patterns only).
124
+ if ("requires" in raw) {
125
+ const v = raw["requires"];
126
+ if (typeof v !== "string") {
127
+ throw new FromJSONError("JSON rules must use string `requires` (functions aren't JSON-expressible)", `${path}.requires`);
128
+ }
129
+ rule.requires = v;
130
+ }
131
+ if ("unless" in raw) {
132
+ const v = raw["unless"];
133
+ if (typeof v !== "string") {
134
+ throw new FromJSONError("JSON rules must use string `unless` (functions aren't JSON-expressible)", `${path}.unless`);
135
+ }
136
+ rule.unless = v;
137
+ }
138
+ // Optional: noOverride.
139
+ if ("noOverride" in raw) {
140
+ const v = raw["noOverride"];
141
+ if (typeof v !== "boolean") {
142
+ throw new FromJSONError("`noOverride` must be a boolean", `${path}.noOverride`);
143
+ }
144
+ rule.noOverride = v;
145
+ }
146
+ // Optional: when. V1 JSON supports only `when.cwd` (string pattern).
147
+ // Every other key under `when` is plugin-specific and can't be
148
+ // expressed in JSON.
149
+ if ("when" in raw) {
150
+ rule.when = convertWhen(raw["when"], `${path}.when`);
151
+ }
152
+ // V1 JSON has no `observer` field — observers are v2-only. But if
153
+ // someone hand-edits `observer: "name"` into their JSON, warn loudly.
154
+ if ("observer" in raw) {
155
+ throw new FromJSONError("JSON rules cannot reference observers; observers are v2-only (TS)", `${path}.observer`);
156
+ }
157
+ return rule;
158
+ }
159
+ /** Convert the v1 `when` object into a v2 {@link WhenClause}. */
160
+ function convertWhen(raw, path) {
161
+ if (!isPlainObject(raw)) {
162
+ throw new FromJSONError("`when` must be an object", path);
163
+ }
164
+ const out = {};
165
+ for (const key of Object.keys(raw)) {
166
+ if (key !== "cwd") {
167
+ throw new FromJSONError(`JSON config cannot express \`when.${key}\`; ` +
168
+ `plugin-registered predicates and \`when.not\` / \`when.condition\` are v2-only (TS)`, `${path}.${key}`);
169
+ }
170
+ }
171
+ if ("cwd" in raw) {
172
+ const v = raw["cwd"];
173
+ if (typeof v !== "string") {
174
+ throw new FromJSONError("JSON `when.cwd` must be a string pattern", `${path}.cwd`);
175
+ }
176
+ out.cwd = v;
177
+ }
178
+ return out;
179
+ }
180
+ //# sourceMappingURL=compat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compat.js","sourceRoot":"","sources":["../src/compat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ export declare const DEFAULT_RULES: readonly [{
2
+ readonly name: "no-force-push";
3
+ readonly tool: "bash";
4
+ readonly field: "command";
5
+ readonly pattern: "^git\\b(?:\\s+-{1,2}[A-Za-z]\\S*(?:\\s+\\S+)?)*\\s+push\\b.*(?:--force(?!-with-lease)|\\s-f(?:\\s|$))";
6
+ readonly reason: "Force push rewrites remote history and can destroy teammates' work. Use `git push --force-with-lease` if you must, or create a new commit instead.";
7
+ }, {
8
+ readonly name: "no-hard-reset";
9
+ readonly tool: "bash";
10
+ readonly field: "command";
11
+ readonly pattern: "^git\\b(?:\\s+-{1,2}[A-Za-z]\\S*(?:\\s+\\S+)?)*\\s+reset\\s+--hard\\b";
12
+ readonly reason: "Hard reset discards uncommitted changes permanently. Use `git stash` to save work first, or `git reset --soft` to keep changes staged.";
13
+ }, {
14
+ readonly name: "no-rm-rf-slash";
15
+ readonly tool: "bash";
16
+ readonly field: "command";
17
+ readonly pattern: "^rm\\b(?=.*(?:-[A-Za-z]*[rR][A-Za-z]*|--recursive))(?=.*(?:-[A-Za-z]*f[A-Za-z]*|--force)).*\\s/(?:\\s|$)";
18
+ readonly reason: "Recursive force-delete from root is catastrophic and irreversible. Specify a safe path (e.g. a subdirectory of the project or a temp dir).";
19
+ readonly noOverride: true;
20
+ }, {
21
+ readonly name: "no-long-running-commands";
22
+ readonly tool: "bash";
23
+ readonly field: "command";
24
+ readonly pattern: "^(?:npm\\s+(?:run\\s+dev|start)|yarn\\s+(?:dev|start)|pnpm\\s+(?:run\\s+)?(?:dev|start)|npx\\s+.*--watch|webpack\\s+(?:--watch|serve)|jest\\s+--watch|nodemon\\b|tsc\\s+--watch|vite(?:\\s+(?:dev|serve|preview))?(?!\\s+[A-Za-z])|astro\\s+(?:dev|preview)|next\\s+dev|deno\\s+task\\s+(?:dev|start|serve)|bun\\s+(?:dev|run\\s+dev))\\b";
25
+ readonly reason: "Long-running dev servers and watchers block the agent loop. Ask the user to run it manually in another terminal, or use a background-process tool. Representative — add your own via `.pi/steering.ts` if a framework isn't listed here.";
26
+ }];
27
+ /**
28
+ * Default plugins shipped by the package.
29
+ *
30
+ * Deliberately empty since the v0.1.x monorepo split: the git plugin
31
+ * (`pi-steering/plugins/git`) is OPT-IN, not default-on. Users who
32
+ * want the `branch` / `upstream` / `commitsAhead` / `hasStagedChanges`
33
+ * / `isClean` / `remote` predicates, the `no-main-commit` /
34
+ * `no-main-commit-github` rules, the branch tracker, or the `cwd.git`
35
+ * tracker extension must declare it explicitly:
36
+ *
37
+ * ```ts
38
+ * import gitPlugin from "@cad0p/pi-steering/plugins/git";
39
+ * export default defineConfig({ plugins: [gitPlugin] });
40
+ * ```
41
+ *
42
+ * Declaring the plugin explicitly is what gives `defineConfig`'s
43
+ * generics the rule / plugin name unions for typo-checking on
44
+ * `disabledRules` / `disabledPlugins` — an undeclared plugin's rule
45
+ * names are NOT in the inferred union, so typos surface as compile
46
+ * errors instead of silent no-ops. Runtime defaults and type-level
47
+ * visibility must not diverge; an empty default list is the only way
48
+ * to guarantee they can't.
49
+ *
50
+ * Rationale for opt-in (reverses the v0.1.0 D1 default-on decision):
51
+ * git discipline rules are opinionated project policy, not universal
52
+ * safety rails — `DEFAULT_RULES` (domain-agnostic rails like
53
+ * `no-force-push`, `no-rm-rf-slash`) remains default-on; domain
54
+ * plugins opt in.
55
+ *
56
+ * The count lock in `defaults.test.ts` guards this list: any addition
57
+ * to the default plugin list is a deliberate ship-surface change and
58
+ * must update the assertion explicitly.
59
+ */
60
+ export declare const DEFAULT_PLUGINS: readonly [];
61
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAoCA,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;EA4EU,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,eAAe,aAA0C,CAAC"}
@@ -0,0 +1,106 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Part of pi-steering.
3
+ export const DEFAULT_RULES = [
4
+ // `as const satisfies readonly Rule[]` (vs. `: Rule[]`) preserves
5
+ // each rule's `name` literal through to `defaults` consumers. The
6
+ // type unions in `define-config.ts` (`DefaultRuleName`,
7
+ // `AllRuleNames`) project off `(typeof DEFAULT_RULES)[number]["name"]`
8
+ // — a bare `Rule[]` annotation widens those literals to `string`
9
+ // and silently disables typo-detection on `disabledRules`.
10
+ {
11
+ name: "no-force-push",
12
+ tool: "bash",
13
+ field: "command",
14
+ // Block `git push --force` / `-f`, allow `--force-with-lease`.
15
+ // Anchored so `echo 'git push --force'` (basename=echo) is NOT
16
+ // flagged. The pre-subcommand flag slot
17
+ // `(?:\s+-{1,2}[A-Za-z]\S*(?:\s+\S+)?)*` allows short and long
18
+ // git-flags before the subcommand:
19
+ // - `git -C /path push --force`
20
+ // - `git -c key=val push --force`
21
+ // - `git --git-dir=/x push --force`
22
+ // All three are silent bypasses with a plain `^git\s+push` anchor.
23
+ //
24
+ // Known limit: this pattern over-matches on
25
+ // `git log --grep="push --force"` because `--grep=push` is a
26
+ // single token that still satisfies `\bpush\b`. Real agents don't
27
+ // emit that; if it becomes a problem we'll move to args-array
28
+ // matching.
29
+ pattern: "^git\\b(?:\\s+-{1,2}[A-Za-z]\\S*(?:\\s+\\S+)?)*\\s+push\\b.*(?:--force(?!-with-lease)|\\s-f(?:\\s|$))",
30
+ reason: "Force push rewrites remote history and can destroy teammates' work. Use `git push --force-with-lease` if you must, or create a new commit instead.",
31
+ },
32
+ {
33
+ name: "no-hard-reset",
34
+ tool: "bash",
35
+ field: "command",
36
+ // Same pre-subcommand flag broadening as `no-force-push` so
37
+ // `git -C /other reset --hard` and `git -c key=val reset --hard`
38
+ // are also caught.
39
+ pattern: "^git\\b(?:\\s+-{1,2}[A-Za-z]\\S*(?:\\s+\\S+)?)*\\s+reset\\s+--hard\\b",
40
+ reason: "Hard reset discards uncommitted changes permanently. Use `git stash` to save work first, or `git reset --soft` to keep changes staged.",
41
+ },
42
+ {
43
+ name: "no-rm-rf-slash",
44
+ tool: "bash",
45
+ field: "command",
46
+ // rm with recursive AND force flags in any form, operating on `/`.
47
+ // Uses two independent lookaheads so separated flags (`-r -f`),
48
+ // long-form flags (`--recursive --force`), mixed case (`-Rf`),
49
+ // and reversed order (`-fr`) are all caught. Anchored to the
50
+ // basename so `echo 'rm -rf /'` (basename=echo) is NOT flagged.
51
+ pattern: "^rm\\b(?=.*(?:-[A-Za-z]*[rR][A-Za-z]*|--recursive))(?=.*(?:-[A-Za-z]*f[A-Za-z]*|--force)).*\\s/(?:\\s|$)",
52
+ reason: "Recursive force-delete from root is catastrophic and irreversible. Specify a safe path (e.g. a subdirectory of the project or a temp dir).",
53
+ // HARD block — inherent destructiveness, no override possible.
54
+ // Explicit `noOverride: true` guarantees this even when a layer
55
+ // sets `defaultNoOverride: false`.
56
+ noOverride: true,
57
+ },
58
+ {
59
+ name: "no-long-running-commands",
60
+ tool: "bash",
61
+ field: "command",
62
+ // Covers npm / yarn / pnpm dev + start, npx --watch, webpack dev
63
+ // modes, jest / tsc --watch, nodemon, and the modern
64
+ // bundler/runtime ecosystem (vite, astro, next dev, deno task
65
+ // dev/start/serve, bun dev). Representative, not exhaustive —
66
+ // consumers with other watchers should add them via their own
67
+ // `.pi/steering.ts`.
68
+ pattern: "^(?:npm\\s+(?:run\\s+dev|start)|yarn\\s+(?:dev|start)|pnpm\\s+(?:run\\s+)?(?:dev|start)|npx\\s+.*--watch|webpack\\s+(?:--watch|serve)|jest\\s+--watch|nodemon\\b|tsc\\s+--watch|vite(?:\\s+(?:dev|serve|preview))?(?!\\s+[A-Za-z])|astro\\s+(?:dev|preview)|next\\s+dev|deno\\s+task\\s+(?:dev|start|serve)|bun\\s+(?:dev|run\\s+dev))\\b",
69
+ reason: "Long-running dev servers and watchers block the agent loop. Ask the user to run it manually in another terminal, or use a background-process tool. Representative — add your own via `.pi/steering.ts` if a framework isn't listed here.",
70
+ },
71
+ ];
72
+ /**
73
+ * Default plugins shipped by the package.
74
+ *
75
+ * Deliberately empty since the v0.1.x monorepo split: the git plugin
76
+ * (`pi-steering/plugins/git`) is OPT-IN, not default-on. Users who
77
+ * want the `branch` / `upstream` / `commitsAhead` / `hasStagedChanges`
78
+ * / `isClean` / `remote` predicates, the `no-main-commit` /
79
+ * `no-main-commit-github` rules, the branch tracker, or the `cwd.git`
80
+ * tracker extension must declare it explicitly:
81
+ *
82
+ * ```ts
83
+ * import gitPlugin from "@cad0p/pi-steering/plugins/git";
84
+ * export default defineConfig({ plugins: [gitPlugin] });
85
+ * ```
86
+ *
87
+ * Declaring the plugin explicitly is what gives `defineConfig`'s
88
+ * generics the rule / plugin name unions for typo-checking on
89
+ * `disabledRules` / `disabledPlugins` — an undeclared plugin's rule
90
+ * names are NOT in the inferred union, so typos surface as compile
91
+ * errors instead of silent no-ops. Runtime defaults and type-level
92
+ * visibility must not diverge; an empty default list is the only way
93
+ * to guarantee they can't.
94
+ *
95
+ * Rationale for opt-in (reverses the v0.1.0 D1 default-on decision):
96
+ * git discipline rules are opinionated project policy, not universal
97
+ * safety rails — `DEFAULT_RULES` (domain-agnostic rails like
98
+ * `no-force-push`, `no-rm-rf-slash`) remains default-on; domain
99
+ * plugins opt in.
100
+ *
101
+ * The count lock in `defaults.test.ts` guards this list: any addition
102
+ * to the default plugin list is a deliberate ship-surface change and
103
+ * must update the assertion explicitly.
104
+ */
105
+ export const DEFAULT_PLUGINS = [];
106
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,uBAAuB;AAmCvB,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,kEAAkE;IAClE,kEAAkE;IAClE,wDAAwD;IACxD,uEAAuE;IACvE,iEAAiE;IACjE,2DAA2D;IAC3D;QACE,IAAI,EAAE,eAAe;QACrB,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,SAAS;QAChB,+DAA+D;QAC/D,+DAA+D;QAC/D,wCAAwC;QACxC,+DAA+D;QAC/D,mCAAmC;QACnC,kCAAkC;QAClC,oCAAoC;QACpC,sCAAsC;QACtC,mEAAmE;QACnE,EAAE;QACF,4CAA4C;QAC5C,6DAA6D;QAC7D,kEAAkE;QAClE,8DAA8D;QAC9D,YAAY;QACZ,OAAO,EACL,uGAAuG;QACzG,MAAM,EACJ,oJAAoJ;KACvJ;IACD;QACE,IAAI,EAAE,eAAe;QACrB,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,SAAS;QAChB,4DAA4D;QAC5D,iEAAiE;QACjE,mBAAmB;QACnB,OAAO,EACL,uEAAuE;QACzE,MAAM,EACJ,wIAAwI;KAC3I;IACD;QACE,IAAI,EAAE,gBAAgB;QACtB,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,SAAS;QAChB,mEAAmE;QACnE,gEAAgE;QAChE,+DAA+D;QAC/D,6DAA6D;QAC7D,gEAAgE;QAChE,OAAO,EACL,0GAA0G;QAC5G,MAAM,EACJ,4IAA4I;QAC9I,+DAA+D;QAC/D,gEAAgE;QAChE,mCAAmC;QACnC,UAAU,EAAE,IAAI;KACjB;IACD;QACE,IAAI,EAAE,0BAA0B;QAChC,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,SAAS;QAChB,iEAAiE;QACjE,qDAAqD;QACrD,8DAA8D;QAC9D,8DAA8D;QAC9D,8DAA8D;QAC9D,qBAAqB;QACrB,OAAO,EACL,2UAA2U;QAC7U,MAAM,EACJ,0OAA0O;KAC7O;CACiC,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,EAAuC,CAAC"}
@@ -0,0 +1,235 @@
1
+ /**
2
+ * `defineConfig` — compile-time-typed config builder.
3
+ *
4
+ * Two supported authoring styles per the accepted ADR ("Design →
5
+ * `defineConfig` and compile-time inference"):
6
+ *
7
+ * 1. **`defineConfig`** — uses `const`-generics on plugins / observers
8
+ * to infer the union of observer names, then constrains
9
+ * {@link Rule.observer} string references to that union. Typos in
10
+ * `observer: "description-read"` (when the plugin registers
11
+ * `description-reads`) produce a compile error.
12
+ *
13
+ * 2. **`satisfies SteeringConfig`** — plain TypeScript construct users
14
+ * can fall back to when they don't want the generic inference
15
+ * complexity. Gets shape validation but no cross-reference name
16
+ * checking.
17
+ *
18
+ * The function itself does minimal runtime work — it just returns the
19
+ * config unchanged. All the value is in the types.
20
+ *
21
+ * Generics threaded through (ADR §8):
22
+ * - `AllObserverNames<P, Inline>` — for `Rule.observer` string refs.
23
+ * - `AllWrites<P, R, Inline>` — for `Rule.when.happened.event`.
24
+ * - `AllRuleNames<P, R>` — for `config.disabledRules`.
25
+ * - `AllPluginNames<P>` — for `config.disabledPlugins`.
26
+ *
27
+ * All four helpers are exported from this module but NOT re-exported
28
+ * from the package root; they're internal plumbing, not user-facing
29
+ * API. Stable enough that plugin authors who import them directly can
30
+ * rely on their shape within a single minor version, but the contract
31
+ * is "use via defineConfig".
32
+ */
33
+ import type { DEFAULT_PLUGINS, DEFAULT_RULES } from "./defaults.ts";
34
+ import type { Observer, Plugin, Rule, SteeringConfig } from "./schema.ts";
35
+ /**
36
+ * Pull a single projection off every element of an array type.
37
+ *
38
+ * - `K = "name"` — value is the element's `name` literal
39
+ * (`{ name: N }` → `N`).
40
+ * - `K = "writes"` — value is each element of the element's
41
+ * `writes` tuple (`{ writes: readonly [..., S] }`
42
+ * → `S`).
43
+ *
44
+ * Elements missing the field (optional `writes`, widened `name`)
45
+ * contribute `never`. Non-tuple `T` inputs short-circuit to `never`.
46
+ */
47
+ type ProjectField<T, K extends "name" | "writes"> = T extends readonly (infer E)[] ? E extends Record<K, infer V> ? K extends "writes" ? V extends readonly (infer S extends string)[] ? S : never : V extends string ? V : never : never : never;
48
+ /**
49
+ * Walk a tuple of plugins and union a {@link ProjectField} projection
50
+ * across every plugin's `Source` array (`"rules"` or `"observers"`).
51
+ *
52
+ * Replaces four near-identical recursive walkers that differed only in
53
+ * `(Source, K)` pair — see git blame for the pre-R2 shape.
54
+ */
55
+ type FromPluginField<P extends readonly Plugin[], Source extends "rules" | "observers", K extends "name" | "writes"> = P extends readonly [infer First, ...infer Rest] ? (First extends Plugin ? First[Source] extends infer X ? X extends readonly (Rule | Observer)[] ? ProjectField<X, K> : never : never : never) | (Rest extends readonly Plugin[] ? FromPluginField<Rest, Source, K> : never) : never;
56
+ /**
57
+ * Extract the union of observer names registered across:
58
+ * - every plugin's `observers: Observer[]` array, AND
59
+ * - the top-level inline `observers: Observer[]` array.
60
+ *
61
+ * Used to constrain string references in {@link Rule.observer} so typos
62
+ * surface as compile errors in `defineConfig` call sites.
63
+ *
64
+ * Falls back to `never` when no observers are registered (correct:
65
+ * string references should be rejected entirely when there's nothing
66
+ * to reference).
67
+ */
68
+ export type AllObserverNames<P extends readonly Plugin[], Inline extends readonly Observer[]> = FromPluginField<P, "observers", "name"> | ProjectField<Inline, "name">;
69
+ /**
70
+ * Union of {@link DEFAULT_RULES} `name` literals — the names of rules
71
+ * shipped by the engine itself. Folded into {@link AllRuleNames}; the
72
+ * live list is the `name` field of each entry in {@link DEFAULT_RULES}.
73
+ *
74
+ * Relies on {@link DEFAULT_RULES} being authored as
75
+ * `as const satisfies readonly Rule[]` so the literal `name` values
76
+ * survive through the array — a bare `Rule[]` annotation widens to
77
+ * `string` and collapses this union.
78
+ */
79
+ export type DefaultRuleName = (typeof DEFAULT_RULES)[number]["name"];
80
+ /**
81
+ * Union of {@link DEFAULT_PLUGINS} `name` literals. Folded into
82
+ * {@link AllPluginNames} so `disabledPlugins` typechecks against
83
+ * shipped defaults without a cast.
84
+ */
85
+ export type DefaultPluginName = (typeof DEFAULT_PLUGINS)[number]["name"];
86
+ /**
87
+ * Extract the union of plugin names registered in the `plugins` tuple,
88
+ * **plus** the names of {@link DEFAULT_PLUGINS} (those plugins ship
89
+ * pre-loaded by the engine and `disabledPlugins` honors them at
90
+ * runtime).
91
+ *
92
+ * Used to constrain {@link SteeringConfig.disabledPlugins} so typos
93
+ * surface as compile errors. Default-plugin names are always part of
94
+ * this union, even when the user passes no `plugins` themselves.
95
+ *
96
+ * Falls back to just {@link DefaultPluginName} when no user plugins
97
+ * are registered — typing `disabledPlugins` against an empty
98
+ * user-tuple still accepts the engine defaults.
99
+ */
100
+ export type AllPluginNames<P extends readonly Plugin[]> = DefaultPluginName | ProjectField<P, "name">;
101
+ /**
102
+ * Extract the union of rule names across:
103
+ * - every {@link DEFAULT_RULES} entry (engine-shipped defaults),
104
+ * - every plugin's `rules: Rule[]` array, AND
105
+ * - the top-level inline `rules: Rule[]` array.
106
+ *
107
+ * Used to constrain {@link SteeringConfig.disabledRules} so typos
108
+ * surface as compile errors. Default rule names are always part of
109
+ * this union — disabling a default (`disabledRules: ["no-force-push"]`)
110
+ * typechecks the same as disabling a user or plugin rule.
111
+ *
112
+ * Falls back to just {@link DefaultRuleName} when no plugin or user
113
+ * rules are registered.
114
+ */
115
+ export type AllRuleNames<P extends readonly Plugin[], R extends readonly Rule[]> = DefaultRuleName | FromPluginField<P, "rules", "name"> | ProjectField<R, "name">;
116
+ /**
117
+ * Extract the union of session-entry custom types declared via `writes`
118
+ * arrays across:
119
+ * - every plugin's `rules: Rule[]` (rule-side writes via `onFire`),
120
+ * - every plugin's `observers: Observer[]` (observer-side writes),
121
+ * - the top-level inline `rules`, AND
122
+ * - the top-level inline `observers`.
123
+ *
124
+ * Used to constrain {@link BuiltInWhenLeavesOuter.happened} `event` so typos
125
+ * (e.g., `happened: { event: "sync-don" }` when the observer writes
126
+ * `"sync-done"`) surface as compile errors.
127
+ *
128
+ * Authors who omit `writes` on a rule/observer don't contribute to the
129
+ * union — the rule's write is undeclared, and any downstream
130
+ * `when.happened.event` referencing it will be rejected. Matches the
131
+ * "declare your writes" discipline that `writes[]` encourages.
132
+ */
133
+ export type AllWrites<P extends readonly Plugin[], R extends readonly Rule[], Inline extends readonly Observer[] = readonly []> = FromPluginField<P, "rules", "writes"> | FromPluginField<P, "observers", "writes"> | ProjectField<R, "writes"> | ProjectField<Inline, "writes">;
134
+ /**
135
+ * Config author surface — the shape `defineConfig` accepts. Matches
136
+ * {@link SteeringConfig} but with `const`-generic tuple slots on
137
+ * `plugins` / `rules` / `observers` so tuple literal types survive
138
+ * through the call and drive name inference.
139
+ *
140
+ * Generic constraints:
141
+ * - `disabledRules` / `disabledPlugins` typed against the rule / plugin
142
+ * name unions — typos rejected at compile time.
143
+ * - `rules[].when.happened.event` and `rules[].when.happened.since`
144
+ * are both typed against `AllWrites` — typos rejected at compile
145
+ * time.
146
+ */
147
+ export interface DefineConfigInput<P extends readonly Plugin[], Inline extends readonly Observer[], R extends readonly Rule<AllObserverNames<P, Inline>, AllWrites<P, R, Inline>>[]> extends SteeringConfig {
148
+ disabledRules?: readonly AllRuleNames<P, R>[];
149
+ disabledPlugins?: readonly AllPluginNames<P>[];
150
+ plugins?: P;
151
+ rules?: R;
152
+ observers?: Inline;
153
+ }
154
+ /**
155
+ * Build a {@link SteeringConfig} with cross-reference name checking.
156
+ *
157
+ * Observer references in {@link Rule.observer} are typed against the
158
+ * union of observer names gathered from `plugins[*].observers` AND the
159
+ * top-level `observers` array — a typo produces a compile error.
160
+ *
161
+ * The `disabledRules` / `disabledPlugins` arrays are typed against the unions
162
+ * of registered rule / plugin names — typos rejected.
163
+ *
164
+ * `rules[].when.happened.event` and `rules[].when.happened.since` are
165
+ * both typed against the union of all `writes` declarations across
166
+ * plugin rules, plugin observers, user rules, and user observers —
167
+ * typos rejected. (The `since` field on the `Writes` union enforces
168
+ * the same contract as `event`: the sentinel event must be known to
169
+ * the config, not a free-form string.)
170
+ *
171
+ * Runtime behavior: returns a shallow copy of the input with optional
172
+ * fields normalized from `readonly` arrays to mutable arrays (the
173
+ * {@link SteeringConfig} shape doesn't constrain mutability). The
174
+ * return value is safe to pass to the loader / buildConfig.
175
+ *
176
+ * ## Authoring pattern — preserving observer/plugin names for inference
177
+ *
178
+ * For compile-time typo detection on rule `observer` references, declare
179
+ * your observers and plugins with `as const satisfies` so TypeScript
180
+ * preserves the literal `name` values through to `AllObserverNames`:
181
+ *
182
+ * const myObs = {
183
+ * name: "description-read",
184
+ * onResult: (event, ctx) => { ... },
185
+ * } as const satisfies Observer;
186
+ *
187
+ * const myPlugin = {
188
+ * name: "my-plugin",
189
+ * observers: [{ name: "sync-done", onResult: ... }],
190
+ * } as const satisfies Plugin;
191
+ *
192
+ * Authors who prefer type annotations (`const myObs: Observer = ...`)
193
+ * get widened `name: string`, which collapses `AllObserverNames` to
194
+ * `string` and silently disables typo detection. Use `as const satisfies`
195
+ * to keep the inference.
196
+ *
197
+ * ## Behavior with no observers declared
198
+ *
199
+ * When no plugins contribute observers AND no inline `observers[]` is
200
+ * passed, `AllObserverNames` resolves to `never`, which causes ANY
201
+ * string `observer` reference on a Rule to be a compile error. This is
202
+ * deliberate — fail-closed on unknown observer names. For configs that
203
+ * deliberately reference observers by name without registering them
204
+ * inline (e.g., deferred to runtime), use `satisfies SteeringConfig`
205
+ * as a fallback; you lose typo detection but regain flexibility.
206
+ *
207
+ * ## Hover ergonomics for plugin-predicate JSDoc
208
+ *
209
+ * The `const R extends readonly Rule[]` signature narrows the
210
+ * contextual type of inline rule literals to their `const`-inferred
211
+ * shape, bypassing the homomorphic mapped-type linkage that surfaces
212
+ * source-declared JSDoc on hover (e.g. on `when.isClean:`). Factor
213
+ * rules out into `const myRule = { ... } as const satisfies Rule`
214
+ * bindings before passing them to `defineConfig` to keep the
215
+ * hover-rich shape; see the
216
+ * `examples/dynamic-reason-runtime-cwd/steering.ts` example. The
217
+ * `as const` modifier on the binding (and the `const R` modifier on
218
+ * the signature) preserves each rule's literal `name` so
219
+ * `disabledRules` typo detection fires — the alternatives `: Rule`
220
+ * and bare `satisfies Rule` restore hover but widen the inferred
221
+ * type and collapse typo detection (and `when.happened.event`
222
+ * narrowing across declared `writes`).
223
+ *
224
+ * @example
225
+ * export default defineConfig({
226
+ * plugins: [gitPlugin],
227
+ * observers: [descriptionReadObserver],
228
+ * rules: [
229
+ * { name: "must-read-docs", ..., observer: "description-read" },
230
+ * ],
231
+ * });
232
+ */
233
+ export declare function defineConfig<const P extends readonly Plugin[] = [], const Inline extends readonly Observer[] = [], const R extends readonly Rule<AllObserverNames<P, Inline>, AllWrites<P, R, Inline>>[] = []>(config: DefineConfigInput<P, Inline, R>): SteeringConfig;
234
+ export {};
235
+ //# sourceMappingURL=define-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-config.d.ts","sourceRoot":"","sources":["../src/define-config.ts"],"names":[],"mappings":"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"}