devlyn-cli 4.1.0 → 4.2.1

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 (201) hide show
  1. package/AGENTS.md +10 -94
  2. package/CLAUDE.md +7 -160
  3. package/LICENSE +21 -0
  4. package/README.md +68 -111
  5. package/bin/devlyn.js +265 -259
  6. package/bin/instruction-templates.json +43 -2
  7. package/bin/instructions.js +29 -8
  8. package/bin/skill-history.json +140 -0
  9. package/bin/skill-ownership.js +67 -0
  10. package/config/skills/_shared/adapters/claude.md +8 -85
  11. package/config/skills/_shared/adapters/codex.md +14 -34
  12. package/config/skills/_shared/adapters/omp.md +1 -11
  13. package/config/skills/_shared/codex-monitored.sh +4 -54
  14. package/config/skills/_shared/engine-doctor.sh +18 -58
  15. package/config/skills/_shared/engine-preflight.md +15 -47
  16. package/config/skills/_shared/expected-contract.py +242 -0
  17. package/config/skills/_shared/expected.schema.json +10 -95
  18. package/config/skills/_shared/invocation-receipt.py +26 -757
  19. package/config/skills/_shared/platform-support.py +37 -28
  20. package/config/skills/_shared/role-config.py +100 -421
  21. package/config/skills/_shared/runtime-principles.md +17 -93
  22. package/config/skills/_shared/task-complete.py +490 -285
  23. package/config/skills/_shared/task-completion.md +235 -0
  24. package/config/skills/devlyn-engines/SKILL.md +9 -11
  25. package/config/skills/devlyn-ideate/SKILL.md +57 -107
  26. package/config/skills/devlyn-ideate/references/elicitation.md +18 -99
  27. package/config/skills/devlyn-ideate/references/loop.md +110 -0
  28. package/config/skills/devlyn-ideate/references/package-format.md +128 -0
  29. package/config/skills/devlyn-ideate/scripts/acceptance.py +795 -0
  30. package/config/skills/devlyn-ideate/scripts/queue.py +1780 -0
  31. package/node_modules/@inquirer/ansi/LICENSE +22 -0
  32. package/node_modules/@inquirer/ansi/README.md +121 -0
  33. package/node_modules/@inquirer/ansi/dist/index.d.ts +14 -0
  34. package/node_modules/@inquirer/ansi/dist/index.js +21 -0
  35. package/node_modules/@inquirer/ansi/package.json +79 -0
  36. package/node_modules/@inquirer/checkbox/LICENSE +22 -0
  37. package/node_modules/@inquirer/checkbox/README.md +265 -0
  38. package/node_modules/@inquirer/checkbox/dist/index.d.ts +67 -0
  39. package/node_modules/@inquirer/checkbox/dist/index.js +210 -0
  40. package/node_modules/@inquirer/checkbox/package.json +93 -0
  41. package/node_modules/@inquirer/core/LICENSE +22 -0
  42. package/node_modules/@inquirer/core/README.md +424 -0
  43. package/node_modules/@inquirer/core/dist/index.d.ts +13 -0
  44. package/node_modules/@inquirer/core/dist/index.js +12 -0
  45. package/node_modules/@inquirer/core/dist/lib/Separator.d.ts +10 -0
  46. package/node_modules/@inquirer/core/dist/lib/Separator.js +21 -0
  47. package/node_modules/@inquirer/core/dist/lib/__probe/p1.d.ts +9 -0
  48. package/node_modules/@inquirer/core/dist/lib/__probe/p1.js +4 -0
  49. package/node_modules/@inquirer/core/dist/lib/__probe/p2.d.ts +9 -0
  50. package/node_modules/@inquirer/core/dist/lib/__probe/p2.js +3 -0
  51. package/node_modules/@inquirer/core/dist/lib/__probe/p3.d.ts +9 -0
  52. package/node_modules/@inquirer/core/dist/lib/__probe/p3.js +5 -0
  53. package/node_modules/@inquirer/core/dist/lib/create-prompt.d.ts +11 -0
  54. package/node_modules/@inquirer/core/dist/lib/create-prompt.js +185 -0
  55. package/node_modules/@inquirer/core/dist/lib/errors.d.ts +20 -0
  56. package/node_modules/@inquirer/core/dist/lib/errors.js +21 -0
  57. package/node_modules/@inquirer/core/dist/lib/hook-engine.d.ts +17 -0
  58. package/node_modules/@inquirer/core/dist/lib/hook-engine.js +109 -0
  59. package/node_modules/@inquirer/core/dist/lib/key.d.ts +15 -0
  60. package/node_modules/@inquirer/core/dist/lib/key.js +34 -0
  61. package/node_modules/@inquirer/core/dist/lib/make-theme.d.ts +3 -0
  62. package/node_modules/@inquirer/core/dist/lib/make-theme.js +33 -0
  63. package/node_modules/@inquirer/core/dist/lib/pagination/use-pagination.d.ts +15 -0
  64. package/node_modules/@inquirer/core/dist/lib/pagination/use-pagination.js +121 -0
  65. package/node_modules/@inquirer/core/dist/lib/screen-manager.d.ts +14 -0
  66. package/node_modules/@inquirer/core/dist/lib/screen-manager.js +84 -0
  67. package/node_modules/@inquirer/core/dist/lib/theme.d.ts +166 -0
  68. package/node_modules/@inquirer/core/dist/lib/theme.js +29 -0
  69. package/node_modules/@inquirer/core/dist/lib/use-effect.d.ts +2 -0
  70. package/node_modules/@inquirer/core/dist/lib/use-effect.js +11 -0
  71. package/node_modules/@inquirer/core/dist/lib/use-keypress.d.ts +3 -0
  72. package/node_modules/@inquirer/core/dist/lib/use-keypress.js +20 -0
  73. package/node_modules/@inquirer/core/dist/lib/use-memo.d.ts +1 -0
  74. package/node_modules/@inquirer/core/dist/lib/use-memo.js +14 -0
  75. package/node_modules/@inquirer/core/dist/lib/use-prefix.d.ts +5 -0
  76. package/node_modules/@inquirer/core/dist/lib/use-prefix.js +35 -0
  77. package/node_modules/@inquirer/core/dist/lib/use-ref.d.ts +6 -0
  78. package/node_modules/@inquirer/core/dist/lib/use-ref.js +4 -0
  79. package/node_modules/@inquirer/core/dist/lib/use-state.d.ts +7 -0
  80. package/node_modules/@inquirer/core/dist/lib/use-state.js +28 -0
  81. package/node_modules/@inquirer/core/dist/lib/utils.d.ts +13 -0
  82. package/node_modules/@inquirer/core/dist/lib/utils.js +25 -0
  83. package/node_modules/@inquirer/core/package.json +98 -0
  84. package/node_modules/@inquirer/figures/LICENSE +22 -0
  85. package/node_modules/@inquirer/figures/dist/index.d.ts +275 -0
  86. package/node_modules/@inquirer/figures/dist/index.js +315 -0
  87. package/node_modules/@inquirer/figures/package.json +81 -0
  88. package/node_modules/@inquirer/select/LICENSE +22 -0
  89. package/node_modules/@inquirer/select/README.md +258 -0
  90. package/node_modules/@inquirer/select/dist/index.d.ts +42 -0
  91. package/node_modules/@inquirer/select/dist/index.js +191 -0
  92. package/node_modules/@inquirer/select/package.json +93 -0
  93. package/node_modules/@inquirer/type/LICENSE +22 -0
  94. package/node_modules/@inquirer/type/dist/index.d.ts +2 -0
  95. package/node_modules/@inquirer/type/dist/index.js +2 -0
  96. package/node_modules/@inquirer/type/dist/inquirer.d.ts +35 -0
  97. package/node_modules/@inquirer/type/dist/inquirer.js +1 -0
  98. package/node_modules/@inquirer/type/dist/utils.d.ts +45 -0
  99. package/node_modules/@inquirer/type/dist/utils.js +16 -0
  100. package/node_modules/@inquirer/type/package.json +88 -0
  101. package/node_modules/cli-width/LICENSE +13 -0
  102. package/node_modules/cli-width/README.md +71 -0
  103. package/node_modules/cli-width/index.d.ts +13 -0
  104. package/node_modules/cli-width/index.js +49 -0
  105. package/node_modules/cli-width/package.json +40 -0
  106. package/node_modules/fast-string-truncated-width/dist/index.d.ts +4 -0
  107. package/node_modules/fast-string-truncated-width/dist/index.js +111 -0
  108. package/node_modules/fast-string-truncated-width/dist/types.d.ts +19 -0
  109. package/node_modules/fast-string-truncated-width/dist/types.js +2 -0
  110. package/node_modules/fast-string-truncated-width/dist/utils.d.ts +4 -0
  111. package/node_modules/fast-string-truncated-width/dist/utils.js +20 -0
  112. package/node_modules/fast-string-truncated-width/license +21 -0
  113. package/node_modules/fast-string-truncated-width/package.json +35 -0
  114. package/node_modules/fast-string-truncated-width/readme.md +59 -0
  115. package/node_modules/fast-string-width/dist/index.d.ts +4 -0
  116. package/node_modules/fast-string-width/dist/index.js +14 -0
  117. package/node_modules/fast-string-width/license +21 -0
  118. package/node_modules/fast-string-width/package.json +34 -0
  119. package/node_modules/fast-string-width/readme.md +42 -0
  120. package/node_modules/fast-wrap-ansi/LICENSE +23 -0
  121. package/node_modules/fast-wrap-ansi/README.md +26 -0
  122. package/node_modules/fast-wrap-ansi/lib/main.d.ts +6 -0
  123. package/node_modules/fast-wrap-ansi/lib/main.js +218 -0
  124. package/node_modules/fast-wrap-ansi/package.json +51 -0
  125. package/node_modules/mute-stream/LICENSE +15 -0
  126. package/node_modules/mute-stream/README.md +68 -0
  127. package/node_modules/mute-stream/lib/index.js +142 -0
  128. package/node_modules/mute-stream/package.json +54 -0
  129. package/node_modules/signal-exit/LICENSE.txt +16 -0
  130. package/node_modules/signal-exit/README.md +74 -0
  131. package/node_modules/signal-exit/dist/cjs/browser.d.ts +12 -0
  132. package/node_modules/signal-exit/dist/cjs/browser.d.ts.map +1 -0
  133. package/node_modules/signal-exit/dist/cjs/browser.js +10 -0
  134. package/node_modules/signal-exit/dist/cjs/browser.js.map +1 -0
  135. package/node_modules/signal-exit/dist/cjs/index.d.ts +48 -0
  136. package/node_modules/signal-exit/dist/cjs/index.d.ts.map +1 -0
  137. package/node_modules/signal-exit/dist/cjs/index.js +279 -0
  138. package/node_modules/signal-exit/dist/cjs/index.js.map +1 -0
  139. package/node_modules/signal-exit/dist/cjs/package.json +3 -0
  140. package/node_modules/signal-exit/dist/cjs/signals.d.ts +29 -0
  141. package/node_modules/signal-exit/dist/cjs/signals.d.ts.map +1 -0
  142. package/node_modules/signal-exit/dist/cjs/signals.js +42 -0
  143. package/node_modules/signal-exit/dist/cjs/signals.js.map +1 -0
  144. package/node_modules/signal-exit/dist/mjs/browser.d.ts +12 -0
  145. package/node_modules/signal-exit/dist/mjs/browser.d.ts.map +1 -0
  146. package/node_modules/signal-exit/dist/mjs/browser.js +4 -0
  147. package/node_modules/signal-exit/dist/mjs/browser.js.map +1 -0
  148. package/node_modules/signal-exit/dist/mjs/index.d.ts +48 -0
  149. package/node_modules/signal-exit/dist/mjs/index.d.ts.map +1 -0
  150. package/node_modules/signal-exit/dist/mjs/index.js +275 -0
  151. package/node_modules/signal-exit/dist/mjs/index.js.map +1 -0
  152. package/node_modules/signal-exit/dist/mjs/package.json +3 -0
  153. package/node_modules/signal-exit/dist/mjs/signals.d.ts +29 -0
  154. package/node_modules/signal-exit/dist/mjs/signals.d.ts.map +1 -0
  155. package/node_modules/signal-exit/dist/mjs/signals.js +39 -0
  156. package/node_modules/signal-exit/dist/mjs/signals.js.map +1 -0
  157. package/node_modules/signal-exit/package.json +106 -0
  158. package/package.json +17 -4
  159. package/config/commit-conventions.md +0 -28
  160. package/config/skills/_shared/adapters/README.md +0 -74
  161. package/config/skills/_shared/adapters/grok.md +0 -100
  162. package/config/skills/_shared/archive_run.py +0 -969
  163. package/config/skills/_shared/codex-config.md +0 -81
  164. package/config/skills/_shared/collect-codex-findings.py +0 -505
  165. package/config/skills/_shared/finish-gate.py +0 -521
  166. package/config/skills/_shared/grok-anchor-guard.py +0 -78
  167. package/config/skills/_shared/judge-output-parser.py +0 -200
  168. package/config/skills/_shared/judge-role-evidence.py +0 -320
  169. package/config/skills/_shared/phase-prompt-render.py +0 -183
  170. package/config/skills/_shared/process-evidence.py +0 -926
  171. package/config/skills/_shared/resolve-bootstrap.py +0 -1410
  172. package/config/skills/_shared/resolve-stop-hook.py +0 -359
  173. package/config/skills/_shared/run-bounded.py +0 -58
  174. package/config/skills/_shared/spec-verify-check.py +0 -5083
  175. package/config/skills/_shared/state-phase-write.py +0 -5989
  176. package/config/skills/_shared/terminal-claim-check.py +0 -777
  177. package/config/skills/_shared/verify-merge-findings.py +0 -3528
  178. package/config/skills/devlyn-design-ui/SKILL.md +0 -650
  179. package/config/skills/devlyn-ideate/references/from-spec-mode.md +0 -61
  180. package/config/skills/devlyn-ideate/references/project-mode.md +0 -80
  181. package/config/skills/devlyn-ideate/references/spec-template.md +0 -121
  182. package/config/skills/devlyn-ideate/references/templates/decision.md +0 -48
  183. package/config/skills/devlyn-ideate/references/templates/roadmap.md +0 -50
  184. package/config/skills/devlyn-ideate/references/templates/vision.md +0 -44
  185. package/config/skills/devlyn-queue/SKILL.md +0 -66
  186. package/config/skills/devlyn-queue/scripts/append.py +0 -94
  187. package/config/skills/devlyn-resolve/SKILL.md +0 -379
  188. package/config/skills/devlyn-resolve/references/free-form-mode.md +0 -71
  189. package/config/skills/devlyn-resolve/references/outer-loop.md +0 -23
  190. package/config/skills/devlyn-resolve/references/phases/build-gate.md +0 -75
  191. package/config/skills/devlyn-resolve/references/phases/cleanup.md +0 -33
  192. package/config/skills/devlyn-resolve/references/phases/implement.md +0 -44
  193. package/config/skills/devlyn-resolve/references/phases/plan.md +0 -39
  194. package/config/skills/devlyn-resolve/references/phases/probe-derive.md +0 -261
  195. package/config/skills/devlyn-resolve/references/phases/surface-close.md +0 -34
  196. package/config/skills/devlyn-resolve/references/phases/verify.md +0 -310
  197. package/config/skills/devlyn-resolve/references/state-schema.md +0 -152
  198. package/config/skills/devlyn-resolve/references/task-completion.md +0 -160
  199. package/config/templates/prompt-templates.md +0 -71
  200. package/config/templates/template-feature.spec.md +0 -255
  201. package/config/templates/template-product-spec.md +0 -680
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,CAAA;AAuBlB;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,OAAO,GAAG,CACpB,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,KAC1B,IAAI,GAAG,IAAI,CAAA;AA8QhB,eAAO;AACL;;;;;;;;GAQG;AACH,MAAM,OAzMO,OAAO;;wBAPiD,IAAI;AAkNzE;;;;;;GAMG;AACH,IAAI;AAEJ;;;;;;GAMG;AACH,MAAM,YAGP,CAAA"}
@@ -0,0 +1,275 @@
1
+ // Note: since nyc uses this module to output coverage, any lines
2
+ // that are in the direct sync flow of nyc's outputCoverage are
3
+ // ignored, since we can never get coverage for them.
4
+ // grab a reference to node's real process object right away
5
+ import { signals } from './signals.js';
6
+ export { signals };
7
+ const processOk = (process) => !!process &&
8
+ typeof process === 'object' &&
9
+ typeof process.removeListener === 'function' &&
10
+ typeof process.emit === 'function' &&
11
+ typeof process.reallyExit === 'function' &&
12
+ typeof process.listeners === 'function' &&
13
+ typeof process.kill === 'function' &&
14
+ typeof process.pid === 'number' &&
15
+ typeof process.on === 'function';
16
+ const kExitEmitter = Symbol.for('signal-exit emitter');
17
+ const global = globalThis;
18
+ const ObjectDefineProperty = Object.defineProperty.bind(Object);
19
+ // teeny special purpose ee
20
+ class Emitter {
21
+ emitted = {
22
+ afterExit: false,
23
+ exit: false,
24
+ };
25
+ listeners = {
26
+ afterExit: [],
27
+ exit: [],
28
+ };
29
+ count = 0;
30
+ id = Math.random();
31
+ constructor() {
32
+ if (global[kExitEmitter]) {
33
+ return global[kExitEmitter];
34
+ }
35
+ ObjectDefineProperty(global, kExitEmitter, {
36
+ value: this,
37
+ writable: false,
38
+ enumerable: false,
39
+ configurable: false,
40
+ });
41
+ }
42
+ on(ev, fn) {
43
+ this.listeners[ev].push(fn);
44
+ }
45
+ removeListener(ev, fn) {
46
+ const list = this.listeners[ev];
47
+ const i = list.indexOf(fn);
48
+ /* c8 ignore start */
49
+ if (i === -1) {
50
+ return;
51
+ }
52
+ /* c8 ignore stop */
53
+ if (i === 0 && list.length === 1) {
54
+ list.length = 0;
55
+ }
56
+ else {
57
+ list.splice(i, 1);
58
+ }
59
+ }
60
+ emit(ev, code, signal) {
61
+ if (this.emitted[ev]) {
62
+ return false;
63
+ }
64
+ this.emitted[ev] = true;
65
+ let ret = false;
66
+ for (const fn of this.listeners[ev]) {
67
+ ret = fn(code, signal) === true || ret;
68
+ }
69
+ if (ev === 'exit') {
70
+ ret = this.emit('afterExit', code, signal) || ret;
71
+ }
72
+ return ret;
73
+ }
74
+ }
75
+ class SignalExitBase {
76
+ }
77
+ const signalExitWrap = (handler) => {
78
+ return {
79
+ onExit(cb, opts) {
80
+ return handler.onExit(cb, opts);
81
+ },
82
+ load() {
83
+ return handler.load();
84
+ },
85
+ unload() {
86
+ return handler.unload();
87
+ },
88
+ };
89
+ };
90
+ class SignalExitFallback extends SignalExitBase {
91
+ onExit() {
92
+ return () => { };
93
+ }
94
+ load() { }
95
+ unload() { }
96
+ }
97
+ class SignalExit extends SignalExitBase {
98
+ // "SIGHUP" throws an `ENOSYS` error on Windows,
99
+ // so use a supported signal instead
100
+ /* c8 ignore start */
101
+ #hupSig = process.platform === 'win32' ? 'SIGINT' : 'SIGHUP';
102
+ /* c8 ignore stop */
103
+ #emitter = new Emitter();
104
+ #process;
105
+ #originalProcessEmit;
106
+ #originalProcessReallyExit;
107
+ #sigListeners = {};
108
+ #loaded = false;
109
+ constructor(process) {
110
+ super();
111
+ this.#process = process;
112
+ // { <signal>: <listener fn>, ... }
113
+ this.#sigListeners = {};
114
+ for (const sig of signals) {
115
+ this.#sigListeners[sig] = () => {
116
+ // If there are no other listeners, an exit is coming!
117
+ // Simplest way: remove us and then re-send the signal.
118
+ // We know that this will kill the process, so we can
119
+ // safely emit now.
120
+ const listeners = this.#process.listeners(sig);
121
+ let { count } = this.#emitter;
122
+ // This is a workaround for the fact that signal-exit v3 and signal
123
+ // exit v4 are not aware of each other, and each will attempt to let
124
+ // the other handle it, so neither of them do. To correct this, we
125
+ // detect if we're the only handler *except* for previous versions
126
+ // of signal-exit, and increment by the count of listeners it has
127
+ // created.
128
+ /* c8 ignore start */
129
+ const p = process;
130
+ if (typeof p.__signal_exit_emitter__ === 'object' &&
131
+ typeof p.__signal_exit_emitter__.count === 'number') {
132
+ count += p.__signal_exit_emitter__.count;
133
+ }
134
+ /* c8 ignore stop */
135
+ if (listeners.length === count) {
136
+ this.unload();
137
+ const ret = this.#emitter.emit('exit', null, sig);
138
+ /* c8 ignore start */
139
+ const s = sig === 'SIGHUP' ? this.#hupSig : sig;
140
+ if (!ret)
141
+ process.kill(process.pid, s);
142
+ /* c8 ignore stop */
143
+ }
144
+ };
145
+ }
146
+ this.#originalProcessReallyExit = process.reallyExit;
147
+ this.#originalProcessEmit = process.emit;
148
+ }
149
+ onExit(cb, opts) {
150
+ /* c8 ignore start */
151
+ if (!processOk(this.#process)) {
152
+ return () => { };
153
+ }
154
+ /* c8 ignore stop */
155
+ if (this.#loaded === false) {
156
+ this.load();
157
+ }
158
+ const ev = opts?.alwaysLast ? 'afterExit' : 'exit';
159
+ this.#emitter.on(ev, cb);
160
+ return () => {
161
+ this.#emitter.removeListener(ev, cb);
162
+ if (this.#emitter.listeners['exit'].length === 0 &&
163
+ this.#emitter.listeners['afterExit'].length === 0) {
164
+ this.unload();
165
+ }
166
+ };
167
+ }
168
+ load() {
169
+ if (this.#loaded) {
170
+ return;
171
+ }
172
+ this.#loaded = true;
173
+ // This is the number of onSignalExit's that are in play.
174
+ // It's important so that we can count the correct number of
175
+ // listeners on signals, and don't wait for the other one to
176
+ // handle it instead of us.
177
+ this.#emitter.count += 1;
178
+ for (const sig of signals) {
179
+ try {
180
+ const fn = this.#sigListeners[sig];
181
+ if (fn)
182
+ this.#process.on(sig, fn);
183
+ }
184
+ catch (_) { }
185
+ }
186
+ this.#process.emit = (ev, ...a) => {
187
+ return this.#processEmit(ev, ...a);
188
+ };
189
+ this.#process.reallyExit = (code) => {
190
+ return this.#processReallyExit(code);
191
+ };
192
+ }
193
+ unload() {
194
+ if (!this.#loaded) {
195
+ return;
196
+ }
197
+ this.#loaded = false;
198
+ signals.forEach(sig => {
199
+ const listener = this.#sigListeners[sig];
200
+ /* c8 ignore start */
201
+ if (!listener) {
202
+ throw new Error('Listener not defined for signal: ' + sig);
203
+ }
204
+ /* c8 ignore stop */
205
+ try {
206
+ this.#process.removeListener(sig, listener);
207
+ /* c8 ignore start */
208
+ }
209
+ catch (_) { }
210
+ /* c8 ignore stop */
211
+ });
212
+ this.#process.emit = this.#originalProcessEmit;
213
+ this.#process.reallyExit = this.#originalProcessReallyExit;
214
+ this.#emitter.count -= 1;
215
+ }
216
+ #processReallyExit(code) {
217
+ /* c8 ignore start */
218
+ if (!processOk(this.#process)) {
219
+ return 0;
220
+ }
221
+ this.#process.exitCode = code || 0;
222
+ /* c8 ignore stop */
223
+ this.#emitter.emit('exit', this.#process.exitCode, null);
224
+ return this.#originalProcessReallyExit.call(this.#process, this.#process.exitCode);
225
+ }
226
+ #processEmit(ev, ...args) {
227
+ const og = this.#originalProcessEmit;
228
+ if (ev === 'exit' && processOk(this.#process)) {
229
+ if (typeof args[0] === 'number') {
230
+ this.#process.exitCode = args[0];
231
+ /* c8 ignore start */
232
+ }
233
+ /* c8 ignore start */
234
+ const ret = og.call(this.#process, ev, ...args);
235
+ /* c8 ignore start */
236
+ this.#emitter.emit('exit', this.#process.exitCode, null);
237
+ /* c8 ignore stop */
238
+ return ret;
239
+ }
240
+ else {
241
+ return og.call(this.#process, ev, ...args);
242
+ }
243
+ }
244
+ }
245
+ const process = globalThis.process;
246
+ // wrap so that we call the method on the actual handler, without
247
+ // exporting it directly.
248
+ export const {
249
+ /**
250
+ * Called when the process is exiting, whether via signal, explicit
251
+ * exit, or running out of stuff to do.
252
+ *
253
+ * If the global process object is not suitable for instrumentation,
254
+ * then this will be a no-op.
255
+ *
256
+ * Returns a function that may be used to unload signal-exit.
257
+ */
258
+ onExit,
259
+ /**
260
+ * Load the listeners. Likely you never need to call this, unless
261
+ * doing a rather deep integration with signal-exit functionality.
262
+ * Mostly exposed for the benefit of testing.
263
+ *
264
+ * @internal
265
+ */
266
+ load,
267
+ /**
268
+ * Unload the listeners. Likely you never need to call this, unless
269
+ * doing a rather deep integration with signal-exit functionality.
270
+ * Mostly exposed for the benefit of testing.
271
+ *
272
+ * @internal
273
+ */
274
+ unload, } = signalExitWrap(processOk(process) ? new SignalExit(process) : new SignalExitFallback());
275
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// Note: since nyc uses this module to output coverage, any lines\n// that are in the direct sync flow of nyc's outputCoverage are\n// ignored, since we can never get coverage for them.\n// grab a reference to node's real process object right away\nimport { signals } from './signals.js'\nexport { signals }\n\n// just a loosened process type so we can do some evil things\ntype ProcessRE = NodeJS.Process & {\n reallyExit: (code?: number | undefined | null) => any\n emit: (ev: string, ...a: any[]) => any\n}\n\nconst processOk = (process: any): process is ProcessRE =>\n !!process &&\n typeof process === 'object' &&\n typeof process.removeListener === 'function' &&\n typeof process.emit === 'function' &&\n typeof process.reallyExit === 'function' &&\n typeof process.listeners === 'function' &&\n typeof process.kill === 'function' &&\n typeof process.pid === 'number' &&\n typeof process.on === 'function'\n\nconst kExitEmitter = Symbol.for('signal-exit emitter')\nconst global: typeof globalThis & { [kExitEmitter]?: Emitter } = globalThis\nconst ObjectDefineProperty = Object.defineProperty.bind(Object)\n\n/**\n * A function that takes an exit code and signal as arguments\n *\n * In the case of signal exits *only*, a return value of true\n * will indicate that the signal is being handled, and we should\n * not synthetically exit with the signal we received. Regardless\n * of the handler return value, the handler is unloaded when an\n * otherwise fatal signal is received, so you get exactly 1 shot\n * at it, unless you add another onExit handler at that point.\n *\n * In the case of numeric code exits, we may already have committed\n * to exiting the process, for example via a fatal exception or\n * unhandled promise rejection, so it is impossible to stop safely.\n */\nexport type Handler = (\n code: number | null | undefined,\n signal: NodeJS.Signals | null\n) => true | void\ntype ExitEvent = 'afterExit' | 'exit'\ntype Emitted = { [k in ExitEvent]: boolean }\ntype Listeners = { [k in ExitEvent]: Handler[] }\n\n// teeny special purpose ee\nclass Emitter {\n emitted: Emitted = {\n afterExit: false,\n exit: false,\n }\n\n listeners: Listeners = {\n afterExit: [],\n exit: [],\n }\n\n count: number = 0\n id: number = Math.random()\n\n constructor() {\n if (global[kExitEmitter]) {\n return global[kExitEmitter]\n }\n ObjectDefineProperty(global, kExitEmitter, {\n value: this,\n writable: false,\n enumerable: false,\n configurable: false,\n })\n }\n\n on(ev: ExitEvent, fn: Handler) {\n this.listeners[ev].push(fn)\n }\n\n removeListener(ev: ExitEvent, fn: Handler) {\n const list = this.listeners[ev]\n const i = list.indexOf(fn)\n /* c8 ignore start */\n if (i === -1) {\n return\n }\n /* c8 ignore stop */\n if (i === 0 && list.length === 1) {\n list.length = 0\n } else {\n list.splice(i, 1)\n }\n }\n\n emit(\n ev: ExitEvent,\n code: number | null | undefined,\n signal: NodeJS.Signals | null\n ): boolean {\n if (this.emitted[ev]) {\n return false\n }\n this.emitted[ev] = true\n let ret: boolean = false\n for (const fn of this.listeners[ev]) {\n ret = fn(code, signal) === true || ret\n }\n if (ev === 'exit') {\n ret = this.emit('afterExit', code, signal) || ret\n }\n return ret\n }\n}\n\nabstract class SignalExitBase {\n abstract onExit(cb: Handler, opts?: { alwaysLast?: boolean }): () => void\n abstract load(): void\n abstract unload(): void\n}\n\nconst signalExitWrap = <T extends SignalExitBase>(handler: T) => {\n return {\n onExit(cb: Handler, opts?: { alwaysLast?: boolean }) {\n return handler.onExit(cb, opts)\n },\n load() {\n return handler.load()\n },\n unload() {\n return handler.unload()\n },\n }\n}\n\nclass SignalExitFallback extends SignalExitBase {\n onExit() {\n return () => {}\n }\n load() {}\n unload() {}\n}\n\nclass SignalExit extends SignalExitBase {\n // \"SIGHUP\" throws an `ENOSYS` error on Windows,\n // so use a supported signal instead\n /* c8 ignore start */\n #hupSig = process.platform === 'win32' ? 'SIGINT' : 'SIGHUP'\n /* c8 ignore stop */\n #emitter = new Emitter()\n #process: ProcessRE\n #originalProcessEmit: ProcessRE['emit']\n #originalProcessReallyExit: ProcessRE['reallyExit']\n\n #sigListeners: { [k in NodeJS.Signals]?: () => void } = {}\n #loaded: boolean = false\n\n constructor(process: ProcessRE) {\n super()\n this.#process = process\n // { <signal>: <listener fn>, ... }\n this.#sigListeners = {}\n for (const sig of signals) {\n this.#sigListeners[sig] = () => {\n // If there are no other listeners, an exit is coming!\n // Simplest way: remove us and then re-send the signal.\n // We know that this will kill the process, so we can\n // safely emit now.\n const listeners = this.#process.listeners(sig)\n let { count } = this.#emitter\n // This is a workaround for the fact that signal-exit v3 and signal\n // exit v4 are not aware of each other, and each will attempt to let\n // the other handle it, so neither of them do. To correct this, we\n // detect if we're the only handler *except* for previous versions\n // of signal-exit, and increment by the count of listeners it has\n // created.\n /* c8 ignore start */\n const p = process as unknown as {\n __signal_exit_emitter__?: { count: number }\n }\n if (\n typeof p.__signal_exit_emitter__ === 'object' &&\n typeof p.__signal_exit_emitter__.count === 'number'\n ) {\n count += p.__signal_exit_emitter__.count\n }\n /* c8 ignore stop */\n if (listeners.length === count) {\n this.unload()\n const ret = this.#emitter.emit('exit', null, sig)\n /* c8 ignore start */\n const s = sig === 'SIGHUP' ? this.#hupSig : sig\n if (!ret) process.kill(process.pid, s)\n /* c8 ignore stop */\n }\n }\n }\n\n this.#originalProcessReallyExit = process.reallyExit\n this.#originalProcessEmit = process.emit\n }\n\n onExit(cb: Handler, opts?: { alwaysLast?: boolean }) {\n /* c8 ignore start */\n if (!processOk(this.#process)) {\n return () => {}\n }\n /* c8 ignore stop */\n\n if (this.#loaded === false) {\n this.load()\n }\n\n const ev = opts?.alwaysLast ? 'afterExit' : 'exit'\n this.#emitter.on(ev, cb)\n return () => {\n this.#emitter.removeListener(ev, cb)\n if (\n this.#emitter.listeners['exit'].length === 0 &&\n this.#emitter.listeners['afterExit'].length === 0\n ) {\n this.unload()\n }\n }\n }\n\n load() {\n if (this.#loaded) {\n return\n }\n this.#loaded = true\n\n // This is the number of onSignalExit's that are in play.\n // It's important so that we can count the correct number of\n // listeners on signals, and don't wait for the other one to\n // handle it instead of us.\n this.#emitter.count += 1\n\n for (const sig of signals) {\n try {\n const fn = this.#sigListeners[sig]\n if (fn) this.#process.on(sig, fn)\n } catch (_) {}\n }\n\n this.#process.emit = (ev: string, ...a: any[]) => {\n return this.#processEmit(ev, ...a)\n }\n this.#process.reallyExit = (code?: number | null | undefined) => {\n return this.#processReallyExit(code)\n }\n }\n\n unload() {\n if (!this.#loaded) {\n return\n }\n this.#loaded = false\n\n signals.forEach(sig => {\n const listener = this.#sigListeners[sig]\n /* c8 ignore start */\n if (!listener) {\n throw new Error('Listener not defined for signal: ' + sig)\n }\n /* c8 ignore stop */\n try {\n this.#process.removeListener(sig, listener)\n /* c8 ignore start */\n } catch (_) {}\n /* c8 ignore stop */\n })\n this.#process.emit = this.#originalProcessEmit\n this.#process.reallyExit = this.#originalProcessReallyExit\n this.#emitter.count -= 1\n }\n\n #processReallyExit(code?: number | null | undefined) {\n /* c8 ignore start */\n if (!processOk(this.#process)) {\n return 0\n }\n this.#process.exitCode = code || 0\n /* c8 ignore stop */\n\n this.#emitter.emit('exit', this.#process.exitCode, null)\n return this.#originalProcessReallyExit.call(\n this.#process,\n this.#process.exitCode\n )\n }\n\n #processEmit(ev: string, ...args: any[]): any {\n const og = this.#originalProcessEmit\n if (ev === 'exit' && processOk(this.#process)) {\n if (typeof args[0] === 'number') {\n this.#process.exitCode = args[0]\n /* c8 ignore start */\n }\n /* c8 ignore start */\n const ret = og.call(this.#process, ev, ...args)\n /* c8 ignore start */\n this.#emitter.emit('exit', this.#process.exitCode, null)\n /* c8 ignore stop */\n return ret\n } else {\n return og.call(this.#process, ev, ...args)\n }\n }\n}\n\nconst process = globalThis.process\n// wrap so that we call the method on the actual handler, without\n// exporting it directly.\nexport const {\n /**\n * Called when the process is exiting, whether via signal, explicit\n * exit, or running out of stuff to do.\n *\n * If the global process object is not suitable for instrumentation,\n * then this will be a no-op.\n *\n * Returns a function that may be used to unload signal-exit.\n */\n onExit,\n\n /**\n * Load the listeners. Likely you never need to call this, unless\n * doing a rather deep integration with signal-exit functionality.\n * Mostly exposed for the benefit of testing.\n *\n * @internal\n */\n load,\n\n /**\n * Unload the listeners. Likely you never need to call this, unless\n * doing a rather deep integration with signal-exit functionality.\n * Mostly exposed for the benefit of testing.\n *\n * @internal\n */\n unload,\n} = signalExitWrap(\n processOk(process) ? new SignalExit(process) : new SignalExitFallback()\n)\n"]}
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,29 @@
1
+ /// <reference types="node" />
2
+ /**
3
+ * This is not the set of all possible signals.
4
+ *
5
+ * It IS, however, the set of all signals that trigger
6
+ * an exit on either Linux or BSD systems. Linux is a
7
+ * superset of the signal names supported on BSD, and
8
+ * the unknown signals just fail to register, so we can
9
+ * catch that easily enough.
10
+ *
11
+ * Windows signals are a different set, since there are
12
+ * signals that terminate Windows processes, but don't
13
+ * terminate (or don't even exist) on Posix systems.
14
+ *
15
+ * Don't bother with SIGKILL. It's uncatchable, which
16
+ * means that we can't fire any callbacks anyway.
17
+ *
18
+ * If a user does happen to register a handler on a non-
19
+ * fatal signal like SIGWINCH or something, and then
20
+ * exit, it'll end up firing `process.emit('exit')`, so
21
+ * the handler will be fired anyway.
22
+ *
23
+ * SIGBUS, SIGFPE, SIGSEGV and SIGILL, when not raised
24
+ * artificially, inherently leave the process in a
25
+ * state from which it is not safe to try and enter JS
26
+ * listeners.
27
+ */
28
+ export declare const signals: NodeJS.Signals[];
29
+ //# sourceMappingURL=signals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signals.d.ts","sourceRoot":"","sources":["../../src/signals.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,OAAO,EAAO,CAAA"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * This is not the set of all possible signals.
3
+ *
4
+ * It IS, however, the set of all signals that trigger
5
+ * an exit on either Linux or BSD systems. Linux is a
6
+ * superset of the signal names supported on BSD, and
7
+ * the unknown signals just fail to register, so we can
8
+ * catch that easily enough.
9
+ *
10
+ * Windows signals are a different set, since there are
11
+ * signals that terminate Windows processes, but don't
12
+ * terminate (or don't even exist) on Posix systems.
13
+ *
14
+ * Don't bother with SIGKILL. It's uncatchable, which
15
+ * means that we can't fire any callbacks anyway.
16
+ *
17
+ * If a user does happen to register a handler on a non-
18
+ * fatal signal like SIGWINCH or something, and then
19
+ * exit, it'll end up firing `process.emit('exit')`, so
20
+ * the handler will be fired anyway.
21
+ *
22
+ * SIGBUS, SIGFPE, SIGSEGV and SIGILL, when not raised
23
+ * artificially, inherently leave the process in a
24
+ * state from which it is not safe to try and enter JS
25
+ * listeners.
26
+ */
27
+ export const signals = [];
28
+ signals.push('SIGHUP', 'SIGINT', 'SIGTERM');
29
+ if (process.platform !== 'win32') {
30
+ signals.push('SIGALRM', 'SIGABRT', 'SIGVTALRM', 'SIGXCPU', 'SIGXFSZ', 'SIGUSR2', 'SIGTRAP', 'SIGSYS', 'SIGQUIT', 'SIGIOT'
31
+ // should detect profiler and enable/disable accordingly.
32
+ // see #21
33
+ // 'SIGPROF'
34
+ );
35
+ }
36
+ if (process.platform === 'linux') {
37
+ signals.push('SIGIO', 'SIGPOLL', 'SIGPWR', 'SIGSTKFLT');
38
+ }
39
+ //# sourceMappingURL=signals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signals.js","sourceRoot":"","sources":["../../src/signals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,MAAM,OAAO,GAAqB,EAAE,CAAA;AAC3C,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAA;AAE3C,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE;IAChC,OAAO,CAAC,IAAI,CACV,SAAS,EACT,SAAS,EACT,WAAW,EACX,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,QAAQ,EACR,SAAS,EACT,QAAQ;IACR,yDAAyD;IACzD,UAAU;IACV,YAAY;KACb,CAAA;CACF;AAED,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE;IAChC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAA;CACxD","sourcesContent":["/**\n * This is not the set of all possible signals.\n *\n * It IS, however, the set of all signals that trigger\n * an exit on either Linux or BSD systems. Linux is a\n * superset of the signal names supported on BSD, and\n * the unknown signals just fail to register, so we can\n * catch that easily enough.\n *\n * Windows signals are a different set, since there are\n * signals that terminate Windows processes, but don't\n * terminate (or don't even exist) on Posix systems.\n *\n * Don't bother with SIGKILL. It's uncatchable, which\n * means that we can't fire any callbacks anyway.\n *\n * If a user does happen to register a handler on a non-\n * fatal signal like SIGWINCH or something, and then\n * exit, it'll end up firing `process.emit('exit')`, so\n * the handler will be fired anyway.\n *\n * SIGBUS, SIGFPE, SIGSEGV and SIGILL, when not raised\n * artificially, inherently leave the process in a\n * state from which it is not safe to try and enter JS\n * listeners.\n */\nexport const signals: NodeJS.Signals[] = []\nsignals.push('SIGHUP', 'SIGINT', 'SIGTERM')\n\nif (process.platform !== 'win32') {\n signals.push(\n 'SIGALRM',\n 'SIGABRT',\n 'SIGVTALRM',\n 'SIGXCPU',\n 'SIGXFSZ',\n 'SIGUSR2',\n 'SIGTRAP',\n 'SIGSYS',\n 'SIGQUIT',\n 'SIGIOT'\n // should detect profiler and enable/disable accordingly.\n // see #21\n // 'SIGPROF'\n )\n}\n\nif (process.platform === 'linux') {\n signals.push('SIGIO', 'SIGPOLL', 'SIGPWR', 'SIGSTKFLT')\n}\n"]}
@@ -0,0 +1,106 @@
1
+ {
2
+ "name": "signal-exit",
3
+ "version": "4.1.0",
4
+ "description": "when you want to fire an event no matter how a process exits.",
5
+ "main": "./dist/cjs/index.js",
6
+ "module": "./dist/mjs/index.js",
7
+ "browser": "./dist/mjs/browser.js",
8
+ "types": "./dist/mjs/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/mjs/index.d.ts",
13
+ "default": "./dist/mjs/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/cjs/index.d.ts",
17
+ "default": "./dist/cjs/index.js"
18
+ }
19
+ },
20
+ "./signals": {
21
+ "import": {
22
+ "types": "./dist/mjs/signals.d.ts",
23
+ "default": "./dist/mjs/signals.js"
24
+ },
25
+ "require": {
26
+ "types": "./dist/cjs/signals.d.ts",
27
+ "default": "./dist/cjs/signals.js"
28
+ }
29
+ },
30
+ "./browser": {
31
+ "import": {
32
+ "types": "./dist/mjs/browser.d.ts",
33
+ "default": "./dist/mjs/browser.js"
34
+ },
35
+ "require": {
36
+ "types": "./dist/cjs/browser.d.ts",
37
+ "default": "./dist/cjs/browser.js"
38
+ }
39
+ }
40
+ },
41
+ "files": [
42
+ "dist"
43
+ ],
44
+ "engines": {
45
+ "node": ">=14"
46
+ },
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "https://github.com/tapjs/signal-exit.git"
50
+ },
51
+ "keywords": [
52
+ "signal",
53
+ "exit"
54
+ ],
55
+ "author": "Ben Coe <ben@npmjs.com>",
56
+ "license": "ISC",
57
+ "devDependencies": {
58
+ "@types/cross-spawn": "^6.0.2",
59
+ "@types/node": "^18.15.11",
60
+ "@types/signal-exit": "^3.0.1",
61
+ "@types/tap": "^15.0.8",
62
+ "c8": "^7.13.0",
63
+ "prettier": "^2.8.6",
64
+ "tap": "^16.3.4",
65
+ "ts-node": "^10.9.1",
66
+ "typedoc": "^0.23.28",
67
+ "typescript": "^5.0.2"
68
+ },
69
+ "scripts": {
70
+ "preversion": "npm test",
71
+ "postversion": "npm publish",
72
+ "prepublishOnly": "git push origin --follow-tags",
73
+ "preprepare": "rm -rf dist",
74
+ "prepare": "tsc -p tsconfig.json && tsc -p tsconfig-esm.json && bash ./scripts/fixup.sh",
75
+ "pretest": "npm run prepare",
76
+ "presnap": "npm run prepare",
77
+ "test": "c8 tap",
78
+ "snap": "c8 tap",
79
+ "format": "prettier --write . --loglevel warn",
80
+ "typedoc": "typedoc --tsconfig tsconfig-esm.json ./src/*.ts"
81
+ },
82
+ "prettier": {
83
+ "semi": false,
84
+ "printWidth": 75,
85
+ "tabWidth": 2,
86
+ "useTabs": false,
87
+ "singleQuote": true,
88
+ "jsxSingleQuote": false,
89
+ "bracketSameLine": true,
90
+ "arrowParens": "avoid",
91
+ "endOfLine": "lf"
92
+ },
93
+ "tap": {
94
+ "coverage": false,
95
+ "jobs": 1,
96
+ "node-arg": [
97
+ "--no-warnings",
98
+ "--loader",
99
+ "ts-node/esm"
100
+ ],
101
+ "ts": false
102
+ },
103
+ "funding": {
104
+ "url": "https://github.com/sponsors/isaacs"
105
+ }
106
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "devlyn-cli",
3
- "version": "4.1.0",
4
- "description": "Loop & harness engineering for AI coding agents (Claude Code, Codex, and any AGENTS.md-driven agent) — take intent to shipped, engineer-quality software hands-free with phase-gated pipelines and agent orchestration",
3
+ "version": "4.2.1",
4
+ "description": "Loop & harness engineering for AI coding agents (Claude Code, Codex, and any AGENTS.md-driven agent) — core engineering principles for CLAUDE.md/AGENTS.md and a loop designer that splits one intent into tasks agents drain hands-free",
5
5
  "homepage": "https://github.com/fysoul17/devlyn-cli#readme",
6
6
  "bin": {
7
7
  "devlyn": "bin/devlyn.js"
@@ -39,7 +39,6 @@
39
39
  "loop-engineering",
40
40
  "agent-orchestration",
41
41
  "prompt-engineering",
42
- "code-review",
43
42
  "devlyn",
44
43
  "developer-tools",
45
44
  "automation",
@@ -50,5 +49,19 @@
50
49
  "repository": {
51
50
  "type": "git",
52
51
  "url": "git+https://github.com/fysoul17/devlyn-cli.git"
53
- }
52
+ },
53
+ "dependencies": {
54
+ "@inquirer/checkbox": "5.2.6",
55
+ "@inquirer/select": "5.2.6"
56
+ },
57
+ "devDependencies": {
58
+ "@xterm/headless": "6.0.0"
59
+ },
60
+ "engines": {
61
+ "node": "^20.17.0 || ^22.13.0 || >=23.5.0"
62
+ },
63
+ "bundleDependencies": [
64
+ "@inquirer/checkbox",
65
+ "@inquirer/select"
66
+ ]
54
67
  }
@@ -1,28 +0,0 @@
1
- # Commit Conventions
2
-
3
- Use conventional commits: `<type>: <description>` (scope optional: `<type>(<scope>): <description>`)
4
-
5
- | Type | Use for |
6
- |------------|------------------------------------------------------|
7
- | `feat` | New feature |
8
- | `fix` | Bug fix |
9
- | `chore` | Routine tasks (deps, non-production changes) |
10
- | `docs` | Documentation updates |
11
- | `style` | Code style only (formatting, spacing) |
12
- | `refactor` | Restructuring (no fix or feature) |
13
- | `perf` | Performance improvements |
14
- | `test` | Adding or updating tests |
15
- | `build` | Build system or dependency changes |
16
- | `ci` | CI/CD pipeline updates |
17
- | `revert` | Reverting a previous commit |
18
- | `temp` | Temporary changes (not for long-term merge) |
19
-
20
- ## Examples
21
-
22
- ```
23
- feat: add user authentication
24
- fix: resolve null pointer in auth middleware
25
- refactor(api): simplify request validation
26
- docs: update API usage guide
27
- chore: upgrade next.js to v16.2
28
- ```
@@ -1,74 +0,0 @@
1
- # Per-engine prompt adapters
2
-
3
- This folder is the LLM-specific delta layer. The harness's canonical phase prompts (in each skill's `references/phases/<phase>.md`) stay model-neutral and outcome-first. Each adapter file in this folder is a **small delta header** that gets injected BEFORE the canonical body when the phase runs against that specific engine.
4
-
5
- ## Why adapters exist
6
-
7
- Anthropic and OpenAI publish official prompt-engineering guides for their flagship models. The two guides converge on outcome-first + decision rules + mechanical validation but **diverge on tactics** (XML structure vs stop-rules format, literal interpretation vs decision-rule phrasing, self-check pattern vs validation-tool primacy). A single canonical prompt can't hit both ceilings.
8
-
9
- The split:
10
- - **Canonical body** (in `<skill>/references/phases/`) = the contract: goal, output format, invariants, common-ground rules from both guides.
11
- - **Adapter header** (here) = the per-engine elaboration: model-specific guidance from that engine's official guide.
12
-
13
- This is also the load-bearing piece for **multi-LLM evolution**. When Qwen / Gemini / Gemma are added (Mission 2/3), each gets its own adapter file here. The canonical body never moves.
14
-
15
- ## Format
16
-
17
- Each adapter is a single markdown file named `<engine>.md`, matching the `--engine` selector value (e.g. `claude.md`, `codex.md`). Structure:
18
-
19
- ```markdown
20
- # <Engine> adapter
21
-
22
- > Source: <official-prompt-engineering-guide URL>
23
-
24
- ## Identity
25
- 1-2 lines telling the model who it is + which guide governs.
26
-
27
- ## Output discipline
28
- Verbosity, formatting, length conventions specific to this model.
29
-
30
- ## Tool-use posture
31
- When to use tools, when to reason, parallel/sequential preferences.
32
-
33
- ## Effort and autonomy
34
- Optional. Model-specific guidance for effort levels or autonomous-vs-interactive runs when the vendor guide calls this out.
35
-
36
- ## Validation pattern
37
- How this model verifies its work — mechanical-first vs self-check, etc.
38
-
39
- ## Anti-patterns
40
- Specific patterns the official guide warns about for this model.
41
- ```
42
-
43
- Keep each section to ≤ 8 lines. Adapters are deltas, not full prompts. If an adapter grows past ~80 lines, the content probably belongs in canonical body.
44
-
45
- ## Role-scoped and non-agentic adapters
46
-
47
- Adapters may add two more sections beyond the base format:
48
-
49
- - **`## Role eligibility`** — fixed ASCII fields, one per line: `executor: yes|no`, `pair_judge: yes|no`. Absent section = eligible for both roles (every adapter shipped before iter-0051). Present only when a backend is structurally unable or not certified for a role — e.g. a completion-only local model has no tool/file-edit access, so it declares `executor: no`. `_shared/engine-preflight.md` and `/devlyn-engines` read this section as a structured marker, not prose, so it validates mechanically.
50
- - **`## Invocation`** — for backends called over an API or CLI backends with non-default invocation contracts: the concrete request shape (endpoint, JSON body, timeout) and the adapter-declared availability probe (`_shared/engine-preflight.md#role-resolution`'s "an adapter may declare a different probe" hook).
51
-
52
- ## When to add a new adapter
53
-
54
- A new adapter file ships when:
55
- 1. A new LLM is integrated into the pipeline (the engine is now invocable).
56
- 2. An official prompt-engineering guide for that LLM exists (or a vendor-recommended pattern set).
57
- 3. An empirical A/B shows the adapter's specific guidance lifts that model's performance over the canonical body alone.
58
-
59
- Not all models need adapters. If a model performs well on the canonical body without delta, ship without one.
60
-
61
- ## What NOT to put here
62
-
63
- - ❌ Universal rules (those go in canonical body or `_shared/runtime-principles.md`).
64
- - ❌ Iter-history annotations (`*(iter-0020: F4 evidence...)*` style).
65
- - ❌ Full phase prompts (defeats the decoupling).
66
- - ❌ Per-task or per-spec content (adapters are model-scope, not task-scope).
67
-
68
- ## Runtime injection
69
-
70
- A skill's phase invocation prepends the resolved engine's adapter file to the canonical body before sending. Mechanism is left to each skill (a `_shared/adapter-inject.sh` helper may land in a later iter); for now, skills consume the adapter file by direct read at phase-spawn time.
71
-
72
- ## Standing rule
73
-
74
- Any iter that touches an adapter file MUST cite the corresponding official guide as part of acceptance: "guide section X.Y says Z, this change applies Z." Generic preferences ("feels cleaner") are rejected.