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
|
@@ -142,7 +142,7 @@ function main() {
|
|
|
142
142
|
const build = spawnSync(
|
|
143
143
|
'cargo',
|
|
144
144
|
['test', '--lib', '--no-run', '--message-format=json'],
|
|
145
|
-
{ cwd: crateDir, encoding: 'utf8', shell: false, maxBuffer: 256 * 1024 * 1024, env }
|
|
145
|
+
{ windowsHide: true, cwd: crateDir, encoding: 'utf8', shell: false, maxBuffer: 256 * 1024 * 1024, env }
|
|
146
146
|
);
|
|
147
147
|
if (build.status !== 0) {
|
|
148
148
|
const compilerErrors = renderCompilerErrors(build.stdout);
|
|
@@ -180,7 +180,7 @@ function main() {
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
// 3. Run the artifact directly.
|
|
183
|
-
const run = spawnSync(testExe, exeArgs, { stdio: 'inherit' });
|
|
183
|
+
const run = spawnSync(testExe, exeArgs, { windowsHide: true, stdio: 'inherit' });
|
|
184
184
|
process.exit(run.status ?? 1);
|
|
185
185
|
}
|
|
186
186
|
|
|
@@ -36,7 +36,7 @@ function evaluate(streamText, expected) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
function copyRepo(dest) {
|
|
39
|
-
const files = execFileSync('git', ['ls-files', '-z'], { cwd: REPO, encoding: 'utf8', maxBuffer: 1 << 28 }).split('\0').filter(Boolean);
|
|
39
|
+
const files = execFileSync('git', ['ls-files', '-z'], { windowsHide: true, cwd: REPO, encoding: 'utf8', maxBuffer: 1 << 28 }).split('\0').filter(Boolean);
|
|
40
40
|
for (const f of files) {
|
|
41
41
|
const from = path.join(REPO, f);
|
|
42
42
|
if (!fs.existsSync(from)) continue;
|
|
@@ -49,11 +49,11 @@ function runScenario(sc, opts) {
|
|
|
49
49
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'skill-replay-'));
|
|
50
50
|
try {
|
|
51
51
|
copyRepo(dir);
|
|
52
|
-
execFileSync('git', ['init', '-q'], { cwd: dir });
|
|
52
|
+
execFileSync('git', ['init', '-q'], { windowsHide: true, cwd: dir });
|
|
53
53
|
const prompt = (sc.prefix === undefined ? '/Ansuz ' : sc.prefix) + sc.message;
|
|
54
54
|
// Prompt goes on stdin: with shell:true (needed on Windows) argv is concatenated unquoted, which split the message.
|
|
55
55
|
const r = spawnSync('claude', ['-p', '--output-format', 'stream-json', '--verbose', '--max-turns', String(opts.maxTurns)],
|
|
56
|
-
{ cwd: dir, input: prompt, encoding: 'utf8', maxBuffer: 1 << 28, timeout: opts.timeoutMs, shell: process.platform === 'win32' });
|
|
56
|
+
{ windowsHide: true, cwd: dir, input: prompt, encoding: 'utf8', maxBuffer: 1 << 28, timeout: opts.timeoutMs, shell: process.platform === 'win32' });
|
|
57
57
|
const ev = evaluate(r.stdout || '', sc.expected);
|
|
58
58
|
return { id: sc.id, expected: sc.expected, hit: ev.hit, calls: ev.calls, exit: r.status, error: r.error && r.error.message };
|
|
59
59
|
} finally {
|
|
@@ -120,7 +120,7 @@ async function testSlowServe() {
|
|
|
120
120
|
KENAZ_FIXTURE_SLOW_PHASE_MS: '4000',
|
|
121
121
|
});
|
|
122
122
|
const spawnedAt = Date.now();
|
|
123
|
-
const child = spawn(process.execPath, [SLOW_SERVE], { env, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
123
|
+
const child = spawn(process.execPath, [SLOW_SERVE], { windowsHide: true, env, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
124
124
|
spawnedPids.push(child.pid);
|
|
125
125
|
let out = '';
|
|
126
126
|
child.stdout.on('data', (d) => (out += d.toString()));
|
|
@@ -166,7 +166,7 @@ async function testSlowServe() {
|
|
|
166
166
|
|
|
167
167
|
async function testNeverListens() {
|
|
168
168
|
console.log('\n[selftest] never-listens.server.js');
|
|
169
|
-
const child = spawn(process.execPath, [NEVER_LISTENS], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
169
|
+
const child = spawn(process.execPath, [NEVER_LISTENS], { windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
170
170
|
spawnedPids.push(child.pid);
|
|
171
171
|
let exited = false;
|
|
172
172
|
child.on('exit', () => {
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// TASK_2474 guard: every child process Kenaz starts must be hidden on Windows.
|
|
3
|
+
// - Node: spawn/exec* calls in shipped JS must pass windowsHide (inline in the call, or on an options
|
|
4
|
+
// object defined in the same file). JS_ALLOW = intentionally visible terminals, with reasons.
|
|
5
|
+
// - Rust: Command::new in non-test Rust files outside common/process.rs must be hidden_command.
|
|
6
|
+
const test = require('node:test');
|
|
7
|
+
const assert = require('node:assert');
|
|
8
|
+
const fs = require('node:fs');
|
|
9
|
+
const path = require('node:path');
|
|
10
|
+
|
|
11
|
+
const ROOT = path.resolve(__dirname, '..', '..');
|
|
12
|
+
const JS_DIRS = ['.specialist/tools', '.formation', '.claude/hooks', 'plugins/kenaz/hooks', 'plugins/kenaz/scripts',
|
|
13
|
+
'.collaboration/core', 'packages/kenaz/lib', 'packages/kenaz/bin', 'dashboard/scripts', 'dev/scripts'];
|
|
14
|
+
const RS_DIR = 'dashboard/src-tauri/src';
|
|
15
|
+
|
|
16
|
+
// "relative/path.js:line" -> reason. Intentionally visible terminals only.
|
|
17
|
+
const JS_ALLOW = {
|
|
18
|
+
'dev/scripts/task1961/selftest.js:132': 'false positive: "spawn (took ..." inside a template-string message, not a call',
|
|
19
|
+
};
|
|
20
|
+
// "relative/path.rs" -> reason (whole file may use raw Command::new outside #[cfg(test)]).
|
|
21
|
+
const RS_ALLOW = {
|
|
22
|
+
'dashboard/src-tauri/src/common/process.rs': 'the hidden_command constructors themselves',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function walk(dir, exts, out = []) {
|
|
26
|
+
const abs = path.join(ROOT, dir);
|
|
27
|
+
if (!fs.existsSync(abs)) return out;
|
|
28
|
+
for (const e of fs.readdirSync(abs, { withFileTypes: true })) {
|
|
29
|
+
if (['node_modules', '.git', 'target', 'dist', 'fixtures'].includes(e.name)) continue;
|
|
30
|
+
const rel = dir + '/' + e.name;
|
|
31
|
+
if (e.isDirectory()) walk(rel, exts, out);
|
|
32
|
+
else if (exts.some((x) => e.name.endsWith(x)) && !/\.test\.[cm]?js$/.test(e.name)) out.push(rel);
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// plugins/kenaz/hooks/core-resolve.js is generated (gitignored) from .claude/hooks/core-resolve.js
|
|
38
|
+
const isGenerated = (rel) => rel === 'plugins/kenaz/hooks/core-resolve.js';
|
|
39
|
+
|
|
40
|
+
function matchParen(src, open) {
|
|
41
|
+
let depth = 0, q = null;
|
|
42
|
+
for (let i = open; i < src.length; i++) {
|
|
43
|
+
const c = src[i];
|
|
44
|
+
if (q) { if (c === '\\') i++; else if (c === q) q = null; continue; }
|
|
45
|
+
if (c === '"' || c === "'" || c === '`') { q = c; continue; }
|
|
46
|
+
if (c === '(') depth++;
|
|
47
|
+
else if (c === ')' && --depth === 0) return i;
|
|
48
|
+
}
|
|
49
|
+
return src.length;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function scanJs(rel, src) {
|
|
53
|
+
if (!/child_process/.test(src)) return [];
|
|
54
|
+
const bad = [];
|
|
55
|
+
// blank block comments and whole-line // comments (offsets and line numbers preserved)
|
|
56
|
+
src = src.replace(/\/\*[\s\S]*?\*\//g, (c) => c.replace(/[^\n]/g, ' ')).replace(/^[ \t]*\/\/.*$/gm, (c) => ' '.repeat(c.length));
|
|
57
|
+
const re = /(?<![\w$.])(?:(?:cp|child_process|childProcess|proc)\.)?(spawn|spawnSync|exec|execSync|execFile|execFileSync)\s*\(/g;
|
|
58
|
+
let m;
|
|
59
|
+
while ((m = re.exec(src))) {
|
|
60
|
+
const open = m.index + m[0].length - 1;
|
|
61
|
+
const args = src.slice(open, matchParen(src, open) + 1);
|
|
62
|
+
if (/windowsHide/.test(args)) continue;
|
|
63
|
+
// options passed as a bare identifier: accept when that identifier is defined with windowsHide in the file
|
|
64
|
+
const last = args.slice(1, -1).split(',').pop().trim();
|
|
65
|
+
if (/^[A-Za-z_$][\w$]*$/.test(last) && new RegExp(`${last}\\s*=\\s*\\{[^;]*windowsHide`).test(src)) continue;
|
|
66
|
+
const line = src.slice(0, m.index).split('\n').length;
|
|
67
|
+
if (JS_ALLOW[`${rel}:${line}`]) continue;
|
|
68
|
+
bad.push(`${rel}:${line} ${m[1]}`);
|
|
69
|
+
}
|
|
70
|
+
return bad;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function scanRs(rel, src) {
|
|
74
|
+
if (RS_ALLOW[rel]) return [];
|
|
75
|
+
const cut = src.search(/#\[cfg\(test\)\]/);
|
|
76
|
+
const body = cut >= 0 ? src.slice(0, cut) : src;
|
|
77
|
+
const bad = [];
|
|
78
|
+
body.split('\n').forEach((ln, i) => {
|
|
79
|
+
if (/^\s*\/\//.test(ln)) return;
|
|
80
|
+
if (/Command::new\s*\(/.test(ln) && !/hidden_/.test(ln)) bad.push(`${rel}:${i + 1}`);
|
|
81
|
+
});
|
|
82
|
+
return bad;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
test('Node: every child_process call passes windowsHide', () => {
|
|
86
|
+
const files = JS_DIRS.flatMap((d) => walk(d, ['.js', '.cjs', '.mjs'])).filter((f) => !isGenerated(f));
|
|
87
|
+
const bad = files.flatMap((f) => scanJs(f, fs.readFileSync(path.join(ROOT, f), 'utf8')));
|
|
88
|
+
console.log(`node violations: ${bad.length}`);
|
|
89
|
+
assert.deepStrictEqual(bad, []);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('Rust: no raw Command::new in production code outside common/process.rs', () => {
|
|
93
|
+
const files = walk(RS_DIR, ['.rs']).filter((f) => !/(^|\/)tests?\.rs$|_tests?\.rs$|\/tests\//.test(f));
|
|
94
|
+
const bad = files.flatMap((f) => scanRs(f, fs.readFileSync(path.join(ROOT, f), 'utf8')));
|
|
95
|
+
console.log(`rust violations: ${bad.length}`);
|
|
96
|
+
assert.deepStrictEqual(bad, []);
|
|
97
|
+
});
|
|
@@ -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 "${CLAUDE_PLUGIN_ROOT}/hooks/core-resolve.js" .specialist/tools/ansuz-boot-fast.js`(有 `<id>` 加 `--conversation-id="<id>"`;有 PROJECT_PATH 加 `--project-path`)。
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
[Kenaz plugin: mode full] Kenaz MCP tools (kenaz-dashboard) are available when kenaz-server runs on localhost:18080.
|
|
2
2
|
- Code navigation: kenaz_lsp_callers / kenaz_lsp_references / kenaz_lsp_definition / kenaz_lsp_symbols (always pass projectPath). Text search: Grep/Glob.
|
|
3
3
|
- Collaboration: kenaz_peer_send / kenaz_peer_threads (other sessions, other machines, Codex), /kenaz ygg status|join.
|
|
4
|
+
- Rooms: when the user pastes a room key or asks to join/create a room, run `kenaz ygg join <key>` / `kenaz ygg create` yourself (never ask them to type it), then relay the status block and any "restart Claude Code" note.
|
|
5
|
+
- Reporting a Kenaz problem or idea: call kenaz_wish_create (scope kenaz) yourself; it uploads to the Kenaz mother (Alaya) automatically, no user step. Claude Code's SendFeedback tool and /feedback go to Anthropic, never to Kenaz.
|
|
4
6
|
- Reply in the user's language. Commands: /Ansuz, /Amelia, /review, /security, /incident, /spec.
|
|
5
7
|
- Switch: /kenaz off | assist | full (or env KENAZ_MODE). off disables every Kenaz hook.
|
|
@@ -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
|
}
|
|
@@ -51,6 +51,15 @@
|
|
|
51
51
|
}
|
|
52
52
|
]
|
|
53
53
|
},
|
|
54
|
+
{
|
|
55
|
+
"matcher": "mcp__.*kenaz_peer_send|Bash",
|
|
56
|
+
"hooks": [
|
|
57
|
+
{
|
|
58
|
+
"type": "command",
|
|
59
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/kenaz-hook.js\" peer-session-fill"
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
},
|
|
54
63
|
{
|
|
55
64
|
"matcher": "Agent|Task",
|
|
56
65
|
"hooks": [
|
|
@@ -13,7 +13,8 @@ const fs = require('fs');
|
|
|
13
13
|
const path = require('path');
|
|
14
14
|
const net = require('net');
|
|
15
15
|
const http = require('http');
|
|
16
|
-
const
|
|
16
|
+
const os = require('os');
|
|
17
|
+
const { spawn, spawnSync } = require('child_process');
|
|
17
18
|
const { resolveMode, allows, projectDir } = require('./mode');
|
|
18
19
|
const { isSdkSession } = require('./sdk-gate');
|
|
19
20
|
|
|
@@ -33,6 +34,7 @@ const HOOKS = {
|
|
|
33
34
|
'synapse-first': { kind: 'script', file: 'synapse-first-investigation.js', minMode: 'full' },
|
|
34
35
|
'memory-guard': { kind: 'script', file: 'memory-guard.js', call: 'main', minMode: 'full' },
|
|
35
36
|
'vendor-agent-guard': { kind: 'script', file: 'vendor-agent-guard.js', call: 'main', minMode: 'assist' },
|
|
37
|
+
'peer-session-fill': { kind: 'script', file: 'peer-session-fill.js', call: 'main', minMode: 'assist' },
|
|
36
38
|
'task-bridge': { kind: 'script', file: 'task-bridge.js', minMode: 'assist' },
|
|
37
39
|
};
|
|
38
40
|
|
|
@@ -48,6 +50,42 @@ function backendUp(cb) {
|
|
|
48
50
|
s.on('error', () => done(false));
|
|
49
51
|
}
|
|
50
52
|
|
|
53
|
+
// TASK_2468: after a reboot nothing has started kenaz-server yet (the plugin MCP is a static http URL).
|
|
54
|
+
// Start the installed binary detached, the way the installer does; never by process name, and only
|
|
55
|
+
// called after the port probe failed (a second server is never started over an answering one).
|
|
56
|
+
function startServer(dir) {
|
|
57
|
+
try {
|
|
58
|
+
const home = os.homedir();
|
|
59
|
+
const meta = JSON.parse(fs.readFileSync(path.join(home, '.kenaz', 'install.json'), 'utf8'));
|
|
60
|
+
const bin = meta && meta.serverBin;
|
|
61
|
+
if (!bin || !fs.existsSync(bin)) return false;
|
|
62
|
+
const vbs = path.join(home, '.kenaz', 'kenaz-server.vbs');
|
|
63
|
+
const core = coreDir(dir);
|
|
64
|
+
const env = Object.assign({}, process.env, core ? { KENAZ_SOURCE_DIR: core } : {});
|
|
65
|
+
const opts = { env, detached: true, stdio: 'ignore', windowsHide: true };
|
|
66
|
+
const viaNode = /\.js$/.test(bin); // a script (tests) runs under node; the real server is a binary
|
|
67
|
+
let child;
|
|
68
|
+
if (process.platform === 'win32') {
|
|
69
|
+
child = fs.existsSync(vbs) ? spawn('wscript.exe', [vbs], opts) : viaNode ? spawn(process.execPath, [bin], opts) : spawn(bin, [], opts);
|
|
70
|
+
} else {
|
|
71
|
+
const log = path.join(home, '.kenaz', 'kenaz-server.log');
|
|
72
|
+
const sq = (v) => "'" + String(v).replace(/'/g, "'\\''") + "'";
|
|
73
|
+
const cmd = 'if command -v setsid >/dev/null 2>&1; then S=setsid; else S=; fi; $S nohup ' +
|
|
74
|
+
(viaNode ? sq(process.execPath) + ' ' : '') + sq(bin) + ' >> ' + sq(log) + ' 2>&1 < /dev/null &';
|
|
75
|
+
child = spawn('sh', ['-c', cmd], opts);
|
|
76
|
+
}
|
|
77
|
+
child.on('error', () => {});
|
|
78
|
+
child.unref();
|
|
79
|
+
return true;
|
|
80
|
+
} catch (_) { return false; }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function waitForBackend(ms, cb) {
|
|
84
|
+
const end = Date.now() + ms;
|
|
85
|
+
const tick = () => backendUp((ok) => { if (ok || Date.now() >= end) return cb(ok); setTimeout(tick, 150); });
|
|
86
|
+
tick();
|
|
87
|
+
}
|
|
88
|
+
|
|
51
89
|
function post(body, cb) {
|
|
52
90
|
const r = http.request({ hostname: HOST, port: PORT, path: '/api/rust/agent-flow/hook', method: 'POST',
|
|
53
91
|
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }, timeout: 3000 },
|
|
@@ -74,7 +112,7 @@ function run(spec, stdin, dir) {
|
|
|
74
112
|
const stdio = ['pipe', 'inherit', 'ignore'];
|
|
75
113
|
if (spec.kind === 'core-script') {
|
|
76
114
|
const target = path.join(core, spec.rel);
|
|
77
|
-
if (fs.existsSync(target)) spawnSync(process.execPath, [target, ...(spec.args || [])], { cwd: dir, env, input: stdin, stdio, timeout: 10000 });
|
|
115
|
+
if (fs.existsSync(target)) spawnSync(process.execPath, [target, ...(spec.args || [])], { cwd: dir, env, input: stdin, stdio, timeout: 10000, windowsHide: true });
|
|
78
116
|
return;
|
|
79
117
|
}
|
|
80
118
|
const file = path.join(core, '.claude', 'hooks', spec.file);
|
|
@@ -82,7 +120,7 @@ function run(spec, stdin, dir) {
|
|
|
82
120
|
// Same shape as the core's own `node -e "chdir; require(hook)"` wrappers (require.main stays undefined).
|
|
83
121
|
const code = 'process.chdir(process.env.CLAUDE_PROJECT_DIR);const m=require(process.argv[1]);' +
|
|
84
122
|
(spec.call ? 'm[process.argv[2]]();' : '');
|
|
85
|
-
spawnSync(process.execPath, ['-e', code, file, spec.call || ''], { cwd: dir, env, input: stdin, stdio, timeout: 10000 });
|
|
123
|
+
spawnSync(process.execPath, ['-e', code, file, spec.call || ''], { cwd: dir, env, input: stdin, stdio, timeout: 10000, windowsHide: true });
|
|
86
124
|
}
|
|
87
125
|
|
|
88
126
|
function main() {
|
|
@@ -104,7 +142,18 @@ function main() {
|
|
|
104
142
|
}
|
|
105
143
|
run(spec, stdin, dir);
|
|
106
144
|
};
|
|
107
|
-
if (spec.backend)
|
|
145
|
+
if (!spec.backend) return go();
|
|
146
|
+
backendUp((ok) => {
|
|
147
|
+
if (ok) return go();
|
|
148
|
+
if (spec.kind !== 'session-start') return;
|
|
149
|
+
const finish = (up) => {
|
|
150
|
+
if (up) go();
|
|
151
|
+
process.stdout.write(up
|
|
152
|
+
? 'kenaz-server was not running; the Kenaz hook started it on ' + HOST + ':' + PORT + '. If the kenaz MCP tools (kenaz-dashboard) show as failed, ask the user to run /mcp to reconnect them (they connected before the server was up).\n'
|
|
153
|
+
: 'kenaz-server is not running on ' + HOST + ':' + PORT + ', so the kenaz MCP tools are unavailable. Ask the user to run `! npx kenaz install` (registers the boot service and starts it), then /mcp to reconnect kenaz-dashboard.\n');
|
|
154
|
+
};
|
|
155
|
+
if (startServer(dir)) waitForBackend(3000, finish); else finish(false);
|
|
156
|
+
});
|
|
108
157
|
}
|
|
109
158
|
|
|
110
159
|
process.on('uncaughtException', () => process.exit(0));
|
|
@@ -58,11 +58,14 @@ test('kenaz.yaml switch is honoured; env wins', () => {
|
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
test('backend down: no output, no stderr, exit 0', () => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
const home = proj(); // temp HOME: session-start would otherwise start the real installed server (TASK_2468)
|
|
62
|
+
const env = { KENAZ_BACKEND_PORT: '1', HOME: home, USERPROFILE: home };
|
|
63
|
+
const r = hook('flow-post', proj(), env);
|
|
64
|
+
assert.strictEqual(r.status, 0);
|
|
65
|
+
assert.strictEqual(r.stdout + r.stderr, '');
|
|
66
|
+
const s = hook('session-start', proj(), env); // only session-start speaks: one line naming the fix
|
|
67
|
+
assert.strictEqual(s.stderr, '');
|
|
68
|
+
assert.match(s.stdout, /npx kenaz install/);
|
|
66
69
|
});
|
|
67
70
|
|
|
68
71
|
test('ygg status fixture: masked key, room, peers, mail (TASK_2366)', () => {
|
|
@@ -202,3 +205,67 @@ test('ygg create explains 429, 503, missing relay_url and unreachable relay (TAS
|
|
|
202
205
|
await assert.rejects(createTeam(dir, '', 'project'), /cannot reach the relay at http:\/\/127\.0\.0\.1:1/);
|
|
203
206
|
});
|
|
204
207
|
});
|
|
208
|
+
|
|
209
|
+
// TASK_2468: SessionStart starts the installed server when the port is closed (temp HOME, free port, fake server script).
|
|
210
|
+
async function freePort() {
|
|
211
|
+
const srv = require('net').createServer();
|
|
212
|
+
await new Promise((r) => srv.listen(0, '127.0.0.1', r));
|
|
213
|
+
const p = srv.address().port;
|
|
214
|
+
await new Promise((r) => srv.close(r));
|
|
215
|
+
return p;
|
|
216
|
+
}
|
|
217
|
+
function fakeHome(withBin) {
|
|
218
|
+
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'kz-home-'));
|
|
219
|
+
fs.mkdirSync(path.join(home, '.kenaz'));
|
|
220
|
+
const bin = path.join(home, 'fake-server.js');
|
|
221
|
+
fs.writeFileSync(bin, "const http=require('http');require('fs').writeFileSync(process.env.PIDFILE,String(process.pid));" +
|
|
222
|
+
"http.createServer((q,r)=>{r.end('{}')}).listen(Number(process.env.KENAZ_BACKEND_PORT),'127.0.0.1');setTimeout(()=>process.exit(0),15000);");
|
|
223
|
+
if (withBin) fs.writeFileSync(path.join(home, '.kenaz', 'install.json'), JSON.stringify({ serverBin: bin }));
|
|
224
|
+
return { home, pidfile: path.join(home, 'pid') };
|
|
225
|
+
}
|
|
226
|
+
const isUp = (port) => new Promise((r) => { const s = require('net').connect({ host: '127.0.0.1', port }); s.on('connect', () => { s.destroy(); r(true); }); s.on('error', () => r(false)); });
|
|
227
|
+
const killPid = (f) => { try { process.kill(Number(fs.readFileSync(f, 'utf8'))); } catch (_) { /* never started */ } };
|
|
228
|
+
const hookAsync = (env) => new Promise((resolve) => {
|
|
229
|
+
const c = require('child_process').spawn(process.execPath, [HOOK, 'session-start'], { env: Object.assign({}, process.env, { CLAUDE_PROJECT_DIR: proj(), KENAZ_MODE: '', KENAZ_SOURCE_DIR: CORE }, env) });
|
|
230
|
+
let out = ''; c.stdout.on('data', (d) => { out += d; }); c.on('close', () => resolve(out)); c.stdin.end('{}');
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test('TASK_2468: session-start with the port closed starts the installed server and says so', async () => {
|
|
234
|
+
const port = await freePort(); const f = fakeHome(true);
|
|
235
|
+
const env = { HOME: f.home, USERPROFILE: f.home, KENAZ_BACKEND_PORT: String(port), PIDFILE: f.pidfile };
|
|
236
|
+
try {
|
|
237
|
+
const out = await hookAsync(env);
|
|
238
|
+
assert.ok(await isUp(port), 'server came up');
|
|
239
|
+
assert.match(out, /started it/); assert.match(out, /\/mcp/);
|
|
240
|
+
} finally { killPid(f.pidfile); }
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test('TASK_2468: no install.json -> one line naming `npx kenaz install` and /mcp; nothing spawned', async () => {
|
|
244
|
+
const port = await freePort(); const f = fakeHome(false);
|
|
245
|
+
const env = { HOME: f.home, USERPROFILE: f.home, KENAZ_BACKEND_PORT: String(port), PIDFILE: f.pidfile };
|
|
246
|
+
const out = await hookAsync(env);
|
|
247
|
+
assert.match(out, /npx kenaz install/); assert.match(out, /\/mcp/);
|
|
248
|
+
assert.strictEqual(out.trim().split('\n').length, 1);
|
|
249
|
+
assert.ok(!(await isUp(port)));
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test('TASK_2468: KENAZ_MODE=off and sdk entrypoint -> no output, nothing spawned', async () => {
|
|
253
|
+
for (const extra of [{ KENAZ_MODE: 'off' }, { CLAUDE_CODE_ENTRYPOINT: 'sdk-ts' }]) {
|
|
254
|
+
const port = await freePort(); const f = fakeHome(true);
|
|
255
|
+
const env = Object.assign({ HOME: f.home, USERPROFILE: f.home, KENAZ_BACKEND_PORT: String(port), PIDFILE: f.pidfile }, extra);
|
|
256
|
+
try {
|
|
257
|
+
assert.strictEqual(await hookAsync(env), '');
|
|
258
|
+
assert.ok(!(await isUp(port)) && !fs.existsSync(f.pidfile));
|
|
259
|
+
} finally { killPid(f.pidfile); }
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test('TASK_2468: port already answering -> nothing spawned, no extra line', async () => {
|
|
264
|
+
const port = await freePort(); const f = fakeHome(true);
|
|
265
|
+
const srv = require('http').createServer((q, r) => r.end('{}'));
|
|
266
|
+
await new Promise((r) => srv.listen(port, '127.0.0.1', r));
|
|
267
|
+
try {
|
|
268
|
+
const out = await hookAsync({ HOME: f.home, USERPROFILE: f.home, KENAZ_BACKEND_PORT: String(port), PIDFILE: f.pidfile });
|
|
269
|
+
assert.strictEqual(out, ''); assert.ok(!fs.existsSync(f.pidfile));
|
|
270
|
+
} finally { srv.close(); }
|
|
271
|
+
});
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
// /api/yggdrasil/config + /api/peer/status (same per-project/global key rules as the Dashboard,
|
|
6
6
|
// TASK_2321/2349). k-cli `ygg` shells out to this file - the one implementation.
|
|
7
7
|
const path = require('path');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const os = require('os');
|
|
8
10
|
|
|
9
11
|
const BASE = () => 'http://127.0.0.1:' + (process.env.KENAZ_HTTP_PORT || process.env.KENAZ_BACKEND_PORT || '18080');
|
|
10
12
|
|
|
@@ -86,6 +88,33 @@ function buildStatus(cfg, relay, peer) {
|
|
|
86
88
|
};
|
|
87
89
|
}
|
|
88
90
|
|
|
91
|
+
// TASK_2461: push needs the kenaz-peer plugin loaded by Claude Code (only in Ansuz sessions). join/create
|
|
92
|
+
// leave the node ready: register kenaz-peer in ~/.claude/settings.json when missing (same writer as the
|
|
93
|
+
// installer). Returns {state: 'ok'|'added'|'missing'|'skipped'|'error', note}. Never throws.
|
|
94
|
+
function loadInstaller() {
|
|
95
|
+
for (const p of ['../../../dashboard/scripts/install-kenaz-cli.js', '../../../packages/kenaz/vendor/install-kenaz-cli.js']) {
|
|
96
|
+
try { return require(path.join(__dirname, p)); } catch (e) { if (e.code !== 'MODULE_NOT_FOUND') throw e; }
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
function peerPlugin(home, register, version) {
|
|
101
|
+
try {
|
|
102
|
+
const inst = loadInstaller();
|
|
103
|
+
if (!inst) return { state: 'skipped', note: 'installer not found' };
|
|
104
|
+
const settings = path.join(home, '.claude', 'settings.json');
|
|
105
|
+
if (!fs.existsSync(path.join(home, '.claude'))) return { state: 'skipped', note: 'claude is not installed' };
|
|
106
|
+
const ver = version === undefined ? inst.claudeVersion() : version;
|
|
107
|
+
if (ver && inst.cmpVersion(ver, inst.MIN_CLI) < 0) return { state: 'skipped', note: 'claude ' + ver.join('.') + ' < ' + inst.MIN_CLI.join('.') + ' (run `claude update`)' };
|
|
108
|
+
const core = path.resolve(process.env.KENAZ_CORE_DIR || path.join(__dirname, '..', '..', '..'));
|
|
109
|
+
const dir = path.join(core, 'plugins', 'kenaz-peer');
|
|
110
|
+
const list = inst.entriesOf(inst.loadSettings(settings));
|
|
111
|
+
if (list.some((e) => inst.norm(e) === inst.norm(dir))) return { state: 'ok', note: 'kenaz-peer plugin is registered' };
|
|
112
|
+
if (!register || !fs.existsSync(dir)) return { state: 'missing', note: 'kenaz-peer plugin is not registered in ' + settings + (fs.existsSync(dir) ? ' (run `kenaz ygg join <key>` or `kenaz install`)' : ' (run `npx kenaz@latest install`)') };
|
|
113
|
+
inst.registerPlugin(settings, dir, false);
|
|
114
|
+
return { state: 'added', note: 'registered the kenaz-peer plugin in ' + settings + '; restart Claude Code (new session) for it to load, then run /Ansuz for idle push' };
|
|
115
|
+
} catch (e) { return { state: 'error', note: 'could not check the kenaz-peer plugin: ' + e.message }; }
|
|
116
|
+
}
|
|
117
|
+
|
|
89
118
|
function renderStatus(s) {
|
|
90
119
|
const L = [
|
|
91
120
|
'Yggdrasil room: ' + s.room,
|
|
@@ -102,14 +131,19 @@ function renderStatus(s) {
|
|
|
102
131
|
'Last poll: ' + (s.last_poll_at ? s.last_poll_at + (s.last_poll_ok === false ? ' (FAILED)' : ' (ok)') : 'none yet'),
|
|
103
132
|
'Delivery: ' + s.delivery_mode + (s.sessions.length ? '' : ' (no live sessions)'),
|
|
104
133
|
];
|
|
105
|
-
for (const x of s.sessions) L.push('
|
|
134
|
+
for (const x of s.sessions) L.push(' ' + (x.name || x.agent) + ' (' + String(x.session_id).slice(0, 8) + ') - ' + x.delivery_mode);
|
|
106
135
|
if (s.created) {
|
|
107
136
|
L.push('', 'New team key (shown once, save it): ' + s.created.key + (s.created.team_name ? ' [' + s.created.team_name + ']' : ''));
|
|
108
137
|
L.push('share with teammates: kenaz ygg join ' + s.created.key);
|
|
109
138
|
}
|
|
110
139
|
if (s.same_name_warning) L.push('WARNING: ' + s.same_name_warning);
|
|
111
140
|
if (s.room_warning) L.push('WARNING: ' + s.room_warning);
|
|
112
|
-
|
|
141
|
+
const pl = s.plugin;
|
|
142
|
+
if (pl && ['added', 'missing', 'error'].includes(pl.state)) L.push('Push plugin: ' + pl.note);
|
|
143
|
+
// The server cannot know whether the plugin is registered; when this machine knows it is, do not repeat "register it".
|
|
144
|
+
const stale = pl && (pl.state === 'ok' || pl.state === 'added') && /register the kenaz-peer plugin|may not be registered/.test(s.delivery_note || '');
|
|
145
|
+
if (stale) L.push('Note: No push channel yet: the kenaz-peer plugin is registered but only serves Ansuz sessions. Run /Ansuz or /kenaz:Ansuz in this session' + (pl.state === 'added' ? ' after restarting Claude Code' : '') + ' for idle push; until then messages arrive on your next prompt.');
|
|
146
|
+
else if (s.delivery_note) L.push('Note: ' + s.delivery_note);
|
|
113
147
|
return L.join('\n');
|
|
114
148
|
}
|
|
115
149
|
|
|
@@ -200,7 +234,7 @@ async function leave(dir) {
|
|
|
200
234
|
}
|
|
201
235
|
|
|
202
236
|
async function main(argv) {
|
|
203
|
-
const rest = []; let dir = process.env.CLAUDE_PROJECT_DIR || process.cwd(); let scope = 'project'; let json = false; let name = '';
|
|
237
|
+
const rest = []; let dir = process.env.CLAUDE_PROJECT_DIR || process.cwd(); let scope = 'project'; let json = false; let name = ''; let home = os.homedir();
|
|
204
238
|
for (let i = 0; i < argv.length; i++) {
|
|
205
239
|
const a = argv[i];
|
|
206
240
|
if (a === '--project-path' && argv[i + 1]) dir = argv[++i];
|
|
@@ -208,6 +242,7 @@ async function main(argv) {
|
|
|
208
242
|
else if (a === '--global') scope = 'global';
|
|
209
243
|
else if (a === '--project') scope = 'project';
|
|
210
244
|
else if (a === '--json') json = true;
|
|
245
|
+
else if (a === '--home' && argv[i + 1]) home = argv[++i];
|
|
211
246
|
else if (a === '--name' && argv[i + 1]) name = argv[++i];
|
|
212
247
|
else if (a.startsWith('--name=')) name = a.slice(7);
|
|
213
248
|
else rest.push(a);
|
|
@@ -221,6 +256,7 @@ async function main(argv) {
|
|
|
221
256
|
else if (sub === 'leave') s = await leave(dir);
|
|
222
257
|
else if (sub === 'status') s = await status(dir);
|
|
223
258
|
else throw new Error('unknown ygg subcommand: ' + sub + ' (join <key> | create [--name N] | status | leave)');
|
|
259
|
+
s.plugin = peerPlugin(path.resolve(home), sub === 'join' || sub === 'create');
|
|
224
260
|
} catch (e) {
|
|
225
261
|
console.error('ygg: ' + e.message + (/fetch failed|timeout|abort/i.test(e.message) ? ' (is the Kenaz backend running on ' + BASE() + '?)' : ''));
|
|
226
262
|
process.exit(1);
|
|
@@ -228,5 +264,5 @@ async function main(argv) {
|
|
|
228
264
|
console.log(json ? JSON.stringify(s, null, 2) : renderStatus(s));
|
|
229
265
|
}
|
|
230
266
|
|
|
231
|
-
module.exports = { nodeIsOnline, maskKey, buildStatus, renderStatus, join, createTeam };
|
|
267
|
+
module.exports = { peerPlugin, nodeIsOnline, maskKey, buildStatus, renderStatus, join, createTeam };
|
|
232
268
|
if (require.main === module) main(process.argv.slice(2));
|
|
@@ -29,10 +29,10 @@ async function withBackend(persist, fn) {
|
|
|
29
29
|
try { return await fn(srv.address().port); } finally { srv.close(); }
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
function cli(port, dir) {
|
|
32
|
+
function cli(port, dir, home = fs.mkdtempSync(path.join(os.tmpdir(), 'ygg-home-'))) { // never the real ~/.claude
|
|
33
33
|
return new Promise((resolve) => {
|
|
34
|
-
const c = spawn(process.execPath, [YGG, 'join', 'ygk_abc', '--project-path', dir],
|
|
35
|
-
{ env: Object.assign({}, process.env, { KENAZ_HTTP_PORT: String(port) }) });
|
|
34
|
+
const c = spawn(process.execPath, [YGG, 'join', 'ygk_abc', '--project-path', dir].concat(['--home', home]),
|
|
35
|
+
{ env: Object.assign({}, process.env, { KENAZ_HTTP_PORT: String(port), KENAZ_CORE_DIR: CORE }) });
|
|
36
36
|
let out = ''; let err = '';
|
|
37
37
|
c.stdout.on('data', (d) => { out += d; }); c.stderr.on('data', (d) => { err += d; });
|
|
38
38
|
c.on('close', (code) => resolve({ code, out, err }));
|
|
@@ -70,3 +70,63 @@ test('join refuses when the backend reports no effective_project_key (nothing to
|
|
|
70
70
|
assert.match(r.err, /cannot work out this project's room/);
|
|
71
71
|
} finally { srv.close(); }
|
|
72
72
|
});
|
|
73
|
+
|
|
74
|
+
// TASK_2461: join leaves the node push-ready (kenaz-peer registered), touching only the --home it is given.
|
|
75
|
+
// a temp core: the checkout may itself be a .kenaz-worktrees path, which the installer refuses to register
|
|
76
|
+
const CORE = fs.mkdtempSync(path.join(os.tmpdir(), 'ygg2461-core-'));
|
|
77
|
+
const CORE_PEER = path.join(CORE, 'plugins', 'kenaz-peer');
|
|
78
|
+
fs.mkdirSync(CORE_PEER, { recursive: true });
|
|
79
|
+
function tempHome(settings) {
|
|
80
|
+
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'ygg2461-'));
|
|
81
|
+
fs.mkdirSync(path.join(home, '.claude'));
|
|
82
|
+
const f = path.join(home, '.claude', 'settings.json');
|
|
83
|
+
fs.writeFileSync(f, settings);
|
|
84
|
+
return { home, f };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
test('join registers a missing kenaz-peer in the temp settings.json, names the restart, and is idempotent', async () => {
|
|
88
|
+
const { home, f } = tempHome(JSON.stringify({ env: { CLAUDE_CODE_PLUGIN_DIRS: '/other/plugin' } }));
|
|
89
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ygg2461-'));
|
|
90
|
+
await withBackend(true, async (port) => {
|
|
91
|
+
const r = await cli(port, dir, home);
|
|
92
|
+
assert.strictEqual(r.code, 0, r.err);
|
|
93
|
+
assert.ok(JSON.parse(fs.readFileSync(f, 'utf8')).env.CLAUDE_CODE_PLUGIN_DIRS.split(path.delimiter).includes(CORE_PEER));
|
|
94
|
+
assert.match(r.out, /restart Claude Code/);
|
|
95
|
+
const after = fs.readFileSync(f, 'utf8');
|
|
96
|
+
const r2 = await cli(port, dir, home);
|
|
97
|
+
assert.strictEqual(r2.code, 0, r2.err);
|
|
98
|
+
assert.strictEqual(fs.readFileSync(f, 'utf8'), after);
|
|
99
|
+
assert.doesNotMatch(r2.out, /restart Claude Code/);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('join leaves settings.json byte-identical when kenaz-peer is already registered', async () => {
|
|
104
|
+
const txt = JSON.stringify({ env: { CLAUDE_CODE_PLUGIN_DIRS: CORE_PEER } });
|
|
105
|
+
const { home, f } = tempHome(txt);
|
|
106
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ygg2461-'));
|
|
107
|
+
await withBackend(true, async (port) => {
|
|
108
|
+
const r = await cli(port, dir, home);
|
|
109
|
+
assert.strictEqual(r.code, 0, r.err);
|
|
110
|
+
assert.strictEqual(fs.readFileSync(f, 'utf8'), txt);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('rendered status: registered but not Ansuz never says to register the plugin; unregistered says what to do', () => {
|
|
115
|
+
const { renderStatus, buildStatus } = require('./ygg.js');
|
|
116
|
+
const base = buildStatus({ effective_project_key: 'r', node_name: 'n', enabled: true, collaboration_projects: ['r'] }, { connected: true, nodes: [] },
|
|
117
|
+
{ delivery_mode: 'pull-on-prompt', delivery_note: 'No push channel. No kenaz-peer plugin heartbeat seen: register the kenaz-peer plugin, then run /Ansuz' });
|
|
118
|
+
const ok = renderStatus(Object.assign({}, base, { plugin: { state: 'ok', note: 'x' } }));
|
|
119
|
+
assert.doesNotMatch(ok, /register the kenaz-peer plugin/);
|
|
120
|
+
assert.match(ok, /only serves Ansuz sessions/);
|
|
121
|
+
const miss = renderStatus(Object.assign({}, base, { plugin: { state: 'missing', note: 'kenaz-peer plugin is not registered in S (run `kenaz ygg join <key>` or `kenaz install`)' } }));
|
|
122
|
+
assert.match(miss, /Push plugin:.*not registered/);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('TASK_2463: rendered status shows each session as name (short id) - mode', () => {
|
|
126
|
+
const { renderStatus, buildStatus } = require('./ygg.js');
|
|
127
|
+
const st = buildStatus({ effective_project_key: 'r', node_name: 'n', enabled: true, collaboration_projects: ['r'] }, { connected: true, nodes: [] },
|
|
128
|
+
{ delivery_mode: 'push', sessions: [{ session_id: 'abcdef123456', agent: 'claude', name: 'my-work', delivery_mode: 'push' }, { session_id: 'zzzzzzzz9999', agent: 'codex', delivery_mode: 'pull-on-prompt' }] });
|
|
129
|
+
const out = renderStatus(st);
|
|
130
|
+
assert.match(out, /^ {2}my-work \(abcdef12\) - push$/m);
|
|
131
|
+
assert.match(out, /^ {2}codex \(zzzzzzzz\) - pull-on-prompt$/m);
|
|
132
|
+
});
|
|
@@ -2,14 +2,17 @@ import { test, expect, mock } from 'claude-code/testing'
|
|
|
2
2
|
|
|
3
3
|
const msg = { id: 'm1', framed: '[Peer message] hello' }
|
|
4
4
|
|
|
5
|
-
function setup(on: any, rows: () => unknown[]) {
|
|
5
|
+
function setup(on: any, rows: () => unknown[], marker?: () => string | null) {
|
|
6
6
|
const calls: string[] = []
|
|
7
7
|
let delivered = false
|
|
8
8
|
on('session.start', async (_$: any, e: any) => ({ cwd: e.cwd }))
|
|
9
9
|
on('session.id', async () => ({ value: 'sess-1' }))
|
|
10
10
|
on('session.cwd', async () => ({ value: '/tmp/proj' }))
|
|
11
11
|
on('session.messages', async () => ({ value: rows() }))
|
|
12
|
-
on('fs.read', async (
|
|
12
|
+
on('fs.read', async (_$: any, e: any) => {
|
|
13
|
+
const m = marker?.()
|
|
14
|
+
return String(e.path ?? e.file ?? '').endsWith('/sess-1.json') && m ? { value: m } : { deny: 'no pin' }
|
|
15
|
+
})
|
|
13
16
|
on('http.fetch', async (_$: any, e: any) => {
|
|
14
17
|
const url = String(e.url)
|
|
15
18
|
calls.push(`${e.init?.method ?? 'GET'} ${url}`)
|
|
@@ -144,3 +147,57 @@ test('plugin-namespaced /kenaz:Ansuz also activates delivery', async ($, on) =>
|
|
|
144
147
|
expect(calls[0]).toContain('POST http://127.0.0.1:18080/api/peer/mod-heartbeat')
|
|
145
148
|
expect(submits.length).toBe(1)
|
|
146
149
|
})
|
|
150
|
+
|
|
151
|
+
// TASK_2465: collaboration opt-in (marker written by peer-session-fill.js, or KENAZ_PEER_OPTIN=1 from kenaz launch).
|
|
152
|
+
const PLAIN = [{ role: 'user', content: 'hello there' }]
|
|
153
|
+
const MARKER_ENV = { CLAUDE_CODE_ENTRYPOINT: 'cli', KENAZ_PEER_OPTIN_DIR: '/m' }
|
|
154
|
+
|
|
155
|
+
test('TASK_2465 privacy: plain session without opt-in never heartbeats, peeks or submits', async ($, on) => {
|
|
156
|
+
const clock = mock.clock(on)
|
|
157
|
+
mock.env(on, MARKER_ENV)
|
|
158
|
+
const { calls, submits } = setup(on, () => PLAIN, () => null)
|
|
159
|
+
await $.session.start(START)
|
|
160
|
+
await clock.advance(15_000)
|
|
161
|
+
expect(calls.length).toBe(0)
|
|
162
|
+
expect(submits.length).toBe(0)
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
test('TASK_2465: opt-in marker makes a plain session heartbeat and receive push', async ($, on) => {
|
|
166
|
+
const clock = mock.clock(on)
|
|
167
|
+
mock.env(on, MARKER_ENV)
|
|
168
|
+
const { calls, submits } = setup(on, () => PLAIN, () => JSON.stringify({ ts: Date.now() }))
|
|
169
|
+
await $.session.start(START)
|
|
170
|
+
await clock.advance(5_000)
|
|
171
|
+
await clock.advance(5_000)
|
|
172
|
+
expect(calls[0]).toContain('POST http://127.0.0.1:18080/api/peer/mod-heartbeat')
|
|
173
|
+
expect(submits.length).toBe(1)
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
test('TASK_2465: an expired marker is not an opt-in', async ($, on) => {
|
|
177
|
+
const clock = mock.clock(on)
|
|
178
|
+
mock.env(on, MARKER_ENV)
|
|
179
|
+
const { calls, submits } = setup(on, () => PLAIN, () => JSON.stringify({ ts: Date.now() - 25 * 3600 * 1000 }))
|
|
180
|
+
await $.session.start(START)
|
|
181
|
+
await clock.advance(15_000)
|
|
182
|
+
expect(calls.length).toBe(0)
|
|
183
|
+
expect(submits.length).toBe(0)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
test('TASK_2465: KENAZ_PEER_OPTIN=1 does not opt in a claude -p worker (sdk-cli)', async ($, on) => {
|
|
187
|
+
const clock = mock.clock(on)
|
|
188
|
+
mock.env(on, { CLAUDE_CODE_ENTRYPOINT: 'sdk-cli', KENAZ_PEER_OPTIN: '1' })
|
|
189
|
+
const { calls } = setup(on, () => PLAIN)
|
|
190
|
+
await $.session.start(START)
|
|
191
|
+
await clock.advance(15_000)
|
|
192
|
+
expect(calls.length).toBe(0)
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
test('TASK_2465: KENAZ_PEER_OPTIN=1 (kenaz launch) opts a plain session in', async ($, on) => {
|
|
196
|
+
const clock = mock.clock(on)
|
|
197
|
+
mock.env(on, { CLAUDE_CODE_ENTRYPOINT: 'cli', KENAZ_PEER_OPTIN: '1' })
|
|
198
|
+
const { calls } = setup(on, () => PLAIN)
|
|
199
|
+
await $.session.start(START)
|
|
200
|
+
await clock.advance(5_000)
|
|
201
|
+
await clock.advance(5_000)
|
|
202
|
+
expect(calls[0]).toContain('POST http://127.0.0.1:18080/api/peer/mod-heartbeat')
|
|
203
|
+
})
|