omniconductor 0.5.0 → 1.0.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 (52) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +59 -19
  3. package/VISION.md +2 -2
  4. package/adapters/README.md +8 -8
  5. package/adapters/claude/README.md +3 -6
  6. package/adapters/claude/SUPPORTED-FEATURES.md +4 -4
  7. package/adapters/claude/metadata.json +39 -0
  8. package/adapters/claude/transform-spec.md +1 -1
  9. package/adapters/claude/transform.sh +208 -38
  10. package/adapters/codex/README.md +23 -27
  11. package/adapters/codex/metadata.json +35 -0
  12. package/adapters/codex/transform-spec.md +5 -6
  13. package/adapters/codex/transform.sh +284 -37
  14. package/adapters/copilot/README.md +26 -29
  15. package/adapters/copilot/metadata.json +36 -0
  16. package/adapters/copilot/transform.sh +126 -35
  17. package/adapters/cursor/README.md +31 -30
  18. package/adapters/cursor/metadata.json +35 -0
  19. package/adapters/cursor/transform.sh +117 -28
  20. package/adapters/gemini/README.md +14 -15
  21. package/adapters/gemini/metadata.json +36 -0
  22. package/adapters/gemini/transform.sh +312 -36
  23. package/adapters/windsurf/README.md +20 -19
  24. package/adapters/windsurf/metadata.json +36 -0
  25. package/adapters/windsurf/transform.sh +139 -55
  26. package/bin/doctor.js +257 -0
  27. package/bin/omniconductor.js +15 -2
  28. package/core/anti-patterns/frequent-rule-file-edit.md +1 -1
  29. package/core/anti-patterns/single-monolithic-rule-file.md +1 -1
  30. package/core/hooks/README.md +1 -0
  31. package/core/hooks/pretool-loop-guard.sh.template +177 -0
  32. package/core/recipes/README.md +6 -4
  33. package/core/recipes/loop-engineering.md +73 -0
  34. package/core/universal-rules/README.md +4 -4
  35. package/core/universal-rules/meta-discipline.md +4 -4
  36. package/core/universal-rules/spec-as-you-go.md +1 -1
  37. package/docs/ADAPTER-LIVE-VERIFICATION.md +73 -0
  38. package/docs/COMPARISON.md +133 -0
  39. package/docs/COMPATIBILITY-MATRIX.md +126 -0
  40. package/docs/DESIGN-DECISIONS.md +1268 -0
  41. package/docs/MANUAL-INSTALL.md +15 -15
  42. package/docs/PUBLICATION-POLICY.md +46 -0
  43. package/docs/PUBLISH-GUIDE.md +141 -0
  44. package/package.json +7 -1
  45. package/tools/check-adapter-metadata.sh +211 -0
  46. package/tools/check-stale-tokens.sh +130 -0
  47. package/tools/generate-adapter-docs.js +123 -0
  48. package/tools/live-verify.sh +195 -0
  49. package/tools/manifest-safety.sh +118 -0
  50. package/tools/stale-tokens.txt +32 -0
  51. package/tools/test-install-modes.sh +277 -0
  52. package/tools/validate-adapter-output.sh +1 -1
@@ -0,0 +1,277 @@
1
+ #!/usr/bin/env bash
2
+ # CONDUCTOR --mode preset test harness (ADR-044).
3
+ # Usage: bash tools/test-install-modes.sh <tool> [base-tmp-dir]
4
+ # Exit 0 = all mode behaviors verified for the tool, 1 = a check failed, 2 = usage.
5
+ #
6
+ # Verifies, per tool:
7
+ # full — installs + validator passes + manifest stamps "mode": "full"
8
+ # minimal — rule text + docs present; NO reflector runtime (.conductor/) even
9
+ # with --recipes=self-improvement; tool-specific extras absent
10
+ # strict — fresh target installs; pre-seeded baseline ABORTS (exit 3, no writes)
11
+ # recipes-only — ONLY recipe content lands (file or marked block); --recipes required
12
+ # (error without); uninstall removes it losslessly
13
+ # reflector-only — self-improvement text + Reflector runtime, nothing else; uninstall clean
14
+ # block modes — (gemini/codex) pre-existing baseline content survives append + strip;
15
+ # customized block is left in place on uninstall
16
+
17
+ set -u
18
+
19
+ TOOL="${1:-}"
20
+ case "$TOOL" in
21
+ claude|cursor|copilot|gemini|codex|windsurf) : ;;
22
+ *) echo "Usage: $0 <claude|cursor|copilot|gemini|codex|windsurf> [base-tmp-dir]" >&2; exit 2 ;;
23
+ esac
24
+
25
+ cd "$(dirname "$0")/.." || exit 2
26
+ BASE="${2:-$(mktemp -d "${TMPDIR:-/tmp}/conductor-modes-$TOOL.XXXXXX")}"
27
+ T="adapters/$TOOL/transform.sh"
28
+
29
+ FAIL=0
30
+ ok() { echo "OK [$TOOL] $1"; }
31
+ bad() { echo "FAIL [$TOOL] $1"; FAIL=1; }
32
+ have() { [ -e "$1" ]; }
33
+
34
+ # Per-tool file map
35
+ case "$TOOL" in
36
+ claude) BASELINE="CLAUDE.md"; RULE="" ;;
37
+ cursor) BASELINE=".cursor/rules/workflow.mdc"; RULE=".cursor/rules/workflow.mdc" ;;
38
+ copilot) BASELINE=".github/copilot-instructions.md"; RULE=".github/copilot-instructions.md" ;;
39
+ gemini) BASELINE="GEMINI.md"; RULE="GEMINI.md" ;;
40
+ codex) BASELINE="AGENTS.md"; RULE="AGENTS.md" ;;
41
+ windsurf) BASELINE=".windsurfrules"; RULE=".devin/rules/workflow.md" ;;
42
+ esac
43
+ # claude special-case: universal rule location
44
+ [ "$TOOL" = "claude" ] && RULE=".claude/rules/workflow.md"
45
+
46
+ recipe_artifact() { # path proving the tdd recipe landed, per tool + mode-kind (file|block)
47
+ local d="$1"
48
+ case "$TOOL" in
49
+ claude) echo "$d/.claude/rules/tdd.md" ;;
50
+ cursor) echo "$d/.cursor/rules/tdd.mdc" ;;
51
+ copilot) echo "$d/.github/instructions/tdd.instructions.md" ;;
52
+ windsurf) echo "$d/.devin/rules/tdd.md" ;;
53
+ gemini) echo "$d/GEMINI.md" ;; # block host
54
+ codex) echo "$d/AGENTS.md" ;; # block host
55
+ esac
56
+ }
57
+
58
+ # ---- full ------------------------------------------------------------------
59
+ d="$BASE/full"; mkdir -p "$d"
60
+ if bash "$T" "$d" --no-prompt --recipes=tdd,self-improvement >/dev/null 2>&1 \
61
+ && bash tools/validate-adapter-output.sh "$d" "$TOOL" >/dev/null 2>&1 \
62
+ && grep -q '"mode": "full"' "$d/.conductor-manifest.json"; then
63
+ ok "full: install + validator + manifest mode stamp"
64
+ else bad "full mode"; fi
65
+
66
+ # ---- manifest safety: repeat install + user edits -------------------------
67
+ # Re-running a full install must retain the original pre-CONDUCTOR baseline
68
+ # for uninstall, and uninstall must never delete a post-install user edit.
69
+ d="$BASE/full-reinstall"; mkdir -p "$d/$(dirname "$BASELINE")"
70
+ printf 'ORIGINAL-USER-BASELINE-%s\n' "$TOOL" > "$d/$BASELINE"
71
+ before="$(/usr/bin/cksum < "$d/$BASELINE")"
72
+ bash "$T" "$d" --no-prompt >/dev/null 2>&1 \
73
+ && bash "$T" "$d" --no-prompt >/dev/null 2>&1 \
74
+ && bash "$T" "$d" --uninstall >/dev/null 2>&1
75
+ after="MISSING"; [ -f "$d/$BASELINE" ] && after="$(/usr/bin/cksum < "$d/$BASELINE")"
76
+ [ "$before" = "$after" ] && ok "full: re-install + uninstall restores original baseline" || bad "full re-install lost original baseline"
77
+
78
+ d="$BASE/full-customized"; mkdir -p "$d"
79
+ bash "$T" "$d" --no-prompt >/dev/null 2>&1
80
+ printf '\nUSER-CUSTOMIZATION-MUST-SURVIVE\n' >> "$d/$BASELINE"
81
+ bash "$T" "$d" --uninstall >/dev/null 2>&1
82
+ grep -q 'USER-CUSTOMIZATION-MUST-SURVIVE' "$d/$BASELINE" 2>/dev/null \
83
+ && ok "full: uninstall preserves user-modified emitted file" \
84
+ || bad "full uninstall deleted user customization"
85
+
86
+ d="$BASE/full-edit-then-update"; mkdir -p "$d"
87
+ bash "$T" "$d" --no-prompt >/dev/null 2>&1
88
+ printf '\nUSER-EDIT-BEFORE-UPDATE\n' >> "$d/$BASELINE"
89
+ bash "$T" "$d" --no-prompt >/dev/null 2>&1
90
+ bash "$T" "$d" --uninstall >/dev/null 2>&1
91
+ grep -q 'USER-EDIT-BEFORE-UPDATE' "$d/$BASELINE" 2>/dev/null \
92
+ && ok "full: update snapshots a user edit before replacement" \
93
+ || bad "full update lost user edit"
94
+
95
+ # ---- minimal ---------------------------------------------------------------
96
+ d="$BASE/minimal"; mkdir -p "$d"
97
+ bash "$T" "$d" --mode=minimal --recipes=tdd,self-improvement >/dev/null 2>&1
98
+ if have "$d/$RULE" && have "$d/docs/CURRENT_WORK.md" && [ ! -d "$d/.conductor" ]; then
99
+ ok "minimal: rule text + docs, no Reflector runtime"
100
+ else bad "minimal mode"; fi
101
+ if [ "$TOOL" = "claude" ]; then
102
+ { [ ! -d "$d/.claude/agents" ] && [ ! -d "$d/.claude/hooks" ]; } && ok "minimal: no agents/hooks (claude)" || bad "minimal leaked agents/hooks (claude)"
103
+ fi
104
+
105
+ # ---- strict ----------------------------------------------------------------
106
+ d="$BASE/strict-fresh"; mkdir -p "$d"
107
+ bash "$T" "$d" --mode=strict --no-prompt >/dev/null 2>&1 && have "$d/$RULE" \
108
+ && ok "strict: fresh target installs" || bad "strict fresh install"
109
+ d="$BASE/strict-seeded"; mkdir -p "$d"
110
+ case "$TOOL" in
111
+ cursor) mkdir -p "$d/.cursor/rules"; echo x > "$d/.cursor/rules/mine.mdc" ;;
112
+ copilot) mkdir -p "$d/.github"; echo MINE > "$d/.github/copilot-instructions.md" ;;
113
+ *) mkdir -p "$d/$(dirname "$BASELINE")" 2>/dev/null; echo MINE > "$d/$BASELINE" ;;
114
+ esac
115
+ bash "$T" "$d" --mode=strict --no-prompt >/dev/null 2>&1
116
+ rc=$?
117
+ if [ "$rc" -eq 3 ] && [ ! -f "$d/.conductor-manifest.json" ]; then
118
+ ok "strict: seeded baseline aborts (exit 3, no manifest)"
119
+ else bad "strict seeded abort (rc=$rc)"; fi
120
+
121
+ # Multi-surface tools: seeding ONLY the secondary rules surface must also abort.
122
+ case "$TOOL" in
123
+ claude|copilot|gemini|windsurf)
124
+ d="$BASE/strict-seeded2"; mkdir -p "$d"
125
+ case "$TOOL" in
126
+ claude) mkdir -p "$d/.claude/rules"; echo x > "$d/.claude/rules/mine.md" ;;
127
+ copilot) mkdir -p "$d/.github/instructions"; echo x > "$d/.github/instructions/mine.instructions.md" ;;
128
+ gemini) mkdir -p "$d/.gemini"; echo x > "$d/.gemini/styleguide.md" ;;
129
+ windsurf) mkdir -p "$d/.devin/rules"; echo x > "$d/.devin/rules/mine.md" ;;
130
+ esac
131
+ bash "$T" "$d" --mode=strict --no-prompt >/dev/null 2>&1
132
+ rc=$?
133
+ if [ "$rc" -eq 3 ] && [ ! -f "$d/.conductor-manifest.json" ]; then
134
+ ok "strict: seeded secondary rules surface also aborts"
135
+ else bad "strict secondary-surface abort (rc=$rc)"; fi
136
+ ;;
137
+ esac
138
+
139
+ # ---- recipes-only ----------------------------------------------------------
140
+ d="$BASE/ro-noargs"; mkdir -p "$d"
141
+ bash "$T" "$d" --mode=recipes-only >/dev/null 2>&1
142
+ [ $? -eq 1 ] && ok "recipes-only: requires --recipes (errors without)" || bad "recipes-only missing-recipes guard"
143
+
144
+ d="$BASE/ro"; mkdir -p "$d"
145
+ PRE=""
146
+ if [ "$TOOL" = "gemini" ] || [ "$TOOL" = "codex" ]; then
147
+ PRE="MY EXISTING RULES"
148
+ echo "$PRE" > "$d/$BASELINE"
149
+ fi
150
+ bash "$T" "$d" --mode=recipes-only --recipes=tdd >/dev/null 2>&1
151
+ art="$(recipe_artifact "$d")"
152
+ okay=true
153
+ have "$art" || okay=false
154
+ [ -d "$d/docs" ] && okay=false
155
+ if [ "$TOOL" = "gemini" ] || [ "$TOOL" = "codex" ]; then
156
+ grep -q 'conductor:block recipes' "$art" || okay=false
157
+ grep -q "$PRE" "$art" || okay=false
158
+ else
159
+ # per-file tools must NOT have emitted universal rules
160
+ have "$d/$RULE" && okay=false
161
+ fi
162
+ $okay && ok "recipes-only: only recipe content landed" || bad "recipes-only emission"
163
+
164
+ bash "$T" "$d" --uninstall >/dev/null 2>&1
165
+ okay=true
166
+ if [ "$TOOL" = "gemini" ] || [ "$TOOL" = "codex" ]; then
167
+ grep -q 'conductor:block' "$art" 2>/dev/null && okay=false
168
+ grep -q "$PRE" "$art" 2>/dev/null || okay=false
169
+ else
170
+ have "$art" && okay=false
171
+ fi
172
+ $okay && ok "recipes-only: uninstall lossless" || bad "recipes-only uninstall"
173
+
174
+ # ---- reflector-only --------------------------------------------------------
175
+ d="$BASE/reflonly"; mkdir -p "$d"
176
+ bash "$T" "$d" --mode=reflector-only >/dev/null 2>&1
177
+ okay=true
178
+ if [ "$TOOL" = "claude" ]; then
179
+ # Claude's trajectory logger is the Stop hook, not the portable stdin logger.
180
+ [ -x "$d/.claude/hooks/stop-trajectory-log.sh" ] || okay=false
181
+ [ -d "$d/.conductor/reflect" ] || okay=false
182
+ else
183
+ [ -x "$d/.conductor/reflect/trajectory-log.sh" ] || okay=false
184
+ fi
185
+ [ -d "$d/docs" ] && okay=false
186
+ case "$TOOL" in
187
+ claude) { have "$d/.claude/rules/self-improvement.md" && have "$d/.claude/agents/reflector.md" && have "$d/.claude/hooks/stop-trajectory-log.sh" && have "$d/.claude/settings.json" && [ ! -f "$d/CLAUDE.md" ]; } || okay=false ;;
188
+ cursor) { have "$d/.cursor/rules/self-improvement.mdc" && have "$d/.cursor/hooks.json" && [ ! -f "$d/.cursor/rules/workflow.mdc" ]; } || okay=false ;;
189
+ copilot) { have "$d/.github/instructions/self-improvement.instructions.md" && have "$d/.github/hooks/conductor-reflect.json" && [ ! -f "$d/.github/copilot-instructions.md" ]; } || okay=false ;;
190
+ gemini) { grep -q 'conductor:block reflector' "$d/GEMINI.md" && have "$d/.gemini/settings.json"; } || okay=false ;;
191
+ codex) { grep -q 'conductor:block reflector' "$d/AGENTS.md" && have "$d/.codex/hooks.json"; } || okay=false ;;
192
+ windsurf) { have "$d/.devin/rules/self-improvement.md" && have "$d/.windsurf/hooks.json" && [ ! -f "$d/.windsurfrules" ]; } || okay=false ;;
193
+ esac
194
+ $okay && ok "reflector-only: loop artifacts only" || bad "reflector-only emission"
195
+ bash "$T" "$d" --uninstall >/dev/null 2>&1
196
+ [ ! -d "$d/.conductor" ] && ok "reflector-only: uninstall clean" || bad "reflector-only uninstall"
197
+
198
+ # ---- zero valid recipes must fail (all tools) -------------------------------
199
+ d="$BASE/ro-badname"; mkdir -p "$d"
200
+ bash "$T" "$d" --mode=recipes-only --recipes=notarealrecipe >/dev/null 2>&1
201
+ if [ $? -eq 1 ] && [ ! -f "$d/.conductor-manifest.json" ] && [ ! -f "$d/.conductor-manifest.json.staging" ]; then
202
+ ok "recipes-only: zero valid recipes → exit 1, no manifest/staging litter"
203
+ else bad "recipes-only zero-valid-recipes guard"; fi
204
+
205
+ # ---- byte-lossless block round-trip + cross-mode (single-file tools) ---------
206
+ if [ "$TOOL" = "gemini" ] || [ "$TOOL" = "codex" ]; then
207
+ # A user-owned marker (including a malformed one) must never be treated as
208
+ # an existing CONDUCTOR block. Refuse without touching the host file.
209
+ d="$BASE/marker-collision"; mkdir -p "$d"
210
+ printf 'USER PREFIX\n<!-- conductor:block recipes -->\nUSER CONTENT AFTER MARKER\n' > "$d/$BASELINE"
211
+ before="$(/usr/bin/cksum < "$d/$BASELINE")"
212
+ bash "$T" "$d" --mode=recipes-only --recipes=tdd >/dev/null 2>&1
213
+ rc=$?
214
+ after="$(/usr/bin/cksum < "$d/$BASELINE")"
215
+ if [ "$rc" -eq 1 ] && [ "$before" = "$after" ] && [ ! -f "$d/.conductor-manifest.json" ] && [ ! -f "$d/.conductor-manifest.json.staging" ]; then
216
+ ok "block: foreign/unpaired marker aborts without data loss"
217
+ else bad "block: foreign marker collision (rc=$rc)"; fi
218
+
219
+ d="$BASE/marker-foreign-paired"; mkdir -p "$d"
220
+ printf 'USER PREFIX\n<!-- conductor:block recipes -->\nUSER BLOCK\n<!-- /conductor:block recipes -->\nUSER SUFFIX\n' > "$d/$BASELINE"
221
+ before="$(/usr/bin/cksum < "$d/$BASELINE")"
222
+ bash "$T" "$d" --mode=recipes-only --recipes=tdd >/dev/null 2>&1
223
+ rc=$?
224
+ after="$(/usr/bin/cksum < "$d/$BASELINE")"
225
+ if [ "$rc" -eq 1 ] && [ "$before" = "$after" ] && [ ! -f "$d/.conductor-manifest.json" ] && [ ! -f "$d/.conductor-manifest.json.staging" ]; then
226
+ ok "block: foreign paired marker aborts without data loss"
227
+ else bad "block: foreign paired-marker collision (rc=$rc)"; fi
228
+
229
+ d="$BASE/marker-custom-reinstall"; mkdir -p "$d"
230
+ bash "$T" "$d" --mode=recipes-only --recipes=tdd >/dev/null 2>&1
231
+ /usr/bin/sed -i.bak 's/## Recipe — tdd/## Recipe — tdd (CUSTOMIZED)/' "$d/$BASELINE" && rm -f "$d/$BASELINE.bak"
232
+ before="$(/usr/bin/cksum < "$d/$BASELINE")"
233
+ bash "$T" "$d" --mode=recipes-only --recipes=tdd >/dev/null 2>&1
234
+ rc=$?
235
+ after="$(/usr/bin/cksum < "$d/$BASELINE")"
236
+ if [ "$rc" -eq 1 ] && [ "$before" = "$after" ] && [ ! -f "$d/.conductor-manifest.json.staging" ]; then
237
+ ok "block: customized managed block is not overwritten on re-install"
238
+ else bad "block: customized managed block overwritten (rc=$rc)"; fi
239
+
240
+ d="$BASE/lossless"; mkdir -p "$d"
241
+ printf 'MY EXISTING RULES\n' > "$d/$BASELINE"
242
+ before="$(/usr/bin/cksum < "$d/$BASELINE")"
243
+ for _i in 1 2 3; do bash "$T" "$d" --mode=recipes-only --recipes=tdd >/dev/null 2>&1; done
244
+ bash "$T" "$d" --uninstall >/dev/null 2>&1
245
+ after="$(/usr/bin/cksum < "$d/$BASELINE")"
246
+ [ "$before" = "$after" ] && ok "block: 3x reinstall + uninstall is byte-lossless" || bad "block: reinstall/uninstall not byte-lossless"
247
+
248
+ d="$BASE/crossmode"; mkdir -p "$d"
249
+ printf 'BASE\n' > "$d/$BASELINE"
250
+ bash "$T" "$d" --mode=recipes-only --recipes=tdd >/dev/null 2>&1
251
+ bash "$T" "$d" --mode=reflector-only >/dev/null 2>&1
252
+ bash "$T" "$d" --uninstall >/dev/null 2>&1
253
+ if [ "$(grep -c 'conductor:block' "$d/$BASELINE" 2>/dev/null)" -eq 0 ] && grep -q 'BASE' "$d/$BASELINE"; then
254
+ ok "block: cross-mode (recipes-only → reflector-only) uninstall strips BOTH blocks"
255
+ else bad "block: cross-mode uninstall left an orphaned block"; fi
256
+ fi
257
+
258
+ # ---- customized block survives uninstall (single-file tools) ----------------
259
+ if [ "$TOOL" = "gemini" ] || [ "$TOOL" = "codex" ]; then
260
+ d="$BASE/custom"; mkdir -p "$d"
261
+ echo "BASE" > "$d/$BASELINE"
262
+ bash "$T" "$d" --mode=recipes-only --recipes=tdd >/dev/null 2>&1
263
+ # mutate INSIDE the block
264
+ /usr/bin/sed -i.bak 's/## Recipe — tdd/## Recipe — tdd (CUSTOMIZED)/' "$d/$BASELINE" && rm -f "$d/$BASELINE.bak"
265
+ bash "$T" "$d" --uninstall >/dev/null 2>&1
266
+ if grep -q 'CUSTOMIZED' "$d/$BASELINE"; then
267
+ ok "block: customized block left in place on uninstall"
268
+ else bad "block: customized block was destroyed"; fi
269
+ fi
270
+
271
+ echo
272
+ if [ "$FAIL" -eq 0 ]; then
273
+ echo "OK — all --mode behaviors verified for $TOOL ($BASE)"
274
+ exit 0
275
+ fi
276
+ echo "FAIL — mode behavior broken for $TOOL (workdirs kept at $BASE)"
277
+ exit 1
@@ -54,7 +54,7 @@
54
54
  #
55
55
  # windsurf:
56
56
  # - .windsurfrules exists, non-empty
57
- # - .windsurf/rules/ contains the 5 universal rule files
57
+ # - .devin/rules/ (or legacy .windsurf/rules/) contains the 5 universal rule files
58
58
  # (meta-discipline/operations/quality-gates/spec-as-you-go/workflow)
59
59
  # - no unsubstituted ${...} placeholders (outside code fences)
60
60
  # - no reference-product leakage