loki-mode 5.58.2 → 5.59.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.
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 v5.58.2
6
+ # Loki Mode v5.59.0
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -263,4 +263,4 @@ The following features are documented in skill modules but not yet fully automat
263
263
  | Quality gates 3-reviewer system | Implemented (v5.35.0) | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
264
264
  | Benchmarks (HumanEval, SWE-bench) | Infrastructure only | Runner scripts and datasets exist in `benchmarks/`; no published results |
265
265
 
266
- **v5.58.2 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
266
+ **v5.59.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 5.58.2
1
+ 5.59.0
package/autonomy/loki CHANGED
@@ -4839,6 +4839,8 @@ cmd_migrate_help() {
4839
4839
  echo " --resume Resume from last checkpoint"
4840
4840
  echo " --multi-repo <glob> Multiple repository paths (glob pattern)"
4841
4841
  echo " --export-report Export migration report to file"
4842
+ echo " --no-dashboard Disable web dashboard during migration"
4843
+ echo " --no-docs Skip migration_docs/ generation"
4842
4844
  echo " --list List all migrations"
4843
4845
  echo " --status [migration-id] Show migration status"
4844
4846
  echo ""
@@ -4994,6 +4996,8 @@ cmd_migrate_start() {
4994
4996
  local resume="$8"
4995
4997
  local multi_repo="$9"
4996
4998
  local export_report="${10}"
4999
+ local no_dashboard="${11:-false}"
5000
+ local no_docs="${12:-false}"
4997
5001
 
4998
5002
  local migrations_dir="${HOME}/.loki/migrations"
4999
5003
  local migration_id=""
@@ -5118,9 +5122,18 @@ if manifests:
5118
5122
  echo ""
5119
5123
  fi
5120
5124
 
5121
- # Create migration via engine
5122
- local create_result
5123
- create_result=$(PYTHONPATH="${SKILL_DIR:-.}" python3 -c "
5125
+ local migration_dir
5126
+ if [ -n "$migration_id" ]; then
5127
+ # Resume: load existing migration directory
5128
+ migration_dir="${migrations_dir}/${migration_id}"
5129
+ if [ ! -d "$migration_dir" ]; then
5130
+ echo -e "${RED}Error: Migration directory not found: ${migration_dir}${NC}"
5131
+ return 1
5132
+ fi
5133
+ else
5134
+ # Create new migration via engine
5135
+ local create_result
5136
+ create_result=$(PYTHONPATH="${SKILL_DIR:-.}" python3 -c "
5124
5137
  import json, sys
5125
5138
  sys.path.insert(0, '.')
5126
5139
  from dashboard.migration_engine import MigrationPipeline
@@ -5128,13 +5141,13 @@ pipeline = MigrationPipeline(sys.argv[1], sys.argv[2])
5128
5141
  manifest = pipeline.create_manifest()
5129
5142
  print(json.dumps({'id': manifest.id, 'dir': str(pipeline.migration_dir)}))
5130
5143
  " "$codebase_path" "$target" 2>&1) || {
5131
- echo -e "${RED}Error: Failed to create migration${NC}"
5132
- echo "$create_result"
5133
- return 1
5134
- }
5135
- migration_id=$(echo "$create_result" | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")
5136
- local migration_dir
5137
- migration_dir=$(echo "$create_result" | python3 -c "import json,sys; print(json.load(sys.stdin)['dir'])")
5144
+ echo -e "${RED}Error: Failed to create migration${NC}"
5145
+ echo "$create_result"
5146
+ return 1
5147
+ }
5148
+ migration_id=$(echo "$create_result" | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")
5149
+ migration_dir=$(echo "$create_result" | python3 -c "import json,sys; print(json.load(sys.stdin)['dir'])")
5150
+ fi
5138
5151
 
5139
5152
  # Security scan (first step of understand phase)
5140
5153
  echo -e "${BOLD}[Security] Scanning for secrets...${NC}"
@@ -5190,6 +5203,79 @@ print(json.dumps({'id': manifest.id, 'dir': str(pipeline.migration_dir)}))
5190
5203
  [ -n "$compliance" ] && echo -e " Compliance: ${compliance}"
5191
5204
  echo ""
5192
5205
 
5206
+ # Dashboard cleanup trap for error paths
5207
+ local migrate_dashboard_pid=""
5208
+ _migrate_cleanup_dashboard() {
5209
+ if [ -n "$migrate_dashboard_pid" ]; then
5210
+ kill "$migrate_dashboard_pid" 2>/dev/null || true
5211
+ wait "$migrate_dashboard_pid" 2>/dev/null || true
5212
+ rm -f "${codebase_path}/.loki/dashboard/dashboard.pid" 2>/dev/null || true
5213
+ fi
5214
+ }
5215
+ trap '_migrate_cleanup_dashboard' RETURN
5216
+
5217
+ # Start dashboard for migration monitoring (unless --no-dashboard)
5218
+ if [ "$no_dashboard" != "true" ]; then
5219
+ local dashboard_port="${LOKI_DASHBOARD_PORT:-57374}"
5220
+ local skill_dir="${SKILL_DIR:-.}"
5221
+ local dashboard_venv="$HOME/.loki/dashboard-venv"
5222
+ local python_cmd="python3"
5223
+ local url_scheme="http"
5224
+
5225
+ # Use venv python if available
5226
+ if [ -x "${dashboard_venv}/bin/python3" ]; then
5227
+ python_cmd="${dashboard_venv}/bin/python3"
5228
+ fi
5229
+
5230
+ # Check if dashboard deps are available
5231
+ if "$python_cmd" -c "import fastapi" 2>/dev/null; then
5232
+ # Find available port (increment until a free port is found)
5233
+ local port_attempts=0
5234
+ while lsof -i :"$dashboard_port" &>/dev/null && [ $port_attempts -lt 10 ]; do
5235
+ dashboard_port=$((dashboard_port + 1))
5236
+ port_attempts=$((port_attempts + 1))
5237
+ done
5238
+
5239
+ if [ $port_attempts -lt 10 ]; then
5240
+ # Determine TLS
5241
+ if [ -n "${LOKI_TLS_CERT:-}" ] && [ -n "${LOKI_TLS_KEY:-}" ]; then
5242
+ url_scheme="https"
5243
+ fi
5244
+
5245
+ if ! mkdir -p "${codebase_path}/.loki/dashboard/logs" 2>/dev/null; then
5246
+ echo -e " ${YELLOW}Dashboard skipped (cannot create log directory)${NC}"
5247
+ else
5248
+ local log_file="${codebase_path}/.loki/dashboard/logs/dashboard.log"
5249
+
5250
+ LOKI_DASHBOARD_PORT="$dashboard_port" \
5251
+ LOKI_DASHBOARD_HOST="127.0.0.1" \
5252
+ LOKI_PROJECT_PATH="$codebase_path" \
5253
+ LOKI_SKILL_DIR="$skill_dir" \
5254
+ LOKI_TLS_CERT="${LOKI_TLS_CERT:-}" \
5255
+ LOKI_TLS_KEY="${LOKI_TLS_KEY:-}" \
5256
+ PYTHONPATH="$skill_dir" \
5257
+ nohup "$python_cmd" -m dashboard.server > "$log_file" 2>&1 &
5258
+ migrate_dashboard_pid=$!
5259
+
5260
+ echo "$migrate_dashboard_pid" > "${codebase_path}/.loki/dashboard/dashboard.pid"
5261
+
5262
+ sleep 2
5263
+ if kill -0 "$migrate_dashboard_pid" 2>/dev/null; then
5264
+ echo -e " Dashboard: ${CYAN}${url_scheme}://127.0.0.1:${dashboard_port}/${NC}"
5265
+ if [[ "$OSTYPE" == "darwin"* ]]; then
5266
+ open "${url_scheme}://127.0.0.1:${dashboard_port}/" 2>/dev/null || true
5267
+ fi
5268
+ echo ""
5269
+ else
5270
+ echo -e " ${YELLOW}Dashboard failed to start (check logs)${NC}"
5271
+ migrate_dashboard_pid=""
5272
+ echo ""
5273
+ fi
5274
+ fi
5275
+ fi
5276
+ fi
5277
+ fi
5278
+
5193
5279
  # Determine which phases to run
5194
5280
  local phases_to_run=()
5195
5281
  if [ -n "$phase" ]; then
@@ -5238,7 +5324,7 @@ Tasks:
5238
5324
  1. Analyze the full codebase structure (languages, frameworks, dependencies, architecture)
5239
5325
  2. Create the docs directory: mkdir -p ${migration_dir}/docs
5240
5326
  3. Write analysis documentation to ${migration_dir}/docs/analysis.md
5241
- 3. Identify migration seams (logical boundaries for incremental migration) and write them to ${migration_dir}/seams.json as a JSON array of objects with fields: id (string, e.g. 'seam-01'), name (string), description (string), type (string: 'module'/'api'/'config'/'adapter'), files (array of file paths), dependencies (array of seam ids), priority (string: 'high'/'medium'/'low')
5327
+ 4. Identify migration seams (logical boundaries for incremental migration) and write them to ${migration_dir}/seams.json as a JSON array of objects with fields: id (string, e.g. 'seam-01'), name (string), description (string), type (string: 'module'/'api'/'config'/'adapter'), files (array of file paths), dependencies (array of seam ids), priority (string: 'high'/'medium'/'low')
5242
5328
 
5243
5329
  You MUST create both files. The migration cannot proceed without them.
5244
5330
  Write the analysis doc first, then the seams.json."
@@ -5383,6 +5469,119 @@ pipeline.advance_phase(sys.argv[2])
5383
5469
  echo ""
5384
5470
  done
5385
5471
 
5472
+ # Generate migration_docs/ in the codebase (unless --no-docs or --plan-only)
5473
+ if [ "$plan_only" != "true" ] && [ "$no_docs" != "true" ]; then
5474
+ echo -e "${CYAN}[Phase: document]${NC} Generating migration documentation..."
5475
+
5476
+ local doc_prompt="You are generating comprehensive migration documentation for a completed codebase migration.
5477
+
5478
+ Target: ${target}
5479
+ Codebase: ${codebase_path}
5480
+ Migration dir: ${migration_dir}
5481
+
5482
+ Read the following files for context:
5483
+ - ${migration_dir}/docs/analysis.md (original codebase analysis)
5484
+ - ${migration_dir}/docs/verification-report.md (verification results)
5485
+ - ${migration_dir}/features.json (feature tracking)
5486
+ - ${migration_dir}/seams.json (migration boundaries)
5487
+ - ${migration_dir}/migration-plan.json (migration steps)
5488
+
5489
+ Create a directory called migration_docs/ in the codebase root (${codebase_path}/migration_docs/) with the following files:
5490
+
5491
+ 1. **README.md** - Executive summary of the migration:
5492
+ - What the codebase was before (source language/framework/architecture)
5493
+ - What it is now (target: ${target})
5494
+ - High-level summary of changes made
5495
+ - Link to other docs in migration_docs/
5496
+
5497
+ 2. **USAGE.md** - How to use the migrated codebase:
5498
+ - Getting started / quick start
5499
+ - Key commands and workflows
5500
+ - Configuration options
5501
+ - Environment variables
5502
+
5503
+ 3. **INSTALLATION.md** - Installation and setup:
5504
+ - Prerequisites and dependencies
5505
+ - Step-by-step installation
5506
+ - Build instructions
5507
+ - Development environment setup
5508
+
5509
+ 4. **TESTING.md** - Testing the migrated codebase:
5510
+ - How to run tests
5511
+ - Test coverage status
5512
+ - Known test gaps
5513
+ - How to add new tests
5514
+
5515
+ 5. **WHAT-CHANGED.md** - Detailed migration changelog:
5516
+ - Files added, modified, and removed
5517
+ - Architecture changes
5518
+ - API changes (if any)
5519
+ - Configuration changes
5520
+ - Dependency changes
5521
+
5522
+ 6. **WHAT-WORKS.md** - Current status:
5523
+ - Features confirmed working (from verification)
5524
+ - Features that need manual testing
5525
+ - Known limitations
5526
+
5527
+ 7. **WHAT-PENDING.md** - Remaining work:
5528
+ - Items flagged during verification
5529
+ - Manual steps required
5530
+ - Recommended follow-up tasks
5531
+ - Security items to address
5532
+
5533
+ 8. **DEPLOYMENT.md** - Deployment guide (if applicable):
5534
+ - How to deploy the migrated application
5535
+ - CI/CD considerations
5536
+ - Infrastructure changes needed
5537
+ - Rollback instructions
5538
+
5539
+ IMPORTANT RULES:
5540
+ - Only document what is TRUE and VERIFIED - never fabricate features or capabilities
5541
+ - Reference actual files and test results from the migration artifacts
5542
+ - If something was not tested or verified, say so explicitly
5543
+ - Mark uncertain items with 'NEEDS VERIFICATION' tags
5544
+ - Do NOT use emojis anywhere in the documentation"
5545
+
5546
+ local doc_exit=0
5547
+ local provider_name="${LOKI_PROVIDER:-claude}"
5548
+ case "$provider_name" in
5549
+ claude)
5550
+ (cd "$codebase_path" && claude --dangerously-skip-permissions -p "$doc_prompt" --output-format stream-json --verbose 2>&1) | \
5551
+ while IFS= read -r line; do
5552
+ if echo "$line" | python3 -c "
5553
+ import sys, json
5554
+ try:
5555
+ d = json.loads(sys.stdin.read())
5556
+ if d.get('type') == 'assistant':
5557
+ for item in d.get('message', {}).get('content', []):
5558
+ if item.get('type') == 'text':
5559
+ print(item.get('text', ''), end='')
5560
+ except Exception: pass
5561
+ " 2>/dev/null; then
5562
+ true
5563
+ fi
5564
+ done
5565
+ doc_exit=${PIPESTATUS[0]}
5566
+ ;;
5567
+ codex)
5568
+ (cd "$codebase_path" && codex exec --full-auto "$doc_prompt" 2>&1) || doc_exit=$?
5569
+ ;;
5570
+ gemini)
5571
+ (cd "$codebase_path" && gemini --approval-mode=yolo "$doc_prompt" 2>&1) || doc_exit=$?
5572
+ ;;
5573
+ esac
5574
+
5575
+ if [ "$doc_exit" -eq 0 ] && [ -d "${codebase_path}/migration_docs" ]; then
5576
+ local doc_count
5577
+ doc_count=$(find "${codebase_path}/migration_docs" -name "*.md" -type f 2>/dev/null | wc -l | tr -d ' ')
5578
+ echo -e "${GREEN} [Phase: document] Complete (${doc_count} docs generated)${NC}"
5579
+ else
5580
+ echo -e "${YELLOW} [Phase: document] Warning: Documentation may be incomplete${NC}"
5581
+ fi
5582
+ echo ""
5583
+ fi
5584
+
5386
5585
  # Plan-only: generate plan file and stop
5387
5586
  if [ "$plan_only" = "true" ]; then
5388
5587
  echo -e "${BOLD}Migration plan generated.${NC}"
@@ -5471,6 +5670,8 @@ cmd_migrate() {
5471
5670
  local do_list="false"
5472
5671
  local do_status="false"
5473
5672
  local status_id=""
5673
+ local no_dashboard="false"
5674
+ local no_docs="false"
5474
5675
 
5475
5676
  if [ $# -eq 0 ]; then
5476
5677
  cmd_migrate_help
@@ -5575,6 +5776,14 @@ cmd_migrate() {
5575
5776
  export_report="true"
5576
5777
  shift
5577
5778
  ;;
5779
+ --no-dashboard)
5780
+ no_dashboard="true"
5781
+ shift
5782
+ ;;
5783
+ --no-docs)
5784
+ no_docs="true"
5785
+ shift
5786
+ ;;
5578
5787
  -*)
5579
5788
  echo -e "${RED}Unknown option: $1${NC}"
5580
5789
  echo "Run 'loki migrate --help' for usage."
@@ -5618,7 +5827,7 @@ cmd_migrate() {
5618
5827
  return 1
5619
5828
  fi
5620
5829
 
5621
- cmd_migrate_start "$codebase_path" "$target" "$plan_only" "$phase" "$parallel" "$compliance" "$dry_run" "$do_resume" "$multi_repo" "$export_report"
5830
+ cmd_migrate_start "$codebase_path" "$target" "$plan_only" "$phase" "$parallel" "$compliance" "$dry_run" "$do_resume" "$multi_repo" "$export_report" "$no_dashboard" "$no_docs"
5622
5831
  }
5623
5832
 
5624
5833
  # Main command dispatcher
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "5.58.2"
10
+ __version__ = "5.59.0"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -126,6 +126,10 @@ class MigrationManifest:
126
126
  feature_list_path: str = ""
127
127
  migration_plan_path: str = ""
128
128
  checkpoints: list[str] = field(default_factory=list)
129
+ status: str = "pending"
130
+ progress_pct: int = 0
131
+ updated_at: str = ""
132
+ source_path: str = ""
129
133
 
130
134
 
131
135
  # ---------------------------------------------------------------------------
@@ -149,7 +153,7 @@ def _atomic_write(path: Path, content: str) -> None:
149
153
  os.fsync(fd)
150
154
  finally:
151
155
  os.close(fd)
152
- os.rename(tmp_path, str(path))
156
+ os.replace(tmp_path, str(path))
153
157
  except OSError as exc:
154
158
  logger.error("Failed to write %s: %s", path, exc)
155
159
  # Clean up temp file on failure
@@ -312,20 +316,23 @@ class MigrationPipeline:
312
316
  return phase_data.get("status", "pending")
313
317
 
314
318
  def start_phase(self, phase: str) -> None:
315
- """Start a phase (transition from pending to in_progress). Idempotent if already in_progress."""
319
+ """Start a phase (transition to in_progress).
320
+
321
+ Idempotent if already in_progress. Also allows restarting completed or
322
+ failed phases (e.g., when using --resume --phase <phase>).
323
+ """
316
324
  if phase not in PHASE_ORDER:
317
325
  raise ValueError(f"Unknown phase: {phase}")
318
326
  with self._lock:
319
327
  manifest = self._load_manifest_unlocked()
320
- current_status = manifest.phases[phase]["status"]
328
+ if phase not in manifest.phases:
329
+ manifest.phases[phase] = {"status": "pending", "started_at": "", "completed_at": ""}
330
+ current_status = manifest.phases[phase].get("status", "pending")
321
331
  if current_status == "in_progress":
322
332
  return # Already started, idempotent
323
- if current_status != "pending":
324
- raise RuntimeError(
325
- f"Cannot start phase '{phase}': status is '{current_status}', expected 'pending'"
326
- )
327
333
  manifest.phases[phase]["status"] = "in_progress"
328
334
  manifest.phases[phase]["started_at"] = datetime.now(timezone.utc).isoformat()
335
+ manifest.phases[phase]["completed_at"] = ""
329
336
  self._save_manifest_unlocked(manifest)
330
337
 
331
338
  def _check_phase_gate_unlocked(self, from_phase: str, to_phase: str) -> tuple[bool, str]:
@@ -402,55 +409,13 @@ class MigrationPipeline:
402
409
  def check_phase_gate(self, from_phase: str, to_phase: str) -> tuple[bool, str]:
403
410
  """Validate whether transition from from_phase to to_phase is allowed.
404
411
 
412
+ Thread-safe wrapper that delegates to _check_phase_gate_unlocked under lock.
413
+
405
414
  Returns:
406
415
  Tuple of (allowed, reason). If allowed is False, reason explains why.
407
416
  """
408
- if from_phase not in PHASE_ORDER or to_phase not in PHASE_ORDER:
409
- return False, f"Unknown phase: {from_phase} or {to_phase}"
410
-
411
- from_idx = PHASE_ORDER.index(from_phase)
412
- to_idx = PHASE_ORDER.index(to_phase)
413
- if to_idx != from_idx + 1:
414
- return False, f"Cannot jump from {from_phase} to {to_phase}"
415
-
416
- # Gate: understand -> guardrail
417
- if from_phase == "understand" and to_phase == "guardrail":
418
- docs_dir = self.migration_dir / "docs"
419
- has_docs = any(docs_dir.iterdir()) if docs_dir.exists() else False
420
- if not has_docs:
421
- return False, "Phase gate failed: no documentation generated in docs/"
422
- seams_path = self.migration_dir / "seams.json"
423
- if not seams_path.exists():
424
- return False, "Phase gate failed: seams.json does not exist"
425
- return True, "Gate passed: docs generated and seams.json exists"
426
-
427
- # Gate: guardrail -> migrate
428
- if from_phase == "guardrail" and to_phase == "migrate":
429
- try:
430
- features = self.load_features()
431
- except FileNotFoundError:
432
- return False, "Phase gate failed: features.json not found"
433
- if not features:
434
- return False, "No features defined"
435
- failing = [f for f in features if not f.passes]
436
- if failing:
437
- ids = ", ".join(f.id for f in failing[:5])
438
- return False, f"Phase gate failed: {len(failing)} characterization tests not passing ({ids})"
439
- return True, "Gate passed: all characterization tests pass"
440
-
441
- # Gate: migrate -> verify
442
- if from_phase == "migrate" and to_phase == "verify":
443
- try:
444
- plan = self.load_plan()
445
- except FileNotFoundError:
446
- return False, "Phase gate failed: migration-plan.json not found"
447
- incomplete = [s for s in plan.steps if s.status != "completed"]
448
- if incomplete:
449
- ids = ", ".join(s.id for s in incomplete[:5])
450
- return False, f"Phase gate failed: {len(incomplete)} steps not completed ({ids})"
451
- return True, "Gate passed: all migration steps completed"
452
-
453
- return True, "Gate passed"
417
+ with self._lock:
418
+ return self._check_phase_gate_unlocked(from_phase, to_phase)
454
419
 
455
420
  def advance_phase(self, phase: str) -> PhaseResult:
456
421
  """Mark the current phase as complete and start the next one.
@@ -492,6 +457,8 @@ class MigrationPipeline:
492
457
 
493
458
  # Start next phase if there is one
494
459
  if next_phase is not None:
460
+ if next_phase not in manifest.phases:
461
+ manifest.phases[next_phase] = {"status": "pending", "started_at": "", "completed_at": ""}
495
462
  manifest.phases[next_phase]["status"] = "in_progress"
496
463
  manifest.phases[next_phase]["started_at"] = now
497
464
 
@@ -705,7 +672,7 @@ class MigrationPipeline:
705
672
  if status == "completed":
706
673
  current_phase = phase
707
674
  completed_phases.append(phase)
708
- overall_status = "completed"
675
+ overall_status = "in_progress" # partial completion
709
676
 
710
677
  # Feature stats
711
678
  features_total = 0
@@ -754,19 +721,47 @@ class MigrationPipeline:
754
721
  except (FileNotFoundError, json.JSONDecodeError):
755
722
  last_checkpoint_data = {"tag": last_tag, "step_id": "", "timestamp": ""}
756
723
 
724
+ # Check if all phases are completed
725
+ if len(completed_phases) == len(PHASE_ORDER):
726
+ overall_status = "completed"
727
+
728
+ # Seam stats
729
+ seams_data: Optional[dict[str, Any]] = None
730
+ try:
731
+ seams = self.load_seams()
732
+ seams_high = sum(1 for s in seams if getattr(s, "priority", "medium") == "high")
733
+ seams_medium = sum(1 for s in seams if getattr(s, "priority", "medium") == "medium")
734
+ seams_low = sum(1 for s in seams if getattr(s, "priority", "medium") == "low")
735
+ seams_data = {"total": len(seams), "high": seams_high, "medium": seams_medium, "low": seams_low}
736
+ except (FileNotFoundError, json.JSONDecodeError, TypeError):
737
+ pass
738
+
739
+ # Flatten source/target to strings for UI consumption
740
+ source_path = ""
741
+ target_name = ""
742
+ if isinstance(manifest.source_info, dict):
743
+ source_path = manifest.source_info.get("path", "")
744
+ elif isinstance(manifest.source_info, str):
745
+ source_path = manifest.source_info
746
+ if isinstance(manifest.target_info, dict):
747
+ target_name = manifest.target_info.get("target", "")
748
+ elif isinstance(manifest.target_info, str):
749
+ target_name = manifest.target_info
750
+
757
751
  return {
758
752
  "migration_id": self.migration_id,
759
753
  "status": overall_status,
760
754
  "current_phase": current_phase,
761
755
  "phases": manifest.phases,
762
756
  "completed_phases": completed_phases,
763
- "source": manifest.source_info,
764
- "target": manifest.target_info,
757
+ "source": source_path,
758
+ "target": target_name,
765
759
  "current_step": current_step,
766
760
  "features": {"passing": features_passing, "total": features_total},
767
761
  "steps": {"current": current_step_index, "completed": steps_completed, "total": steps_total},
768
762
  "last_checkpoint": last_checkpoint_data,
769
763
  "checkpoints_count": len(manifest.checkpoints),
764
+ "seams": seams_data,
770
765
  }
771
766
 
772
767
  def generate_plan_summary(self) -> str:
@@ -893,21 +888,34 @@ def list_migrations() -> list[dict[str, Any]]:
893
888
  # Determine overall status from phases (clean string, no parenthesized phase)
894
889
  phases = data.get("phases", {})
895
890
  status = "pending"
891
+ all_completed = True
896
892
  for phase in PHASE_ORDER:
897
893
  phase_status = phases.get(phase, {}).get("status", "pending")
894
+ if phase_status == "failed":
895
+ status = "failed"
896
+ all_completed = False
897
+ break
898
898
  if phase_status == "in_progress":
899
899
  status = "in_progress"
900
+ all_completed = False
900
901
  break
901
902
  if phase_status == "completed":
902
- status = "completed"
903
+ status = "in_progress" # partial completion
904
+ else:
905
+ all_completed = False
906
+ if all_completed:
907
+ status = "completed"
903
908
 
904
909
  source_info = data.get("source_info", {})
910
+ source_path = source_info.get("path", "") if isinstance(source_info, dict) else str(source_info)
911
+ target_info = data.get("target_info", {})
912
+ target_name = target_info.get("target", "") if isinstance(target_info, dict) else str(target_info)
905
913
  results.append({
906
914
  "id": data.get("id", entry.name),
907
915
  "created_at": data.get("created_at", ""),
908
- "source": source_info,
909
- "source_path": source_info.get("path", ""),
910
- "target": data.get("target_info", {}).get("target", ""),
916
+ "source": source_path,
917
+ "source_path": source_path,
918
+ "target": target_name,
911
919
  "status": status,
912
920
  })
913
921
  except (json.JSONDecodeError, OSError) as exc:
@@ -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:** v5.58.2
5
+ **Version:** v5.59.0
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__ = '5.58.2'
60
+ __version__ = '5.59.0'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loki-mode",
3
- "version": "5.58.2",
3
+ "version": "5.59.0",
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",