loki-mode 7.7.34 → 7.8.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/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/loki +7 -0
- package/autonomy/run.sh +220 -9
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/providers/claude.sh +17 -0
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: Autonomous spec-to-product system. Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product via the RARV-C closure loop, with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v7.
|
|
6
|
+
# Loki Mode v7.8.1
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -381,4 +381,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
381
381
|
|
|
382
382
|
---
|
|
383
383
|
|
|
384
|
-
**v7.
|
|
384
|
+
**v7.8.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.
|
|
1
|
+
7.8.1
|
package/autonomy/loki
CHANGED
|
@@ -911,6 +911,13 @@ cmd_start() {
|
|
|
911
911
|
args+=("--parallel")
|
|
912
912
|
shift
|
|
913
913
|
;;
|
|
914
|
+
--regen-prd|--regenerate-prd|--regen)
|
|
915
|
+
# v7.8.1: force a fresh generated PRD on a no-PRD run, overriding
|
|
916
|
+
# the staleness-aware reuse (decide_generated_prd_action in
|
|
917
|
+
# run.sh reads LOKI_PRD_REGEN). Exported so the runner sees it.
|
|
918
|
+
export LOKI_PRD_REGEN=1
|
|
919
|
+
shift
|
|
920
|
+
;;
|
|
914
921
|
--bg|--background)
|
|
915
922
|
args+=("--bg")
|
|
916
923
|
shift
|
package/autonomy/run.sh
CHANGED
|
@@ -3973,6 +3973,124 @@ print(json.dumps(data, indent=2))
|
|
|
3973
3973
|
}
|
|
3974
3974
|
|
|
3975
3975
|
# Track iteration completion - move task to completed queue
|
|
3976
|
+
# v7.8.1: staleness-aware generated-PRD reuse helpers.
|
|
3977
|
+
# Hash stdin with whatever digest tool is available (mirrors the existing
|
|
3978
|
+
# stat -f%z || stat -c%s dual-probe portability pattern). Echoes a short hash.
|
|
3979
|
+
_loki_hash_stdin() {
|
|
3980
|
+
if command -v shasum >/dev/null 2>&1; then
|
|
3981
|
+
shasum -a 256 | cut -c1-16
|
|
3982
|
+
elif command -v sha256sum >/dev/null 2>&1; then
|
|
3983
|
+
sha256sum | cut -c1-16
|
|
3984
|
+
else
|
|
3985
|
+
cksum | tr -d ' ' | cut -c1-16
|
|
3986
|
+
fi
|
|
3987
|
+
}
|
|
3988
|
+
|
|
3989
|
+
# Compute a cheap, clone-stable signature of the codebase so we can tell whether
|
|
3990
|
+
# it changed since the generated PRD was last written. Git repos: HEAD sha +
|
|
3991
|
+
# dirty flag (.loki/.git churn filtered out). Non-git: a hash of sorted
|
|
3992
|
+
# path+size pairs (size, not mtime, so it is clone-stable). Echoes the signature.
|
|
3993
|
+
compute_codebase_signature() {
|
|
3994
|
+
local dir="${1:-.}"
|
|
3995
|
+
( cd "$dir" 2>/dev/null || exit 0
|
|
3996
|
+
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
3997
|
+
local head dirty porcelain
|
|
3998
|
+
head=$(git rev-parse HEAD 2>/dev/null || echo "nohead")
|
|
3999
|
+
porcelain=$(git status --porcelain 2>/dev/null | grep -vE '(^...?\.loki/|/\.loki/| \.loki/|\.git/)' || true)
|
|
4000
|
+
if [ -z "$porcelain" ]; then
|
|
4001
|
+
dirty="clean"
|
|
4002
|
+
else
|
|
4003
|
+
dirty=$(printf '%s' "$porcelain" | _loki_hash_stdin)
|
|
4004
|
+
fi
|
|
4005
|
+
echo "git:${head}:${dirty}"
|
|
4006
|
+
else
|
|
4007
|
+
local listing count
|
|
4008
|
+
listing=$(find . \
|
|
4009
|
+
-type d \( -name .loki -o -name .git -o -name node_modules -o -name dist \
|
|
4010
|
+
-o -name build -o -name .next -o -name target -o -name vendor \
|
|
4011
|
+
-o -name __pycache__ -o -name .venv -o -name venv \) -prune -o \
|
|
4012
|
+
-type f -print 2>/dev/null \
|
|
4013
|
+
| while IFS= read -r f; do
|
|
4014
|
+
local sz
|
|
4015
|
+
sz=$(stat -f%z "$f" 2>/dev/null || stat -c%s "$f" 2>/dev/null || echo 0)
|
|
4016
|
+
printf '%s\t%s\n' "$f" "$sz"
|
|
4017
|
+
done | LC_ALL=C sort)
|
|
4018
|
+
count=$(printf '%s\n' "$listing" | grep -c . || echo 0)
|
|
4019
|
+
echo "files:$(printf '%s' "$listing" | _loki_hash_stdin):${count}"
|
|
4020
|
+
fi
|
|
4021
|
+
)
|
|
4022
|
+
}
|
|
4023
|
+
|
|
4024
|
+
# Decide what to do with a previously generated PRD on a no-PRD run.
|
|
4025
|
+
# Echoes one of: reuse | update | generate. Never fails the run.
|
|
4026
|
+
# - LOKI_PRD_REGEN=1 (or --regen-prd, which sets it) -> generate (force fresh).
|
|
4027
|
+
# - no generated PRD present -> generate (first run).
|
|
4028
|
+
# - generated PRD present, no recorded signature -> update (have a PRD but no
|
|
4029
|
+
# provenance: reconcile incrementally rather than trust-blindly or discard).
|
|
4030
|
+
# - signature matches current codebase -> reuse (unchanged).
|
|
4031
|
+
# - signature differs -> update (codebase changed; update incrementally).
|
|
4032
|
+
decide_generated_prd_action() {
|
|
4033
|
+
local loki_dir="${TARGET_DIR:-.}/.loki"
|
|
4034
|
+
if [ "${LOKI_PRD_REGEN:-}" = "1" ]; then
|
|
4035
|
+
echo "generate"; return 0
|
|
4036
|
+
fi
|
|
4037
|
+
if [ ! -f "$loki_dir/generated-prd.md" ] && [ ! -f "$loki_dir/generated-prd.json" ]; then
|
|
4038
|
+
echo "generate"; return 0
|
|
4039
|
+
fi
|
|
4040
|
+
local sig_file="$loki_dir/state/prd-signature.json"
|
|
4041
|
+
if [ ! -f "$sig_file" ]; then
|
|
4042
|
+
echo "update"; return 0
|
|
4043
|
+
fi
|
|
4044
|
+
local stored current
|
|
4045
|
+
stored=$(LOKI_SIG_FILE="$sig_file" python3 -c "
|
|
4046
|
+
import json, os
|
|
4047
|
+
try:
|
|
4048
|
+
print(json.load(open(os.environ['LOKI_SIG_FILE'])).get('signature',''))
|
|
4049
|
+
except Exception:
|
|
4050
|
+
print('')
|
|
4051
|
+
" 2>/dev/null)
|
|
4052
|
+
[ -z "$stored" ] && { echo "update"; return 0; }
|
|
4053
|
+
current=$(compute_codebase_signature "${TARGET_DIR:-.}")
|
|
4054
|
+
if [ "$stored" = "$current" ]; then
|
|
4055
|
+
echo "reuse"
|
|
4056
|
+
else
|
|
4057
|
+
echo "update"
|
|
4058
|
+
fi
|
|
4059
|
+
}
|
|
4060
|
+
|
|
4061
|
+
# Persist the current codebase signature after a clean no-PRD iteration that has
|
|
4062
|
+
# a generated PRD, so the next run can decide reuse vs update. Best-effort; never
|
|
4063
|
+
# fails the run. Only records on exit_code==0 (do not bless a broken iteration).
|
|
4064
|
+
persist_prd_signature_if_present() {
|
|
4065
|
+
local exit_code="${1:-0}"
|
|
4066
|
+
[ "$exit_code" = "0" ] || return 0
|
|
4067
|
+
# only for no-PRD runs whose generated PRD exists
|
|
4068
|
+
case "${prd_path:-}" in
|
|
4069
|
+
""|*.loki/generated-prd.md|*.loki/generated-prd.json) ;;
|
|
4070
|
+
*) return 0 ;;
|
|
4071
|
+
esac
|
|
4072
|
+
local loki_dir="${TARGET_DIR:-.}/.loki"
|
|
4073
|
+
[ -f "$loki_dir/generated-prd.md" ] || [ -f "$loki_dir/generated-prd.json" ] || return 0
|
|
4074
|
+
local sig
|
|
4075
|
+
sig=$(compute_codebase_signature "${TARGET_DIR:-.}")
|
|
4076
|
+
[ -n "$sig" ] || return 0
|
|
4077
|
+
mkdir -p "$loki_dir/state" 2>/dev/null || return 0
|
|
4078
|
+
local mode="files"; case "$sig" in git:*) mode="git" ;; esac
|
|
4079
|
+
local tmp="$loki_dir/state/.prd-signature.json.tmp.$$"
|
|
4080
|
+
LOKI_SIG="$sig" LOKI_SIG_MODE="$mode" LOKI_SIG_VER="$(get_version 2>/dev/null || echo unknown)" \
|
|
4081
|
+
python3 -c "
|
|
4082
|
+
import json, os, datetime
|
|
4083
|
+
rec = {
|
|
4084
|
+
'signature': os.environ['LOKI_SIG'],
|
|
4085
|
+
'generated_at': datetime.datetime.now(datetime.timezone.utc).isoformat().replace('+00:00','Z'),
|
|
4086
|
+
'prd_path': '.loki/generated-prd.md',
|
|
4087
|
+
'mode': os.environ['LOKI_SIG_MODE'],
|
|
4088
|
+
'loki_version': os.environ['LOKI_SIG_VER'],
|
|
4089
|
+
}
|
|
4090
|
+
print(json.dumps(rec))
|
|
4091
|
+
" > "$tmp" 2>/dev/null && mv -f "$tmp" "$loki_dir/state/prd-signature.json" 2>/dev/null || rm -f "$tmp" 2>/dev/null
|
|
4092
|
+
}
|
|
4093
|
+
|
|
3976
4094
|
track_iteration_complete() {
|
|
3977
4095
|
local iteration="$1"
|
|
3978
4096
|
local exit_code="${2:-0}"
|
|
@@ -9586,7 +9704,18 @@ build_prompt() {
|
|
|
9586
9704
|
local sdlc_instruction="SDLC_PHASES_ENABLED: [$phases]. Execute ALL enabled phases. Log results to .loki/logs/. See .loki/SKILL.md for phase details. Skill modules at .loki/skills/."
|
|
9587
9705
|
|
|
9588
9706
|
# Codebase Analysis Mode - when no PRD provided
|
|
9589
|
-
|
|
9707
|
+
# v7.8.1: improved 3-pass instruction. More efficient (no blind full scan)
|
|
9708
|
+
# and more accurate (high-signal files first, fixed PRD section template so
|
|
9709
|
+
# the result is diff-friendly for later incremental updates).
|
|
9710
|
+
local analysis_instruction="CODEBASE_ANALYSIS_MODE: No PRD provided. Reverse-engineer a precise PRD from the existing code in three passes, cheaply and without blind full scans. PASS 1 (orient): list the top two directory levels; read ONLY high-signal manifests that exist (package.json, requirements.txt, pyproject.toml, Cargo.toml, go.mod, pom.xml, build.gradle, composer.json) to identify language, framework, and scripts; read README and any docs index. PASS 2 (locate): from the manifests and conventional layout, identify the entrypoints, the public API or CLI surface, the test directory and runner, and the config or env contract; read those first; skip generated, vendored, and lockfile content; prefer LSP workspace symbols when the lsp-proxy server is available. PASS 3 (write): write .loki/generated-prd.md with these sections: Overview, Detected Stack, Entrypoints and Components, Existing Behavior and Requirements (reverse-engineered, observable), Test and Build Setup, Gaps and TODOs, Out of Scope. Keep it under 200 lines, plain Markdown, no emojis, no em dashes. Do not invent features not evidenced by the code. THEN execute SDLC phases against that PRD."
|
|
9711
|
+
|
|
9712
|
+
# v7.8.1: incremental-update instruction for when a generated PRD already
|
|
9713
|
+
# exists and the codebase changed (GENERATED_PRD_ACTION=update). Reconcile,
|
|
9714
|
+
# do not regenerate, so the PRD stays continuous and the update is cheap.
|
|
9715
|
+
local update_instruction=""
|
|
9716
|
+
if [ "${GENERATED_PRD_ACTION:-}" = "update" ]; then
|
|
9717
|
+
update_instruction="GENERATED_PRD_UPDATE_MODE: A previously generated PRD exists at .loki/generated-prd.md and the codebase has changed since it was written. Do NOT regenerate it from scratch. Read the existing .loki/generated-prd.md first, then reconcile it with the current code: add requirements for new entrypoints, components, or behaviors; remove or mark obsolete requirements whose code was deleted; correct the Detected Stack and Test and Build Setup sections if they drifted. Preserve the existing structure and still-accurate content. Keep edits minimal and evidence-based, under 200 lines, plain Markdown, no emojis, no em dashes. THEN execute SDLC phases against the updated PRD."
|
|
9718
|
+
fi
|
|
9590
9719
|
|
|
9591
9720
|
# Context Memory Instructions (integrated with new memory system)
|
|
9592
9721
|
local memory_instruction="MEMORY SYSTEM: Relevant context from past sessions is provided below (if any). Your actions will be automatically recorded for future reference. For complex handoffs: create .loki/memory/handoffs/{timestamp}.md. For important decisions: they will be captured in the timeline. Check .loki/CONTINUITY.md for session-level working memory."
|
|
@@ -9935,7 +10064,7 @@ except Exception:
|
|
|
9935
10064
|
else
|
|
9936
10065
|
if [ $retry -eq 0 ]; then
|
|
9937
10066
|
if [ -n "$prd" ]; then
|
|
9938
|
-
echo "Loki Mode with PRD at $prd. $human_directive $gate_failure_context $queue_tasks $bmad_context $openspec_context $mirofish_context $magic_context $checklist_status $app_runner_info $playwright_info $memory_context_section $rarv_instruction $memory_instruction $usage_doc_instruction $lsp_grounding_instruction $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
10067
|
+
echo "Loki Mode with PRD at $prd. $update_instruction $human_directive $gate_failure_context $queue_tasks $bmad_context $openspec_context $mirofish_context $magic_context $checklist_status $app_runner_info $playwright_info $memory_context_section $rarv_instruction $memory_instruction $usage_doc_instruction $lsp_grounding_instruction $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
9939
10068
|
else
|
|
9940
10069
|
echo "Loki Mode. $human_directive $gate_failure_context $queue_tasks $bmad_context $openspec_context $mirofish_context $magic_context $checklist_status $app_runner_info $playwright_info $memory_context_section $analysis_instruction $rarv_instruction $memory_instruction $usage_doc_instruction $lsp_grounding_instruction $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
9941
10070
|
fi
|
|
@@ -10023,6 +10152,13 @@ except Exception:
|
|
|
10023
10152
|
if [ -z "$prd" ]; then
|
|
10024
10153
|
printf '%s\n' "$analysis_instruction"
|
|
10025
10154
|
fi
|
|
10155
|
+
# v7.8.1: when reusing a generated PRD whose codebase changed, append the
|
|
10156
|
+
# incremental-update instruction (prd is the generated PRD here, so the
|
|
10157
|
+
# anchor already says "with PRD at .loki/generated-prd.md"). Decided once per
|
|
10158
|
+
# run (GENERATED_PRD_ACTION), so it stays cache-stable across iterations.
|
|
10159
|
+
if [ -n "$update_instruction" ]; then
|
|
10160
|
+
printf '%s\n' "$update_instruction"
|
|
10161
|
+
fi
|
|
10026
10162
|
printf '</loki_system>\n'
|
|
10027
10163
|
printf '[CACHE_BREAKPOINT]\n'
|
|
10028
10164
|
|
|
@@ -10933,13 +11069,33 @@ run_autonomous() {
|
|
|
10933
11069
|
if [ -f ".loki/generated-prd.md" ] || [ -f ".loki/generated-prd.json" ]; then
|
|
10934
11070
|
log_warn "Using user PRD ($found_prd) instead of generated PRD (.loki/generated-prd.md). Remove generated PRD if no longer needed."
|
|
10935
11071
|
fi
|
|
10936
|
-
elif [ -f ".loki/generated-prd.md" ]; then
|
|
10937
|
-
|
|
10938
|
-
|
|
10939
|
-
|
|
10940
|
-
|
|
10941
|
-
|
|
11072
|
+
elif [ -f ".loki/generated-prd.md" ] || [ -f ".loki/generated-prd.json" ]; then
|
|
11073
|
+
# v7.8.1: staleness-aware reuse. Decide reuse|update|generate ONCE
|
|
11074
|
+
# (the decision must be stable across iterations so the cached static
|
|
11075
|
+
# prompt prefix does not change mid-run). reuse/update both point
|
|
11076
|
+
# prd_path at the existing generated PRD; generate (forced via
|
|
11077
|
+
# LOKI_PRD_REGEN) falls through to Codebase Analysis Mode.
|
|
11078
|
+
GENERATED_PRD_ACTION=$(decide_generated_prd_action)
|
|
11079
|
+
export GENERATED_PRD_ACTION
|
|
11080
|
+
local _gen_prd=".loki/generated-prd.md"
|
|
11081
|
+
[ -f ".loki/generated-prd.md" ] || _gen_prd=".loki/generated-prd.json"
|
|
11082
|
+
case "$GENERATED_PRD_ACTION" in
|
|
11083
|
+
reuse)
|
|
11084
|
+
log_info "No user PRD found. Reusing generated PRD (codebase unchanged): $_gen_prd"
|
|
11085
|
+
prd_path="$_gen_prd"
|
|
11086
|
+
;;
|
|
11087
|
+
update)
|
|
11088
|
+
log_info "No user PRD found. Codebase changed since the generated PRD; will update it incrementally: $_gen_prd"
|
|
11089
|
+
prd_path="$_gen_prd"
|
|
11090
|
+
;;
|
|
11091
|
+
*)
|
|
11092
|
+
log_info "Regenerating PRD from codebase (forced)"
|
|
11093
|
+
prd_path=""
|
|
11094
|
+
;;
|
|
11095
|
+
esac
|
|
10942
11096
|
else
|
|
11097
|
+
GENERATED_PRD_ACTION="generate"
|
|
11098
|
+
export GENERATED_PRD_ACTION
|
|
10943
11099
|
log_info "No PRD found - will analyze codebase and generate one"
|
|
10944
11100
|
fi
|
|
10945
11101
|
fi
|
|
@@ -11222,6 +11378,26 @@ except Exception as exc:
|
|
|
11222
11378
|
&& loki_claude_flag_supported "--append-system-prompt"; then
|
|
11223
11379
|
_loki_claude_argv+=("--append-system-prompt" "$(_loki_autonomy_override_text)")
|
|
11224
11380
|
fi
|
|
11381
|
+
# v7.8.0: explicit settings precedence. Pin the loaded settings sources
|
|
11382
|
+
# so Loki's invocation does not drift if Claude Code changes its implicit
|
|
11383
|
+
# default. Behavior-neutral (these are the standard sources). Gated +
|
|
11384
|
+
# falls back to the implicit default when unsupported. Opt out with
|
|
11385
|
+
# LOKI_SETTING_SOURCES=off.
|
|
11386
|
+
if [ "${LOKI_SETTING_SOURCES:-on}" != "off" ] \
|
|
11387
|
+
&& type loki_claude_flag_supported >/dev/null 2>&1 \
|
|
11388
|
+
&& loki_claude_flag_supported "--setting-sources"; then
|
|
11389
|
+
_loki_claude_argv+=("--setting-sources" "user,project,local")
|
|
11390
|
+
fi
|
|
11391
|
+
# v7.8.0: stream partial assistant deltas so the dashboard renders the
|
|
11392
|
+
# agent's output in real time instead of only at message boundaries. The
|
|
11393
|
+
# stream-json parser below handles the partial event type additively and
|
|
11394
|
+
# ignores it if unrecognized. Gated + fallback. Opt out with
|
|
11395
|
+
# LOKI_PARTIAL_MESSAGES=off.
|
|
11396
|
+
if [ "${LOKI_PARTIAL_MESSAGES:-on}" != "off" ] \
|
|
11397
|
+
&& type loki_claude_flag_supported >/dev/null 2>&1 \
|
|
11398
|
+
&& loki_claude_flag_supported "--include-partial-messages"; then
|
|
11399
|
+
_loki_claude_argv+=("--include-partial-messages")
|
|
11400
|
+
fi
|
|
11225
11401
|
case "${PROVIDER_NAME:-claude}" in
|
|
11226
11402
|
claude)
|
|
11227
11403
|
# Claude: Full features with stream-json output and agent tracking
|
|
@@ -11365,6 +11541,12 @@ def process_stream():
|
|
|
11365
11541
|
init_orchestrator()
|
|
11366
11542
|
print(f"{MAGENTA}[Orchestrator Active]{NC} Main agent started", flush=True)
|
|
11367
11543
|
|
|
11544
|
+
# v7.8.0: track whether the current assistant message text was already
|
|
11545
|
+
# streamed live via --include-partial-messages stream_event deltas, so the
|
|
11546
|
+
# final assistant block does not re-print it. Reset after each assistant
|
|
11547
|
+
# message. Stays False when partial messages are off (no stream_event lines).
|
|
11548
|
+
streamed_text_blocks = False
|
|
11549
|
+
|
|
11368
11550
|
for line in sys.stdin:
|
|
11369
11551
|
line = line.strip()
|
|
11370
11552
|
if not line:
|
|
@@ -11373,6 +11555,29 @@ def process_stream():
|
|
|
11373
11555
|
data = json.loads(line)
|
|
11374
11556
|
msg_type = data.get("type", "")
|
|
11375
11557
|
|
|
11558
|
+
# v7.8.0: --include-partial-messages emits incremental stream_event
|
|
11559
|
+
# records (content_block_delta) BEFORE the final assistant message.
|
|
11560
|
+
# Render the delta text live so the dashboard/terminal shows progress
|
|
11561
|
+
# in real time, and remember that we streamed it so the final
|
|
11562
|
+
# assistant block does not print the same text again (double-print).
|
|
11563
|
+
# Purely additive: if partial messages are off, no stream_event lines
|
|
11564
|
+
# arrive and this branch never fires.
|
|
11565
|
+
if msg_type == "stream_event":
|
|
11566
|
+
ev = data.get("event", {})
|
|
11567
|
+
ev_type = ev.get("type")
|
|
11568
|
+
if ev_type == "message_start":
|
|
11569
|
+
# New message beginning: reset the streamed-text tracker so
|
|
11570
|
+
# the deltas of this message are tracked independently.
|
|
11571
|
+
streamed_text_blocks = False
|
|
11572
|
+
elif ev_type == "content_block_delta":
|
|
11573
|
+
delta = ev.get("delta", {})
|
|
11574
|
+
if delta.get("type") == "text_delta":
|
|
11575
|
+
dtext = delta.get("text", "")
|
|
11576
|
+
if dtext:
|
|
11577
|
+
print(dtext, end="", flush=True)
|
|
11578
|
+
streamed_text_blocks = True
|
|
11579
|
+
continue
|
|
11580
|
+
|
|
11376
11581
|
if msg_type == "assistant":
|
|
11377
11582
|
# Extract and print assistant text
|
|
11378
11583
|
message = data.get("message", {})
|
|
@@ -11380,7 +11585,9 @@ def process_stream():
|
|
|
11380
11585
|
for item in content:
|
|
11381
11586
|
if item.get("type") == "text":
|
|
11382
11587
|
text = item.get("text", "")
|
|
11383
|
-
if text
|
|
11588
|
+
# Skip if we already streamed this text via stream_event
|
|
11589
|
+
# deltas (avoids printing the full message a second time).
|
|
11590
|
+
if text and not streamed_text_blocks:
|
|
11384
11591
|
print(text, end="", flush=True)
|
|
11385
11592
|
elif item.get("type") == "tool_use":
|
|
11386
11593
|
tool = item.get("name", "unknown")
|
|
@@ -11606,6 +11813,10 @@ if __name__ == "__main__":
|
|
|
11606
11813
|
|
|
11607
11814
|
# Auto-track iteration completion (for dashboard task queue)
|
|
11608
11815
|
track_iteration_complete "$ITERATION_COUNT" "$exit_code"
|
|
11816
|
+
# v7.8.1: record the codebase signature after a clean no-PRD iteration
|
|
11817
|
+
# that has a generated PRD, so the next no-PRD run can decide reuse vs
|
|
11818
|
+
# update. Best-effort, never fails the iteration.
|
|
11819
|
+
persist_prd_signature_if_present "$exit_code"
|
|
11609
11820
|
|
|
11610
11821
|
# Sentrux architectural-drift gate diff + finding emission (opt-in, v7.5.15).
|
|
11611
11822
|
_loki_sentrux_iteration_end "$ITERATION_COUNT" "${TARGET_DIR:-.}"
|
package/dashboard/__init__.py
CHANGED
package/docs/INSTALLATION.md
CHANGED
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var _7=Object.defineProperty;var I7=(K)=>K;function P7(K,$){this[K]=I7.bind(null,$)}var v=(K,$)=>{for(var Q in $)_7(K,Q,{get:$[Q],enumerable:!0,configurable:!0,set:P7.bind($,Q)})};var w=(K,$)=>()=>(K&&($=K(K=0)),$);var t=import.meta.require;var e1={};v(e1,{lokiDir:()=>L,homeLokiDir:()=>k1,findRepoRootForVersion:()=>S1,REPO_ROOT:()=>p});import{resolve as u,dirname as N1}from"path";import{fileURLToPath as L7}from"url";import{existsSync as J1}from"fs";import{homedir as R7}from"os";function E7(){let K=i1;for(let $=0;$<6;$++){if(J1(u(K,"VERSION"))&&J1(u(K,"autonomy/run.sh")))return K;let Q=N1(K);if(Q===K)break;K=Q}return u(i1,"..","..","..")}function S1(K){let $=K;for(let Q=0;Q<6;Q++){if(J1(u($,"VERSION"))&&J1(u($,"autonomy/run.sh")))return $;let X=N1($);if(X===$)break;$=X}return u(K,"..","..","..")}function L(){return process.env.LOKI_DIR??u(process.cwd(),".loki")}function k1(){return u(R7(),".loki")}var i1,p;var g=w(()=>{i1=N1(L7(import.meta.url));p=E7()});import{readFileSync as w7}from"fs";import{resolve as x7,dirname as F7}from"path";import{fileURLToPath as N7}from"url";function G1(){if(o!==null)return o;let K="7.
|
|
2
|
+
var _7=Object.defineProperty;var I7=(K)=>K;function P7(K,$){this[K]=I7.bind(null,$)}var v=(K,$)=>{for(var Q in $)_7(K,Q,{get:$[Q],enumerable:!0,configurable:!0,set:P7.bind($,Q)})};var w=(K,$)=>()=>(K&&($=K(K=0)),$);var t=import.meta.require;var e1={};v(e1,{lokiDir:()=>L,homeLokiDir:()=>k1,findRepoRootForVersion:()=>S1,REPO_ROOT:()=>p});import{resolve as u,dirname as N1}from"path";import{fileURLToPath as L7}from"url";import{existsSync as J1}from"fs";import{homedir as R7}from"os";function E7(){let K=i1;for(let $=0;$<6;$++){if(J1(u(K,"VERSION"))&&J1(u(K,"autonomy/run.sh")))return K;let Q=N1(K);if(Q===K)break;K=Q}return u(i1,"..","..","..")}function S1(K){let $=K;for(let Q=0;Q<6;Q++){if(J1(u($,"VERSION"))&&J1(u($,"autonomy/run.sh")))return $;let X=N1($);if(X===$)break;$=X}return u(K,"..","..","..")}function L(){return process.env.LOKI_DIR??u(process.cwd(),".loki")}function k1(){return u(R7(),".loki")}var i1,p;var g=w(()=>{i1=N1(L7(import.meta.url));p=E7()});import{readFileSync as w7}from"fs";import{resolve as x7,dirname as F7}from"path";import{fileURLToPath as N7}from"url";function G1(){if(o!==null)return o;let K="7.8.1";if(typeof K==="string"&&K.length>0)return o=K,o;try{let $=F7(N7(import.meta.url)),Q=S1($);o=w7(x7(Q,"VERSION"),"utf-8").trim()}catch{o="unknown"}return o}var o=null;var C1=w(()=>{g()});var $0={};v($0,{runOrThrow:()=>S7,run:()=>C,commandVersion:()=>C7,commandExists:()=>b,ShellError:()=>D1});async function C(K,$={}){let Q=Bun.spawn({cmd:[...K],stdout:"pipe",stderr:"pipe",env:$.env?{...process.env,...$.env}:process.env,cwd:$.cwd}),X,Z;if($.timeoutMs&&$.timeoutMs>0)X=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}Z=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},$.timeoutMs);try{let[W,z,q]=await Promise.all([new Response(Q.stdout).text(),new Response(Q.stderr).text(),Q.exited]);return{stdout:W,stderr:z,exitCode:q}}finally{if(X)clearTimeout(X);if(Z)clearTimeout(Z)}}async function S7(K,$={}){let Q=await C(K,$);if(Q.exitCode!==0)throw new D1(`command failed (${Q.exitCode}): ${K.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function b(K){let $=k7(K),Q=await C(["sh","-c",`command -v ${$}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function k7(K){if(!/^[A-Za-z0-9._/-]+$/.test(K))throw Error(`refused to shell-escape suspect token: ${K}`);return K}async function C7(K,$="--version"){if(!await b(K))return null;let X=await C([K,$],{timeoutMs:5000});if(X.exitCode!==0)return null;return((X.stdout||X.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var D1;var n=w(()=>{D1=class D1 extends Error{message;exitCode;stdout;stderr;constructor(K,$,Q,X){super(K);this.message=K;this.exitCode=$;this.stdout=Q;this.stderr=X;this.name="ShellError"}}});function c(K){return D7?"":K}var D7,F,y,N,A6,A,D,S,H;var a=w(()=>{D7=(process.env.NO_COLOR??"").length>0;F=c("\x1B[0;31m"),y=c("\x1B[0;32m"),N=c("\x1B[1;33m"),A6=c("\x1B[0;34m"),A=c("\x1B[0;36m"),D=c("\x1B[1m"),S=c("\x1B[2m"),H=c("\x1B[0m")});import{existsSync as c7}from"fs";async function i(){if(Z1!==void 0)return Z1;let K="/opt/homebrew/bin/python3.12";if(c7(K))return Z1=K,K;let $=await b("python3.12");if($)return Z1=$,$;let Q=await b("python3");return Z1=Q,Q}async function s(K,$={}){let Q=await i();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return C([Q,"-c",K],$)}var Z1;var z1=w(()=>{n()});var G0={};v(G0,{runStatus:()=>X5});import{existsSync as k,readFileSync as K1,readdirSync as W0,statSync as H0}from"fs";import{resolve as R,basename as a7}from"path";import{homedir as s7}from"os";async function t7(){if(await b("jq"))return!0;return process.stdout.write(`${F}Error: jq is required but not installed.${H}
|
|
3
3
|
`),process.stdout.write(`Install with:
|
|
4
4
|
`),process.stdout.write(` brew install jq (macOS)
|
|
5
5
|
`),process.stdout.write(` apt install jq (Debian/Ubuntu)
|
|
@@ -604,4 +604,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
604
604
|
`),2}default:return process.stderr.write(`Unknown command: ${$}
|
|
605
605
|
`),process.stderr.write(j7),2}}process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var Z6=await X6(Bun.argv.slice(2));process.exit(Z6);
|
|
606
606
|
|
|
607
|
-
//# debugId=
|
|
607
|
+
//# debugId=729ABA8F615F047264756E2164756E21
|
package/mcp/__init__.py
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loki-mode",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.8.1",
|
|
4
4
|
"description": "Loki Mode by Autonomi. Autonomous spec-to-product system: takes a PRD, GitHub issue, OpenAPI/JSON/YAML, or one-line brief to a deployed app via the RARV-C closure loop with 11 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
package/providers/claude.sh
CHANGED
|
@@ -216,6 +216,23 @@ _loki_build_claude_auto_flags() {
|
|
|
216
216
|
&& loki_claude_flag_supported "--append-system-prompt"; then
|
|
217
217
|
_LOKI_CLAUDE_AUTO_FLAGS+=("--append-system-prompt" "$(_loki_autonomy_override_text)")
|
|
218
218
|
fi
|
|
219
|
+
|
|
220
|
+
# --setting-sources (v7.8.0): pin the settings precedence so the invocation
|
|
221
|
+
# does not drift with Claude Code's implicit default. Behavior-neutral.
|
|
222
|
+
# Default-on; opt out with LOKI_SETTING_SOURCES=off.
|
|
223
|
+
if [ "${LOKI_SETTING_SOURCES:-on}" != "off" ] \
|
|
224
|
+
&& loki_claude_flag_supported "--setting-sources"; then
|
|
225
|
+
_LOKI_CLAUDE_AUTO_FLAGS+=("--setting-sources" "user,project,local")
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
# --include-partial-messages (v7.8.0): stream incremental assistant deltas
|
|
229
|
+
# for real-time dashboard/terminal output. The stream-json parser handles
|
|
230
|
+
# the stream_event type additively and de-dupes against the final message.
|
|
231
|
+
# Default-on; opt out with LOKI_PARTIAL_MESSAGES=off.
|
|
232
|
+
if [ "${LOKI_PARTIAL_MESSAGES:-on}" != "off" ] \
|
|
233
|
+
&& loki_claude_flag_supported "--include-partial-messages"; then
|
|
234
|
+
_LOKI_CLAUDE_AUTO_FLAGS+=("--include-partial-messages")
|
|
235
|
+
fi
|
|
219
236
|
}
|
|
220
237
|
|
|
221
238
|
# The system-prompt text that authorizes autonomous operation and resolves
|