@techgoblin/gobstack 0.4.4-beta.6 → 0.4.4-beta.8
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/README.md +16 -6
- package/bin/goblin-init +152 -97
- package/bin/goblin-install +63 -5
- package/bin/goblin-lib.sh +31 -0
- package/bin/goblin-verify +22 -0
- package/docs/ADOPTION.md +9 -6
- package/docs/CONTRACTS.md +25 -13
- package/docs/GUIDE.md +17 -15
- package/docs/LIMITS.md +1 -1
- package/docs/w6-tty-redraw-pty-transcript.txt +334 -0
- package/package.json +1 -1
- package/skills/goblin-bootstrap/SKILL.md +10 -5
- package/templates/AGENTS.md.tmpl +14 -5
package/README.md
CHANGED
|
@@ -50,14 +50,17 @@ After installing, in this order:
|
|
|
50
50
|
|
|
51
51
|
cd <target> && git add -A && git commit # the install is a change like any other
|
|
52
52
|
gob verify # or .goblin/bin/goblin-verify, inside the target
|
|
53
|
-
|
|
53
|
+
gob emit --platform <p> # optional, per platform: the agent skills are an opt-in
|
|
54
54
|
gob audit # once, deliberately: the ONLY network step (SC-07)
|
|
55
55
|
|
|
56
|
-
**A class-A install
|
|
56
|
+
**A default class-A install (no agent skills — those are `gob emit`'s job) verifies green —
|
|
57
|
+
`38 passed, 0 failed, 11 advisory, 33 skipped`, exit 0 — once
|
|
57
58
|
`HANDOFF.md` names a commit that exists. Before that edit the scaffold's `0000000` placeholder is
|
|
58
|
-
the one expected red: `
|
|
59
|
-
the fix are step 2 of `docs/GUIDE.md`.**
|
|
60
|
-
|
|
59
|
+
the one expected red: `37 passed, 1 failed`, `HP-05`. Both numbers measured at W6 (neutral-first);
|
|
60
|
+
the run and the fix are step 2 of `docs/GUIDE.md`.**
|
|
61
|
+
Thirty-three rows skip — the five skill rows (`SK-01`..`SK-04`, `AU-04`) skip on the `playbooks`
|
|
62
|
+
opt-out a skills-free install records, then the not-yet rows (`HS-02` has no pinned pre-change
|
|
63
|
+
commit yet, so the REPLAY is not provable;
|
|
61
64
|
`AU-02` and `AU-03` have no report to audit; `SC-06`, `SC-07` and `SC-08` have no dependency
|
|
62
65
|
manifest, no lockfile and no audit record to read; `PF-01` has no measured perf baseline;
|
|
63
66
|
`BN-01`/`BN-02`/`BN-05` have no `src/` for a ban to read, and `BN-03` plus the four electron bans
|
|
@@ -83,7 +86,7 @@ The measurement and the vacuous-pass reading are in `docs/CONTRACTS.md`.
|
|
|
83
86
|
| `gob verify` | run the rule matrix against the current repo — `PASS`/`FAIL`/`SKIP` per row, exit 0 pass · 1 a check failed · 2 could not run · 3 the manifest is broken |
|
|
84
87
|
| `gob bans` | run the ban list (per-pattern red lines over the source tree) |
|
|
85
88
|
| `gob audit` | check recorded dependency claims against live advisory feeds — the only command that touches the network |
|
|
86
|
-
| `gob install` | install the manifest, skills
|
|
89
|
+
| `gob install` | install the harness into a target repo: manifest, verifier, gates, HANDOFF — no agent skills (those are an opt-in: `gob emit --platform <p>`, or `--skills yes`) |
|
|
87
90
|
| `gob uninstall` | remove everything an install wrote, byte-exactly (`gob install --target <dir> --uninstall` is the same job) |
|
|
88
91
|
| `gob upgrade` | migrate a repo to the shared global engine at `~/.goblin/engine` — 8 steps, two commits, one report |
|
|
89
92
|
| `gob doctor` | one run across the platforms below: DETECTED / NOT-DETECTED / DRIFT per platform |
|
|
@@ -111,6 +114,13 @@ One run of `gob emit --platform <p> --scope project` writes the skills and the c
|
|
|
111
114
|
session of that platform reads; `--scope global` writes to the machine-level anchor. `--dry-run`
|
|
112
115
|
prints the full write plan first.
|
|
113
116
|
|
|
117
|
+
**Agent skills are opt-in.** A `gob install` writes the neutral harness only — `.goblin/`,
|
|
118
|
+
`HANDOFF.md`, `AGENTS.md`, the checks and the `.gitignore` block; no skills directory, and no
|
|
119
|
+
files belonging to any coding agent. The guided path is `gob init`'s emit screen; the one-shot
|
|
120
|
+
path is `gob emit --platform <p>` after installing. Repos whose install predates the opt-in
|
|
121
|
+
default keep their skills through `gob upgrade` (the install record names them; only an explicit
|
|
122
|
+
`--skills no`, or `--uninstall`, removes them).
|
|
123
|
+
|
|
114
124
|
## What it is not
|
|
115
125
|
|
|
116
126
|
Not a rules document (every rule carries a runnable check or is explicitly counted as
|
package/bin/goblin-init
CHANGED
|
@@ -5,10 +5,13 @@
|
|
|
5
5
|
# [--branch <name>] [--email <addr>] [--gate <cmd>]
|
|
6
6
|
# [--emit <p[,p..]>] [--scope project|global] [--yes] [--dry-run]
|
|
7
7
|
#
|
|
8
|
-
# One screen per question, answered steps collapsing into the ✔/◆/○ rail above.
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
8
|
+
# One screen per question, answered steps collapsing into the ✔/◆/○ rail above. On a tty
|
|
9
|
+
# the whole wizard frame (logotype, header, answered rows, rail) is redrawn in place from
|
|
10
|
+
# a saved buffer of the wizard's own lines — never by counting printed output, which the
|
|
11
|
+
# installer/emit engines would break. Every question has a flag: with the flags set (or
|
|
12
|
+
# --yes, or a non-tty stdin) the wizard asks nothing and runs from the flags and the
|
|
13
|
+
# defaults — non-interactive parity is structural, not a mode. Over a pipe the redraw
|
|
14
|
+
# degrades to a numbered cascade (rustup shape) with zero cursor motion.
|
|
12
15
|
#
|
|
13
16
|
# The wizard is a FRONT END over the existing engines, never a second installer:
|
|
14
17
|
# .goblin/ + the payload -> bin/goblin-install (its refusal contract — an existing
|
|
@@ -47,9 +50,11 @@ VERSION=$(cat "$SRC/VERSION" 2>/dev/null || printf 'unknown')
|
|
|
47
50
|
# nearest 16-colour slot. NO_COLOR or a piped stdout turns colour off entirely.
|
|
48
51
|
TTY_OUT=0; [ -t 1 ] && TTY_OUT=1
|
|
49
52
|
TTY_IN=0; [ -t 0 ] && TTY_IN=1
|
|
50
|
-
C_ACCENT="" C_GREEN="" C_TEXT="" C_MUTED="" C_DIM="" C_RED="" C_YELLOW="" C_RESET=""
|
|
53
|
+
C_ACCENT="" C_GREEN="" C_TEXT="" C_MUTED="" C_DIM="" C_RED="" C_YELLOW="" C_RESET=""
|
|
54
|
+
# Single-accent discipline: one accent (#F54E00) + green + text + muted + dim. No bold
|
|
55
|
+
# is ever emitted — bold was a second emphasis voice and is gone from every screen.
|
|
51
56
|
if [ "$TTY_OUT" -eq 1 ] && [ -z "${NO_COLOR:-}" ]; then
|
|
52
|
-
C_RESET=$'\033[0m'
|
|
57
|
+
C_RESET=$'\033[0m'
|
|
53
58
|
case "${COLORTERM:-}" in
|
|
54
59
|
truecolor|24bit)
|
|
55
60
|
C_ACCENT=$'\033[38;2;245;78;0m'; C_GREEN=$'\033[38;2;74;222;128m'
|
|
@@ -64,41 +69,64 @@ if [ "$TTY_OUT" -eq 1 ] && [ -z "${NO_COLOR:-}" ]; then
|
|
|
64
69
|
fi
|
|
65
70
|
|
|
66
71
|
# ---------------------------------------------------------------- the rail ----
|
|
67
|
-
# 6 steps; the ✔/◆/○ vocabulary. The rail is redrawn in place on a tty
|
|
68
|
-
#
|
|
72
|
+
# 6 steps; the ✔/◆/○ vocabulary. The rail is redrawn in place on a tty from a SAVED
|
|
73
|
+
# BUFFER of the wizard's own lines (RAIL_BUF) — never by counting the output between
|
|
74
|
+
# draws, because the installer/emit engines print freely and break naive accounting.
|
|
75
|
+
# Over a pipe nothing is saved and nothing rewinds: each screen is one numbered line.
|
|
69
76
|
STEP_LABELS=("platforms" "class" "branch/email" "first gate" "emit" "done")
|
|
70
77
|
STEP_VAL=("" "" "" "" "" "")
|
|
71
|
-
RAIL_LINES=0
|
|
72
|
-
Q_LINES=0
|
|
73
78
|
CURRENT=1
|
|
74
79
|
|
|
75
|
-
|
|
80
|
+
# rail_say: one line of wizard output on a tty — stored in the redraw buffer AND
|
|
81
|
+
# printed with a VT100 clear-line prefix (the redraw may overwrite a longer old line).
|
|
82
|
+
# Over a pipe this is exactly printf '%s\n' and nothing is stored.
|
|
83
|
+
rail_say() {
|
|
84
|
+
if [ "$TTY_OUT" -eq 1 ]; then
|
|
85
|
+
RAIL_BUF+=("$*")
|
|
86
|
+
printf '\033[2K%s\n' "$*"
|
|
87
|
+
else
|
|
88
|
+
printf '%s\n' "$*"
|
|
89
|
+
fi
|
|
90
|
+
}
|
|
91
|
+
# rail_seed: the pre-draw frame (logo + header). On a tty it only FILLS the buffer —
|
|
92
|
+
# rail_draw prints it; over a pipe nothing is buffered and it prints directly.
|
|
93
|
+
rail_seed() {
|
|
94
|
+
if [ "$TTY_OUT" -eq 1 ]; then
|
|
95
|
+
RAIL_BUF+=("$*")
|
|
96
|
+
else
|
|
97
|
+
printf '%s\n' "$*"
|
|
98
|
+
fi
|
|
99
|
+
}
|
|
100
|
+
# rail_note: a line that prints now but never joins the redraw buffer (engine output,
|
|
101
|
+
# transcripts, the verify echo): the next redraw wipes it with its clear-below.
|
|
102
|
+
rail_note() {
|
|
76
103
|
printf '%s\n' "$*"
|
|
77
|
-
Q_LINES=$((Q_LINES + 1))
|
|
78
104
|
}
|
|
79
105
|
rail_rewind() {
|
|
80
|
-
if [ "$TTY_OUT" -eq 1 ]
|
|
81
|
-
printf '\033[%dA\033[J' "$
|
|
106
|
+
if [ "$TTY_OUT" -eq 1 ]; then
|
|
107
|
+
printf '\033[%dA\033[J' "${#RAIL_BUF[@]}"
|
|
82
108
|
fi
|
|
83
|
-
RAIL_LINES=0; Q_LINES=0
|
|
84
109
|
}
|
|
85
|
-
|
|
86
|
-
local i glyph col label
|
|
110
|
+
rail_draw() { # reprint the saved buffer, then the live rail row for $CURRENT
|
|
111
|
+
local i line glyph col label
|
|
112
|
+
if [ "$TTY_OUT" -eq 0 ]; then return 0; fi
|
|
113
|
+
for line in "${RAIL_BUF[@]}"; do
|
|
114
|
+
printf '\033[2K%s\n' "$line"
|
|
115
|
+
done
|
|
87
116
|
for i in 1 2 3 4 5 6; do
|
|
88
117
|
label="${STEP_LABELS[$((i - 1))]}"
|
|
89
118
|
if [ "$i" -lt "$CURRENT" ]; then
|
|
90
|
-
|
|
119
|
+
glyph="✔"; col="$C_GREEN"
|
|
91
120
|
elif [ "$i" -eq "$CURRENT" ]; then
|
|
92
|
-
|
|
93
|
-
RAIL_LINES=$((RAIL_LINES + 1))
|
|
94
|
-
return 0 # the asking row is printed by the screen itself, after its question
|
|
121
|
+
glyph="◆"; col="$C_ACCENT"
|
|
95
122
|
else
|
|
96
|
-
|
|
123
|
+
glyph="○"; col="$C_DIM"
|
|
97
124
|
fi
|
|
98
|
-
|
|
125
|
+
printf '\033[2K %s%s %d. %-13s%s%s%s\n' "${col}${glyph}" "$C_RESET" "$i" "$label" "$C_TEXT" "${STEP_VAL[$((i - 1))]}" "$C_RESET"
|
|
99
126
|
done
|
|
100
127
|
}
|
|
101
|
-
|
|
128
|
+
rail_reset() { RAIL_BUF=(); }
|
|
129
|
+
cascade() { # the pipe-degraded shape: one numbered line per screen (zero cursor motion)
|
|
102
130
|
printf 'gob init [%d/6] %s\n' "$CURRENT" "$1"
|
|
103
131
|
}
|
|
104
132
|
|
|
@@ -108,7 +136,7 @@ ask_value() {
|
|
|
108
136
|
if [ "$TTY_IN" -eq 0 ]; then REPLY="$dflt"; return 0; fi
|
|
109
137
|
printf ' %s%s%s %s─ %s' "$C_TEXT" "$label" "$C_RESET" "$C_DIM" "$dflt"
|
|
110
138
|
read -r REPLY
|
|
111
|
-
|
|
139
|
+
rail_say ""
|
|
112
140
|
[ -n "$REPLY" ] || REPLY="$dflt"
|
|
113
141
|
return 0
|
|
114
142
|
}
|
|
@@ -146,18 +174,25 @@ TARGET=$(g_expand_tilde "$TARGET")
|
|
|
146
174
|
TARGET=$(cd "$TARGET" && pwd)
|
|
147
175
|
|
|
148
176
|
# ------------------------------------------------------------ screen 1: welcome
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
printf
|
|
152
|
-
printf '%s\n' "${C_ACCENT}▀██▀▀ ▀██▀ ██▀ ▀▀▀▀ ▀█▀ █ █ ██▀▀ ▀ █ █ ▀██▀ █ █${C_RESET}"
|
|
153
|
-
printf '%s\n' "${C_DIM} discipline around whatever executes${C_RESET}"
|
|
154
|
-
printf '\n'
|
|
155
|
-
printf '%s\n' "${C_TEXT} gob init${C_RESET} ${C_MUTED}— first-run setup (gobstack v$VERSION)${C_RESET}"
|
|
156
|
-
printf '%s\n' " ${C_MUTED}6 steps · every step has a flag · nothing is written until the plan is confirmed${C_RESET}"
|
|
157
|
-
printf '\n'
|
|
177
|
+
# The logotype and the header ride the redraw buffer (rail_say): every later screen
|
|
178
|
+
# redraws logo + header + rail as one stable frame. Over a pipe rail_say is plain
|
|
179
|
+
# printf and nothing is buffered — the cascade keeps zero cursor motion.
|
|
158
180
|
CURRENT=1
|
|
159
|
-
|
|
160
|
-
|
|
181
|
+
rail_reset
|
|
182
|
+
rail_seed "${C_ACCENT}▄███▄ ▄██▄ ██▄ █ ▄█▄ █ █ ▄██▀ █████ ▄███▄ ▄███ █ █${C_RESET}"
|
|
183
|
+
rail_seed "${C_ACCENT}█ █ █ █ █▄▀█ █ █ █▄ █ ▀██▄ █ █ █ █ █ █${C_RESET}"
|
|
184
|
+
rail_seed "${C_ACCENT}█ ▄██ █ █ █▄▀█ █ █ █ ▄█ ▄█▀ █ █████ █ ██${C_RESET}"
|
|
185
|
+
rail_seed "${C_ACCENT}▀██▀▀ ▀██▀ ██▀ ▀▀▀▀ ▀█▀ █ █ ██▀▀ ▀ █ █ ▀██▀ █ █${C_RESET}"
|
|
186
|
+
rail_seed " ${C_MUTED}discipline around whatever executes${C_RESET}"
|
|
187
|
+
rail_seed ""
|
|
188
|
+
rail_seed "${C_TEXT} gob init${C_RESET} ${C_MUTED}— first-run setup (gobstack v$VERSION)${C_RESET}"
|
|
189
|
+
rail_seed " ${C_MUTED}6 steps · every step has a flag · nothing is written until the plan is confirmed${C_RESET}"
|
|
190
|
+
rail_seed ""
|
|
191
|
+
if [ "$TTY_OUT" -eq 1 ]; then
|
|
192
|
+
rail_draw
|
|
193
|
+
else
|
|
194
|
+
cascade "welcome: gobstack v$VERSION — the guided first run"
|
|
195
|
+
fi
|
|
161
196
|
|
|
162
197
|
# Platform enum assembled from fragments (the MD_SLUG precedent): a literal tool name in
|
|
163
198
|
# bin/ trips MD-01's model-name pattern, and run-tests.sh greps this directory.
|
|
@@ -170,7 +205,7 @@ PLATFORMS="$PLA hermes copilot cursor opencode codex $GEM"
|
|
|
170
205
|
# ---------------------------------------------------------- screen 2: detect --
|
|
171
206
|
CURRENT=2
|
|
172
207
|
rail_rewind
|
|
173
|
-
if [ "$TTY_OUT" -eq 1 ]; then
|
|
208
|
+
if [ "$TTY_OUT" -eq 1 ]; then rail_draw; fi
|
|
174
209
|
DET_ROWS=""
|
|
175
210
|
DET_N=0
|
|
176
211
|
for p in $PLATFORMS; do
|
|
@@ -194,19 +229,17 @@ for p in $PLATFORMS; do
|
|
|
194
229
|
DET_N=$((DET_N + 1))
|
|
195
230
|
fi
|
|
196
231
|
done
|
|
197
|
-
STEP_VAL[0]="
|
|
232
|
+
STEP_VAL[0]="$C_DIM$DET_N detected$C_RESET"
|
|
198
233
|
if [ "$TTY_OUT" -eq 1 ]; then
|
|
199
|
-
|
|
200
|
-
|
|
234
|
+
rail_say ""
|
|
235
|
+
rail_say " ✔ 1. platforms $C_TEXT$DET_N detected$C_RESET"
|
|
201
236
|
if [ "$DET_N" -eq 0 ]; then
|
|
202
|
-
|
|
203
|
-
RAIL_LINES=$((RAIL_LINES + 1))
|
|
237
|
+
rail_say " ${C_YELLOW}no platform detected here — the wizard will still install the harness; emit later with gob emit$C_RESET"
|
|
204
238
|
else
|
|
205
239
|
while IFS=$'\t' read -r p proj glob hit; do
|
|
206
240
|
[ -n "$p" ] || continue
|
|
207
241
|
case "$glob" in "~"*) glob="${glob/\~/$HOME}" ;; esac
|
|
208
|
-
|
|
209
|
-
RAIL_LINES=$((RAIL_LINES + 1))
|
|
242
|
+
rail_say " $C_GREEN✔$C_RESET $C_TEXT$p$C_RESET $glob $C_DIM($hit)$C_RESET"
|
|
210
243
|
done <<< "$DET_ROWS"
|
|
211
244
|
fi
|
|
212
245
|
else
|
|
@@ -216,23 +249,23 @@ fi
|
|
|
216
249
|
# ------------------------------------------------------------ screen 3: class -
|
|
217
250
|
CURRENT=3
|
|
218
251
|
rail_rewind
|
|
219
|
-
if [ "$TTY_OUT" -eq 1 ]; then
|
|
252
|
+
if [ "$TTY_OUT" -eq 1 ]; then rail_draw; fi
|
|
220
253
|
CLASS_DEFAULT=1
|
|
221
254
|
if [ -z "$CLASS" ]; then
|
|
222
255
|
if [ "$TTY_IN" -eq 1 ]; then
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
256
|
+
rail_say ""
|
|
257
|
+
rail_say " ${C_MUTED}what kind of work does this repo do?$C_RESET"
|
|
258
|
+
rail_say " $C_TEXT 1 ▸ app shipped features, PRs, review gates ${C_DIM}(class A)$C_RESET"
|
|
259
|
+
rail_say " $C_TEXT 2 service backend jobs, config, unattended runs ${C_DIM}(class B)$C_RESET"
|
|
260
|
+
rail_say " $C_TEXT 3 game playable builds, perf budgets ${C_DIM}(class C)$C_RESET"
|
|
261
|
+
rail_say " $C_TEXT 4 research specs, replays, reference corpora ${C_DIM}(class D)$C_RESET"
|
|
262
|
+
rail_say " $C_TEXT 5 agent fleets, loops, unattended automation ${C_DIM}(class E)$C_RESET"
|
|
263
|
+
rail_say " $C_TEXT 6 desktop the shell you live in, perf budget ${C_DIM}(class F)$C_RESET"
|
|
264
|
+
rail_say " ${C_MUTED}↑ number + Enter · Enter = app · the class letter works too (A-F)$C_RESET"
|
|
232
265
|
printf ' '
|
|
233
266
|
read -r REPLY_CLASS
|
|
234
267
|
ASKED_ANY=1
|
|
235
|
-
|
|
268
|
+
rail_say ""
|
|
236
269
|
else
|
|
237
270
|
REPLY_CLASS="1"
|
|
238
271
|
cascade "class: app (default; --class app|service|game|research|agent|desktop or A-F)"
|
|
@@ -258,10 +291,9 @@ case "$CLASS" in
|
|
|
258
291
|
exit 2 ;;
|
|
259
292
|
esac
|
|
260
293
|
CLASS_NAME=$(ls "$SRC"/presets/"$CLASS"-*.yaml 2>/dev/null | head -n 1)
|
|
261
|
-
STEP_VAL[1]="
|
|
294
|
+
STEP_VAL[1]="$C_TEXT$CLASS ($([ -n "$CLASS_NAME" ] && basename "$CLASS_NAME" .yaml | cut -d- -f2-))$C_RESET"
|
|
262
295
|
if [ "$TTY_OUT" -eq 1 ]; then
|
|
263
|
-
|
|
264
|
-
RAIL_LINES=$((RAIL_LINES + 1))
|
|
296
|
+
rail_say " ✔ $((CURRENT - 1)). class $C_TEXT$CLASS$([ -n "$CLASS_NAME" ] && printf ' — %s' "$(basename "$CLASS_NAME" .yaml | cut -d- -f2-)")$C_RESET"
|
|
265
297
|
else
|
|
266
298
|
cascade "class: $CLASS"
|
|
267
299
|
fi
|
|
@@ -269,15 +301,15 @@ fi
|
|
|
269
301
|
# ----------------------------------------------------- screen 4: branch/email --
|
|
270
302
|
CURRENT=4
|
|
271
303
|
rail_rewind
|
|
272
|
-
if [ "$TTY_OUT" -eq 1 ]; then
|
|
304
|
+
if [ "$TTY_OUT" -eq 1 ]; then rail_draw; fi
|
|
273
305
|
DEF_BRANCH=$( cd "$TARGET" && git rev-parse --abbrev-ref HEAD 2>/dev/null )
|
|
274
306
|
case "$DEF_BRANCH" in ""|HEAD) DEF_BRANCH=main ;; esac
|
|
275
307
|
DEF_EMAIL=$( cd "$TARGET" && git config user.email 2>/dev/null )
|
|
276
308
|
[ -n "$DEF_EMAIL" ] || DEF_EMAIL="you@example.com"
|
|
277
309
|
if [ -z "$BRANCH" ] || [ -z "$EMAIL" ]; then
|
|
278
310
|
if [ "$TTY_IN" -eq 1 ]; then
|
|
279
|
-
|
|
280
|
-
|
|
311
|
+
rail_say ""
|
|
312
|
+
rail_say " ${C_MUTED}the forge contract — the identity this repo commits with (CM-01)$C_RESET"
|
|
281
313
|
ask_value "default branch" "$DEF_BRANCH"; BRANCH="${BRANCH:-$REPLY}"
|
|
282
314
|
ask_value "owner email" "$DEF_EMAIL"; EMAIL="${EMAIL:-$REPLY}"
|
|
283
315
|
else
|
|
@@ -288,29 +320,27 @@ if [ -z "$BRANCH" ] || [ -z "$EMAIL" ]; then
|
|
|
288
320
|
fi
|
|
289
321
|
BRANCH="${BRANCH:-$DEF_BRANCH}"
|
|
290
322
|
EMAIL="${EMAIL:-$DEF_EMAIL}"
|
|
291
|
-
STEP_VAL[2]="
|
|
323
|
+
STEP_VAL[2]="$C_TEXT$BRANCH · $EMAIL$C_RESET"
|
|
292
324
|
if [ "$TTY_OUT" -eq 1 ]; then
|
|
293
|
-
|
|
294
|
-
RAIL_LINES=$((RAIL_LINES + 1))
|
|
325
|
+
rail_say " ✔ $((CURRENT - 1)). branch/email $C_TEXT$BRANCH · $EMAIL$C_RESET"
|
|
295
326
|
fi
|
|
296
327
|
|
|
297
328
|
# -------------------------------------------------------- screen 5: first gate -
|
|
298
329
|
CURRENT=5
|
|
299
330
|
rail_rewind
|
|
300
|
-
if [ "$TTY_OUT" -eq 1 ]; then
|
|
331
|
+
if [ "$TTY_OUT" -eq 1 ]; then rail_draw; fi
|
|
301
332
|
GATE_DEFAULT="bash tests/run-tests.sh"
|
|
302
333
|
if [ -z "$GATE" ]; then
|
|
303
334
|
if [ "$TTY_IN" -eq 1 ]; then
|
|
304
|
-
|
|
305
|
-
|
|
335
|
+
rail_say ""
|
|
336
|
+
rail_say " ${C_MUTED}one real command that must stay green — suggested for a bash suite:$C_RESET"
|
|
306
337
|
if [ -f "$TARGET/tests/run-tests.sh" ]; then
|
|
307
|
-
|
|
338
|
+
rail_say " $C_DIM$GATE_DEFAULT$C_RESET"
|
|
308
339
|
ask_value "gate command" "$GATE_DEFAULT"; GATE="$REPLY"
|
|
309
340
|
else
|
|
310
341
|
ask_value "gate command" "true"; GATE="$REPLY"
|
|
311
342
|
if [ "$GATE" = "true" ]; then
|
|
312
|
-
|
|
313
|
-
Q_LINES=$((Q_LINES + 1))
|
|
343
|
+
rail_say " ${C_YELLOW}no tests/run-tests.sh found — a placeholder gate is allowed but it proves nothing; replace it in .goblin/goblin.yaml$C_RESET"
|
|
314
344
|
fi
|
|
315
345
|
fi
|
|
316
346
|
else
|
|
@@ -318,16 +348,15 @@ if [ -z "$GATE" ]; then
|
|
|
318
348
|
cascade "gate: $GATE (default)"
|
|
319
349
|
fi
|
|
320
350
|
fi
|
|
321
|
-
STEP_VAL[3]="
|
|
351
|
+
STEP_VAL[3]="$C_TEXT$GATE$C_RESET"
|
|
322
352
|
if [ "$TTY_OUT" -eq 1 ]; then
|
|
323
|
-
|
|
324
|
-
RAIL_LINES=$((RAIL_LINES + 1))
|
|
353
|
+
rail_say " ✔ $((CURRENT - 1)). first gate $C_TEXT$GATE$C_RESET"
|
|
325
354
|
fi
|
|
326
355
|
|
|
327
356
|
# --------------------------------------------------------- screen 6: the plan --
|
|
328
357
|
CURRENT=6
|
|
329
358
|
rail_rewind
|
|
330
|
-
if [ "$TTY_OUT" -eq 1 ]; then
|
|
359
|
+
if [ "$TTY_OUT" -eq 1 ]; then rail_draw; fi
|
|
331
360
|
|
|
332
361
|
# The emit selection: pre-tick the DETECTED platforms (in doctor's enum order).
|
|
333
362
|
EMIT_SELECTED=""
|
|
@@ -346,29 +375,37 @@ if [ -n "$EMIT" ]; then
|
|
|
346
375
|
[ -n "$ok" ] || { g_err "--emit: unknown platform '$w' — I ship $PLA, hermes, copilot, cursor, opencode, codex, $GEM"; exit 2; }
|
|
347
376
|
done
|
|
348
377
|
else
|
|
378
|
+
# neutral-first: a flags-only run with no --emit emits NOTHING. The detected rows are
|
|
379
|
+
# shown on the emit screen as pre-ticked SUGGESTIONS for the interactive path; in
|
|
380
|
+
# non-interactive mode silence means the neutral harness only.
|
|
349
381
|
EMIT_SELECTED=""
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
382
|
+
if [ "$YES" -eq 0 ] && [ "$TTY_IN" -eq 1 ]; then
|
|
383
|
+
while IFS=$'\t' read -r p _proj _glob _hit; do
|
|
384
|
+
[ -n "$p" ] || continue
|
|
385
|
+
EMIT_SELECTED="$EMIT_SELECTED $p"
|
|
386
|
+
done <<< "$DET_ROWS"
|
|
387
|
+
fi
|
|
354
388
|
fi
|
|
355
389
|
EMIT_N=$(printf '%s' "$EMIT_SELECTED" | awk 'NF' | wc -l | tr -d '[:space:]')
|
|
356
390
|
|
|
357
391
|
if [ "$TTY_IN" -eq 1 ] && [ -z "$EMIT" ] && [ "$YES" -eq 0 ]; then
|
|
358
|
-
|
|
359
|
-
|
|
392
|
+
rail_say ""
|
|
393
|
+
rail_say " ${C_MUTED}agent skills are opt-in, per platform — emit them into:$C_RESET"
|
|
360
394
|
for p in $PLATFORMS; do
|
|
361
395
|
hit=""
|
|
362
396
|
while IFS=$' ' read -r dp _a _b _c; do [ "$dp" = "$p" ] && hit=1; done <<< "$DET_ROWS"
|
|
363
397
|
[ -n "$hit" ] || continue
|
|
364
|
-
|
|
398
|
+
rail_say " ${C_GREEN}✔$C_RESET $C_TEXT$p$C_RESET"
|
|
365
399
|
done
|
|
366
|
-
|
|
400
|
+
rail_say " ${C_MUTED}the install itself writes none — the harness stays neutral$C_RESET"
|
|
401
|
+
rail_say " ${C_MUTED}Enter = these; or type platform names to override (none: type x)$C_RESET"
|
|
367
402
|
printf ' '
|
|
368
403
|
read -r REPLY_EMIT
|
|
369
404
|
ASKED_ANY=1
|
|
370
|
-
|
|
371
|
-
if [
|
|
405
|
+
rail_say ""
|
|
406
|
+
if [ "$REPLY_EMIT" = "x" ] || [ "$REPLY_EMIT" = "none" ]; then
|
|
407
|
+
EMIT_SELECTED=""
|
|
408
|
+
elif [ -n "$REPLY_EMIT" ]; then
|
|
372
409
|
EMIT_SELECTED=""
|
|
373
410
|
for w in $REPLY_EMIT; do
|
|
374
411
|
ok=""
|
|
@@ -377,16 +414,18 @@ if [ "$TTY_IN" -eq 1 ] && [ -z "$EMIT" ] && [ "$YES" -eq 0 ]; then
|
|
|
377
414
|
done
|
|
378
415
|
fi
|
|
379
416
|
fi
|
|
380
|
-
STEP_VAL[4]="
|
|
417
|
+
STEP_VAL[4]="$C_TEXT$EMIT_N platform(s) · scope $SCOPE$C_RESET"
|
|
381
418
|
if [ "$TTY_OUT" -eq 1 ]; then
|
|
382
|
-
|
|
383
|
-
RAIL_LINES=$((RAIL_LINES + 1))
|
|
419
|
+
rail_say " ✔ $((CURRENT - 1)). emit $C_TEXT$EMIT_N platform(s) · scope $SCOPE$C_RESET"
|
|
384
420
|
fi
|
|
385
421
|
|
|
386
422
|
# ---- the plan box: the wizard never writes until this has been on the screen ----
|
|
423
|
+
# Spacing: one blank line separates the rail from the box (the spacing pass); the box
|
|
424
|
+
# carries two-space inner padding on every row so the columns align across rows.
|
|
387
425
|
BOX=""
|
|
388
426
|
add() { BOX="$BOX$1
|
|
389
427
|
"; }
|
|
428
|
+
add ""
|
|
390
429
|
add " ${C_DIM}╭──────────────────────────────────────────────────────╮${C_RESET}"
|
|
391
430
|
add " ${C_DIM}│${C_RESET} ${C_GREEN}✔${C_RESET} ${C_TEXT}install${C_RESET} $C_DIM.goblin/ + the payload into$C_RESET $C_TEXT$TARGET$C_RESET"
|
|
392
431
|
add " ${C_DIM}│${C_RESET} ${C_GREEN}✔${C_RESET} ${C_TEXT}class${C_RESET} $C_TEXT$CLASS$C_RESET ${C_DIM}· branch $C_TEXT$BRANCH$C_RESET ${C_DIM}· email$C_RESET $C_TEXT$EMAIL$C_RESET"
|
|
@@ -400,10 +439,11 @@ if [ "$DRYRUN" -eq 1 ]; then
|
|
|
400
439
|
fi
|
|
401
440
|
# A question the wizard genuinely asked (a screen that waited on read) buys the confirm;
|
|
402
441
|
# flag/non-tty runs already stated their answer as a flag — proceeding is the contract.
|
|
442
|
+
# Single-accent: the confirm prompt is muted + dim, never bold, never a second accent.
|
|
403
443
|
if [ "$YES" -eq 0 ] && [ "${ASKED_ANY:-0}" -eq 1 ]; then
|
|
404
444
|
printf '%s\n' "$BOX"
|
|
405
445
|
if [ "$TTY_IN" -eq 1 ]; then
|
|
406
|
-
printf ' %sproceed?%s %s[Enter = yes]%s ' "$
|
|
446
|
+
printf ' %sproceed?%s %s[Enter = yes]%s ' "$C_MUTED" "$C_RESET" "$C_DIM" "$C_RESET"
|
|
407
447
|
read -r REPLY_GO
|
|
408
448
|
case "$REPLY_GO" in n*|N*) g_info "aborted — nothing was written"; exit 0 ;; esac
|
|
409
449
|
else
|
|
@@ -416,8 +456,15 @@ fi
|
|
|
416
456
|
# carry: the declared identity (branch/owner_email) and the first gate, patched into
|
|
417
457
|
# .goblin/goblin.yaml right after install renders it. The patch is fail-closed — the
|
|
418
458
|
# gate must read back through the engine's own parser or the run stops here.
|
|
419
|
-
|
|
420
|
-
|
|
459
|
+
# rail_note, not rail_say: engine output below is never redrawn over, and the wizard
|
|
460
|
+
# never rewinds past its own buffer by counting lines it did not print.
|
|
461
|
+
if [ "$TTY_OUT" -eq 1 ]; then
|
|
462
|
+
rail_note ""
|
|
463
|
+
rail_note " ${C_MUTED}running: goblin-install --target $TARGET --class $CLASS${C_RESET}"
|
|
464
|
+
else
|
|
465
|
+
printf 'gob init [run] goblin-install --target %s --class %s\n' "$TARGET" "$CLASS"
|
|
466
|
+
fi
|
|
467
|
+
bash "$SRC/bin/goblin-install" --target "$TARGET" --class "$CLASS" --skills no --yes
|
|
421
468
|
EXIT_INSTALL=$?
|
|
422
469
|
if [ "$EXIT_INSTALL" -ne 0 ]; then
|
|
423
470
|
g_err "install failed (exit $EXIT_INSTALL) — the refusals above name the path and the fix"
|
|
@@ -453,7 +500,11 @@ fi
|
|
|
453
500
|
|
|
454
501
|
EMITTED=0
|
|
455
502
|
for p in $EMIT_SELECTED; do
|
|
456
|
-
[ "$TTY_OUT" -eq 0 ]
|
|
503
|
+
if [ "$TTY_OUT" -eq 0 ]; then
|
|
504
|
+
printf 'gob init [run] goblin-emit --platform %s --scope %s\n' "$p" "$SCOPE"
|
|
505
|
+
else
|
|
506
|
+
rail_note " ${C_MUTED}running: goblin-emit --platform $p --scope $SCOPE${C_RESET}"
|
|
507
|
+
fi
|
|
457
508
|
bash "$SRC/bin/goblin-emit" --platform "$p" --scope "$SCOPE" --target "$TARGET"
|
|
458
509
|
EXIT_EMIT=$?
|
|
459
510
|
if [ "$EXIT_EMIT" -ne 0 ]; then
|
|
@@ -464,9 +515,9 @@ for p in $EMIT_SELECTED; do
|
|
|
464
515
|
done
|
|
465
516
|
|
|
466
517
|
# --------------------------------------------------- screen 6b: done + verify --
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
518
|
+
# Spacing pass: exactly one blank line before each done-screen section; the engine's
|
|
519
|
+
# verify output and this screen's own summary stay visually separated.
|
|
520
|
+
if [ "$TTY_OUT" -eq 0 ]; then
|
|
470
521
|
printf 'gob init [verify] .goblin/bin/goblin-verify\n'
|
|
471
522
|
fi
|
|
472
523
|
if [ "$DRYRUN" -eq 0 ]; then
|
|
@@ -494,16 +545,20 @@ if [ "$DRYRUN" -eq 0 ]; then
|
|
|
494
545
|
else
|
|
495
546
|
printf ' %sgob verify: %s passed, %s failed, %s skipped · exit %s%s\n' "$C_RED" "$PASSED" "$FAILED" "$SKIPPED" "$VERIFY_RC" "$C_RESET"
|
|
496
547
|
fi
|
|
497
|
-
printf '\n'
|
|
498
548
|
else
|
|
499
549
|
g_info "gob init [verify] exit $VERIFY_RC: $PASSED passed, $FAILED failed, $ADVISORY advisory, $SKIPPED skipped"
|
|
500
550
|
fi
|
|
501
551
|
|
|
502
|
-
printf '
|
|
552
|
+
printf '\n'
|
|
553
|
+
printf ' %snext:%s\n' "$C_MUTED" "$C_RESET"
|
|
503
554
|
printf ' 1. %sthe %s skipped checks ARE your checklist — gob verify names each one%s\n' "$C_TEXT" "$SKIPPED" "$C_RESET"
|
|
504
555
|
printf ' 2. %sreplace the placeholder gate in .goblin/goblin.yaml with your real commands (P8 step 3)%s\n' "$C_TEXT" "$C_RESET"
|
|
505
556
|
printf ' 3. %snot emitted here: gob emit --platform <p> --scope global%s\n' "$C_TEXT" "$C_RESET"
|
|
506
|
-
|
|
557
|
+
if [ "$EMIT_N" -gt 0 ]; then
|
|
558
|
+
printf ' 4. %shermes skills trust %s # one-time, so the project-tier skills load%s\n' "$C_TEXT" "$TARGET" "$C_RESET"
|
|
559
|
+
else
|
|
560
|
+
printf ' 4. %sagent skills are opt-in per platform: gob emit --platform <p>%s\n' "$C_TEXT" "$C_RESET"
|
|
561
|
+
fi
|
|
507
562
|
printf '\n'
|
|
508
563
|
if [ "$VERIFY_RC" -eq 0 ]; then
|
|
509
564
|
printf ' %s▙ the goblin sees you. keep the gate green.%s\n' "$C_ACCENT" "$C_RESET"
|
package/bin/goblin-install
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
# --practice <path> the referenced standard (default: $GOBLIN_PRACTICE -> ~/projects/PROJECT-PRACTICE.md); a named path that is absent is reported, never silently dropped
|
|
9
9
|
# --parts <list> comma list to install; default = every part the class requires
|
|
10
10
|
# --archive mark the project archive: verify requires no HANDOFF and no gates
|
|
11
|
-
# --skills yes|no install .hermes/skills (default
|
|
11
|
+
# --skills yes|no install agent skills under .hermes/skills (default no; opt in per
|
|
12
|
+
# platform with: gob emit --platform <p>)
|
|
12
13
|
# --dry-run print the plan; write nothing
|
|
13
14
|
# --upgrade re-install at the current version; report created/updated/unchanged/skipped
|
|
14
15
|
# --opt-out <part> record the part in disabled: so its required checks are skipped
|
|
@@ -40,7 +41,16 @@ PRACTICE="${GOBLIN_PRACTICE:-$HOME/projects/PROJECT-PRACTICE.md}"
|
|
|
40
41
|
PRACTICE_ARG=""
|
|
41
42
|
PARTS=""
|
|
42
43
|
ARCHIVE="false"
|
|
43
|
-
|
|
44
|
+
# W6 neutral-first: a default install is a NEUTRAL harness (manifest + verify + HANDOFF +
|
|
45
|
+
# .goblin) with NO agent skills. Agent skills are an explicit opt-in (--skills yes, or the
|
|
46
|
+
# per-platform gob emit path the wizard's emit screen drives). The old default (yes) was the
|
|
47
|
+
# house's own setup, not a product default: a non-agent user got a dead .hermes/ tree, and a
|
|
48
|
+
# user of another agent harness got nothing at all.
|
|
49
|
+
SKILLS="no"
|
|
50
|
+
# Whether --skills was named on the command line: an OMITTED flag on a re-install means
|
|
51
|
+
# "whatever the record says" (the migration contract below); a NAMED --skills no is an
|
|
52
|
+
# explicit opt-out and is honoured verbatim.
|
|
53
|
+
SKILLS_NAMED=0
|
|
44
54
|
DRY_RUN=0
|
|
45
55
|
UPGRADE=0
|
|
46
56
|
UNINSTALL=0
|
|
@@ -63,7 +73,7 @@ while [ $# -gt 0 ]; do
|
|
|
63
73
|
--practice) PRACTICE="${2:-}"; PRACTICE_ARG="$PRACTICE"; shift 2 ;;
|
|
64
74
|
--parts) PARTS="${2:-}"; shift 2 ;;
|
|
65
75
|
--archive) ARCHIVE="true"; shift ;;
|
|
66
|
-
--skills) SKILLS="${2:-}"; shift 2 ;;
|
|
76
|
+
--skills) SKILLS="${2:-}"; SKILLS_NAMED=1; shift 2 ;;
|
|
67
77
|
--dry-run) DRY_RUN=1; shift ;;
|
|
68
78
|
--upgrade) UPGRADE=1; shift ;;
|
|
69
79
|
--uninstall) UNINSTALL=1; shift ;;
|
|
@@ -233,6 +243,20 @@ fi
|
|
|
233
243
|
case "$CLASS" in A|B|C|D|E|F) ;; *) g_err "--class must be one of A B C D E F, got '$CLASS'"; exit 2 ;; esac
|
|
234
244
|
case "$SKILLS" in yes|no) ;; *) g_err "--skills must be yes or no"; exit 2 ;; esac
|
|
235
245
|
|
|
246
|
+
# W6 migration safety: on a --upgrade (or any re-install) of a repo whose record shows skills
|
|
247
|
+
# were installed, an OMITTED --skills flag must READ that state, not silently strip it. The
|
|
248
|
+
# idempotence contract (created/updated/unchanged) holds only if the effective skills choice
|
|
249
|
+
# for a flag-less re-install is `the record's choice`, so previously-installed skills survive
|
|
250
|
+
# every upgrade unchanged. An EXPLICIT --skills no is honoured verbatim (the switch stays
|
|
251
|
+
# real), and --uninstall still removes exactly what the record lists.
|
|
252
|
+
if [ -f "$INSTALLED" ] && [ "$SKILLS_NAMED" -eq 0 ]; then
|
|
253
|
+
PREV_SKILLS=$(g_installed_scalar_options "$INSTALLED" skills)
|
|
254
|
+
if [ "$PREV_SKILLS" = "yes" ]; then
|
|
255
|
+
SKILLS="yes"
|
|
256
|
+
g_info "skills: this repo's install record has agent skills installed - they are kept (remove them with an explicit --skills no, or --uninstall)"
|
|
257
|
+
fi
|
|
258
|
+
fi
|
|
259
|
+
|
|
236
260
|
PRESET=$(ls "$SRC"/presets/"$CLASS"-*.yaml 2>/dev/null | head -n 1)
|
|
237
261
|
[ -n "$PRESET" ] || { g_err "no preset for class $CLASS"; exit 2; }
|
|
238
262
|
|
|
@@ -572,6 +596,30 @@ if [ "$SKILLS" = "yes" ] && part_wanted playbooks; then
|
|
|
572
596
|
done
|
|
573
597
|
fi
|
|
574
598
|
|
|
599
|
+
# W6 neutral-first: an EXPLICIT --skills no re-install of a repo that HAS recorded skills
|
|
600
|
+
# removes them — the switch stays real in both directions. The paths come from the previous
|
|
601
|
+
# record, so exactly what was installed is what goes; a skill file the project edited by hand
|
|
602
|
+
# is refused by put's own contract (never clobbered silently), reported in the summary.
|
|
603
|
+
if [ "$SKILLS_NAMED" -eq 1 ] && [ "$SKILLS" = "no" ] && [ -n "$PREV_FILES" ]; then
|
|
604
|
+
while IFS= read -r rel; do
|
|
605
|
+
case "$rel" in .hermes/skills/*) ;; *) continue ;; esac
|
|
606
|
+
dst="$TARGET/$rel"
|
|
607
|
+
[ -f "$dst" ] || { rm -f "$TARGET/.goblin/$(basename "$rel")" 2>/dev/null; continue; }
|
|
608
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
609
|
+
g_info "plan: remove $rel (explicit --skills no)"
|
|
610
|
+
else
|
|
611
|
+
rm -f "$dst"
|
|
612
|
+
g_info "removed $rel (explicit --skills no)"
|
|
613
|
+
fi
|
|
614
|
+
done <<EOF
|
|
615
|
+
$PREV_FILES
|
|
616
|
+
EOF
|
|
617
|
+
# the emptied .hermes/skills/*/<name> and .hermes/ directories go too (deepest first)
|
|
618
|
+
if [ "$DRY_RUN" -eq 0 ]; then
|
|
619
|
+
find "$TARGET/.hermes" -depth -type d -exec rmdir {} + 2>/dev/null
|
|
620
|
+
fi
|
|
621
|
+
fi
|
|
622
|
+
|
|
575
623
|
# The automation producers. They land in the target so the target is self-contained and
|
|
576
624
|
# IN-02/SK-02 hash them like every other installed file; the cron copy step then reads from
|
|
577
625
|
# here (see the "next:" block and automations/README.md).
|
|
@@ -667,12 +715,20 @@ fi
|
|
|
667
715
|
} > "$TMP/installed.json"
|
|
668
716
|
|
|
669
717
|
PREV_VERSION=""
|
|
670
|
-
|
|
718
|
+
RECORD_SKILLS_EFFECTIVE=""
|
|
719
|
+
if [ -f "$INSTALLED" ]; then
|
|
720
|
+
PREV_VERSION=$(g_installed_scalar "$INSTALLED" version)
|
|
721
|
+
# The record's own skills choice (after the migration read above): a flip of the effective
|
|
722
|
+
# choice IS a change even when no file hash moved — the explicit --skills no re-install must
|
|
723
|
+
# rewrite the record (and drop the files), never report no-op over a state change.
|
|
724
|
+
RECORD_SKILLS_EFFECTIVE=$(g_installed_scalar_options "$INSTALLED" skills)
|
|
725
|
+
fi
|
|
671
726
|
|
|
672
727
|
|
|
673
728
|
if [ "$DRY_RUN" -eq 0 ]; then
|
|
674
729
|
mkdir -p "$TARGET/.goblin"
|
|
675
|
-
if [ "$CREATED" -eq 0 ] && [ "$UPDATED" -eq 0 ] && [ "$PREV_VERSION" = "$VERSION" ]
|
|
730
|
+
if [ "$CREATED" -eq 0 ] && [ "$UPDATED" -eq 0 ] && [ "$PREV_VERSION" = "$VERSION" ] \
|
|
731
|
+
&& [ "$(printf '%s' "$RECORD_SKILLS_EFFECTIVE" )" = "$(printf '%s' "$SKILLS")" ]; then
|
|
676
732
|
g_info "no-op: $UNCHANGED files unchanged (v$VERSION already installed)"
|
|
677
733
|
else
|
|
678
734
|
cp "$TMP/installed.json" "$INSTALLED"
|
|
@@ -699,6 +755,8 @@ if [ "$DRY_RUN" -eq 0 ]; then
|
|
|
699
755
|
g_info " 3. edit .goblin/goblin.yaml: replace the default gate with your real commands (P8 step 3)"
|
|
700
756
|
if [ "$SKILLS" = "yes" ]; then
|
|
701
757
|
g_info " 4. hermes skills trust $TARGET # one-time, so the project-tier skills load"
|
|
758
|
+
else
|
|
759
|
+
g_info " 4. agent skills are opt-in: gob emit --platform <p> # run gob doctor for the platform list"
|
|
702
760
|
fi
|
|
703
761
|
g_info ""
|
|
704
762
|
g_info "automations (optional; neither writes outside this repo, and A-02 has no agent in it):"
|