loki-mode 6.36.2 → 6.36.4

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 CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: Multi-agent autonomous startup system. Triggers on "Loki Mode". Takes PRD to deployed product with minimal human intervention. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v6.36.2
6
+ # Loki Mode v6.36.4
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -267,4 +267,4 @@ The following features are documented in skill modules but not yet fully automat
267
267
  | Quality gates 3-reviewer system | Implemented (v5.35.0) | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
268
268
  | Benchmarks (HumanEval, SWE-bench) | Infrastructure only | Runner scripts and datasets exist in `benchmarks/`; no published results |
269
269
 
270
- **v6.36.2 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
270
+ **v6.36.4 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 6.36.2
1
+ 6.36.4
package/autonomy/loki CHANGED
@@ -219,7 +219,7 @@ require_jq() {
219
219
  echo " brew install jq (macOS)"
220
220
  echo " apt install jq (Debian/Ubuntu)"
221
221
  echo " yum install jq (RHEL/CentOS)"
222
- exit 1
222
+ return 1
223
223
  fi
224
224
  }
225
225
 
@@ -551,6 +551,10 @@ cmd_start() {
551
551
  ;;
552
552
  --provider)
553
553
  if [[ -n "${2:-}" ]]; then
554
+ if [[ "$2" == --* ]]; then
555
+ echo -e "${RED}Missing value for --provider flag${NC}"
556
+ exit 1
557
+ fi
554
558
  provider="$2"
555
559
  args+=("--provider" "$provider")
556
560
  shift 2
@@ -1450,7 +1454,7 @@ cmd_status() {
1450
1454
  esac
1451
1455
  done
1452
1456
 
1453
- require_jq
1457
+ require_jq || return 1
1454
1458
 
1455
1459
  if [ ! -d "$LOKI_DIR" ]; then
1456
1460
  echo -e "${BOLD}Loki Mode Status${NC}"
@@ -3451,7 +3455,7 @@ cmd_issue_parse() {
3451
3455
 
3452
3456
  # View parsed GitHub issue details
3453
3457
  cmd_issue_view() {
3454
- require_jq
3458
+ require_jq || return 1
3455
3459
 
3456
3460
  local issue_ref="${1:-}"
3457
3461
 
@@ -3566,7 +3570,7 @@ cmd_issue_view() {
3566
3570
  #===============================================================================
3567
3571
 
3568
3572
  cmd_run() {
3569
- require_jq
3573
+ require_jq || return 1
3570
3574
 
3571
3575
  local issue_ref=""
3572
3576
  local dry_run=false
@@ -3731,6 +3735,22 @@ cmd_run() {
3731
3735
  esac
3732
3736
  done
3733
3737
 
3738
+ # Validate git prerequisites for --ship and --pr flags
3739
+ if $create_pr || $auto_merge; then
3740
+ if ! command -v git &>/dev/null; then
3741
+ echo -e "${RED}Error: --pr/--ship requires git but it is not installed.${NC}"
3742
+ return 1
3743
+ fi
3744
+ if ! git rev-parse --git-dir &>/dev/null 2>&1; then
3745
+ echo -e "${RED}Error: --pr/--ship requires a git repository but the current directory is not one.${NC}"
3746
+ return 1
3747
+ fi
3748
+ if [[ -z "$(git remote 2>/dev/null)" ]]; then
3749
+ echo -e "${RED}Error: --pr/--ship requires a git remote but none is configured.${NC}"
3750
+ return 1
3751
+ fi
3752
+ fi
3753
+
3734
3754
  # Add --parallel once if worktree mode is enabled (not per-flag)
3735
3755
  if $use_worktree; then
3736
3756
  start_args+=("--parallel")
@@ -3994,7 +4014,7 @@ cmd_issue() {
3994
4014
  echo -e "${YELLOW} 'loki run' supports GitHub, GitLab, Jira, and Azure DevOps issues.${NC}" >&2
3995
4015
  echo "" >&2
3996
4016
 
3997
- require_jq
4017
+ require_jq || return 1
3998
4018
 
3999
4019
  local issue_ref=""
4000
4020
  local repo=""
@@ -6595,6 +6615,19 @@ QPRDEOF
6595
6615
 
6596
6616
  # Project scaffolding (v6.28.0)
6597
6617
  cmd_init() {
6618
+ # Guard: check if .loki/ already exists to avoid overwriting active session
6619
+ if [[ -d ".loki" ]]; then
6620
+ if [[ -f ".loki/loki.pid" ]]; then
6621
+ local pid
6622
+ pid=$(cat ".loki/loki.pid" 2>/dev/null)
6623
+ if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
6624
+ echo -e "${RED}Cannot initialize: active session running. Stop it first with 'loki stop'.${NC}"
6625
+ return 1
6626
+ fi
6627
+ fi
6628
+ echo -e "${YELLOW}Reinitializing existing .loki/ directory${NC}"
6629
+ fi
6630
+
6598
6631
  local version=$(get_version)
6599
6632
  local project_name=""
6600
6633
  local template_name=""
@@ -10269,10 +10302,25 @@ cmd_worktree() {
10269
10302
  return 1
10270
10303
  fi
10271
10304
 
10305
+ # Validate JSON is parseable and contains required fields
10306
+ if ! python3 -c "
10307
+ import json, sys
10308
+ try:
10309
+ data = json.load(open('$signal_file'))
10310
+ assert data.get('branch'), 'missing branch'
10311
+ assert data.get('worktree'), 'missing worktree'
10312
+ except Exception as e:
10313
+ print(str(e), file=sys.stderr)
10314
+ sys.exit(1)
10315
+ " 2>/dev/null; then
10316
+ echo -e "${RED}Invalid or corrupted merge signal file${NC}"
10317
+ exit 1
10318
+ fi
10319
+
10272
10320
  local branch
10273
- branch=$(python3 -c "import json; print(json.load(open('$signal_file'))['branch'])" 2>/dev/null)
10321
+ branch=$(python3 -c "import json; print(json.load(open('$signal_file'))['branch'])")
10274
10322
  local worktree_path
10275
- worktree_path=$(python3 -c "import json; print(json.load(open('$signal_file'))['worktree'])" 2>/dev/null)
10323
+ worktree_path=$(python3 -c "import json; print(json.load(open('$signal_file'))['worktree'])")
10276
10324
 
10277
10325
  if git merge --no-ff "$branch" -m "merge($name): auto-merge from parallel worktree"; then
10278
10326
  echo -e "${GREEN}Merge successful${NC}"
@@ -10785,7 +10833,7 @@ print()
10785
10833
 
10786
10834
  # Reset session state
10787
10835
  cmd_reset() {
10788
- require_jq
10836
+ require_jq || return 1
10789
10837
 
10790
10838
  local subcommand="${1:-all}"
10791
10839
 
package/autonomy/run.sh CHANGED
@@ -1212,13 +1212,17 @@ detect_complexity() {
1212
1212
  fi
1213
1213
 
1214
1214
  # Count files in project (excluding common non-source dirs)
1215
- local file_count=$(find "$target_dir" -type f \
1215
+ local file_count=0
1216
+ file_count=$(find "$target_dir" -type f \
1216
1217
  \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \
1217
1218
  -o -name "*.py" -o -name "*.go" -o -name "*.rs" -o -name "*.java" \
1218
1219
  -o -name "*.rb" -o -name "*.php" -o -name "*.swift" -o -name "*.kt" \) \
1219
1220
  ! -path "*/node_modules/*" ! -path "*/.git/*" ! -path "*/vendor/*" \
1220
1221
  ! -path "*/dist/*" ! -path "*/build/*" ! -path "*/__pycache__/*" \
1221
1222
  2>/dev/null | wc -l | tr -d ' ')
1223
+ # Validate file_count is numeric (guard against empty/malformed pipeline output)
1224
+ file_count="${file_count:-0}"
1225
+ file_count="${file_count//[^0-9]/}"
1222
1226
 
1223
1227
  # Check for external integrations
1224
1228
  local has_external=false
@@ -7901,8 +7905,10 @@ build_prompt() {
7901
7905
  # Load existing context if resuming
7902
7906
  local context_injection=""
7903
7907
  if [ $retry -gt 0 ]; then
7904
- local ledger=$(load_ledger_context)
7905
- local handoff=$(load_handoff_context)
7908
+ local ledger=""
7909
+ type -t load_ledger_context &>/dev/null && ledger=$(load_ledger_context)
7910
+ local handoff=""
7911
+ type -t load_handoff_context &>/dev/null && handoff=$(load_handoff_context)
7906
7912
 
7907
7913
  if [ -n "$ledger" ]; then
7908
7914
  context_injection="PREVIOUS_LEDGER_STATE: $ledger"
@@ -8432,11 +8438,15 @@ run_autonomous() {
8432
8438
  if [ -n "$found_prd" ]; then
8433
8439
  log_info "Found existing PRD: $found_prd"
8434
8440
  prd_path="$found_prd"
8441
+ # Warn if a generated PRD also exists (user file takes precedence)
8442
+ if [ -f ".loki/generated-prd.md" ] || [ -f ".loki/generated-prd.json" ]; then
8443
+ log_warn "Using user PRD ($found_prd) instead of generated PRD (.loki/generated-prd.md). Remove generated PRD if no longer needed."
8444
+ fi
8435
8445
  elif [ -f ".loki/generated-prd.md" ]; then
8436
- log_info "Using previously generated PRD: .loki/generated-prd.md"
8446
+ log_info "No user PRD found. Using previously generated PRD: .loki/generated-prd.md"
8437
8447
  prd_path=".loki/generated-prd.md"
8438
8448
  elif [ -f ".loki/generated-prd.json" ]; then
8439
- log_info "Using previously generated PRD: .loki/generated-prd.json"
8449
+ log_info "No user PRD found. Using previously generated PRD: .loki/generated-prd.json"
8440
8450
  prd_path=".loki/generated-prd.json"
8441
8451
  else
8442
8452
  log_info "No PRD found - will analyze codebase and generate one"
@@ -9194,8 +9204,9 @@ check_human_intervention() {
9194
9204
  log_warn "PAUSE file created by budget limit - NOT auto-clearing in perpetual mode"
9195
9205
  log_warn "Budget limit reached. Remove .loki/signals/BUDGET_EXCEEDED and .loki/PAUSE to continue."
9196
9206
  notify_intervention_needed "Budget limit reached - execution paused" 2>/dev/null || true
9207
+ local pause_result
9197
9208
  handle_pause
9198
- local pause_result=$?
9209
+ pause_result=$?
9199
9210
  rm -f "$loki_dir/PAUSE"
9200
9211
  if [ "$pause_result" -eq 1 ]; then
9201
9212
  return 2
@@ -9211,8 +9222,9 @@ check_human_intervention() {
9211
9222
  fi
9212
9223
  log_warn "PAUSE file detected - pausing execution"
9213
9224
  notify_intervention_needed "Execution paused via PAUSE file"
9225
+ local pause_result
9214
9226
  handle_pause
9215
- local pause_result=$?
9227
+ pause_result=$?
9216
9228
  rm -f "$loki_dir/PAUSE"
9217
9229
  if [ "$pause_result" -eq 1 ]; then
9218
9230
  # STOP was requested during pause
@@ -9228,8 +9240,9 @@ check_human_intervention() {
9228
9240
  rm -f "$loki_dir/PAUSE_AT_CHECKPOINT"
9229
9241
  notify_intervention_needed "Execution paused at checkpoint"
9230
9242
  touch "$loki_dir/PAUSE"
9243
+ local pause_result
9231
9244
  handle_pause
9232
- local pause_result=$?
9245
+ pause_result=$?
9233
9246
  rm -f "$loki_dir/PAUSE"
9234
9247
  if [ "$pause_result" -eq 1 ]; then
9235
9248
  return 2
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "6.36.2"
10
+ __version__ = "6.36.3"
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/). Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v6.36.2
5
+ **Version:** v6.36.3
6
6
 
7
7
  ---
8
8
 
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '6.36.2'
60
+ __version__ = '6.36.3'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loki-mode",
3
- "version": "6.36.2",
3
+ "version": "6.36.4",
4
4
  "description": "Loki Mode by Autonomi - Multi-agent autonomous startup system for Claude Code, Codex CLI, and Gemini CLI",
5
5
  "keywords": [
6
6
  "agent",
package/web-app/server.py CHANGED
@@ -44,9 +44,20 @@ DIST_DIR = SCRIPT_DIR / "dist"
44
44
 
45
45
  app = FastAPI(title="Purple Lab", docs_url=None, redoc_url=None)
46
46
 
47
+ _default_cors_origins = [
48
+ f"http://127.0.0.1:{PORT}",
49
+ f"http://localhost:{PORT}",
50
+ ]
51
+ _cors_env = os.environ.get("PURPLE_LAB_CORS_ORIGINS", "")
52
+ _cors_origins = (
53
+ [o.strip() for o in _cors_env.split(",") if o.strip()]
54
+ if _cors_env
55
+ else _default_cors_origins
56
+ )
57
+
47
58
  app.add_middleware(
48
59
  CORSMiddleware,
49
- allow_origins=["http://127.0.0.1:57375", "http://localhost:57375"],
60
+ allow_origins=_cors_origins,
50
61
  allow_methods=["*"],
51
62
  allow_headers=["*"],
52
63
  )
@@ -149,12 +160,25 @@ def _loki_dir() -> Path:
149
160
 
150
161
 
151
162
  def _safe_resolve(base: Path, requested: str) -> Optional[Path]:
152
- """Resolve a path ensuring it stays within base (path traversal protection)."""
163
+ """Resolve a path ensuring it stays within base (path traversal protection).
164
+
165
+ Uses os.path.commonpath to avoid the startswith prefix collision where
166
+ /tmp/proj would incorrectly pass a check against /tmp/projother.
167
+ Also rejects symlinks that escape the base directory.
168
+ """
153
169
  try:
154
170
  resolved = (base / requested).resolve()
155
171
  base_resolved = base.resolve()
156
- if str(resolved).startswith(str(base_resolved)):
157
- return resolved
172
+ # Ensure resolved is strictly inside base_resolved
173
+ resolved.relative_to(base_resolved)
174
+ # Reject if any component is a symlink pointing outside base
175
+ check = base_resolved
176
+ for part in resolved.relative_to(base_resolved).parts:
177
+ check = check / part
178
+ if check.is_symlink():
179
+ link_target = check.resolve()
180
+ link_target.relative_to(base_resolved) # raises ValueError if outside
181
+ return resolved
158
182
  except (ValueError, OSError):
159
183
  pass
160
184
  return None