omniconductor 0.3.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.
- package/CHANGELOG.md +64 -0
- package/LICENSE +219 -0
- package/NOTICE +11 -0
- package/README.md +526 -0
- package/THIRD_PARTY_NOTICES.md +31 -0
- package/TRADEMARKS.md +26 -0
- package/VISION.md +106 -0
- package/adapters/README.md +121 -0
- package/adapters/claude/README.md +102 -0
- package/adapters/claude/SUPPORTED-FEATURES.md +66 -0
- package/adapters/claude/hookify-templates/.recipe-scoped +9 -0
- package/adapters/claude/hookify-templates/README.md +93 -0
- package/adapters/claude/hookify-templates/block-completion-claim-without-push.local.md.template +56 -0
- package/adapters/claude/hookify-templates/block-direct-push-protected-branch.local.md.template +18 -0
- package/adapters/claude/hookify-templates/block-force-push-protected-branch.local.md.template +42 -0
- package/adapters/claude/hookify-templates/block-server-secret-in-client.local.md.template +36 -0
- package/adapters/claude/hookify-templates/warn-any-type-added.local.md.template +44 -0
- package/adapters/claude/hookify-templates/warn-commit-without-pre-commit-review.local.md.template +52 -0
- package/adapters/claude/hookify-templates/warn-console-direct.local.md.template +55 -0
- package/adapters/claude/hookify-templates/warn-create-table-without-access-control.local.md.template +35 -0
- package/adapters/claude/hookify-templates/warn-current-work-without-remaining-tasks.local.md.template +36 -0
- package/adapters/claude/hookify-templates/warn-gh-pr-create-without-pre-merge-review.local.md.template +38 -0
- package/adapters/claude/hookify-templates/warn-hardcoded-text-without-i18n-key.local.md.template +21 -0
- package/adapters/claude/hookify-templates/warn-on-gh-pr-merge.local.md.template +33 -0
- package/adapters/claude/hookify-templates/warn-plan-spec-without-remaining-tasks.local.md.template +44 -0
- package/adapters/claude/hookify-templates/warn-raw-hex-instead-of-token.local.md.template +21 -0
- package/adapters/claude/hookify-templates/warn-security-definer-without-search-path.local.md.template +30 -0
- package/adapters/claude/hookify-templates/warn-stop-commit-without-current-work.local.md.template +47 -0
- package/adapters/claude/hookify-templates/warn-user-manual-completion.local.md.template +40 -0
- package/adapters/claude/transform-spec.md +140 -0
- package/adapters/claude/transform.sh +1169 -0
- package/adapters/codex/README.md +87 -0
- package/adapters/codex/SUPPORTED-FEATURES.md +66 -0
- package/adapters/codex/transform-spec.md +90 -0
- package/adapters/codex/transform.sh +748 -0
- package/adapters/copilot/README.md +88 -0
- package/adapters/copilot/SUPPORTED-FEATURES.md +73 -0
- package/adapters/copilot/transform-spec.md +111 -0
- package/adapters/copilot/transform.sh +757 -0
- package/adapters/cursor/README.md +87 -0
- package/adapters/cursor/SUPPORTED-FEATURES.md +90 -0
- package/adapters/cursor/transform-spec.md +124 -0
- package/adapters/cursor/transform.sh +731 -0
- package/adapters/gemini/README.md +93 -0
- package/adapters/gemini/SUPPORTED-FEATURES.md +80 -0
- package/adapters/gemini/transform-spec.md +102 -0
- package/adapters/gemini/transform.sh +844 -0
- package/adapters/windsurf/README.md +93 -0
- package/adapters/windsurf/SUPPORTED-FEATURES.md +67 -0
- package/adapters/windsurf/transform-spec.md +99 -0
- package/adapters/windsurf/transform.sh +727 -0
- package/bin/omniconductor.js +130 -0
- package/core/README.md +64 -0
- package/core/anti-patterns/README.md +142 -0
- package/core/anti-patterns/dynamic-system-prompt.md +113 -0
- package/core/anti-patterns/frequent-rule-file-edit.md +83 -0
- package/core/anti-patterns/large-file-read-no-range.md +90 -0
- package/core/anti-patterns/no-sub-agent-dispatch.md +89 -0
- package/core/anti-patterns/single-monolithic-rule-file.md +99 -0
- package/core/anti-patterns/skill-eager-load.md +83 -0
- package/core/anti-patterns/tool-call-spam.md +93 -0
- package/core/docs-templates/CURRENT_WORK.md +64 -0
- package/core/docs-templates/INDEX.md +57 -0
- package/core/docs-templates/PLANS.md +44 -0
- package/core/docs-templates/README.md +40 -0
- package/core/docs-templates/REMAINING_TASKS.md +43 -0
- package/core/docs-templates/TASKS.md +51 -0
- package/core/docs-templates/specs/_example.md +121 -0
- package/core/hooks/README.md +62 -0
- package/core/hooks/pretool-agent-routing.sh.template +97 -0
- package/core/hooks/pretool-commit-current-work-check.sh.template +103 -0
- package/core/hooks/pretool-commit-test-coverage-check.sh.template +116 -0
- package/core/hooks/pretool-large-file-read-guard.sh.template +117 -0
- package/core/hooks/stop-cache-hit-baseline-check.sh.template +133 -0
- package/core/hooks/stop-r6-review-check.sh.template +80 -0
- package/core/hooks/stop-session-log-check.sh.template +101 -0
- package/core/hooks/stop-trajectory-log.sh.template +96 -0
- package/core/memory-pattern/EXAMPLES.md +114 -0
- package/core/memory-pattern/README.md +133 -0
- package/core/recipes/README.md +46 -0
- package/core/recipes/auto-mock-data.md +82 -0
- package/core/recipes/branch-strategy.md +100 -0
- package/core/recipes/coding-conventions.md +123 -0
- package/core/recipes/database-discipline.md +65 -0
- package/core/recipes/debugging.md +143 -0
- package/core/recipes/design-system.md +30 -0
- package/core/recipes/i18n.md +118 -0
- package/core/recipes/monorepo.md +101 -0
- package/core/recipes/self-improvement.md +61 -0
- package/core/recipes/tdd.md +128 -0
- package/core/recipes/web-mobile-parity.md +87 -0
- package/core/reflector/SCHEDULING.md +83 -0
- package/core/reflector/prune-lessons.sh +105 -0
- package/core/reflector/reflect-brief.md +11 -0
- package/core/reflector/reflect.command.md +13 -0
- package/core/reflector/run-weekly.sh +54 -0
- package/core/reflector/trajectory-log.sh +55 -0
- package/core/roles/README.md +61 -0
- package/core/roles/builder.md +75 -0
- package/core/roles/designer.md +93 -0
- package/core/roles/helper.md +70 -0
- package/core/roles/planner.md +123 -0
- package/core/roles/reflector.md +72 -0
- package/core/roles/reviewer.md +104 -0
- package/core/roles/scribe.md +89 -0
- package/core/universal-rules/README.md +71 -0
- package/core/universal-rules/meta-discipline.md +292 -0
- package/core/universal-rules/operations.md +162 -0
- package/core/universal-rules/quality-gates.md +191 -0
- package/core/universal-rules/spec-as-you-go.md +123 -0
- package/core/universal-rules/workflow.md +153 -0
- package/core/workflow/PHASES.md +134 -0
- package/core/workflow/README.md +56 -0
- package/docs/MANUAL-INSTALL.md +456 -0
- package/package.json +52 -0
- package/tools/check-framework-purity.sh +91 -0
- package/tools/measure-tokens.sh +169 -0
- package/tools/validate-adapter-output.sh +611 -0
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# CONDUCTOR — Cursor adapter transform.sh
|
|
4
|
+
#
|
|
5
|
+
# Reads core/ assets and writes them into a target project as native Cursor
|
|
6
|
+
# files: .cursor/rules/*.mdc, optional .cursorrules (legacy bundle), docs/*.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# bash adapters/cursor/transform.sh <target-project> [--recipes=<comma-list>] [--dry-run]
|
|
10
|
+
# [--no-prompt] [--legacy-cursorrules]
|
|
11
|
+
# bash adapters/cursor/transform.sh <target-project> --uninstall [--dry-run] [--force]
|
|
12
|
+
#
|
|
13
|
+
# Examples:
|
|
14
|
+
# bash adapters/cursor/transform.sh ~/Projects/my-app
|
|
15
|
+
# bash adapters/cursor/transform.sh ~/Projects/my-app --recipes=i18n,monorepo
|
|
16
|
+
# bash adapters/cursor/transform.sh /tmp/test-project --dry-run
|
|
17
|
+
# bash adapters/cursor/transform.sh . --no-prompt --legacy-cursorrules
|
|
18
|
+
# bash adapters/cursor/transform.sh . --uninstall # revert install
|
|
19
|
+
# bash adapters/cursor/transform.sh . --uninstall --force # bypass safety checks
|
|
20
|
+
#
|
|
21
|
+
# Layer 2 transformation (per ADR-004 honesty + ADR-021):
|
|
22
|
+
# core/universal-rules/*.md → <target>/.cursor/rules/*.mdc (alwaysApply:true, modern Cursor format)
|
|
23
|
+
# core/recipes/*.md (selected) → <target>/.cursor/rules/*.mdc (path-scoped via globs:)
|
|
24
|
+
# core/docs-templates/*.md → <target>/docs/*.md (CURRENT_WORK, REMAINING_TASKS, etc.)
|
|
25
|
+
# <synthesized> → <target>/.cursorrules (only if --legacy-cursorrules)
|
|
26
|
+
# core/hooks/*.sh.template → SKIPPED (Reflector hook emitted via --recipes=self-improvement, ADR-032; other guards Claude-only, ADR-034)
|
|
27
|
+
# core/roles/*.md → SKIPPED (Cursor has no sub-agent dispatch)
|
|
28
|
+
# adapters/claude/hookify-... → SKIPPED (Claude-only plugin)
|
|
29
|
+
|
|
30
|
+
set -eu
|
|
31
|
+
|
|
32
|
+
# ----- arg parsing --------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
TARGET=""
|
|
35
|
+
RECIPES=""
|
|
36
|
+
DRY_RUN="false"
|
|
37
|
+
NO_PROMPT="false"
|
|
38
|
+
UNINSTALL="false"
|
|
39
|
+
FORCE="false"
|
|
40
|
+
LEGACY_CURSORRULES="false"
|
|
41
|
+
|
|
42
|
+
# Onboarding wizard state
|
|
43
|
+
WIZARD_APPLY_RULES="true"
|
|
44
|
+
|
|
45
|
+
while [ $# -gt 0 ]; do
|
|
46
|
+
case "$1" in
|
|
47
|
+
--recipes=*) RECIPES="${1#--recipes=}" ;;
|
|
48
|
+
--dry-run) DRY_RUN="true" ;;
|
|
49
|
+
--no-prompt) NO_PROMPT="true" ;;
|
|
50
|
+
--uninstall|--rollback) UNINSTALL="true" ;;
|
|
51
|
+
--force) FORCE="true" ;;
|
|
52
|
+
--legacy-cursorrules) LEGACY_CURSORRULES="true" ;;
|
|
53
|
+
--help|-h)
|
|
54
|
+
/bin/cat <<EOF
|
|
55
|
+
Usage: bash adapters/cursor/transform.sh <target-project> [options]
|
|
56
|
+
|
|
57
|
+
Options:
|
|
58
|
+
--recipes=A,B,C Comma-separated list of recipes to install
|
|
59
|
+
--dry-run Preview only — no files written
|
|
60
|
+
--no-prompt Skip all interactive prompts; apply sensible defaults (CI-safe)
|
|
61
|
+
--legacy-cursorrules ALSO emit a flat .cursorrules bundle alongside .cursor/rules/*.mdc
|
|
62
|
+
(for Cursor versions < 0.45 that only read .cursorrules).
|
|
63
|
+
Default off — modern .mdc format is the canonical surface.
|
|
64
|
+
--uninstall Revert a previous install using <target>/.conductor-manifest.json
|
|
65
|
+
(alias: --rollback). Restores backups when present, deletes
|
|
66
|
+
Conductor-emitted files when none. Customizations not in the
|
|
67
|
+
manifest are preserved.
|
|
68
|
+
--force Bypass uninstall safety checks (active worktrees, missing manifest)
|
|
69
|
+
|
|
70
|
+
Recipes available: web-mobile-parity, i18n, monorepo, branch-strategy, auto-mock-data, coding-conventions, tdd, debugging, database-discipline, design-system, self-improvement
|
|
71
|
+
|
|
72
|
+
What this adapter does NOT install (per ADR-004 honesty + ADR-021):
|
|
73
|
+
- Hook guards (CONDUCTOR emits the Reflector hook when --recipes=self-improvement, ADR-032; other guards remain Claude-only, ADR-034)
|
|
74
|
+
- Sub-agent personas (Cursor has no sub-agent dispatch — single chat session per task)
|
|
75
|
+
- Hookify rule templates (Claude-only plugin)
|
|
76
|
+
EOF
|
|
77
|
+
exit 0
|
|
78
|
+
;;
|
|
79
|
+
*)
|
|
80
|
+
if [ -z "$TARGET" ]; then
|
|
81
|
+
TARGET="$1"
|
|
82
|
+
else
|
|
83
|
+
echo "Unknown argument: $1" >&2
|
|
84
|
+
exit 1
|
|
85
|
+
fi
|
|
86
|
+
;;
|
|
87
|
+
esac
|
|
88
|
+
shift
|
|
89
|
+
done
|
|
90
|
+
|
|
91
|
+
if [ -z "$TARGET" ]; then
|
|
92
|
+
echo "Error: target-project path is required." >&2
|
|
93
|
+
echo "Usage: bash adapters/cursor/transform.sh <target-project> [--recipes=...]" >&2
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# Resolve CONDUCTOR root (where this script lives: adapters/cursor/).
|
|
98
|
+
CONDUCTOR_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
99
|
+
CORE_ROOT="$CONDUCTOR_ROOT/core"
|
|
100
|
+
[ -d "$CORE_ROOT" ] || { echo "Error: core/ not found at $CORE_ROOT" >&2; exit 1; }
|
|
101
|
+
|
|
102
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
103
|
+
mkdir -p "$TARGET"
|
|
104
|
+
fi
|
|
105
|
+
TARGET_ABS="$(cd "$TARGET" 2>/dev/null && pwd)" || { echo "Error: target directory does not exist or is not a directory: $TARGET" >&2; exit 1; }
|
|
106
|
+
|
|
107
|
+
# ----- helpers ------------------------------------------------------------
|
|
108
|
+
|
|
109
|
+
log() {
|
|
110
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
111
|
+
echo "[dry-run] $*"
|
|
112
|
+
else
|
|
113
|
+
echo "[conductor] $*"
|
|
114
|
+
fi
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
mkdir_if_real() {
|
|
118
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
119
|
+
log "would mkdir -p $1"
|
|
120
|
+
else
|
|
121
|
+
mkdir -p "$1"
|
|
122
|
+
fi
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
# Strip the CONDUCTOR universal frontmatter (first --- ... --- block) from src body.
|
|
126
|
+
# Print body to stdout. Used by .mdc emit + legacy .cursorrules bundling.
|
|
127
|
+
strip_frontmatter() {
|
|
128
|
+
local src="$1"
|
|
129
|
+
/usr/bin/awk 'BEGIN{f=0} /^---$/{c++; if(c==2){f=1; next}} f==1' "$src"
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# Derive a `description:` value from a markdown file. First H1 line, or filename.
|
|
133
|
+
derive_description() {
|
|
134
|
+
local src="$1"
|
|
135
|
+
local desc
|
|
136
|
+
desc="$(strip_frontmatter "$src" | /usr/bin/grep -m1 '^# ' | /usr/bin/sed -e 's/^# *//' -e 's/"/\\"/g' | /usr/bin/head -c 120)"
|
|
137
|
+
if [ -z "$desc" ]; then
|
|
138
|
+
desc="$(basename "$src" .md)"
|
|
139
|
+
fi
|
|
140
|
+
printf '%s' "$desc"
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
# Derive `globs:` from a recipe's filename. Universal rules use ["**"].
|
|
144
|
+
# This is best-effort — the spec keeps it simple: each recipe maps to a sensible glob default.
|
|
145
|
+
# Adopter is expected to tighten globs after install if they want stricter scoping.
|
|
146
|
+
derive_globs_for_recipe() {
|
|
147
|
+
local recipe_id="$1"
|
|
148
|
+
case "$recipe_id" in
|
|
149
|
+
monorepo) echo '"apps/**", "packages/**"' ;;
|
|
150
|
+
web-mobile-parity) echo '"apps/web/**", "apps/mobile/**", "packages/shared/**"' ;;
|
|
151
|
+
i18n) echo '"**/i18n/**", "**/translations.ts", "**/locales/**"' ;;
|
|
152
|
+
branch-strategy) echo '"**"' ;;
|
|
153
|
+
auto-mock-data) echo '"**/*.sql", "**/migrations/**", "**/seeds/**"' ;;
|
|
154
|
+
coding-conventions) echo '"**/*.ts", "**/*.tsx"' ;;
|
|
155
|
+
database-discipline) echo '"**/*.sql", "**/migrations/**"' ;;
|
|
156
|
+
design-system) echo '"**/*.tsx", "**/*.css", "**/*.scss"' ;;
|
|
157
|
+
tdd) echo '"**/*.test.*", "**/*.spec.*", "**/__tests__/**", "**/e2e/**"' ;;
|
|
158
|
+
*) echo '"**"' ;;
|
|
159
|
+
esac
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
# Emit a `.mdc` file from a `core/*.md` source.
|
|
163
|
+
# emit_mdc <src> <dest> <description> <globs-yaml-array> <alwaysApply-bool>
|
|
164
|
+
emit_mdc() {
|
|
165
|
+
local src="$1" dest="$2" desc="$3" globs="$4" always="$5"
|
|
166
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
167
|
+
log "would write $dest (alwaysApply=$always, globs=$globs)"
|
|
168
|
+
return
|
|
169
|
+
fi
|
|
170
|
+
/bin/cat > "$dest" <<EOF
|
|
171
|
+
---
|
|
172
|
+
description: "$desc"
|
|
173
|
+
globs: [$globs]
|
|
174
|
+
alwaysApply: $always
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
EOF
|
|
178
|
+
strip_frontmatter "$src" >> "$dest"
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# backup_if_exists <dest>
|
|
182
|
+
# If <dest> is a regular file, copy it to <dest>.conductor-backup-<timestamp> before any
|
|
183
|
+
# downstream step overwrites it. Honors DRY_RUN. Idempotent across re-installs.
|
|
184
|
+
# Origin: ADR-019 (Claude adapter pattern, mirrored here per ADR-021).
|
|
185
|
+
backup_if_exists() {
|
|
186
|
+
local dest="$1"
|
|
187
|
+
if [ -f "$dest" ]; then
|
|
188
|
+
local ts
|
|
189
|
+
ts="$(/bin/date +%Y%m%d-%H%M%S)"
|
|
190
|
+
local backup="${dest}.conductor-backup-${ts}"
|
|
191
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
192
|
+
log "would back up existing $dest -> $backup"
|
|
193
|
+
else
|
|
194
|
+
/bin/cp "$dest" "$backup"
|
|
195
|
+
log " backed up existing $dest -> $backup"
|
|
196
|
+
fi
|
|
197
|
+
fi
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
# ----- manifest tracking (ADR-020, mirrored per ADR-021) ------------------
|
|
201
|
+
#
|
|
202
|
+
# Format identical to Claude adapter's manifest. POSIX shell + sed only — no jq.
|
|
203
|
+
|
|
204
|
+
MANIFEST_PATH="$TARGET_ABS/.conductor-manifest.json"
|
|
205
|
+
MANIFEST_STAGE_PATH=""
|
|
206
|
+
MANIFEST_TS=""
|
|
207
|
+
MANIFEST_LAST_BACKUP=""
|
|
208
|
+
|
|
209
|
+
init_manifest() {
|
|
210
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
211
|
+
log "would init manifest staging at $MANIFEST_PATH.staging"
|
|
212
|
+
return
|
|
213
|
+
fi
|
|
214
|
+
MANIFEST_TS="$(/bin/date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
215
|
+
MANIFEST_STAGE_PATH="$TARGET_ABS/.conductor-manifest.json.staging"
|
|
216
|
+
/bin/rm -f "$MANIFEST_STAGE_PATH"
|
|
217
|
+
: > "$MANIFEST_STAGE_PATH"
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
record_emit() {
|
|
221
|
+
if [ "$DRY_RUN" = "true" ] || [ "$UNINSTALL" = "true" ]; then
|
|
222
|
+
return
|
|
223
|
+
fi
|
|
224
|
+
local relpath="$1" src="$2" backup="${3:-}"
|
|
225
|
+
local had_backup="false"
|
|
226
|
+
[ -n "$backup" ] && had_backup="true"
|
|
227
|
+
local esc_path esc_src esc_backup
|
|
228
|
+
esc_path="$(printf '%s' "$relpath" | /usr/bin/sed 's/\\/\\\\/g; s/"/\\"/g')"
|
|
229
|
+
esc_src="$(printf '%s' "$src" | /usr/bin/sed 's/\\/\\\\/g; s/"/\\"/g')"
|
|
230
|
+
esc_backup="$(printf '%s' "$backup" | /usr/bin/sed 's/\\/\\\\/g; s/"/\\"/g')"
|
|
231
|
+
printf ' {"path": "%s", "source": "%s", "had_backup": %s, "backup_path": "%s"},\n' \
|
|
232
|
+
"$esc_path" "$esc_src" "$had_backup" "$esc_backup" >> "$MANIFEST_STAGE_PATH"
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
finalize_manifest() {
|
|
236
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
237
|
+
log "would finalize manifest -> $MANIFEST_PATH"
|
|
238
|
+
return
|
|
239
|
+
fi
|
|
240
|
+
[ -z "$MANIFEST_STAGE_PATH" ] && return
|
|
241
|
+
[ -f "$MANIFEST_STAGE_PATH" ] || return
|
|
242
|
+
|
|
243
|
+
if [ -f "$MANIFEST_PATH" ]; then
|
|
244
|
+
backup_if_exists "$MANIFEST_PATH"
|
|
245
|
+
fi
|
|
246
|
+
|
|
247
|
+
local recipes_json="[]"
|
|
248
|
+
if [ -n "$RECIPES" ]; then
|
|
249
|
+
recipes_json="["
|
|
250
|
+
local IFS_BAK=$IFS
|
|
251
|
+
IFS=','
|
|
252
|
+
local first="true"
|
|
253
|
+
for r in $RECIPES; do
|
|
254
|
+
r="$(printf '%s' "$r" | /usr/bin/sed 's/^ *//; s/ *$//')"
|
|
255
|
+
[ -z "$r" ] && continue
|
|
256
|
+
if [ "$first" = "true" ]; then
|
|
257
|
+
recipes_json="${recipes_json}\"$r\""
|
|
258
|
+
first="false"
|
|
259
|
+
else
|
|
260
|
+
recipes_json="${recipes_json}, \"$r\""
|
|
261
|
+
fi
|
|
262
|
+
done
|
|
263
|
+
IFS=$IFS_BAK
|
|
264
|
+
recipes_json="${recipes_json}]"
|
|
265
|
+
fi
|
|
266
|
+
|
|
267
|
+
local entries
|
|
268
|
+
if [ -s "$MANIFEST_STAGE_PATH" ]; then
|
|
269
|
+
entries="$(/usr/bin/sed -e '$ s/,$//' "$MANIFEST_STAGE_PATH")"
|
|
270
|
+
else
|
|
271
|
+
entries=""
|
|
272
|
+
fi
|
|
273
|
+
|
|
274
|
+
/bin/cat > "$MANIFEST_PATH" <<EOF
|
|
275
|
+
{
|
|
276
|
+
"version": "v0.2.0",
|
|
277
|
+
"adapter": "cursor",
|
|
278
|
+
"install_timestamp": "$MANIFEST_TS",
|
|
279
|
+
"conductor_root": "$CONDUCTOR_ROOT",
|
|
280
|
+
"recipes_enabled": $recipes_json,
|
|
281
|
+
"legacy_cursorrules": $LEGACY_CURSORRULES,
|
|
282
|
+
"emitted_files": [
|
|
283
|
+
$entries
|
|
284
|
+
]
|
|
285
|
+
}
|
|
286
|
+
EOF
|
|
287
|
+
/bin/rm -f "$MANIFEST_STAGE_PATH"
|
|
288
|
+
log " wrote manifest $MANIFEST_PATH"
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
backup_and_remember() {
|
|
292
|
+
MANIFEST_LAST_BACKUP=""
|
|
293
|
+
if [ -f "$1" ]; then
|
|
294
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
295
|
+
log "would back up existing $1 -> $1.conductor-backup-<ts>"
|
|
296
|
+
MANIFEST_LAST_BACKUP=""
|
|
297
|
+
else
|
|
298
|
+
local ts
|
|
299
|
+
ts="$(/bin/date +%Y%m%d-%H%M%S)"
|
|
300
|
+
local backup="$1.conductor-backup-$ts"
|
|
301
|
+
/bin/cp "$1" "$backup"
|
|
302
|
+
log " backed up existing $1 -> $backup"
|
|
303
|
+
MANIFEST_LAST_BACKUP="${backup#$TARGET_ABS/}"
|
|
304
|
+
fi
|
|
305
|
+
fi
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
# ----- uninstall flow (mirrored from Claude adapter) ----------------------
|
|
309
|
+
|
|
310
|
+
do_uninstall() {
|
|
311
|
+
log "uninstall mode (target: $TARGET_ABS)"
|
|
312
|
+
|
|
313
|
+
if [ ! -f "$MANIFEST_PATH" ]; then
|
|
314
|
+
if [ "$FORCE" = "true" ]; then
|
|
315
|
+
log "WARNING: no manifest at $MANIFEST_PATH — proceeding under --force (legacy backup scan only)"
|
|
316
|
+
uninstall_legacy_scan
|
|
317
|
+
return 0
|
|
318
|
+
fi
|
|
319
|
+
echo "Error: no manifest at $MANIFEST_PATH." >&2
|
|
320
|
+
echo " This target was either installed by a pre-manifest version or has already been uninstalled." >&2
|
|
321
|
+
echo " Re-run with --force to scan for legacy .conductor-backup-* files and delete them anyway:" >&2
|
|
322
|
+
echo " bash $0 $TARGET_ABS --uninstall --force" >&2
|
|
323
|
+
exit 1
|
|
324
|
+
fi
|
|
325
|
+
|
|
326
|
+
if [ -d "$TARGET_ABS/.git" ]; then
|
|
327
|
+
if [ -f "$TARGET_ABS/.git/MERGE_HEAD" ] || [ -f "$TARGET_ABS/.git/REBASE_HEAD" ] || [ -d "$TARGET_ABS/.git/rebase-merge" ]; then
|
|
328
|
+
if [ "$FORCE" != "true" ]; then
|
|
329
|
+
echo "Error: target has an active git operation (merge/rebase in progress)." >&2
|
|
330
|
+
echo " Resolve the in-flight operation first, or pass --force to override." >&2
|
|
331
|
+
exit 1
|
|
332
|
+
fi
|
|
333
|
+
log "WARNING: active git operation detected — proceeding under --force"
|
|
334
|
+
fi
|
|
335
|
+
fi
|
|
336
|
+
|
|
337
|
+
log "loading manifest entries..."
|
|
338
|
+
local entries_count=0
|
|
339
|
+
local restored=0
|
|
340
|
+
local deleted=0
|
|
341
|
+
local missing=0
|
|
342
|
+
|
|
343
|
+
while IFS= read -r line; do
|
|
344
|
+
case "$line" in
|
|
345
|
+
*'"path":'*'"source":'*'"had_backup":'*)
|
|
346
|
+
;;
|
|
347
|
+
*) continue ;;
|
|
348
|
+
esac
|
|
349
|
+
entries_count=$((entries_count + 1))
|
|
350
|
+
local rel_path src had_backup backup_path
|
|
351
|
+
rel_path="$(printf '%s' "$line" | /usr/bin/sed -E 's/.*"path": *"([^"]*)".*/\1/')"
|
|
352
|
+
src="$(printf '%s' "$line" | /usr/bin/sed -E 's/.*"source": *"([^"]*)".*/\1/')"
|
|
353
|
+
had_backup="$(printf '%s' "$line" | /usr/bin/sed -E 's/.*"had_backup": *(true|false).*/\1/')"
|
|
354
|
+
backup_path="$(printf '%s' "$line" | /usr/bin/sed -E 's/.*"backup_path": *"([^"]*)".*/\1/')"
|
|
355
|
+
|
|
356
|
+
local abs_dest="$TARGET_ABS/$rel_path"
|
|
357
|
+
local abs_backup=""
|
|
358
|
+
[ -n "$backup_path" ] && abs_backup="$TARGET_ABS/$backup_path"
|
|
359
|
+
|
|
360
|
+
if [ "$had_backup" = "true" ] && [ -n "$abs_backup" ]; then
|
|
361
|
+
if [ -f "$abs_backup" ]; then
|
|
362
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
363
|
+
log " would restore $abs_backup -> $abs_dest"
|
|
364
|
+
else
|
|
365
|
+
/bin/mv -f "$abs_backup" "$abs_dest"
|
|
366
|
+
log " restored $abs_backup -> $abs_dest"
|
|
367
|
+
fi
|
|
368
|
+
restored=$((restored + 1))
|
|
369
|
+
else
|
|
370
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
371
|
+
log " would delete $abs_dest (backup $abs_backup missing)"
|
|
372
|
+
else
|
|
373
|
+
/bin/rm -f "$abs_dest"
|
|
374
|
+
log " deleted $abs_dest (backup $abs_backup missing)"
|
|
375
|
+
fi
|
|
376
|
+
missing=$((missing + 1))
|
|
377
|
+
fi
|
|
378
|
+
else
|
|
379
|
+
if [ -f "$abs_dest" ]; then
|
|
380
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
381
|
+
log " would delete $abs_dest"
|
|
382
|
+
else
|
|
383
|
+
/bin/rm -f "$abs_dest"
|
|
384
|
+
log " deleted $abs_dest"
|
|
385
|
+
fi
|
|
386
|
+
deleted=$((deleted + 1))
|
|
387
|
+
else
|
|
388
|
+
log " skip $abs_dest (already absent)"
|
|
389
|
+
fi
|
|
390
|
+
fi
|
|
391
|
+
done < "$MANIFEST_PATH"
|
|
392
|
+
|
|
393
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
394
|
+
log " would delete $MANIFEST_PATH"
|
|
395
|
+
for mb in "$MANIFEST_PATH".conductor-backup-*; do
|
|
396
|
+
[ -e "$mb" ] && log " would delete $mb"
|
|
397
|
+
done
|
|
398
|
+
else
|
|
399
|
+
/bin/rm -f "$MANIFEST_PATH"
|
|
400
|
+
for mb in "$MANIFEST_PATH".conductor-backup-*; do
|
|
401
|
+
[ -e "$mb" ] && /bin/rm -f "$mb"
|
|
402
|
+
done
|
|
403
|
+
log " deleted $MANIFEST_PATH"
|
|
404
|
+
fi
|
|
405
|
+
|
|
406
|
+
# Try to clean up empty .cursor/rules and .cursor dirs left behind.
|
|
407
|
+
# (children before parents so nested empties collapse in one pass)
|
|
408
|
+
for d in .cursor/rules .cursor/skills/reflect .cursor/skills .cursor/agents .cursor .conductor/reflect .conductor; do
|
|
409
|
+
local abs_d="$TARGET_ABS/$d"
|
|
410
|
+
if [ -d "$abs_d" ]; then
|
|
411
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
412
|
+
if [ -z "$(/bin/ls -A "$abs_d" 2>/dev/null)" ]; then
|
|
413
|
+
log " would rmdir empty $abs_d"
|
|
414
|
+
fi
|
|
415
|
+
else
|
|
416
|
+
/bin/rmdir "$abs_d" 2>/dev/null && log " rmdir empty $abs_d" || true
|
|
417
|
+
fi
|
|
418
|
+
fi
|
|
419
|
+
done
|
|
420
|
+
|
|
421
|
+
echo ""
|
|
422
|
+
echo "========================================================"
|
|
423
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
424
|
+
echo " Uninstall preview (dry-run)"
|
|
425
|
+
else
|
|
426
|
+
echo " Uninstall complete"
|
|
427
|
+
fi
|
|
428
|
+
echo " Target: $TARGET_ABS"
|
|
429
|
+
echo " Entries processed: $entries_count"
|
|
430
|
+
echo " Backups restored: $restored"
|
|
431
|
+
echo " Files deleted: $deleted"
|
|
432
|
+
echo " Backup-missing deletes: $missing"
|
|
433
|
+
echo "========================================================"
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
uninstall_legacy_scan() {
|
|
437
|
+
log "legacy scan mode — searching for .conductor-backup-* files under $TARGET_ABS"
|
|
438
|
+
local found=0
|
|
439
|
+
while IFS= read -r f; do
|
|
440
|
+
found=$((found + 1))
|
|
441
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
442
|
+
log " would delete legacy backup $f"
|
|
443
|
+
else
|
|
444
|
+
/bin/rm -f "$f"
|
|
445
|
+
log " deleted legacy backup $f"
|
|
446
|
+
fi
|
|
447
|
+
done < <(/usr/bin/find "$TARGET_ABS" -type f -name '*.conductor-backup-*' 2>/dev/null)
|
|
448
|
+
log "legacy scan: $found backup file(s)"
|
|
449
|
+
log "WARNING: legacy mode does not delete Conductor-emitted source files (no manifest)."
|
|
450
|
+
log " Delete .cursor/rules/{workflow,spec-as-you-go,quality-gates,operations,meta-discipline}.mdc manually if desired."
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if [ "$UNINSTALL" = "true" ]; then
|
|
454
|
+
do_uninstall
|
|
455
|
+
exit 0
|
|
456
|
+
fi
|
|
457
|
+
|
|
458
|
+
# ----- onboarding wizard --------------------------------------------------
|
|
459
|
+
# Wizard fires when adopter signal is detected: existing .cursor/ OR existing .cursorrules.
|
|
460
|
+
# Otherwise (truly fresh target) wizard is skipped.
|
|
461
|
+
|
|
462
|
+
IS_ADOPTER_CASE="false"
|
|
463
|
+
if [ -d "$TARGET_ABS/.cursor" ] || [ -f "$TARGET_ABS/.cursorrules" ]; then
|
|
464
|
+
IS_ADOPTER_CASE="true"
|
|
465
|
+
fi
|
|
466
|
+
|
|
467
|
+
if [ "$IS_ADOPTER_CASE" = "true" ] && [ "$NO_PROMPT" = "false" ] && [ "$DRY_RUN" = "false" ]; then
|
|
468
|
+
echo ""
|
|
469
|
+
echo "========================================================"
|
|
470
|
+
echo " Welcome to CONDUCTOR setup (Cursor adapter)"
|
|
471
|
+
echo " Target: $TARGET_ABS"
|
|
472
|
+
echo "========================================================"
|
|
473
|
+
echo ""
|
|
474
|
+
|
|
475
|
+
printf "Detect existing rules? (y/N): "
|
|
476
|
+
read -r _detect_answer
|
|
477
|
+
if [ "$_detect_answer" = "y" ] || [ "$_detect_answer" = "Y" ]; then
|
|
478
|
+
_existing_rules=$(ls "$TARGET_ABS/.cursor/rules/" 2>/dev/null | wc -l | /usr/bin/tr -d ' ')
|
|
479
|
+
_has_legacy="no"
|
|
480
|
+
[ -f "$TARGET_ABS/.cursorrules" ] && _has_legacy="yes"
|
|
481
|
+
echo " Found $_existing_rules .mdc rule files in .cursor/rules/, .cursorrules present: $_has_legacy"
|
|
482
|
+
fi
|
|
483
|
+
|
|
484
|
+
printf "Apply universal-rules? (Y/n): "
|
|
485
|
+
read -r _apply_answer
|
|
486
|
+
if [ "$_apply_answer" = "n" ] || [ "$_apply_answer" = "N" ]; then
|
|
487
|
+
WIZARD_APPLY_RULES="false"
|
|
488
|
+
echo " Skipping universal-rules installation."
|
|
489
|
+
fi
|
|
490
|
+
|
|
491
|
+
echo ""
|
|
492
|
+
echo "Available recipes:"
|
|
493
|
+
echo " web-mobile-parity, i18n, monorepo, branch-strategy, auto-mock-data, coding-conventions, tdd, debugging, database-discipline, design-system, self-improvement"
|
|
494
|
+
printf "Select recipes (comma-separated, or leave blank for none): "
|
|
495
|
+
read -r _recipe_answer
|
|
496
|
+
if [ -n "$_recipe_answer" ]; then
|
|
497
|
+
RECIPES="$_recipe_answer"
|
|
498
|
+
echo " Recipes selected: $RECIPES"
|
|
499
|
+
else
|
|
500
|
+
echo " No recipes selected."
|
|
501
|
+
fi
|
|
502
|
+
|
|
503
|
+
printf "Also emit legacy .cursorrules bundle? (for Cursor < 0.45) (y/N): "
|
|
504
|
+
read -r _legacy_answer
|
|
505
|
+
if [ "$_legacy_answer" = "y" ] || [ "$_legacy_answer" = "Y" ]; then
|
|
506
|
+
LEGACY_CURSORRULES="true"
|
|
507
|
+
echo " Will emit .cursorrules bundle."
|
|
508
|
+
fi
|
|
509
|
+
|
|
510
|
+
echo ""
|
|
511
|
+
elif [ "$IS_ADOPTER_CASE" = "true" ] && [ "$NO_PROMPT" = "true" ]; then
|
|
512
|
+
log "Adopter case detected — applying defaults (--no-prompt): rules=yes, recipes=${RECIPES:-(none)}, legacy_cursorrules=$LEGACY_CURSORRULES"
|
|
513
|
+
fi
|
|
514
|
+
|
|
515
|
+
# ----- step 1: universal rules -> .cursor/rules/*.mdc --------------------
|
|
516
|
+
|
|
517
|
+
init_manifest
|
|
518
|
+
|
|
519
|
+
UNIVERSAL_RULES="workflow spec-as-you-go quality-gates operations meta-discipline"
|
|
520
|
+
|
|
521
|
+
if [ "$WIZARD_APPLY_RULES" = "true" ]; then
|
|
522
|
+
log "Step 1/4: universal-rules → .cursor/rules/"
|
|
523
|
+
mkdir_if_real "$TARGET_ABS/.cursor/rules"
|
|
524
|
+
|
|
525
|
+
for rule in $UNIVERSAL_RULES; do
|
|
526
|
+
src="$CORE_ROOT/universal-rules/$rule.md"
|
|
527
|
+
dest="$TARGET_ABS/.cursor/rules/$rule.mdc"
|
|
528
|
+
if [ ! -f "$src" ]; then
|
|
529
|
+
echo "Warning: $src not found; skipping" >&2
|
|
530
|
+
continue
|
|
531
|
+
fi
|
|
532
|
+
backup_and_remember "$dest"
|
|
533
|
+
desc="$(derive_description "$src")"
|
|
534
|
+
# All universal rules are always-loaded per core/universal-rules/README.md → alwaysApply: true
|
|
535
|
+
emit_mdc "$src" "$dest" "$desc" '"**"' "true"
|
|
536
|
+
record_emit ".cursor/rules/$rule.mdc" "core/universal-rules/$rule.md" "$MANIFEST_LAST_BACKUP"
|
|
537
|
+
done
|
|
538
|
+
else
|
|
539
|
+
log "Step 1/4: universal-rules — skipped (user opted out)"
|
|
540
|
+
fi
|
|
541
|
+
|
|
542
|
+
# ----- step 2: recipes (opt-in) -> .cursor/rules/*.mdc -------------------
|
|
543
|
+
|
|
544
|
+
log "Step 2/4: recipes (opt-in) → .cursor/rules/"
|
|
545
|
+
INSTALLED_RECIPES=""
|
|
546
|
+
if [ -n "$RECIPES" ]; then
|
|
547
|
+
mkdir_if_real "$TARGET_ABS/.cursor/rules"
|
|
548
|
+
IFS=',' read -ra RECIPE_LIST <<< "$RECIPES"
|
|
549
|
+
for r in "${RECIPE_LIST[@]}"; do
|
|
550
|
+
r="$(printf '%s' "$r" | /usr/bin/sed 's/^ *//; s/ *$//')"
|
|
551
|
+
[ -z "$r" ] && continue
|
|
552
|
+
src="$CORE_ROOT/recipes/$r.md"
|
|
553
|
+
dest="$TARGET_ABS/.cursor/rules/$r.mdc"
|
|
554
|
+
if [ ! -f "$src" ]; then
|
|
555
|
+
echo "Warning: recipe '$r' not found at $src; skipping" >&2
|
|
556
|
+
continue
|
|
557
|
+
fi
|
|
558
|
+
backup_and_remember "$dest"
|
|
559
|
+
desc="$(derive_description "$src")"
|
|
560
|
+
globs="$(derive_globs_for_recipe "$r")"
|
|
561
|
+
# Recipes are path-scoped (alwaysApply: false) — Cursor lazy-loads when a matching path is touched.
|
|
562
|
+
emit_mdc "$src" "$dest" "$desc" "$globs" "false"
|
|
563
|
+
record_emit ".cursor/rules/$r.mdc" "core/recipes/$r.md" "$MANIFEST_LAST_BACKUP"
|
|
564
|
+
INSTALLED_RECIPES="$INSTALLED_RECIPES $r"
|
|
565
|
+
done
|
|
566
|
+
else
|
|
567
|
+
log " (no recipes selected — pass --recipes=name1,name2 to install)"
|
|
568
|
+
fi
|
|
569
|
+
|
|
570
|
+
# ----- step 2.5: optional legacy .cursorrules bundle ---------------------
|
|
571
|
+
#
|
|
572
|
+
# When --legacy-cursorrules is set, also emit a flat .cursorrules file at the project root that
|
|
573
|
+
# concatenates the universal-rules body + selected recipes. This is for Cursor versions < 0.45
|
|
574
|
+
# that don't yet read .cursor/rules/*.mdc. Modern Cursor reads BOTH (per ADR-021) — adopters can
|
|
575
|
+
# safely keep both surfaces during a transition.
|
|
576
|
+
|
|
577
|
+
if [ "$LEGACY_CURSORRULES" = "true" ]; then
|
|
578
|
+
log "Step 2.5/4: legacy bundle → .cursorrules"
|
|
579
|
+
CURSORRULES_DEST="$TARGET_ABS/.cursorrules"
|
|
580
|
+
backup_and_remember "$CURSORRULES_DEST"
|
|
581
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
582
|
+
log "would write $CURSORRULES_DEST (universal + recipes bundle)"
|
|
583
|
+
else
|
|
584
|
+
{
|
|
585
|
+
echo "# CONDUCTOR — .cursorrules (legacy bundle)"
|
|
586
|
+
echo ""
|
|
587
|
+
echo "> Auto-generated by adapters/cursor/transform.sh with --legacy-cursorrules."
|
|
588
|
+
echo "> Modern Cursor (>= 0.45) reads .cursor/rules/*.mdc. This file is for older versions."
|
|
589
|
+
echo "> Edit .cursor/rules/*.mdc as the source of truth; re-run the adapter to regenerate."
|
|
590
|
+
echo ""
|
|
591
|
+
if [ "$WIZARD_APPLY_RULES" = "true" ]; then
|
|
592
|
+
for rule in $UNIVERSAL_RULES; do
|
|
593
|
+
src="$CORE_ROOT/universal-rules/$rule.md"
|
|
594
|
+
[ -f "$src" ] || continue
|
|
595
|
+
echo "## Universal rule: $rule"
|
|
596
|
+
echo ""
|
|
597
|
+
strip_frontmatter "$src"
|
|
598
|
+
echo ""
|
|
599
|
+
done
|
|
600
|
+
fi
|
|
601
|
+
if [ -n "$INSTALLED_RECIPES" ]; then
|
|
602
|
+
for r in $INSTALLED_RECIPES; do
|
|
603
|
+
src="$CORE_ROOT/recipes/$r.md"
|
|
604
|
+
[ -f "$src" ] || continue
|
|
605
|
+
echo "## Recipe: $r"
|
|
606
|
+
echo ""
|
|
607
|
+
strip_frontmatter "$src"
|
|
608
|
+
echo ""
|
|
609
|
+
done
|
|
610
|
+
fi
|
|
611
|
+
} > "$CURSORRULES_DEST"
|
|
612
|
+
record_emit ".cursorrules" "<synthesized>" "$MANIFEST_LAST_BACKUP"
|
|
613
|
+
log " wrote $CURSORRULES_DEST"
|
|
614
|
+
fi
|
|
615
|
+
fi
|
|
616
|
+
|
|
617
|
+
# ---- Step 2.6: self-improvement runtime (only with --recipes=self-improvement) ----
|
|
618
|
+
case ",$RECIPES," in
|
|
619
|
+
*",self-improvement,"*)
|
|
620
|
+
log "Step 2.6/4: self-improvement (Reflector) → hooks/skills/agents"
|
|
621
|
+
if [ "$DRY_RUN" != "true" ]; then
|
|
622
|
+
/bin/mkdir -p "$TARGET_ABS/.conductor/reflect" "$TARGET_ABS/.cursor/skills/reflect" "$TARGET_ABS/.cursor/agents"
|
|
623
|
+
gi="$TARGET_ABS/.gitignore"
|
|
624
|
+
grep -qxF '.conductor/' "$gi" 2>/dev/null || printf '\n# CONDUCTOR runtime (local trajectories/lessons)\n.conductor/\n' >> "$gi"
|
|
625
|
+
# portable scripts
|
|
626
|
+
for s in trajectory-log prune-lessons run-weekly; do
|
|
627
|
+
d="$TARGET_ABS/.conductor/reflect/$s.sh"
|
|
628
|
+
backup_and_remember "$d"; /bin/cp "$CORE_ROOT/reflector/$s.sh" "$d"; /bin/chmod +x "$d"
|
|
629
|
+
record_emit ".conductor/reflect/$s.sh" "core/reflector/$s.sh" "$MANIFEST_LAST_BACKUP"
|
|
630
|
+
done
|
|
631
|
+
# scheduling assets: run-weekly.sh needs the brief; SCHEDULING.md documents registration
|
|
632
|
+
for m in reflect-brief SCHEDULING; do
|
|
633
|
+
d="$TARGET_ABS/.conductor/reflect/$m.md"
|
|
634
|
+
backup_and_remember "$d"; /bin/cp "$CORE_ROOT/reflector/$m.md" "$d"
|
|
635
|
+
record_emit ".conductor/reflect/$m.md" "core/reflector/$m.md" "$MANIFEST_LAST_BACKUP"
|
|
636
|
+
done
|
|
637
|
+
# hook config — write only if absent (never clobber a user's hooks.json)
|
|
638
|
+
hc="$TARGET_ABS/.cursor/hooks.json"
|
|
639
|
+
if [ ! -f "$hc" ]; then
|
|
640
|
+
backup_and_remember "$hc"
|
|
641
|
+
/bin/cat > "$hc" <<'HOOK'
|
|
642
|
+
{
|
|
643
|
+
"version": 1,
|
|
644
|
+
"hooks": {
|
|
645
|
+
"stop": [ { "command": "./.conductor/reflect/trajectory-log.sh" } ]
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
HOOK
|
|
649
|
+
record_emit ".cursor/hooks.json" "<synthesized>" "$MANIFEST_LAST_BACKUP"
|
|
650
|
+
else
|
|
651
|
+
log " .cursor/hooks.json exists — add a stop entry calling ./.conductor/reflect/trajectory-log.sh manually"
|
|
652
|
+
fi
|
|
653
|
+
# /reflect skill (self-contained brief)
|
|
654
|
+
sk="$TARGET_ABS/.cursor/skills/reflect/SKILL.md"
|
|
655
|
+
backup_and_remember "$sk"
|
|
656
|
+
{ printf -- '---\nname: reflect\ndescription: Run the CONDUCTOR Reflector — propose lessons from recent sessions (propose-only).\ndisable-model-invocation: true\n---\n\n'; /bin/cat "$CORE_ROOT/reflector/reflect-brief.md"; } > "$sk"
|
|
657
|
+
record_emit ".cursor/skills/reflect/SKILL.md" "core/reflector/reflect-brief.md" "$MANIFEST_LAST_BACKUP"
|
|
658
|
+
# reflector agent (native persona) — strip core frontmatter, add Cursor frontmatter
|
|
659
|
+
ag="$TARGET_ABS/.cursor/agents/reflector.md"
|
|
660
|
+
backup_and_remember "$ag"
|
|
661
|
+
{ printf -- '---\nname: reflector\ndescription: Reads session trajectories and proposes atomic lesson deltas. Propose-only; never applies.\nmodel: inherit\nreadonly: true\n---\n\n'; strip_frontmatter "$CORE_ROOT/roles/reflector.md"; } > "$ag"
|
|
662
|
+
record_emit ".cursor/agents/reflector.md" "core/roles/reflector.md" "$MANIFEST_LAST_BACKUP"
|
|
663
|
+
fi
|
|
664
|
+
;;
|
|
665
|
+
esac
|
|
666
|
+
|
|
667
|
+
# ----- step 3: docs templates --------------------------------------------
|
|
668
|
+
|
|
669
|
+
log "Step 3/4: docs templates → docs/"
|
|
670
|
+
mkdir_if_real "$TARGET_ABS/docs"
|
|
671
|
+
mkdir_if_real "$TARGET_ABS/docs/specs"
|
|
672
|
+
|
|
673
|
+
for tpl in CURRENT_WORK REMAINING_TASKS PLANS TASKS INDEX; do
|
|
674
|
+
src="$CORE_ROOT/docs-templates/$tpl.md"
|
|
675
|
+
dest="$TARGET_ABS/docs/$tpl.md"
|
|
676
|
+
[ -f "$src" ] || continue
|
|
677
|
+
if [ -f "$dest" ]; then
|
|
678
|
+
log " $dest exists — leaving in place"
|
|
679
|
+
continue
|
|
680
|
+
fi
|
|
681
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
682
|
+
log "would copy $src -> $dest"
|
|
683
|
+
else
|
|
684
|
+
/bin/cp "$src" "$dest"
|
|
685
|
+
record_emit "docs/$tpl.md" "core/docs-templates/$tpl.md" ""
|
|
686
|
+
fi
|
|
687
|
+
done
|
|
688
|
+
|
|
689
|
+
if [ -f "$CORE_ROOT/docs-templates/specs/_example.md" ]; then
|
|
690
|
+
src="$CORE_ROOT/docs-templates/specs/_example.md"
|
|
691
|
+
dest="$TARGET_ABS/docs/specs/_example.md"
|
|
692
|
+
if [ ! -f "$dest" ]; then
|
|
693
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
694
|
+
log "would copy $src -> $dest"
|
|
695
|
+
else
|
|
696
|
+
/bin/cp "$src" "$dest"
|
|
697
|
+
record_emit "docs/specs/_example.md" "core/docs-templates/specs/_example.md" ""
|
|
698
|
+
fi
|
|
699
|
+
fi
|
|
700
|
+
fi
|
|
701
|
+
|
|
702
|
+
# Finalize manifest after all emits.
|
|
703
|
+
finalize_manifest
|
|
704
|
+
|
|
705
|
+
# ----- step 4: completion summary ----------------------------------------
|
|
706
|
+
|
|
707
|
+
log "Step 4/4: activation reminder"
|
|
708
|
+
echo ""
|
|
709
|
+
echo "========================================================"
|
|
710
|
+
echo " Done."
|
|
711
|
+
echo " Target: $TARGET_ABS"
|
|
712
|
+
echo " Adapter: cursor"
|
|
713
|
+
echo " Universal rules: 5 (.cursor/rules/*.mdc, alwaysApply:true)"
|
|
714
|
+
echo " Recipes installed:${INSTALLED_RECIPES:- (none)}"
|
|
715
|
+
if [ "$LEGACY_CURSORRULES" = "true" ]; then
|
|
716
|
+
echo " Legacy .cursorrules bundle: emitted"
|
|
717
|
+
fi
|
|
718
|
+
echo ""
|
|
719
|
+
echo " Skipped (per ADR-004 honesty):"
|
|
720
|
+
echo " - Hooks: CONDUCTOR emits the Reflector hook when --recipes=self-improvement (ADR-032); other guards remain Claude-only (ADR-034)."
|
|
721
|
+
echo " - Sub-agent personas: Cursor has no sub-agent dispatch — single chat session per task."
|
|
722
|
+
echo " - Hookify rule templates: Claude-only plugin."
|
|
723
|
+
echo ""
|
|
724
|
+
echo " Activation: reload Cursor window (Cmd/Ctrl+Shift+P → 'Developer: Reload Window')."
|
|
725
|
+
echo "========================================================"
|
|
726
|
+
echo ""
|
|
727
|
+
echo "Next steps for the project:"
|
|
728
|
+
echo " 1. Open $TARGET_ABS in Cursor."
|
|
729
|
+
echo " 2. Edit docs/CURRENT_WORK.md with your project's current state."
|
|
730
|
+
echo " 3. Verify rule loading: open the Cursor settings → Rules tab → confirm 5 universal + recipes shown."
|
|
731
|
+
echo " 4. Tighten recipe globs if needed (.cursor/rules/<recipe>.mdc has a sensible default)."
|