loki-mode 7.28.1 → 7.29.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,584 @@
1
+ #!/usr/bin/env bash
2
+ # quickstart.sh -- guided first-build interview (v7.29.0, design feature 3).
3
+ #
4
+ # `loki quickstart` is a thin orchestrator over three already-shipped pieces:
5
+ # 1. the provider install offer (autonomy/provider-offer.sh, slice B)
6
+ # 2. the honest cost estimator (show_prd_plan in autonomy/loki, slice A)
7
+ # 3. cmd_start (autonomy/loki) for the actual build
8
+ # plus one new, deterministic, offline keyword matcher over templates/.
9
+ #
10
+ # It NEVER reimplements the runner and NEVER fabricates a number: every figure
11
+ # in step 4 comes from the same estimator cmd_start will run with, so the quote
12
+ # equals the charge by construction (the slice-A honesty keystone).
13
+ #
14
+ # Sourcing contract (load-bearing for tests): this file defines functions ONLY.
15
+ # It runs no top-level command and never calls `main`. autonomy/loki sources it
16
+ # near the top so cmd_quickstart and its helpers are in scope for the dispatch
17
+ # case. Tests source it directly, override the _qs_non_interactive predicate, and
18
+ # stub cmd_start / provider_offer_gate to prove the composition without spending
19
+ # or starting a build. Because it is sourced (not a subprocess), it relies on the
20
+ # caller (autonomy/loki) for SKILL_DIR, the color vars, show_prd_plan,
21
+ # provider_offer_gate, and cmd_start; the test harness provides stubs for those
22
+ # it does not exercise for real.
23
+
24
+ # Guard against double-source.
25
+ if [ -n "${_LOKI_QUICKSTART_SOURCED:-}" ]; then
26
+ return 0 2>/dev/null || true
27
+ fi
28
+ _LOKI_QUICKSTART_SOURCED=1
29
+
30
+ # --- Self-contained colors (ANSI-interpreted, _QS_-prefixed) ---------------
31
+ # autonomy/loki's own BOLD/RED/etc. hold LITERAL "\033[..." strings meant for
32
+ # `echo -e`; this file uses printf, so it defines its OWN $'...'-interpreted
33
+ # vars (the provider-offer.sh pattern). They are _QS_-prefixed so sourcing this
34
+ # file never clobbers loki's color globals (the rest of the CLI uses echo -e).
35
+ # Honors NO_COLOR and non-TTY.
36
+ if [ -n "${NO_COLOR:-}" ] || [ ! -t 1 ]; then
37
+ _QS_BOLD=''; _QS_DIM=''; _QS_CYAN=''; _QS_YELLOW=''; _QS_RED=''; _QS_NC=''
38
+ else
39
+ _QS_BOLD=$'\033[1m'
40
+ _QS_DIM=$'\033[2m'
41
+ _QS_CYAN=$'\033[0;36m'
42
+ _QS_YELLOW=$'\033[1;33m'
43
+ _QS_RED=$'\033[0;31m'
44
+ _QS_NC=$'\033[0m'
45
+ fi
46
+
47
+ # _qs_templates_dir: resolve the templates directory. Prefers SKILL_DIR (set by
48
+ # autonomy/loki); falls back to this script's sibling templates/ for tests.
49
+ _qs_templates_dir() {
50
+ if [ -n "${SKILL_DIR:-}" ] && [ -d "${SKILL_DIR}/templates" ]; then
51
+ printf '%s\n' "${SKILL_DIR}/templates"
52
+ return 0
53
+ fi
54
+ local self_dir
55
+ self_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
56
+ printf '%s\n' "$(cd "$self_dir/.." && pwd)/templates"
57
+ }
58
+
59
+ # _qs_non_interactive: true (0) when we must NEVER prompt (non-TTY or CI).
60
+ # Named (not inlined) so tests can override it to drive the interview without a
61
+ # real terminal. Mirrors provider-offer.sh's _po_non_interactive idiom.
62
+ _qs_non_interactive() {
63
+ [ ! -t 0 ] && return 0
64
+ [ ! -t 1 ] && return 0
65
+ [ -n "${CI:-}" ] && return 0
66
+ return 1
67
+ }
68
+
69
+ # _qs_assume_yes: true when the user opted into auto-confirm (--yes /
70
+ # LOKI_ASSUME_YES / the LOKI_AUTO_CONFIRM that --yes already sets at loki:1013).
71
+ _qs_assume_yes() {
72
+ [ "${LOKI_ASSUME_YES:-}" = "1" ] && return 0
73
+ [ "${LOKI_ASSUME_YES:-}" = "true" ] && return 0
74
+ [ "${LOKI_AUTO_CONFIRM:-}" = "true" ] && return 0
75
+ return 1
76
+ }
77
+
78
+ # _qs_keyword_map: curated keyword -> template -> weight table (design 3.5).
79
+ # Format per line: keyword:template:weight. Deterministic, offline, no LLM.
80
+ # A template's own flagship term carries a higher weight so the head noun wins
81
+ # (e.g. "todo" -> simple-todo-app outranks incidental account/user matches).
82
+ _qs_keyword_map() {
83
+ cat <<'MAP'
84
+ todo:simple-todo-app:5
85
+ list:simple-todo-app:3
86
+ auth:rest-api-auth:3
87
+ auth:saas-starter:3
88
+ login:rest-api-auth:3
89
+ login:saas-starter:3
90
+ account:saas-starter:4
91
+ accounts:saas-starter:4
92
+ account:rest-api-auth:3
93
+ accounts:rest-api-auth:3
94
+ user:saas-starter:3
95
+ users:saas-starter:3
96
+ user:rest-api-auth:2
97
+ users:rest-api-auth:2
98
+ signup:saas-starter:3
99
+ tenant:saas-starter:3
100
+ saas:saas-starter:3
101
+ subscription:saas-starter:3
102
+ billing:saas-starter:3
103
+ api:rest-api:3
104
+ api:api-only:3
105
+ endpoint:rest-api:3
106
+ endpoints:rest-api:3
107
+ rest:rest-api:3
108
+ backend:rest-api:3
109
+ bot:discord-bot:3
110
+ bot:slack-bot:3
111
+ discord:discord-bot:3
112
+ slack:slack-bot:3
113
+ chat:ai-chatbot:3
114
+ chatbot:ai-chatbot:3
115
+ ai:ai-chatbot:3
116
+ llm:ai-chatbot:3
117
+ shop:e-commerce:3
118
+ store:e-commerce:3
119
+ ecommerce:e-commerce:3
120
+ commerce:e-commerce:3
121
+ cart:e-commerce:3
122
+ blog:blog-platform:3
123
+ cms:blog-platform:3
124
+ post:blog-platform:3
125
+ posts:blog-platform:3
126
+ dashboard:dashboard:3
127
+ analytics:dashboard:3
128
+ admin:dashboard:3
129
+ cli:cli-tool:3
130
+ terminal:cli-tool:3
131
+ command:cli-tool:3
132
+ game:game:3
133
+ play:game:3
134
+ mobile:mobile-app:3
135
+ ios:mobile-app:3
136
+ android:mobile-app:3
137
+ scraper:web-scraper:3
138
+ scrape:web-scraper:3
139
+ crawl:web-scraper:3
140
+ pipeline:data-pipeline:3
141
+ etl:data-pipeline:3
142
+ data:data-pipeline:3
143
+ microservice:microservice:3
144
+ service:microservice:3
145
+ library:npm-library:3
146
+ package:npm-library:3
147
+ npm:npm-library:3
148
+ extension:chrome-extension:3
149
+ chrome:chrome-extension:3
150
+ browser:chrome-extension:3
151
+ landing:static-landing-page:3
152
+ static:static-landing-page:3
153
+ marketing:static-landing-page:3
154
+ fullstack:full-stack-demo:3
155
+ MAP
156
+ }
157
+
158
+ # _qs_is_stopword: filter generic words that would add filename-token noise
159
+ # (e.g. "app" matching mobile-app for every brief). Returns 0 for a stopword.
160
+ _qs_is_stopword() {
161
+ case "$1" in
162
+ a|an|the|my|your|our|with|for|and|to|of|in|on|app|application|build|make|create|want|that|this|some|simple) return 0;;
163
+ esac
164
+ return 1
165
+ }
166
+
167
+ # _qs_score_templates <brief>: print the top-3 closest templates, one name per
168
+ # line, in deterministic rank order. simple-todo-app is the guaranteed default:
169
+ # it gets a +1 baseline and wins exact-score ties (priority column in the sort).
170
+ # Scoring: +2 per non-stopword token that matches a template filename token,
171
+ # plus the curated keyword weights. No network, no provider, no LLM.
172
+ _qs_score_templates() {
173
+ local brief="$1"
174
+ local tdir; tdir="$(_qs_templates_dir)"
175
+ local brief_lc; brief_lc=$(printf '%s' "$brief" | tr '[:upper:]' '[:lower:]')
176
+
177
+ declare -A scores
178
+ local name f
179
+ while IFS= read -r f; do
180
+ [ -z "$f" ] && continue
181
+ name=$(basename "$f" .md)
182
+ [ "$name" = "README" ] && continue
183
+ scores["$name"]=0
184
+ done < <(ls "$tdir"/*.md 2>/dev/null)
185
+
186
+ # No templates resolvable: fall back to the guaranteed default only.
187
+ if [ "${#scores[@]}" -eq 0 ]; then
188
+ printf 'simple-todo-app\n'
189
+ return 0
190
+ fi
191
+
192
+ local -a tokens
193
+ read -ra tokens <<< "$(printf '%s' "$brief_lc" | tr -cs 'a-z0-9' ' ')"
194
+
195
+ local tok kw tmpl wt
196
+ for tok in "${tokens[@]}"; do
197
+ [ -z "$tok" ] && continue
198
+ _qs_is_stopword "$tok" && continue
199
+ for name in "${!scores[@]}"; do
200
+ case "-$name-" in
201
+ *"-$tok-"*) scores["$name"]=$(( ${scores["$name"]} + 2 ));;
202
+ esac
203
+ done
204
+ while IFS=: read -r kw tmpl wt; do
205
+ [ -z "$kw" ] && continue
206
+ [ -z "$wt" ] && wt=3
207
+ if [ "$tok" = "$kw" ] && [ -n "${scores[$tmpl]+x}" ]; then
208
+ scores["$tmpl"]=$(( ${scores["$tmpl"]} + wt ))
209
+ fi
210
+ done < <(_qs_keyword_map)
211
+ done
212
+
213
+ # Guaranteed default baseline.
214
+ if [ -n "${scores[simple-todo-app]+x}" ]; then
215
+ scores["simple-todo-app"]=$(( ${scores["simple-todo-app"]} + 1 ))
216
+ fi
217
+
218
+ # Emit "score<TAB>priority<TAB>name"; sort by score desc, priority asc
219
+ # (simple-todo-app=0 wins ties), then name asc for full determinism.
220
+ local prio
221
+ for name in "${!scores[@]}"; do
222
+ prio=1
223
+ [ "$name" = "simple-todo-app" ] && prio=0
224
+ printf '%s\t%s\t%s\n' "${scores[$name]}" "$prio" "$name"
225
+ done | sort -t$'\t' -k1,1nr -k2,2n -k3,3 | head -3 | cut -f3
226
+ }
227
+
228
+ # _qs_template_summary <name>: a short one-line description for the picker.
229
+ # Read from the template's first prose line would be fragile; use a small
230
+ # curated table so the picker copy is stable and honest.
231
+ _qs_template_summary() {
232
+ case "$1" in
233
+ simple-todo-app) printf 'A minimal todo list app';;
234
+ saas-starter) printf 'Multi-tenant SaaS with auth';;
235
+ rest-api-auth) printf 'REST API with authentication';;
236
+ rest-api) printf 'REST API service';;
237
+ api-only) printf 'Backend API, no frontend';;
238
+ ai-chatbot) printf 'AI chatbot with an LLM backend';;
239
+ blog-platform) printf 'Blog / CMS platform';;
240
+ chrome-extension) printf 'Chrome browser extension';;
241
+ cli-tool) printf 'Command-line tool';;
242
+ dashboard) printf 'Analytics / admin dashboard';;
243
+ data-pipeline) printf 'Data pipeline / ETL';;
244
+ discord-bot) printf 'Discord bot';;
245
+ e-commerce) printf 'E-commerce storefront';;
246
+ full-stack-demo) printf 'Full-stack demo app';;
247
+ game) printf 'Browser game';;
248
+ microservice) printf 'Standalone microservice';;
249
+ mobile-app) printf 'Mobile app';;
250
+ npm-library) printf 'Publishable npm library';;
251
+ slack-bot) printf 'Slack bot';;
252
+ static-landing-page) printf 'Static marketing landing page';;
253
+ web-scraper) printf 'Web scraper';;
254
+ *) printf 'PRD template';;
255
+ esac
256
+ }
257
+
258
+ # _qs_emit_plan <prd_path>: render the step-4 plan block from the REAL estimator.
259
+ # Honesty invariant: NO LOKI_COMPLEXITY override is passed, so the complexity,
260
+ # iterations, and cost are exactly what cmd_start will run with (cmd_start
261
+ # auto-detects complexity from the same PRD). Returns 0 if a number was shown,
262
+ # non-zero if the estimator gave no result (caller falls back to a no-number
263
+ # confirm, never fabricating a figure).
264
+ _qs_emit_plan() {
265
+ local prd_path="$1" template_name="$2"
266
+ local plan_json=""
267
+ plan_json=$(show_prd_plan "$prd_path" "true" "false" 2>/dev/null) || plan_json=""
268
+ if [ -z "$plan_json" ]; then
269
+ return 1
270
+ fi
271
+ local parsed
272
+ parsed=$(printf '%s' "$plan_json" | python3 -c "
273
+ import json, sys
274
+ try:
275
+ d = json.load(sys.stdin)
276
+ except Exception:
277
+ sys.exit(1)
278
+ cost = d.get('cost', {}).get('total_usd')
279
+ time_est = d.get('time', {}).get('estimated')
280
+ iters = d.get('iterations', {}).get('estimated')
281
+ rng = d.get('iterations', {}).get('range', [])
282
+ tier = d.get('complexity', {}).get('tier', '')
283
+ if cost is None or time_est is None or iters is None:
284
+ sys.exit(1)
285
+ rng_str = ''
286
+ if isinstance(rng, list) and len(rng) == 2:
287
+ rng_str = ' (range {}-{})'.format(rng[0], rng[1])
288
+ print(tier.upper())
289
+ print('{:.2f}'.format(float(cost)))
290
+ print(time_est)
291
+ print('{}{}'.format(iters, rng_str))
292
+ " 2>/dev/null) || parsed=""
293
+ if [ -z "$parsed" ]; then
294
+ return 1
295
+ fi
296
+ local tier_u cost_u time_u iter_u
297
+ tier_u=$(printf '%s' "$parsed" | sed -n '1p')
298
+ cost_u=$(printf '%s' "$parsed" | sed -n '2p')
299
+ time_u=$(printf '%s' "$parsed" | sed -n '3p')
300
+ iter_u=$(printf '%s' "$parsed" | sed -n '4p')
301
+
302
+ printf ' Template: %s\n' "$template_name"
303
+ printf ' Complexity: %s\n' "$tier_u"
304
+ printf ' Cost: ~$%s\n' "$cost_u"
305
+ printf ' Time: ~%s\n' "$time_u"
306
+ printf ' Iterations: %s\n' "$iter_u"
307
+ printf '\n'
308
+ printf ' This is an estimate. Actual usage depends on PRD complexity, the AI\n'
309
+ printf ' provider, and how many iterations the build needs.\n'
310
+ printf '\n'
311
+ return 0
312
+ }
313
+
314
+ # _qs_help: concise usage for `loki quickstart --help`.
315
+ _qs_help() {
316
+ printf '%sloki quickstart%s - guided first build (setup, idea, template, plan, go)\n' "$_QS_BOLD" "$_QS_NC"
317
+ printf '\n'
318
+ printf 'A 4-step interview that takes you from a clean install to a verified\n'
319
+ printf 'first build. Press Enter at every step to build the sample Todo app.\n'
320
+ printf '\n'
321
+ printf 'Usage: loki quickstart [IDEA|PRD-PATH] [options]\n'
322
+ printf '\n'
323
+ printf 'Arguments:\n'
324
+ printf ' IDEA A one-line description (pre-fills step 2)\n'
325
+ printf ' PRD-PATH A path to an existing PRD file (skips steps 2-3)\n'
326
+ printf '\n'
327
+ printf 'Options:\n'
328
+ printf ' --yes, -y Auto-confirm the final build prompt (still shows the plan)\n'
329
+ printf ' --help, -h Show this help and exit\n'
330
+ printf '\n'
331
+ printf 'Steps:\n'
332
+ printf ' 1. Setup Check for an AI provider; offer to install if missing\n'
333
+ printf ' 2. Build Describe what you want, or Enter for the sample Todo app\n'
334
+ printf ' 3. Template Pick the closest starting template (offline keyword match)\n'
335
+ printf ' 4. Plan Review the honest cost/time estimate, then confirm\n'
336
+ printf '\n'
337
+ printf 'The PRD is written to ./prd.md in the current directory, then the build\n'
338
+ printf 'starts. For non-interactive automation use: loki start <prd> --yes\n'
339
+ return 0
340
+ }
341
+
342
+ # cmd_quickstart: the 4-step guided interview. Composes provider_offer_gate
343
+ # (slice B), show_prd_plan (slice A), the template matcher, and cmd_start.
344
+ #
345
+ # Order is load-bearing:
346
+ # --help (exit 0) -> non-TTY/CI gate (hint + exit 2) -> provider gate ->
347
+ # step 2 (idea / PRD path) -> step 3 (template) -> step 4 (plan + confirm) ->
348
+ # write PRD to CWD -> cmd_start --yes --no-plan (subshelled; it execs the runner).
349
+ cmd_quickstart() {
350
+ local positional=""
351
+ local assume_yes=false
352
+ if _qs_assume_yes; then assume_yes=true; fi
353
+
354
+ while [ $# -gt 0 ]; do
355
+ case "$1" in
356
+ --help|-h)
357
+ _qs_help
358
+ exit 0
359
+ ;;
360
+ --yes|-y)
361
+ assume_yes=true
362
+ shift
363
+ ;;
364
+ --*)
365
+ printf '%sUnknown option: %s%s\n' "$_QS_RED" "$1" "$_QS_NC" >&2
366
+ printf "Run 'loki quickstart --help' for usage.\n" >&2
367
+ exit 2
368
+ ;;
369
+ *)
370
+ if [ -z "$positional" ]; then
371
+ positional="$1"
372
+ else
373
+ printf '%sUnexpected extra argument: %s%s\n' "$_QS_RED" "$1" "$_QS_NC" >&2
374
+ printf "Run 'loki quickstart --help' for usage.\n" >&2
375
+ exit 2
376
+ fi
377
+ shift
378
+ ;;
379
+ esac
380
+ done
381
+
382
+ # Non-TTY / CI: quickstart is interactive by definition. Never hang on read;
383
+ # print the automation hint to stderr and exit 2 (design 3.8).
384
+ if _qs_non_interactive; then
385
+ printf 'loki quickstart is interactive and needs a terminal. For automation use: loki start <prd> --yes\n' >&2
386
+ exit 2
387
+ fi
388
+
389
+ printf '\n'
390
+ printf '%sLoki Mode quickstart -- four quick questions, then your build starts.%s\n' "$_QS_BOLD" "$_QS_NC"
391
+ printf '\n'
392
+
393
+ # ----- Step 1 of 4: Setup (reuse the slice-B provider offer) -------------
394
+ printf '%sStep 1 of 4: Setup%s\n' "$_QS_BOLD" "$_QS_NC"
395
+ printf ' Checking for an AI provider CLI ...\n'
396
+ if detect_any_provider; then
397
+ local found=""
398
+ local _p
399
+ for _p in claude codex cline aider; do
400
+ if command -v "$_p" >/dev/null 2>&1; then found="$_p"; break; fi
401
+ done
402
+ printf ' Found: %s. Good.\n' "$found"
403
+ else
404
+ # Run the inline install + login offer. provider_offer_gate returns 2 if
405
+ # no provider ends up available (declined, or install failed).
406
+ if ! provider_offer_gate; then
407
+ printf '%sNo provider available; cannot start a build. Install one and re-run loki quickstart.%s\n' "$_QS_RED" "$_QS_NC" >&2
408
+ exit 2
409
+ fi
410
+ fi
411
+ printf '\n'
412
+
413
+ # ----- Step 2 of 4: What to build ---------------------------------------
414
+ # A positional PRD path skips steps 2-3 entirely (design 3.8). A positional
415
+ # one-liner pre-fills the idea. Otherwise prompt (Enter = sample Todo app).
416
+ local prd_source="" # an existing PRD file path, when the user has one
417
+ local brief="" # the one-line idea (drives template matching)
418
+ local template_name=""
419
+
420
+ if [ -n "$positional" ] && [ -f "$positional" ]; then
421
+ prd_source="$positional"
422
+ printf '%sUsing your PRD: %s%s\n' "$_QS_DIM" "$positional" "$_QS_NC"
423
+ printf '\n'
424
+ else
425
+ printf '%sStep 2 of 4: What do you want to build?%s\n' "$_QS_BOLD" "$_QS_NC"
426
+ printf ' Describe it in one line, or paste a path to a PRD file.\n'
427
+ printf ' (Press Enter to build the sample Todo app.)\n'
428
+ if [ -n "$positional" ]; then
429
+ brief="$positional"
430
+ printf '> %s\n' "$brief"
431
+ else
432
+ local answer=""
433
+ printf '> '
434
+ read -r answer 2>/dev/null || answer=""
435
+ # If the typed value is an existing file, treat it as a PRD path.
436
+ if [ -n "$answer" ] && [ -f "$answer" ]; then
437
+ prd_source="$answer"
438
+ else
439
+ brief="$answer"
440
+ fi
441
+ fi
442
+ printf '\n'
443
+ fi
444
+
445
+ # ----- Step 3 of 4: Pick a template (skipped if a PRD path was given) ----
446
+ if [ -z "$prd_source" ]; then
447
+ local -a top3=()
448
+ local line
449
+ while IFS= read -r line; do
450
+ [ -n "$line" ] && top3+=("$line")
451
+ done < <(_qs_score_templates "$brief")
452
+
453
+ # Defensive: guarantee a default if scoring produced nothing.
454
+ if [ "${#top3[@]}" -eq 0 ]; then
455
+ top3=("simple-todo-app")
456
+ fi
457
+
458
+ printf '%sStep 3 of 4: Pick a starting template%s\n' "$_QS_BOLD" "$_QS_NC"
459
+ if [ -n "$brief" ]; then
460
+ printf ' Closest matches for "%s":\n' "$brief"
461
+ else
462
+ printf ' Closest matches for the sample Todo app:\n'
463
+ fi
464
+ local i=1 t suffix
465
+ for t in "${top3[@]}"; do
466
+ suffix=""
467
+ [ "$i" -eq 1 ] && suffix=" (default)"
468
+ printf ' %d) %-18s %s%s\n' "$i" "$t" "$(_qs_template_summary "$t")" "$suffix"
469
+ i=$((i + 1))
470
+ done
471
+ printf ' Choose 1-%d, or press Enter for 1.\n' "${#top3[@]}"
472
+
473
+ local pick=""
474
+ printf '> '
475
+ read -r pick 2>/dev/null || pick=""
476
+ printf '\n'
477
+
478
+ case "$pick" in
479
+ ""|1) template_name="${top3[0]}";;
480
+ 2) template_name="${top3[1]:-${top3[0]}}";;
481
+ 3) template_name="${top3[2]:-${top3[0]}}";;
482
+ *) template_name="${top3[0]}";; # any unexpected input -> the default
483
+ esac
484
+
485
+ local tdir; tdir="$(_qs_templates_dir)"
486
+ prd_source="$tdir/$template_name.md"
487
+ if [ ! -f "$prd_source" ]; then
488
+ printf '%sTemplate file not found: %s%s\n' "$_QS_RED" "$prd_source" "$_QS_NC" >&2
489
+ exit 1
490
+ fi
491
+ else
492
+ template_name="$(basename "$prd_source")"
493
+ fi
494
+
495
+ # ----- Step 4 of 4: Review the plan (reuse the slice-A estimator) --------
496
+ printf '%sStep 4 of 4: Review the plan%s\n' "$_QS_BOLD" "$_QS_NC"
497
+ local estimate_ok=true
498
+ if ! _qs_emit_plan "$prd_source" "$template_name"; then
499
+ estimate_ok=false
500
+ printf '%sCould not compute a cost estimate (the estimator did not return a result).%s\n' "$_QS_YELLOW" "$_QS_NC"
501
+ printf '\n'
502
+ fi
503
+
504
+ # ----- Confirm ----------------------------------------------------------
505
+ if [ "$assume_yes" != true ]; then
506
+ local confirm=""
507
+ if [ "$estimate_ok" = true ]; then
508
+ # Default YES.
509
+ printf 'Start the build now? [Y/n] '
510
+ read -r confirm 2>/dev/null || confirm=""
511
+ if [[ -n "$confirm" && ! "$confirm" =~ ^[Yy] ]]; then
512
+ printf '\nCancelled. Nothing was spent.\n'
513
+ exit 0
514
+ fi
515
+ else
516
+ # No honest number available: default NO (the safe direction).
517
+ printf 'Start the build anyway? [y/N] '
518
+ read -r confirm 2>/dev/null || confirm=""
519
+ if [[ ! "$confirm" =~ ^[Yy] ]]; then
520
+ printf '\nCancelled. Nothing was spent.\n'
521
+ exit 0
522
+ fi
523
+ fi
524
+ fi
525
+
526
+ # ----- Land the PRD at ./prd.md (design 3.6) ----------------------------
527
+ local target="./prd.md"
528
+ if [ -e "$target" ]; then
529
+ local overwrite=""
530
+ printf 'prd.md already exists. Overwrite? [y/N] '
531
+ read -r overwrite 2>/dev/null || overwrite=""
532
+ if [[ ! "$overwrite" =~ ^[Yy] ]]; then
533
+ # Declining to overwrite one file must never silently destroy
534
+ # another (bug-hunt MEDIUM): the fallback gets the same existence
535
+ # guard, walking numbered suffixes until a free name is found.
536
+ target="./prd-quickstart.md"
537
+ local _qs_n=2
538
+ while [ -e "$target" ]; do
539
+ target="./prd-quickstart-${_qs_n}.md"
540
+ _qs_n=$((_qs_n + 1))
541
+ if [ "$_qs_n" -gt 100 ]; then
542
+ printf '%sCould not find a free PRD filename (prd-quickstart-*.md all taken).%s\n' "$_QS_RED" "$_QS_NC" >&2
543
+ exit 1
544
+ fi
545
+ done
546
+ fi
547
+ fi
548
+ if ! cp "$prd_source" "$target" 2>/dev/null; then
549
+ printf '%sCould not write the PRD to the current directory. Try a writable directory.%s\n' "$_QS_RED" "$_QS_NC" >&2
550
+ exit 1
551
+ fi
552
+
553
+ printf '\n'
554
+ printf 'Starting your build. Progress streams here in the terminal.\n'
555
+ printf ' PRD saved to: %s\n' "$target"
556
+ printf " Tip: run 'loki dashboard' in another terminal to watch in a browser.\n"
557
+ printf '\n'
558
+
559
+ # ----- Compose with cmd_start -------------------------------------------
560
+ # Consent was collected in step 4, so --yes is correct. The plan was already
561
+ # shown in step 4, so --no-plan avoids double-printing it. No --simple: the
562
+ # estimate shown was the auto-detect estimate, and the runner's own
563
+ # complexity detection agrees with the estimator for the default PRD
564
+ # (verified: detect_complexity and show_prd_plan both classify the sample
565
+ # Todo app SIMPLE), so the quote matches the charge.
566
+ #
567
+ # cmd_start EXECS the runner (loki:1856, _loki_new_session_exec), so it never
568
+ # returns into this function. We wrap it in a subshell -- the exact pattern
569
+ # cmd_demo uses (loki:9337) -- so the exec replaces the SUBSHELL while the
570
+ # interactive controlling tty (and Ctrl+C) is preserved, the build runs in
571
+ # the foreground, and on failure this function's honest message stays
572
+ # reachable. We do NOT auto-open a dashboard: the default `loki start` does
573
+ # not start one (it is gated on --api, loki:1825), and starting one here
574
+ # would bind port 57374, which quickstart deliberately must not own.
575
+ local start_exit=0
576
+ ( cmd_start "$target" --yes --no-plan ) || start_exit=$?
577
+
578
+ if [ "$start_exit" -ne 0 ]; then
579
+ printf "%sThe build did not start cleanly. Run 'loki doctor' and try 'loki start ./prd.md'.%s\n" "$_QS_RED" "$_QS_NC" >&2
580
+ exit "$start_exit"
581
+ fi
582
+
583
+ return 0
584
+ }
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.28.1"
10
+ __version__ = "7.29.0"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v7.28.1
5
+ **Version:** v7.29.0
6
6
 
7
7
  ---
8
8
 
@@ -128,6 +128,15 @@ package, while `loki-mode-sdk` is the thin client.
128
128
 
129
129
  ## Quick Start
130
130
 
131
+ New here? `loki quickstart` (v7.29.0) walks you through your first build in
132
+ four questions (setup check, one-line idea, template pick, plan review with
133
+ a real cost estimate before anything is spent). Pressing Enter at every step
134
+ builds the sample Todo app.
135
+
136
+ ```bash
137
+ loki quickstart
138
+ ```
139
+
131
140
  Drop a spec -- any artifact that describes what you want built -- and Loki
132
141
  Mode takes it from spec to deployed app. Specs can be a markdown PRD, a
133
142
  GitHub issue URL, or a YAML feature description.
@@ -1,6 +1,6 @@
1
1
  # Alternative Installation Methods
2
2
 
3
- The recommended installation is via npm or Homebrew (see [README](../README.md#installation)). These alternatives serve specific use cases.
3
+ The recommended installation is via npm or Homebrew (see [README](../README.md#get-started-in-30-seconds)). These alternatives serve specific use cases.
4
4
 
5
5
  ---
6
6