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
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const { test } = require('node:test');
|
|
3
|
-
const assert = require('node:assert/strict');
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const os = require('os');
|
|
6
|
-
const path = require('path');
|
|
7
|
-
const vm = require('vm');
|
|
8
|
-
const helper = require('./jev-correction');
|
|
9
|
-
const { applyJevCorrection, buildBatch, makeRequest, makeBatchRequest, requestJev, requestBatch, safeText, MODEL } = helper;
|
|
10
|
-
const KEY = 'synthetic-test-credential';
|
|
11
|
-
function setup(t) {
|
|
12
|
-
const projectPath = fs.mkdtempSync(path.join(os.tmpdir(), 'jev-correction-'));
|
|
13
|
-
t.after(() => fs.rmSync(projectPath, { recursive: true, force: true }));
|
|
14
|
-
const config = path.join(projectPath, '.kenaz', 'dashboard-models.json');
|
|
15
|
-
fs.mkdirSync(path.dirname(config));
|
|
16
|
-
const toggle = value => fs.writeFileSync(config, JSON.stringify({ jevCorrectionEnabled: value }));
|
|
17
|
-
toggle(true);
|
|
18
|
-
const lines = ['[12:00:00][Agent] I will change production.', '[12:00:01][User] Change the development version instead.', '[12:00:02][Agent] I will inspect the development files.', '[12:00:03][User] What is the current status?'];
|
|
19
|
-
const userMessages = [{ idx: 0, lineIndex: 1, text: lines[1].split('[User] ')[1] }, { idx: 1, lineIndex: 3, text: lines[3].split('[User] ')[1] }];
|
|
20
|
-
const baseline = [{ idx: 0, translated: 'Existing English translation.', labels: ['delegation'] }, { idx: 1, translated: 'Existing second translation.', labels: ['correction', 'question', 'length_too_short'] }];
|
|
21
|
-
return { projectPath, config, toggle, lines, userMessages, baseline };
|
|
22
|
-
}
|
|
23
|
-
function body(ids = ['message_0', 'message_1'], probabilities = [0.9, 0.1]) {
|
|
24
|
-
return { model: MODEL, answers: Object.fromEntries(ids.map((id, i) => [id, { type: 'noul', noul: probabilities[i] ?? 0.7 }])), usage: { input_tokens: 10, output_tokens: 2 } };
|
|
25
|
-
}
|
|
26
|
-
const spawned = (response = body()) => ({ status: 0, stdout: JSON.stringify({ status: 'ok', body: response }) });
|
|
27
|
-
const options = spawnSync => ({ key: KEY, spawnSync });
|
|
28
|
-
|
|
29
|
-
test('OFF, malformed config, missing/invalid key and another project never spawn', t => {
|
|
30
|
-
const ctx = setup(t); let calls = 0;
|
|
31
|
-
const deps = options(() => { calls++; throw Error('must not spawn'); });
|
|
32
|
-
for (const flag of [false, undefined, 'true', 1]) { ctx.toggle(flag); assert.equal(applyJevCorrection(ctx, deps), ctx.baseline); }
|
|
33
|
-
fs.writeFileSync(ctx.config, '{'); assert.equal(applyJevCorrection(ctx, deps), ctx.baseline);
|
|
34
|
-
fs.unlinkSync(ctx.config); assert.equal(applyJevCorrection(ctx, deps), ctx.baseline);
|
|
35
|
-
ctx.toggle(true);
|
|
36
|
-
for (const key of ['', ' ', '\n', 'value\n', 'a b']) assert.equal(applyJevCorrection(ctx, { ...deps, key }), ctx.baseline);
|
|
37
|
-
assert.equal(applyJevCorrection({ ...ctx, projectPath: path.join(ctx.projectPath, 'other') }, deps), ctx.baseline);
|
|
38
|
-
assert.equal(applyJevCorrection({ ...ctx, projectPath: undefined }, deps), ctx.baseline);
|
|
39
|
-
assert.equal(calls, 0);
|
|
40
|
-
});
|
|
41
|
-
test('active decisions add/remove correction, preserve other labels/translations, never mutate baseline', t => {
|
|
42
|
-
const ctx = setup(t), original = structuredClone(ctx.baseline);
|
|
43
|
-
const result = applyJevCorrection(ctx, options(() => spawned()));
|
|
44
|
-
assert.deepEqual(result.map(x => x.labels), [['correction', 'delegation'], ['question', 'length_too_short']]);
|
|
45
|
-
assert.deepEqual(result.map(x => x.translated), original.map(x => x.translated));
|
|
46
|
-
assert.deepEqual(ctx.baseline, original);
|
|
47
|
-
assert.notEqual(result, ctx.baseline);
|
|
48
|
-
});
|
|
49
|
-
test('existing secondary correction becomes primary without reordering remaining labels or fields', t => {
|
|
50
|
-
const ctx = setup(t);
|
|
51
|
-
ctx.baseline[0] = { ...ctx.baseline[0], labels: ['delegation', 'question', 'correction', 'challenge'], extra: 'preserved' };
|
|
52
|
-
const original = structuredClone(ctx.baseline);
|
|
53
|
-
const result = applyJevCorrection(ctx, options(() => spawned()));
|
|
54
|
-
assert.deepEqual(result[0], { ...original[0], labels: ['correction', 'delegation', 'question', 'challenge'] });
|
|
55
|
-
assert.deepEqual(ctx.baseline, original);
|
|
56
|
-
ctx.toggle(false);
|
|
57
|
-
assert.equal(applyJevCorrection(ctx, options(() => spawned())), ctx.baseline);
|
|
58
|
-
ctx.toggle(true);
|
|
59
|
-
assert.equal(applyJevCorrection(ctx, options(() => ({ status: 1 }))), ctx.baseline);
|
|
60
|
-
});
|
|
61
|
-
test('null baseline supplies only positive correction without invented translation; threshold is inclusive', t => {
|
|
62
|
-
const ctx = setup(t);
|
|
63
|
-
assert.deepEqual(applyJevCorrection({ ...ctx, baseline: null }, options(() => spawned(body(undefined, [0.5, 0.499])))), [{ idx: 0, labels: ['correction'] }]);
|
|
64
|
-
assert.deepEqual(applyJevCorrection({ ...ctx, baseline: [] }, options(() => spawned())), [{ idx: 0, labels: ['correction'] }]);
|
|
65
|
-
const partial = applyJevCorrection({ ...ctx, baseline: [ctx.baseline[1]] }, options(() => spawned()));
|
|
66
|
-
assert.deepEqual(partial.find(item => item.idx === 0), { idx: 0, labels: ['correction'] });
|
|
67
|
-
});
|
|
68
|
-
test('hidden bounded Node child uses stdin; no credential or message in argv', t => {
|
|
69
|
-
const ctx = setup(t); let calls = 0;
|
|
70
|
-
applyJevCorrection(ctx, options((command, args, opts) => {
|
|
71
|
-
calls++;
|
|
72
|
-
assert.equal(command, process.execPath); assert.equal(args.length, 2); assert.equal(args[1], '--worker');
|
|
73
|
-
assert.equal(opts.timeout, 1500); assert.equal(opts.maxBuffer, 65536); assert.equal(opts.windowsHide, true); assert.equal(opts.shell, false);
|
|
74
|
-
assert.equal(JSON.stringify(args).includes(ctx.userMessages[0].text), false);
|
|
75
|
-
assert.equal(JSON.stringify({ args, opts }).includes(KEY), false);
|
|
76
|
-
const packet = JSON.parse(opts.input); assert.equal(packet.requests.length, 2);
|
|
77
|
-
assert.equal(packet.requests[0].state.message, ctx.userMessages[0].text);
|
|
78
|
-
return spawned();
|
|
79
|
-
}));
|
|
80
|
-
assert.equal(calls, 1);
|
|
81
|
-
});
|
|
82
|
-
test('in-flight disable discards results and preserves baseline reference', t => {
|
|
83
|
-
const ctx = setup(t);
|
|
84
|
-
assert.equal(applyJevCorrection(ctx, options(() => { ctx.toggle(false); return spawned(); })), ctx.baseline);
|
|
85
|
-
assert.equal(JSON.parse(fs.readFileSync(path.join(ctx.projectPath, '.kenaz/jev/decisions.jsonl'), 'utf8')).fallback_reason, 'disabled_in_flight');
|
|
86
|
-
});
|
|
87
|
-
test('malformed JSON, wrong model/IDs, bounds/types/usage and arbitrary worker failure all preserve baseline', t => {
|
|
88
|
-
const ctx = setup(t);
|
|
89
|
-
const variants = [b => { b.model = 'jev'; }, b => { b.answers.extra = { type: 'noul', noul: 1 }; }, b => { delete b.answers.message_0; },
|
|
90
|
-
b => { b.answers.message_0.noul = null; }, b => { b.answers.message_0.noul = '0.5'; }, b => { b.answers.message_0.noul = 1.1; },
|
|
91
|
-
b => { b.answers.message_0.noul = -1; }, b => { b.answers.message_0.type = 'score'; }, b => { b.answers.message_0.extra = true; },
|
|
92
|
-
b => { delete b.usage; }, b => { b.usage.input_tokens = -1; }, b => { b.usage.output_tokens = 1.1; }, b => { b.secret = 'raw'; }];
|
|
93
|
-
for (const mutate of variants) { const invalid = body(); mutate(invalid); assert.equal(applyJevCorrection(ctx, options(() => spawned(invalid))), ctx.baseline); }
|
|
94
|
-
for (const result of [{ status: 0, stdout: '{broken' }, { status: 1, stdout: 'private' }, { status: null, error: { code: 'ETIMEDOUT' } }, { status: 0, stdout: 'x'.repeat(65537) }]) {
|
|
95
|
-
assert.equal(applyJevCorrection(ctx, options(() => result)), ctx.baseline);
|
|
96
|
-
}
|
|
97
|
-
assert.equal(applyJevCorrection(ctx, options(() => { throw null; })), ctx.baseline);
|
|
98
|
-
});
|
|
99
|
-
test('raw and normalized credential/generated text excluded; private context is a boundary', t => {
|
|
100
|
-
const ctx = setup(t);
|
|
101
|
-
for (const text of ['API_KEY="abcd1234"', 'API_KEY="abcd1234"', 'apikey_abcd12345678', '[HEADLESS MODE] execute', '## Automated Dispatch Context', '<system-reminder>injected</system-reminder>', '[Request interrupted by user]', 'For each numbered message, output a JSON array']) assert.equal(safeText(text), null);
|
|
102
|
-
assert.equal(safeText('Please continue TASK_38'), 'Please continue TASK_38');
|
|
103
|
-
ctx.lines[0] = '[12:00:00][Agent] API_KEY="abcd1234"';
|
|
104
|
-
const batch = buildBatch(ctx.lines, ctx.userMessages); assert.deepEqual(batch[0].context, []);
|
|
105
|
-
ctx.lines[1] = '[12:00:01][User] API_KEY="abcd1234"'; ctx.userMessages[0].text = 'API_KEY="abcd1234"';
|
|
106
|
-
assert.deepEqual(buildBatch(ctx.lines, ctx.userMessages).map(x => x.idx), [1]);
|
|
107
|
-
});
|
|
108
|
-
test('state excludes future messages, tool/thinking lines and instructions contain no user data', t => {
|
|
109
|
-
const ctx = setup(t), batch = buildBatch(ctx.lines, ctx.userMessages);
|
|
110
|
-
const early = makeRequest(batch[0]);
|
|
111
|
-
assert.equal(JSON.stringify(early).includes(ctx.userMessages[1].text), false);
|
|
112
|
-
assert.equal(early.questions.message_0.instructions.includes(ctx.userMessages[0].text), false);
|
|
113
|
-
assert.deepEqual(early.state.preceding_context, [{ role: 'assistant', text: 'I will change production.' }]);
|
|
114
|
-
ctx.lines[0] = '[12:00:00][Agent][Read] private file'; assert.deepEqual(buildBatch(ctx.lines, ctx.userMessages)[0].context, []);
|
|
115
|
-
ctx.lines[0] = '[12:00:00][Thinking] hidden reasoning'; assert.deepEqual(buildBatch(ctx.lines, ctx.userMessages)[0].context, []);
|
|
116
|
-
ctx.userMessages[0].text = 'different identity'; assert.deepEqual(buildBatch(ctx.lines, ctx.userMessages).map(x => x.idx), [1]);
|
|
117
|
-
});
|
|
118
|
-
test('only most recent eight eligible messages and at most 32KB are sent', () => {
|
|
119
|
-
const lines = Array.from({ length: 30 }, (_, i) => `[12:00:00][User] Message ${i} ` + 'x'.repeat(3000));
|
|
120
|
-
const messages = lines.map((line, idx) => ({ idx, lineIndex: idx, text: line.split('[User] ')[1] }));
|
|
121
|
-
const batch = buildBatch(lines, messages);
|
|
122
|
-
assert.ok(batch.length > 0 && batch.length <= 8); assert.equal(batch.at(-1).idx, 29);
|
|
123
|
-
assert.ok(Buffer.byteLength(JSON.stringify(makeBatchRequest(batch))) <= 32768);
|
|
124
|
-
});
|
|
125
|
-
test('typed Noul request fixed endpoint and redirect rejection; no network without valid key', async () => {
|
|
126
|
-
const request = makeRequest({ idx: 0, message: 'test correction', context: [] }); let calls = 0;
|
|
127
|
-
const fetchImpl = async (url, options) => {
|
|
128
|
-
calls++; assert.equal(url, helper.ENDPOINT); assert.equal(options.redirect, 'error'); assert.equal(options.method, 'POST');
|
|
129
|
-
assert.equal(options.headers.Authorization, `Bearer ${KEY}`);
|
|
130
|
-
return new Response(JSON.stringify(body(['message_0'])));
|
|
131
|
-
};
|
|
132
|
-
assert.equal((await requestJev(request, { key: '', fetchImpl })).status, 'missing_key'); assert.equal(calls, 0);
|
|
133
|
-
assert.equal((await requestJev(request, { key: KEY, fetchImpl })).status, 'ok'); assert.equal(calls, 1);
|
|
134
|
-
});
|
|
135
|
-
test('401/429/529 differentiated; malformed JSON and oversize streamed body fail safely', async () => {
|
|
136
|
-
const request = makeRequest({ idx: 0, message: 'test correction', context: [] });
|
|
137
|
-
for (const status of [401, 429, 529]) assert.equal((await requestJev(request, { key: KEY, fetchImpl: async () => new Response('private body', { status }) })).status, `http_${status}`);
|
|
138
|
-
for (const text of ['{broken', 'x'.repeat(65537)]) {
|
|
139
|
-
const result = await requestJev(request, { key: KEY, fetchImpl: async () => new Response(text) });
|
|
140
|
-
assert.deepEqual(result, { status: 'invalid_response' });
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
test('deadline covers stalled fetch AND stalled body, even if mock ignores abort', async () => {
|
|
144
|
-
const request = makeRequest({ idx: 0, message: 'test correction', context: [] });
|
|
145
|
-
for (const fetchImpl of [() => new Promise(() => {}), async () => new Response(new ReadableStream({ start() {} }))]) {
|
|
146
|
-
const start = Date.now(); const result = await requestJev(request, { key: KEY, deadlineMs: 25, fetchImpl });
|
|
147
|
-
assert.deepEqual(result, { status: 'timeout' }); assert.ok(Date.now() - start < 1500);
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
test('parallel isolated calls bounded to eight, all-or-none failure, aggregate usage', async t => {
|
|
151
|
-
const ctx = setup(t), packet = makeBatchRequest(buildBatch(ctx.lines, ctx.userMessages)); let calls = 0;
|
|
152
|
-
const fetchImpl = async (_, options) => {
|
|
153
|
-
calls++; const r = JSON.parse(options.body); const id = Object.keys(r.questions)[0];
|
|
154
|
-
assert.equal(Object.keys(r.questions).length, 1);
|
|
155
|
-
if (id === 'message_0') assert.equal(JSON.stringify(r).includes(ctx.userMessages[1].text), false);
|
|
156
|
-
return new Response(JSON.stringify(body([id])));
|
|
157
|
-
};
|
|
158
|
-
const result = await requestBatch(packet, { key: KEY, fetchImpl });
|
|
159
|
-
assert.equal(result.status, 'ok'); assert.equal(calls, 2); assert.deepEqual(result.body.usage, { input_tokens: 20, output_tokens: 4 });
|
|
160
|
-
assert.equal((await requestBatch(packet, { key: KEY, fetchImpl: async () => new Response('', { status: 429 }) })).status, 'http_429');
|
|
161
|
-
assert.equal((await requestBatch({ ...packet, requests: Array(9).fill(packet.requests[0]) }, { key: KEY, fetchImpl })).status, 'invalid_response');
|
|
162
|
-
const start = Date.now();
|
|
163
|
-
assert.equal((await requestBatch(packet, { key: KEY, deadlineMs: 30, fetchImpl: () => new Promise(() => {}) })).status, 'timeout');
|
|
164
|
-
assert.ok(Date.now() - start < 500, 'parallel calls share the deadline, not sequential timeouts');
|
|
165
|
-
});
|
|
166
|
-
test('bounded metadata-only telemetry and logging failure cannot break decisions', t => {
|
|
167
|
-
const ctx = setup(t), deps = options(() => spawned());
|
|
168
|
-
for (let i = 0; i < 160; i++) applyJevCorrection(ctx, deps);
|
|
169
|
-
const file = path.join(ctx.projectPath, '.kenaz', 'jev', 'decisions.jsonl'), text = fs.readFileSync(file, 'utf8');
|
|
170
|
-
assert.ok(Buffer.byteLength(text) <= 65536);
|
|
171
|
-
for (const privateText of [KEY, ctx.projectPath, ...ctx.userMessages.map(x => x.text), 'Existing English']) assert.equal(text.includes(privateText), false);
|
|
172
|
-
for (const line of text.trim().split('\n')) {
|
|
173
|
-
const row = JSON.parse(line); assert.equal(row.mode, 'active'); assert.equal(row.status, 'applied');
|
|
174
|
-
assert.ok(row.decisions.every(x => /^[a-f0-9]{64}$/.test(x.message_id)));
|
|
175
|
-
}
|
|
176
|
-
fs.rmSync(path.dirname(file), { recursive: true }); fs.writeFileSync(path.dirname(file), 'obstruction');
|
|
177
|
-
assert.ok(applyJevCorrection(ctx, deps)[0].labels.includes('correction'));
|
|
178
|
-
});
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Formation Log Reader
|
|
4
|
-
*
|
|
5
|
-
* 讀取 Formation log 檔案的尾端/頭端/錯誤行
|
|
6
|
-
* 用途:讓 Ansuz 按需讀取 log,避免載入完整 log 爆 context
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* node .formation/tools/log-reader.js --log <path> --tail 50
|
|
10
|
-
* node .formation/tools/log-reader.js --task TASK_XXX --tail 50
|
|
11
|
-
* node .formation/tools/log-reader.js --latest unit --tail 30
|
|
12
|
-
* node .formation/tools/log-reader.js --log <path> --errors
|
|
13
|
-
*
|
|
14
|
-
* Options:
|
|
15
|
-
* --log <path> 直接讀指定 log 檔
|
|
16
|
-
* --task TASK_XXX 在 .formation/logs/ 下搜尋包含 TASK_XXX 的最新 log
|
|
17
|
-
* --latest <formation> 讀指定陣型目錄下最新的 log
|
|
18
|
-
* --tail N 只輸出最後 N 行(預設 50)
|
|
19
|
-
* --head N 只輸出前 N 行
|
|
20
|
-
* --errors 只輸出包含 ERROR/FAILED/❌ 的行
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
const fs = require('fs');
|
|
24
|
-
const path = require('path');
|
|
25
|
-
const { LOG_DIR, getLogDir } = require('./utils');
|
|
26
|
-
|
|
27
|
-
// ─────────────────────────────────────────
|
|
28
|
-
// 尋找 log 檔案
|
|
29
|
-
// ─────────────────────────────────────────
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* 搜尋包含 taskId 的最新 log 檔案
|
|
33
|
-
* @param {string} taskId - TASK_XXX
|
|
34
|
-
* @param {string} [projectPath] - 專案路徑(多專案支援)
|
|
35
|
-
* @returns {string|null}
|
|
36
|
-
*/
|
|
37
|
-
function findTaskLog(taskId, projectPath) {
|
|
38
|
-
const logDir = projectPath ? getLogDir(projectPath) : LOG_DIR;
|
|
39
|
-
const formations = ['unit', 'solo', 'squad', 'legion', 'misc'];
|
|
40
|
-
let candidates = [];
|
|
41
|
-
|
|
42
|
-
for (const formation of formations) {
|
|
43
|
-
const dir = path.join(logDir, formation);
|
|
44
|
-
if (!fs.existsSync(dir)) continue;
|
|
45
|
-
|
|
46
|
-
const files = fs.readdirSync(dir)
|
|
47
|
-
.filter(f => f.endsWith('.log'))
|
|
48
|
-
.sort((a, b) => {
|
|
49
|
-
const aErr = a.endsWith('.err.log') ? 1 : 0;
|
|
50
|
-
const bErr = b.endsWith('.err.log') ? 1 : 0;
|
|
51
|
-
return aErr - bErr;
|
|
52
|
-
});
|
|
53
|
-
for (const file of files) {
|
|
54
|
-
const filePath = path.join(dir, file);
|
|
55
|
-
try {
|
|
56
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
57
|
-
if (content.includes(taskId)) {
|
|
58
|
-
const stats = fs.statSync(filePath);
|
|
59
|
-
candidates.push({ path: filePath, mtime: stats.mtime });
|
|
60
|
-
}
|
|
61
|
-
} catch (e) {
|
|
62
|
-
// skip unreadable files
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (candidates.length === 0) return null;
|
|
68
|
-
|
|
69
|
-
// 回傳最新的
|
|
70
|
-
candidates.sort((a, b) => b.mtime - a.mtime);
|
|
71
|
-
return candidates[0].path;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* 取得指定 formation 的最新 log
|
|
76
|
-
* @param {string} formation - unit / solo / squad / legion
|
|
77
|
-
* @param {string} [projectPath] - 專案路徑(多專案支援)
|
|
78
|
-
* @returns {string|null}
|
|
79
|
-
*/
|
|
80
|
-
function findLatestLog(formation, projectPath) {
|
|
81
|
-
const logDir = projectPath ? getLogDir(projectPath) : LOG_DIR;
|
|
82
|
-
const dir = path.join(logDir, formation);
|
|
83
|
-
if (!fs.existsSync(dir)) return null;
|
|
84
|
-
|
|
85
|
-
const files = fs.readdirSync(dir)
|
|
86
|
-
.filter(f => f.endsWith('.log'))
|
|
87
|
-
.sort((a, b) => {
|
|
88
|
-
const aErr = a.endsWith('.err.log') ? 1 : 0;
|
|
89
|
-
const bErr = b.endsWith('.err.log') ? 1 : 0;
|
|
90
|
-
return aErr - bErr;
|
|
91
|
-
})
|
|
92
|
-
.map(f => {
|
|
93
|
-
const filePath = path.join(dir, f);
|
|
94
|
-
const stats = fs.statSync(filePath);
|
|
95
|
-
return { path: filePath, mtime: stats.mtime };
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
if (files.length === 0) return null;
|
|
99
|
-
|
|
100
|
-
files.sort((a, b) => b.mtime - a.mtime);
|
|
101
|
-
return files[0].path;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// ─────────────────────────────────────────
|
|
105
|
-
// 讀取與過濾
|
|
106
|
-
// ─────────────────────────────────────────
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* 讀取 log 並根據選項過濾
|
|
110
|
-
* @param {string} logPath
|
|
111
|
-
* @param {object} opts
|
|
112
|
-
* @param {number} [opts.tail] - 最後 N 行
|
|
113
|
-
* @param {number} [opts.head] - 前 N 行
|
|
114
|
-
* @param {boolean} [opts.errors] - 只顯示錯誤
|
|
115
|
-
*/
|
|
116
|
-
function readLog(logPath, opts = {}) {
|
|
117
|
-
const { tail, head, errors } = opts;
|
|
118
|
-
|
|
119
|
-
if (!fs.existsSync(logPath)) {
|
|
120
|
-
console.error(`❌ Log file not found: ${logPath}`);
|
|
121
|
-
process.exit(1);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const content = fs.readFileSync(logPath, 'utf-8');
|
|
125
|
-
let lines = content.split('\n');
|
|
126
|
-
|
|
127
|
-
// 過濾錯誤行
|
|
128
|
-
if (errors) {
|
|
129
|
-
// TASK_1712: Added REFUSED to surface injection-refusal runs in --errors filter
|
|
130
|
-
const errorKeywords = ['ERROR', 'FAILED', 'FAIL', 'REFUSED', '❌', 'exit 1', 'Exit Code: 1'];
|
|
131
|
-
lines = lines.filter(line => {
|
|
132
|
-
const upper = line.toUpperCase();
|
|
133
|
-
return errorKeywords.some(kw => upper.includes(kw.toUpperCase()));
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// 取前/後 N 行
|
|
138
|
-
if (head) {
|
|
139
|
-
lines = lines.slice(0, head);
|
|
140
|
-
} else if (tail) {
|
|
141
|
-
lines = lines.slice(-tail);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
console.log(`─── Log: ${logPath} ───`);
|
|
145
|
-
if (tail) console.log(`─── Last ${tail} lines ───`);
|
|
146
|
-
if (head) console.log(`─── First ${head} lines ───`);
|
|
147
|
-
if (errors) console.log(`─── Error lines only ───`);
|
|
148
|
-
console.log('');
|
|
149
|
-
|
|
150
|
-
console.log(lines.join('\n'));
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// ─────────────────────────────────────────
|
|
154
|
-
// CLI
|
|
155
|
-
// ─────────────────────────────────────────
|
|
156
|
-
|
|
157
|
-
if (require.main === module) {
|
|
158
|
-
const args = process.argv.slice(2);
|
|
159
|
-
|
|
160
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
161
|
-
console.log(`
|
|
162
|
-
⚔️ Formation Log Reader
|
|
163
|
-
|
|
164
|
-
讀取 Formation log 檔案的尾端/頭端/錯誤行
|
|
165
|
-
|
|
166
|
-
Usage:
|
|
167
|
-
node .formation/tools/log-reader.js --log <path> --tail 50
|
|
168
|
-
node .formation/tools/log-reader.js --task TASK_XXX --tail 50
|
|
169
|
-
node .formation/tools/log-reader.js --latest unit --tail 30
|
|
170
|
-
|
|
171
|
-
Options:
|
|
172
|
-
--log <path> 直接讀指定 log 檔
|
|
173
|
-
--task TASK_XXX 在 .formation/logs/ 下搜尋包含 TASK_XXX 的最新 log
|
|
174
|
-
--latest <formation> 讀指定陣型目錄下最新的 log
|
|
175
|
-
--tail N 只輸出最後 N 行(預設 50)
|
|
176
|
-
--head N 只輸出前 N 行
|
|
177
|
-
--errors 只輸出包含 ERROR/FAILED/❌ 的行
|
|
178
|
-
|
|
179
|
-
Examples:
|
|
180
|
-
node .formation/tools/log-reader.js --log .formation/logs/unit/do_abc123.log --tail 30
|
|
181
|
-
node .formation/tools/log-reader.js --task TASK_248 --errors
|
|
182
|
-
node .formation/tools/log-reader.js --latest squad --tail 50
|
|
183
|
-
`);
|
|
184
|
-
process.exit(0);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const getArg = (flag) => {
|
|
188
|
-
for (let i = 0; i < args.length; i++) {
|
|
189
|
-
if (args[i].startsWith(`${flag}=`)) return args[i].split('=').slice(1).join('=');
|
|
190
|
-
if (args[i] === flag && args[i + 1]) return args[i + 1];
|
|
191
|
-
}
|
|
192
|
-
return null;
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
const logPath = getArg('--log');
|
|
196
|
-
const taskId = getArg('--task');
|
|
197
|
-
const latest = getArg('--latest');
|
|
198
|
-
const projectPath = getArg('--project-path');
|
|
199
|
-
const tail = parseInt(getArg('--tail') || '50', 10);
|
|
200
|
-
const head = parseInt(getArg('--head') || '0', 10);
|
|
201
|
-
const errors = args.includes('--errors');
|
|
202
|
-
|
|
203
|
-
let resolvedPath = null;
|
|
204
|
-
|
|
205
|
-
if (logPath) {
|
|
206
|
-
resolvedPath = logPath;
|
|
207
|
-
} else if (taskId) {
|
|
208
|
-
resolvedPath = findTaskLog(taskId, projectPath);
|
|
209
|
-
if (!resolvedPath) {
|
|
210
|
-
console.error(`❌ No log found for task: ${taskId}`);
|
|
211
|
-
process.exit(1);
|
|
212
|
-
}
|
|
213
|
-
} else if (latest) {
|
|
214
|
-
resolvedPath = findLatestLog(latest, projectPath);
|
|
215
|
-
if (!resolvedPath) {
|
|
216
|
-
console.error(`❌ No log found in formation: ${latest}`);
|
|
217
|
-
process.exit(1);
|
|
218
|
-
}
|
|
219
|
-
} else {
|
|
220
|
-
console.error('❌ Must specify one of: --log, --task, --latest');
|
|
221
|
-
process.exit(1);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const opts = {};
|
|
225
|
-
if (head > 0) opts.head = head;
|
|
226
|
-
else if (tail > 0) opts.tail = tail;
|
|
227
|
-
if (errors) opts.errors = true;
|
|
228
|
-
|
|
229
|
-
readLog(resolvedPath, opts);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
module.exports = { findTaskLog, findLatestLog, readLog };
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* revert-daemon-state.js
|
|
4
|
-
*
|
|
5
|
-
* On-demand cleanup for daemon-drift on the user branch between task merges.
|
|
6
|
-
*
|
|
7
|
-
* The post-complete merge pipeline already auto-reverts daemon-managed paths
|
|
8
|
-
* via .kenaz/specialist/daemon-manifest.yaml + isDaemonManagedPath() inside
|
|
9
|
-
* post-complete.js (worktree merge step). But that only fires DURING a task
|
|
10
|
-
* merge. Between sessions, daemons keep writing to those files and
|
|
11
|
-
* `git status` shows them as modified — pure noise on the user branch.
|
|
12
|
-
*
|
|
13
|
-
* This CLI uses the same manifest as the merge pipeline, but runs ad-hoc:
|
|
14
|
-
* 1. Load .kenaz/specialist/daemon-manifest.yaml
|
|
15
|
-
* 2. For every enrolled state_files entry + every file matching a static_paths
|
|
16
|
-
* directory entry, check `git status --porcelain` for modification
|
|
17
|
-
* 3. Run `git checkout HEAD -- <path>` to revert each
|
|
18
|
-
*
|
|
19
|
-
* Untracked files are left alone. Files that are not enrolled in the manifest
|
|
20
|
-
* are left alone. The CLI is idempotent and never touches scope_paths or
|
|
21
|
-
* non-daemon files.
|
|
22
|
-
*
|
|
23
|
-
* Usage:
|
|
24
|
-
* node .formation/tools/revert-daemon-state.js # do it
|
|
25
|
-
* node .formation/tools/revert-daemon-state.js --dry # show plan only
|
|
26
|
-
* node .formation/tools/revert-daemon-state.js --quiet # only summary line
|
|
27
|
-
*
|
|
28
|
-
* Exit code: 0 on success (even if nothing reverted), 1 on manifest load
|
|
29
|
-
* failure or git command failure.
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
'use strict';
|
|
33
|
-
|
|
34
|
-
const fs = require('fs');
|
|
35
|
-
const path = require('path');
|
|
36
|
-
const { execSync, spawnSync } = require('child_process');
|
|
37
|
-
|
|
38
|
-
const ROOT = path.resolve(__dirname, '..', '..');
|
|
39
|
-
const MANIFEST = path.join(ROOT, '.kenaz', 'specialist', 'daemon-manifest.yaml');
|
|
40
|
-
|
|
41
|
-
const argv = process.argv.slice(2);
|
|
42
|
-
const DRY = argv.includes('--dry') || argv.includes('--dry-run');
|
|
43
|
-
const QUIET = argv.includes('--quiet');
|
|
44
|
-
|
|
45
|
-
function log(...args) {
|
|
46
|
-
if (!QUIET) console.log(...args);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function loadYaml(filePath) {
|
|
50
|
-
let yaml;
|
|
51
|
-
try {
|
|
52
|
-
yaml = require('js-yaml');
|
|
53
|
-
} catch {
|
|
54
|
-
const candidates = [
|
|
55
|
-
path.resolve(__dirname, '../../.vendor/js-yaml.cjs'),
|
|
56
|
-
path.resolve(__dirname, '.vendor/js-yaml.cjs'),
|
|
57
|
-
path.resolve(__dirname, '..', '.vendor', 'js-yaml.cjs'),
|
|
58
|
-
];
|
|
59
|
-
for (const c of candidates) {
|
|
60
|
-
try { yaml = require(c); break; } catch {}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (!yaml) throw new Error('js-yaml not available — check .formation/.vendor/');
|
|
64
|
-
return yaml.load(fs.readFileSync(filePath, 'utf8'));
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function loadManifest() {
|
|
68
|
-
if (!fs.existsSync(MANIFEST)) {
|
|
69
|
-
throw new Error(`daemon-manifest.yaml not found at ${MANIFEST}`);
|
|
70
|
-
}
|
|
71
|
-
const raw = loadYaml(MANIFEST);
|
|
72
|
-
return {
|
|
73
|
-
stateFiles: Array.isArray(raw?.state_files) ? raw.state_files : [],
|
|
74
|
-
staticPaths: Array.isArray(raw?.static_paths) ? raw.static_paths : [],
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function normalizeRepoPath(p) {
|
|
79
|
-
return String(p || '')
|
|
80
|
-
.replace(/\\/g, '/')
|
|
81
|
-
.replace(/^\.\//, '')
|
|
82
|
-
.replace(/\/+/g, '/');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function listModifiedTracked() {
|
|
86
|
-
const out = execSync('git status --porcelain --untracked-files=no', {
|
|
87
|
-
cwd: ROOT, encoding: 'utf8', windowsHide: true,
|
|
88
|
-
});
|
|
89
|
-
const modified = [];
|
|
90
|
-
for (const line of out.split(/\r?\n/)) {
|
|
91
|
-
if (!line) continue;
|
|
92
|
-
const xy = line.slice(0, 2);
|
|
93
|
-
const rel = normalizeRepoPath(line.slice(3).trim());
|
|
94
|
-
if (xy.includes('M') || xy.includes('A') || xy.includes('D') || xy.includes('R')) {
|
|
95
|
-
modified.push(rel);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return modified;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function isManagedByManifest(relPath, manifest) {
|
|
102
|
-
const norm = normalizeRepoPath(relPath);
|
|
103
|
-
if (!norm) return false;
|
|
104
|
-
if (manifest.stateFiles.includes(norm)) return true;
|
|
105
|
-
for (const sp of manifest.staticPaths) {
|
|
106
|
-
const spn = normalizeRepoPath(sp);
|
|
107
|
-
if (spn === norm) return true;
|
|
108
|
-
if (spn.endsWith('/') && norm.startsWith(spn)) return true;
|
|
109
|
-
}
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function gitCheckoutHead(paths) {
|
|
114
|
-
if (paths.length === 0) return { ok: true, stderr: '' };
|
|
115
|
-
// Use spawnSync with --pathspec-file via stdin to avoid command-line length limits
|
|
116
|
-
const args = ['checkout', 'HEAD', '--', ...paths];
|
|
117
|
-
const r = spawnSync('git', args, { cwd: ROOT, encoding: 'utf8', windowsHide: true });
|
|
118
|
-
return { ok: r.status === 0, stderr: r.stderr || '' };
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function main() {
|
|
122
|
-
let manifest;
|
|
123
|
-
try { manifest = loadManifest(); }
|
|
124
|
-
catch (e) {
|
|
125
|
-
console.error(`✗ ${e.message}`);
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const modified = listModifiedTracked();
|
|
130
|
-
const managed = modified.filter(p => isManagedByManifest(p, manifest));
|
|
131
|
-
const unmanaged = modified.filter(p => !isManagedByManifest(p, manifest));
|
|
132
|
-
|
|
133
|
-
if (managed.length === 0) {
|
|
134
|
-
log(`✓ No daemon-managed drift in working tree (${modified.length} other modified file(s) untouched).`);
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
log(`Daemon-managed drift detected (${managed.length} file(s)):`);
|
|
139
|
-
for (const p of managed) log(` ~ ${p}`);
|
|
140
|
-
|
|
141
|
-
if (unmanaged.length > 0 && !QUIET) {
|
|
142
|
-
log('');
|
|
143
|
-
log(`Non-daemon modifications (left alone):`);
|
|
144
|
-
for (const p of unmanaged) log(` ${p}`);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (DRY) {
|
|
148
|
-
log('');
|
|
149
|
-
log('[--dry] No changes made. Re-run without --dry to revert.');
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
log('');
|
|
154
|
-
const result = gitCheckoutHead(managed);
|
|
155
|
-
if (!result.ok) {
|
|
156
|
-
console.error(`✗ git checkout failed:\n${result.stderr.trim()}`);
|
|
157
|
-
process.exit(1);
|
|
158
|
-
}
|
|
159
|
-
console.log(`✓ Reverted ${managed.length} daemon-managed file(s) to HEAD.`);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
main();
|