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,757 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# CONDUCTOR — GitHub Copilot adapter transform.sh
|
|
4
|
+
#
|
|
5
|
+
# Reads core/ assets and writes them into a target project as native GitHub Copilot
|
|
6
|
+
# custom instructions files: .github/copilot-instructions.md (repo-wide) and
|
|
7
|
+
# .github/instructions/*.instructions.md (per-file with applyTo: globs).
|
|
8
|
+
#
|
|
9
|
+
# Strategic value: Copilot custom instructions are IDE-agnostic — a single file format
|
|
10
|
+
# is consumed by VS Code, Cursor (Copilot extension), Windsurf (Copilot adapter),
|
|
11
|
+
# JetBrains (Copilot plugin), and Neovim (copilot.vim). One adapter covers 5 IDEs.
|
|
12
|
+
#
|
|
13
|
+
# Usage:
|
|
14
|
+
# bash adapters/copilot/transform.sh <target-project> [--recipes=<comma-list>] [--dry-run]
|
|
15
|
+
# [--no-prompt] [--per-rule] [--force]
|
|
16
|
+
# bash adapters/copilot/transform.sh <target-project> --uninstall [--dry-run] [--force]
|
|
17
|
+
#
|
|
18
|
+
# Examples:
|
|
19
|
+
# bash adapters/copilot/transform.sh ~/Projects/my-app
|
|
20
|
+
# bash adapters/copilot/transform.sh ~/Projects/my-app --recipes=monorepo,i18n
|
|
21
|
+
# bash adapters/copilot/transform.sh /tmp/test --dry-run
|
|
22
|
+
# bash adapters/copilot/transform.sh . --no-prompt --recipes=coding-conventions
|
|
23
|
+
# bash adapters/copilot/transform.sh . --per-rule # split universal rules into 5 per-file files
|
|
24
|
+
# bash adapters/copilot/transform.sh . --uninstall # revert install
|
|
25
|
+
# bash adapters/copilot/transform.sh . --uninstall --dry-run
|
|
26
|
+
#
|
|
27
|
+
# Layer 2 transformation (default — single-file universal):
|
|
28
|
+
# core/universal-rules/{workflow,spec-as-you-go,quality-gates,operations,meta-discipline}.md
|
|
29
|
+
# → <target>/.github/copilot-instructions.md (concatenated, body only)
|
|
30
|
+
# core/recipes/<r>.md (selected) → <target>/.github/instructions/<r>.instructions.md (applyTo: from paths)
|
|
31
|
+
# core/docs-templates/*.md → <target>/docs/*.md
|
|
32
|
+
# core/hooks/* → SKIP (Reflector hook emitted via --recipes=self-improvement, ADR-032; other guards Claude-only, ADR-034)
|
|
33
|
+
# core/roles/* → SKIP (Copilot has no sub-agent dispatch)
|
|
34
|
+
#
|
|
35
|
+
# Layer 2 transformation (--per-rule alternative):
|
|
36
|
+
# core/universal-rules/<r>.md → <target>/.github/instructions/<r>.instructions.md (applyTo: '**')
|
|
37
|
+
# (everything else identical to default)
|
|
38
|
+
#
|
|
39
|
+
# Frontmatter translation:
|
|
40
|
+
# Conductor source frontmatter:
|
|
41
|
+
# ---
|
|
42
|
+
# paths:
|
|
43
|
+
# - "apps/**/*.ts"
|
|
44
|
+
# - "apps/**/*.tsx"
|
|
45
|
+
# ---
|
|
46
|
+
# Copilot output frontmatter:
|
|
47
|
+
# ---
|
|
48
|
+
# applyTo: 'apps/**/*.ts,apps/**/*.tsx'
|
|
49
|
+
# ---
|
|
50
|
+
# Copilot uses a single CSV glob string. Multi-line YAML lists are flattened to
|
|
51
|
+
# comma-separated. If always_loaded:true (or no paths), output applyTo: '**'.
|
|
52
|
+
|
|
53
|
+
set -eu
|
|
54
|
+
|
|
55
|
+
# ----- arg parsing --------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
TARGET=""
|
|
58
|
+
RECIPES=""
|
|
59
|
+
DRY_RUN="false"
|
|
60
|
+
NO_PROMPT="false"
|
|
61
|
+
PER_RULE="false"
|
|
62
|
+
UNINSTALL="false"
|
|
63
|
+
FORCE="false"
|
|
64
|
+
|
|
65
|
+
WIZARD_APPLY_RULES="true"
|
|
66
|
+
|
|
67
|
+
while [ $# -gt 0 ]; do
|
|
68
|
+
case "$1" in
|
|
69
|
+
--recipes=*) RECIPES="${1#--recipes=}" ;;
|
|
70
|
+
--dry-run) DRY_RUN="true" ;;
|
|
71
|
+
--no-prompt) NO_PROMPT="true" ;;
|
|
72
|
+
--per-rule) PER_RULE="true" ;;
|
|
73
|
+
--uninstall|--rollback) UNINSTALL="true" ;;
|
|
74
|
+
--force) FORCE="true" ;;
|
|
75
|
+
--help|-h)
|
|
76
|
+
/bin/cat <<EOF
|
|
77
|
+
Usage: bash adapters/copilot/transform.sh <target-project> [options]
|
|
78
|
+
|
|
79
|
+
Options:
|
|
80
|
+
--recipes=A,B,C Comma-separated list of recipes to install
|
|
81
|
+
--dry-run Preview only — no files written
|
|
82
|
+
--no-prompt Skip interactive prompts; apply defaults (CI-safe)
|
|
83
|
+
--per-rule Split 5 universal rules into per-file .instructions.md
|
|
84
|
+
(default: concatenate into single .github/copilot-instructions.md)
|
|
85
|
+
--uninstall Revert a previous install using <target>/.conductor-manifest.json
|
|
86
|
+
(alias: --rollback). Restores backups when present, deletes
|
|
87
|
+
Conductor-emitted files when none.
|
|
88
|
+
--force Bypass uninstall safety checks (active worktrees, missing manifest)
|
|
89
|
+
|
|
90
|
+
Recipes available: web-mobile-parity, i18n, monorepo, branch-strategy, auto-mock-data, coding-conventions, tdd, debugging, database-discipline, design-system, self-improvement
|
|
91
|
+
|
|
92
|
+
Output (default):
|
|
93
|
+
<target>/.github/copilot-instructions.md (5 universal rules merged)
|
|
94
|
+
<target>/.github/instructions/<recipe>.instructions.md (per recipe, applyTo: from paths)
|
|
95
|
+
<target>/docs/{CURRENT_WORK,REMAINING_TASKS,PLANS,TASKS,INDEX}.md
|
|
96
|
+
|
|
97
|
+
Skipped (not supported by Copilot):
|
|
98
|
+
Sub-agent dispatch (roles) — Copilot has no Agent equivalent
|
|
99
|
+
PreToolUse / Stop hooks — Copilot has no commit-blocking hooks
|
|
100
|
+
|
|
101
|
+
IDE coverage: VS Code, Cursor (Copilot ext), Windsurf (Copilot adapter), JetBrains
|
|
102
|
+
(Copilot plugin), Neovim (copilot.vim) all read .github/instructions/.
|
|
103
|
+
EOF
|
|
104
|
+
exit 0
|
|
105
|
+
;;
|
|
106
|
+
*)
|
|
107
|
+
if [ -z "$TARGET" ]; then
|
|
108
|
+
TARGET="$1"
|
|
109
|
+
else
|
|
110
|
+
echo "Unknown argument: $1" >&2
|
|
111
|
+
exit 1
|
|
112
|
+
fi
|
|
113
|
+
;;
|
|
114
|
+
esac
|
|
115
|
+
shift
|
|
116
|
+
done
|
|
117
|
+
|
|
118
|
+
if [ -z "$TARGET" ]; then
|
|
119
|
+
echo "Error: target-project path is required." >&2
|
|
120
|
+
echo "Usage: bash adapters/copilot/transform.sh <target-project> [--recipes=...]" >&2
|
|
121
|
+
exit 1
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
CONDUCTOR_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
125
|
+
CORE_ROOT="$CONDUCTOR_ROOT/core"
|
|
126
|
+
[ -d "$CORE_ROOT" ] || { echo "Error: core/ not found at $CORE_ROOT" >&2; exit 1; }
|
|
127
|
+
|
|
128
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
129
|
+
mkdir -p "$TARGET"
|
|
130
|
+
fi
|
|
131
|
+
TARGET_ABS="$(cd "$TARGET" 2>/dev/null && pwd)" || { echo "Error: target directory does not exist: $TARGET" >&2; exit 1; }
|
|
132
|
+
|
|
133
|
+
# ----- helpers ------------------------------------------------------------
|
|
134
|
+
|
|
135
|
+
log() {
|
|
136
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
137
|
+
echo "[dry-run] $*"
|
|
138
|
+
else
|
|
139
|
+
echo "[conductor] $*"
|
|
140
|
+
fi
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
mkdir_if_real() {
|
|
144
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
145
|
+
log "would mkdir -p $1"
|
|
146
|
+
else
|
|
147
|
+
mkdir -p "$1"
|
|
148
|
+
fi
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
# strip_frontmatter <src-file>
|
|
152
|
+
# Print only the body (everything after the second `---` line). If no frontmatter,
|
|
153
|
+
# print the whole file.
|
|
154
|
+
strip_frontmatter() {
|
|
155
|
+
/usr/bin/awk 'BEGIN{c=0; f=0} /^---$/{c++; if(c==2){f=1; next} else {next}} f==1 || c==0' "$1"
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
# extract_paths_csv <src-file>
|
|
159
|
+
# Read frontmatter `paths:` YAML list, return CSV glob string.
|
|
160
|
+
# Falls back to `applies_to:` (used by universal-rules). Returns empty if absent
|
|
161
|
+
# or if always_loaded:true is set.
|
|
162
|
+
extract_paths_csv() {
|
|
163
|
+
local src="$1"
|
|
164
|
+
# Always-loaded check first.
|
|
165
|
+
local always
|
|
166
|
+
always=$(/usr/bin/awk '
|
|
167
|
+
BEGIN{c=0; in_fm=0}
|
|
168
|
+
/^---$/{c++; if(c==1){in_fm=1; next} else {exit}}
|
|
169
|
+
in_fm && /^always_loaded:[[:space:]]*true/{print "true"; exit}
|
|
170
|
+
' "$src")
|
|
171
|
+
if [ "$always" = "true" ]; then
|
|
172
|
+
echo "**"
|
|
173
|
+
return
|
|
174
|
+
fi
|
|
175
|
+
# Try paths: list (recipe style).
|
|
176
|
+
local paths_csv
|
|
177
|
+
paths_csv=$(/usr/bin/awk '
|
|
178
|
+
BEGIN{c=0; in_fm=0; in_paths=0; out=""}
|
|
179
|
+
/^---$/{c++; if(c==1){in_fm=1; next} else {exit}}
|
|
180
|
+
in_fm && /^paths:/{in_paths=1; next}
|
|
181
|
+
in_fm && in_paths && /^[[:space:]]*-[[:space:]]*"/{
|
|
182
|
+
gsub(/^[[:space:]]*-[[:space:]]*"/, "")
|
|
183
|
+
gsub(/"[[:space:]]*$/, "")
|
|
184
|
+
if(out=="") out=$0; else out=out "," $0
|
|
185
|
+
next
|
|
186
|
+
}
|
|
187
|
+
in_fm && in_paths && /^[^[:space:]-]/{in_paths=0}
|
|
188
|
+
END{print out}
|
|
189
|
+
' "$src")
|
|
190
|
+
if [ -n "$paths_csv" ]; then
|
|
191
|
+
echo "$paths_csv"
|
|
192
|
+
return
|
|
193
|
+
fi
|
|
194
|
+
# Try applies_to: list (universal-rule style).
|
|
195
|
+
local applies_csv
|
|
196
|
+
applies_csv=$(/usr/bin/awk '
|
|
197
|
+
BEGIN{c=0; in_fm=0; in_a=0; out=""}
|
|
198
|
+
/^---$/{c++; if(c==1){in_fm=1; next} else {exit}}
|
|
199
|
+
in_fm && /^applies_to:/{
|
|
200
|
+
# Inline form: applies_to: ["a","b"]
|
|
201
|
+
if(match($0, /\[.*\]/)){
|
|
202
|
+
s=substr($0, RSTART+1, RLENGTH-2)
|
|
203
|
+
gsub(/"/, "", s); gsub(/[[:space:]]/, "", s)
|
|
204
|
+
print s; exit
|
|
205
|
+
}
|
|
206
|
+
in_a=1; next
|
|
207
|
+
}
|
|
208
|
+
in_fm && in_a && /^[[:space:]]*-[[:space:]]*"/{
|
|
209
|
+
gsub(/^[[:space:]]*-[[:space:]]*"/, "")
|
|
210
|
+
gsub(/"[[:space:]]*$/, "")
|
|
211
|
+
if(out=="") out=$0; else out=out "," $0
|
|
212
|
+
next
|
|
213
|
+
}
|
|
214
|
+
in_fm && in_a && /^[^[:space:]-]/{in_a=0}
|
|
215
|
+
END{if(out!="") print out}
|
|
216
|
+
' "$src")
|
|
217
|
+
if [ -n "$applies_csv" ]; then
|
|
218
|
+
# all-tools sentinel maps to ** (matches everything).
|
|
219
|
+
case "$applies_csv" in
|
|
220
|
+
*all-tools*) echo "**" ;;
|
|
221
|
+
*) echo "$applies_csv" ;;
|
|
222
|
+
esac
|
|
223
|
+
return
|
|
224
|
+
fi
|
|
225
|
+
# Default fallback.
|
|
226
|
+
echo "**"
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
# write_copilot_per_file <src> <dest> <apply-to-csv>
|
|
230
|
+
# Emit a Copilot per-file instructions.md with applyTo: <csv> frontmatter,
|
|
231
|
+
# then strip the source's Conductor frontmatter and append the body.
|
|
232
|
+
write_copilot_per_file() {
|
|
233
|
+
local src="$1" dest="$2" applyto="$3"
|
|
234
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
235
|
+
log "would write $dest with applyTo: '$applyto'"
|
|
236
|
+
return
|
|
237
|
+
fi
|
|
238
|
+
/bin/cat > "$dest" <<EOF
|
|
239
|
+
---
|
|
240
|
+
applyTo: '$applyto'
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
EOF
|
|
244
|
+
strip_frontmatter "$src" >> "$dest"
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
# backup_if_exists <dest>
|
|
248
|
+
backup_if_exists() {
|
|
249
|
+
local dest="$1"
|
|
250
|
+
MANIFEST_LAST_BACKUP=""
|
|
251
|
+
if [ -f "$dest" ]; then
|
|
252
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
253
|
+
log "would back up existing $dest -> $dest.conductor-backup-<ts>"
|
|
254
|
+
else
|
|
255
|
+
local ts
|
|
256
|
+
ts="$(/bin/date +%Y%m%d-%H%M%S)"
|
|
257
|
+
local backup="${dest}.conductor-backup-${ts}"
|
|
258
|
+
/bin/cp "$dest" "$backup"
|
|
259
|
+
log " backed up existing $dest -> $backup"
|
|
260
|
+
MANIFEST_LAST_BACKUP="${backup#$TARGET_ABS/}"
|
|
261
|
+
fi
|
|
262
|
+
fi
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
# ----- manifest tracking (mirrors claude adapter ADR-020) ----------------
|
|
266
|
+
|
|
267
|
+
MANIFEST_PATH="$TARGET_ABS/.conductor-manifest.json"
|
|
268
|
+
MANIFEST_STAGE_PATH=""
|
|
269
|
+
MANIFEST_TS=""
|
|
270
|
+
MANIFEST_LAST_BACKUP=""
|
|
271
|
+
|
|
272
|
+
init_manifest() {
|
|
273
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
274
|
+
log "would init manifest staging at $MANIFEST_PATH.staging"
|
|
275
|
+
return
|
|
276
|
+
fi
|
|
277
|
+
MANIFEST_TS="$(/bin/date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
278
|
+
MANIFEST_STAGE_PATH="$TARGET_ABS/.conductor-manifest.json.staging"
|
|
279
|
+
/bin/rm -f "$MANIFEST_STAGE_PATH"
|
|
280
|
+
: > "$MANIFEST_STAGE_PATH"
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
record_emit() {
|
|
284
|
+
if [ "$DRY_RUN" = "true" ] || [ "$UNINSTALL" = "true" ]; then
|
|
285
|
+
return
|
|
286
|
+
fi
|
|
287
|
+
local relpath="$1" src="$2" backup="${3:-}"
|
|
288
|
+
local had_backup="false"
|
|
289
|
+
[ -n "$backup" ] && had_backup="true"
|
|
290
|
+
local esc_path esc_src esc_backup
|
|
291
|
+
esc_path="$(printf '%s' "$relpath" | /usr/bin/sed 's/\\/\\\\/g; s/"/\\"/g')"
|
|
292
|
+
esc_src="$(printf '%s' "$src" | /usr/bin/sed 's/\\/\\\\/g; s/"/\\"/g')"
|
|
293
|
+
esc_backup="$(printf '%s' "$backup" | /usr/bin/sed 's/\\/\\\\/g; s/"/\\"/g')"
|
|
294
|
+
printf ' {"path": "%s", "source": "%s", "had_backup": %s, "backup_path": "%s"},\n' \
|
|
295
|
+
"$esc_path" "$esc_src" "$had_backup" "$esc_backup" >> "$MANIFEST_STAGE_PATH"
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
finalize_manifest() {
|
|
299
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
300
|
+
log "would finalize manifest -> $MANIFEST_PATH"
|
|
301
|
+
return
|
|
302
|
+
fi
|
|
303
|
+
[ -z "$MANIFEST_STAGE_PATH" ] && return
|
|
304
|
+
[ -f "$MANIFEST_STAGE_PATH" ] || return
|
|
305
|
+
|
|
306
|
+
if [ -f "$MANIFEST_PATH" ]; then
|
|
307
|
+
backup_if_exists "$MANIFEST_PATH"
|
|
308
|
+
fi
|
|
309
|
+
|
|
310
|
+
local recipes_json="[]"
|
|
311
|
+
if [ -n "$RECIPES" ]; then
|
|
312
|
+
recipes_json="["
|
|
313
|
+
local IFS_BAK=$IFS
|
|
314
|
+
IFS=','
|
|
315
|
+
local first="true"
|
|
316
|
+
for r in $RECIPES; do
|
|
317
|
+
r="$(printf '%s' "$r" | /usr/bin/sed 's/^ *//; s/ *$//')"
|
|
318
|
+
[ -z "$r" ] && continue
|
|
319
|
+
if [ "$first" = "true" ]; then
|
|
320
|
+
recipes_json="${recipes_json}\"$r\""
|
|
321
|
+
first="false"
|
|
322
|
+
else
|
|
323
|
+
recipes_json="${recipes_json}, \"$r\""
|
|
324
|
+
fi
|
|
325
|
+
done
|
|
326
|
+
IFS=$IFS_BAK
|
|
327
|
+
recipes_json="${recipes_json}]"
|
|
328
|
+
fi
|
|
329
|
+
|
|
330
|
+
local entries
|
|
331
|
+
if [ -s "$MANIFEST_STAGE_PATH" ]; then
|
|
332
|
+
entries="$(/usr/bin/sed -e '$ s/,$//' "$MANIFEST_STAGE_PATH")"
|
|
333
|
+
else
|
|
334
|
+
entries=""
|
|
335
|
+
fi
|
|
336
|
+
|
|
337
|
+
/bin/cat > "$MANIFEST_PATH" <<EOF
|
|
338
|
+
{
|
|
339
|
+
"version": "v0.2.0",
|
|
340
|
+
"adapter": "copilot",
|
|
341
|
+
"install_timestamp": "$MANIFEST_TS",
|
|
342
|
+
"conductor_root": "$CONDUCTOR_ROOT",
|
|
343
|
+
"per_rule_mode": $PER_RULE,
|
|
344
|
+
"recipes_enabled": $recipes_json,
|
|
345
|
+
"emitted_files": [
|
|
346
|
+
$entries
|
|
347
|
+
]
|
|
348
|
+
}
|
|
349
|
+
EOF
|
|
350
|
+
/bin/rm -f "$MANIFEST_STAGE_PATH"
|
|
351
|
+
log " wrote manifest $MANIFEST_PATH"
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
# ----- uninstall flow ----------------------------------------------------
|
|
355
|
+
|
|
356
|
+
do_uninstall() {
|
|
357
|
+
log "uninstall mode (target: $TARGET_ABS)"
|
|
358
|
+
|
|
359
|
+
if [ ! -f "$MANIFEST_PATH" ]; then
|
|
360
|
+
if [ "$FORCE" = "true" ]; then
|
|
361
|
+
log "WARNING: no manifest at $MANIFEST_PATH — proceeding under --force (legacy scan)"
|
|
362
|
+
uninstall_legacy_scan
|
|
363
|
+
return 0
|
|
364
|
+
fi
|
|
365
|
+
echo "Error: no manifest at $MANIFEST_PATH." >&2
|
|
366
|
+
echo " Re-run with --force to scan for legacy .conductor-backup-* files anyway." >&2
|
|
367
|
+
exit 1
|
|
368
|
+
fi
|
|
369
|
+
|
|
370
|
+
if [ -d "$TARGET_ABS/.git" ]; then
|
|
371
|
+
if [ -f "$TARGET_ABS/.git/MERGE_HEAD" ] || [ -f "$TARGET_ABS/.git/REBASE_HEAD" ] || [ -d "$TARGET_ABS/.git/rebase-merge" ]; then
|
|
372
|
+
if [ "$FORCE" != "true" ]; then
|
|
373
|
+
echo "Error: target has an active git operation (merge/rebase in progress)." >&2
|
|
374
|
+
echo " Resolve or pass --force to override." >&2
|
|
375
|
+
exit 1
|
|
376
|
+
fi
|
|
377
|
+
log "WARNING: active git operation — proceeding under --force"
|
|
378
|
+
fi
|
|
379
|
+
fi
|
|
380
|
+
|
|
381
|
+
log "loading manifest entries..."
|
|
382
|
+
local entries_count=0
|
|
383
|
+
local restored=0
|
|
384
|
+
local deleted=0
|
|
385
|
+
local missing=0
|
|
386
|
+
|
|
387
|
+
while IFS= read -r line; do
|
|
388
|
+
case "$line" in
|
|
389
|
+
*'"path":'*'"source":'*'"had_backup":'*) ;;
|
|
390
|
+
*) continue ;;
|
|
391
|
+
esac
|
|
392
|
+
entries_count=$((entries_count + 1))
|
|
393
|
+
local rel_path had_backup backup_path
|
|
394
|
+
rel_path="$(printf '%s' "$line" | /usr/bin/sed -E 's/.*"path": *"([^"]*)".*/\1/')"
|
|
395
|
+
had_backup="$(printf '%s' "$line" | /usr/bin/sed -E 's/.*"had_backup": *(true|false).*/\1/')"
|
|
396
|
+
backup_path="$(printf '%s' "$line" | /usr/bin/sed -E 's/.*"backup_path": *"([^"]*)".*/\1/')"
|
|
397
|
+
|
|
398
|
+
local abs_dest="$TARGET_ABS/$rel_path"
|
|
399
|
+
local abs_backup=""
|
|
400
|
+
[ -n "$backup_path" ] && abs_backup="$TARGET_ABS/$backup_path"
|
|
401
|
+
|
|
402
|
+
if [ "$had_backup" = "true" ] && [ -n "$abs_backup" ]; then
|
|
403
|
+
if [ -f "$abs_backup" ]; then
|
|
404
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
405
|
+
log " would restore $abs_backup -> $abs_dest"
|
|
406
|
+
else
|
|
407
|
+
/bin/mv -f "$abs_backup" "$abs_dest"
|
|
408
|
+
log " restored $abs_backup -> $abs_dest"
|
|
409
|
+
fi
|
|
410
|
+
restored=$((restored + 1))
|
|
411
|
+
else
|
|
412
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
413
|
+
log " would delete $abs_dest (backup $abs_backup missing)"
|
|
414
|
+
else
|
|
415
|
+
/bin/rm -f "$abs_dest"
|
|
416
|
+
log " deleted $abs_dest (backup $abs_backup missing)"
|
|
417
|
+
fi
|
|
418
|
+
missing=$((missing + 1))
|
|
419
|
+
fi
|
|
420
|
+
else
|
|
421
|
+
if [ -f "$abs_dest" ]; then
|
|
422
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
423
|
+
log " would delete $abs_dest"
|
|
424
|
+
else
|
|
425
|
+
/bin/rm -f "$abs_dest"
|
|
426
|
+
log " deleted $abs_dest"
|
|
427
|
+
fi
|
|
428
|
+
deleted=$((deleted + 1))
|
|
429
|
+
else
|
|
430
|
+
log " skip $abs_dest (already absent)"
|
|
431
|
+
fi
|
|
432
|
+
fi
|
|
433
|
+
done < "$MANIFEST_PATH"
|
|
434
|
+
|
|
435
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
436
|
+
log " would delete $MANIFEST_PATH"
|
|
437
|
+
for mb in "$MANIFEST_PATH".conductor-backup-*; do
|
|
438
|
+
[ -e "$mb" ] && log " would delete $mb"
|
|
439
|
+
done
|
|
440
|
+
else
|
|
441
|
+
/bin/rm -f "$MANIFEST_PATH"
|
|
442
|
+
for mb in "$MANIFEST_PATH".conductor-backup-*; do
|
|
443
|
+
[ -e "$mb" ] && /bin/rm -f "$mb"
|
|
444
|
+
done
|
|
445
|
+
log " deleted $MANIFEST_PATH"
|
|
446
|
+
fi
|
|
447
|
+
|
|
448
|
+
for d in .github/instructions .github/hooks .github/prompts .github/agents .github .conductor/reflect .conductor; do
|
|
449
|
+
local abs_d="$TARGET_ABS/$d"
|
|
450
|
+
if [ -d "$abs_d" ]; then
|
|
451
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
452
|
+
if [ -z "$(/bin/ls -A "$abs_d" 2>/dev/null)" ]; then
|
|
453
|
+
log " would rmdir empty $abs_d"
|
|
454
|
+
fi
|
|
455
|
+
else
|
|
456
|
+
/bin/rmdir "$abs_d" 2>/dev/null && log " rmdir empty $abs_d" || true
|
|
457
|
+
fi
|
|
458
|
+
fi
|
|
459
|
+
done
|
|
460
|
+
|
|
461
|
+
echo ""
|
|
462
|
+
echo "========================================================"
|
|
463
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
464
|
+
echo " Uninstall preview (dry-run)"
|
|
465
|
+
else
|
|
466
|
+
echo " Uninstall complete"
|
|
467
|
+
fi
|
|
468
|
+
echo " Target: $TARGET_ABS"
|
|
469
|
+
echo " Entries processed: $entries_count"
|
|
470
|
+
echo " Backups restored: $restored"
|
|
471
|
+
echo " Files deleted: $deleted"
|
|
472
|
+
echo " Backup-missing deletes: $missing"
|
|
473
|
+
echo "========================================================"
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
uninstall_legacy_scan() {
|
|
477
|
+
log "legacy scan mode — searching for .conductor-backup-* files under $TARGET_ABS"
|
|
478
|
+
local found=0
|
|
479
|
+
while IFS= read -r f; do
|
|
480
|
+
found=$((found + 1))
|
|
481
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
482
|
+
log " would delete legacy backup $f"
|
|
483
|
+
else
|
|
484
|
+
/bin/rm -f "$f"
|
|
485
|
+
log " deleted legacy backup $f"
|
|
486
|
+
fi
|
|
487
|
+
done < <(/usr/bin/find "$TARGET_ABS" -type f -name '*.conductor-backup-*' 2>/dev/null)
|
|
488
|
+
log "legacy scan: $found backup file(s)"
|
|
489
|
+
log "WARNING: legacy mode does not delete Conductor-emitted source files."
|
|
490
|
+
log " Manually remove .github/copilot-instructions.md and .github/instructions/*.instructions.md if desired."
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
if [ "$UNINSTALL" = "true" ]; then
|
|
494
|
+
do_uninstall
|
|
495
|
+
exit 0
|
|
496
|
+
fi
|
|
497
|
+
|
|
498
|
+
# ----- onboarding wizard --------------------------------------------------
|
|
499
|
+
# Adopter case = .github/copilot-instructions.md OR .github/instructions/ already exists.
|
|
500
|
+
|
|
501
|
+
IS_ADOPTER_CASE="false"
|
|
502
|
+
if [ -f "$TARGET_ABS/.github/copilot-instructions.md" ] || [ -d "$TARGET_ABS/.github/instructions" ]; then
|
|
503
|
+
IS_ADOPTER_CASE="true"
|
|
504
|
+
fi
|
|
505
|
+
|
|
506
|
+
if [ "$IS_ADOPTER_CASE" = "true" ] && [ "$NO_PROMPT" = "false" ] && [ "$DRY_RUN" = "false" ]; then
|
|
507
|
+
echo ""
|
|
508
|
+
echo "========================================================"
|
|
509
|
+
echo " Welcome to CONDUCTOR (Copilot adapter) setup"
|
|
510
|
+
echo " Target: $TARGET_ABS"
|
|
511
|
+
echo "========================================================"
|
|
512
|
+
echo ""
|
|
513
|
+
echo " Detected existing Copilot custom instructions."
|
|
514
|
+
echo " Existing files will be backed up to .conductor-backup-<timestamp> before overwrite."
|
|
515
|
+
echo ""
|
|
516
|
+
|
|
517
|
+
printf "Apply universal-rules? (Y/n): "
|
|
518
|
+
read -r _apply_answer
|
|
519
|
+
if [ "$_apply_answer" = "n" ] || [ "$_apply_answer" = "N" ]; then
|
|
520
|
+
WIZARD_APPLY_RULES="false"
|
|
521
|
+
echo " Skipping universal-rules installation."
|
|
522
|
+
fi
|
|
523
|
+
|
|
524
|
+
echo ""
|
|
525
|
+
echo "Available recipes:"
|
|
526
|
+
echo " web-mobile-parity, i18n, monorepo, branch-strategy, auto-mock-data, coding-conventions, tdd, debugging, database-discipline, design-system, self-improvement"
|
|
527
|
+
printf "Select recipes (comma-separated, blank for none): "
|
|
528
|
+
read -r _recipe_answer
|
|
529
|
+
if [ -n "$_recipe_answer" ]; then
|
|
530
|
+
RECIPES="$_recipe_answer"
|
|
531
|
+
echo " Recipes selected: $RECIPES"
|
|
532
|
+
fi
|
|
533
|
+
|
|
534
|
+
printf "Use --per-rule mode (split universal-rules into 5 separate per-file files)? (y/N): "
|
|
535
|
+
read -r _per_rule_answer
|
|
536
|
+
if [ "$_per_rule_answer" = "y" ] || [ "$_per_rule_answer" = "Y" ]; then
|
|
537
|
+
PER_RULE="true"
|
|
538
|
+
echo " Per-rule mode enabled (output: .github/instructions/{workflow,...}.instructions.md)."
|
|
539
|
+
fi
|
|
540
|
+
|
|
541
|
+
echo ""
|
|
542
|
+
elif [ "$IS_ADOPTER_CASE" = "true" ] && [ "$NO_PROMPT" = "true" ]; then
|
|
543
|
+
log "Adopter case detected — applying defaults (--no-prompt): rules=yes, recipes=${RECIPES:-(none)}, per_rule=$PER_RULE"
|
|
544
|
+
fi
|
|
545
|
+
|
|
546
|
+
# ----- step 1: universal rules → .github/copilot-instructions.md ---------
|
|
547
|
+
|
|
548
|
+
init_manifest
|
|
549
|
+
|
|
550
|
+
if [ "$WIZARD_APPLY_RULES" = "true" ]; then
|
|
551
|
+
if [ "$PER_RULE" = "true" ]; then
|
|
552
|
+
log "Step 1/4: universal-rules → .github/instructions/*.instructions.md (per-rule mode)"
|
|
553
|
+
mkdir_if_real "$TARGET_ABS/.github/instructions"
|
|
554
|
+
for rule in workflow spec-as-you-go quality-gates operations meta-discipline; do
|
|
555
|
+
src="$CORE_ROOT/universal-rules/$rule.md"
|
|
556
|
+
dest="$TARGET_ABS/.github/instructions/$rule.instructions.md"
|
|
557
|
+
if [ ! -f "$src" ]; then
|
|
558
|
+
echo "Warning: $src not found; skipping" >&2
|
|
559
|
+
continue
|
|
560
|
+
fi
|
|
561
|
+
backup_if_exists "$dest"
|
|
562
|
+
# Universal rules are always-loaded → applyTo: '**'
|
|
563
|
+
write_copilot_per_file "$src" "$dest" "**"
|
|
564
|
+
record_emit ".github/instructions/$rule.instructions.md" "core/universal-rules/$rule.md" "$MANIFEST_LAST_BACKUP"
|
|
565
|
+
done
|
|
566
|
+
else
|
|
567
|
+
log "Step 1/4: universal-rules → .github/copilot-instructions.md (single-file mode)"
|
|
568
|
+
mkdir_if_real "$TARGET_ABS/.github"
|
|
569
|
+
dest="$TARGET_ABS/.github/copilot-instructions.md"
|
|
570
|
+
backup_if_exists "$dest"
|
|
571
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
572
|
+
log "would synthesize $dest from 5 universal-rule bodies"
|
|
573
|
+
else
|
|
574
|
+
/bin/cat > "$dest" <<'HEADER_EOF'
|
|
575
|
+
# Project Custom Instructions (installed by CONDUCTOR — Copilot adapter)
|
|
576
|
+
|
|
577
|
+
> Loaded automatically by GitHub Copilot for every chat in this repository.
|
|
578
|
+
> Read the 5 universal rules below before any tool call.
|
|
579
|
+
> Source: https://github.com/<your-org>/conductor (universal-rules/*.md)
|
|
580
|
+
|
|
581
|
+
## Topology note (Copilot)
|
|
582
|
+
|
|
583
|
+
GitHub Copilot does not have sub-agent dispatch or commit-blocking hooks. The 5
|
|
584
|
+
universal rules below are self-policed: the human (and Copilot Chat) must follow
|
|
585
|
+
the same Plan → Architecture → Tasks → Implementation → Review → Spec workflow
|
|
586
|
+
that Claude Code enforces with hooks. Two-stage code review degrades to the
|
|
587
|
+
Copilot PR review feature for Stage B (configure separately at the repo level).
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
HEADER_EOF
|
|
592
|
+
for rule in workflow spec-as-you-go quality-gates operations meta-discipline; do
|
|
593
|
+
src="$CORE_ROOT/universal-rules/$rule.md"
|
|
594
|
+
if [ ! -f "$src" ]; then
|
|
595
|
+
echo "Warning: $src not found; skipping" >&2
|
|
596
|
+
continue
|
|
597
|
+
fi
|
|
598
|
+
echo "" >> "$dest"
|
|
599
|
+
echo "<!-- ===== universal-rule: $rule ===== -->" >> "$dest"
|
|
600
|
+
echo "" >> "$dest"
|
|
601
|
+
strip_frontmatter "$src" >> "$dest"
|
|
602
|
+
echo "" >> "$dest"
|
|
603
|
+
echo "---" >> "$dest"
|
|
604
|
+
done
|
|
605
|
+
log " wrote $dest ($(/usr/bin/wc -l < "$dest" | /usr/bin/tr -d ' ') lines)"
|
|
606
|
+
fi
|
|
607
|
+
record_emit ".github/copilot-instructions.md" "<synthesized:5-universal-rules>" "$MANIFEST_LAST_BACKUP"
|
|
608
|
+
fi
|
|
609
|
+
else
|
|
610
|
+
log "Step 1/4: universal-rules — skipped (user opted out)"
|
|
611
|
+
fi
|
|
612
|
+
|
|
613
|
+
# ----- step 2: recipes → .github/instructions/<r>.instructions.md --------
|
|
614
|
+
|
|
615
|
+
log "Step 2/4: recipes (opt-in) → .github/instructions/"
|
|
616
|
+
if [ -n "$RECIPES" ]; then
|
|
617
|
+
mkdir_if_real "$TARGET_ABS/.github/instructions"
|
|
618
|
+
IFS=',' read -ra RECIPE_LIST <<< "$RECIPES"
|
|
619
|
+
for r in "${RECIPE_LIST[@]}"; do
|
|
620
|
+
r="$(printf '%s' "$r" | /usr/bin/sed 's/^ *//; s/ *$//')"
|
|
621
|
+
[ -z "$r" ] && continue
|
|
622
|
+
src="$CORE_ROOT/recipes/$r.md"
|
|
623
|
+
dest="$TARGET_ABS/.github/instructions/$r.instructions.md"
|
|
624
|
+
if [ ! -f "$src" ]; then
|
|
625
|
+
echo "Warning: recipe '$r' not found at $src; skipping" >&2
|
|
626
|
+
continue
|
|
627
|
+
fi
|
|
628
|
+
backup_if_exists "$dest"
|
|
629
|
+
applyto="$(extract_paths_csv "$src")"
|
|
630
|
+
[ -z "$applyto" ] && applyto="**"
|
|
631
|
+
write_copilot_per_file "$src" "$dest" "$applyto"
|
|
632
|
+
log " recipe $r → applyTo: '$applyto'"
|
|
633
|
+
record_emit ".github/instructions/$r.instructions.md" "core/recipes/$r.md" "$MANIFEST_LAST_BACKUP"
|
|
634
|
+
done
|
|
635
|
+
else
|
|
636
|
+
log " (no recipes selected — pass --recipes=name1,name2 to install)"
|
|
637
|
+
fi
|
|
638
|
+
|
|
639
|
+
# ---- Step 2.6: self-improvement runtime (only with --recipes=self-improvement) ----
|
|
640
|
+
case ",$RECIPES," in
|
|
641
|
+
*",self-improvement,"*)
|
|
642
|
+
log "Step 2.6/4: self-improvement (Reflector) → hooks/prompt/agent"
|
|
643
|
+
if [ "$DRY_RUN" != "true" ]; then
|
|
644
|
+
/bin/mkdir -p "$TARGET_ABS/.conductor/reflect" "$TARGET_ABS/.github/hooks" "$TARGET_ABS/.github/prompts" "$TARGET_ABS/.github/agents"
|
|
645
|
+
gi="$TARGET_ABS/.gitignore"
|
|
646
|
+
grep -qxF '.conductor/' "$gi" 2>/dev/null || printf '\n# CONDUCTOR runtime (local trajectories/lessons)\n.conductor/\n' >> "$gi"
|
|
647
|
+
for s in trajectory-log prune-lessons run-weekly; do
|
|
648
|
+
d="$TARGET_ABS/.conductor/reflect/$s.sh"
|
|
649
|
+
backup_if_exists "$d"; /bin/cp "$CORE_ROOT/reflector/$s.sh" "$d"; /bin/chmod +x "$d"
|
|
650
|
+
record_emit ".conductor/reflect/$s.sh" "core/reflector/$s.sh" "$MANIFEST_LAST_BACKUP"
|
|
651
|
+
done
|
|
652
|
+
# scheduling assets: run-weekly.sh needs the brief; SCHEDULING.md documents registration
|
|
653
|
+
for m in reflect-brief SCHEDULING; do
|
|
654
|
+
d="$TARGET_ABS/.conductor/reflect/$m.md"
|
|
655
|
+
backup_if_exists "$d"; /bin/cp "$CORE_ROOT/reflector/$m.md" "$d"
|
|
656
|
+
record_emit ".conductor/reflect/$m.md" "core/reflector/$m.md" "$MANIFEST_LAST_BACKUP"
|
|
657
|
+
done
|
|
658
|
+
hc="$TARGET_ABS/.github/hooks/conductor-reflect.json"
|
|
659
|
+
if [ ! -f "$hc" ]; then
|
|
660
|
+
backup_if_exists "$hc"
|
|
661
|
+
/bin/cat > "$hc" <<'HOOK'
|
|
662
|
+
{
|
|
663
|
+
"version": 1,
|
|
664
|
+
"hooks": {
|
|
665
|
+
"agentStop": [ { "type": "command", "bash": "./.conductor/reflect/trajectory-log.sh", "timeoutSec": 10 } ]
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
HOOK
|
|
669
|
+
record_emit ".github/hooks/conductor-reflect.json" "<synthesized>" "$MANIFEST_LAST_BACKUP"
|
|
670
|
+
else
|
|
671
|
+
log " .github/hooks/conductor-reflect.json exists — add an agentStop hook calling ./.conductor/reflect/trajectory-log.sh manually"
|
|
672
|
+
fi
|
|
673
|
+
pr="$TARGET_ABS/.github/prompts/reflect.prompt.md"
|
|
674
|
+
backup_if_exists "$pr"
|
|
675
|
+
{ printf -- "---\ndescription: 'Run the CONDUCTOR Reflector — propose lessons from recent sessions (propose-only)'\nagent: 'agent'\n---\n\n"; /bin/cat "$CORE_ROOT/reflector/reflect-brief.md"; } > "$pr"
|
|
676
|
+
record_emit ".github/prompts/reflect.prompt.md" "core/reflector/reflect-brief.md" "$MANIFEST_LAST_BACKUP"
|
|
677
|
+
ag="$TARGET_ABS/.github/agents/reflector.agent.md"
|
|
678
|
+
backup_if_exists "$ag"
|
|
679
|
+
{ printf -- '---\nname: reflector\ndescription: "Reads session trajectories and proposes atomic lesson deltas. Propose-only; never applies."\n---\n\n'; strip_frontmatter "$CORE_ROOT/roles/reflector.md"; } > "$ag"
|
|
680
|
+
record_emit ".github/agents/reflector.agent.md" "core/roles/reflector.md" "$MANIFEST_LAST_BACKUP"
|
|
681
|
+
fi
|
|
682
|
+
;;
|
|
683
|
+
esac
|
|
684
|
+
|
|
685
|
+
# ----- step 3: docs templates --------------------------------------------
|
|
686
|
+
|
|
687
|
+
log "Step 3/4: docs templates → docs/"
|
|
688
|
+
mkdir_if_real "$TARGET_ABS/docs"
|
|
689
|
+
mkdir_if_real "$TARGET_ABS/docs/specs"
|
|
690
|
+
|
|
691
|
+
for tpl in CURRENT_WORK REMAINING_TASKS PLANS TASKS INDEX; do
|
|
692
|
+
src="$CORE_ROOT/docs-templates/$tpl.md"
|
|
693
|
+
dest="$TARGET_ABS/docs/$tpl.md"
|
|
694
|
+
[ -f "$src" ] || continue
|
|
695
|
+
if [ -f "$dest" ]; then
|
|
696
|
+
log " $dest exists — leaving in place"
|
|
697
|
+
continue
|
|
698
|
+
fi
|
|
699
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
700
|
+
log "would copy $src -> $dest"
|
|
701
|
+
else
|
|
702
|
+
/bin/cp "$src" "$dest"
|
|
703
|
+
record_emit "docs/$tpl.md" "core/docs-templates/$tpl.md" ""
|
|
704
|
+
fi
|
|
705
|
+
done
|
|
706
|
+
|
|
707
|
+
if [ -f "$CORE_ROOT/docs-templates/specs/_example.md" ]; then
|
|
708
|
+
src="$CORE_ROOT/docs-templates/specs/_example.md"
|
|
709
|
+
dest="$TARGET_ABS/docs/specs/_example.md"
|
|
710
|
+
if [ ! -f "$dest" ]; then
|
|
711
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
712
|
+
log "would copy $src -> $dest"
|
|
713
|
+
else
|
|
714
|
+
/bin/cp "$src" "$dest"
|
|
715
|
+
record_emit "docs/specs/_example.md" "core/docs-templates/specs/_example.md" ""
|
|
716
|
+
fi
|
|
717
|
+
fi
|
|
718
|
+
fi
|
|
719
|
+
|
|
720
|
+
# ----- step 4: skip notice -----------------------------------------------
|
|
721
|
+
|
|
722
|
+
log "Step 4/4: skipped layers (Copilot has no equivalent)"
|
|
723
|
+
log " - core/roles/ → SKIP (no sub-agent dispatch)"
|
|
724
|
+
log " - core/hooks/ → SKIP except Reflector hook (--recipes=self-improvement, ADR-032; other guards Claude-only, ADR-034)"
|
|
725
|
+
log " - hookify-templates/ → SKIP (Claude Code plugin only)"
|
|
726
|
+
|
|
727
|
+
finalize_manifest
|
|
728
|
+
|
|
729
|
+
# ----- summary -----------------------------------------------------------
|
|
730
|
+
|
|
731
|
+
echo ""
|
|
732
|
+
echo "========================================================"
|
|
733
|
+
echo " Done."
|
|
734
|
+
echo " Target: $TARGET_ABS"
|
|
735
|
+
if [ "$PER_RULE" = "true" ]; then
|
|
736
|
+
echo " Universal rules: 5 → .github/instructions/*.instructions.md (per-rule mode)"
|
|
737
|
+
else
|
|
738
|
+
echo " Universal rules: 5 → .github/copilot-instructions.md (single-file mode)"
|
|
739
|
+
fi
|
|
740
|
+
echo " Recipes installed: ${RECIPES:-(none)}"
|
|
741
|
+
echo " Skipped (Copilot incompatibility): roles, hooks, hookify-templates"
|
|
742
|
+
echo ""
|
|
743
|
+
echo " Activation:"
|
|
744
|
+
echo " GitHub Copilot reads .github/copilot-instructions.md and .github/instructions/"
|
|
745
|
+
echo " automatically — no restart required. The same files work in:"
|
|
746
|
+
echo " - VS Code (Copilot extension)"
|
|
747
|
+
echo " - Cursor (Copilot extension)"
|
|
748
|
+
echo " - Windsurf (Copilot adapter)"
|
|
749
|
+
echo " - JetBrains IDEs (Copilot plugin)"
|
|
750
|
+
echo " - Neovim (copilot.vim)"
|
|
751
|
+
echo "========================================================"
|
|
752
|
+
echo ""
|
|
753
|
+
echo "Next steps for the project:"
|
|
754
|
+
echo " 1. Open the project in any IDE with Copilot enabled."
|
|
755
|
+
echo " 2. Edit docs/CURRENT_WORK.md with the project's current state."
|
|
756
|
+
echo " 3. Configure Copilot PR review at the repo level (Stage B analog)."
|
|
757
|
+
echo " 4. Verify: cat $TARGET_ABS/.github/copilot-instructions.md | head -30"
|