loki-mode 7.26.0 → 7.28.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.
@@ -0,0 +1,321 @@
1
+ #!/usr/bin/env bash
2
+ # autonomy/grill.sh - Loki spec interrogation (loki grill).
3
+ #
4
+ # Net-new capability (internal/SDD-PANEL-A.md ranks it HIGHEST: no Claude Code
5
+ # equivalent, no Loki equivalent). It exposes the Devil's-Advocate /
6
+ # anti-sycophancy posture that today fires only INSIDE the build loop
7
+ # (run.sh:7807) as a standalone PRE-build interrogation of a spec.
8
+ #
9
+ # What it does: invokes the provider ONCE (claude -p, the same single-shot
10
+ # pattern used for in-loop adversarial review and the haiku USAGE regen at
11
+ # run.sh:9832) with a Devil's-Advocate prompt that produces the 10-15 hardest
12
+ # questions exposing ambiguities, missing acceptance criteria, unstated
13
+ # assumptions, and security/scale blind spots. Output is a structured markdown
14
+ # report at .loki/grill/report.md. With --apply it appends a "Grill findings"
15
+ # section to the spec itself.
16
+ #
17
+ # Honest about provider dependency: requires the provider CLI; clean error when
18
+ # absent (no fabricated questions, no silent success).
19
+ #
20
+ # Spec source resolution (first match wins, same as loki spec):
21
+ # 1. explicit path argument
22
+ # 2. prd.md
23
+ # 3. .loki/generated-prd.md
24
+ # 4. PRD.md
25
+ # 5. docs/prd.md
26
+ #
27
+ # Exit codes:
28
+ # 0 report written
29
+ # 2 usage / spec-not-found error
30
+ # 3 provider unavailable or interrogation failed (never silent)
31
+
32
+ set -uo pipefail
33
+
34
+ GRILL_EXIT_OK=0
35
+ GRILL_EXIT_USAGE=2
36
+ GRILL_EXIT_ERROR=3
37
+
38
+ GRILL_DIR_DEFAULT=".loki/grill"
39
+ GRILL_REPORT_NAME="report.md"
40
+
41
+ _grill_log() { printf '[grill] %s\n' "$*" >&2; }
42
+ _grill_err() { printf '[grill][error] %s\n' "$*" >&2; }
43
+
44
+ # ---------------------------------------------------------------------------
45
+ # Resolve the spec source path. Echoes the path on success, empty on failure.
46
+ # Default order favors a hand-written prd.md over a generated one for grilling,
47
+ # since grilling is a pre-build hardening step on the human's intent.
48
+ # ---------------------------------------------------------------------------
49
+ grill_resolve_source() {
50
+ local explicit="${1:-}"
51
+ if [ -n "$explicit" ]; then
52
+ if [ -f "$explicit" ]; then
53
+ printf '%s\n' "$explicit"
54
+ return 0
55
+ fi
56
+ return 1
57
+ fi
58
+ local candidate
59
+ for candidate in \
60
+ "prd.md" \
61
+ ".loki/generated-prd.md" \
62
+ "PRD.md" \
63
+ "docs/prd.md"; do
64
+ if [ -f "$candidate" ]; then
65
+ printf '%s\n' "$candidate"
66
+ return 0
67
+ fi
68
+ done
69
+ return 1
70
+ }
71
+
72
+ # ---------------------------------------------------------------------------
73
+ # Build the Devil's-Advocate interrogation prompt for a given spec body.
74
+ # ---------------------------------------------------------------------------
75
+ grill_build_prompt() {
76
+ local spec_path="$1"
77
+ local spec_body="$2"
78
+ cat <<EOF
79
+ You are a Devil's Advocate spec reviewer. Your job is to interrogate the
80
+ following specification and surface its weaknesses BEFORE any code is written.
81
+ Do not praise it. Do not summarize it. Do not propose an implementation.
82
+
83
+ Produce the 10 to 15 HARDEST questions that expose:
84
+ - ambiguities and underspecified behavior
85
+ - missing or untestable acceptance criteria
86
+ - unstated assumptions and implicit requirements
87
+ - security blind spots (authn/authz, input validation, secrets, data exposure)
88
+ - scale and reliability blind spots (concurrency, failure modes, limits)
89
+ - edge cases the spec does not address
90
+
91
+ Output STRICT markdown in exactly this shape and nothing else:
92
+
93
+ ## Grill findings
94
+
95
+ ### Ambiguities and missing acceptance criteria
96
+ 1. <hard question>
97
+ 2. <hard question>
98
+
99
+ ### Unstated assumptions
100
+ 1. <hard question>
101
+
102
+ ### Security blind spots
103
+ 1. <hard question>
104
+
105
+ ### Scale and reliability blind spots
106
+ 1. <hard question>
107
+
108
+ Each question must be specific to THIS spec (quote or reference the relevant
109
+ part), answerable, and uncomfortable. Total questions across all sections: 10
110
+ to 15. If a category has no real issue, write "None identified." under it
111
+ rather than padding.
112
+
113
+ === SPEC: $spec_path ===
114
+ $spec_body
115
+ === END SPEC ===
116
+ EOF
117
+ }
118
+
119
+ # ---------------------------------------------------------------------------
120
+ # Invoke the provider once and capture the interrogation report.
121
+ # Echoes the report markdown on success; returns nonzero on failure.
122
+ # ---------------------------------------------------------------------------
123
+ # LOW-6: validate the provider (the same checks that produce rc=3) WITHOUT
124
+ # invoking it, so grill_main can fail cleanly BEFORE logging "interrogating ...
125
+ # via <provider>". Returns 0 if the provider is supported and its CLI is present;
126
+ # otherwise prints the same error grill_invoke_provider would and returns rc=3.
127
+ grill_check_provider() {
128
+ local provider="${LOKI_PROVIDER:-claude}"
129
+ case "$provider" in
130
+ claude)
131
+ if ! command -v claude >/dev/null 2>&1; then
132
+ _grill_err "Claude Code CLI not found. Install: https://docs.anthropic.com/en/docs/claude-code"
133
+ return $GRILL_EXIT_ERROR
134
+ fi
135
+ ;;
136
+ codex)
137
+ if ! command -v codex >/dev/null 2>&1; then
138
+ _grill_err "Codex CLI not found."
139
+ return $GRILL_EXIT_ERROR
140
+ fi
141
+ ;;
142
+ *)
143
+ _grill_err "grill currently supports the claude and codex providers (got: $provider)"
144
+ return $GRILL_EXIT_ERROR
145
+ ;;
146
+ esac
147
+ return 0
148
+ }
149
+
150
+ grill_invoke_provider() {
151
+ local prompt="$1"
152
+ local provider="${LOKI_PROVIDER:-claude}"
153
+
154
+ case "$provider" in
155
+ claude)
156
+ if ! command -v claude >/dev/null 2>&1; then
157
+ _grill_err "Claude Code CLI not found. Install: https://docs.anthropic.com/en/docs/claude-code"
158
+ return $GRILL_EXIT_ERROR
159
+ fi
160
+ local out
161
+ # Single-shot, non-interactive. Same pattern as the in-loop
162
+ # adversarial reviewer (run.sh:7807) and USAGE regen (run.sh:9832).
163
+ out="$(printf '%s' "$prompt" \
164
+ | timeout "${LOKI_GRILL_TIMEOUT:-180}" claude --dangerously-skip-permissions --model "${LOKI_GRILL_MODEL:-sonnet}" -p - 2>/dev/null)"
165
+ if [ -z "$out" ]; then
166
+ _grill_err "provider returned no output (timeout or invocation error)"
167
+ return $GRILL_EXIT_ERROR
168
+ fi
169
+ printf '%s\n' "$out"
170
+ return 0
171
+ ;;
172
+ codex)
173
+ if ! command -v codex >/dev/null 2>&1; then
174
+ _grill_err "Codex CLI not found."
175
+ return $GRILL_EXIT_ERROR
176
+ fi
177
+ local out
178
+ out="$(printf '%s' "$prompt" | timeout "${LOKI_GRILL_TIMEOUT:-180}" codex exec --full-auto - 2>/dev/null)"
179
+ if [ -z "$out" ]; then
180
+ _grill_err "provider returned no output"
181
+ return $GRILL_EXIT_ERROR
182
+ fi
183
+ printf '%s\n' "$out"
184
+ return 0
185
+ ;;
186
+ *)
187
+ _grill_err "grill currently supports the claude and codex providers (got: $provider)"
188
+ return $GRILL_EXIT_ERROR
189
+ ;;
190
+ esac
191
+ }
192
+
193
+ # ---------------------------------------------------------------------------
194
+ # Help
195
+ # ---------------------------------------------------------------------------
196
+ grill_help() {
197
+ cat <<'EOF'
198
+ loki grill - interrogate a spec with the hardest questions before you build it.
199
+
200
+ USAGE:
201
+ loki grill [<spec-path>] [options]
202
+
203
+ DESCRIPTION:
204
+ Invokes the provider once with a Devil's-Advocate prompt to produce the
205
+ 10-15 hardest questions exposing ambiguities, missing acceptance criteria,
206
+ unstated assumptions, and security/scale blind spots in a spec. Writes a
207
+ structured markdown report to .loki/grill/report.md.
208
+
209
+ This is a PRE-build hardening step: a grilled spec is a better Reason input
210
+ to the RARV-C loop. It requires the provider CLI and fails cleanly (no
211
+ fabricated questions) when the provider is unavailable.
212
+
213
+ SPEC RESOLUTION (when <spec-path> is omitted, first match wins):
214
+ prd.md -> .loki/generated-prd.md -> PRD.md -> docs/prd.md
215
+
216
+ OPTIONS:
217
+ --apply Append the "Grill findings" section to the spec file itself.
218
+ --out <dir> Output directory for the report. Default: .loki/grill
219
+ -h, --help Show this help.
220
+
221
+ ENVIRONMENT:
222
+ LOKI_PROVIDER Provider to use (claude default; codex supported).
223
+ LOKI_GRILL_MODEL Claude model for the interrogation (default: sonnet).
224
+ LOKI_GRILL_TIMEOUT Per-invocation timeout in seconds (default: 180).
225
+
226
+ EXIT CODES:
227
+ 0 report written
228
+ 2 usage error (spec not found)
229
+ 3 provider unavailable or interrogation failed (never silent)
230
+
231
+ OUTPUT:
232
+ <out>/report.md the structured interrogation report
233
+ EOF
234
+ }
235
+
236
+ # ---------------------------------------------------------------------------
237
+ # Entry point
238
+ # ---------------------------------------------------------------------------
239
+ grill_main() {
240
+ local spec_arg=""
241
+ local out_dir="$GRILL_DIR_DEFAULT"
242
+ local apply="false"
243
+
244
+ while [ $# -gt 0 ]; do
245
+ case "$1" in
246
+ -h|--help) grill_help; return $GRILL_EXIT_OK ;;
247
+ --apply) apply="true"; shift ;;
248
+ --out) out_dir="${2:-}"; shift 2 ;;
249
+ --) shift; break ;;
250
+ -*) _grill_err "unknown option: $1"; grill_help; return $GRILL_EXIT_USAGE ;;
251
+ *)
252
+ if [ -z "$spec_arg" ]; then spec_arg="$1"; else
253
+ _grill_err "unexpected argument: $1"; return $GRILL_EXIT_USAGE
254
+ fi
255
+ shift ;;
256
+ esac
257
+ done
258
+
259
+ local spec_path
260
+ if ! spec_path="$(grill_resolve_source "$spec_arg")"; then
261
+ if [ -n "$spec_arg" ]; then
262
+ _grill_err "spec file not found: $spec_arg"
263
+ else
264
+ _grill_err "no spec found (looked for prd.md, .loki/generated-prd.md, PRD.md, docs/prd.md). Pass a path explicitly."
265
+ fi
266
+ return $GRILL_EXIT_USAGE
267
+ fi
268
+
269
+ local spec_body
270
+ spec_body="$(cat "$spec_path" 2>/dev/null)"
271
+ if [ -z "$spec_body" ]; then
272
+ _grill_err "spec file is empty: $spec_path"
273
+ return $GRILL_EXIT_USAGE
274
+ fi
275
+
276
+ # LOW-6: validate the provider BEFORE logging the interrogation line, so an
277
+ # unavailable/unsupported provider fails cleanly (rc=3) without first printing
278
+ # a misleading "interrogating ... via <provider>" message.
279
+ grill_check_provider || return $GRILL_EXIT_ERROR
280
+
281
+ local prompt
282
+ prompt="$(grill_build_prompt "$spec_path" "$spec_body")"
283
+
284
+ _grill_log "interrogating $spec_path via ${LOKI_PROVIDER:-claude}..."
285
+ local report
286
+ report="$(grill_invoke_provider "$prompt")" || return $GRILL_EXIT_ERROR
287
+
288
+ mkdir -p "$out_dir" || { _grill_err "cannot create $out_dir"; return $GRILL_EXIT_ERROR; }
289
+ local report_path="$out_dir/$GRILL_REPORT_NAME"
290
+
291
+ {
292
+ printf '# Spec grill report\n\n'
293
+ # printf format strings must not begin with '-': bash's printf builtin
294
+ # parses a leading dash as an option flag (rc=2 "invalid option") and
295
+ # silently drops the line. Use '%s\n' with the dash inside the argument.
296
+ printf '%s\n' "- Spec: $spec_path"
297
+ printf '%s\n' "- Generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
298
+ printf '%s\n\n' "- Provider: ${LOKI_PROVIDER:-claude}"
299
+ printf '%s\n' "$report"
300
+ } >"$report_path" || { _grill_err "failed to write $report_path"; return $GRILL_EXIT_ERROR; }
301
+
302
+ _grill_log "report written: $report_path"
303
+ printf 'Grill report: %s\n' "$report_path"
304
+
305
+ if [ "$apply" = "true" ]; then
306
+ {
307
+ printf '\n\n<!-- loki grill: appended %s -->\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
308
+ printf '%s\n' "$report"
309
+ } >>"$spec_path" || { _grill_err "failed to append findings to $spec_path"; return $GRILL_EXIT_ERROR; }
310
+ _grill_log "appended Grill findings to $spec_path"
311
+ printf 'Appended Grill findings to: %s\n' "$spec_path"
312
+ fi
313
+
314
+ return $GRILL_EXIT_OK
315
+ }
316
+
317
+ # Allow direct execution: bash autonomy/grill.sh [args]
318
+ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
319
+ grill_main "$@"
320
+ exit $?
321
+ fi