loki-mode 5.56.2 → 5.57.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 +271 -16
- package/dashboard/__init__.py +1 -1
- package/dashboard/migration_engine.py +3 -1
- package/docs/INSTALLATION.md +1 -1
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
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.
|
|
6
|
+
# Loki Mode v5.57.1
|
|
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.
|
|
266
|
+
**v5.57.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.
|
|
1
|
+
5.57.1
|
package/autonomy/loki
CHANGED
|
@@ -419,6 +419,7 @@ show_help() {
|
|
|
419
419
|
echo " doctor [--json] Check system prerequisites and skill symlinks"
|
|
420
420
|
echo " setup-skill Create skill symlinks for all providers"
|
|
421
421
|
echo " watchdog [cmd] Process health monitoring (status|help)"
|
|
422
|
+
echo " remote [PRD] Start remote session (connect from phone/browser, Claude Pro/Max)"
|
|
422
423
|
echo " version Show version"
|
|
423
424
|
echo " help Show this help"
|
|
424
425
|
echo ""
|
|
@@ -455,6 +456,8 @@ show_help() {
|
|
|
455
456
|
echo " loki issue 123 # Generate PRD from issue #123"
|
|
456
457
|
echo " loki issue 123 --start # Generate PRD and start Loki Mode"
|
|
457
458
|
echo " loki issue https://github.com/owner/repo/issues/123 # From URL"
|
|
459
|
+
echo " loki remote # Start remote session (phone/browser)"
|
|
460
|
+
echo " loki remote ./prd.md # Remote session with PRD context"
|
|
458
461
|
echo ""
|
|
459
462
|
echo "Environment Variables:"
|
|
460
463
|
echo " See: $RUN_SH (header comments)"
|
|
@@ -4851,7 +4854,7 @@ cmd_migrate_help() {
|
|
|
4851
4854
|
}
|
|
4852
4855
|
|
|
4853
4856
|
cmd_migrate_list() {
|
|
4854
|
-
python3 -c "
|
|
4857
|
+
PYTHONPATH="${SKILL_DIR:-.}" python3 -c "
|
|
4855
4858
|
import sys
|
|
4856
4859
|
sys.path.insert(0, '.')
|
|
4857
4860
|
from dashboard.migration_engine import list_migrations
|
|
@@ -4897,7 +4900,7 @@ cmd_migrate_status() {
|
|
|
4897
4900
|
return 1
|
|
4898
4901
|
fi
|
|
4899
4902
|
|
|
4900
|
-
python3 -c "
|
|
4903
|
+
PYTHONPATH="${SKILL_DIR:-.}" python3 -c "
|
|
4901
4904
|
import json, sys
|
|
4902
4905
|
sys.path.insert(0, '.')
|
|
4903
4906
|
from dashboard.migration_engine import MigrationPipeline
|
|
@@ -4952,7 +4955,7 @@ print(data.get('id', ''))
|
|
|
4952
4955
|
" "$plan_file" 2>/dev/null || echo "")
|
|
4953
4956
|
|
|
4954
4957
|
if [ -n "$migration_id_for_plan" ]; then
|
|
4955
|
-
python3 -c "
|
|
4958
|
+
PYTHONPATH="${SKILL_DIR:-.}" python3 -c "
|
|
4956
4959
|
import sys
|
|
4957
4960
|
sys.path.insert(0, '.')
|
|
4958
4961
|
from dashboard.migration_engine import MigrationPipeline
|
|
@@ -5118,7 +5121,7 @@ if manifests:
|
|
|
5118
5121
|
|
|
5119
5122
|
# Create migration via engine
|
|
5120
5123
|
local create_result
|
|
5121
|
-
create_result=$(python3 -c "
|
|
5124
|
+
create_result=$(PYTHONPATH="${SKILL_DIR:-.}" python3 -c "
|
|
5122
5125
|
import json, sys
|
|
5123
5126
|
sys.path.insert(0, '.')
|
|
5124
5127
|
from dashboard.migration_engine import MigrationPipeline
|
|
@@ -5212,7 +5215,7 @@ print(json.dumps({'id': manifest.id, 'dir': str(pipeline.migration_dir)}))
|
|
|
5212
5215
|
echo -e "${CYAN}[Phase: ${p}]${NC} Starting..."
|
|
5213
5216
|
|
|
5214
5217
|
# Start phase via engine
|
|
5215
|
-
python3 -c "
|
|
5218
|
+
PYTHONPATH="${SKILL_DIR:-.}" python3 -c "
|
|
5216
5219
|
import sys
|
|
5217
5220
|
sys.path.insert(0, '.')
|
|
5218
5221
|
from dashboard.migration_engine import MigrationPipeline
|
|
@@ -5223,31 +5226,140 @@ pipeline.start_phase(sys.argv[2])
|
|
|
5223
5226
|
return 1
|
|
5224
5227
|
}
|
|
5225
5228
|
|
|
5226
|
-
# Phase-specific logic
|
|
5229
|
+
# Phase-specific logic -- invoke Claude to do real work
|
|
5230
|
+
local phase_prompt=""
|
|
5227
5231
|
case "$p" in
|
|
5228
5232
|
understand)
|
|
5229
5233
|
echo -e " ${DIM}Analyzing codebase structure...${NC}"
|
|
5230
|
-
|
|
5231
|
-
|
|
5234
|
+
phase_prompt="You are performing the UNDERSTAND phase of a codebase migration.
|
|
5235
|
+
Target: ${target}
|
|
5236
|
+
Codebase: ${codebase_path}
|
|
5237
|
+
|
|
5238
|
+
Tasks:
|
|
5239
|
+
1. Analyze the full codebase structure (languages, frameworks, dependencies, architecture)
|
|
5240
|
+
2. Create the docs directory: mkdir -p ${migration_dir}/docs
|
|
5241
|
+
3. Write analysis documentation to ${migration_dir}/docs/analysis.md
|
|
5242
|
+
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, name, description, files (array of file paths), dependencies (array of seam ids), priority (high/medium/low)
|
|
5243
|
+
|
|
5244
|
+
You MUST create both files. The migration cannot proceed without them.
|
|
5245
|
+
Write the analysis doc first, then the seams.json."
|
|
5232
5246
|
;;
|
|
5233
5247
|
guardrail)
|
|
5234
|
-
echo -e " ${DIM}
|
|
5235
|
-
|
|
5236
|
-
|
|
5248
|
+
echo -e " ${DIM}Setting up safety nets and characterization tests...${NC}"
|
|
5249
|
+
phase_prompt="You are performing the GUARDRAIL phase of a codebase migration.
|
|
5250
|
+
Target: ${target}
|
|
5251
|
+
Codebase: ${codebase_path}
|
|
5252
|
+
Migration dir: ${migration_dir}
|
|
5253
|
+
|
|
5254
|
+
Read ${migration_dir}/docs/analysis.md and ${migration_dir}/seams.json for context.
|
|
5255
|
+
|
|
5256
|
+
Tasks:
|
|
5257
|
+
1. Identify existing tests and create characterization tests that capture current behavior
|
|
5258
|
+
2. Write ${migration_dir}/features.json as a JSON array of objects with fields: id, name, description, test_command (shell command to verify), passes (boolean, set to true for existing passing behavior)
|
|
5259
|
+
3. Create a git checkpoint: cd ${codebase_path} && git stash || true
|
|
5260
|
+
|
|
5261
|
+
All features in features.json must have passes: true for the gate to pass."
|
|
5237
5262
|
;;
|
|
5238
5263
|
migrate)
|
|
5239
|
-
echo -e " ${DIM}Executing migration transforms
|
|
5240
|
-
|
|
5264
|
+
echo -e " ${DIM}Executing migration transforms...${NC}"
|
|
5265
|
+
phase_prompt="You are performing the MIGRATE phase of a codebase migration.
|
|
5266
|
+
Target: ${target}
|
|
5267
|
+
Codebase: ${codebase_path}
|
|
5268
|
+
Migration dir: ${migration_dir}
|
|
5269
|
+
|
|
5270
|
+
Read ${migration_dir}/docs/analysis.md, ${migration_dir}/seams.json, and ${migration_dir}/features.json for context.
|
|
5271
|
+
|
|
5272
|
+
Tasks:
|
|
5273
|
+
1. Create a migration plan and write it to ${migration_dir}/migration-plan.json with fields: target, source_path, steps (array of objects with: id, name, description, status set to 'completed' after you do the step)
|
|
5274
|
+
2. Execute the actual code migration transforms in ${codebase_path} -- convert code from the current framework/language to ${target}
|
|
5275
|
+
3. Update each step status to 'completed' as you finish it
|
|
5276
|
+
4. Work incrementally seam by seam from ${migration_dir}/seams.json
|
|
5277
|
+
|
|
5278
|
+
All steps must have status: completed for the gate to pass."
|
|
5241
5279
|
;;
|
|
5242
5280
|
verify)
|
|
5243
5281
|
echo -e " ${DIM}Running verification suite...${NC}"
|
|
5244
|
-
|
|
5245
|
-
|
|
5282
|
+
phase_prompt="You are performing the VERIFY phase of a codebase migration.
|
|
5283
|
+
Target: ${target}
|
|
5284
|
+
Codebase: ${codebase_path}
|
|
5285
|
+
Migration dir: ${migration_dir}
|
|
5286
|
+
|
|
5287
|
+
Tasks:
|
|
5288
|
+
1. Run syntax validation on all migrated files
|
|
5289
|
+
2. Run any test commands from ${migration_dir}/features.json to verify behavior is preserved
|
|
5290
|
+
3. Check for common migration issues (missing imports, broken references, etc.)
|
|
5291
|
+
4. Write a verification report to ${migration_dir}/docs/verification-report.md
|
|
5292
|
+
5. Summarize what was migrated, what works, and any remaining issues"
|
|
5293
|
+
;;
|
|
5294
|
+
esac
|
|
5295
|
+
|
|
5296
|
+
# Invoke Claude to execute the phase
|
|
5297
|
+
local phase_exit=0
|
|
5298
|
+
if [ -n "$phase_prompt" ]; then
|
|
5299
|
+
local provider_name="${LOKI_PROVIDER:-claude}"
|
|
5300
|
+
case "$provider_name" in
|
|
5301
|
+
claude)
|
|
5302
|
+
(cd "$codebase_path" && claude --dangerously-skip-permissions -p "$phase_prompt" --output-format stream-json --verbose 2>&1) | \
|
|
5303
|
+
while IFS= read -r line; do
|
|
5304
|
+
# Extract text from stream-json
|
|
5305
|
+
if echo "$line" | python3 -c "
|
|
5306
|
+
import sys, json
|
|
5307
|
+
try:
|
|
5308
|
+
d = json.loads(sys.stdin.read())
|
|
5309
|
+
if d.get('type') == 'assistant':
|
|
5310
|
+
for item in d.get('message', {}).get('content', []):
|
|
5311
|
+
if item.get('type') == 'text':
|
|
5312
|
+
print(item.get('text', ''), end='')
|
|
5313
|
+
except Exception: pass
|
|
5314
|
+
" 2>/dev/null; then
|
|
5315
|
+
true
|
|
5316
|
+
fi
|
|
5317
|
+
done
|
|
5318
|
+
phase_exit=${PIPESTATUS[0]}
|
|
5319
|
+
;;
|
|
5320
|
+
codex)
|
|
5321
|
+
(cd "$codebase_path" && codex exec --full-auto "$phase_prompt" 2>&1) || phase_exit=$?
|
|
5322
|
+
;;
|
|
5323
|
+
gemini)
|
|
5324
|
+
(cd "$codebase_path" && gemini --approval-mode=yolo "$phase_prompt" 2>&1) || phase_exit=$?
|
|
5325
|
+
;;
|
|
5326
|
+
esac
|
|
5327
|
+
fi
|
|
5328
|
+
|
|
5329
|
+
# Verify phase gate artifacts exist before advancing
|
|
5330
|
+
local gate_ok=true
|
|
5331
|
+
case "$p" in
|
|
5332
|
+
understand)
|
|
5333
|
+
if [ ! -f "${migration_dir}/docs/analysis.md" ] || [ ! -f "${migration_dir}/seams.json" ]; then
|
|
5334
|
+
echo -e "${RED}Error: Phase 'understand' did not produce required artifacts${NC}"
|
|
5335
|
+
echo -e "${DIM} Expected: docs/analysis.md, seams.json${NC}"
|
|
5336
|
+
gate_ok=false
|
|
5337
|
+
fi
|
|
5338
|
+
;;
|
|
5339
|
+
guardrail)
|
|
5340
|
+
if [ ! -f "${migration_dir}/features.json" ]; then
|
|
5341
|
+
echo -e "${RED}Error: Phase 'guardrail' did not produce required artifacts${NC}"
|
|
5342
|
+
echo -e "${DIM} Expected: features.json${NC}"
|
|
5343
|
+
gate_ok=false
|
|
5344
|
+
fi
|
|
5345
|
+
;;
|
|
5346
|
+
migrate)
|
|
5347
|
+
if [ ! -f "${migration_dir}/migration-plan.json" ]; then
|
|
5348
|
+
echo -e "${RED}Error: Phase 'migrate' did not produce required artifacts${NC}"
|
|
5349
|
+
echo -e "${DIM} Expected: migration-plan.json${NC}"
|
|
5350
|
+
gate_ok=false
|
|
5351
|
+
fi
|
|
5246
5352
|
;;
|
|
5247
5353
|
esac
|
|
5248
5354
|
|
|
5355
|
+
if [ "$gate_ok" != "true" ]; then
|
|
5356
|
+
echo -e "${YELLOW}Phase '$p' artifacts missing. Migration halted.${NC}"
|
|
5357
|
+
echo -e "${DIM}Re-run with: loki migrate <path> --target ${target} --phase ${p} --resume${NC}"
|
|
5358
|
+
return 1
|
|
5359
|
+
fi
|
|
5360
|
+
|
|
5249
5361
|
# Complete phase via engine
|
|
5250
|
-
python3 -c "
|
|
5362
|
+
PYTHONPATH="${SKILL_DIR:-.}" python3 -c "
|
|
5251
5363
|
import sys
|
|
5252
5364
|
sys.path.insert(0, '.')
|
|
5253
5365
|
from dashboard.migration_engine import MigrationPipeline
|
|
@@ -5627,6 +5739,9 @@ main() {
|
|
|
5627
5739
|
syslog)
|
|
5628
5740
|
cmd_syslog "$@"
|
|
5629
5741
|
;;
|
|
5742
|
+
remote|rc)
|
|
5743
|
+
cmd_remote "$@"
|
|
5744
|
+
;;
|
|
5630
5745
|
version|--version|-v)
|
|
5631
5746
|
cmd_version
|
|
5632
5747
|
;;
|
|
@@ -8483,6 +8598,146 @@ for line in sys.stdin:
|
|
|
8483
8598
|
esac
|
|
8484
8599
|
}
|
|
8485
8600
|
|
|
8601
|
+
# Remote Control - start a remote-controllable Claude session
|
|
8602
|
+
cmd_remote() {
|
|
8603
|
+
local rc_flags=()
|
|
8604
|
+
local prd_file=""
|
|
8605
|
+
|
|
8606
|
+
while [[ $# -gt 0 ]]; do
|
|
8607
|
+
case "$1" in
|
|
8608
|
+
--help|-h)
|
|
8609
|
+
echo -e "${BOLD}loki remote${NC} - Start a remote-controllable Claude Code session"
|
|
8610
|
+
echo ""
|
|
8611
|
+
echo "Usage: loki remote [PRD] [options]"
|
|
8612
|
+
echo ""
|
|
8613
|
+
echo "Starts a Claude Code Remote Control session with Loki Mode skill"
|
|
8614
|
+
echo "pre-loaded. Connect from your phone, tablet, or any browser via"
|
|
8615
|
+
echo "claude.ai/code or the Claude mobile app."
|
|
8616
|
+
echo ""
|
|
8617
|
+
echo "Arguments:"
|
|
8618
|
+
echo " PRD Path to PRD file (optional)"
|
|
8619
|
+
echo ""
|
|
8620
|
+
echo "Options:"
|
|
8621
|
+
echo " --verbose Show detailed connection and session logs"
|
|
8622
|
+
echo " --sandbox Enable sandboxing for filesystem/network isolation"
|
|
8623
|
+
echo " --no-sandbox Disable sandboxing (default)"
|
|
8624
|
+
echo ""
|
|
8625
|
+
echo "Requirements:"
|
|
8626
|
+
echo " - Claude Code CLI installed"
|
|
8627
|
+
echo " - Anthropic Pro or Max plan (API keys not supported)"
|
|
8628
|
+
echo " - Signed in via 'claude' then '/login'"
|
|
8629
|
+
echo ""
|
|
8630
|
+
echo "Examples:"
|
|
8631
|
+
echo " loki remote # Start remote session"
|
|
8632
|
+
echo " loki remote ./prd.md # Remote session with PRD context"
|
|
8633
|
+
echo " loki remote --verbose # Verbose connection logging"
|
|
8634
|
+
echo ""
|
|
8635
|
+
echo "Once connected from your device, say:"
|
|
8636
|
+
echo " \"Loki Mode\" # Start autonomous mode"
|
|
8637
|
+
echo " \"Loki Mode with PRD at path\" # Start with specific PRD"
|
|
8638
|
+
exit 0
|
|
8639
|
+
;;
|
|
8640
|
+
--verbose)
|
|
8641
|
+
rc_flags+=("--verbose")
|
|
8642
|
+
shift
|
|
8643
|
+
;;
|
|
8644
|
+
--sandbox)
|
|
8645
|
+
rc_flags+=("--sandbox")
|
|
8646
|
+
shift
|
|
8647
|
+
;;
|
|
8648
|
+
--no-sandbox)
|
|
8649
|
+
rc_flags+=("--no-sandbox")
|
|
8650
|
+
shift
|
|
8651
|
+
;;
|
|
8652
|
+
--provider|--provider=*)
|
|
8653
|
+
echo -e "${RED}Error: Remote Control only supports the Claude provider${NC}"
|
|
8654
|
+
echo "The --provider flag is not available for 'loki remote'."
|
|
8655
|
+
exit 1
|
|
8656
|
+
;;
|
|
8657
|
+
-*)
|
|
8658
|
+
echo -e "${RED}Unknown option: $1${NC}"
|
|
8659
|
+
echo "Run 'loki remote --help' for usage."
|
|
8660
|
+
exit 1
|
|
8661
|
+
;;
|
|
8662
|
+
*)
|
|
8663
|
+
if [ -z "$prd_file" ]; then
|
|
8664
|
+
prd_file="$1"
|
|
8665
|
+
else
|
|
8666
|
+
echo -e "${RED}Error: Unexpected argument: $1${NC}"
|
|
8667
|
+
echo "Run 'loki remote --help' for usage."
|
|
8668
|
+
exit 1
|
|
8669
|
+
fi
|
|
8670
|
+
shift
|
|
8671
|
+
;;
|
|
8672
|
+
esac
|
|
8673
|
+
done
|
|
8674
|
+
|
|
8675
|
+
# Validate Claude CLI is available
|
|
8676
|
+
if ! command -v claude >/dev/null 2>&1; then
|
|
8677
|
+
echo -e "${RED}Error: Claude Code CLI not found${NC}"
|
|
8678
|
+
echo "Install: https://docs.anthropic.com/en/docs/claude-code"
|
|
8679
|
+
exit 1
|
|
8680
|
+
fi
|
|
8681
|
+
|
|
8682
|
+
# Remote control is Claude-only
|
|
8683
|
+
local provider="${LOKI_PROVIDER:-claude}"
|
|
8684
|
+
if [ "$provider" != "claude" ]; then
|
|
8685
|
+
echo -e "${RED}Error: Remote Control is only available with Claude provider${NC}"
|
|
8686
|
+
echo "Current provider: $provider"
|
|
8687
|
+
echo "Remote Control requires Claude Code with a Pro or Max plan."
|
|
8688
|
+
exit 1
|
|
8689
|
+
fi
|
|
8690
|
+
|
|
8691
|
+
# Ensure skill symlink exists so SKILL.md is available in the session
|
|
8692
|
+
local skill_link="$HOME/.claude/skills/loki-mode"
|
|
8693
|
+
if [ ! -f "$skill_link/SKILL.md" ] && [ -n "$SKILL_DIR" ]; then
|
|
8694
|
+
mkdir -p "$HOME/.claude/skills" 2>/dev/null || true
|
|
8695
|
+
ln -sf "$SKILL_DIR" "$skill_link" 2>/dev/null || true
|
|
8696
|
+
fi
|
|
8697
|
+
|
|
8698
|
+
# Resolve PRD to absolute path
|
|
8699
|
+
local prd_abs=""
|
|
8700
|
+
if [ -n "$prd_file" ]; then
|
|
8701
|
+
if [ ! -f "$prd_file" ]; then
|
|
8702
|
+
echo -e "${RED}Error: PRD file not found: $prd_file${NC}"
|
|
8703
|
+
exit 1
|
|
8704
|
+
fi
|
|
8705
|
+
prd_abs="$(cd "$(dirname "$prd_file")" && pwd)/$(basename "$prd_file")"
|
|
8706
|
+
fi
|
|
8707
|
+
|
|
8708
|
+
# Start dashboard in background if enabled
|
|
8709
|
+
if [ "${LOKI_DASHBOARD:-true}" = "true" ]; then
|
|
8710
|
+
cmd_api start >/dev/null 2>&1 &
|
|
8711
|
+
fi
|
|
8712
|
+
|
|
8713
|
+
local version=$(get_version)
|
|
8714
|
+
echo -e "${BOLD}Loki Mode v$version - Remote Control${NC}"
|
|
8715
|
+
echo ""
|
|
8716
|
+
echo -e "${CYAN}Starting Claude Code Remote Control session...${NC}"
|
|
8717
|
+
echo -e "${DIM}Connect from phone, tablet, or browser via claude.ai/code${NC}"
|
|
8718
|
+
echo -e "${DIM}Press spacebar in the terminal to show QR code for mobile${NC}"
|
|
8719
|
+
echo ""
|
|
8720
|
+
if [ -n "$prd_abs" ]; then
|
|
8721
|
+
echo -e "${GREEN}PRD loaded:${NC} $prd_abs"
|
|
8722
|
+
echo ""
|
|
8723
|
+
echo -e "Once connected, say:"
|
|
8724
|
+
echo -e " ${BOLD}Loki Mode with PRD at $prd_abs${NC}"
|
|
8725
|
+
else
|
|
8726
|
+
echo -e "Once connected, say:"
|
|
8727
|
+
echo -e " ${BOLD}Loki Mode${NC}"
|
|
8728
|
+
fi
|
|
8729
|
+
echo ""
|
|
8730
|
+
|
|
8731
|
+
# Track session start
|
|
8732
|
+
record_session_start
|
|
8733
|
+
|
|
8734
|
+
# Emit event
|
|
8735
|
+
emit_event session cli remote_start "prd=${prd_abs:-none}"
|
|
8736
|
+
|
|
8737
|
+
# Hand off to claude remote-control
|
|
8738
|
+
exec claude remote-control ${rc_flags[@]+"${rc_flags[@]}"}
|
|
8739
|
+
}
|
|
8740
|
+
|
|
8486
8741
|
# Syslog/SIEM integration management
|
|
8487
8742
|
cmd_syslog() {
|
|
8488
8743
|
local subcommand="${1:-help}"
|
package/dashboard/__init__.py
CHANGED
|
@@ -307,12 +307,14 @@ class MigrationPipeline:
|
|
|
307
307
|
return phase_data.get("status", "pending")
|
|
308
308
|
|
|
309
309
|
def start_phase(self, phase: str) -> None:
|
|
310
|
-
"""Start a phase (transition from pending to in_progress)."""
|
|
310
|
+
"""Start a phase (transition from pending to in_progress). Idempotent if already in_progress."""
|
|
311
311
|
if phase not in PHASE_ORDER:
|
|
312
312
|
raise ValueError(f"Unknown phase: {phase}")
|
|
313
313
|
with self._lock:
|
|
314
314
|
manifest = self._load_manifest_unlocked()
|
|
315
315
|
current_status = manifest.phases[phase]["status"]
|
|
316
|
+
if current_status == "in_progress":
|
|
317
|
+
return # Already started, idempotent
|
|
316
318
|
if current_status != "pending":
|
|
317
319
|
raise RuntimeError(
|
|
318
320
|
f"Cannot start phase '{phase}': status is '{current_status}', expected 'pending'"
|
package/docs/INSTALLATION.md
CHANGED
package/mcp/__init__.py
CHANGED