kenaz 0.3.22 → 0.3.24
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/core/.claude/hooks/_user-scope.js +1 -1
- package/core/.claude/hooks/core-resolve.js +1 -1
- package/core/.claude/hooks/worktree-create.js +11 -29
- package/core/.claude/hooks/worktree-remove.js +12 -38
- package/core/.collaboration/core/reconcile.js +16 -1
- package/core/.collaboration/core/task-2467-lifecycle.test.js +63 -0
- package/core/.collaboration/core/task-api.js +37 -7
- package/core/.formation/bifrost/launch.js +1 -1
- package/core/.formation/tools/dispatch-detail/worktree-subagent-mode.md +6 -1
- package/core/.formation/tools/kenaz-stats.js +1 -1
- package/core/.formation/tools/post-complete/git-merge.js +1 -1
- package/core/.formation/tools/prompt.js +1 -1
- package/core/.formation/tools/utils.js +5 -5
- package/core/.formation/tools/worktree-reclaim.js +73 -2
- package/core/.formation/tools/worktree-reclaim.test.js +113 -0
- package/core/.kenaz/specialist/ansuz_rules.yaml +7 -51
- package/core/.kenaz/specialist/ansuz_session_handoff.schema.json +2 -6
- package/core/.rule/general/project-self-containment.yaml +2 -2
- package/core/.specialist/Amelia/README.md +1 -1
- package/core/.specialist/Amelia/rule.yaml +1 -1
- package/core/.specialist/Ansuz/RULES.md +6 -2
- package/core/.specialist/Ansuz/concepts/commitment_layer.yaml +1 -1
- package/core/.specialist/Ansuz/concepts/evolvable_guardrails.yaml +1 -1
- package/core/.specialist/Ansuz/knowledge.yaml +1 -3
- package/core/.specialist/tools/__tests__/task-2409-lessons.test.js +10 -0
- package/core/.specialist/tools/__tests__/task-2427-harvest.test.js +25 -0
- package/core/.specialist/tools/ansuz-boot-fast.js +1 -1
- package/core/.specialist/tools/failure-detector.js +4 -4
- package/core/.specialist/tools/handoff-doc.js +1 -1
- package/core/.specialist/tools/lessons.js +2 -2
- package/core/CLAUDE.md +5 -5
- package/core/KENAZ_CORE_VERSION +1 -1
- package/core/dashboard/scripts/install-kenaz-cli.js +56 -30
- package/core/dev/scripts/check-file-size.js +1 -1
- package/core/dev/scripts/ci-feedback.js +1 -1
- package/core/dev/scripts/gc-agent-worktrees.js +13 -12
- package/core/dev/scripts/kenaz-session.js +3 -3
- package/core/dev/scripts/peer-identity-e2e.js +6 -4
- package/core/dev/scripts/run-rust-tests.js +2 -2
- package/core/dev/scripts/skill-routing-replay.js +3 -3
- package/core/dev/scripts/task1961/selftest.js +2 -2
- package/core/dev/scripts/windows-hide-guard.test.js +112 -0
- package/core/plugins/kenaz/guidance.md +1 -0
- package/core/plugins/kenaz/hooks/core-resolve.js +1 -1
- package/core/plugins/kenaz/hooks/hooks.json +9 -0
- package/core/plugins/kenaz/hooks/kenaz-hook.js +58 -4
- package/core/plugins/kenaz/hooks/worktree-guard.js +121 -0
- package/core/plugins/kenaz/hooks/worktree-guard.test.js +75 -0
- package/core/plugins/kenaz/scripts/kenaz.test.js +72 -5
- package/core/plugins/kenaz-peer/hooks/register.test.ts +14 -2
- package/core/plugins/kenaz-peer/hooks/register.ts +6 -1
- package/lib/cli.js +9 -5
- package/package.json +6 -6
- package/vendor/install-kenaz-cli.js +56 -30
- package/core/.kenaz/designs/METAPLASTICITY_CONFIDENCE_DESIGN.md +0 -537
- package/core/.kenaz/designs/REFLEX_INITIATOR_DESIGN.md +0 -501
- package/core/.kenaz/designs/TASK_1385_handoff_mcp_design.md +0 -426
- package/core/.kenaz/designs/TASK_932_hippocampus_consolidation.md +0 -103
- package/core/.kenaz/designs/TASK_934_efe_signal_engine.md +0 -97
- package/core/.kenaz/designs/TASK_935_self_evolution_framework.md +0 -154
- package/core/.kenaz/designs/VISION_cognitive_architecture_roadmap.md +0 -159
- package/core/.kenaz/designs/metaplasticity_simulation_results.json +0 -696
- package/core/.kenaz/main-memory/README.md +0 -230
- package/core/.kenaz/main-memory/kenaz/cortex/001_cortex_overview.md +0 -168
- package/core/.kenaz/main-memory/kenaz/cortex/001_rag_engine_overview.md +0 -30
- package/core/.kenaz/main-memory/kenaz/memory-system/001_three_tier_architecture.md +0 -361
- package/core/.kenaz/main-memory/kenaz/pipeline/001_task_execution_flow.md +0 -117
- package/core/.kenaz/main-memory/kenaz/pipeline/001_task_granularity.md +0 -154
- package/core/.kenaz/main-memory/kenaz/pipeline/002_team_priority.md +0 -51
- package/core/.kenaz/main-memory/kenaz/pipeline/003_feature_id_system.md +0 -46
- package/core/.kenaz/main-memory/kenaz/rules/001_gen_007_self_containment.md +0 -108
- package/core/.kenaz/main-memory/kenaz/rules/001_rule_priority_system.md +0 -121
- package/core/.kenaz/main-memory/kenaz/rules/002_agt_006_pre_check.md +0 -0
- package/core/.kenaz/main-memory/kenaz/rules/003_ppl_002_task_archival.md +0 -48
- package/core/.kenaz/main-memory/kenaz/system/001_dual_track_task_system.md +0 -197
- package/core/.kenaz/main-memory/knowledge/patterns/001_json_bom_issue.md +0 -78
- package/core/.kenaz/main-memory/knowledge/patterns/001_leader_worker_pattern.md +0 -0
- package/core/.kenaz/main-memory/knowledge/patterns/002_scope_path_conflict.md +0 -115
- package/core/.kenaz/main-memory/knowledge/patterns/002_task_dependency_resolution.md +0 -223
- package/core/.kenaz/main-memory/knowledge/patterns/003_question_protocol.md +0 -242
- package/core/.kenaz/main-memory/knowledge/patterns/004_user_insight_value.md +0 -111
- package/core/.kenaz/main-memory/knowledge/patterns/005_file_lock_pattern.md +0 -274
- package/core/.kenaz/main-memory/knowledge/references/001_ppl_rules.md +0 -28
- package/core/.kenaz/main-memory/knowledge/solutions/001_json_bom_issue.md +0 -137
- package/core/.kenaz/main-memory/knowledge/solutions/001_team_invocation.md +0 -0
- package/core/.kenaz/main-memory/knowledge/solutions/002_passive_monitoring.md +0 -0
- package/core/.kenaz/main-memory/knowledge/solutions/003_windows_file_race_condition.md +0 -239
- package/core/.kenaz/main-memory/registry.json +0 -326
- package/core/.kenaz/main-memory/version.yaml +0 -58
- package/core/.kenaz/specialist/memory_guard_fired.json +0 -1
- package/core/.kenaz/specialist/memory_guard_trigger_cache.json +0 -1
- package/core/.kenaz/specialist/memory_source_priority.yaml +0 -260
- package/core/.kenaz/specialist/prompt_1775.txt +0 -317
- package/core/.kenaz/specialist/prompt_1776.txt +0 -326
- package/core/.kenaz/specialist/prompt_1791.txt +0 -320
- package/core/.kenaz/specialist/prompt_1792.txt +0 -320
- package/core/.kenaz/specialist/prompt_1795.txt +0 -281
- package/core/.kenaz/specialist/prompt_1813.txt +0 -332
- package/core/.kenaz/specialist/prompt_1814.txt +0 -401
- package/core/.kenaz/specialist/prompt_1815.txt +0 -394
- package/core/.kenaz/specialist/prompt_1816.txt +0 -437
- package/core/.kenaz/specialist/prompt_1818.txt +0 -361
- package/core/.kenaz/specialist/prompt_1819.txt +0 -421
- package/core/.kenaz/specialist/prompt_1821.txt +0 -345
- package/core/.kenaz/specialist/prompt_1822.txt +0 -313
- package/core/.kenaz/specialist/prompt_1824.txt +0 -348
- package/core/.kenaz/specialist/prompt_1826.txt +0 -388
- package/core/.kenaz/specialist/rune_efe_salience_gap.md +0 -21
- package/core/.kenaz/specialist/task1825_update.json +0 -43
- package/core/.kenaz/specialist/taskgen/consolidation_i.json +0 -38
- package/core/.kenaz/specialist/taskgen/csharp_a.json +0 -44
- package/core/.kenaz/specialist/taskgen/csharp_b.json +0 -40
- package/core/.kenaz/specialist/taskgen/embedding_e.json +0 -34
- package/core/.kenaz/specialist/taskgen/hygiene_fg.json +0 -39
- package/core/.kenaz/specialist/taskgen/pathology_h.json +0 -36
- package/core/.kenaz/specialist/taskgen/unity_noise_d.json +0 -38
- package/core/.kenaz/specialist/taskgen/visibility_c.json +0 -38
- package/core/.specialist/Ansuz/decisions/ADR-requirement-graph-layer.md +0 -390
- package/core/.specialist/Ansuz/self_model_seed.backup.json +0 -100
- package/core/.specialist/ansuz_session_handoff.json +0 -96
- package/core/dev/ALAYA_FIELD_FEEDBACK_DESIGN.md +0 -126
- package/core/dev/ANSUZ_SCAFFOLD_DIET_2026-09-20.md +0 -231
- package/core/dev/ANSUZ_STARTUP_INJECTION_REVIEW.md +0 -355
- package/core/dev/ANSUZ_WORKFLOW_MODERNIZATION.md +0 -143
- package/core/dev/CORTEX_META_GRAPH_ASSESSMENT.md +0 -99
- package/core/dev/FREYA_DOWNSTREAM_BUGS_2026-09-19.md +0 -44
- package/core/dev/GLOBAL_MEMORY_IDENTITY_CONSOLIDATION.md +0 -263
- package/core/dev/GUARD_EFFECTIVENESS_DESIGN.md +0 -180
- package/core/dev/HEALTH_GATE_VARIANCE_ASSESSMENT.md +0 -271
- package/core/dev/HOOK_DIET_2026-09-21.md +0 -126
- package/core/dev/JEV_INTEGRATION_PLAN.md +0 -189
- package/core/dev/KENAZ_AGENT_CONNECT_v1_SPEC.md +0 -139
- package/core/dev/MOBILE_DESIGN_SPEC.md +0 -569
- package/core/dev/MULTI_ANSUZ_SPEC.md +0 -136
- package/core/dev/MUNINN_RETIREMENT.md +0 -115
- package/core/dev/PHASE3_ATTRIBUTION_POPULATION_DIAGNOSIS.md +0 -211
- package/core/dev/PHASE3_P3-2B_TASKLEVEL_PROBE.md +0 -83
- package/core/dev/PHASE3_P3-2_POPULATION_DISJOINTNESS.md +0 -83
- package/core/dev/PHASE3_P3-2_REMEASURE_REPORT.md +0 -231
- package/core/dev/PREDICTION_GROUNDING_DESIGN.md +0 -376
- package/core/dev/PRODUCT_SPEC.md +0 -317
- package/core/dev/PROJECT_REGISTRY_DUPLICATE_ASSESSMENT.md +0 -86
- package/core/dev/STARTUP_TIMELINE_TASK_1995.md +0 -181
- package/core/dev/SYNAPSE_WORKER_PER_PROJECT.md +0 -881
- package/core/dev/TASK_1680_P3-3_DISPATCH_SPEC.md +0 -96
- package/core/dev/TASK_1684_CALIBRATION_IMPACT_ANALYSIS.md +0 -170
- package/core/dev/TASK_1684_FORK_A_GOODHART_DESIGN.md +0 -171
- package/core/dev/TASK_1684_P3_2_REMEASURE_VALIDATION.md +0 -262
- package/core/dev/TASK_1903_SIGNAL_AUC_MEASUREMENT.md +0 -207
- package/core/dev/TASK_1904_BRANCH_INTEGRITY_AUDIT.md +0 -146
- package/core/dev/ansuz-eval/README.md +0 -38
- package/core/dev/ansuz-eval/build-fixtures.js +0 -87
- package/core/dev/ansuz-eval/grade.js +0 -96
- package/core/dev/ansuz-eval/grader.test.js +0 -117
- package/core/dev/ansuz-eval/tasks.md +0 -32
- package/core/dev/archive/ansuz/BOOT_FULL.md +0 -141
- package/core/dev/archive/ansuz/agents/ansuz-boot.md +0 -176
- package/core/dev/archive/ansuz/designs/alaya-passive-surfacing.md +0 -84
- package/core/dev/archive/ansuz/designs/cc_memory_indexing.md +0 -307
- package/core/dev/archive/ansuz/designs/check_guard.md +0 -322
- package/core/dev/archive/ansuz/designs/consequence_loop.md +0 -227
- package/core/dev/archive/ansuz/designs/mcp_kuzudb_lock_architecture.md +0 -312
- package/core/dev/archive/ansuz/designs/phase0_git_enrichment.md +0 -278
- package/core/dev/archive/ansuz/designs/refactor_mcp_tools_synapse.md +0 -490
- package/core/dev/archive/ansuz/designs/semantic_markdown_extraction.md +0 -167
- package/core/dev/archive/ansuz/designs/task_1372_prompt_injects_into.md +0 -193
- package/core/dev/archive/ansuz/designs/thought_stack.md +0 -286
- package/core/dev/archive/ansuz/investigations/reachability_unified_api_2026-04-11.md +0 -330
- package/core/dev/archive/ansuz/investigations/synapse_orphans_indexing_bug_2026-04-11.md +0 -352
- package/core/dev/archive/ansuz/investigations/synapse_orphans_reindex_verification_2026-04-11.md +0 -48
- package/core/dev/archive/ansuz/investigations/synapse_orphans_verification_2026-04-11.md +0 -170
- package/core/dev/archive/ansuz/legacy/kernel.yaml +0 -399
- package/core/dev/archive/ansuz/legacy/phases/phase_0.yaml +0 -418
- package/core/dev/archive/ansuz/legacy/phases/phase_1.yaml +0 -376
- package/core/dev/archive/ansuz/legacy/phases/phase_10.yaml +0 -35
- package/core/dev/archive/ansuz/legacy/phases/phase_11.yaml +0 -582
- package/core/dev/archive/ansuz/legacy/phases/phase_2.yaml +0 -94
- package/core/dev/archive/ansuz/legacy/phases/phase_3.yaml +0 -274
- package/core/dev/archive/ansuz/legacy/phases/phase_4.yaml +0 -67
- package/core/dev/archive/ansuz/legacy/phases/phase_5.yaml +0 -32
- package/core/dev/archive/ansuz/legacy/phases/phase_6.yaml +0 -91
- package/core/dev/archive/ansuz/legacy/phases/phase_7.yaml +0 -58
- package/core/dev/archive/ansuz/legacy/phases/phase_8.yaml +0 -146
- package/core/dev/archive/ansuz/legacy/phases/phase_9.yaml +0 -424
- package/core/dev/archive/ansuz/specs/INDEX_bifrost_hook_parity.yaml +0 -92
- package/core/dev/archive/ansuz/specs/active-inference-formation.yaml +0 -453
- package/core/dev/archive/ansuz/specs/brief_TASK_1228_userpromptsubmit.yaml +0 -191
- package/core/dev/archive/ansuz/specs/brief_TASK_1229_pretooluse_posttooluse.yaml +0 -250
- package/core/dev/archive/ansuz/specs/brief_TASK_1230_sessionstart_stop.yaml +0 -301
- package/core/dev/archive/ansuz/specs/brief_TASK_1231_verification.yaml +0 -272
- package/core/dev/archive/skills/feature_agent/SKILL.md +0 -289
- package/core/dev/design/PHASE4_DESIGN.md +0 -559
- package/core/dev/design/dashboard_docs_integration_PLAN.md +0 -388
- package/core/dev/design/matrix_format_ABBREVIATIONS.md +0 -655
- package/core/dev/design/matrix_format_DESIGN_SPEC.md +0 -601
- package/core/dev/design/matrix_format_DISCUSSION_SUMMARY.md +0 -484
- package/core/dev/design/matrix_format_EXAMPLE_memory.matrix +0 -13
- package/core/dev/design/matrix_format_EXAMPLE_project_status.matrix +0 -9
- package/core/dev/design/matrix_format_EXAMPLE_tasks.matrix +0 -14
- package/core/dev/design/matrix_format_SYNTAX_BNF.md +0 -754
- package/core/dev/design/matrix_format_TOKEN_COMPARISON.md +0 -484
- package/core/dev/design/matrix_v2_COGNITIVE_DESIGN.md +0 -503
- package/core/dev/design/matrix_v2_DISCUSSION_SUMMARY.md +0 -414
- package/core/dev/design/matrix_v2_EXAMPLE.matrix +0 -67
- package/core/dev/designs/EXPANDABLE_CARD_DESIGN_SPEC.md +0 -509
- package/core/dev/jev-eval/ACTIVE_CORRECTION.md +0 -52
- package/core/dev/jev-eval/ANNOTATION_GUIDE.md +0 -68
- package/core/dev/jev-eval/BASELINE_COMPARISON.md +0 -45
- package/core/dev/jev-eval/CONTEXT_REVIEW.md +0 -31
- package/core/dev/jev-eval/EVALUATION_STATUS.md +0 -39
- package/core/dev/jev-eval/HISTORICAL_CORPUS.md +0 -33
- package/core/dev/jev-eval/PACKAGING_VERIFICATION.md +0 -42
- package/core/dev/jev-eval/README.md +0 -101
- package/core/dev/jev-eval/THALAMUS_RANKING.md +0 -58
- package/core/dev/jev-eval/VALIDATION_PROGRESS.md +0 -54
- package/core/dev/jev-eval/cli.mjs +0 -50
- package/core/dev/jev-eval/context.mjs +0 -183
- package/core/dev/jev-eval/corpus.mjs +0 -186
- package/core/dev/jev-eval/fixtures/conversation-review.seed.jsonl +0 -6
- package/core/dev/jev-eval/fixtures/synthetic.jsonl +0 -11
- package/core/dev/jev-eval/metrics.mjs +0 -67
- package/core/dev/jev-eval/muninn-baseline.mjs +0 -119
- package/core/dev/jev-eval/providers.mjs +0 -95
- package/core/dev/jev-eval/reports/annotation-pilot-2026-09-20.json +0 -32
- package/core/dev/jev-eval/reports/context-review-2026-09-20.json +0 -48
- package/core/dev/jev-eval/reports/conversation-review.pending.json +0 -151
- package/core/dev/jev-eval/reports/history-corpus-2026-09-20.json +0 -52
- package/core/dev/jev-eval/reports/length-probe-2026-09-20.json +0 -133
- package/core/dev/jev-eval/reports/live-smoke-2026-09-20.json +0 -616
- package/core/dev/jev-eval/reports/muninn-baseline-2026-09-20.json +0 -52
- package/core/dev/jev-eval/reports/resilience-2026-09-20.json +0 -520
- package/core/dev/jev-eval/reports/rules-baseline.json +0 -197
- package/core/dev/jev-eval/reports/synthetic-review.pending.json +0 -357
- package/core/dev/jev-eval/resilience.mjs +0 -71
- package/core/dev/jev-eval/review.mjs +0 -78
- package/core/dev/jev-eval/schema.mjs +0 -87
- package/core/dev/jev-eval/test/context.test.mjs +0 -95
- package/core/dev/jev-eval/test/corpus.test.mjs +0 -102
- package/core/dev/jev-eval/test/eval.test.mjs +0 -146
- package/core/dev/jev-eval/test/muninn-baseline.test.mjs +0 -71
- package/core/dev/jev-eval/test/resilience.test.mjs +0 -72
- package/core/dev/jev-eval/test/review.test.mjs +0 -61
- package/core/dev/pipeline/PHASE3_PLANNING_SUMMARY.md +0 -338
- package/core/dev/pipeline/PROGRESS.md +0 -113
- package/core/dev/pipeline/PROGRESS_template.md +0 -860
- package/core/dev/pipeline/QA_REPORT_F008.md +0 -431
- package/core/dev/pipeline/QA_REPORT_LOCAL_CLAUDE_DIAGNOSIS.md +0 -583
- package/core/dev/pipeline/README.md +0 -210
- package/core/dev/pipeline/WORKFLOW_GUIDE.md +0 -824
- package/core/dev/pipeline/archived/TASK_015_tauri_init.yaml +0 -53
- package/core/dev/pipeline/archived/TASK_016_tauri_commands.yaml +0 -54
- package/core/dev/pipeline/archived/TASK_017_rust_pty.yaml +0 -54
- package/core/dev/pipeline/archived/TASK_018_tauri_bundle.yaml +0 -53
- package/core/dev/pipeline/archived/TASK_019_release_workflow.yaml +0 -56
- package/core/dev/pipeline/archived/TASK_020_update_ui.yaml +0 -54
- package/core/dev/pipeline/archived/TASK_021_update_notification_ui.yaml +0 -55
- package/core/dev/pipeline/archived/TASK_022_remove_node_pty.yaml +0 -65
- package/core/dev/pipeline/archived/TASK_023_api_routes_migration.yaml +0 -86
- package/core/dev/pipeline/archived/TASK_024_feature_id_system.yaml +0 -51
- package/core/dev/pipeline/archived/TASK_025_executable_acceptance.yaml +0 -66
- package/core/dev/pipeline/archived/TASK_026_spec_versioning.yaml +0 -56
- package/core/dev/pipeline/archived/TASK_027_drift_detection.yaml +0 -60
- package/core/dev/pipeline/archived/TASK_028_tauri_pty_manager.yaml +0 -60
- package/core/dev/pipeline/archived/TASK_029_tauri_pty_commands.yaml +0 -62
- package/core/dev/pipeline/archived/TASK_030_frontend_session_adapter.yaml +0 -54
- package/core/dev/pipeline/archived/TASK_031_terminal_tauri_events.yaml +0 -56
- package/core/dev/pipeline/archived/TASK_041_scheduler_data_models.yaml +0 -54
- package/core/dev/pipeline/archived/TASK_042_path_lock_manager.yaml +0 -53
- package/core/dev/pipeline/archived/TASK_043_process_manager.yaml +0 -57
- package/core/dev/pipeline/archived/TASK_044_git_integration.yaml +0 -85
- package/core/dev/pipeline/archived/TASK_045_task_queue_manager.yaml +0 -54
- package/core/dev/pipeline/archived/TASK_046_scheduler_dispatcher.yaml +0 -60
- package/core/dev/pipeline/archived/TASK_047_tauri_scheduler_commands.yaml +0 -55
- package/core/dev/pipeline/archived/TASK_048_realtime_status_push.yaml +0 -92
- package/core/dev/pipeline/archived/TASK_049_frontend_scheduler_integration.yaml +0 -58
- package/core/dev/pipeline/archived/TASK_050_error_recovery.yaml +0 -56
- package/core/dev/pipeline/archived/TASK_051_optimization_proposals_backend.yaml +0 -53
- package/core/dev/pipeline/archived/TASK_052_optimization_proposals_ui.yaml +0 -58
- package/core/dev/pipeline/archived/TASK_053_optimization_proposals_diff_view.yaml +0 -54
- package/core/dev/pipeline/archived/TASK_054_multi_project_scheduler_state.yaml +0 -76
- package/core/dev/pipeline/archived/TASK_055_scheduler_api_project_scope.yaml +0 -64
- package/core/dev/pipeline/archived/TASK_056_frontend_scheduler_project_scope.yaml +0 -62
- package/core/dev/pipeline/archived/TASK_057_scheduler_module_structure.yaml +0 -54
- package/core/dev/pipeline/archived/TASK_058_scheduler_models_extract.yaml +0 -51
- package/core/dev/pipeline/archived/TASK_059_scheduler_core_extract.yaml +0 -51
- package/core/dev/pipeline/archived/TASK_060_scheduler_commands_extract.yaml +0 -51
- package/core/dev/pipeline/archived/TASK_061_scheduler_specialized_commands.yaml +0 -53
- package/core/dev/pipeline/archived/TASK_062_process_manager_module_structure.yaml +0 -56
- package/core/dev/pipeline/archived/TASK_063_process_manager_core_extract.yaml +0 -56
- package/core/dev/pipeline/archived/TASK_064_recovery_module_structure.yaml +0 -57
- package/core/dev/pipeline/archived/TASK_065_recovery_core_extract.yaml +0 -56
- package/core/dev/pipeline/archived/TASK_066_self_enhancement_scheduler_models.yaml +0 -60
- package/core/dev/pipeline/archived/TASK_067_agents_section_quick_actions.yaml +0 -53
- package/core/dev/pipeline/archived/TASK_067_self_enhancement_scheduler_core.yaml +0 -65
- package/core/dev/pipeline/archived/TASK_068_agent_workflow_rule_enhancement.yaml +0 -46
- package/core/dev/pipeline/archived/TASK_068_self_enhancement_tauri_commands.yaml +0 -61
- package/core/dev/pipeline/archived/TASK_069_agent_wizard_responsive.yaml +0 -46
- package/core/dev/pipeline/archived/TASK_069_self_enhancement_ui.yaml +0 -62
- package/core/dev/pipeline/archived/TASK_070_preview_panel_redesign.yaml +0 -62
- package/core/dev/pipeline/archived/TASK_070_self_enhancement_integration.yaml +0 -57
- package/core/dev/pipeline/archived/TASK_071_preview_panel_qa.yaml +0 -98
- package/core/dev/pipeline/archived/TASK_071_single_agent_config.yaml +0 -57
- package/core/dev/pipeline/archived/TASK_072_preview_modal_esc_fix.yaml +0 -71
- package/core/dev/pipeline/archived/TASK_072_product_review_module.yaml +0 -57
- package/core/dev/pipeline/archived/TASK_073_agents_page_component_refactor.yaml +0 -68
- package/core/dev/pipeline/archived/TASK_073_rename_single_agent.yaml +0 -64
- package/core/dev/pipeline/archived/TASK_074_preview_mode_ui_diagnosis.yaml +0 -71
- package/core/dev/pipeline/archived/TASK_074_single_agent_ui_toggle.yaml +0 -56
- package/core/dev/pipeline/archived/TASK_075_agents_page_component_refactor.yaml +0 -69
- package/core/dev/pipeline/archived/TASK_075_single_agent_integration_test.yaml +0 -75
- package/core/dev/pipeline/archived/TASK_076_preview_mode_ui_diagnosis.yaml +0 -70
- package/core/dev/pipeline/archived/TASK_076_single_agent_continuous_mode.yaml +0 -65
- package/core/dev/pipeline/archived/TASK_077_failure_directory_structure.yaml +0 -51
- package/core/dev/pipeline/archived/TASK_077_single_agent_typescript_types.yaml +0 -59
- package/core/dev/pipeline/archived/TASK_078_fail_001_agent_no_output.yaml +0 -53
- package/core/dev/pipeline/archived/TASK_078_single_agent_ui_simplify.yaml +0 -74
- package/core/dev/pipeline/archived/TASK_079_dashboard_rwd_responsive.yaml +0 -63
- package/core/dev/pipeline/archived/TASK_079_fail_002_json_bom.yaml +0 -56
- package/core/dev/pipeline/archived/TASK_080_fail_003_queue_pollution.yaml +0 -56
- package/core/dev/pipeline/archived/TASK_080_http_server_module.yaml +0 -55
- package/core/dev/pipeline/archived/TASK_081_failure_logging_tools.yaml +0 -57
- package/core/dev/pipeline/archived/TASK_081_http_api_routes.yaml +0 -62
- package/core/dev/pipeline/archived/TASK_082_frontend_api_adapter.yaml +0 -51
- package/core/dev/pipeline/archived/TASK_082_integrate_failure_to_workflow.yaml +0 -60
- package/core/dev/pipeline/archived/TASK_083_cleanup_nextjs_api.yaml +0 -48
- package/core/dev/pipeline/archived/TASK_083_task_queue_cleanup.yaml +0 -67
- package/core/dev/pipeline/archived/TASK_084_rust_dead_code_cleanup.yaml +0 -66
- package/core/dev/pipeline/archived/TASK_084_scheduler_sync_logic_fix.yaml +0 -68
- package/core/dev/pipeline/archived/TASK_085_http_api_web_mode_fixes.yaml +0 -46
- package/core/dev/pipeline/archived/TASK_085_memory_consolidation_0204_0205.yaml +0 -98
- package/core/dev/pipeline/archived/TASK_086_collapsible_preview_panel.yaml +0 -161
- package/core/dev/pipeline/archived/TASK_086_web_mode_feature_parity.yaml +0 -62
- package/core/dev/pipeline/archived/TASK_087_manual_memory_archival.yaml +0 -163
- package/core/dev/pipeline/archived/TASK_087_mobile_http_control.yaml +0 -68
- package/core/dev/pipeline/archived/TASK_088_fail_003_to_ppl_002_rule.yaml +0 -63
- package/core/dev/pipeline/archived/TASK_088_mobile_http_qa.yaml +0 -121
- package/core/dev/pipeline/archived/TASK_089_agent_collaboration_topic.yaml +0 -60
- package/core/dev/pipeline/archived/TASK_089_web_mode_monitor_fix.yaml +0 -58
- package/core/dev/pipeline/archived/TASK_090_cli_detection_fix.yaml +0 -61
- package/core/dev/pipeline/archived/TASK_090_ui_ux_lessons_topic.yaml +0 -63
- package/core/dev/pipeline/archived/TASK_091_expandable_card_design.yaml +0 -56
- package/core/dev/pipeline/archived/TASK_091_move_agent_section_components.yaml +0 -65
- package/core/dev/pipeline/archived/TASK_092_expandable_card_component.yaml +0 -60
- package/core/dev/pipeline/archived/TASK_092_refactor_agent_create_wizard.yaml +0 -84
- package/core/dev/pipeline/archived/TASK_093_taskcard_detail_integration.yaml +0 -57
- package/core/dev/pipeline/archived/TASK_093_update_barrel_exports_test.yaml +0 -84
- package/core/dev/pipeline/archived/TASK_094_ai_agent_generation_enhancement.yaml +0 -115
- package/core/dev/pipeline/archived/TASK_094_proposalcard_detail_integration.yaml +0 -55
- package/core/dev/pipeline/archived/TASK_095_preview_fixes.yaml +0 -129
- package/core/dev/pipeline/archived/TASK_095_single_agent_task_selector.yaml +0 -60
- package/core/dev/pipeline/archived/TASK_096_agents_page_ux_improvements.yaml +0 -163
- package/core/dev/pipeline/archived/TASK_096_single_agent_status_sync.yaml +0 -60
- package/core/dev/pipeline/archived/TASK_097_local_claude_error_logging.yaml +0 -59
- package/core/dev/pipeline/archived/TASK_097_single_agent_monitor_events.yaml +0 -59
- package/core/dev/pipeline/archived/TASK_098_product_manager_team_phase2.yaml +0 -60
- package/core/dev/pipeline/archived/TASK_098_single_agent_refactor_test.yaml +0 -58
- package/core/dev/pipeline/archived/TASK_099_backend_team_creation.yaml +0 -258
- package/core/dev/pipeline/archived/TASK_100_frontend_team_creation.yaml +0 -61
- package/core/dev/pipeline/archived/TASK_101_orchestrator_system.yaml +0 -403
- package/core/dev/pipeline/archived/TASK_102_designer_sre_teams.yaml +0 -90
- package/core/dev/pipeline/archived/TASK_103_integration_testing.yaml +0 -86
- package/core/dev/pipeline/archived/TASK_128_integration_testing.yaml +0 -83
- package/core/dev/pipeline/archived/TASK_134_cortex_v2_phase3_file_watcher.yaml +0 -65
- package/core/dev/pipeline/archived/TASK_134_team_http_api.yaml +0 -96
- package/core/dev/pipeline/archived/TASK_135_cortex_v2_phase3_git_monitor.yaml +0 -63
- package/core/dev/pipeline/archived/TASK_136_cortex_v2_phase3_change_analyzer.yaml +0 -69
- package/core/dev/pipeline/archived/TASK_137_cortex_v2_phase3_cache_invalidator.yaml +0 -67
- package/core/dev/pipeline/archived/TASK_137_team_orchestrator_integration.yaml +0 -93
- package/core/dev/pipeline/archived/TASK_138_cortex_v2_phase3_distributed_cache.yaml +0 -68
- package/core/dev/pipeline/archived/TASK_138_matrix_folder_structure.yaml +0 -61
- package/core/dev/pipeline/archived/TASK_139_cortex_v2_phase3_cache_prewarming.yaml +0 -65
- package/core/dev/pipeline/archived/TASK_146_unified_documentation.yaml +0 -141
- package/core/dev/pipeline/archived/TASK_147_pipeline_execution_rules.yaml +0 -76
- package/core/dev/pipeline/archived/TASK_148_update_claude_md.yaml +0 -77
- package/core/dev/pipeline/archived/TASK_149_fix_nextjs_mdx_serialization.yaml +0 -63
- package/core/dev/pipeline/archived/TASK_149_update_team_docs.yaml +0 -77
- package/core/dev/pipeline/archived/TASK_150_pipeline_workflow_guide.yaml +0 -82
- package/core/dev/pipeline/archived/TASK_164_fix_smart_context_switch.yaml +0 -49
- package/core/dev/pipeline/archived/TASK_165_ctags_symbol_index.yaml +0 -53
- package/core/dev/pipeline/archived/TASK_166_symbol_search_mcp_tool.yaml +0 -48
- package/core/dev/pipeline/archived/TASK_167_llm_module_summary.yaml +0 -55
- package/core/dev/pipeline/archived/TASK_168_map_reduce_context.yaml +0 -51
- package/core/dev/pipeline/archived/TASK_169_v2_integration_testing.yaml +0 -52
- package/core/dev/pipeline/backups/archive_2026-02-04T22-07-22/TASK_QUEUE_backup.json +0 -95
- package/core/dev/pipeline/docs/multi-agent-coordination.md +0 -446
- package/core/dev/pipeline/docs/overview.md +0 -406
- package/core/dev/pipeline/docs/task-management.md +0 -412
- package/core/dev/pipeline/docs/workflows.md +0 -32
- package/core/dev/pipeline/orchestrator/schemas/task_queue_schema.json +0 -58
- package/core/dev/pipeline/orchestrator/schemas/task_schema.json +0 -139
- package/core/dev/pipeline/plans/PLAN_task_cli_refactor.md +0 -281
- package/core/dev/pipeline/test_results/TASK_TEST_PARALLEL_002_result.txt +0 -27
- package/core/dev/plans/ANSUZ_SLIM_PLAN.md +0 -68
- package/core/dev/plans/CLI_FIRST_INSTALL_FLOW.md +0 -120
- package/core/dev/plans/FORMATION_RETIREMENT_PLAN.md +0 -313
- package/core/dev/plans/KUZU_REPLACEMENT_PLAN.md +0 -126
- package/core/dev/scripts/CRASH_DIAGNOSIS.md +0 -47
- package/core/dev/scripts/README.md +0 -142
- package/core/dev/scripts/RUN_ONCE.bat +0 -40
- package/core/dev/scripts/START_DEV_AGENT.bat +0 -63
- package/core/dev/scripts/START_MULTI_AGENT.bat +0 -80
- package/core/dev/scripts/dev_agent_scheduler.ps1 +0 -536
- package/core/dev/scripts/init_git.ps1 +0 -396
- package/core/dev/scripts/init_git.sh +0 -337
- package/core/dev/scripts/init_project.ps1 +0 -851
- package/core/dev/scripts/init_project.sh +0 -477
- package/core/dev/scripts/multi_agent_scheduler.ps1 +0 -2021
- package/core/dev/scripts/multi_agent_scheduler.sh +0 -1344
- package/core/dev/scripts/run_once.sh +0 -47
- package/core/dev/scripts/setup_rag.ps1 +0 -304
- package/core/dev/scripts/setup_rag.sh +0 -230
- package/core/dev/scripts/start_dev_agent.sh +0 -137
- package/core/dev/scripts/start_multi_agent.sh +0 -41
- package/core/dev/scripts/sync_task_queue.ps1 +0 -505
- package/core/dev/scripts/sync_task_queue.sh +0 -392
- package/core/dev/specs/agent-connect/PROTOCOL.md +0 -177
- package/core/dev/specs/agent-connect/SDK_GUIDE.md +0 -915
- package/core/dev/specs/agent-connect/examples/minimal-site/README.md +0 -137
- package/core/dev/specs/agent-connect/examples/minimal-site/package.json +0 -12
- package/core/dev/specs/agent-connect/examples/minimal-site/public/index.html +0 -116
- package/core/dev/specs/agent-connect/examples/minimal-site/public/install-redirect.js +0 -73
- package/core/dev/specs/agent-connect/examples/minimal-site/server.js +0 -516
- package/core/dev/specs/agent-connect/examples/minimal-site/test/smoke.test.mjs +0 -263
- package/core/dev/specs/agent-connect/history/DEFECT_REPORT_kenaz-agent-hub_2026-09-09_v0.4-empty-prompt-no-first-turn.md +0 -77
- package/core/dev/specs/agent-connect/history/DEFECT_REPORT_kenaz-agent-hub_2026-09-09_v0.4-prompt-mojibake.md +0 -90
- package/core/dev/specs/agent-connect/history/FEATURE_REQUEST_kenaz-agent-hub_2026-09-07.md +0 -61
- package/core/dev/specs/agent-connect/history/FEATURE_REQUEST_kenaz-agent-hub_2026-09-08_relay-v1-conformance.md +0 -76
- package/core/dev/specs/agent-connect/history/FEATURE_REQUEST_kenaz-agent-hub_2026-09-09_v0.4-session-channel.md +0 -87
- package/core/dev/specs/agent-connect/history/FLOWPIX_REPLY_agent-hub_2026-09-08_relay-v1-answers.md +0 -68
- package/core/dev/specs/agent-connect/history/FLOWPIX_bridge_CONTRACT.md +0 -642
- package/core/dev/specs/agent-connect/history/FLOWPIX_spikes_relay_spike_relay.mjs +0 -244
- package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-07.md +0 -77
- package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-08_direction-change-v0.3.md +0 -44
- package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-08_relay-v1-answers-ack.md +0 -37
- package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-08_relay-v1-conformance.md +0 -56
- package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-09_annotations-install-coldstart.md +0 -44
- package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-09_empty-prompt-first-turn.md +0 -56
- package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-09_prompt-mojibake-fixed.md +0 -48
- package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-09_session-channel.md +0 -57
- package/core/dev/specs/agent-connect/history/PROTOCOL_v0.3_snapshot.md +0 -379
- package/core/dev/specs/agent-connect/history/README.md +0 -47
- package/core/dev/specs/peer/CODEX_INTERACTIVE_PUSH.md +0 -65
- package/core/dev/specs/peer/CODEX_MANAGED_DAEMON.md +0 -54
- package/core/dev/specs/peer/CODEX_PEER_PULL_SPIKE.md +0 -54
- package/core/dev/specs/peer/MODS_SPIKE.md +0 -133
- package/core/dev/specs/peer/PER_PROJECT_ADDRESSING.md +0 -143
- package/core/dev/specs/peer/SWARM_PROTOCOLS.md +0 -256
- package/core/dev/specs/peer/codex-hook-shim-peer.test.js +0 -113
- package/core/dev/specs/peer/codex-push-launcher.test.js +0 -142
- package/core/dev/specs/peer/setup-codex-hooks.test.js +0 -60
- package/core/dev/specs/peer-ansuz/DELIVERY_SPIKE.md +0 -397
- package/core/dev/specs/peer-ansuz/DESIGN.md +0 -109
- package/core/dev/specs/peer-ansuz/spike/claude_poke.js +0 -129
- package/core/dev/specs/peer-ansuz/spike/claude_session_start_hook.js +0 -37
- package/core/dev/specs/peer-ansuz/spike/claude_settings_hook.json +0 -14
- package/core/dev/specs/peer-ansuz/spike/codex_appserver_client.js +0 -105
- package/core/dev/specs/peer-ansuz/spike/codex_appserver_query.js +0 -29
- package/core/dev/specs/peer-ansuz/spike/extract_timeline.js +0 -26
- package/core/dev/specs/synapse/SYMBOL_IDENTITY.md +0 -305
- package/core/dev/spikes/A4-cypher-varlen.md +0 -176
- package/core/dev/spikes/bloat-rounds.sh +0 -25
- package/core/dev/spikes/mods/clean-claude.js +0 -9
- package/core/dev/spikes/mods/drive-probe.js +0 -29
- package/core/dev/spikes/mods/e2e-peer.js +0 -71
- package/core/dev/spikes/mods/evidence/e2e-peer-control.json +0 -17
- package/core/dev/spikes/mods/evidence/e2e-peer-mod.json +0 -17
- package/core/dev/spikes/mods/evidence/probe-006a6e65-d807-4bc1-b648-fd7d1edfe0b4.jsonl +0 -4
- package/core/dev/spikes/mods/evidence/probe-cebdf1f7-f9c4-47a8-83cd-7c0786177404.jsonl +0 -12
- package/core/dev/spikes/mods/evidence/probe-eb21147d-10ce-4c32-93ed-37ca37d88d5a.jsonl +0 -11
- package/core/dev/spikes/mods/evidence/stream-cebdf1f7.jsonl +0 -18
- package/core/dev/spikes/mods/kenaz-mod-probe/.claude-plugin/plugin.json +0 -1
- package/core/dev/spikes/mods/kenaz-mod-probe/hooks/hooks.json +0 -1
- package/core/dev/spikes/mods/kenaz-mod-probe/hooks/register.ts +0 -65
- package/core/dev/spikes/mods/kenaz-mod-probe/tsconfig.json +0 -3
|
@@ -19,5 +19,5 @@ if (!hookFile) process.exit(0);
|
|
|
19
19
|
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
20
20
|
if (fs.existsSync(path.join(projectDir, '.claude', 'hooks', hookFile))) process.exit(0);
|
|
21
21
|
|
|
22
|
-
const res = spawnSync(process.execPath, [path.join(__dirname, hookFile), ...args], { stdio: 'inherit' });
|
|
22
|
+
const res = spawnSync(process.execPath, [path.join(__dirname, hookFile), ...args], { windowsHide: true, stdio: 'inherit' });
|
|
23
23
|
process.exit(res.status == null ? 0 : res.status);
|
|
@@ -221,7 +221,7 @@ if (require.main === module) {
|
|
|
221
221
|
if (!result) process.exit(0); // silent -- non-blocking hook
|
|
222
222
|
|
|
223
223
|
try {
|
|
224
|
-
execFileSync("node", [result.resolved, ...result.args], { stdio: "inherit" });
|
|
224
|
+
execFileSync("node", [result.resolved, ...result.args], { stdio: "inherit", windowsHide: true });
|
|
225
225
|
} catch {
|
|
226
226
|
// Hook errors should not block
|
|
227
227
|
}
|
|
@@ -99,7 +99,7 @@ function readStdin() {
|
|
|
99
99
|
* a no-op (GEN-009). We do NOT write to any git config file here.
|
|
100
100
|
*/
|
|
101
101
|
function git(repo, args) {
|
|
102
|
-
return execFileSync('git', ['-C', repo, '-c', 'core.longpaths=true', ...args], {
|
|
102
|
+
return execFileSync('git', ['-C', repo, '-c', 'core.longpaths=true', ...args], { windowsHide: true,
|
|
103
103
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
104
104
|
}).toString().trim();
|
|
105
105
|
}
|
|
@@ -107,7 +107,7 @@ function git(repo, args) {
|
|
|
107
107
|
function isGitRepo(dir) {
|
|
108
108
|
if (!dir) return false;
|
|
109
109
|
try {
|
|
110
|
-
return execFileSync('git', ['-C', dir, 'rev-parse', '--is-inside-work-tree'], {
|
|
110
|
+
return execFileSync('git', ['-C', dir, 'rev-parse', '--is-inside-work-tree'], { windowsHide: true,
|
|
111
111
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
112
112
|
}).toString().trim() === 'true';
|
|
113
113
|
} catch { return false; }
|
|
@@ -334,7 +334,7 @@ function isLandedProgressNote(repo, relPath) {
|
|
|
334
334
|
function classifyWorktreeDirt(worktreeDir, noisePaths, repo) {
|
|
335
335
|
const raw = execFileSync('git', ['-C', worktreeDir, '-c', 'core.longpaths=true',
|
|
336
336
|
'-c', 'core.quotePath=false', 'status', '--porcelain'],
|
|
337
|
-
{ stdio: ['ignore', 'pipe', 'pipe'] }).toString();
|
|
337
|
+
{ windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] }).toString();
|
|
338
338
|
const real = [];
|
|
339
339
|
const noise = [];
|
|
340
340
|
for (const line of raw.split('\n')) {
|
|
@@ -491,7 +491,8 @@ function gcStaleWorktrees(repo, keepPath, opts) {
|
|
|
491
491
|
// Windows recurses through junctions on delete and would empty the MAIN
|
|
492
492
|
// checkout's node_modules (measured; see unlinkNodeModulesLinks). This
|
|
493
493
|
// also covers pre-TASK_1844 worktrees where agents hand-built junctions.
|
|
494
|
-
unlinkNodeModulesLinks(p);
|
|
494
|
+
const unl = unlinkNodeModulesLinks(p);
|
|
495
|
+
if (unl.failed.length) { diag(name + ' KEPT — cannot unlink: ' + unl.failed.slice(0, 3).join(', ')); continue; }
|
|
495
496
|
// Lock-resilient removal: try git remove --force, fall back to fs.rmSync
|
|
496
497
|
// with retries, then prune. If still locked -> log to stderr (observable)
|
|
497
498
|
// but never throw (GC must remain non-blocking).
|
|
@@ -635,18 +636,18 @@ function findNodeModulesParents(repo) {
|
|
|
635
636
|
function ensureWorktreeIgnores(worktreePath, relPath) {
|
|
636
637
|
try {
|
|
637
638
|
execFileSync('git', ['-C', worktreePath, 'check-ignore', '-q', '--', relPath],
|
|
638
|
-
{ stdio: ['ignore', 'ignore', 'ignore'] });
|
|
639
|
+
{ windowsHide: true, stdio: ['ignore', 'ignore', 'ignore'] });
|
|
639
640
|
return; // exit 0 → already ignored (repo .gitignore covers it)
|
|
640
641
|
} catch { /* not ignored → add per-worktree exclude */ }
|
|
641
642
|
const gp = execFileSync('git', ['-C', worktreePath, 'rev-parse', '--git-path', 'info/exclude'],
|
|
642
|
-
{ stdio: ['ignore', 'pipe', 'pipe'] }).toString().trim();
|
|
643
|
+
{ windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] }).toString().trim();
|
|
643
644
|
const excludeFile = path.isAbsolute(gp) ? gp : path.resolve(worktreePath, gp);
|
|
644
645
|
fs.mkdirSync(path.dirname(excludeFile), { recursive: true });
|
|
645
646
|
fs.appendFileSync(excludeFile, '/' + relPath + '/\n');
|
|
646
647
|
}
|
|
647
648
|
|
|
648
649
|
/**
|
|
649
|
-
* Unlink
|
|
650
|
+
* Unlink EVERY junction/symlink (any name, any depth) inside `root` (a worktree about to be
|
|
650
651
|
* removed). MUST run before any `git worktree remove`: Git for Windows RECURSES
|
|
651
652
|
* THROUGH junctions when deleting and EMPTIES THE LINK TARGET — measured
|
|
652
653
|
* 2026-08-13 (TASK_1844 control test): a junction into the source repo's
|
|
@@ -657,27 +658,8 @@ function ensureWorktreeIgnores(worktreePath, relPath) {
|
|
|
657
658
|
* left for the normal removal path. Best-effort: never throws.
|
|
658
659
|
*/
|
|
659
660
|
function unlinkNodeModulesLinks(root) {
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
663
|
-
for (const ent of entries) {
|
|
664
|
-
const p = path.join(dir, ent.name);
|
|
665
|
-
if (ent.name === 'node_modules') {
|
|
666
|
-
try {
|
|
667
|
-
if (fs.lstatSync(p).isSymbolicLink()) {
|
|
668
|
-
try { fs.unlinkSync(p); } catch { fs.rmdirSync(p); }
|
|
669
|
-
}
|
|
670
|
-
} catch { /* best-effort */ }
|
|
671
|
-
continue;
|
|
672
|
-
}
|
|
673
|
-
let isDir = false;
|
|
674
|
-
try { const st = fs.lstatSync(p); isDir = st.isDirectory() && !st.isSymbolicLink(); }
|
|
675
|
-
catch { continue; }
|
|
676
|
-
if (!isDir || NODE_MODULES_SCAN_PRUNE.has(ent.name)) continue;
|
|
677
|
-
if (depth + 1 <= NODE_MODULES_SCAN_DEPTH) walk(p, depth + 1);
|
|
678
|
-
}
|
|
679
|
-
};
|
|
680
|
-
walk(root, 0);
|
|
661
|
+
// TASK_2485: ONE implementation (lstat walk, every link, any name/depth) lives in worktree-reclaim.js.
|
|
662
|
+
return require('../../.formation/tools/worktree-reclaim.js').unlinkAllLinks(root); // {removed, failed}
|
|
681
663
|
}
|
|
682
664
|
|
|
683
665
|
/**
|
|
@@ -797,7 +779,7 @@ function main() {
|
|
|
797
779
|
const taken = (b) => {
|
|
798
780
|
try {
|
|
799
781
|
execFileSync('git', ['-C', repo, 'rev-parse', '--verify', '--quiet', 'refs/heads/' + b],
|
|
800
|
-
{ stdio: ['ignore', 'pipe', 'ignore'] });
|
|
782
|
+
{ windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] });
|
|
801
783
|
return true;
|
|
802
784
|
} catch { return false; }
|
|
803
785
|
};
|
|
@@ -25,7 +25,7 @@ function readStdin() {
|
|
|
25
25
|
|
|
26
26
|
function gitTry(repo, args) {
|
|
27
27
|
try {
|
|
28
|
-
execFileSync('git', ['-C', repo, ...args], { stdio: ['ignore', 'ignore', 'ignore'] });
|
|
28
|
+
execFileSync('git', ['-C', repo, ...args], { windowsHide: true, stdio: ['ignore', 'ignore', 'ignore'] });
|
|
29
29
|
return true;
|
|
30
30
|
} catch { return false; }
|
|
31
31
|
}
|
|
@@ -33,7 +33,7 @@ function gitTry(repo, args) {
|
|
|
33
33
|
function gitTopLevel(dir) {
|
|
34
34
|
try {
|
|
35
35
|
return execFileSync('git', ['-C', dir, 'rev-parse', '--show-toplevel'],
|
|
36
|
-
{ stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
|
|
36
|
+
{ windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
|
|
37
37
|
} catch { return null; }
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -51,47 +51,21 @@ function gitTopLevel(dir) {
|
|
|
51
51
|
// falsified by the control test above — keep this note so nobody re-litigates
|
|
52
52
|
// it in either direction without re-measuring.)
|
|
53
53
|
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
// require.main === undefined, which its entry
|
|
58
|
-
//
|
|
59
|
-
// worktree in the middle of a remove.
|
|
60
|
-
const NM_SCAN_DEPTH = 3;
|
|
61
|
-
const NM_SCAN_PRUNE = new Set([
|
|
62
|
-
'.git', 'node_modules', '.next', 'target', 'dist', 'build', 'out',
|
|
63
|
-
'vendor', 'worktrees', '.kenaz-worktrees',
|
|
64
|
-
]);
|
|
65
|
-
|
|
54
|
+
// TASK_2485: the unlink walk is shared (worktree-reclaim.js unlinkAllLinks): every
|
|
55
|
+
// symlink/junction at any depth, because git recurses through ALL of them. Do not
|
|
56
|
+
// require('./worktree-create.js') here: this hook is wired as `node -e "require(...)"`,
|
|
57
|
+
// so inside a required worktree-create.js require.main === undefined, which its entry
|
|
58
|
+
// dispatch treats as "I am the WorktreeCreate hook" and would CREATE a worktree.
|
|
66
59
|
function unlinkNodeModulesLinks(root) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
70
|
-
for (const ent of entries) {
|
|
71
|
-
const p = path.join(dir, ent.name);
|
|
72
|
-
if (ent.name === 'node_modules') {
|
|
73
|
-
try {
|
|
74
|
-
if (fs.lstatSync(p).isSymbolicLink()) {
|
|
75
|
-
try { fs.unlinkSync(p); } catch { fs.rmdirSync(p); }
|
|
76
|
-
}
|
|
77
|
-
} catch { /* best-effort */ }
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
let isDir = false;
|
|
81
|
-
try { const st = fs.lstatSync(p); isDir = st.isDirectory() && !st.isSymbolicLink(); }
|
|
82
|
-
catch { continue; }
|
|
83
|
-
if (!isDir || NM_SCAN_PRUNE.has(ent.name)) continue;
|
|
84
|
-
if (depth + 1 <= NM_SCAN_DEPTH) walk(p, depth + 1);
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
try { walk(root, 0); } catch { /* best-effort */ }
|
|
60
|
+
try { return require('../../.formation/tools/worktree-reclaim.js').unlinkAllLinks(root); }
|
|
61
|
+
catch { return { removed: [], failed: ['(unlink helper unavailable)'] }; }
|
|
88
62
|
}
|
|
89
63
|
|
|
90
64
|
/** Find the branch checked out in `worktreePath` via `git worktree list --porcelain`. */
|
|
91
65
|
function worktreeBranch(repo, worktreePath) {
|
|
92
66
|
try {
|
|
93
67
|
const out = execFileSync('git', ['-C', repo, 'worktree', 'list', '--porcelain'],
|
|
94
|
-
{ stdio: ['ignore', 'pipe', 'ignore'] }).toString();
|
|
68
|
+
{ windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] }).toString();
|
|
95
69
|
const want = path.resolve(worktreePath).replace(/\\/g, '/').toLowerCase();
|
|
96
70
|
let curPath = null;
|
|
97
71
|
for (const line of out.split(/\r?\n/)) {
|
|
@@ -132,7 +106,7 @@ function main() {
|
|
|
132
106
|
let repo = null;
|
|
133
107
|
try {
|
|
134
108
|
const common = execFileSync('git', ['-C', worktreePath, 'rev-parse', '--git-common-dir'],
|
|
135
|
-
{ stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
|
|
109
|
+
{ windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
|
|
136
110
|
if (common) {
|
|
137
111
|
const commonAbs = path.resolve(worktreePath, common); // handles relative or absolute
|
|
138
112
|
if (path.basename(commonAbs) === '.git') repo = path.dirname(commonAbs);
|
|
@@ -164,7 +138,7 @@ function main() {
|
|
|
164
138
|
// TASK_1844: MUST unlink node_modules junctions before the git removal —
|
|
165
139
|
// Git for Windows traverses junctions on delete (measured; see header
|
|
166
140
|
// comment above unlinkNodeModulesLinks). fs.rmSync below is safe as-is.
|
|
167
|
-
unlinkNodeModulesLinks(worktreePath);
|
|
141
|
+
if (unlinkNodeModulesLinks(worktreePath).failed.length) process.exit(0); // refuse: a surviving link would be traversed
|
|
168
142
|
gitTry(repo, ['worktree', 'remove', '--force', worktreePath]);
|
|
169
143
|
gitTry(repo, ['worktree', 'prune']);
|
|
170
144
|
if (branch) gitTry(repo, ['branch', '-D', branch]);
|
|
@@ -192,4 +192,19 @@ function externalLanding(repo, card, opts) {
|
|
|
192
192
|
return wts.some((w) => w.branch && names.has(String(w.branch).replace(/^refs\/heads\//, ''))) ? null : a;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
|
|
195
|
+
/**
|
|
196
|
+
* TASK_2467: validate a hand-supplied landing sha. Returns {sha, baseRef, landed_into}; throws unless <sha>
|
|
197
|
+
* resolves to a commit that is an ancestor of the base ref (origin/main, else local main).
|
|
198
|
+
*/
|
|
199
|
+
function verifyLandedSha(repo, sha, { remote = 'origin', base = 'main' } = {}) {
|
|
200
|
+
if (!/^[0-9a-fA-F]{7,64}$/.test(String(sha))) throw new Error(`--landed-sha: '${sha}' is not a commit sha`);
|
|
201
|
+
const baseRef = resolveBaseRef(repo, remote, base);
|
|
202
|
+
if (!baseRef) throw new Error(`--landed-sha: no ${remote}/${base} or ${base} ref to verify against`);
|
|
203
|
+
let full;
|
|
204
|
+
try { full = git(repo, ['rev-parse', '--verify', '--quiet', `${sha}^{commit}`]); } catch { throw new Error(`--landed-sha: commit ${sha} not found`); }
|
|
205
|
+
const into = baseRef.replace(/^refs\/(heads|remotes)\//, '');
|
|
206
|
+
if (!gitOk(repo, ['merge-base', '--is-ancestor', full, baseRef])) throw new Error(`--landed-sha: ${full.slice(0, 10)} is not an ancestor of ${into}`);
|
|
207
|
+
return { landing_sha: full, landed_into: into };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
module.exports = { verifyLandedSha, reconcile, assessCard, landedAssessment, externalLanding, countLanded };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// TASK_2467 -- update --status rejected; complete --landed-sha from any open state; list shows archived.
|
|
2
|
+
// Real CLI in a temp git project. TASK_API=<path> runs the same checks against another task-api.js (control).
|
|
3
|
+
// Usage: node .collaboration/core/task-2467-lifecycle.test.js
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const cp = require('child_process');
|
|
8
|
+
const API = process.env.TASK_API || path.join(__dirname, 'task-api.js');
|
|
9
|
+
const SB = fs.mkdtempSync(path.join(os.tmpdir(), 't2467_'));
|
|
10
|
+
const git = (...a) => cp.execFileSync('git', a, { cwd: SB, encoding: 'utf8', windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] }).trim();
|
|
11
|
+
const cli = (...a) => { const r = cp.spawnSync(process.execPath, [API, ...a, '--project-path', SB], { cwd: SB, encoding: 'utf8', windowsHide: true }); return { code: r.status, out: (r.stdout || '') + (r.stderr || '') }; };
|
|
12
|
+
let pass = 0, fail = 0;
|
|
13
|
+
const check = (n, c, d) => { console.log((c ? 'PASS' : 'FAIL') + ' | ' + n + (c ? '' : ' | ' + d)); c ? pass++ : fail++; };
|
|
14
|
+
const T = (...p) => path.join(SB, '.kenaz', 'tasks', ...p);
|
|
15
|
+
const dirOf = (id) => ['backlog', 'active', 'done', 'blocked', 'archived'].find(d => fs.existsSync(T(d)) && fs.readdirSync(T(d)).some(f => f.startsWith(id + '_')));
|
|
16
|
+
const card = (id, dir) => { fs.mkdirSync(T(dir), { recursive: true }); fs.writeFileSync(T(dir, `${id}_t.yaml`), `id: ${id}\ntitle: t\npriority: P2\nstatus: ${dir === 'active' ? 'in_progress' : 'pending'}\n`); };
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
git('init', '-q', '-b', 'main'); git('config', 'user.email', 't@e.com'); git('config', 'user.name', 't');
|
|
20
|
+
fs.writeFileSync(path.join(SB, 'a.txt'), '1'); git('add', 'a.txt'); git('commit', '-q', '-m', 'base');
|
|
21
|
+
fs.writeFileSync(path.join(SB, 'b.txt'), '2'); git('add', 'b.txt'); git('commit', '-q', '-m', 'landed');
|
|
22
|
+
const sha = git('rev-parse', 'HEAD');
|
|
23
|
+
git('checkout', '-q', '-b', 'side'); fs.writeFileSync(path.join(SB, 'c.txt'), '3'); git('add', 'c.txt'); git('commit', '-q', '-m', 'off-base');
|
|
24
|
+
const offSha = git('rev-parse', 'HEAD'); git('checkout', '-q', 'main');
|
|
25
|
+
for (const [id, d] of [['TASK_1', 'backlog'], ['TASK_2', 'blocked'], ['TASK_3', 'active'], ['TASK_4', 'backlog'], ['TASK_5', 'backlog']]) card(id, d);
|
|
26
|
+
|
|
27
|
+
// 1) update --status
|
|
28
|
+
let r = cli('update', 'TASK_5', '--status', 'done', '--priority', 'P1');
|
|
29
|
+
check('1 update --status (with other fields) exits non-zero', r.code !== 0, r.out);
|
|
30
|
+
check('1 error names move/complete', /move|complete/.test(r.out), r.out);
|
|
31
|
+
check('1 card unchanged', dirOf('TASK_5') === 'backlog' && !/priority: P1/.test(fs.readFileSync(T('backlog', 'TASK_5_t.yaml'), 'utf8')), r.out);
|
|
32
|
+
r = cli('update', 'TASK_5', '--status', 'done');
|
|
33
|
+
check('1 update --status alone exits non-zero naming move/complete', r.code !== 0 && /move|complete/.test(r.out), r.out);
|
|
34
|
+
r = cli('update', 'TASK_5', '--status=done', '--priority', 'P1');
|
|
35
|
+
check('1 update --status=done form is refused too', r.code !== 0 && !/priority: P1/.test(fs.readFileSync(T('backlog', 'TASK_5_t.yaml'), 'utf8')), r.out);
|
|
36
|
+
|
|
37
|
+
// 2) complete --landed-sha
|
|
38
|
+
r = cli('complete', 'TASK_4', '--landed-sha', offSha);
|
|
39
|
+
check('2 sha not on base refused, card unchanged', r.code !== 0 && dirOf('TASK_4') === 'backlog', r.out);
|
|
40
|
+
r = cli('complete', 'TASK_4', '--landed-sha', 'zzzz');
|
|
41
|
+
check('2 garbage sha refused', r.code !== 0 && dirOf('TASK_4') === 'backlog', r.out);
|
|
42
|
+
card('TASK_6', 'cancel');
|
|
43
|
+
r = cli('complete', 'TASK_6', '--landed-sha', sha);
|
|
44
|
+
check('2 a cancelled card is not closed by --landed-sha', r.code !== 0 && fs.existsSync(T('cancel', 'TASK_6_t.yaml')), r.out);
|
|
45
|
+
for (const id of ['TASK_1', 'TASK_2', 'TASK_3']) {
|
|
46
|
+
r = id === 'TASK_2' ? cli('complete', id, `--landed-sha=${sha}`) : cli('complete', id, '--landed-sha', sha);
|
|
47
|
+
check(`2 ${id} -> done in one command`, r.code === 0 && dirOf(id) === 'done', r.out);
|
|
48
|
+
}
|
|
49
|
+
const y = dirOf('TASK_1') === 'done' ? fs.readFileSync(T('done', 'TASK_1_t.yaml'), 'utf8') : '';
|
|
50
|
+
check('2 records external landing fields', /integration: external/.test(y) && y.includes(sha), y);
|
|
51
|
+
|
|
52
|
+
// 3) list shows archived
|
|
53
|
+
card('TASK_90', 'archived'); card('TASK_91', 'archived');
|
|
54
|
+
r = cli('list');
|
|
55
|
+
const tot = /\((\d+) total\)/.exec(r.out);
|
|
56
|
+
check('3 list summary has archived count; total includes it', /archived: 2 \(hidden; --include-archived\)/.test(r.out) && tot && tot[1] === '8', r.out);
|
|
57
|
+
r = cli('list', '--all');
|
|
58
|
+
check('3 list --all summary has archived count', /archived: 2 \(hidden; --include-archived\)/.test(r.out), r.out);
|
|
59
|
+
r = cli('list', '--include-archived');
|
|
60
|
+
check('3 --include-archived shows archived, no hidden note', /archived: 2/.test(r.out) && !/hidden/.test(r.out), r.out);
|
|
61
|
+
} finally { try { fs.rmSync(SB, { recursive: true, force: true }); } catch (e) { /* best effort */ } }
|
|
62
|
+
console.log(`\n${pass} passed, ${fail} failed`);
|
|
63
|
+
process.exit(fail ? 1 : 0);
|
|
@@ -2080,8 +2080,15 @@ class TaskAPI {
|
|
|
2080
2080
|
throw await this._missingTaskError(taskId);
|
|
2081
2081
|
}
|
|
2082
2082
|
|
|
2083
|
+
// TASK_2467: card finished outside the dispatch flow (plumbing merge, no card branch): verify the sha
|
|
2084
|
+
// is on the base, then close from backlog/active/blocked in one step (no post-complete).
|
|
2085
|
+
if (options.landedSha) {
|
|
2086
|
+
if (!['backlog', 'active', 'blocked'].includes(task._status_dir)) throw new Error(`--landed-sha closes a backlog/active/blocked card; ${taskId} is in ${task._status_dir}`);
|
|
2087
|
+
options = { ...options, external: require('./reconcile').verifyLandedSha(this._resolveProjectRoot(), options.landedSha), skipPostComplete: true };
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2083
2090
|
// TASK_2233: backlog → done is not an allowed transition; say what to do instead
|
|
2084
|
-
if (task.status === 'backlog' || task.status === 'pending' || task._status_dir === 'backlog') {
|
|
2091
|
+
if (!options.landedSha && (task.status === 'backlog' || task.status === 'pending' || task._status_dir === 'backlog')) {
|
|
2085
2092
|
throw new Error(`無法從 backlog 直接完成 ${taskId}: move it to active first (node task-api.js move ${taskId} active), then complete.`);
|
|
2086
2093
|
}
|
|
2087
2094
|
|
|
@@ -2119,7 +2126,7 @@ class TaskAPI {
|
|
|
2119
2126
|
|
|
2120
2127
|
const postCompleteRuns = !options.skipPostComplete;
|
|
2121
2128
|
let postCompleteSummary = null;
|
|
2122
|
-
const result = await this.moveTask(taskId, 'done', updatedTask);
|
|
2129
|
+
const result = await this.moveTask(taskId, 'done', updatedTask, options.landedSha ? { skipValidation: true } : {});
|
|
2123
2130
|
|
|
2124
2131
|
try {
|
|
2125
2132
|
this._autoPopThoughtsForCompletedTask(taskId, now);
|
|
@@ -2516,7 +2523,7 @@ Commands:
|
|
|
2516
2523
|
預設為整組取代(會回顯被移除的既有項目);加 --append 改為追加到既有項目之後(與既有重複的項目會略過並回顯)
|
|
2517
2524
|
node task-api.js pull TASK_ID 匯入共享 room 的唯讀 replica 為本機卡片(標記 origin_node;TASK_2421)
|
|
2518
2525
|
node task-api.js claim TASK_ID
|
|
2519
|
-
node task-api.js complete TASK_ID [--actual-hours HOURS]
|
|
2526
|
+
node task-api.js complete TASK_ID [--actual-hours HOURS] [--landed-sha SHA] --landed-sha: work already on main (no card branch); closes from backlog/active/blocked in one step
|
|
2520
2527
|
node task-api.js reconcile [--remote origin] [--base main] [--apply] [--worktrees] 關閉分支已併入 base 的 active 任務(預設 dry-run)
|
|
2521
2528
|
node task-api.js move TASK_ID STATUS [--force]
|
|
2522
2529
|
node task-api.js delete TASK_ID (僅限 backlog 狀態)
|
|
@@ -2683,7 +2690,12 @@ Create 選項:
|
|
|
2683
2690
|
}
|
|
2684
2691
|
}
|
|
2685
2692
|
const totalCount = Object.values(dirCounts).reduce((a, b) => a + b, 0);
|
|
2686
|
-
|
|
2693
|
+
// TASK_2467: archived/ is not scanned without --include-archived; count it instead of hiding it
|
|
2694
|
+
let archivedHidden = 0;
|
|
2695
|
+
if (!includeArchived) {
|
|
2696
|
+
try { archivedHidden = (await fs.readdir(path.join(api.tasksDir, 'archived'))).filter(n => n.startsWith('TASK_')).length; } catch { /* absent */ }
|
|
2697
|
+
}
|
|
2698
|
+
if (totalCount + archivedHidden === 0) emitEmptyListDiagnostics(api.tasksDir); // TASK_1778
|
|
2687
2699
|
|
|
2688
2700
|
// Load tasks: full _scanDir for small dirs, fast path for large dirs
|
|
2689
2701
|
const grouped = {};
|
|
@@ -2747,10 +2759,11 @@ Create 選項:
|
|
|
2747
2759
|
'done': 'done', 'cancel': 'cancel', 'archived': 'archived'
|
|
2748
2760
|
};
|
|
2749
2761
|
|
|
2750
|
-
|
|
2762
|
+
let statusSummary = statusOrder
|
|
2751
2763
|
.map(s => `${statusLabels[s]}: ${dirCounts[s] || 0}`)
|
|
2752
2764
|
.join(' | ');
|
|
2753
|
-
|
|
2765
|
+
if (!includeArchived) statusSummary += ` | archived: ${archivedHidden} (hidden; --include-archived)`;
|
|
2766
|
+
console.log(`任務統計 (${totalCount + archivedHidden} total)`);
|
|
2754
2767
|
console.log(` ${statusSummary}`);
|
|
2755
2768
|
|
|
2756
2769
|
const formatTask = (t) => {
|
|
@@ -2818,9 +2831,15 @@ Create 選項:
|
|
|
2818
2831
|
const dir = CONFIG.STATUS_MAP[oldStatus] || oldStatus;
|
|
2819
2832
|
byDir[dir] = (byDir[dir] || 0) + count;
|
|
2820
2833
|
}
|
|
2834
|
+
// TASK_2467: getStatistics folds hidden archived/ files into completed; split them back out
|
|
2835
|
+
let archivedHidden = 0;
|
|
2836
|
+
if (!showArchived) {
|
|
2837
|
+
try { archivedHidden = (await fs.readdir(path.join(api.tasksDir, 'archived'))).filter(n => n.startsWith('TASK_')).length; } catch { /* absent */ }
|
|
2838
|
+
byDir.done = Math.max(0, (byDir.done || 0) - archivedHidden);
|
|
2839
|
+
}
|
|
2821
2840
|
const statusSummary = statusOrder
|
|
2822
2841
|
.map(s => `${statusLabels[s]}: ${byDir[s] || 0}`)
|
|
2823
|
-
.join(' | ');
|
|
2842
|
+
.join(' | ') + (showArchived ? '' : ` | archived: ${archivedHidden} (hidden; --include-archived)`);
|
|
2824
2843
|
console.log(`任務統計 (${stats.total} total)`);
|
|
2825
2844
|
console.log(` ${statusSummary}`);
|
|
2826
2845
|
|
|
@@ -3076,6 +3095,12 @@ Create 選項:
|
|
|
3076
3095
|
if (args.includes('--skip-post-complete')) {
|
|
3077
3096
|
options.skipPostComplete = true;
|
|
3078
3097
|
}
|
|
3098
|
+
const landedIdx = args.findIndex(a => a === '--landed-sha' || a.startsWith('--landed-sha='));
|
|
3099
|
+
if (landedIdx >= 0) {
|
|
3100
|
+
const v = args[landedIdx].includes('=') ? args[landedIdx].slice('--landed-sha='.length) : args[landedIdx + 1];
|
|
3101
|
+
if (!v || v.startsWith('--')) { console.error('❌ --landed-sha: requires a commit sha'); process.exit(1); }
|
|
3102
|
+
options.landedSha = v;
|
|
3103
|
+
}
|
|
3079
3104
|
// TASK_2420: runtime/config tasks with no branch — git merge/verify reported as skipped
|
|
3080
3105
|
if (args.includes('--no-source')) {
|
|
3081
3106
|
options.noSource = true;
|
|
@@ -3431,6 +3456,11 @@ Create 選項:
|
|
|
3431
3456
|
process.exit(1);
|
|
3432
3457
|
}
|
|
3433
3458
|
}
|
|
3459
|
+
// TASK_2467: status is not an update field; never accept it silently.
|
|
3460
|
+
if (args.some(a => a === '--status' || a.startsWith('--status='))) {
|
|
3461
|
+
console.error(`❌ update 不接受 --status(不會變更狀態)。請用: move ${taskId} <backlog|active|blocked|cancel|done> 或 complete ${taskId}`);
|
|
3462
|
+
process.exit(1);
|
|
3463
|
+
}
|
|
3434
3464
|
const title = getArg('--title'); if (title) updates.title = title;
|
|
3435
3465
|
// Clearable text fields: '' is a legitimate value, so gate on presence (!== null).
|
|
3436
3466
|
const desc = getArg('--description'); if (desc !== null) updates.description = desc;
|
|
@@ -315,7 +315,7 @@ let stopPush = () => {};
|
|
|
315
315
|
|
|
316
316
|
function killTree(child) {
|
|
317
317
|
try {
|
|
318
|
-
if (process.platform === 'win32' && child.pid) execFileSync('taskkill', ['/PID', String(child.pid), '/T', '/F'], { stdio: 'ignore' }); // own child PID only
|
|
318
|
+
if (process.platform === 'win32' && child.pid) execFileSync('taskkill', ['/PID', String(child.pid), '/T', '/F'], { windowsHide: true, stdio: 'ignore' }); // own child PID only
|
|
319
319
|
else child.kill('SIGTERM');
|
|
320
320
|
} catch { /* already gone */ }
|
|
321
321
|
}
|
|
@@ -15,7 +15,12 @@ waiting for a green build. The commit command (built by
|
|
|
15
15
|
paths (`.kenaz/sessions`, `.kenaz/specialist`, `.formation/.dispatch-branch`,
|
|
16
16
|
`.formation/analytics`) — never hand-type `git add -A` to replace it, and
|
|
17
17
|
never delete the worktree yourself even after committing; the main thread
|
|
18
|
-
owns worktree lifecycle.
|
|
18
|
+
owns worktree lifecycle. When the main thread (or an agent told to clean up)
|
|
19
|
+
must remove one, the only safe command is
|
|
20
|
+
`node <core>/.formation/tools/worktree-reclaim.js remove <path>`: dispatch
|
|
21
|
+
worktrees hold junctions into the main repo (node_modules, workspace links),
|
|
22
|
+
and on Windows `git worktree remove --force` follows every one of them and
|
|
23
|
+
empties the target (TASK_2485).
|
|
19
24
|
|
|
20
25
|
## Why "sending a message ends execution" is a hard rule
|
|
21
26
|
|
|
@@ -75,7 +75,7 @@ function peerStats({ roots, now, days }) {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
function git(cwd, ...args) {
|
|
78
|
-
return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 64 << 20 });
|
|
78
|
+
return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 64 << 20, windowsHide: true });
|
|
79
79
|
}
|
|
80
80
|
/** Commits of the repo (all refs): [{sha, parents, t, subject, firstParent, onHead}] newest first; null when not a git repo. */
|
|
81
81
|
function readCommits(repo, sinceMs, untilMs) {
|
|
@@ -2592,7 +2592,7 @@ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"`, { cwd: worktreePath }
|
|
|
2592
2592
|
if (baseRef && fs.existsSync(checkScript)) {
|
|
2593
2593
|
const gateProc = spawnSync(process.execPath, [
|
|
2594
2594
|
checkScript, '--base', baseRef, '--head', taskBranch, '--repo', projectRoot, '--json',
|
|
2595
|
-
], { encoding: 'utf8' });
|
|
2595
|
+
], { windowsHide: true, encoding: 'utf8' });
|
|
2596
2596
|
let gate = null;
|
|
2597
2597
|
try { gate = JSON.parse((gateProc.stdout || '').trim() || '{}'); } catch { gate = null; }
|
|
2598
2598
|
if (gate && Array.isArray(gate.blocks) && gate.blocks.length > 0) {
|
|
@@ -187,7 +187,7 @@ function buildSubagentPrompt(skillDef, taskInput, taskData, options = {}) {
|
|
|
187
187
|
lines.push('Before editing, grep the callers, tests and fixtures your change reaches; fix what it breaks. Least code that completes the card: reuse existing helpers, no unrequested abstraction, option or duplicate implementation.');
|
|
188
188
|
lines.push('Run the tests the card names (narrow, not the full suite); say what you did not verify.');
|
|
189
189
|
// The worktree branches off the project's current branch; user projects are not always on `main`.
|
|
190
|
-
const sizeBase = (require('child_process').spawnSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: options.projectPath || process.cwd(), encoding: 'utf8' }).stdout || '').trim() || 'main';
|
|
190
|
+
const sizeBase = (require('child_process').spawnSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: options.projectPath || process.cwd(), encoding: 'utf8', windowsHide: true }).stdout || '').trim() || 'main';
|
|
191
191
|
const sizeCorePath = options.corePath || resolveCorePath(options.projectPath) || path.resolve(__dirname, '..', '..');
|
|
192
192
|
lines.push(`Before reporting run \`node ${sizeCorePath}/dev/scripts/check-file-size.js --base ${sizeBase} --head HEAD\`; if it BLOCKs, split by responsibility (module / component / hook) first.`);
|
|
193
193
|
const onBlocked = skillDef.collaboration && skillDef.collaboration.on_blocked;
|
|
@@ -63,10 +63,9 @@ function linkNodeModulesBestEffort(projectRoot, worktreePath) {
|
|
|
63
63
|
* target (the main checkout's node_modules). Never throws.
|
|
64
64
|
*/
|
|
65
65
|
function unlinkNodeModulesBestEffort(worktreePath) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
catch (e) { console.warn(` ⚠️ node_modules unlink failed: ${(e && e.message) || 'unknown'}`); }
|
|
66
|
+
// TASK_2485: unlinks EVERY link (any name/depth); returns {removed, failed}. failed != [] => do not remove.
|
|
67
|
+
try { return require('./worktree-reclaim').unlinkAllLinks(worktreePath); }
|
|
68
|
+
catch (e) { console.warn(` ⚠️ link unlink failed: ${(e && e.message) || 'unknown'}`); return { removed: [], failed: ['(unlink helper unavailable)'] }; }
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
/**
|
|
@@ -848,7 +847,8 @@ function removeWorktree(projectRoot, worktreePath, maxRetries = 3) {
|
|
|
848
847
|
// TASK_1844/TASK_2030: MUST run before any `git worktree remove` -- Git for
|
|
849
848
|
// Windows recurses through a node_modules junction/symlink on removal and
|
|
850
849
|
// empties the link target (the main checkout's node_modules).
|
|
851
|
-
unlinkNodeModulesBestEffort(worktreePath);
|
|
850
|
+
const unl = unlinkNodeModulesBestEffort(worktreePath);
|
|
851
|
+
if (unl.failed.length) { console.warn(` ⚠️ not removing ${worktreePath}: link(s) survive unlink: ${unl.failed.slice(0, 3).join(', ')}`); return false; }
|
|
852
852
|
for (let i = 0; i < maxRetries; i++) {
|
|
853
853
|
try {
|
|
854
854
|
execSync(`git worktree remove --force "${worktreePath}"`, {
|
|
@@ -73,6 +73,66 @@ function walkTree(root) {
|
|
|
73
73
|
return { size, newest, links, managedLinks };
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* TASK_2485: unlink EVERY symlink/junction under `root` (any name, any depth, also inside a real
|
|
78
|
+
* node_modules). lstat walk: a link is removed as a link and never entered. Measured on Windows:
|
|
79
|
+
* `git worktree remove --force` traverses every junction and empties its target (the main repo's
|
|
80
|
+
* node_modules, backend/.env, tracked files); fs.rmSync, Git Bash rm -rf and cmd rmdir /s do not.
|
|
81
|
+
* @returns {{removed:string[], failed:string[]}} paths relative to root
|
|
82
|
+
*/
|
|
83
|
+
function unlinkAllLinks(root) {
|
|
84
|
+
const removed = []; const failed = [];
|
|
85
|
+
const stack = [root];
|
|
86
|
+
while (stack.length) {
|
|
87
|
+
const dir = stack.pop();
|
|
88
|
+
let names;
|
|
89
|
+
try { names = fs.readdirSync(dir); } catch { continue; }
|
|
90
|
+
for (const name of names) {
|
|
91
|
+
const p = path.join(dir, name);
|
|
92
|
+
let st;
|
|
93
|
+
try { st = fs.lstatSync(p); } catch { continue; }
|
|
94
|
+
const rel = path.relative(root, p).replace(/\\/g, '/');
|
|
95
|
+
if (st.isSymbolicLink()) {
|
|
96
|
+
try { fs.unlinkSync(p); } catch { try { fs.rmdirSync(p); } catch { failed.push(rel); continue; } }
|
|
97
|
+
removed.push(rel);
|
|
98
|
+
} else if (st.isDirectory()) stack.push(p);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return { removed, failed };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The one safe removal: unlink all links, re-scan, then remove (git worktree remove --force when
|
|
106
|
+
* registered, fs.rmSync for orphan dirs). Refuses (ok:false) when a link survives or the path is
|
|
107
|
+
* not a worktree of this repo / under .formation/worktrees or .claude/worktrees.
|
|
108
|
+
* @returns {{ok:boolean, registered:boolean, unlinked:string[], reason?:string}}
|
|
109
|
+
*/
|
|
110
|
+
function safeRemoveWorktree(repo, target) {
|
|
111
|
+
const abs = path.resolve(target);
|
|
112
|
+
const { main, worktrees } = listRegisteredWorktrees(repo);
|
|
113
|
+
const registered = worktrees.some((w) => norm(w.path) === norm(abs));
|
|
114
|
+
const inside = (...dir) => { const rel = path.relative(path.join(repo, ...dir), abs); return !!rel && !rel.startsWith('..') && !path.isAbsolute(rel); };
|
|
115
|
+
const res = { ok: false, registered, unlinked: [] };
|
|
116
|
+
if (norm(abs) === norm(main.path)) return { ...res, reason: 'is the main checkout' };
|
|
117
|
+
if (!registered && !inside('.formation', 'worktrees') && !inside('.claude', 'worktrees')) return { ...res, reason: 'not a registered worktree and not under .formation/worktrees or .claude/worktrees' };
|
|
118
|
+
if (!fs.existsSync(abs) && !registered) return { ...res, ok: true, reason: 'already gone' };
|
|
119
|
+
const first = unlinkAllLinks(abs);
|
|
120
|
+
res.unlinked = first.removed;
|
|
121
|
+
const again = unlinkAllLinks(abs);
|
|
122
|
+
if (first.failed.length || again.removed.length || again.failed.length) {
|
|
123
|
+
return { ...res, reason: `cannot unlink: ${[...first.failed, ...again.removed, ...again.failed].slice(0, 5).join(', ')}` };
|
|
124
|
+
}
|
|
125
|
+
const rm = () => fs.rmSync(abs, { recursive: true, force: true, maxRetries: 3 });
|
|
126
|
+
try {
|
|
127
|
+
if (registered) {
|
|
128
|
+
try { git(main.path, ['worktree', 'remove', '--force', abs]); } catch { if (fs.existsSync(abs)) rm(); }
|
|
129
|
+
} else rm();
|
|
130
|
+
} catch (e) { return { ...res, reason: `remove failed: ${String(e.message).split('\n')[0]}` }; }
|
|
131
|
+
try { git(main.path, ['worktree', 'prune']); } catch { /* non-fatal */ }
|
|
132
|
+
const gone = !fs.existsSync(abs);
|
|
133
|
+
return { ...res, ok: gone, reason: gone ? undefined : 'directory still present (locked file?)' };
|
|
134
|
+
}
|
|
135
|
+
|
|
76
136
|
/** Uncommitted content beyond telemetry. Compares content (CRLF-only changes do not count). */
|
|
77
137
|
function dirtyContent(wt) {
|
|
78
138
|
const dirty = [];
|
|
@@ -177,7 +237,7 @@ function removeWorktree(repo, w, opts = {}) {
|
|
|
177
237
|
const { main } = listRegisteredWorktrees(repo);
|
|
178
238
|
// Drop dispatch's own node_modules links first: `git worktree remove` would
|
|
179
239
|
// traverse a junction and empty its target (the main checkout's node_modules).
|
|
180
|
-
(opts.unlinkManaged ||
|
|
240
|
+
(opts.unlinkManaged || unlinkAllLinks)(w.path);
|
|
181
241
|
const left = walkTree(w.path);
|
|
182
242
|
if (left.links.length || left.managedLinks.length) {
|
|
183
243
|
log(`KEPT ${w.path}: links still present after unlink: ${[...left.links, ...left.managedLinks].slice(0, 3).join(', ')}`);
|
|
@@ -256,4 +316,15 @@ function formatReport(evald) {
|
|
|
256
316
|
].join('\n')).join('\n') || '(no worktrees)';
|
|
257
317
|
}
|
|
258
318
|
|
|
259
|
-
|
|
319
|
+
// CLI for agents: node worktree-reclaim.js remove <path> (never raw `git worktree remove` / rm -rf)
|
|
320
|
+
if (require.main === module) {
|
|
321
|
+
const target = process.argv[2] === 'remove' ? process.argv[3] : null;
|
|
322
|
+
if (!target) { process.stderr.write('usage: node worktree-reclaim.js remove <worktree-or-orphan-dir>\n'); process.exit(2); }
|
|
323
|
+
let repo;
|
|
324
|
+
try { repo = path.dirname(git(process.cwd(), ['rev-parse', '--path-format=absolute', '--git-common-dir']).trim()); } catch { process.stderr.write('run from inside the repo\n'); process.exit(2); }
|
|
325
|
+
const r = safeRemoveWorktree(repo, target);
|
|
326
|
+
process.stdout.write(`${r.ok ? 'REMOVED' : 'REFUSED'} ${path.resolve(target)}${r.unlinked.length ? ` (unlinked ${r.unlinked.length} link(s): ${r.unlinked.slice(0, 5).join(', ')})` : ''}${r.reason ? ` - ${r.reason}` : ''}\n`);
|
|
327
|
+
process.exit(r.ok ? 0 : 1);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
module.exports = { unlinkAllLinks, safeRemoveWorktree, evaluateWorktrees, ensureWorktreeSpace, reclaimAllEligible, listRegisteredWorktrees, formatReport, DEFAULT_MIN_IDLE_MS };
|