reins-method 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ADAPTERS.md +117 -0
- package/HISTORIC_MODE.md +52 -0
- package/LICENSE +21 -0
- package/MIGRATION.md +82 -0
- package/README.md +332 -0
- package/SKILLS.md +111 -0
- package/agents/README.md +61 -0
- package/bin/reins +750 -0
- package/core/evaluation/README.md +134 -0
- package/core/evaluation/templates/monthly.md +121 -0
- package/core/evaluation/templates/task-entry.md +20 -0
- package/core/skills/code-review/SKILL.md +115 -0
- package/core/skills/party-mode/SKILL.md +76 -0
- package/core/skills/reins-business-analyst/SKILL.md +51 -0
- package/core/skills/reins-product-manager/SKILL.md +50 -0
- package/core/skills/reins-senior-engineer/SKILL.md +51 -0
- package/core/skills/reins-system-architect/SKILL.md +48 -0
- package/core/skills/reins-technical-writer/SKILL.md +47 -0
- package/core/skills/reins-ux-designer/SKILL.md +48 -0
- package/core/skills/skill-creator/SKILL.md +112 -0
- package/core/templates/adapter.md +94 -0
- package/core/templates/context.md +42 -0
- package/core/templates/current_task.md +37 -0
- package/core/templates/plan.md +32 -0
- package/core/templates/skill.md +67 -0
- package/core/templates/spec.md +62 -0
- package/core/workflow/1_orchestrator.md +224 -0
- package/core/workflow/2_new_task.md +133 -0
- package/core/workflow/3_implement.md +118 -0
- package/core/workflow/4_close_task.md +166 -0
- package/package.json +30 -0
- package/tools/installer/cli.js +231 -0
package/bin/reins
ADDED
|
@@ -0,0 +1,750 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# REINS Method CLI — install, update, and manage the global REINS workflow.
|
|
3
|
+
# POSIX-ish bash, no Node/Python runtime required.
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
REINS_HOME="${REINS_HOME:-$HOME/.reins}"
|
|
8
|
+
CORE_DIR="$REINS_HOME/core"
|
|
9
|
+
USER_DIR="$REINS_HOME/user"
|
|
10
|
+
AGENTS_DIR="$REINS_HOME/agents"
|
|
11
|
+
CONFIG="$USER_DIR/config.yaml"
|
|
12
|
+
|
|
13
|
+
# ---------------------------------------------------------------------------
|
|
14
|
+
# helpers
|
|
15
|
+
# ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
log() { printf '%s\n' "$*"; }
|
|
18
|
+
err() { printf 'Error: %s\n' "$*" >&2; }
|
|
19
|
+
die() { err "$*"; exit 1; }
|
|
20
|
+
|
|
21
|
+
config_get() {
|
|
22
|
+
# config_get <key> -> prints scalar value for "key: value" line, empty if absent
|
|
23
|
+
[ -f "$CONFIG" ] || return 0
|
|
24
|
+
grep -E "^${1}:" "$CONFIG" 2>/dev/null | head -n1 | sed -E "s/^${1}:[[:space:]]*//"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
config_set() {
|
|
28
|
+
# config_set <key> <value> -> upsert "key: value" line
|
|
29
|
+
local key="$1" value="$2"
|
|
30
|
+
mkdir -p "$USER_DIR"
|
|
31
|
+
touch "$CONFIG"
|
|
32
|
+
if grep -qE "^${key}:" "$CONFIG"; then
|
|
33
|
+
sed -i.bak -E "s|^${key}:.*|${key}: ${value}|" "$CONFIG" && rm -f "${CONFIG}.bak"
|
|
34
|
+
else
|
|
35
|
+
printf '%s: %s\n' "$key" "$value" >> "$CONFIG"
|
|
36
|
+
fi
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
config_adapters() {
|
|
40
|
+
# prints one adapter name per line, from the "adapters:" YAML list
|
|
41
|
+
[ -f "$CONFIG" ] || return 0
|
|
42
|
+
awk '/^adapters:/{found=1; next} /^[a-zA-Z]/{found=0} found && /^[[:space:]]*-/{gsub(/^[[:space:]]*-[[:space:]]*/,""); print}' "$CONFIG"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
ensure_managed_block() {
|
|
46
|
+
# ensure_managed_block <target-file> <content-line>
|
|
47
|
+
# Inserts/replaces a REINS-managed block in <target-file> without touching
|
|
48
|
+
# the rest of the file's content.
|
|
49
|
+
local target="$1" content="$2"
|
|
50
|
+
local begin="<!-- REINS:BEGIN -->" end="<!-- REINS:END -->"
|
|
51
|
+
mkdir -p "$(dirname "$target")"
|
|
52
|
+
touch "$target"
|
|
53
|
+
if grep -qF "$begin" "$target"; then
|
|
54
|
+
awk -v b="$begin" -v e="$end" -v c="$content" '
|
|
55
|
+
$0 == b {print; print c; skip=1; next}
|
|
56
|
+
$0 == e {print; skip=0; next}
|
|
57
|
+
skip {next}
|
|
58
|
+
{print}
|
|
59
|
+
' "$target" > "$target.tmp" && mv "$target.tmp" "$target"
|
|
60
|
+
else
|
|
61
|
+
{
|
|
62
|
+
printf '\n%s\n' "$begin"
|
|
63
|
+
printf '%s\n' "$content"
|
|
64
|
+
printf '%s\n' "$end"
|
|
65
|
+
} >> "$target"
|
|
66
|
+
fi
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
remove_managed_block() {
|
|
70
|
+
# remove_managed_block <target-file>
|
|
71
|
+
# Strips the REINS-managed block (and the blank line preceding it) from
|
|
72
|
+
# <target-file>, if present. No-op if the file or block doesn't exist.
|
|
73
|
+
local target="$1"
|
|
74
|
+
local begin="<!-- REINS:BEGIN -->" end="<!-- REINS:END -->"
|
|
75
|
+
[ -f "$target" ] || return 0
|
|
76
|
+
grep -qF "$begin" "$target" || return 0
|
|
77
|
+
awk -v b="$begin" -v e="$end" '
|
|
78
|
+
$0 == b {skip=1; if (out ~ /\n\n$/) { sub(/\n$/, "", out) }; next}
|
|
79
|
+
$0 == e {skip=0; next}
|
|
80
|
+
skip {next}
|
|
81
|
+
{out = out $0 "\n"}
|
|
82
|
+
END {printf "%s", out}
|
|
83
|
+
' "$target" > "$target.tmp" && mv "$target.tmp" "$target"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
agent_bridge_target() {
|
|
87
|
+
# agent_bridge_target <agent> -> prints "<bridge-file>:<native-target>:<import-syntax>"
|
|
88
|
+
case "$1" in
|
|
89
|
+
claude-code) echo "$AGENTS_DIR/CLAUDE.md:$HOME/.claude/CLAUDE.md:import" ;;
|
|
90
|
+
gemini) echo "$AGENTS_DIR/GEMINI.md:$HOME/.gemini/GEMINI.md:import" ;;
|
|
91
|
+
copilot) echo "$AGENTS_DIR/copilot-instructions.md:$HOME/.copilot/instructions.md:inline" ;;
|
|
92
|
+
codex) echo "$AGENTS_DIR/AGENTS.md:$HOME/.codex/AGENTS.md:inline" ;;
|
|
93
|
+
aider|opencode|other)
|
|
94
|
+
echo "$AGENTS_DIR/AGENTS.md:$HOME/AGENTS.md:inline" ;;
|
|
95
|
+
*) return 1 ;;
|
|
96
|
+
esac
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
wire_block() {
|
|
100
|
+
# wire_block <bridge-file> <native-target> <import|inline> -> ensure_managed_block with the right content
|
|
101
|
+
local bridge="$1" native="$2" syntax="$3"
|
|
102
|
+
case "$syntax" in
|
|
103
|
+
import) ensure_managed_block "$native" "@${bridge}" ;;
|
|
104
|
+
inline) ensure_managed_block "$native" "$(printf 'See %s for the full REINS Method instructions (generated by `reins update`).' "$bridge")" ;;
|
|
105
|
+
esac
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
wire_all_agents() {
|
|
109
|
+
# Wires the REINS managed block into every AI agent detected on this machine
|
|
110
|
+
# (its config directory already exists), plus the generic ~/AGENTS.md
|
|
111
|
+
# fallback for Aider/OpenCode/Cursor/other — so skills and instructions are
|
|
112
|
+
# available no matter which agent you're using, without per-agent installs.
|
|
113
|
+
local wired=()
|
|
114
|
+
|
|
115
|
+
if [ -d "$HOME/.claude" ]; then
|
|
116
|
+
wire_block "$AGENTS_DIR/CLAUDE.md" "$HOME/.claude/CLAUDE.md" import
|
|
117
|
+
wired+=("Claude Code -> $HOME/.claude/CLAUDE.md")
|
|
118
|
+
fi
|
|
119
|
+
if [ -d "$HOME/.gemini" ]; then
|
|
120
|
+
wire_block "$AGENTS_DIR/GEMINI.md" "$HOME/.gemini/GEMINI.md" import
|
|
121
|
+
wired+=("Gemini CLI -> $HOME/.gemini/GEMINI.md")
|
|
122
|
+
fi
|
|
123
|
+
if [ -d "$HOME/.copilot" ]; then
|
|
124
|
+
wire_block "$AGENTS_DIR/copilot-instructions.md" "$HOME/.copilot/instructions.md" inline
|
|
125
|
+
wired+=("GitHub Copilot CLI -> $HOME/.copilot/instructions.md")
|
|
126
|
+
fi
|
|
127
|
+
if [ -d "$HOME/.codex" ]; then
|
|
128
|
+
wire_block "$AGENTS_DIR/AGENTS.md" "$HOME/.codex/AGENTS.md" inline
|
|
129
|
+
wired+=("Codex CLI -> $HOME/.codex/AGENTS.md")
|
|
130
|
+
fi
|
|
131
|
+
# Generic fallback — Aider/OpenCode/Cursor/other share a plain ~/AGENTS.md
|
|
132
|
+
# with no dedicated config directory to detect, so always wire it.
|
|
133
|
+
wire_block "$AGENTS_DIR/AGENTS.md" "$HOME/AGENTS.md" inline
|
|
134
|
+
wired+=("Aider / OpenCode / Cursor / other -> $HOME/AGENTS.md")
|
|
135
|
+
|
|
136
|
+
local w
|
|
137
|
+
for w in "${wired[@]}"; do
|
|
138
|
+
log "Wired: $w"
|
|
139
|
+
done
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
unwire_all_agents() {
|
|
143
|
+
# Removes the REINS managed block from every native config it may have been
|
|
144
|
+
# wired into (no-op for files without a managed block).
|
|
145
|
+
local t
|
|
146
|
+
for t in "$HOME/.claude/CLAUDE.md" "$HOME/.gemini/GEMINI.md" \
|
|
147
|
+
"$HOME/.copilot/instructions.md" "$HOME/.codex/AGENTS.md" \
|
|
148
|
+
"$HOME/AGENTS.md"; do
|
|
149
|
+
if [ -f "$t" ] && grep -qF "REINS:BEGIN" "$t" 2>/dev/null; then
|
|
150
|
+
remove_managed_block "$t"
|
|
151
|
+
log "Removed REINS block from $t"
|
|
152
|
+
fi
|
|
153
|
+
done
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
collect_skills() {
|
|
157
|
+
# prints "<skill-dir>\t<reins-managed-name>" for every SKILL.md found under
|
|
158
|
+
# core/skills, user/skills, and active adapters' skills/
|
|
159
|
+
#
|
|
160
|
+
# Every registered name carries the "reins-" prefix exactly once. Skill
|
|
161
|
+
# directories that already start with "reins-" (the convention for core/user
|
|
162
|
+
# skills) are used as-is; older, unprefixed directory names still get
|
|
163
|
+
# "reins-" prepended for backwards compatibility.
|
|
164
|
+
local d name
|
|
165
|
+
for d in "$CORE_DIR"/skills/*/; do
|
|
166
|
+
[ -f "${d}SKILL.md" ] || continue
|
|
167
|
+
name="$(basename "$d")"
|
|
168
|
+
case "$name" in
|
|
169
|
+
reins-*) printf '%s\t%s\n' "${d%/}" "$name" ;;
|
|
170
|
+
*) printf '%s\treins-%s\n' "${d%/}" "$name" ;;
|
|
171
|
+
esac
|
|
172
|
+
done
|
|
173
|
+
for d in "$USER_DIR"/skills/*/; do
|
|
174
|
+
[ -f "${d}SKILL.md" ] || continue
|
|
175
|
+
name="$(basename "$d")"
|
|
176
|
+
case "$name" in
|
|
177
|
+
reins-*) printf '%s\t%s\n' "${d%/}" "$name" ;;
|
|
178
|
+
*) printf '%s\treins-%s\n' "${d%/}" "$name" ;;
|
|
179
|
+
esac
|
|
180
|
+
done
|
|
181
|
+
while IFS= read -r adapter; do
|
|
182
|
+
[ -n "$adapter" ] || continue
|
|
183
|
+
for d in "$USER_DIR/adapters/$adapter/skills"/*/; do
|
|
184
|
+
[ -f "${d}SKILL.md" ] || continue
|
|
185
|
+
name="$(basename "$d")"
|
|
186
|
+
case "$name" in
|
|
187
|
+
reins-*) name="${name#reins-}" ;;
|
|
188
|
+
esac
|
|
189
|
+
printf '%s\treins-%s-%s\n' "${d%/}" "$adapter" "$name"
|
|
190
|
+
done
|
|
191
|
+
done < <(config_adapters)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
skill_frontmatter() {
|
|
195
|
+
# skill_frontmatter <SKILL.md> <key> -> prints a one-line value for "key: value"
|
|
196
|
+
# or "key: >" (folded scalar) frontmatter fields, empty if absent
|
|
197
|
+
local file="$1" key="$2"
|
|
198
|
+
awk -v k="$key" '
|
|
199
|
+
/^---$/ { fm++; next }
|
|
200
|
+
fm != 1 { next }
|
|
201
|
+
$0 ~ "^"k":" {
|
|
202
|
+
line=$0
|
|
203
|
+
sub("^"k":[[:space:]]*", "", line)
|
|
204
|
+
sub(/^>[[:space:]]*/, "", line)
|
|
205
|
+
if (line != "") { print line; found=1; exit }
|
|
206
|
+
found=1; next
|
|
207
|
+
}
|
|
208
|
+
found && /^[[:space:]]+[^[:space:]]/ {
|
|
209
|
+
line=$0
|
|
210
|
+
sub(/^[[:space:]]+/, "", line)
|
|
211
|
+
print line
|
|
212
|
+
exit
|
|
213
|
+
}
|
|
214
|
+
found { exit }
|
|
215
|
+
' "$file"
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
cleanup_claude_skill_links() {
|
|
219
|
+
local claude_skills="$HOME/.claude/skills"
|
|
220
|
+
[ -d "$claude_skills" ] || return 0
|
|
221
|
+
find "$claude_skills" -maxdepth 1 -type l -name 'reins-*' -exec rm -f {} +
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
sync_skills() {
|
|
225
|
+
# Refreshes ~/.claude/skills/reins-* symlinks (if Claude Code is installed) so
|
|
226
|
+
# Claude Code's native Agent Skills discovery picks up REINS skills directly —
|
|
227
|
+
# regardless of which agent is configured as primary.
|
|
228
|
+
cleanup_claude_skill_links
|
|
229
|
+
|
|
230
|
+
[ -d "$HOME/.claude" ] || return 0
|
|
231
|
+
|
|
232
|
+
local claude_skills="$HOME/.claude/skills"
|
|
233
|
+
mkdir -p "$claude_skills"
|
|
234
|
+
while IFS=$'\t' read -r dir reins_name; do
|
|
235
|
+
[ -n "$dir" ] || continue
|
|
236
|
+
ln -sf "$dir" "$claude_skills/$reins_name"
|
|
237
|
+
done < <(collect_skills)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
generate_bridges() {
|
|
241
|
+
# Regenerate ~/.reins/agents/*.md from core + user content, then wire native configs.
|
|
242
|
+
mkdir -p "$AGENTS_DIR"
|
|
243
|
+
local agent
|
|
244
|
+
agent="$(config_get agent)"
|
|
245
|
+
[ -n "$agent" ] || { err "no agent configured — run 'reins install'"; return 1; }
|
|
246
|
+
|
|
247
|
+
local active_adapters
|
|
248
|
+
active_adapters="$(config_adapters | tr '\n' ' ')"
|
|
249
|
+
local historic
|
|
250
|
+
historic="$(config_get historic_mode)"
|
|
251
|
+
|
|
252
|
+
local skills_md=""
|
|
253
|
+
while IFS=$'\t' read -r dir reins_name; do
|
|
254
|
+
[ -n "$dir" ] || continue
|
|
255
|
+
local sname sdesc
|
|
256
|
+
sname="$(skill_frontmatter "$dir/SKILL.md" name)"
|
|
257
|
+
sdesc="$(skill_frontmatter "$dir/SKILL.md" description)"
|
|
258
|
+
skills_md="${skills_md}- **${sname:-$reins_name}** (\`$dir\`): ${sdesc:-(no description)}
|
|
259
|
+
"
|
|
260
|
+
done < <(collect_skills)
|
|
261
|
+
|
|
262
|
+
local body
|
|
263
|
+
body=$(cat <<EOF
|
|
264
|
+
# REINS Method — Agent Bridge
|
|
265
|
+
|
|
266
|
+
This file is generated by \`reins update\` / \`reins install\`. Do not edit by hand —
|
|
267
|
+
edit \`~/.reins/user/config.yaml\` and re-run \`reins update\` instead.
|
|
268
|
+
|
|
269
|
+
## Always read first
|
|
270
|
+
- @${CORE_DIR}/workflow/1_orchestrator.md
|
|
271
|
+
|
|
272
|
+
## User standards
|
|
273
|
+
- @${USER_DIR}/standards/company.md
|
|
274
|
+
- @${USER_DIR}/standards/personal.md
|
|
275
|
+
|
|
276
|
+
## Active adapters
|
|
277
|
+
${active_adapters:-(none configured — generic conventions only)}
|
|
278
|
+
|
|
279
|
+
## Historic mode
|
|
280
|
+
${historic:-off}
|
|
281
|
+
|
|
282
|
+
## Available skills
|
|
283
|
+
On-demand only — never load proactively (see \`1_orchestrator.md\` §5).
|
|
284
|
+
${skills_md:-(none)}
|
|
285
|
+
EOF
|
|
286
|
+
)
|
|
287
|
+
printf '%s\n' "$body" > "$AGENTS_DIR/CLAUDE.md"
|
|
288
|
+
printf '%s\n' "$body" > "$AGENTS_DIR/GEMINI.md"
|
|
289
|
+
printf '%s\n' "$body" > "$AGENTS_DIR/AGENTS.md"
|
|
290
|
+
printf '%s\n' "$body" > "$AGENTS_DIR/copilot-instructions.md"
|
|
291
|
+
|
|
292
|
+
wire_all_agents
|
|
293
|
+
sync_skills
|
|
294
|
+
|
|
295
|
+
log "Bridge files regenerated in $AGENTS_DIR"
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
# ---------------------------------------------------------------------------
|
|
299
|
+
# commands
|
|
300
|
+
# ---------------------------------------------------------------------------
|
|
301
|
+
|
|
302
|
+
cmd_install() {
|
|
303
|
+
local non_interactive=0
|
|
304
|
+
local arg_agent="" arg_standards="" arg_historic="" arg_adapter=""
|
|
305
|
+
for arg in "$@"; do
|
|
306
|
+
case "$arg" in
|
|
307
|
+
--non-interactive) non_interactive=1 ;;
|
|
308
|
+
--agent=*) arg_agent="${arg#--agent=}" ;;
|
|
309
|
+
--standards=*) arg_standards="${arg#--standards=}" ;;
|
|
310
|
+
--historic=*) arg_historic="${arg#--historic=}" ;;
|
|
311
|
+
--adapter=*) arg_adapter="${arg#--adapter=}" ;;
|
|
312
|
+
esac
|
|
313
|
+
done
|
|
314
|
+
|
|
315
|
+
# When run via `curl | bash`, stdin is the piped script, not a terminal, so
|
|
316
|
+
# `read -p` below would fail instantly. Re-attach stdin to the controlling
|
|
317
|
+
# terminal if one is available so the wizard can still prompt interactively.
|
|
318
|
+
if [ "$non_interactive" -eq 0 ] && [ ! -t 0 ] && [ -e /dev/tty ]; then
|
|
319
|
+
exec < /dev/tty
|
|
320
|
+
fi
|
|
321
|
+
|
|
322
|
+
log "Welcome to REINS Method"
|
|
323
|
+
log ""
|
|
324
|
+
|
|
325
|
+
if [ -f "$CONFIG" ] && [ "$non_interactive" -eq 0 ]; then
|
|
326
|
+
read -r -p "REINS is already configured at $REINS_HOME. Re-run the wizard? [y/N] " yn
|
|
327
|
+
case "$yn" in [yY]*) ;; *) log "Aborted."; return 0 ;; esac
|
|
328
|
+
fi
|
|
329
|
+
|
|
330
|
+
mkdir -p "$USER_DIR"/{standards,adapters,skills,projects,historic}
|
|
331
|
+
|
|
332
|
+
local agent
|
|
333
|
+
if [ "$non_interactive" -eq 1 ]; then
|
|
334
|
+
agent="${arg_agent:-other}"
|
|
335
|
+
else
|
|
336
|
+
log "? What's your primary AI Coding Agent?"
|
|
337
|
+
log " (REINS wires every agent it detects on this machine automatically — this"
|
|
338
|
+
log " choice just sets your default for 'reins status'/'reins doctor'.)"
|
|
339
|
+
log " 1) Claude Code"
|
|
340
|
+
log " 2) GitHub Copilot CLI"
|
|
341
|
+
log " 3) Codex CLI"
|
|
342
|
+
log " 4) Gemini CLI"
|
|
343
|
+
log " 5) Aider"
|
|
344
|
+
log " 6) OpenCode / Cursor / Other"
|
|
345
|
+
read -r -p "Choice [1-6]: " agent_choice
|
|
346
|
+
case "$agent_choice" in
|
|
347
|
+
1) agent="claude-code" ;;
|
|
348
|
+
2) agent="copilot" ;;
|
|
349
|
+
3) agent="codex" ;;
|
|
350
|
+
4) agent="gemini" ;;
|
|
351
|
+
5) agent="aider" ;;
|
|
352
|
+
*) agent="other" ;;
|
|
353
|
+
esac
|
|
354
|
+
fi
|
|
355
|
+
config_set agent "$agent"
|
|
356
|
+
|
|
357
|
+
touch "$USER_DIR/standards/company.md" "$USER_DIR/standards/personal.md"
|
|
358
|
+
if [ ! -s "$USER_DIR/standards/company.md" ]; then
|
|
359
|
+
cat > "$USER_DIR/standards/company.md" <<'EOF'
|
|
360
|
+
---
|
|
361
|
+
scope: always
|
|
362
|
+
precedence: 1 # floor — non-negotiable
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
# Company / Project Standards
|
|
366
|
+
|
|
367
|
+
Fill this in with the non-negotiable conventions for your company or project.
|
|
368
|
+
This file is read by the orchestrator on every session.
|
|
369
|
+
EOF
|
|
370
|
+
fi
|
|
371
|
+
if [ ! -s "$USER_DIR/standards/personal.md" ]; then
|
|
372
|
+
cat > "$USER_DIR/standards/personal.md" <<'EOF'
|
|
373
|
+
---
|
|
374
|
+
scope: always
|
|
375
|
+
precedence: 2 # personal style — must never conflict with company.md
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
# Personal Standards
|
|
379
|
+
|
|
380
|
+
Fill this in with your personal style preferences on top of company.md.
|
|
381
|
+
If anything here conflicts with company.md, the orchestrator will stop and ask.
|
|
382
|
+
EOF
|
|
383
|
+
fi
|
|
384
|
+
if [ "$non_interactive" -eq 1 ]; then
|
|
385
|
+
# The Node wizard (npx reins-method install) handles opening an editor
|
|
386
|
+
# for company.md itself, after this script returns — it has the TTY.
|
|
387
|
+
:
|
|
388
|
+
else
|
|
389
|
+
read -r -p "? Do you have company code standards to configure? [y/N] " yn
|
|
390
|
+
case "$yn" in
|
|
391
|
+
[yY]*)
|
|
392
|
+
"${EDITOR:-vi}" "$USER_DIR/standards/company.md"
|
|
393
|
+
;;
|
|
394
|
+
esac
|
|
395
|
+
fi
|
|
396
|
+
|
|
397
|
+
if [ "$non_interactive" -eq 1 ]; then
|
|
398
|
+
case "$arg_historic" in
|
|
399
|
+
on) cmd_historic on --quiet ;;
|
|
400
|
+
*) config_set historic_mode off ;;
|
|
401
|
+
esac
|
|
402
|
+
else
|
|
403
|
+
read -r -p "? Do you want to enable Historic Mode (performance tracking)? [y/N] " yn
|
|
404
|
+
case "$yn" in
|
|
405
|
+
[yY]*) cmd_historic on --quiet ;;
|
|
406
|
+
*) config_set historic_mode off ;;
|
|
407
|
+
esac
|
|
408
|
+
fi
|
|
409
|
+
|
|
410
|
+
local adapter_path
|
|
411
|
+
if [ "$non_interactive" -eq 1 ]; then
|
|
412
|
+
adapter_path="$arg_adapter"
|
|
413
|
+
else
|
|
414
|
+
read -r -p "? Install an adapter pack now? Path to local pack (or leave empty to skip): " adapter_path
|
|
415
|
+
fi
|
|
416
|
+
if [ -n "$adapter_path" ] && [ -d "$adapter_path" ]; then
|
|
417
|
+
local adapter_name
|
|
418
|
+
adapter_name="$(basename "$adapter_path")"
|
|
419
|
+
mkdir -p "$USER_DIR/adapters"
|
|
420
|
+
cp -R "$adapter_path" "$USER_DIR/adapters/$adapter_name"
|
|
421
|
+
if grep -qE "^adapters:" "$CONFIG" 2>/dev/null; then
|
|
422
|
+
printf ' - %s\n' "$adapter_name" >> "$CONFIG"
|
|
423
|
+
else
|
|
424
|
+
printf 'adapters:\n - %s\n' "$adapter_name" >> "$CONFIG"
|
|
425
|
+
fi
|
|
426
|
+
log "Adapter '$adapter_name' installed."
|
|
427
|
+
fi
|
|
428
|
+
|
|
429
|
+
generate_bridges
|
|
430
|
+
|
|
431
|
+
# Make `reins` available on PATH
|
|
432
|
+
local target_bin="/usr/local/bin/reins"
|
|
433
|
+
if [ -w "$(dirname "$target_bin")" ] 2>/dev/null; then
|
|
434
|
+
ln -sf "$REINS_HOME/bin/reins" "$target_bin" 2>/dev/null || true
|
|
435
|
+
fi
|
|
436
|
+
if [ ! -x "$target_bin" ]; then
|
|
437
|
+
for rc in "$HOME/.zshrc" "$HOME/.bashrc"; do
|
|
438
|
+
[ -f "$rc" ] || continue
|
|
439
|
+
grep -qF "$REINS_HOME/bin" "$rc" 2>/dev/null || \
|
|
440
|
+
printf '\nexport PATH="%s/bin:$PATH"\n' "$REINS_HOME" >> "$rc"
|
|
441
|
+
done
|
|
442
|
+
fi
|
|
443
|
+
|
|
444
|
+
log ""
|
|
445
|
+
log "REINS Method installed to $REINS_HOME"
|
|
446
|
+
log "Run: source ~/.zshrc (or restart your terminal) to activate the 'reins' command"
|
|
447
|
+
log ""
|
|
448
|
+
log "Optional companion tools (see README.md 'Companion tools'):"
|
|
449
|
+
log " - headroom: token-efficient context compression (pip install \"headroom-ai[all]\")"
|
|
450
|
+
log " - graphify: codebase knowledge graph for richer architecture context (pip install graphifyy)"
|
|
451
|
+
log ""
|
|
452
|
+
log "Getting started: reins status"
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
cmd_update() {
|
|
456
|
+
if [ -d "$REINS_HOME/.git" ]; then
|
|
457
|
+
if ! git -C "$REINS_HOME" pull --ff-only; then
|
|
458
|
+
err "git pull failed — if this checkout has no upstream tracking branch, run:"
|
|
459
|
+
err " git -C $REINS_HOME branch --set-upstream-to=<remote>/<branch>"
|
|
460
|
+
generate_bridges
|
|
461
|
+
return 1
|
|
462
|
+
fi
|
|
463
|
+
else
|
|
464
|
+
die "$REINS_HOME is not a git checkout. Re-run install.sh to update, or 'git init' it manually."
|
|
465
|
+
fi
|
|
466
|
+
generate_bridges
|
|
467
|
+
log "REINS core updated. ~/.reins/user/ was not touched."
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
cmd_new_adapter() {
|
|
471
|
+
local name="${1:-}"
|
|
472
|
+
[ -n "$name" ] || die "usage: reins new-adapter <name>"
|
|
473
|
+
local dir="$USER_DIR/adapters/$name"
|
|
474
|
+
[ -e "$dir" ] && die "$dir already exists"
|
|
475
|
+
mkdir -p "$dir"/{standards,workflow,skills}
|
|
476
|
+
cat > "$dir/ADAPTER.md" <<EOF
|
|
477
|
+
---
|
|
478
|
+
name: $name
|
|
479
|
+
stacks: [] # e.g. [ruby], [node], [python] — see core/workflow/1_orchestrator.md §2
|
|
480
|
+
author: $(whoami)
|
|
481
|
+
version: 0.1.0
|
|
482
|
+
description: >
|
|
483
|
+
Describe what this adapter is for and what conventions/skills it adds.
|
|
484
|
+
---
|
|
485
|
+
EOF
|
|
486
|
+
cat > "$dir/standards/floor.md" <<'EOF'
|
|
487
|
+
---
|
|
488
|
+
scope: always
|
|
489
|
+
precedence: 1 # floor — non-negotiable
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
# Standards — Floor
|
|
493
|
+
|
|
494
|
+
Non-negotiable conventions for this stack.
|
|
495
|
+
EOF
|
|
496
|
+
log "Adapter scaffolded at $dir"
|
|
497
|
+
log "Edit ADAPTER.md (set 'stacks'), then standards/floor.md."
|
|
498
|
+
if grep -qE "^adapters:" "$CONFIG" 2>/dev/null; then
|
|
499
|
+
printf ' - %s\n' "$name" >> "$CONFIG"
|
|
500
|
+
else
|
|
501
|
+
printf 'adapters:\n - %s\n' "$name" >> "$CONFIG"
|
|
502
|
+
fi
|
|
503
|
+
generate_bridges
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
cmd_new_skill() {
|
|
507
|
+
local name="${1:-}"
|
|
508
|
+
[ -n "$name" ] || die "usage: reins new-skill <name>"
|
|
509
|
+
local dir="$USER_DIR/skills/$name"
|
|
510
|
+
[ -e "$dir" ] && die "$dir already exists"
|
|
511
|
+
mkdir -p "$dir"
|
|
512
|
+
cat > "$dir/SKILL.md" <<EOF
|
|
513
|
+
---
|
|
514
|
+
name: $name
|
|
515
|
+
description: TODO — what this skill does and when to use it.
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
# $name
|
|
519
|
+
|
|
520
|
+
## Trigger
|
|
521
|
+
TODO
|
|
522
|
+
|
|
523
|
+
## Context
|
|
524
|
+
TODO
|
|
525
|
+
|
|
526
|
+
## Steps
|
|
527
|
+
TODO
|
|
528
|
+
|
|
529
|
+
## Output
|
|
530
|
+
TODO
|
|
531
|
+
EOF
|
|
532
|
+
log "Skill scaffolded at $dir/SKILL.md — fill it in (see core/templates/skill.md)."
|
|
533
|
+
generate_bridges >/dev/null
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
cmd_sync() {
|
|
537
|
+
generate_bridges
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
cmd_link_agents() {
|
|
541
|
+
# Wire newly-installed AI agents into the existing bridge files without a
|
|
542
|
+
# full bridge regeneration — handy after installing a new agent on this
|
|
543
|
+
# machine (e.g. you set up Gemini CLI after running `reins install`).
|
|
544
|
+
[ -d "$AGENTS_DIR" ] && [ -n "$(ls -A "$AGENTS_DIR" 2>/dev/null)" ] || { err "no bridge files yet — run 'reins update' or 'reins sync' first"; return 1; }
|
|
545
|
+
wire_all_agents
|
|
546
|
+
sync_skills
|
|
547
|
+
log "Done."
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
cmd_historic() {
|
|
551
|
+
local mode="${1:-}"
|
|
552
|
+
case "$mode" in
|
|
553
|
+
on)
|
|
554
|
+
mkdir -p "$USER_DIR/historic"
|
|
555
|
+
config_set historic_mode on
|
|
556
|
+
local month_file="$USER_DIR/historic/$(date +%Y-%m).md"
|
|
557
|
+
if [ ! -f "$month_file" ]; then
|
|
558
|
+
cp "$CORE_DIR/evaluation/templates/monthly.md" "$month_file"
|
|
559
|
+
[ "${2:-}" = "--quiet" ] || log "Created $month_file — fill in this period's priorities."
|
|
560
|
+
fi
|
|
561
|
+
[ "${2:-}" = "--quiet" ] || log "Historic mode: on"
|
|
562
|
+
;;
|
|
563
|
+
off)
|
|
564
|
+
config_set historic_mode off
|
|
565
|
+
log "Historic mode: off (data preserved at $USER_DIR/historic/)"
|
|
566
|
+
;;
|
|
567
|
+
*) die "usage: reins historic on|off" ;;
|
|
568
|
+
esac
|
|
569
|
+
generate_bridges >/dev/null
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
cmd_status() {
|
|
573
|
+
log "REINS_HOME: $REINS_HOME"
|
|
574
|
+
if [ -d "$REINS_HOME/.git" ]; then
|
|
575
|
+
log "Version: $(git -C "$REINS_HOME" rev-parse --short HEAD 2>/dev/null) ($(git -C "$REINS_HOME" log -1 --format=%cd --date=short 2>/dev/null))"
|
|
576
|
+
fi
|
|
577
|
+
log "Agent: $(config_get agent || echo 'not configured')"
|
|
578
|
+
log "Historic mode: $(config_get historic_mode || echo 'off')"
|
|
579
|
+
local adapters
|
|
580
|
+
adapters="$(config_adapters | tr '\n' ' ')"
|
|
581
|
+
log "Adapters: ${adapters:-none}"
|
|
582
|
+
|
|
583
|
+
if git rev-parse --show-toplevel >/dev/null 2>&1; then
|
|
584
|
+
local slug
|
|
585
|
+
slug="$(basename "$(git rev-parse --show-toplevel)")"
|
|
586
|
+
local ctx_dir="$USER_DIR/projects/$slug/contexts"
|
|
587
|
+
if [ -d "$ctx_dir" ]; then
|
|
588
|
+
local active
|
|
589
|
+
active="$(grep -lE '^status:[[:space:]]*active' "$ctx_dir"/*.md 2>/dev/null || true)"
|
|
590
|
+
if [ -n "$active" ]; then
|
|
591
|
+
log "Active context (this project): $(basename "$active")"
|
|
592
|
+
else
|
|
593
|
+
log "Active context (this project): none"
|
|
594
|
+
fi
|
|
595
|
+
fi
|
|
596
|
+
fi
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
cmd_doctor() {
|
|
600
|
+
local problems=0
|
|
601
|
+
|
|
602
|
+
[ -d "$CORE_DIR" ] || { err "missing $CORE_DIR"; problems=$((problems+1)); }
|
|
603
|
+
[ -f "$CONFIG" ] || { err "missing $CONFIG — run 'reins install'"; problems=$((problems+1)); }
|
|
604
|
+
|
|
605
|
+
local agent
|
|
606
|
+
agent="$(config_get agent)"
|
|
607
|
+
if [ -n "$agent" ]; then
|
|
608
|
+
local entry
|
|
609
|
+
if entry="$(agent_bridge_target "$agent" 2>/dev/null)"; then
|
|
610
|
+
local bridge="${entry%%:*}"; local rest="${entry#*:}"
|
|
611
|
+
local native="${rest%%:*}"
|
|
612
|
+
[ -f "$bridge" ] || { err "missing bridge file $bridge — run 'reins update'"; problems=$((problems+1)); }
|
|
613
|
+
if [ -f "$native" ]; then
|
|
614
|
+
grep -qF "REINS:BEGIN" "$native" || { err "$native is missing the REINS managed block — run 'reins update'"; problems=$((problems+1)); }
|
|
615
|
+
else
|
|
616
|
+
err "$native does not exist — run 'reins update'"; problems=$((problems+1))
|
|
617
|
+
fi
|
|
618
|
+
else
|
|
619
|
+
err "unknown agent '$agent' in config"; problems=$((problems+1))
|
|
620
|
+
fi
|
|
621
|
+
else
|
|
622
|
+
err "no agent configured — run 'reins install'"; problems=$((problems+1))
|
|
623
|
+
fi
|
|
624
|
+
|
|
625
|
+
while IFS= read -r adapter; do
|
|
626
|
+
[ -n "$adapter" ] || continue
|
|
627
|
+
local dir="$USER_DIR/adapters/$adapter"
|
|
628
|
+
[ -d "$dir" ] || { err "adapter '$adapter' listed in config but missing at $dir"; problems=$((problems+1)); }
|
|
629
|
+
[ -f "$dir/ADAPTER.md" ] || { err "adapter '$adapter' missing ADAPTER.md"; problems=$((problems+1)); }
|
|
630
|
+
done < <(config_adapters)
|
|
631
|
+
|
|
632
|
+
if [ "$problems" -eq 0 ]; then
|
|
633
|
+
log "Everything looks good."
|
|
634
|
+
else
|
|
635
|
+
log ""
|
|
636
|
+
log "$problems issue(s) found."
|
|
637
|
+
return 1
|
|
638
|
+
fi
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
cmd_uninstall() {
|
|
642
|
+
log "Uninstalling REINS Method..."
|
|
643
|
+
|
|
644
|
+
# Remove the managed block from every native config REINS may have wired
|
|
645
|
+
unwire_all_agents
|
|
646
|
+
|
|
647
|
+
# Remove REINS-managed Claude Code skill symlinks, if any
|
|
648
|
+
if [ -d "$HOME/.claude/skills" ] && find "$HOME/.claude/skills" -maxdepth 1 -type l -name 'reins-*' -print -quit | grep -q .; then
|
|
649
|
+
cleanup_claude_skill_links
|
|
650
|
+
log "Removed REINS skill symlinks from $HOME/.claude/skills"
|
|
651
|
+
fi
|
|
652
|
+
|
|
653
|
+
# Remove the /usr/local/bin/reins symlink, if it points into REINS_HOME
|
|
654
|
+
local target_bin="/usr/local/bin/reins"
|
|
655
|
+
if [ -L "$target_bin" ] && [ "$(readlink "$target_bin")" = "$REINS_HOME/bin/reins" ]; then
|
|
656
|
+
rm -f "$target_bin"
|
|
657
|
+
log "Removed $target_bin"
|
|
658
|
+
fi
|
|
659
|
+
|
|
660
|
+
# Remove the PATH export line from shell rc files
|
|
661
|
+
for rc in "$HOME/.zshrc" "$HOME/.bashrc"; do
|
|
662
|
+
[ -f "$rc" ] || continue
|
|
663
|
+
if grep -qF "$REINS_HOME/bin" "$rc" 2>/dev/null; then
|
|
664
|
+
grep -vF "export PATH=\"$REINS_HOME/bin:\$PATH\"" "$rc" > "$rc.tmp" && mv "$rc.tmp" "$rc"
|
|
665
|
+
log "Removed PATH export from $rc"
|
|
666
|
+
fi
|
|
667
|
+
done
|
|
668
|
+
|
|
669
|
+
log ""
|
|
670
|
+
log "REINS is unhooked from your AI agent and shell."
|
|
671
|
+
log "$REINS_HOME still contains your data, including \$REINS_HOME/user/ (standards,"
|
|
672
|
+
log "adapters, skills, project contexts, historic records)."
|
|
673
|
+
read -r -p "Delete $REINS_HOME entirely, including user/? This cannot be undone. [y/N] " yn
|
|
674
|
+
case "$yn" in
|
|
675
|
+
[yY]*)
|
|
676
|
+
# Offer to back up user-created skills/adapters before deleting everything
|
|
677
|
+
local has_custom=""
|
|
678
|
+
[ -d "$USER_DIR/skills" ] && find "$USER_DIR/skills" -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null | grep -q . && has_custom=1
|
|
679
|
+
[ -d "$USER_DIR/adapters" ] && find "$USER_DIR/adapters" -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null | grep -q . && has_custom=1
|
|
680
|
+
if [ -n "$has_custom" ]; then
|
|
681
|
+
read -r -p "Back up your custom skills/adapters before deleting? [y/N] " backup_yn
|
|
682
|
+
case "$backup_yn" in
|
|
683
|
+
[yY]*)
|
|
684
|
+
read -r -p "Directory to copy them to: " backup_dir
|
|
685
|
+
if [ -n "$backup_dir" ]; then
|
|
686
|
+
mkdir -p "$backup_dir"
|
|
687
|
+
[ -d "$USER_DIR/skills" ] && cp -R "$USER_DIR/skills" "$backup_dir/"
|
|
688
|
+
[ -d "$USER_DIR/adapters" ] && cp -R "$USER_DIR/adapters" "$backup_dir/"
|
|
689
|
+
log "Copied $USER_DIR/skills and $USER_DIR/adapters to $backup_dir"
|
|
690
|
+
else
|
|
691
|
+
log "No directory given — skipping backup."
|
|
692
|
+
fi
|
|
693
|
+
;;
|
|
694
|
+
esac
|
|
695
|
+
fi
|
|
696
|
+
rm -rf "$REINS_HOME"
|
|
697
|
+
log "Removed $REINS_HOME"
|
|
698
|
+
;;
|
|
699
|
+
*)
|
|
700
|
+
log "Left $REINS_HOME in place."
|
|
701
|
+
;;
|
|
702
|
+
esac
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
# ---------------------------------------------------------------------------
|
|
706
|
+
# dispatch
|
|
707
|
+
# ---------------------------------------------------------------------------
|
|
708
|
+
|
|
709
|
+
usage() {
|
|
710
|
+
cat <<'EOF'
|
|
711
|
+
REINS Method CLI
|
|
712
|
+
|
|
713
|
+
Usage:
|
|
714
|
+
reins install First-time setup (interactive wizard)
|
|
715
|
+
reins install --non-interactive --agent=<id> [--standards=yes|no]
|
|
716
|
+
[--historic=on|off] [--adapter=<path>]
|
|
717
|
+
Non-interactive setup, driven by flags
|
|
718
|
+
(used by `npx reins-method install`)
|
|
719
|
+
reins update Pull latest core, regenerate agent bridge files
|
|
720
|
+
reins new-adapter <name> Scaffold a new adapter pack
|
|
721
|
+
reins new-skill <name> Scaffold a new skill
|
|
722
|
+
reins sync Regenerate agent bridges + skill registration (no git pull)
|
|
723
|
+
reins link-agents Wire any newly-installed AI agents into existing bridges
|
|
724
|
+
reins historic on|off Enable/disable historic mode
|
|
725
|
+
reins status Show installed version, agent, adapters, historic mode
|
|
726
|
+
reins doctor Validate the installation
|
|
727
|
+
reins uninstall Unhook REINS from your agent/shell, optionally delete ~/.reins
|
|
728
|
+
EOF
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
main() {
|
|
732
|
+
local cmd="${1:-}"
|
|
733
|
+
[ -n "$cmd" ] && shift || true
|
|
734
|
+
case "$cmd" in
|
|
735
|
+
install) cmd_install "$@" ;;
|
|
736
|
+
update) cmd_update "$@" ;;
|
|
737
|
+
new-adapter) cmd_new_adapter "$@" ;;
|
|
738
|
+
new-skill) cmd_new_skill "$@" ;;
|
|
739
|
+
sync) cmd_sync "$@" ;;
|
|
740
|
+
link-agents) cmd_link_agents "$@" ;;
|
|
741
|
+
historic) cmd_historic "$@" ;;
|
|
742
|
+
status) cmd_status "$@" ;;
|
|
743
|
+
doctor) cmd_doctor "$@" ;;
|
|
744
|
+
uninstall) cmd_uninstall "$@" ;;
|
|
745
|
+
-h|--help|help|"") usage ;;
|
|
746
|
+
*) err "unknown command: $cmd"; usage; exit 1 ;;
|
|
747
|
+
esac
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
main "$@"
|