kenaz 0.3.18 → 0.3.20
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/agent-worker.md +1 -1
- package/core/.claude/commands/architect.md +1 -2
- package/core/.claude/commands/spec.md +1 -1
- package/core/.claude/commands//347/224/242/345/223/201.md +1 -1
- package/core/.claude/hooks/__tests__/task-1977-dedupe-guard.test.js +8 -8
- package/core/.claude/skills/architect_agent/SKILL.md +11 -92
- package/core/.claude/skills/assessment_agent/SKILL.md +0 -1
- package/core/.claude/skills/backend_engineer/SKILL.md +3 -6
- package/core/.claude/skills/code_reviewer/SKILL.md +1 -1
- package/core/.claude/skills/code_reviewer/patterns.yaml +1 -1
- package/core/.claude/skills/designer/SKILL.md +3 -6
- package/core/.claude/skills/dev_agent/SKILL.md +30 -491
- package/core/.claude/skills/developer/SKILL.md +3 -6
- package/core/.claude/skills/feature_agent/SKILL.md +3 -17
- package/core/.claude/skills/product_manager/SKILL.md +29 -1064
- package/core/.claude/skills/qa_engineer/SKILL.md +12 -17
- package/core/.claude/skills/spec_optimizer/SKILL.md +25 -275
- package/core/.claude/skills/sre_agent/SKILL.md +3 -6
- package/core/.collaboration/README.md +2 -3
- package/core/.collaboration/core/task-api.js +5 -138
- package/core/.collaboration/docs/CLI.md +0 -2
- package/core/.failure/INDEX.yaml +6 -40
- package/core/.failure/agent-execution/FAIL-013-search-cwd-drift-and-case-mismatch.yaml +1 -1
- package/core/.failure/tools/README.md +9 -227
- package/core/.formation/bifrost/launch.js +1 -6
- package/core/.formation/bifrost/launch.test.js +1 -1
- package/core/.formation/bifrost/shared/ansuz-boot-policy.js +3 -3
- package/core/.formation/bifrost/shared/ansuz-boot-policy.test.js +1 -2
- package/core/.formation/tools/__tests__/arms.test.js +41 -0
- package/core/.formation/tools/__tests__/diagnose-bug.test.js +10 -0
- package/core/.formation/tools/__tests__/m1-hmap-convid.test.js +1 -124
- package/core/.formation/tools/arms.js +48 -0
- package/core/.formation/tools/dispatch.js +24 -15
- package/core/.formation/tools/kenaz-stats.js +32 -8
- package/core/.formation/tools/kenaz-stats.test.js +15 -2
- package/core/.formation/tools/loader.js +17 -140
- package/core/.formation/tools/post-complete.js +0 -31
- package/core/.formation/tools/prompt.js +16 -1
- package/core/.formation/tools/prompt.test.js +20 -0
- package/core/.rule/INDEX.yaml +0 -18
- package/core/.rule/general/agent-workflow.yaml +14 -164
- package/core/.rule/general/project-self-containment.yaml +8 -19
- package/core/.rule/general/rust-verify-target.yaml +6 -5
- package/core/.rule/general/synapse-code-intelligence.yaml +3 -3
- package/core/.specialist/Amelia/capability.yaml +12 -12
- package/core/.specialist/Amelia/workflow.yaml +2 -2
- package/core/.specialist/Ansuz/SYSTEM_DOC.md +20 -578
- package/core/.specialist/Ansuz/cognitive_principles.yaml +2 -3
- package/core/.specialist/Ansuz/concepts/boot_rules_summary.yaml +0 -1
- package/core/.specialist/Ansuz/concepts/deep_knowledge_pointers.yaml +1 -2
- package/core/.specialist/Ansuz/core.yaml +6 -108
- package/core/.specialist/Ansuz/guards/lesson_guards.yaml +0 -3
- package/core/.specialist/Ansuz/identity_contract.yaml +0 -1
- package/core/.specialist/Ansuz/knowledge.yaml +0 -4
- package/core/.specialist/Ansuz/rule.yaml +0 -1
- package/core/.specialist/Ansuz/tools/handoff-session-identity.test.js +6 -150
- package/core/.specialist/tests/runtime-discovery/README.md +1 -1
- package/core/.specialist/tools/__tests__/task-2374-usage-counter.test.js +1 -12
- package/core/.specialist/tools/ansuz-boot-fast.js +7 -4
- package/core/.specialist/tools/ansuz-boot-fast.test.js +10 -3
- package/core/.specialist/tools/prompt-files.test.js +1 -0
- package/core/.specialist/tools/task-bridge.js +1 -66
- package/core/.system/README.md +11 -552
- package/core/.system/collaboration/README.md +10 -174
- package/core/CLAUDE.md +1 -6
- package/core/KENAZ_CORE_VERSION +1 -1
- package/core/dev/scripts/js-yaml-resolution.test.js +1 -1
- package/core/dev/scripts/peer-identity-e2e.js +402 -0
- package/core/dev/scripts/peer-identity-e2e.md +47 -0
- package/core/dev/scripts/relay-admin.js +94 -0
- package/core/dev/scripts/relay-admin.test.js +131 -0
- package/core/dev/scripts/run-rust-tests.js +9 -45
- package/core/dev/scripts/rust-verify-worktree.js +4 -4
- package/core/dev/scripts/verify-target-dir.js +6 -6
- package/core/plugins/kenaz/INVENTORY.md +8 -8
- package/core/plugins/kenaz/README.md +10 -0
- package/core/plugins/kenaz/commands/architect.md +1 -2
- package/core/plugins/kenaz/commands/spec.md +1 -1
- package/core/plugins/kenaz/commands//347/224/242/345/223/201.md +1 -1
- package/core/plugins/kenaz/guidance.md +1 -1
- package/core/plugins/kenaz/hooks/kenaz-hook.js +2 -1
- package/core/plugins/kenaz/hooks/sdk-gate.js +9 -0
- package/core/plugins/kenaz/scripts/kenaz.test.js +15 -3
- package/core/plugins/kenaz/scripts/sync.js +1 -1
- package/core/plugins/kenaz/scripts/ygg.js +8 -0
- package/core/plugins/kenaz/scripts/ygg.test.js +72 -0
- package/core/plugins/kenaz/skills/architect_agent/SKILL.md +11 -92
- package/core/plugins/kenaz/skills/assessment_agent/SKILL.md +0 -1
- package/core/plugins/kenaz/skills/backend_engineer/SKILL.md +3 -6
- package/core/plugins/kenaz/skills/code_reviewer/SKILL.md +1 -1
- package/core/plugins/kenaz/skills/code_reviewer/patterns.yaml +1 -1
- package/core/plugins/kenaz/skills/designer/SKILL.md +3 -6
- package/core/plugins/kenaz/skills/dev_agent/SKILL.md +30 -491
- package/core/plugins/kenaz/skills/developer/SKILL.md +3 -6
- package/core/plugins/kenaz/skills/feature_agent/SKILL.md +3 -17
- package/core/plugins/kenaz/skills/product_manager/SKILL.md +29 -1064
- package/core/plugins/kenaz/skills/qa_engineer/SKILL.md +12 -17
- package/core/plugins/kenaz/skills/spec_optimizer/SKILL.md +25 -275
- package/core/plugins/kenaz/skills/sre_agent/SKILL.md +3 -6
- package/core/plugins/kenaz-peer/hooks/register.test.ts +32 -0
- package/core/plugins/kenaz-peer/hooks/register.ts +9 -0
- package/package.json +6 -6
- package/core/.claude/commands/archive.md +0 -24
- package/core/.claude/commands/init.md +0 -112
- package/core/.claude/commands/legacy_dev.md +0 -18
- package/core/.claude/commands/product-review.md +0 -131
- package/core/.claude/commands/strategy.md +0 -141
- package/core/.claude/commands/sunny.md +0 -1
- package/core/.claude/commands//347/224/242/345/223/201/346/252/242/350/246/226.md +0 -129
- package/core/.claude/commands//351/200/262/345/272/246.md +0 -15
- package/core/.claude/hooks/__tests__/substrate.test.js +0 -122
- package/core/.claude/hooks/_substrate-paths.js +0 -95
- package/core/.claude/hooks/context-check.js +0 -51
- package/core/.claude/hooks/fatigue-check.js +0 -52
- package/core/.claude/hooks/handoff-snapshot-on-commit.js +0 -154
- package/core/.claude/hooks/handoff-snapshot-on-question.js +0 -158
- package/core/.claude/hooks/handoff-staleness.js +0 -197
- package/core/.claude/hooks/jev-thalamus-context.js +0 -17
- package/core/.claude/hooks/jev-thalamus-context.test.js +0 -41
- package/core/.claude/hooks/lease-release.js +0 -119
- package/core/.claude/hooks/lease-touch.js +0 -96
- package/core/.claude/hooks/reflex-feedback.js +0 -5
- package/core/.claude/hooks/session-init.ps1 +0 -49
- package/core/.claude/hooks/sound-event.js +0 -70
- package/core/.claude/hooks/sound-event.sh +0 -37
- package/core/.claude/hooks/stream-delta.js +0 -169
- package/core/.claude/no-hooks.settings.json +0 -3
- package/core/.claude/skills/legacy_dev/SKILL.md +0 -202
- package/core/.claude/skills/sunny/SKILL.md +0 -74
- package/core/.collaboration/cli/commands/assign.js +0 -39
- package/core/.collaboration/cli/commands/claim.js +0 -33
- package/core/.collaboration/cli/commands/complete.js +0 -44
- package/core/.collaboration/cli/commands/create.js +0 -110
- package/core/.collaboration/cli/commands/get.js +0 -83
- package/core/.collaboration/cli/commands/index.js +0 -52
- package/core/.collaboration/cli/commands/list.js +0 -51
- package/core/.collaboration/cli/commands/lock.js +0 -45
- package/core/.collaboration/cli/commands/rebuild.js +0 -39
- package/core/.collaboration/cli/commands/search.js +0 -61
- package/core/.collaboration/cli/commands/stats.js +0 -61
- package/core/.collaboration/cli/commands/transfer.js +0 -41
- package/core/.collaboration/cli/commands/unlock.js +0 -48
- package/core/.collaboration/cli/formatters/help-formatter.js +0 -70
- package/core/.collaboration/cli/formatters/index.js +0 -7
- package/core/.collaboration/cli/task-cli.js +0 -46
- package/core/.collaboration/cli/utils/arg-parser.js +0 -74
- package/core/.collaboration/cli/utils/git-user.js +0 -25
- package/core/.collaboration/cli/utils/index.js +0 -9
- package/core/.collaboration/cli/utils/output.js +0 -10
- package/core/.collaboration/config/default.yaml +0 -189
- package/core/.collaboration/core/conflict-resolver.js +0 -461
- package/core/.collaboration/core/conflict-resolver.test.js +0 -389
- package/core/.collaboration/core/migrate-legacy.js +0 -260
- package/core/.collaboration/docs/API.md +0 -787
- package/core/.collaboration/docs/ARCHITECTURE.md +0 -596
- package/core/.collaboration/docs/FAQ.md +0 -695
- package/core/.collaboration/docs/MIGRATION.md +0 -804
- package/core/.collaboration/docs/examples/README.md +0 -320
- package/core/.collaboration/docs/examples/advanced-queries.js +0 -261
- package/core/.collaboration/docs/examples/basic-usage.js +0 -184
- package/core/.collaboration/docs/examples/team-workflow.js +0 -352
- package/core/.failure/agent-execution/FAIL-001-agent-no-output.yaml +0 -391
- package/core/.failure/agent-execution/FAIL-007-pm-lead-wrong-team-invocation.yaml +0 -0
- package/core/.failure/agent-execution/FAIL-010-phase0-poem-format-skipped.yaml +0 -28
- package/core/.failure/agent-execution/FAIL-010-skipping-cognitive-checkpoint-after-verification.yaml +0 -59
- package/core/.failure/agent-execution/FAIL-012-grep-shortcut-during-investigation.yaml +0 -85
- package/core/.failure/architecture/FAIL-006-python-version-compatibility.yaml +0 -445
- package/core/.failure/pipeline/FAIL-003-queue-pollution.yaml +0 -335
- package/core/.failure/pipeline/README.md +0 -95
- package/core/.failure/retired/FAIL-009-skipping-synapse-pulse-before-code-change.yaml +0 -49
- package/core/.failure/tools/USAGE_GUIDE.md +0 -323
- package/core/.failure/tools/check_failures.ps1 +0 -387
- package/core/.failure/tools/log_failure.ps1 +0 -474
- package/core/.formation/.episode-input.json +0 -10
- package/core/.formation/.merge-lock +0 -1
- package/core/.formation/lead.yaml +0 -118
- package/core/.formation/legacy.bak/designer.yaml +0 -57
- package/core/.formation/legacy.bak/pm.yaml +0 -57
- package/core/.formation/legacy.bak/qa.yaml +0 -57
- package/core/.formation/legacy.bak/review.yaml +0 -62
- package/core/.formation/legacy.bak/scheduler.yaml +0 -129
- package/core/.formation/legacy.bak/sre.yaml +0 -57
- package/core/.formation/role-map.yaml +0 -274
- package/core/.formation/roles/_shared/modules/code_review.yaml +0 -98
- package/core/.formation/roles/_shared/modules/security.yaml +0 -103
- package/core/.formation/roles/_shared/modules/testing.yaml +0 -14
- package/core/.formation/roles/backend/role.yaml +0 -93
- package/core/.formation/roles/code_reviewer/role.yaml +0 -102
- package/core/.formation/roles/database_optimizer/role.yaml +0 -107
- package/core/.formation/roles/designer/role.yaml +0 -66
- package/core/.formation/roles/developer/role.yaml +0 -75
- package/core/.formation/roles/frontend/role.yaml +0 -134
- package/core/.formation/roles/incident_response/role.yaml +0 -89
- package/core/.formation/roles/qa/role.yaml +0 -89
- package/core/.formation/roles/security/role.yaml +0 -87
- package/core/.formation/roles/sre/role.yaml +0 -66
- package/core/.formation/skills/coordinator.yaml +0 -52
- package/core/.formation/solo/diagnosis.yaml +0 -78
- package/core/.formation/solo/strategy.yaml +0 -121
- package/core/.formation/tests/hello.txt +0 -2
- package/core/.formation/tools/coordinator.js +0 -350
- package/core/.formation/tools/jev-correction.js +0 -214
- package/core/.formation/tools/jev-correction.test.js +0 -178
- package/core/.formation/tools/log-reader.js +0 -232
- package/core/.formation/tools/revert-daemon-state.js +0 -162
- package/core/.formation/tools/test-drift.js +0 -148
- package/core/.formation/unit/heimdall.yaml +0 -96
- package/core/.formation/unit/plan.yaml +0 -67
- package/core/.formation/unit/think.yaml +0 -58
- package/core/.kenaz/MUNINN_SNAPSHOT9_EPISODE.json +0 -32
- package/core/.kenaz/MUNINN_SNAPSHOT9_RUNE.json +0 -23
- package/core/.kenaz/MUNINN_SNAPSHOT9_SUMMARY.md +0 -89
- package/core/.kenaz/ash_TASK_1428-20260507.json +0 -14
- package/core/.kenaz/muninn_episode_write.ps1 +0 -17
- package/core/.kenaz/muninn_snapshot11_rune_candidates.md +0 -63
- package/core/.kenaz/muninn_snapshot1_rune_candidates.md +0 -49
- package/core/.kenaz/runes_TASK_1428.json +0 -57
- package/core/.kenaz/tmp_heimdall_proposal_8d3e392.json +0 -13
- package/core/.kenaz/tmp_props.json +0 -1
- package/core/.kenaz/tmp_sounddrop_t1_prompt.txt +0 -371
- package/core/.rule/MECHANIZED.md +0 -61
- package/core/.rule/general/cortex-memory-recall.yaml +0 -446
- package/core/.rule/general/pipeline.yaml +0 -246
- package/core/.rule/general/sunny-access-control.yaml +0 -60
- package/core/.rule/general/system-invariants.yaml +0 -231
- package/core/.rule/pipeline/PPL-002-completed-task-archival.yaml +0 -528
- package/core/.rule/pipeline/auto-update.yaml +0 -282
- package/core/.rule/pipeline/task-execution.yaml +0 -365
- package/core/.specialist/Ansuz/concepts/phase_protocol.yaml +0 -65
- package/core/.specialist/Ansuz/experiments/boot_ablation_replay.js +0 -197
- package/core/.specialist/Ansuz/tools/bootstrap-anchors.js +0 -289
- package/core/.specialist/Ansuz/workflow.yaml +0 -1225
- package/core/.specialist/experiments/rg_benchmark.js +0 -523
- package/core/.specialist/experiments/rg_benchmark_results.json +0 -27
- package/core/.specialist/tools/jev-optional-context.js +0 -107
- package/core/.specialist/tools/jev-optional-context.test.js +0 -152
- package/core/.specialist/tools/memory-action-log.js +0 -839
- package/core/.specialist/tools/phase-gate.js +0 -3534
- package/core/.specialist/tools/phase-gate.test.js +0 -85
- package/core/.specialist/tools/precompute-runner.js +0 -444
- package/core/.specialist/tools/rule-engine.js +0 -621
- package/core/.specialist/tools/task-bridge-jev.test.js +0 -170
- package/core/.synapse/memory-shared.json.sync +0 -1
- package/core/.system/agent-skills/README.md +0 -724
- package/core/.system/cli-tools/README.md +0 -724
- package/core/.system/cortex/MODULES.md +0 -1046
- package/core/.system/dashboard/FRONTEND.md +0 -812
- package/core/.system/dashboard/README.md +0 -737
- package/core/.system/failure/README.md +0 -723
- package/core/.system/formation/LEGACY_SKILLS.md +0 -974
- package/core/.system/formation/POST_COMPLETE.md +0 -602
- package/core/.system/formation/README.md +0 -1094
- package/core/.system/formation/TOOLS_REFERENCE.md +0 -1145
- package/core/.system/kenaz/README.md +0 -787
- package/core/.system/memory/README.md +0 -719
- package/core/.system/pipeline/README.md +0 -712
- package/core/.system/rule/README.md +0 -794
- package/core/.system/sunny/README.md +0 -476
- package/core/.system/sunny/RULES.json +0 -89
- package/core/.system/sunny/knowledge/INDEX.md +0 -37
- package/core/.system/sunny/knowledge/agent-teams-api.md +0 -0
- package/core/.system/sunny/knowledge/ansuz-specialist.md +0 -500
- package/core/.system/sunny/knowledge/claude-cli-spawn.md +0 -177
- package/core/.system/sunny/knowledge/collaboration-api.md +0 -925
- package/core/.system/sunny/teammates/README.md +0 -107
- package/core/.system/sunny/teammates/how_to_use.yaml +0 -101
- package/core/.system/sunny/teammates/tools/headless/launcher.js +0 -318
- package/core/.system/sunny/teammates/tools/powershell/launcher.js +0 -241
- package/core/.system/sunny/teammates/tools/powershell/start.bat +0 -50
- package/core/.system/sunny/teammates/tools/utils/env.js +0 -96
- package/core/.system/sunny/teammates/tools/utils/format-output.js +0 -329
- package/core/.system/sunny/tools.json +0 -101
- package/core/dev/scripts/cleanup-queue.js +0 -184
- package/core/dev/scripts/cleanup_queue.js +0 -86
- package/core/dev/scripts/legacy/README.md +0 -39
- package/core/dev/scripts/legacy/dev_agent_scheduler.ps1.bak +0 -536
- package/core/dev/scripts/legacy/multi_agent_scheduler.ps1.bak +0 -1978
- package/core/dev/scripts/matrix/CLI_README.md +0 -214
- package/core/dev/scripts/matrix/README.md +0 -310
- package/core/dev/scripts/matrix/package-lock.json +0 -4266
- package/core/dev/scripts/matrix/package.json +0 -55
- package/core/dev/scripts/matrix/src/abbreviations.ts +0 -295
- package/core/dev/scripts/matrix/src/cli/commands/convert.ts +0 -211
- package/core/dev/scripts/matrix/src/cli/commands/create.ts +0 -95
- package/core/dev/scripts/matrix/src/cli/commands/delete.ts +0 -102
- package/core/dev/scripts/matrix/src/cli/commands/list-tags.ts.wip +0 -140
- package/core/dev/scripts/matrix/src/cli/commands/manage-tags.ts.wip +0 -173
- package/core/dev/scripts/matrix/src/cli/commands/query.ts +0 -261
- package/core/dev/scripts/matrix/src/cli/commands/read.ts +0 -77
- package/core/dev/scripts/matrix/src/cli/commands/stats.ts.wip +0 -168
- package/core/dev/scripts/matrix/src/cli/commands/tokens.ts.wip +0 -126
- package/core/dev/scripts/matrix/src/cli/commands/update.ts +0 -147
- package/core/dev/scripts/matrix/src/cli/commands/validate.ts +0 -275
- package/core/dev/scripts/matrix/src/cli/index.ts +0 -350
- package/core/dev/scripts/matrix/src/cli/utils/file-handler.ts +0 -72
- package/core/dev/scripts/matrix/src/cli/utils/logger.ts +0 -65
- package/core/dev/scripts/matrix/src/index.ts +0 -62
- package/core/dev/scripts/matrix/src/indexer/tag-indexer.ts +0 -165
- package/core/dev/scripts/matrix/src/parser.ts +0 -281
- package/core/dev/scripts/matrix/src/serializer.ts +0 -151
- package/core/dev/scripts/matrix/src/validator.ts +0 -301
- package/core/dev/scripts/matrix/tests/abbreviations.test.ts +0 -122
- package/core/dev/scripts/matrix/tests/cli.test.ts +0 -177
- package/core/dev/scripts/matrix/tests/convert.test.ts +0 -143
- package/core/dev/scripts/matrix/tests/parser.test.ts +0 -203
- package/core/dev/scripts/matrix/tests/query.test.ts +0 -85
- package/core/dev/scripts/matrix/tests/serializer.test.ts +0 -197
- package/core/dev/scripts/matrix/tests/validate.test.ts +0 -141
- package/core/dev/scripts/matrix/tests/validator.test.ts +0 -167
- package/core/dev/scripts/matrix/tsconfig.json +0 -20
- package/core/dev/scripts/matrix/types/index.d.ts +0 -111
- package/core/dev/scripts/matrix/types/index.d.ts.map +0 -1
- package/core/dev/scripts/matrix/types/index.js +0 -8
- package/core/dev/scripts/matrix/types/index.js.map +0 -1
- package/core/dev/scripts/matrix/types/index.ts +0 -124
- package/core/dev/scripts/memory-usage-report.js +0 -152
- package/core/dev/scripts/probe/ansuz-scaffold-ablation.js +0 -579
- package/core/dev/scripts/probe/ansuz-scaffold-ablation.test.js +0 -244
- package/core/dev/scripts/probe/hook-latency-probe.js +0 -315
- package/core/dev/scripts/probe/hook-latency-probe.test.js +0 -105
- package/core/dev/scripts/probe/prediction-signal-auc.js +0 -483
- package/core/dev/scripts/probe/prediction-signal-auc.test.js +0 -200
- package/core/dev/scripts/test-pty-ws-tunnel.js +0 -229
- package/core/dev/scripts/validate_pipeline.js +0 -197
- package/core/dev/spikes/task2145-evidence-20261004/backend-log-excerpt.txt +0 -56
- package/core/dev/spikes/task2145-evidence-20261004/graph.shadow +0 -0
- package/core/dev/spikes/task2145-evidence-20261004/graph.wal +0 -0
- package/core/dev/spikes/task2145-evidence-20261004/graph_corrupt_suspect.marker +0 -1
- package/core/plugins/kenaz/commands/strategy.md +0 -141
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// TASK_2451: relay-admin.js against a local fixture that mimics the relay admin endpoints
|
|
3
|
+
// (same routes, X-Admin-Key check, approve-by-fingerprint semantics as node_admin.rs). Every
|
|
4
|
+
// command in HELP is executed through the real CLI process.
|
|
5
|
+
const test = require('node:test');
|
|
6
|
+
const assert = require('node:assert');
|
|
7
|
+
const http = require('http');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const { spawn, spawnSync } = require('child_process');
|
|
12
|
+
const { HELP } = require('./relay-admin');
|
|
13
|
+
|
|
14
|
+
const KEY = 'sekret-admin-key';
|
|
15
|
+
const CLI = path.join(__dirname, 'relay-admin.js');
|
|
16
|
+
let server, url, tokens, blocks = [], protect = false;
|
|
17
|
+
|
|
18
|
+
test.before(async () => {
|
|
19
|
+
tokens = [
|
|
20
|
+
{ team_id: 't1', node_name: 'n1', fingerprint: 'AAAA-1111', status: 'pending', created_at: '2026-10-13T00:00:00Z' },
|
|
21
|
+
{ team_id: 't1', node_name: 'n1', fingerprint: 'BBBB-2222', status: 'active', created_at: '2026-10-12T00:00:00Z' },
|
|
22
|
+
];
|
|
23
|
+
server = http.createServer((req, res) => {
|
|
24
|
+
let b = '';
|
|
25
|
+
req.on('data', (c) => (b += c));
|
|
26
|
+
req.on('end', () => {
|
|
27
|
+
const send = (s, o) => { res.statusCode = s; res.end(JSON.stringify(o || {})); };
|
|
28
|
+
if (req.headers['x-admin-key'] !== KEY) return send(401);
|
|
29
|
+
const r = req.url.replace('/api/admin/node-credentials/', '');
|
|
30
|
+
const body = b ? JSON.parse(b) : {};
|
|
31
|
+
if (r === 'pending') return send(200, { pending: tokens.filter((t) => t.status === 'pending') });
|
|
32
|
+
if (r === 'approve') {
|
|
33
|
+
const t = tokens.find((x) => x.status === 'pending' && x.team_id === body.team_id && x.node_name === body.node_name && x.fingerprint === body.fingerprint);
|
|
34
|
+
if (!t) return send(404);
|
|
35
|
+
let n = 0;
|
|
36
|
+
for (const o of tokens) if (o !== t && o.team_id === t.team_id && o.node_name === t.node_name && o.status !== 'revoked') { o.status = 'revoked'; n++; }
|
|
37
|
+
t.status = 'active';
|
|
38
|
+
return send(200, { status: 'active', revoked_others: n });
|
|
39
|
+
}
|
|
40
|
+
if (r === 'revoke') {
|
|
41
|
+
let n = 0;
|
|
42
|
+
for (const o of tokens) if (o.team_id === body.team_id && o.node_name === body.node_name && o.status !== 'revoked') { o.status = 'revoked'; n++; }
|
|
43
|
+
return send(200, { status: 'revoked', revoked: n });
|
|
44
|
+
}
|
|
45
|
+
if (r === 'ban') { blocks.push(body); return send(200, { banned: true, revoked: 1, protect_bound_names: protect }); }
|
|
46
|
+
if (r === 'unban') { blocks = []; return send(200, { unbanned: 1 }); }
|
|
47
|
+
if (r === 'blocked') return send(200, { blocked: blocks.map((b) => ({ team_id: b.team_id, kind: 'name', value: b.node_name, reason: b.reason, created_at: 'now' })) });
|
|
48
|
+
send(404);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
await new Promise((r) => server.listen(0, '127.0.0.1', r));
|
|
52
|
+
url = `http://127.0.0.1:${server.address().port}`;
|
|
53
|
+
});
|
|
54
|
+
test.after(() => server.close());
|
|
55
|
+
|
|
56
|
+
const cli = (args, env = { RELAY_ADMIN_KEY: KEY }) =>
|
|
57
|
+
new Promise((resolve) => {
|
|
58
|
+
const p = spawn(process.execPath, [CLI, '--relay', url, ...args], { env: { ...process.env, RELAY_ADMIN_KEY: '', RELAY_ADMIN_KEY_FILE: '', ...env } });
|
|
59
|
+
let out = ''; p.stdout.on('data', (d) => (out += d)); p.stderr.on('data', (d) => (out += d));
|
|
60
|
+
p.on('close', (code) => resolve({ code, out }));
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('list -> wrong fingerprint refused -> approve activates and reports revoked tokens', async () => {
|
|
64
|
+
let r = await cli(['list']);
|
|
65
|
+
assert.strictEqual(r.code, 0); assert.match(r.out, /t1\tn1\tAAAA-1111/);
|
|
66
|
+
r = await cli(['approve', 't1', 'n1', 'ZZZZ-0000']);
|
|
67
|
+
assert.strictEqual(r.code, 1); assert.match(r.out, /HTTP 404/);
|
|
68
|
+
assert.strictEqual(tokens[0].status, 'pending');
|
|
69
|
+
r = await cli(['approve', 't1', 'n1', 'AAAA-1111']);
|
|
70
|
+
assert.strictEqual(r.code, 0); assert.match(r.out, /revoked 1 other token/);
|
|
71
|
+
assert.strictEqual(tokens[0].status, 'active'); assert.strictEqual(tokens[1].status, 'revoked');
|
|
72
|
+
r = await cli(['list']);
|
|
73
|
+
assert.match(r.out, /no pending/);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('revoke', async () => {
|
|
77
|
+
const r = await cli(['revoke', 't1', 'n1']);
|
|
78
|
+
assert.strictEqual(r.code, 0); assert.match(r.out, /revoked 1 token/);
|
|
79
|
+
assert.strictEqual(tokens[0].status, 'revoked');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('wrong admin key and key file', async () => {
|
|
83
|
+
let r = await cli(['list'], { RELAY_ADMIN_KEY: 'nope' });
|
|
84
|
+
assert.strictEqual(r.code, 1); assert.match(r.out, /wrong admin key/);
|
|
85
|
+
const f = path.join(os.tmpdir(), `relay-admin-key-${process.pid}`);
|
|
86
|
+
fs.writeFileSync(f, KEY + '\n');
|
|
87
|
+
r = await cli(['list'], { RELAY_ADMIN_KEY_FILE: f });
|
|
88
|
+
fs.unlinkSync(f);
|
|
89
|
+
assert.strictEqual(r.code, 0);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('admin key on argv is refused (control: same call via env works)', async () => {
|
|
93
|
+
for (const a of [['--admin-key', KEY, 'list'], ['--key=' + KEY, 'list']]) {
|
|
94
|
+
const r = await cli(a, {});
|
|
95
|
+
assert.strictEqual(r.code, 2); assert.match(r.out, /must not be passed/);
|
|
96
|
+
}
|
|
97
|
+
assert.strictEqual((await cli(['list'])).code, 0);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('missing key refused; help text commands are all executed by this suite', async () => {
|
|
101
|
+
assert.strictEqual((await cli(['list'], {})).code, 2);
|
|
102
|
+
const cmds = [...HELP.matchAll(/^ {2}(list|approve|revoke|ban|unban|blocked)\b/gm)].map((m) => m[1]);
|
|
103
|
+
assert.deepStrictEqual(cmds, ['list', 'approve', 'revoke', 'ban', 'unban', 'blocked']);
|
|
104
|
+
const argsFor = { list: ['list'], approve: ['approve', 't', 'n', 'F'], revoke: ['revoke', 't', 'n'], ban: ['ban', 't', 'n'], unban: ['unban', 't', 'n'], blocked: ['blocked'] };
|
|
105
|
+
for (const c of cmds) assert.notStrictEqual((await cli(argsFor[c])).code, 2, c);
|
|
106
|
+
assert.strictEqual(spawnSync(process.execPath, [CLI, 'bogus']).status, 2);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('ban prints the tokenless-access warning when the flag is off; no warning when on; blocked/unban', async () => {
|
|
110
|
+
protect = false;
|
|
111
|
+
let r = await cli(['ban', 't1', 'evil', 'because']);
|
|
112
|
+
assert.strictEqual(r.code, 0); assert.match(r.out, /banned t1\/evil/);
|
|
113
|
+
assert.match(r.out, /WARNING: protect_bound_names is OFF.*anonymously/);
|
|
114
|
+
assert.strictEqual(blocks[0].reason, 'because');
|
|
115
|
+
protect = true;
|
|
116
|
+
r = await cli(['ban', 't1', 'evil2', '--machine', 'abc']);
|
|
117
|
+
assert.doesNotMatch(r.out, /WARNING/); assert.match(r.out, /machine abc/);
|
|
118
|
+
assert.strictEqual(blocks[1].machine_id_hash, 'abc');
|
|
119
|
+
r = await cli(['blocked']);
|
|
120
|
+
assert.match(r.out, /t1\tname\tevil\t/);
|
|
121
|
+
r = await cli(['unban', 't1', 'evil']);
|
|
122
|
+
assert.match(r.out, /unbanned 1 entry/);
|
|
123
|
+
assert.match((await cli(['blocked'])).out, /nothing blocked/);
|
|
124
|
+
assert.strictEqual((await cli(['ban', 't1'])).code, 2);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('list shows self-reported machine info only when sent', async () => {
|
|
128
|
+
tokens.push({ team_id: 't1', node_name: 'm1', fingerprint: 'CCCC', status: 'pending', created_at: 'x', hostname: 'box', os: 'linux', machine_id_hash: 'abcdef0123456789' });
|
|
129
|
+
const r = await cli(['list']);
|
|
130
|
+
assert.match(r.out, /m1\tCCCC\tx\thost=box os=linux machine=abcdef012345 \(self-reported\)/);
|
|
131
|
+
});
|
|
@@ -1,24 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* run-rust-tests.js —
|
|
3
|
+
* run-rust-tests.js — build the kenaz-dashboard lib test binary and run it (TASK_1619).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* verified empirically 2026-06-11: the build-script output file contains the
|
|
11
|
-
* directives while the rustc -v link invocation does not. Result: the loader
|
|
12
|
-
* binds comctl32 5.82 from WinSxS and dies with STATUS_ENTRYPOINT_NOT_FOUND
|
|
13
|
-
* (0xc0000139, bash exit 127) before any test runs.
|
|
14
|
-
*
|
|
15
|
-
* A global `cargo:rustc-link-arg` is NOT safe: tauri-build already embeds an
|
|
16
|
-
* RT_MANIFEST resource for the app binary; adding /MANIFESTINPUT there creates
|
|
17
|
-
* a duplicate-manifest link failure.
|
|
18
|
-
*
|
|
19
|
-
* Fix here: build with --no-run, copy the test exe to a stable fresh name
|
|
20
|
-
* (the copy's new timestamp busts the per-path SxS activation-context cache),
|
|
21
|
-
* drop an external .manifest beside it, then exec it with passthrough args.
|
|
5
|
+
* Builds with --no-run (so compiler errors can be rendered readably from the JSON
|
|
6
|
+
* message stream), locates the lib unittest executable and execs it with the filter
|
|
7
|
+
* and passthrough args. The Windows Common-Controls manifest workaround this script
|
|
8
|
+
* used to carry was only needed for rfd's static TaskDialogIndirect import; rfd is
|
|
9
|
+
* gone and the test exe starts fine without any manifest.
|
|
22
10
|
*
|
|
23
11
|
* Usage:
|
|
24
12
|
* node dev/scripts/run-rust-tests.js [test-filter] [-- extra-harness-args]
|
|
@@ -36,7 +24,6 @@ const { verifyTargetDir } = require('./verify-target-dir.js');
|
|
|
36
24
|
|
|
37
25
|
const repoRoot = path.resolve(__dirname, '..', '..');
|
|
38
26
|
const crateDir = path.join(repoRoot, 'dashboard', 'src-tauri');
|
|
39
|
-
const manifestSrc = path.join(crateDir, 'test.manifest');
|
|
40
27
|
|
|
41
28
|
// TASK_2058: parse `[test-filter] [-- extra-harness-args]`.
|
|
42
29
|
//
|
|
@@ -137,7 +124,7 @@ function main() {
|
|
|
137
124
|
const exeArgs = buildExeArgs(process.argv.slice(2));
|
|
138
125
|
|
|
139
126
|
// TASK_1879: default to the canonical shared verify target dir. Avoids both
|
|
140
|
-
// Windows MAX_PATH
|
|
127
|
+
// Windows MAX_PATH and the live tauri-dev lock on the
|
|
141
128
|
// repo's own target/. Explicit CARGO_TARGET_DIR still wins.
|
|
142
129
|
const env = { ...process.env };
|
|
143
130
|
if (!env.CARGO_TARGET_DIR) {
|
|
@@ -192,31 +179,8 @@ function main() {
|
|
|
192
179
|
fail(`could not locate lib test executable (got: ${testExe})`);
|
|
193
180
|
}
|
|
194
181
|
|
|
195
|
-
// 3.
|
|
196
|
-
|
|
197
|
-
const direct = spawnSync(testExe, exeArgs, { stdio: 'inherit' });
|
|
198
|
-
process.exit(direct.status ?? 1);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// 4. Windows: copy to a stable name + external manifest (cache-busting copy).
|
|
202
|
-
if (!fs.existsSync(manifestSrc)) {
|
|
203
|
-
fail(`test.manifest not found at ${manifestSrc}`);
|
|
204
|
-
}
|
|
205
|
-
const runDir = path.dirname(testExe);
|
|
206
|
-
const runExe = path.join(runDir, 'kenaz_dashboard_unit_tests.exe');
|
|
207
|
-
fs.copyFileSync(testExe, runExe); // fresh timestamp invalidates SxS manifest cache
|
|
208
|
-
fs.copyFileSync(manifestSrc, `${runExe}.manifest`);
|
|
209
|
-
// Stage the PDB too so panic backtraces keep symbols (best effort).
|
|
210
|
-
const pdb = testExe.replace(/\.exe$/i, '.pdb');
|
|
211
|
-
if (fs.existsSync(pdb)) {
|
|
212
|
-
try {
|
|
213
|
-
fs.copyFileSync(pdb, runExe.replace(/\.exe$/i, '.pdb'));
|
|
214
|
-
} catch {
|
|
215
|
-
/* non-fatal */
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const run = spawnSync(runExe, exeArgs, { stdio: 'inherit' });
|
|
182
|
+
// 3. Run the artifact directly.
|
|
183
|
+
const run = spawnSync(testExe, exeArgs, { stdio: 'inherit' });
|
|
220
184
|
process.exit(run.status ?? 1);
|
|
221
185
|
}
|
|
222
186
|
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* PROBLEM: a Rust-scoped worktree subagent cannot safely run `cargo`/
|
|
7
7
|
* `run-rust-tests.js` in its OWN task worktree. That worktree is a fresh
|
|
8
|
-
* source root, so
|
|
9
|
-
* has to be rebuilt from scratch
|
|
8
|
+
* source root, so the whole dependency tree (see verify-target-dir.js)
|
|
9
|
+
* has to be rebuilt from scratch — minutes, which blows the
|
|
10
10
|
* Bash foreground timeout and, per prompt.js rule 13/feedback_agent_orphan_
|
|
11
11
|
* build_holds_lock, leaves an orphan cargo holding the shared target-dir
|
|
12
12
|
* lock forever.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* FIX: ONE canonical, reused worktree (this script owns it) that agents
|
|
15
15
|
* check out their branch into, run a single narrow test filter in, and
|
|
16
16
|
* hand back. The cache stays warm across dispatches because it is always
|
|
17
|
-
* the SAME source root (
|
|
17
|
+
* the SAME source root (build artifacts are keyed per source root).
|
|
18
18
|
*
|
|
19
19
|
* Marker file `.kenaz-rust-verify` inside the worktree is the safety
|
|
20
20
|
* invariant: every subcommand that would touch (create/checkout/lock in)
|
|
@@ -67,7 +67,7 @@ const RUNNER_REL_PATH = path.join('dev', 'scripts', 'run-rust-tests.js');
|
|
|
67
67
|
// and the old `path.resolve(__dirname, '..', '..')` returned the WORKTREE
|
|
68
68
|
// root, not the main repo — deriving a stray `<repoRoot>/.kenaz-worktrees/
|
|
69
69
|
// <repoRoot-basename>/rust-verify` path nested inside the task worktree
|
|
70
|
-
// and paying a cold
|
|
70
|
+
// and paying a cold build, exactly what this script exists to avoid.
|
|
71
71
|
//
|
|
72
72
|
// Fix: ask git. `git rev-parse --git-common-dir` always resolves to the
|
|
73
73
|
// MAIN repo's shared `.git` directory, even when run from a linked
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* generators, taskgen templates, verify scripts, formation runner/integration)
|
|
7
7
|
* must take the value from here instead of inventing its own directory.
|
|
8
8
|
*
|
|
9
|
-
* Why a short path at all (Windows):
|
|
10
|
-
* MAX_PATH (260) when the target dir sits under the repo or a
|
|
11
|
-
* (worktree paths add ~40 chars). A short root like C:/t/...
|
|
9
|
+
* Why a short path at all (Windows): deep C/C++ dependency object paths
|
|
10
|
+
* can approach MAX_PATH (260) when the target dir sits under the repo or a
|
|
11
|
+
* worktree (worktree paths add ~40 chars). A short root like C:/t/... avoids it.
|
|
12
12
|
*
|
|
13
13
|
* Why ONE shared directory (user decision 2026-08-22): the previous convention
|
|
14
14
|
* — "each dispatch invents a unique dir (C:/t/vis, C:/t/emb, C:/t1599, ...)
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
* cargo.exe / build-script-build.exe at 0% CPU with NO rustc children
|
|
28
28
|
* (nothing is compiling). Only in the orphan case kill the holder.
|
|
29
29
|
*
|
|
30
|
-
* Cache-heat caveat:
|
|
31
|
-
*
|
|
30
|
+
* Cache-heat caveat: workspace crates' build artifacts are keyed per
|
|
31
|
+
* source root. Verifies from the SAME checkout (main repo, or a
|
|
32
32
|
* reused worktree) get the warm cache; a verify from a NEW worktree path
|
|
33
|
-
* recompiles
|
|
33
|
+
* recompiles them but coexists in the same dir without invalidating others.
|
|
34
34
|
* The dir therefore still grows across many distinct worktree roots — when it
|
|
35
35
|
* gets too big, delete the whole canonical dir while no cargo is running; it
|
|
36
36
|
* is pure cache and rebuilds itself.
|
|
@@ -13,7 +13,7 @@ off = every hook exits before reading stdin. assist = silent telemetry/bookkeepi
|
|
|
13
13
|
| agent-flow POST (Notification, Pre/PostToolUse, PostToolUseFailure, SessionEnd, Stop, SubagentStart/Stop; user-level curl) | ship | assist | skipped silently when backend down |
|
|
14
14
|
| SessionStart agent-flow ping | ship | assist | |
|
|
15
15
|
| SessionStart guidance (new, replaces injecting CLAUDE.md) | ship | full | `guidance.md` |
|
|
16
|
-
| _run-user-prompt.js (runs
|
|
16
|
+
| _run-user-prompt.js (runs daemon-outbox, turn-context, task-bridge, correction-detect; see DEFAULT_HOOKS) | ship | full | prints context |
|
|
17
17
|
| memory-guard.js | ship | full | |
|
|
18
18
|
| synapse-first-investigation.js | ship | full | |
|
|
19
19
|
| task-bridge.js (PostToolUse Task*) | ship | assist | |
|
|
@@ -26,19 +26,19 @@ off = every hook exits before reading stdin. assist = silent telemetry/bookkeepi
|
|
|
26
26
|
|
|
27
27
|
Hooks only act in Kenaz projects (`.kenaz/project.yaml`, `_project-guard.js`): run `/kenaz init` once in a fresh project.
|
|
28
28
|
|
|
29
|
-
## Commands (
|
|
29
|
+
## Commands (13)
|
|
30
30
|
| Class | Commands |
|
|
31
31
|
|---|---|
|
|
32
|
-
| ship (
|
|
33
|
-
| core | Huginn,
|
|
34
|
-
|
|
|
32
|
+
| ship (11 + new `/kenaz`) | Ansuz, Amelia, architect, incident, review, security, spec, 產品, 設計, 評估 |
|
|
33
|
+
| core | Huginn, canary, agent-worker (invoked by the Rust scheduler with `--task`/`--agent`) |
|
|
34
|
+
| removed | strategy (Strategy Agent skill gone), archive, init, 進度, legacy_dev, 產品檢視, product-review, sunny (Pipeline / TASK_QUEUE / retired-subsystem commands; `/dev`, `/lead`, `/開發`, `multi-agent` retired earlier with Formation) |
|
|
35
35
|
|
|
36
|
-
## Skills
|
|
36
|
+
## Skills
|
|
37
37
|
| Class | Skills |
|
|
38
38
|
|---|---|
|
|
39
39
|
| ship (34) | architect_agent, assessment_agent, backend_agent, backend_engineer, code_reviewer, dev_agent, designer, developer, feature_agent, frontend_agent, incident_response, product_manager, qa_engineer, security_agent, spec_optimizer, sre_agent, adapt, animate, audit, bolder, clarify, colorize, critique, delight, distill, extract, frontend-design, harden, normalize, onboard, optimize, polish, quieter, ui-ux-pro-max |
|
|
40
|
-
| core | heimdall, huginn,
|
|
41
|
-
|
|
|
40
|
+
| core | heimdall, huginn, optimization_agent |
|
|
41
|
+
| removed | legacy_dev, sunny |
|
|
42
42
|
| third-party / domain (not shipped; install separately from .agents/skills) | vercel-react-best-practices, supabase-postgres-best-practices, remotion-best-practices, pcb-review |
|
|
43
43
|
|
|
44
44
|
## Open items / caveats
|
|
@@ -48,8 +48,18 @@ Independent Codex sessions use **pull-on-prompt**: in `full` mode with trusted h
|
|
|
48
48
|
|
|
49
49
|
Resolution: env `KENAZ_MODE` > `<project>/.kenaz/kenaz.yaml` > `~/.kenaz/kenaz.yaml` > `full`. Hooks fail open (backend down or core not found: no output, exit 0).
|
|
50
50
|
|
|
51
|
+
## Agent SDK sessions
|
|
52
|
+
|
|
53
|
+
Programs built on `claude_agent_sdk` (`CLAUDE_CODE_ENTRYPOINT` `sdk-py` / `sdk-ts`) load `~/.claude/settings.json` too, so they would get this plugin. Hooks (`hooks/sdk-gate.js`) and the kenaz-peer mod therefore do nothing for those sessions. `claude -p` (`sdk-cli`) and interactive entrypoints are unchanged. Set `KENAZ_IN_SDK=1` to opt a program in.
|
|
54
|
+
|
|
55
|
+
The MCP tools cannot be gated: `.mcp.json` is a plain HTTP server entry, not a launcher that could exit, and the connection is made before any hook runs. SDK users who do not want the 34 tools pass `setting_sources=[]` (or `[]` plus the sources they need) and `strict_mcp_config` (`extra_args={"strict-mcp-config": None}`) in `ClaudeAgentOptions`.
|
|
56
|
+
|
|
51
57
|
## CLI version gate
|
|
52
58
|
|
|
53
59
|
Not needed: the 2.1.287 gate (TASK_2234, `peer_mod.rs`) exists because kenaz-peer is a mod module (`hooks/register.ts`). This plugin uses only plain hooks, commands, skills and MCP, which older CLIs support. Automatic registration into `CLAUDE_CODE_PLUGIN_DIRS` (as kenaz-peer gets) is a follow-up in `peer_mod.rs`; until then use `--plugin-dir`.
|
|
54
60
|
|
|
55
61
|
Tests: `node --test plugins/kenaz/scripts/kenaz.test.js`. Classification: `INVENTORY.md`.
|
|
62
|
+
|
|
63
|
+
## Peer node identity
|
|
64
|
+
|
|
65
|
+
kenaz-server requests a relay node token on its own; it starts pending until the relay operator approves its fingerprint. `GET http://localhost:18080/api/peer/status?projectPath=<project>` shows `node_identity` (`none` / `pending` with the fingerprint / `active`). Send the fingerprint to the operator out of band. No token or an old relay keeps today behaviour. Operator commands, the enforcement flag and the `X-Node-Token` header for non-Claude agents that call the relay directly: `yggdrasil-relay/DESIGN.md`, section "Node tokens and approval".
|
|
@@ -17,8 +17,7 @@
|
|
|
17
17
|
- 撰寫模組接口規範
|
|
18
18
|
|
|
19
19
|
4. **產出文件**:
|
|
20
|
-
-
|
|
21
|
-
- 建立任務文件至 `dev/pipeline/tasks/`
|
|
20
|
+
- 用 `node "${CLAUDE_PLUGIN_ROOT}/hooks/core-resolve.js" .collaboration/core/task-api.js create` 建立任務卡(`.kenaz/tasks/backlog/`)
|
|
22
21
|
- 撰寫接口文檔至 `docs/`
|
|
23
22
|
|
|
24
23
|
請開始執行系統架構設計。
|
|
@@ -1,5 +1,5 @@
|
|
|
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
|
-
- Reply in the user's language. Commands: /Ansuz, /Amelia, /review, /security, /incident, /spec
|
|
4
|
+
- Reply in the user's language. Commands: /Ansuz, /Amelia, /review, /security, /incident, /spec.
|
|
5
5
|
- Switch: /kenaz off | assist | full (or env KENAZ_MODE). off disables every Kenaz hook.
|
|
@@ -15,6 +15,7 @@ const net = require('net');
|
|
|
15
15
|
const http = require('http');
|
|
16
16
|
const { spawnSync } = require('child_process');
|
|
17
17
|
const { resolveMode, allows, projectDir } = require('./mode');
|
|
18
|
+
const { isSdkSession } = require('./sdk-gate');
|
|
18
19
|
|
|
19
20
|
const HOST = '127.0.0.1';
|
|
20
21
|
const PORT = Number(process.env.KENAZ_BACKEND_PORT) || 18080;
|
|
@@ -86,7 +87,7 @@ function run(spec, stdin, dir) {
|
|
|
86
87
|
|
|
87
88
|
function main() {
|
|
88
89
|
const spec = HOOKS[process.argv[2]];
|
|
89
|
-
if (!spec) return;
|
|
90
|
+
if (!spec || isSdkSession()) return; // TASK_2455: SDK sessions get nothing, before stdin or any write
|
|
90
91
|
const dir = projectDir();
|
|
91
92
|
// A project that ships the Kenaz hooks itself (the core repo) registers them in its own
|
|
92
93
|
// .claude/settings.json; the plugin yields so no hook runs twice (same rule as _user-scope.js).
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// TASK_2455: claude-agent-sdk sessions (CLAUDE_CODE_ENTRYPOINT sdk-py / sdk-ts / ...) must not get Kenaz.
|
|
3
|
+
// sdk-cli is `claude -p` (Kenaz's own dispatch, headless nodes) and keeps working. KENAZ_IN_SDK=1 opts in.
|
|
4
|
+
function isSdkSession(env) {
|
|
5
|
+
env = env || process.env;
|
|
6
|
+
const ep = String(env.CLAUDE_CODE_ENTRYPOINT || '');
|
|
7
|
+
return ep.startsWith('sdk-') && ep !== 'sdk-cli' && env.KENAZ_IN_SDK !== '1';
|
|
8
|
+
}
|
|
9
|
+
module.exports = { isSdkSession };
|
|
@@ -19,6 +19,18 @@ function hook(name, dir, env, input) {
|
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
test('TASK_2455: SDK sessions get nothing; cli / sdk-cli / KENAZ_IN_SDK=1 keep today\'s output', () => {
|
|
23
|
+
const dir = proj();
|
|
24
|
+
for (const n of ['guidance', 'prompt-runner', 'memory-guard', 'flow-post', 'session-start', 'session-stop']) {
|
|
25
|
+
for (const ep of ['sdk-py', 'sdk-ts']) {
|
|
26
|
+
const r = hook(n, dir, { CLAUDE_CODE_ENTRYPOINT: ep });
|
|
27
|
+
assert.strictEqual(r.status, 0); assert.strictEqual(r.stdout, ''); assert.strictEqual(r.stderr, '');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
for (const ep of ['cli', 'sdk-cli']) assert.match(hook('guidance', dir, { CLAUDE_CODE_ENTRYPOINT: ep }).stdout, /Kenaz plugin/);
|
|
31
|
+
assert.match(hook('guidance', dir, { CLAUDE_CODE_ENTRYPOINT: 'sdk-py', KENAZ_IN_SDK: '1' }).stdout, /Kenaz plugin/);
|
|
32
|
+
});
|
|
33
|
+
|
|
22
34
|
test('full prints guidance (control for off)', () => {
|
|
23
35
|
const r = hook('guidance', proj());
|
|
24
36
|
assert.strictEqual(r.status, 0);
|
|
@@ -102,7 +114,7 @@ test('ygg join activates the room in collaboration_projects; status explains how
|
|
|
102
114
|
const srv = http.createServer((req, res) => {
|
|
103
115
|
let b = ''; req.on('data', (d) => { b += d; });
|
|
104
116
|
req.on('end', () => {
|
|
105
|
-
if (req.method === 'POST') posts.push(JSON.parse(b));
|
|
117
|
+
if (req.method === 'POST') { posts.push(JSON.parse(b)); cfg.collaboration_projects = posts[posts.length - 1].collaboration_projects; }
|
|
106
118
|
res.setHeader('content-type', 'application/json');
|
|
107
119
|
res.end(JSON.stringify(req.method === 'POST' ? { ok: true } : cfg));
|
|
108
120
|
});
|
|
@@ -120,7 +132,7 @@ test('ygg join activates the room in collaboration_projects; status explains how
|
|
|
120
132
|
srv.close();
|
|
121
133
|
}
|
|
122
134
|
const { buildStatus, renderStatus } = require('./ygg.js');
|
|
123
|
-
assert.match(renderStatus(buildStatus(cfg, { connected: false, nodes: [] }, null)), /not active in this project - run "ygg join <key>"/);
|
|
135
|
+
assert.match(renderStatus(buildStatus(Object.assign({}, cfg, { collaboration_projects: ['other'] }), { connected: false, nodes: [] }, null)), /not active in this project - run "ygg join <key>"/);
|
|
124
136
|
});
|
|
125
137
|
|
|
126
138
|
// TASK_2416: `ygg create` posts to the relay (config relay_url), then joins with the returned key.
|
|
@@ -139,7 +151,7 @@ async function withStub(relayReply, fn) {
|
|
|
139
151
|
}
|
|
140
152
|
if (req.url.startsWith('/api/nodes')) return res.end('[]');
|
|
141
153
|
if (req.url.startsWith('/api/yggdrasil/config')) {
|
|
142
|
-
if (req.method === 'POST') { seen.config.push(JSON.parse(b)); return res.end(JSON.stringify({ ok: true })); }
|
|
154
|
+
if (req.method === 'POST') { seen.config.push(JSON.parse(b)); cfg.collaboration_projects = seen.config[seen.config.length - 1].collaboration_projects; return res.end(JSON.stringify({ ok: true })); }
|
|
143
155
|
return res.end(JSON.stringify(cfg));
|
|
144
156
|
}
|
|
145
157
|
res.end(JSON.stringify({ success: true, created: false }));
|
|
@@ -16,7 +16,7 @@ const SKILLS = [
|
|
|
16
16
|
'adapt', 'animate', 'audit', 'bolder', 'clarify', 'colorize', 'critique', 'delight', 'distill', 'extract',
|
|
17
17
|
'frontend-design', 'harden', 'normalize', 'onboard', 'optimize', 'polish', 'quieter', 'ui-ux-pro-max',
|
|
18
18
|
];
|
|
19
|
-
const COMMANDS = ['Amelia', 'Ansuz', 'architect', 'incident', 'review', 'security', 'spec', '
|
|
19
|
+
const COMMANDS = ['Amelia', 'Ansuz', 'architect', 'incident', 'review', 'security', 'spec', '產品', '設計', '評估'];
|
|
20
20
|
|
|
21
21
|
// Core-relative references become plugin-relative / core-resolved.
|
|
22
22
|
// In a user's project (npm install) the core lives in ~/.kenaz/core, not the project folder, so a
|
|
@@ -149,7 +149,15 @@ function postBody(cfg, patch) {
|
|
|
149
149
|
async function join(dir, key, scope) {
|
|
150
150
|
if (!key || /\s/.test(key)) throw new Error('usage: ygg join <key> [--project|--global]');
|
|
151
151
|
const cfg = await getConfig(dir);
|
|
152
|
+
// TASK_2441: no room key to activate = the POST below would save an empty list and "succeed".
|
|
153
|
+
if (!cfg.effective_project_key) throw new Error("cannot work out this project's room (" + (cfg.error || 'no effective_project_key') + '); does ' + dir + ' have a repo with a remote?');
|
|
152
154
|
await postConfig(dir, postBody(cfg, { enabled: true, publishable_key: key, key_scope: scope, collaboration_projects: withCollab(cfg, true) }));
|
|
155
|
+
// Verify by reading back: a save that returned ok but did not land must not print a status that says "run join".
|
|
156
|
+
const after = await getConfig(dir);
|
|
157
|
+
if (!(after.collaboration_projects || []).includes(cfg.effective_project_key)) {
|
|
158
|
+
throw new Error('join saved the key but room "' + cfg.effective_project_key + '" is not in collaboration_projects after re-reading the config; expected in ' +
|
|
159
|
+
path.join(dir, '.kenaz', 'yggdrasil.local.yaml') + ' (collaboration_projects: "' + cfg.effective_project_key + '"); the server reports ' + JSON.stringify(after.collaboration_projects || []));
|
|
160
|
+
}
|
|
153
161
|
return status(dir);
|
|
154
162
|
}
|
|
155
163
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// node --test plugins/kenaz/scripts/ygg.test.js
|
|
3
|
+
// TASK_2441: ygg join must read the config back and fail loudly when the room did not land.
|
|
4
|
+
const test = require('node:test');
|
|
5
|
+
const assert = require('node:assert');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const http = require('http');
|
|
10
|
+
const { spawn } = require('child_process');
|
|
11
|
+
|
|
12
|
+
const YGG = path.join(__dirname, 'ygg.js');
|
|
13
|
+
const ROOM = 'github.com/o/r';
|
|
14
|
+
|
|
15
|
+
async function withBackend(persist, fn) {
|
|
16
|
+
const cfg = { enabled: false, effective_project_key: ROOM, node_name: 'Alice', publishable_key: '', collaboration_projects: [], relay_url: '' };
|
|
17
|
+
const srv = http.createServer((req, res) => {
|
|
18
|
+
let b = ''; req.on('data', (d) => { b += d; });
|
|
19
|
+
req.on('end', () => {
|
|
20
|
+
res.setHeader('content-type', 'application/json');
|
|
21
|
+
if (req.url.startsWith('/api/yggdrasil/config') && req.method === 'POST') {
|
|
22
|
+
if (persist) Object.assign(cfg, JSON.parse(b));
|
|
23
|
+
return res.end(JSON.stringify({ ok: true }));
|
|
24
|
+
}
|
|
25
|
+
res.end(JSON.stringify(req.url.startsWith('/api/yggdrasil/config') ? cfg : req.url.startsWith('/api/projects') ? { success: true } : {}));
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
await new Promise((r) => srv.listen(0, '127.0.0.1', r));
|
|
29
|
+
try { return await fn(srv.address().port); } finally { srv.close(); }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function cli(port, dir) {
|
|
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) }) });
|
|
36
|
+
let out = ''; let err = '';
|
|
37
|
+
c.stdout.on('data', (d) => { out += d; }); c.stderr.on('data', (d) => { err += d; });
|
|
38
|
+
c.on('close', (code) => resolve({ code, out, err }));
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
test('join exits non-zero naming the file when the room is not in collaboration_projects after the POST', async () => {
|
|
43
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ygg2441-'));
|
|
44
|
+
await withBackend(false, async (port) => {
|
|
45
|
+
const r = await cli(port, dir);
|
|
46
|
+
assert.notStrictEqual(r.code, 0);
|
|
47
|
+
assert.match(r.err, /yggdrasil\.local\.yaml/);
|
|
48
|
+
assert.ok(r.err.includes(ROOM), r.err);
|
|
49
|
+
assert.doesNotMatch(r.out, /not active in this project/);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('control: join against a backend that persists the room prints "active in this project"', async () => {
|
|
54
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ygg2441-'));
|
|
55
|
+
await withBackend(true, async (port) => {
|
|
56
|
+
const r = await cli(port, dir);
|
|
57
|
+
assert.strictEqual(r.code, 0, r.err);
|
|
58
|
+
assert.match(r.out, /active in this project/);
|
|
59
|
+
assert.doesNotMatch(r.out, /not active/);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('join refuses when the backend reports no effective_project_key (nothing to activate)', async () => {
|
|
64
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ygg2441-'));
|
|
65
|
+
const srv = http.createServer((req, res) => { res.setHeader('content-type', 'application/json'); res.end(JSON.stringify({ enabled: false, error: 'yggdrasil.yaml not found' })); });
|
|
66
|
+
await new Promise((r) => srv.listen(0, '127.0.0.1', r));
|
|
67
|
+
try {
|
|
68
|
+
const r = await cli(srv.address().port, dir);
|
|
69
|
+
assert.notStrictEqual(r.code, 0);
|
|
70
|
+
assert.match(r.err, /cannot work out this project's room/);
|
|
71
|
+
} finally { srv.close(); }
|
|
72
|
+
});
|