kenaz 0.3.21 → 0.3.23
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/commands/Ansuz.md +2 -0
- package/core/.claude/hooks/__tests__/peer-session-fill.test.js +37 -0
- package/core/.claude/hooks/_ansuz-conversation.js +32 -1
- package/core/.claude/hooks/_user-scope.js +1 -1
- package/core/.claude/hooks/core-resolve.js +1 -1
- package/core/.claude/hooks/peer-optin.test.js +74 -0
- package/core/.claude/hooks/peer-session-fill.js +38 -0
- package/core/.claude/hooks/turn-context.js +5 -4
- package/core/.claude/hooks/worktree-create.js +6 -6
- package/core/.claude/hooks/worktree-remove.js +4 -4
- package/core/.claude/settings.json +9 -0
- 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/bifrost/openai/codex-hook-shim.js +21 -4
- package/core/.formation/bifrost/openai/codex-peer-pull.test.js +42 -0
- package/core/.formation/bifrost/provider-launch.js +2 -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/.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 +1 -1
- 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 +471 -0
- 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 +11 -11
- package/core/dev/scripts/kenaz-session.js +3 -3
- package/core/dev/scripts/peer-identity-e2e.js +3 -3
- 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 +97 -0
- package/core/plugins/kenaz/commands/Ansuz.md +2 -0
- package/core/plugins/kenaz/guidance.md +2 -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 +53 -4
- package/core/plugins/kenaz/scripts/kenaz.test.js +72 -5
- package/core/plugins/kenaz/scripts/ygg.js +40 -4
- package/core/plugins/kenaz/scripts/ygg.test.js +63 -3
- package/core/plugins/kenaz-peer/hooks/register.test.ts +59 -2
- package/core/plugins/kenaz-peer/hooks/register.ts +24 -1
- package/lib/cli.js +9 -5
- package/package.json +6 -6
- package/vendor/install-kenaz-cli.js +21 -6
- 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
|
@@ -5,6 +5,8 @@ ARGUMENTS 含 `[專案: X]` 或 `--project-path X` → PROJECT_PATH = X,下方
|
|
|
5
5
|
|
|
6
6
|
你是 **Ansuz**,Kenaz 的 orchestrator:理解需求,自己動手或拆卡派給 subagent,合併前在真實路徑上驗證,然後回報。先查現況、確認 scope,再動手。
|
|
7
7
|
|
|
8
|
+
用戶貼上房間 key 或要求加入/建立房間時,自己執行 `kenaz ygg join <key>` / `kenaz ygg create`(不要請用戶輸入),再轉述 status 區塊與「restart Claude Code」提示。
|
|
9
|
+
|
|
8
10
|
## Boot(一次 Bash)
|
|
9
11
|
1. Turn Context 有 `[session] session_id=<id>` 就記住 `<id>`。
|
|
10
12
|
2. `node .specialist/tools/ansuz-boot-fast.js`(有 `<id>` 加 `--conversation-id="<id>"`;有 PROJECT_PATH 加 `--project-path`)。
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const test = require('node:test');
|
|
3
|
+
const assert = require('node:assert');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { spawnSync } = require('child_process');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
// decide() writes a peer opt-in marker (TASK_2465): keep it out of the real ~/.kenaz (spawned children inherit this).
|
|
9
|
+
process.env.KENAZ_PEER_OPTIN_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'kenaz-optin-test-'));
|
|
10
|
+
const { decide } = require('../peer-session-fill');
|
|
11
|
+
|
|
12
|
+
const base = { session_id: 'sess-123', tool_input: { to: 'n', body: 'hi' } };
|
|
13
|
+
|
|
14
|
+
test('fills missing and wrong sessionId, keeps other args', () => {
|
|
15
|
+
for (const tn of ['mcp__kenaz-dashboard__kenaz_peer_send', 'mcp__plugin_kenaz_kenaz-dashboard__kenaz_peer_send']) {
|
|
16
|
+
for (const extra of [{}, { sessionId: 'wrong' }]) {
|
|
17
|
+
const out = decide({ ...base, tool_name: tn, tool_input: { ...base.tool_input, ...extra } });
|
|
18
|
+
assert.strictEqual(out.hookSpecificOutput.updatedInput.sessionId, 'sess-123');
|
|
19
|
+
assert.strictEqual(out.hookSpecificOutput.updatedInput.body, 'hi');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('non-peer tools and missing session_id produce nothing', () => {
|
|
25
|
+
assert.strictEqual(decide({ ...base, tool_name: 'mcp__kenaz-dashboard__kenaz_peer_threads' }), null);
|
|
26
|
+
assert.strictEqual(decide({ ...base, tool_name: 'Bash' }), null);
|
|
27
|
+
assert.strictEqual(decide({ tool_name: 'mcp__x__kenaz_peer_send', tool_input: {} }), null);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('main() prints JSON for a peer send and nothing on garbage stdin', () => {
|
|
31
|
+
const f = path.join(__dirname, '..', 'peer-session-fill.js');
|
|
32
|
+
const ok = spawnSync(process.execPath, [f], { input: JSON.stringify({ ...base, tool_name: 'mcp__a__kenaz_peer_send' }), encoding: 'utf8' });
|
|
33
|
+
assert.strictEqual(JSON.parse(ok.stdout).hookSpecificOutput.updatedInput.sessionId, 'sess-123');
|
|
34
|
+
const bad = spawnSync(process.execPath, [f], { input: '{nope', encoding: 'utf8' });
|
|
35
|
+
assert.strictEqual(bad.stdout, '');
|
|
36
|
+
assert.strictEqual(bad.status, 0);
|
|
37
|
+
});
|
|
@@ -181,4 +181,35 @@ function taskIdNeedles(id) {
|
|
|
181
181
|
return /^\d+$/.test(s) ? [`TASK_${s}`] : [s];
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
|
|
184
|
+
// TASK_2465: peer collaborator opt-in. A session is a collaborator when it is Ansuz, OR it ran
|
|
185
|
+
// `ygg join`/`ygg create` / called kenaz_peer_send (peer-session-fill.js writes the marker), OR it was started
|
|
186
|
+
// by `kenaz launch` (KENAZ_PEER_OPTIN=1). One marker file per session id under ~/.kenaz/peer-optin, expiring,
|
|
187
|
+
// so mod / turn-context / Codex shim read it without parsing transcripts. Never opted in -> false (privacy).
|
|
188
|
+
const OPTIN_TTL_MS = 24 * 3600 * 1000;
|
|
189
|
+
const SID_OK = /^[A-Za-z0-9_-]{1,128}$/;
|
|
190
|
+
function optinDir() {
|
|
191
|
+
if (process.env.KENAZ_PEER_OPTIN_DIR) return process.env.KENAZ_PEER_OPTIN_DIR;
|
|
192
|
+
return path.join(require('os').homedir(), '.kenaz', 'peer-optin');
|
|
193
|
+
}
|
|
194
|
+
function markPeerOptIn(sessionId, now = Date.now()) {
|
|
195
|
+
const sid = String(sessionId || '');
|
|
196
|
+
if (!SID_OK.test(sid)) return false;
|
|
197
|
+
try {
|
|
198
|
+
fs.mkdirSync(optinDir(), { recursive: true });
|
|
199
|
+
fs.writeFileSync(path.join(optinDir(), sid + '.json'), JSON.stringify({ ts: now }));
|
|
200
|
+
return true;
|
|
201
|
+
} catch { return false; }
|
|
202
|
+
}
|
|
203
|
+
function isPeerOptedIn(sessionId, now = Date.now()) {
|
|
204
|
+
// kenaz launch's env opt-in is inherited by children: only the interactive entrypoint honours it, so a
|
|
205
|
+
// `claude -p` worker (sdk-cli) started inside a launched session never takes the human's peer mail.
|
|
206
|
+
if (process.env.KENAZ_PEER_OPTIN === '1' && process.env.CLAUDE_CODE_ENTRYPOINT === 'cli') return true;
|
|
207
|
+
const sid = String(sessionId || '');
|
|
208
|
+
if (!SID_OK.test(sid)) return false;
|
|
209
|
+
try {
|
|
210
|
+
const m = JSON.parse(fs.readFileSync(path.join(optinDir(), sid + '.json'), 'utf8'));
|
|
211
|
+
return Number.isFinite(m.ts) && now - m.ts >= 0 && now - m.ts < OPTIN_TTL_MS;
|
|
212
|
+
} catch { return false; }
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
module.exports = { isAnsuzConversation, lineIsAnsuzEntry, toolUseMentionsAny, taskIdNeedles, MARKERS, markPeerOptIn, isPeerOptedIn, OPTIN_TTL_MS };
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// TASK_2465: peer collaboration opt-in marker (writer = peer-session-fill.js, reader = turn-context / mod / Codex shim).
|
|
3
|
+
const test = require('node:test');
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'kenaz-optin-'));
|
|
9
|
+
process.env.KENAZ_PEER_OPTIN_DIR = dir;
|
|
10
|
+
delete process.env.KENAZ_PEER_OPTIN;
|
|
11
|
+
const { markPeerOptIn, isPeerOptedIn, OPTIN_TTL_MS } = require('./_ansuz-conversation');
|
|
12
|
+
const { decide } = require('./peer-session-fill');
|
|
13
|
+
|
|
14
|
+
test('no marker -> not opted in (privacy)', () => {
|
|
15
|
+
assert.equal(isPeerOptedIn('plain-session'), false);
|
|
16
|
+
assert.equal(isPeerOptedIn(''), false);
|
|
17
|
+
assert.equal(isPeerOptedIn(undefined), false);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('kenaz_peer_send writes the marker for that session only', () => {
|
|
21
|
+
const out = decide({ tool_name: 'mcp__kenaz-dashboard__kenaz_peer_send', session_id: 'sess-a', tool_input: { to: 'x' } });
|
|
22
|
+
assert.equal(out.hookSpecificOutput.updatedInput.sessionId, 'sess-a'); // sessionId fill unchanged
|
|
23
|
+
assert.equal(isPeerOptedIn('sess-a'), true);
|
|
24
|
+
assert.equal(isPeerOptedIn('sess-b'), false);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('ygg join / ygg create via Bash write the marker; other Bash commands do not', () => {
|
|
28
|
+
assert.equal(decide({ tool_name: 'Bash', session_id: 'sess-j', tool_input: { command: 'kenaz ygg join room-1' } }), null);
|
|
29
|
+
assert.equal(isPeerOptedIn('sess-j'), true);
|
|
30
|
+
decide({ tool_name: 'Bash', session_id: 'sess-c', tool_input: { command: 'node plugins/kenaz/scripts/ygg.js create demo' } });
|
|
31
|
+
assert.equal(isPeerOptedIn('sess-c'), true);
|
|
32
|
+
decide({ tool_name: 'Bash', session_id: 'sess-n', tool_input: { command: 'kenaz ygg status' } });
|
|
33
|
+
decide({ tool_name: 'Bash', session_id: 'sess-n', tool_input: { command: 'ls' } });
|
|
34
|
+
assert.equal(isPeerOptedIn('sess-n'), false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('only a real ygg invocation opts in; grep/echo/git mentions do not', () => {
|
|
38
|
+
const { yggInvocation } = require('./peer-session-fill');
|
|
39
|
+
for (const c of ['kenaz ygg join r', 'npx -y kenaz@1.2 ygg create x', 'node plugins/kenaz/scripts/ygg.js join r',
|
|
40
|
+
'cd x && kenaz ygg join r', 'KENAZ_X=1 kenaz ygg create r | cat', 'ls; kenaz ygg join r']) assert.equal(yggInvocation(c), true, c);
|
|
41
|
+
for (const c of ['grep "ygg join" docs', 'echo kenaz ygg join', 'git log -S"ygg create"', 'cat ygg.js', 'kenaz ygg status',
|
|
42
|
+
'grep -r ygg join . | wc -l', "echo 'x' # kenaz ygg join"]) assert.equal(yggInvocation(c), false, c);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('marker expires', () => {
|
|
46
|
+
const t0 = Date.now();
|
|
47
|
+
markPeerOptIn('sess-e', t0);
|
|
48
|
+
assert.equal(isPeerOptedIn('sess-e', t0 + OPTIN_TTL_MS - 1000), true);
|
|
49
|
+
assert.equal(isPeerOptedIn('sess-e', t0 + OPTIN_TTL_MS + 1000), false);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('unsafe session ids are refused (no path traversal)', () => {
|
|
53
|
+
assert.equal(markPeerOptIn('../evil'), false);
|
|
54
|
+
assert.equal(isPeerOptedIn('../evil'), false);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('KENAZ_PEER_OPTIN=1 (kenaz launch) opts in without a marker; =0 does not', () => {
|
|
58
|
+
process.env.KENAZ_PEER_OPTIN = '1';
|
|
59
|
+
process.env.CLAUDE_CODE_ENTRYPOINT = 'sdk-cli';
|
|
60
|
+
assert.equal(isPeerOptedIn('never-marked'), false); // claude -p worker inherits the env
|
|
61
|
+
process.env.CLAUDE_CODE_ENTRYPOINT = 'cli';
|
|
62
|
+
assert.equal(isPeerOptedIn('never-marked'), true);
|
|
63
|
+
process.env.KENAZ_PEER_OPTIN = '0';
|
|
64
|
+
assert.equal(isPeerOptedIn('never-marked'), false);
|
|
65
|
+
delete process.env.KENAZ_PEER_OPTIN;
|
|
66
|
+
delete process.env.CLAUDE_CODE_ENTRYPOINT;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('kenaz launch plan sets KENAZ_PEER_OPTIN=1 unless the caller set 0', () => {
|
|
70
|
+
const { plan } = require('../../.formation/bifrost/provider-launch.js');
|
|
71
|
+
const env = { DEEPSEEK_API_KEY: 'k' };
|
|
72
|
+
assert.equal(plan(['--provider', 'deepseek'], env, os.tmpdir(), null).env.KENAZ_PEER_OPTIN, '1');
|
|
73
|
+
assert.equal(plan(['--provider', 'deepseek'], { ...env, KENAZ_PEER_OPTIN: '0' }, os.tmpdir(), null).env.KENAZ_PEER_OPTIN, '0');
|
|
74
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// PreToolUse(kenaz_peer_send, any MCP server prefix): fill the tool's `sessionId` with the hook payload's
|
|
3
|
+
// session_id, overriding whatever the model passed (TASK_2462). Reply affinity (TASK_2285) routes a reply back
|
|
4
|
+
// to the asking session only when the outbound row carries that id; the harness knows it, the model should not
|
|
5
|
+
// have to. Fails open: any error or missing id -> no output, the call proceeds unchanged.
|
|
6
|
+
const PEER_SEND = /^mcp__.*kenaz_peer_send$/;
|
|
7
|
+
|
|
8
|
+
// An actual invocation only: a `;`/`&&`/`||`/`|`/newline-separated segment (after optional VAR=x assignments) that starts with
|
|
9
|
+
// `kenaz ygg join|create`, `npx [-y] kenaz[@ver] ygg join|create` or `node <path>/ygg.js join|create`.
|
|
10
|
+
// Mentions inside grep/echo/git arguments or quotes do not count.
|
|
11
|
+
const YGG_CMD = /^(?:[A-Za-z_]\w*=\S*\s+)*(?:kenaz(?:\.cmd)?\s+ygg|npx\s+(?:-y\s+)?kenaz(?:@\S+)?\s+ygg|node\s+\S*ygg\.js)\s+(?:join|create)(?:\s|$)/;
|
|
12
|
+
function yggInvocation(cmd) {
|
|
13
|
+
return String(cmd || '').split(/\|\||&&|[;|&\n]/).some((seg) => YGG_CMD.test(seg.trim()));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function decide(input) {
|
|
17
|
+
// TASK_2465: opt-in marker (see _ansuz-conversation.js): `ygg join/create` via Bash, or any peer send.
|
|
18
|
+
try {
|
|
19
|
+
const sid0 = input && typeof input.session_id === 'string' ? input.session_id.trim() : '';
|
|
20
|
+
const cmd = input && input.tool_name === 'Bash' && input.tool_input ? String(input.tool_input.command || '') : '';
|
|
21
|
+
if (sid0 && (PEER_SEND.test(String(input.tool_name || '')) || yggInvocation(cmd))) require('./_ansuz-conversation').markPeerOptIn(sid0);
|
|
22
|
+
} catch (_) { /* marker is best-effort */ }
|
|
23
|
+
if (!input || !PEER_SEND.test(String(input.tool_name || ''))) return null;
|
|
24
|
+
const sid = typeof input.session_id === 'string' ? input.session_id.trim() : '';
|
|
25
|
+
if (!sid) return null;
|
|
26
|
+
const ti = input.tool_input && typeof input.tool_input === 'object' ? input.tool_input : {};
|
|
27
|
+
return { hookSpecificOutput: { hookEventName: 'PreToolUse', updatedInput: Object.assign({}, ti, { sessionId: sid }) } };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function main() {
|
|
31
|
+
try {
|
|
32
|
+
const out = decide(JSON.parse(require('fs').readFileSync(0, 'utf8') || '{}'));
|
|
33
|
+
if (out) process.stdout.write(JSON.stringify(out));
|
|
34
|
+
} catch (_) { /* fail open */ }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (require.main === module) main();
|
|
38
|
+
module.exports = { decide, main, yggInvocation };
|
|
@@ -214,14 +214,15 @@ const sessionLine = peerHookPayload.session_id
|
|
|
214
214
|
fs.renameSync(tmpPath, STATE_PATH);
|
|
215
215
|
} catch { /* best-effort state write */ }
|
|
216
216
|
|
|
217
|
-
// 2. TASK_2122 — Peer Ansuz MVP: undelivered peer messages,
|
|
217
|
+
// 2. TASK_2122 — Peer Ansuz MVP: undelivered peer messages, collaborator sessions
|
|
218
218
|
// only (DESIGN.md §3.4 — a plain Claude Code session sharing this project
|
|
219
|
-
// dir must never see another user's collaborator traffic).
|
|
219
|
+
// dir must never see another user's collaborator traffic). TASK_2465: collaborator =
|
|
220
|
+
// Ansuz, or opted in (kenaz_peer_send / ygg join|create marker, or `kenaz launch`).
|
|
220
221
|
let peerText = '';
|
|
221
222
|
let waitingText = '';
|
|
222
223
|
try {
|
|
223
|
-
const { isAnsuzConversation } = require('./_ansuz-conversation');
|
|
224
|
-
if (isAnsuzConversation(peerHookPayload.transcript_path, projectDir)) {
|
|
224
|
+
const { isAnsuzConversation, isPeerOptedIn } = require('./_ansuz-conversation');
|
|
225
|
+
if (isPeerOptedIn(peerHookPayload.session_id) || isAnsuzConversation(peerHookPayload.transcript_path, projectDir)) {
|
|
225
226
|
// TASK_2226: a session whose kenaz-peer mod is alive gets its peer
|
|
226
227
|
// messages from the mod (peek -> submit -> ack-by-id); pulling here too
|
|
227
228
|
// would take-and-ack them out from under it. Fail-open: no session_id,
|
|
@@ -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')) {
|
|
@@ -635,11 +635,11 @@ function findNodeModulesParents(repo) {
|
|
|
635
635
|
function ensureWorktreeIgnores(worktreePath, relPath) {
|
|
636
636
|
try {
|
|
637
637
|
execFileSync('git', ['-C', worktreePath, 'check-ignore', '-q', '--', relPath],
|
|
638
|
-
{ stdio: ['ignore', 'ignore', 'ignore'] });
|
|
638
|
+
{ windowsHide: true, stdio: ['ignore', 'ignore', 'ignore'] });
|
|
639
639
|
return; // exit 0 → already ignored (repo .gitignore covers it)
|
|
640
640
|
} catch { /* not ignored → add per-worktree exclude */ }
|
|
641
641
|
const gp = execFileSync('git', ['-C', worktreePath, 'rev-parse', '--git-path', 'info/exclude'],
|
|
642
|
-
{ stdio: ['ignore', 'pipe', 'pipe'] }).toString().trim();
|
|
642
|
+
{ windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] }).toString().trim();
|
|
643
643
|
const excludeFile = path.isAbsolute(gp) ? gp : path.resolve(worktreePath, gp);
|
|
644
644
|
fs.mkdirSync(path.dirname(excludeFile), { recursive: true });
|
|
645
645
|
fs.appendFileSync(excludeFile, '/' + relPath + '/\n');
|
|
@@ -797,7 +797,7 @@ function main() {
|
|
|
797
797
|
const taken = (b) => {
|
|
798
798
|
try {
|
|
799
799
|
execFileSync('git', ['-C', repo, 'rev-parse', '--verify', '--quiet', 'refs/heads/' + b],
|
|
800
|
-
{ stdio: ['ignore', 'pipe', 'ignore'] });
|
|
800
|
+
{ windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] });
|
|
801
801
|
return true;
|
|
802
802
|
} catch { return false; }
|
|
803
803
|
};
|
|
@@ -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
|
|
|
@@ -91,7 +91,7 @@ function unlinkNodeModulesLinks(root) {
|
|
|
91
91
|
function worktreeBranch(repo, worktreePath) {
|
|
92
92
|
try {
|
|
93
93
|
const out = execFileSync('git', ['-C', repo, 'worktree', 'list', '--porcelain'],
|
|
94
|
-
{ stdio: ['ignore', 'pipe', 'ignore'] }).toString();
|
|
94
|
+
{ windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] }).toString();
|
|
95
95
|
const want = path.resolve(worktreePath).replace(/\\/g, '/').toLowerCase();
|
|
96
96
|
let curPath = null;
|
|
97
97
|
for (const line of out.split(/\r?\n/)) {
|
|
@@ -132,7 +132,7 @@ function main() {
|
|
|
132
132
|
let repo = null;
|
|
133
133
|
try {
|
|
134
134
|
const common = execFileSync('git', ['-C', worktreePath, 'rev-parse', '--git-common-dir'],
|
|
135
|
-
{ stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
|
|
135
|
+
{ windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
|
|
136
136
|
if (common) {
|
|
137
137
|
const commonAbs = path.resolve(worktreePath, common); // handles relative or absolute
|
|
138
138
|
if (path.basename(commonAbs) === '.git') repo = path.dirname(commonAbs);
|
|
@@ -72,6 +72,15 @@
|
|
|
72
72
|
],
|
|
73
73
|
"matcher": "mcp__kenaz-dashboard__.*|Read|Edit|Write"
|
|
74
74
|
},
|
|
75
|
+
{
|
|
76
|
+
"matcher": "mcp__.*kenaz_peer_send|Bash",
|
|
77
|
+
"hooks": [
|
|
78
|
+
{
|
|
79
|
+
"type": "command",
|
|
80
|
+
"command": "node -e \"const d=process.env.CLAUDE_PROJECT_DIR;process.chdir(d);require(d+'/.claude/hooks/peer-session-fill.js').main()\""
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
},
|
|
75
84
|
{
|
|
76
85
|
"matcher": "Agent|Task",
|
|
77
86
|
"hooks": [
|
|
@@ -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
|
}
|
|
@@ -65,6 +65,23 @@ async function peekPeerContext(p) {
|
|
|
65
65
|
} catch (err) { console.error('[codex-hook-shim] peer pull err:', err.message); return { text: '', ids: [], root: '' }; }
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
// TASK_2465: an active room = the server's yggdrasil config for the resolved root lists its effective room key in
|
|
69
|
+
// collaboration_projects (the activation). .kenaz/yggdrasil.yaml alone is committed repo config and proves nothing.
|
|
70
|
+
// Fail closed: server down, slow or odd answer -> no pull.
|
|
71
|
+
async function inActiveRoom(cwd, request = peerRequest) {
|
|
72
|
+
try {
|
|
73
|
+
const root = resolvePeerProjectRoot(process.env.KENAZ_CONSOLIDATE_ROOT || cwd);
|
|
74
|
+
if (!root) return false;
|
|
75
|
+
const cfg = await request('GET', '/api/yggdrasil/config?projectPath=' + encodeURIComponent(root));
|
|
76
|
+
const key = cfg && typeof cfg.effective_project_key === 'string' ? cfg.effective_project_key : '';
|
|
77
|
+
return !!key && Array.isArray(cfg.collaboration_projects) && cfg.collaboration_projects.includes(key);
|
|
78
|
+
} catch { return false; }
|
|
79
|
+
}
|
|
80
|
+
async function wantsPeerPull(env, argv, cwd, request) {
|
|
81
|
+
if (env.KENAZ_PEER_PULL === '0') return false;
|
|
82
|
+
return env.KENAZ_PEER_PULL === '1' || (argv || []).includes('--pull') || inActiveRoom(cwd, request);
|
|
83
|
+
}
|
|
84
|
+
|
|
68
85
|
function ackPeer(root, ids) {
|
|
69
86
|
return ids.length ? peerRequest('POST', '/api/peer/ack', { projectPath: root, ids }) : Promise.resolve(null);
|
|
70
87
|
}
|
|
@@ -146,9 +163,9 @@ async function main() {
|
|
|
146
163
|
const cb = rest.find((a) => typeof a === 'function'); if (cb) cb();
|
|
147
164
|
return true;
|
|
148
165
|
};
|
|
149
|
-
// Pull when launch.js set KENAZ_PEER_PULL=1
|
|
150
|
-
// session); KENAZ_PEER_PULL=0 (headless relay) always wins.
|
|
151
|
-
const wantPull =
|
|
166
|
+
// Pull when launch.js set KENAZ_PEER_PULL=1, the registered hook carries --pull (independently opened
|
|
167
|
+
// session), or (TASK_2465) the project is in an active room; KENAZ_PEER_PULL=0 (headless relay) always wins.
|
|
168
|
+
const wantPull = await wantsPeerPull(process.env, process.argv, p.cwd || process.cwd());
|
|
152
169
|
const peerPeek = ev === 'UserPromptSubmit' && mode === 'full' && wantPull ? peekPeerContext(p) : null;
|
|
153
170
|
try {
|
|
154
171
|
const pr = handleHookEvent(payload);
|
|
@@ -187,4 +204,4 @@ async function main() {
|
|
|
187
204
|
}
|
|
188
205
|
|
|
189
206
|
if (require.main === module) main();
|
|
190
|
-
module.exports = { peekPeerContext, ackPeer, writeUserPromptContext, frameForCodex };
|
|
207
|
+
module.exports = { wantsPeerPull, inActiveRoom, peekPeerContext, ackPeer, writeUserPromptContext, frameForCodex };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// TASK_2465: Codex pulls peer mail by default when the server says the project's room is activated.
|
|
3
|
+
const test = require('node:test');
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { wantsPeerPull } = require('./codex-hook-shim.js');
|
|
9
|
+
|
|
10
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'kenaz-codex-room-'));
|
|
11
|
+
fs.mkdirSync(path.join(dir, '.kenaz'), { recursive: true });
|
|
12
|
+
fs.writeFileSync(path.join(dir, '.kenaz', 'project.yaml'), 'name: t\n');
|
|
13
|
+
// Committed repo config naming a room is NOT the activation.
|
|
14
|
+
fs.writeFileSync(path.join(dir, '.kenaz', 'yggdrasil.yaml'), 'yggdrasil_project: "room-a"\n');
|
|
15
|
+
|
|
16
|
+
const server = (cfg) => async (method, p) => { assert.equal(method, 'GET'); assert.ok(p.startsWith('/api/yggdrasil/config?projectPath=')); return cfg; };
|
|
17
|
+
const active = server({ effective_project_key: 'room-a', collaboration_projects: ['room-a'] });
|
|
18
|
+
|
|
19
|
+
test('activated room -> pulls without the flag', async () => {
|
|
20
|
+
assert.equal(await wantsPeerPull({}, ['node', 'shim'], dir, active), true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('room key present in repo config but not activated -> no pull', async () => {
|
|
24
|
+
assert.equal(await wantsPeerPull({}, [], dir, server({ effective_project_key: 'room-a', collaboration_projects: [] })), false);
|
|
25
|
+
assert.equal(await wantsPeerPull({}, [], dir, server({ effective_project_key: 'room-a', collaboration_projects: ['other'] })), false);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('server down or odd answer fails closed', async () => {
|
|
29
|
+
assert.equal(await wantsPeerPull({}, [], dir, async () => null), false);
|
|
30
|
+
assert.equal(await wantsPeerPull({}, [], dir, async () => { throw new Error('x'); }), false);
|
|
31
|
+
assert.equal(await wantsPeerPull({}, [], dir, server({})), false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('KENAZ_PEER_PULL=0 wins over an activated room and --pull', async () => {
|
|
35
|
+
assert.equal(await wantsPeerPull({ KENAZ_PEER_PULL: '0' }, ['--pull'], dir, active), false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('KENAZ_PEER_PULL=1 and --pull still pull without a room', async () => {
|
|
39
|
+
const none = server({});
|
|
40
|
+
assert.equal(await wantsPeerPull({ KENAZ_PEER_PULL: '1' }, [], dir, none), true);
|
|
41
|
+
assert.equal(await wantsPeerPull({}, ['--pull'], dir, none), true);
|
|
42
|
+
});
|