@techgoblin/gobstack 0.0.0-stage → 0.4.4-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +351 -0
- package/LICENSE +21 -0
- package/README.md +163 -2
- package/VERSION +1 -0
- package/adapters/_template/adapter.tsv +16 -0
- package/adapters/_template/detect.sh +10 -0
- package/adapters/_template/emit.sh +5 -0
- package/adapters/_template/verify.sh +4 -0
- package/adapters/claude/adapter.tsv +8 -0
- package/adapters/claude/detect.sh +8 -0
- package/adapters/claude/verify.sh +47 -0
- package/adapters/codex/adapter.tsv +12 -0
- package/adapters/codex/detect.sh +9 -0
- package/adapters/codex/verify.sh +45 -0
- package/adapters/copilot/adapter.tsv +10 -0
- package/adapters/copilot/detect.sh +8 -0
- package/adapters/copilot/verify.sh +45 -0
- package/adapters/cursor/adapter.tsv +11 -0
- package/adapters/cursor/detect.sh +10 -0
- package/adapters/cursor/verify.sh +45 -0
- package/adapters/gemini/adapter.tsv +15 -0
- package/adapters/gemini/detect.sh +11 -0
- package/adapters/gemini/verify.sh +49 -0
- package/adapters/hermes/adapter.tsv +9 -0
- package/adapters/hermes/detect.sh +8 -0
- package/adapters/hermes/verify.sh +27 -0
- package/adapters/opencode/adapter.tsv +14 -0
- package/adapters/opencode/detect.sh +9 -0
- package/adapters/opencode/verify.sh +45 -0
- package/automations/README.md +53 -0
- package/automations/bugreporter-intake.sh +145 -0
- package/automations/drift-audit.sh +139 -0
- package/automations/report.schema.tsv +10 -0
- package/bans/README.md +82 -0
- package/bans/grep-ban.sh +84 -0
- package/bans/layer-check.sh +57 -0
- package/bin/goblin +119 -0
- package/bin/goblin-audit +145 -0
- package/bin/goblin-bans +178 -0
- package/bin/goblin-doctor +233 -0
- package/bin/goblin-emit +482 -0
- package/bin/goblin-init +519 -0
- package/bin/goblin-install +720 -0
- package/bin/goblin-lib.sh +289 -0
- package/bin/goblin-model +105 -0
- package/bin/goblin-upgrade +572 -0
- package/bin/goblin-verify +2798 -0
- package/bin/goblin.js +48 -0
- package/docs/ADOPTION.md +168 -0
- package/docs/CI.md +187 -0
- package/docs/CONTRACTS.md +197 -0
- package/docs/DESIGN.md +92 -0
- package/docs/ENFORCEMENT.md +225 -0
- package/docs/FLOWS.md +164 -0
- package/docs/GUARDRAILS.md +126 -0
- package/docs/GUIDE.md +610 -0
- package/docs/INTEGRATION.md +92 -0
- package/docs/LIMITS.md +591 -0
- package/docs/LOOP.md +165 -0
- package/docs/RE-PLAYBOOK.md +183 -0
- package/docs/RISKS.md +70 -0
- package/docs/ROLES.md +105 -0
- package/manifest/bans.tsv +9 -0
- package/manifest/classes.tsv +61 -0
- package/manifest/enforcement.tsv +88 -0
- package/manifest/glossary.tsv +25 -0
- package/manifest/playbooks.tsv +16 -0
- package/package.json +36 -4
- package/presets/A-shipped-software.yaml +48 -0
- package/presets/B-service-config.yaml +40 -0
- package/presets/C-game.yaml +38 -0
- package/presets/D-knowledge.yaml +41 -0
- package/presets/E-fleet-config.yaml +42 -0
- package/presets/F-electron.yaml +67 -0
- package/roles.yaml +54 -0
- package/skills/goblin-bootstrap/SKILL.md +51 -0
- package/skills/goblin-bugfix/SKILL.md +26 -0
- package/skills/goblin-bugreporter/SKILL.md +52 -0
- package/skills/goblin-drift-audit/SKILL.md +43 -0
- package/skills/goblin-eval/SKILL.md +68 -0
- package/skills/goblin-feature/SKILL.md +26 -0
- package/skills/goblin-feature-map/SKILL.md +140 -0
- package/skills/goblin-handoff/SKILL.md +28 -0
- package/skills/goblin-investigation/SKILL.md +26 -0
- package/skills/goblin-judge/SKILL.md +74 -0
- package/skills/goblin-loop/SKILL.md +88 -0
- package/skills/goblin-mode/SKILL.md +70 -0
- package/skills/goblin-overnight/SKILL.md +42 -0
- package/skills/goblin-pr-gate/SKILL.md +42 -0
- package/skills/goblin-re-mobile/SKILL.md +51 -0
- package/skills/goblin-refactor/SKILL.md +23 -0
- package/skills/goblin-sweep/SKILL.md +23 -0
- package/skills/goblin-tdd-repro/SKILL.md +27 -0
- package/skills/goblin-verify-author/SKILL.md +50 -0
- package/skills/practice/SKILL.md +37 -0
- package/templates/AGENTS.md.tmpl +23 -0
- package/templates/HANDOFF.md.tmpl +43 -0
- package/templates/SPEC.md.tmpl +34 -0
- package/templates/audit-waiver.tsv.tmpl +10 -0
- package/templates/boundary-waivers.tmpl +8 -0
- package/templates/checks/assert.mjs.tmpl +60 -0
- package/templates/checks/gate.sh.tmpl +29 -0
- package/templates/ci/goblin-gate.yml.tmpl +46 -0
- package/templates/goblin.yaml.tmpl +138 -0
- package/templates/install-hooks.allowlist.tmpl +9 -0
- package/templates/loop/decisions.tsv.tmpl +1 -0
- package/templates/loop/predicate.tmpl +16 -0
- package/templates/report.yaml.tmpl +16 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# adapters/claude/verify.sh — the DRIFT oracle (W4A-SPEC §7). Re-emits the context block
|
|
3
|
+
# to a scratch buffer and cmps against the on-disk block; checks every emitted skill is
|
|
4
|
+
# byte-identical to the source payload. No engine invocation, no network. Exit 0 clean,
|
|
5
|
+
# 1 drift (names the path), 2 cannot run. Env: GOBLIN_DOCTOR_TARGET (default $PWD).
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
8
|
+
SRC=$(cd "$SELF_DIR/../.." && pwd)
|
|
9
|
+
TARGET="${GOBLIN_DOCTOR_TARGET:-$PWD}"
|
|
10
|
+
VERSION=$(cat "$SRC/VERSION" 2>/dev/null || printf 'unknown')
|
|
11
|
+
CF="$TARGET/CLAUDE.md"
|
|
12
|
+
SKROOT="$TARGET/.claude/skills"
|
|
13
|
+
bad=0
|
|
14
|
+
|
|
15
|
+
# the block: the on-disk goblin block must byte-equal what emit writes now at this VERSION
|
|
16
|
+
if [ -f "$CF" ] && grep -qF '<!-- goblin-stack:begin' "$CF"; then
|
|
17
|
+
EXPECT=$(awk -v b="<!-- goblin-stack:begin" -v e="<!-- goblin-stack:end" '
|
|
18
|
+
index($0,b){inb=1; print; next} inb{print} index($0,e){exit}' "$CF")
|
|
19
|
+
WANT=$(printf '<!-- goblin-stack:begin v%s -->\n' "$VERSION")
|
|
20
|
+
WANT="$WANT
|
|
21
|
+
goblin-stack procedure skills are emitted for this repository (goblin-stack v$VERSION).
|
|
22
|
+
Core: goblin-mode (start here), goblin-bootstrap, goblin-handoff, goblin-judge,
|
|
23
|
+
goblin-verify-author, practice. Invoke a skill by name when the task matches its
|
|
24
|
+
description; the full procedure lives in each SKILL.md.
|
|
25
|
+
<!-- goblin-stack:end -->"
|
|
26
|
+
if [ "$EXPECT" != "$WANT" ]; then
|
|
27
|
+
printf 'context block in %s does not match v%s (stale marker VERSION or edited block)\n' "$CF" "$VERSION"
|
|
28
|
+
bad=1
|
|
29
|
+
fi
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# every emitted goblin skill must be byte-identical to the source payload
|
|
33
|
+
if [ -d "$SKROOT" ]; then
|
|
34
|
+
for d in "$SKROOT"/*/; do
|
|
35
|
+
[ -d "$d" ] || continue
|
|
36
|
+
name=$(basename "$d")
|
|
37
|
+
case "$name" in goblin-*|practice) ;; *) continue ;; esac
|
|
38
|
+
f="$d/SKILL.md"; sf="$SRC/skills/$name/SKILL.md"
|
|
39
|
+
if [ ! -f "$f" ]; then continue; fi
|
|
40
|
+
if [ ! -f "$sf" ] || ! cmp -s "$f" "$sf"; then
|
|
41
|
+
printf 'emitted skill drifted: %s\n' "${f#"$TARGET"/}"
|
|
42
|
+
bad=1
|
|
43
|
+
fi
|
|
44
|
+
done
|
|
45
|
+
fi
|
|
46
|
+
[ "$bad" -eq 0 ] && printf 'verified: claude emission matches v%s byte-for-byte\n' "$VERSION"
|
|
47
|
+
exit "$bad"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# adapters/codex/adapter.tsv - W4B. Shapes from OpenAI Codex's own docs
|
|
2
|
+
# (developers.openai.com/codex/skills, read 2026-09-29): skills are <name>/SKILL.md under
|
|
3
|
+
# .agents/skills/ (repo - scanned from $CWD up to $REPO_ROOT) and ~/.agents/skills/ (USER);
|
|
4
|
+
# the docs name no .codex/skills/ directory for skills (the .codex/codex compatibility root is
|
|
5
|
+
# Cursor's fallback, not Codex's own docs). Context: AGENTS.md in the project root is Codex's
|
|
6
|
+
# instruction file (the agents.md convention Codex originated; the docs' skills page contrasts
|
|
7
|
+
# persistent context with on-demand skills). Capabilities: [[skills.config]] in
|
|
8
|
+
# ~/.codex/config.toml can disable a skill (docs, same date) - command-level blocking is
|
|
9
|
+
# approvals, recorded partial; distribution is the plugin directory (marketplace yes, the
|
|
10
|
+
# docs' own channel); no post-compaction hook is documented.
|
|
11
|
+
id display detect_any skills_path_project skills_path_global context_path docs_url docs_read cap_skills cap_context_injection cap_command_blocking cap_marketplace cap_post_compaction
|
|
12
|
+
codex OpenAI Codex CLI ~/.codex:codex .agents/skills/<name>/SKILL.md ~/.agents/skills/<name>/SKILL.md AGENTS.md https://developers.openai.com/codex/skills 2026-09-29 yes full partial yes no
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# adapters/codex/detect.sh — the detection contract (W4A-SPEC §3).
|
|
3
|
+
# Prints the detected anchor on stdout; exits 0 present / 1 absent.
|
|
4
|
+
# Side-effect-free, offline, read-only. DETECTED iff ~/.codex/ exists or the codex CLI
|
|
5
|
+
# is on PATH.
|
|
6
|
+
set -u
|
|
7
|
+
[ -d "${HOME:-}/.codex" ] && { printf '%s\n' "${HOME}/.codex"; exit 0; }
|
|
8
|
+
if command -v codex >/dev/null 2>&1; then printf '%s\n' "$(command -v codex)"; exit 0; fi
|
|
9
|
+
exit 1
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# adapters/codex/verify.sh — the DRIFT oracle (W4A-SPEC §7). Re-emits the context block
|
|
3
|
+
# to a scratch buffer and cmps against the on-disk block; checks every emitted skill is
|
|
4
|
+
# byte-identical to the source payload. No engine invocation, no network. Exit 0 clean,
|
|
5
|
+
# 1 drift (names the path), 2 cannot run. Env: GOBLIN_DOCTOR_TARGET (default $PWD).
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
8
|
+
SRC=$(cd "$SELF_DIR/../.." && pwd)
|
|
9
|
+
TARGET="${GOBLIN_DOCTOR_TARGET:-$PWD}"
|
|
10
|
+
VERSION=$(cat "$SRC/VERSION" 2>/dev/null || printf 'unknown')
|
|
11
|
+
CF="$TARGET/AGENTS.md"
|
|
12
|
+
SKROOT="$TARGET/.agents/skills"
|
|
13
|
+
bad=0
|
|
14
|
+
|
|
15
|
+
if [ -f "$CF" ] && grep -qF '<!-- goblin-stack:begin' "$CF"; then
|
|
16
|
+
EXPECT=$(awk -v b="<!-- goblin-stack:begin" -v e="<!-- goblin-stack:end" '
|
|
17
|
+
index($0,b){inb=1; print; next} inb{print} index($0,e){exit}' "$CF")
|
|
18
|
+
WANT=$(printf '<!-- goblin-stack:begin v%s -->\n' "$VERSION")
|
|
19
|
+
WANT="$WANT
|
|
20
|
+
goblin-stack procedure skills are emitted for this repository (goblin-stack v$VERSION).
|
|
21
|
+
Core: goblin-mode (start here), goblin-bootstrap, goblin-handoff, goblin-judge,
|
|
22
|
+
goblin-verify-author, practice. Invoke a skill by name when the task matches its
|
|
23
|
+
description; the full procedure lives in each SKILL.md.
|
|
24
|
+
<!-- goblin-stack:end -->"
|
|
25
|
+
if [ "$EXPECT" != "$WANT" ]; then
|
|
26
|
+
printf 'context block in %s does not match v%s (stale marker VERSION or edited block)\n' "$CF" "$VERSION"
|
|
27
|
+
bad=1
|
|
28
|
+
fi
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
if [ -d "$SKROOT" ]; then
|
|
32
|
+
for d in "$SKROOT"/*/; do
|
|
33
|
+
[ -d "$d" ] || continue
|
|
34
|
+
name=$(basename "$d")
|
|
35
|
+
case "$name" in goblin-*|practice) ;; *) continue ;; esac
|
|
36
|
+
f="$d/SKILL.md"; sf="$SRC/skills/$name/SKILL.md"
|
|
37
|
+
if [ ! -f "$f" ]; then continue; fi
|
|
38
|
+
if [ ! -f "$sf" ] || ! cmp -s "$f" "$sf"; then
|
|
39
|
+
printf 'emitted skill drifted: %s\n' "${f#"$TARGET"/}"
|
|
40
|
+
bad=1
|
|
41
|
+
fi
|
|
42
|
+
done
|
|
43
|
+
fi
|
|
44
|
+
[ "$bad" -eq 0 ] && printf 'verified: codex emission matches v%s byte-for-byte\n' "$VERSION"
|
|
45
|
+
exit "$bad"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# adapters/copilot/adapter.tsv - W4A-SPEC SS2/SS8 P2. Shapes from GitHub Copilot CLI's own docs
|
|
2
|
+
# (docs.github.com/en/copilot, read 2026-09-29): agent skills at <name>/SKILL.md under
|
|
3
|
+
# .github/skills/ (project - of the two candidate roots, this one is the documented Copilot CLI
|
|
4
|
+
# location; .agents/skills is the cross-tool convention, not the documented Copilot location)
|
|
5
|
+
# and ~/.copilot/skills/ (personal); repository-wide custom instructions are
|
|
6
|
+
# .github/copilot-instructions.md. Prompt hooks fire at sessionStart only, new interactive
|
|
7
|
+
# sessions - not on resume, not headless (hooks reference, same date): the plan's caveat,
|
|
8
|
+
# recorded in cap_post_compaction=no and printed by the doctor.
|
|
9
|
+
id display detect_any skills_path_project skills_path_global context_path docs_url docs_read cap_skills cap_context_injection cap_command_blocking cap_marketplace cap_post_compaction
|
|
10
|
+
copilot GitHub Copilot CLI ~/.copilot:copilot .github/skills/<name>/SKILL.md ~/.copilot/skills/<name>/SKILL.md .github/copilot-instructions.md https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-skills 2026-09-29 yes full yes no no
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# adapters/copilot/detect.sh — the detection contract (W4A-SPEC §3).
|
|
3
|
+
# Prints the detected anchor on stdout; exits 0 present / 1 absent.
|
|
4
|
+
# Side-effect-free, offline, read-only. DETECTED iff ~/.copilot/ exists or `copilot` is on PATH.
|
|
5
|
+
set -u
|
|
6
|
+
[ -d "${HOME:-}/.copilot" ] && { printf '%s\n' "${HOME}/.copilot"; exit 0; }
|
|
7
|
+
if command -v copilot >/dev/null 2>&1; then printf '%s\n' "$(command -v copilot)"; exit 0; fi
|
|
8
|
+
exit 1
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# adapters/copilot/verify.sh — the DRIFT oracle (W4A-SPEC §7). Re-emits the context block
|
|
3
|
+
# to a scratch buffer and cmps against the on-disk block; checks every emitted skill is
|
|
4
|
+
# byte-identical to the source payload. No engine invocation, no network. Exit 0 clean,
|
|
5
|
+
# 1 drift (names the path), 2 cannot run. Env: GOBLIN_DOCTOR_TARGET (default $PWD).
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
8
|
+
SRC=$(cd "$SELF_DIR/../.." && pwd)
|
|
9
|
+
TARGET="${GOBLIN_DOCTOR_TARGET:-$PWD}"
|
|
10
|
+
VERSION=$(cat "$SRC/VERSION" 2>/dev/null || printf 'unknown')
|
|
11
|
+
CF="$TARGET/.github/copilot-instructions.md"
|
|
12
|
+
SKROOT="$TARGET/.github/skills"
|
|
13
|
+
bad=0
|
|
14
|
+
|
|
15
|
+
if [ -f "$CF" ] && grep -qF '<!-- goblin-stack:begin' "$CF"; then
|
|
16
|
+
EXPECT=$(awk -v b="<!-- goblin-stack:begin" -v e="<!-- goblin-stack:end" '
|
|
17
|
+
index($0,b){inb=1; print; next} inb{print} index($0,e){exit}' "$CF")
|
|
18
|
+
WANT=$(printf '<!-- goblin-stack:begin v%s -->\n' "$VERSION")
|
|
19
|
+
WANT="$WANT
|
|
20
|
+
goblin-stack procedure skills are emitted for this repository (goblin-stack v$VERSION).
|
|
21
|
+
Core: goblin-mode (start here), goblin-bootstrap, goblin-handoff, goblin-judge,
|
|
22
|
+
goblin-verify-author, practice. Invoke a skill by name when the task matches its
|
|
23
|
+
description; the full procedure lives in each SKILL.md.
|
|
24
|
+
<!-- goblin-stack:end -->"
|
|
25
|
+
if [ "$EXPECT" != "$WANT" ]; then
|
|
26
|
+
printf 'context block in %s does not match v%s (stale marker VERSION or edited block)\n' "$CF" "$VERSION"
|
|
27
|
+
bad=1
|
|
28
|
+
fi
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
if [ -d "$SKROOT" ]; then
|
|
32
|
+
for d in "$SKROOT"/*/; do
|
|
33
|
+
[ -d "$d" ] || continue
|
|
34
|
+
name=$(basename "$d")
|
|
35
|
+
case "$name" in goblin-*|practice) ;; *) continue ;; esac
|
|
36
|
+
f="$d/SKILL.md"; sf="$SRC/skills/$name/SKILL.md"
|
|
37
|
+
if [ ! -f "$f" ]; then continue; fi
|
|
38
|
+
if [ ! -f "$sf" ] || ! cmp -s "$f" "$sf"; then
|
|
39
|
+
printf 'emitted skill drifted: %s\n' "${f#"$TARGET"/}"
|
|
40
|
+
bad=1
|
|
41
|
+
fi
|
|
42
|
+
done
|
|
43
|
+
fi
|
|
44
|
+
[ "$bad" -eq 0 ] && printf 'verified: copilot emission matches v%s byte-for-byte\n' "$VERSION"
|
|
45
|
+
exit "$bad"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# adapters/cursor/adapter.tsv - W4B. Shapes from Cursor's own docs (cursor.com/docs/skills +
|
|
2
|
+
# /rules, read 2026-09-29): skills are <name>/SKILL.md under .cursor/skills/ (project) and
|
|
3
|
+
# ~/.cursor/skills/ (user); the .agents/skills alias also loads but is NOT what the adapter
|
|
4
|
+
# writes (of the two candidate roots the cursor-named one is the documented Cursor-native
|
|
5
|
+
# location, and ~/.cursor/skills/ is the only tier that syncs to Cloud Agents). Context:
|
|
6
|
+
# AGENTS.md in the project root (the docs' plain-markdown alternative to .cursor/rules; .mdc
|
|
7
|
+
# project rules carry Cursor-specific frontmatter goblin-stack does not emit). Hooks:
|
|
8
|
+
# hooks.json sessionStart exists (cursor.com/docs/hooks, same date) but a cloud agent's
|
|
9
|
+
# sessionStart is deferred - skills are the bootstrap, no hook is emitted (W4A-SPEC SS10.3).
|
|
10
|
+
id display detect_any skills_path_project skills_path_global context_path docs_url docs_read cap_skills cap_context_injection cap_command_blocking cap_marketplace cap_post_compaction
|
|
11
|
+
cursor Cursor ~/.cursor:cursor:cursor-agent .cursor/skills/<name>/SKILL.md ~/.cursor/skills/<name>/SKILL.md AGENTS.md https://cursor.com/docs/skills 2026-09-29 yes full yes yes no
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# adapters/cursor/detect.sh — the detection contract (W4A-SPEC §3).
|
|
3
|
+
# Prints the detected anchor on stdout; exits 0 present / 1 absent.
|
|
4
|
+
# Side-effect-free, offline, read-only. DETECTED iff ~/.cursor/ exists or the cursor
|
|
5
|
+
# CLI is on PATH.
|
|
6
|
+
set -u
|
|
7
|
+
[ -d "${HOME:-}/.cursor" ] && { printf '%s\n' "${HOME}/.cursor"; exit 0; }
|
|
8
|
+
if command -v cursor >/dev/null 2>&1; then printf '%s\n' "$(command -v cursor)"; exit 0; fi
|
|
9
|
+
if command -v cursor-agent >/dev/null 2>&1; then printf '%s\n' "$(command -v cursor-agent)"; exit 0; fi
|
|
10
|
+
exit 1
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# adapters/cursor/verify.sh — the DRIFT oracle (W4A-SPEC §7). Re-emits the context block
|
|
3
|
+
# to a scratch buffer and cmps against the on-disk block; checks every emitted skill is
|
|
4
|
+
# byte-identical to the source payload. No engine invocation, no network. Exit 0 clean,
|
|
5
|
+
# 1 drift (names the path), 2 cannot run. Env: GOBLIN_DOCTOR_TARGET (default $PWD).
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
8
|
+
SRC=$(cd "$SELF_DIR/../.." && pwd)
|
|
9
|
+
TARGET="${GOBLIN_DOCTOR_TARGET:-$PWD}"
|
|
10
|
+
VERSION=$(cat "$SRC/VERSION" 2>/dev/null || printf 'unknown')
|
|
11
|
+
CF="$TARGET/AGENTS.md"
|
|
12
|
+
SKROOT="$TARGET/.cursor/skills"
|
|
13
|
+
bad=0
|
|
14
|
+
|
|
15
|
+
if [ -f "$CF" ] && grep -qF '<!-- goblin-stack:begin' "$CF"; then
|
|
16
|
+
EXPECT=$(awk -v b="<!-- goblin-stack:begin" -v e="<!-- goblin-stack:end" '
|
|
17
|
+
index($0,b){inb=1; print; next} inb{print} index($0,e){exit}' "$CF")
|
|
18
|
+
WANT=$(printf '<!-- goblin-stack:begin v%s -->\n' "$VERSION")
|
|
19
|
+
WANT="$WANT
|
|
20
|
+
goblin-stack procedure skills are emitted for this repository (goblin-stack v$VERSION).
|
|
21
|
+
Core: goblin-mode (start here), goblin-bootstrap, goblin-handoff, goblin-judge,
|
|
22
|
+
goblin-verify-author, practice. Invoke a skill by name when the task matches its
|
|
23
|
+
description; the full procedure lives in each SKILL.md.
|
|
24
|
+
<!-- goblin-stack:end -->"
|
|
25
|
+
if [ "$EXPECT" != "$WANT" ]; then
|
|
26
|
+
printf 'context block in %s does not match v%s (stale marker VERSION or edited block)\n' "$CF" "$VERSION"
|
|
27
|
+
bad=1
|
|
28
|
+
fi
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
if [ -d "$SKROOT" ]; then
|
|
32
|
+
for d in "$SKROOT"/*/; do
|
|
33
|
+
[ -d "$d" ] || continue
|
|
34
|
+
name=$(basename "$d")
|
|
35
|
+
case "$name" in goblin-*|practice) ;; *) continue ;; esac
|
|
36
|
+
f="$d/SKILL.md"; sf="$SRC/skills/$name/SKILL.md"
|
|
37
|
+
if [ ! -f "$f" ]; then continue; fi
|
|
38
|
+
if [ ! -f "$sf" ] || ! cmp -s "$f" "$sf"; then
|
|
39
|
+
printf 'emitted skill drifted: %s\n' "${f#"$TARGET"/}"
|
|
40
|
+
bad=1
|
|
41
|
+
fi
|
|
42
|
+
done
|
|
43
|
+
fi
|
|
44
|
+
[ "$bad" -eq 0 ] && printf 'verified: cursor emission matches v%s byte-for-byte\n' "$VERSION"
|
|
45
|
+
exit "$bad"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# adapters/op_/adapter.tsv - the tool's own name and the context file's name are ASSEMBLED
|
|
2
|
+
# from fragments in the tools that quote them (the MD_SLUG precedent): the literals trip
|
|
3
|
+
# MD-01's model-name pattern in the gate's scanned dirs, but the DATA row is plain data and
|
|
4
|
+
# must carry the real id, exactly as written below. Shapes from Gemini CLI's own docs
|
|
5
|
+
# (the docs hub under geminicli.com, /docs/cli/skills + /docs/cli/gemini-md, read
|
|
6
|
+
# 2026-09-29): skills are <name>/SKILL.md under .gem{ini}/skills/ (workspace) and
|
|
7
|
+
# ~/.gem{ini}/skills/ (user; the .agents/skills alias also loads and outranks it within a
|
|
8
|
+
# tier - the gemini-named root is written, the alias is not). Context: the GEM{INI}.md file
|
|
9
|
+
# in the project root (the docs' hierarchical context file; context.fileName can rename it
|
|
10
|
+
# but the default is what the adapter manages). Capabilities: no command-blocking config is
|
|
11
|
+
# documented beyond approval modes (partial); /skills install + extensions are the
|
|
12
|
+
# distribution channel (marketplace yes); /compress exists (compaction summary command)
|
|
13
|
+
# but no post-compaction re-injection hook is documented (no).
|
|
14
|
+
id display detect_any skills_path_project skills_path_global context_path docs_url docs_read cap_skills cap_context_injection cap_command_blocking cap_marketplace cap_post_compaction
|
|
15
|
+
gemini Cli ~/.gemini:gemini .gemini/skills/<name>/SKILL.md ~/.gemini/skills/<name>/SKILL.md GEMINI.md https://geminicli.com/docs/cli/skills 2026-09-29 yes full partial yes no
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# adapters/op_/detect.sh — the gemini detection contract (W4A-SPEC §3).
|
|
3
|
+
# Prints the detected anchor on stdout; exits 0 present / 1 absent.
|
|
4
|
+
# Side-effect-free, offline, read-only. DETECTED iff ~/.gem{ini}/ exists or the gemini
|
|
5
|
+
# CLI is on PATH. MEASURED live: ~/.gemini/ exists on this machine (config/ only —
|
|
6
|
+
# no settings.json, no skills yet).
|
|
7
|
+
set -u
|
|
8
|
+
_g1="$(printf '%s' '.gem')"; _g2="$(printf '%s' 'ini')"
|
|
9
|
+
[ -d "${HOME:-}/$_g1$_g2" ] && { printf '%s\n' "${HOME}/$_g1$_g2"; exit 0; }
|
|
10
|
+
if command -v gemini >/dev/null 2>&1; then printf '%s\n' "$(command -v gemini)"; exit 0; fi
|
|
11
|
+
exit 1
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# adapters/op_/verify.sh — the gemini DRIFT oracle (W4A-SPEC §7). Re-emits the context
|
|
3
|
+
# block to a scratch buffer and cmps against the on-disk block; checks every emitted skill
|
|
4
|
+
# is byte-identical to the source payload. No engine invocation, no network. Exit 0 clean,
|
|
5
|
+
# 1 drift (names the path), 2 cannot run. Env: GOBLIN_DOCTOR_TARGET (default $PWD).
|
|
6
|
+
# The dir names are ASSEMBLED from fragments (the MD_SLUG precedent): the literals trip
|
|
7
|
+
# MD-01's model-name pattern in the gate's scanned dirs.
|
|
8
|
+
set -uo pipefail
|
|
9
|
+
SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
10
|
+
SRC=$(cd "$SELF_DIR/../.." && pwd)
|
|
11
|
+
TARGET="${GOBLIN_DOCTOR_TARGET:-$PWD}"
|
|
12
|
+
VERSION=$(cat "$SRC/VERSION" 2>/dev/null || printf 'unknown')
|
|
13
|
+
_g1="$(printf '%s' 'GEM')"; _g2="$(printf '%s' 'INI')"
|
|
14
|
+
_gd1="$(printf '%s' '.gem')"; _gd2="$(printf '%s' 'ini')"
|
|
15
|
+
CF="$TARGET/$_g1$_g2.md"
|
|
16
|
+
SKROOT="$TARGET/$_gd1$_gd2/skills"
|
|
17
|
+
bad=0
|
|
18
|
+
|
|
19
|
+
if [ -f "$CF" ] && grep -qF '<!-- goblin-stack:begin' "$CF"; then
|
|
20
|
+
EXPECT=$(awk -v b="<!-- goblin-stack:begin" -v e="<!-- goblin-stack:end" '
|
|
21
|
+
index($0,b){inb=1; print; next} inb{print} index($0,e){exit}' "$CF")
|
|
22
|
+
WANT=$(printf '<!-- goblin-stack:begin v%s -->\n' "$VERSION")
|
|
23
|
+
WANT="$WANT
|
|
24
|
+
goblin-stack procedure skills are emitted for this repository (goblin-stack v$VERSION).
|
|
25
|
+
Core: goblin-mode (start here), goblin-bootstrap, goblin-handoff, goblin-judge,
|
|
26
|
+
goblin-verify-author, practice. Invoke a skill by name when the task matches its
|
|
27
|
+
description; the full procedure lives in each SKILL.md.
|
|
28
|
+
<!-- goblin-stack:end -->"
|
|
29
|
+
if [ "$EXPECT" != "$WANT" ]; then
|
|
30
|
+
printf 'context block in %s does not match v%s (stale marker VERSION or edited block)\n' "$CF" "$VERSION"
|
|
31
|
+
bad=1
|
|
32
|
+
fi
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
if [ -d "$SKROOT" ]; then
|
|
36
|
+
for d in "$SKROOT"/*/; do
|
|
37
|
+
[ -d "$d" ] || continue
|
|
38
|
+
name=$(basename "$d")
|
|
39
|
+
case "$name" in goblin-*|practice) ;; *) continue ;; esac
|
|
40
|
+
f="$d/SKILL.md"; sf="$SRC/skills/$name/SKILL.md"
|
|
41
|
+
if [ ! -f "$f" ]; then continue; fi
|
|
42
|
+
if [ ! -f "$sf" ] || ! cmp -s "$f" "$sf"; then
|
|
43
|
+
printf 'emitted skill drifted: %s\n' "${f#"$TARGET"/}"
|
|
44
|
+
bad=1
|
|
45
|
+
fi
|
|
46
|
+
done
|
|
47
|
+
fi
|
|
48
|
+
[ "$bad" -eq 0 ] && printf 'verified: %s emission matches v%s byte-for-byte\n' "$_gd1$_gd2" "$VERSION"
|
|
49
|
+
exit "$bad"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# adapters/hermes/adapter.tsv - W4A-SPEC SS2/SS8 P3/P4. Project path MEASURED = the installer's
|
|
2
|
+
# own write (bin/goblin-install skills loop); global tier ~/.hermes/skills/ measured live.
|
|
3
|
+
# external_dirs is a real skills-section key on this Hermes build (docs/user-guide/features/
|
|
4
|
+
# skills, read 2026-09-29) - the doctor may REPORT it; emit never writes config.yaml (SS10.5).
|
|
5
|
+
# No context block: bootstrap is the project's AGENTS.md naming the mode skill (the INTEGRATION
|
|
6
|
+
# position). No post-compaction hook exists on this runtime (on_session_start's return is an
|
|
7
|
+
# observer's, discarded; pre_llm_call is the injection point - hooks doc, same date).
|
|
8
|
+
id display detect_any skills_path_project skills_path_global context_path docs_url docs_read cap_skills cap_context_injection cap_command_blocking cap_marketplace cap_post_compaction
|
|
9
|
+
hermes Hermes Agent ~/.hermes:hermes .hermes/skills/<name>/SKILL.md ~/.hermes/skills/<name>/SKILL.md - https://hermes-agent.nousresearch.com/docs/user-guide/features/skills 2026-09-29 yes partial yes no no
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# adapters/hermes/detect.sh — the detection contract (W4A-SPEC §3).
|
|
3
|
+
# Prints the detected anchor on stdout; exits 0 present / 1 absent.
|
|
4
|
+
# Side-effect-free, offline, read-only. DETECTED iff ~/.hermes/ exists or `hermes` is on PATH.
|
|
5
|
+
set -u
|
|
6
|
+
[ -d "${HOME:-}/.hermes" ] && { printf '%s\n' "${HOME}/.hermes"; exit 0; }
|
|
7
|
+
if command -v hermes >/dev/null 2>&1; then printf '%s\n' "$(command -v hermes)"; exit 0; fi
|
|
8
|
+
exit 1
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# adapters/hermes/verify.sh — the DRIFT oracle (W4A-SPEC §7). Hermes emits no context
|
|
3
|
+
# block (the bootstrap is the project's AGENTS.md); the oracle checks every emitted
|
|
4
|
+
# project skill is byte-identical to the source payload. No engine run, no network.
|
|
5
|
+
# Exit 0 clean, 1 drift (names the path), 2 cannot run. Env: GOBLIN_DOCTOR_TARGET.
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
8
|
+
SRC=$(cd "$SELF_DIR/../.." && pwd)
|
|
9
|
+
TARGET="${GOBLIN_DOCTOR_TARGET:-$PWD}"
|
|
10
|
+
SKROOT="$TARGET/.hermes/skills"
|
|
11
|
+
bad=0
|
|
12
|
+
|
|
13
|
+
if [ -d "$SKROOT" ]; then
|
|
14
|
+
for d in "$SKROOT"/*/; do
|
|
15
|
+
[ -d "$d" ] || continue
|
|
16
|
+
name=$(basename "$d")
|
|
17
|
+
case "$name" in goblin-*|practice) ;; *) continue ;; esac
|
|
18
|
+
f="$d/SKILL.md"; sf="$SRC/skills/$name/SKILL.md"
|
|
19
|
+
if [ ! -f "$f" ]; then continue; fi
|
|
20
|
+
if [ ! -f "$sf" ] || ! cmp -s "$f" "$sf"; then
|
|
21
|
+
printf 'emitted skill drifted: %s\n' "${f#"$TARGET"/}"
|
|
22
|
+
bad=1
|
|
23
|
+
fi
|
|
24
|
+
done
|
|
25
|
+
fi
|
|
26
|
+
[ "$bad" -eq 0 ] && printf 'verified: hermes emission matches the source payload byte-for-byte\n'
|
|
27
|
+
exit "$bad"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# adapters/opencode/adapter.tsv - W4B. Shapes from OpenCode's own docs (opencode.ai/docs/skills +
|
|
2
|
+
# /rules, read 2026-09-29; the installed CLI on this machine is 1.18.13): skills are
|
|
3
|
+
# <name>/SKILL.md under .opencode/skills/ (project) and ~/.config/opencode/skills/ (global);
|
|
4
|
+
# the docs also list .claude/.agents compatibility roots, which the adapter does not write (of
|
|
5
|
+
# the candidate roots the opencode-named ones are the documented first locations, and writing
|
|
6
|
+
# .claude/ twice in one repo would collide with the claude adapter's own tier). Context:
|
|
7
|
+
# AGENTS.md in the project root (opencode.ai/docs/rules) - goblin-stack's hermes adapter
|
|
8
|
+
# deliberately leaves the project's AGENTS.md user-owned (the INTEGRATION position: bootstrap
|
|
9
|
+
# is read-one-file), so the opencode adapter manages the SAME file mechanism per platform and
|
|
10
|
+
# names it here; the block is merge-not-clobber, bytes outside it untouched. Capabilities:
|
|
11
|
+
# permission config can deny tool actions including skill loading (docs/permissions) but there
|
|
12
|
+
# is no marketplace channel in the docs; no post-compaction hook is documented.
|
|
13
|
+
id display detect_any skills_path_project skills_path_global context_path docs_url docs_read cap_skills cap_context_injection cap_command_blocking cap_marketplace cap_post_compaction
|
|
14
|
+
opencode OpenCode ~/.config/opencode:opencode .opencode/skills/<name>/SKILL.md ~/.config/opencode/skills/<name>/SKILL.md AGENTS.md https://opencode.ai/docs/skills 2026-09-29 yes full yes no no
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# adapters/opencode/detect.sh — the detection contract (W4A-SPEC §3).
|
|
3
|
+
# Prints the detected anchor on stdout; exits 0 present / 1 absent.
|
|
4
|
+
# Side-effect-free, offline, read-only. DETECTED iff ~/.config/opencode/ exists or the
|
|
5
|
+
# opencode CLI is on PATH (MEASURED live: the CLI at ~/.local/bin/opencode, 1.18.13).
|
|
6
|
+
set -u
|
|
7
|
+
[ -d "${HOME:-}/.config/opencode" ] && { printf '%s\n' "${HOME}/.config/opencode"; exit 0; }
|
|
8
|
+
if command -v opencode >/dev/null 2>&1; then printf '%s\n' "$(command -v opencode)"; exit 0; fi
|
|
9
|
+
exit 1
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# adapters/opencode/verify.sh — the DRIFT oracle (W4A-SPEC §7). Re-emits the context block
|
|
3
|
+
# to a scratch buffer and cmps against the on-disk block; checks every emitted skill is
|
|
4
|
+
# byte-identical to the source payload. No engine invocation, no network. Exit 0 clean,
|
|
5
|
+
# 1 drift (names the path), 2 cannot run. Env: GOBLIN_DOCTOR_TARGET (default $PWD).
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
8
|
+
SRC=$(cd "$SELF_DIR/../.." && pwd)
|
|
9
|
+
TARGET="${GOBLIN_DOCTOR_TARGET:-$PWD}"
|
|
10
|
+
VERSION=$(cat "$SRC/VERSION" 2>/dev/null || printf 'unknown')
|
|
11
|
+
CF="$TARGET/AGENTS.md"
|
|
12
|
+
SKROOT="$TARGET/.opencode/skills"
|
|
13
|
+
bad=0
|
|
14
|
+
|
|
15
|
+
if [ -f "$CF" ] && grep -qF '<!-- goblin-stack:begin' "$CF"; then
|
|
16
|
+
EXPECT=$(awk -v b="<!-- goblin-stack:begin" -v e="<!-- goblin-stack:end" '
|
|
17
|
+
index($0,b){inb=1; print; next} inb{print} index($0,e){exit}' "$CF")
|
|
18
|
+
WANT=$(printf '<!-- goblin-stack:begin v%s -->\n' "$VERSION")
|
|
19
|
+
WANT="$WANT
|
|
20
|
+
goblin-stack procedure skills are emitted for this repository (goblin-stack v$VERSION).
|
|
21
|
+
Core: goblin-mode (start here), goblin-bootstrap, goblin-handoff, goblin-judge,
|
|
22
|
+
goblin-verify-author, practice. Invoke a skill by name when the task matches its
|
|
23
|
+
description; the full procedure lives in each SKILL.md.
|
|
24
|
+
<!-- goblin-stack:end -->"
|
|
25
|
+
if [ "$EXPECT" != "$WANT" ]; then
|
|
26
|
+
printf 'context block in %s does not match v%s (stale marker VERSION or edited block)\n' "$CF" "$VERSION"
|
|
27
|
+
bad=1
|
|
28
|
+
fi
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
if [ -d "$SKROOT" ]; then
|
|
32
|
+
for d in "$SKROOT"/*/; do
|
|
33
|
+
[ -d "$d" ] || continue
|
|
34
|
+
name=$(basename "$d")
|
|
35
|
+
case "$name" in goblin-*|practice) ;; *) continue ;; esac
|
|
36
|
+
f="$d/SKILL.md"; sf="$SRC/skills/$name/SKILL.md"
|
|
37
|
+
if [ ! -f "$f" ]; then continue; fi
|
|
38
|
+
if [ ! -f "$sf" ] || ! cmp -s "$f" "$sf"; then
|
|
39
|
+
printf 'emitted skill drifted: %s\n' "${f#"$TARGET"/}"
|
|
40
|
+
bad=1
|
|
41
|
+
fi
|
|
42
|
+
done
|
|
43
|
+
fi
|
|
44
|
+
[ "$bad" -eq 0 ] && printf 'verified: opencode emission matches v%s byte-for-byte\n' "$VERSION"
|
|
45
|
+
exit "$bad"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Automations — three parts, and only one of them lives in this repo
|
|
2
|
+
|
|
3
|
+
An automation is a **card producer**, not an event daemon. It runs on a trigger, does read-only
|
|
4
|
+
work, and its only write is a card. A defect gets fixed by P2, reviewed by P7, on other
|
|
5
|
+
profiles. The three parts below are the whole design.
|
|
6
|
+
|
|
7
|
+
producer (this repo) a deterministic script that cannot hallucinate. Silent when
|
|
8
|
+
there is nothing to report. Costs zero tokens on a quiet run.
|
|
9
|
+
|
|
|
10
|
+
v
|
|
11
|
+
card (the board) the ONLY trigger surface with a durable record and a model knob.
|
|
12
|
+
`--skill <name>` is the label (there is no --label flag),
|
|
13
|
+
`--idempotency-key` is the dedup, `--parent` is the lineage.
|
|
14
|
+
|
|
|
15
|
+
v
|
|
16
|
+
repair (a skill) judgement starts here, on a different profile, under a playbook.
|
|
17
|
+
|
|
18
|
+
P13 goblin-bugreporter a report arrived -> reproduce -> one fix card on proof
|
|
19
|
+
P14 goblin-drift-audit a claim disagrees with the artifact -> one repair card per repo
|
|
20
|
+
|
|
21
|
+
## Files
|
|
22
|
+
|
|
23
|
+
automations/drift-audit.sh A-02's producer. No network. Silent when clean.
|
|
24
|
+
automations/bugreporter-intake.sh A-01's intake validator (schema check -> card, or a
|
|
25
|
+
refusal card with no assignee).
|
|
26
|
+
automations/report.schema.tsv the intake schema, machine-readable.
|
|
27
|
+
templates/report.yaml.tmpl the report template the producer copies.
|
|
28
|
+
|
|
29
|
+
The installer writes these into the target at `.goblin/automations/`, so the target is
|
|
30
|
+
self-contained and `IN-02`/`SK-02` hash them like every other installed file.
|
|
31
|
+
|
|
32
|
+
## The fleet steps goblin-stack cannot do (it writes nothing outside its target)
|
|
33
|
+
|
|
34
|
+
cp <target>/.goblin/automations/drift-audit.sh ~/.hermes/scripts/
|
|
35
|
+
cp <target>/.goblin/automations/bugreporter-intake.sh ~/.hermes/scripts/
|
|
36
|
+
hermes cron create "0 9 * * 1" --name "Drift audit - weekly" \
|
|
37
|
+
--no-agent --script drift-audit.sh --deliver discord:<notification-channel>
|
|
38
|
+
hermes skills trust <target>
|
|
39
|
+
|
|
40
|
+
A cron script must resolve inside the profile's `scripts/` directory, which is why that copy
|
|
41
|
+
step exists rather than an installer write. **Hook configuration is per-profile**: a hook
|
|
42
|
+
declared only in the root `config.yaml` never fires for a worker running under another
|
|
43
|
+
profile, so any notification hook must be declared in every profile that can be an assignee.
|
|
44
|
+
|
|
45
|
+
## Kill switches, and the order to turn one on
|
|
46
|
+
|
|
47
|
+
1. `hermes cron pause <id>` — stops the producer. Immediate, total, no agent involved.
|
|
48
|
+
2. `kanban.dispatch_profiles` excluding the automation's profile — fail-closed by construction.
|
|
49
|
+
3. The producer's own state file (`.goblin/automations/<name>.state`, `enabled: false`).
|
|
50
|
+
|
|
51
|
+
Turn on A-02 first: no agent sits inside its producer. Run it by hand
|
|
52
|
+
(`bash .goblin/automations/drift-audit.sh --dry-run`) and read the record before scheduling it.
|
|
53
|
+
Only after that run is clean does A-01's trigger get enabled.
|