kenaz 0.3.18 → 0.3.19
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__/m1-hmap-convid.test.js +1 -124
- package/core/.formation/tools/dispatch.js +1 -1
- package/core/.formation/tools/loader.js +17 -140
- package/core/.formation/tools/post-complete.js +0 -31
- 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/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/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/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/scripts/sync.js +1 -1
- 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/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,102 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* matrix delete command
|
|
3
|
-
* Deletes a rule from .matrix file and auto-updates @index statistics
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import inquirer from 'inquirer';
|
|
7
|
-
import { MatrixParser } from '../../parser';
|
|
8
|
-
import { MatrixSerializer } from '../../serializer';
|
|
9
|
-
import { Logger } from '../utils/logger';
|
|
10
|
-
import { FileHandler } from '../utils/file-handler';
|
|
11
|
-
|
|
12
|
-
interface DeleteOptions {
|
|
13
|
-
force?: boolean;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export async function deleteCommand(
|
|
17
|
-
filePath: string,
|
|
18
|
-
ruleId: string,
|
|
19
|
-
options: DeleteOptions
|
|
20
|
-
): Promise<void> {
|
|
21
|
-
Logger.header('Delete .matrix Rule');
|
|
22
|
-
|
|
23
|
-
// Ensure .matrix extension
|
|
24
|
-
const targetPath = FileHandler.ensureExtension(filePath);
|
|
25
|
-
|
|
26
|
-
// Read and parse file
|
|
27
|
-
const content = FileHandler.read(targetPath);
|
|
28
|
-
if (!content) {
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const parser = new MatrixParser();
|
|
33
|
-
const result = parser.parse(content);
|
|
34
|
-
|
|
35
|
-
if (!result.success) {
|
|
36
|
-
Logger.error('Failed to parse file');
|
|
37
|
-
if (result.error) {
|
|
38
|
-
Logger.error(` ${result.error.message}`);
|
|
39
|
-
}
|
|
40
|
-
process.exit(1);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const matrixFile = result.data!;
|
|
44
|
-
|
|
45
|
-
// Find rule by ID
|
|
46
|
-
if (!matrixFile.data) {
|
|
47
|
-
Logger.error('No data records found in file');
|
|
48
|
-
process.exit(1);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const recordIndex = matrixFile.data.records.findIndex(r => r.id === ruleId);
|
|
52
|
-
if (recordIndex === -1) {
|
|
53
|
-
Logger.error(`Rule not found: ${ruleId}`);
|
|
54
|
-
Logger.info('Use "matrix read" to list available rules');
|
|
55
|
-
process.exit(1);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const record = matrixFile.data.records[recordIndex];
|
|
59
|
-
|
|
60
|
-
Logger.info(`Found rule: ${ruleId}`);
|
|
61
|
-
Logger.record(ruleId, record.fields);
|
|
62
|
-
|
|
63
|
-
// Confirm deletion
|
|
64
|
-
if (!options.force) {
|
|
65
|
-
const answers = await inquirer.prompt([
|
|
66
|
-
{
|
|
67
|
-
type: 'confirm',
|
|
68
|
-
name: 'confirm',
|
|
69
|
-
message: `Are you sure you want to delete rule "${ruleId}"?`,
|
|
70
|
-
default: false,
|
|
71
|
-
},
|
|
72
|
-
]);
|
|
73
|
-
|
|
74
|
-
if (!answers.confirm) {
|
|
75
|
-
Logger.info('Deletion cancelled');
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Delete record
|
|
81
|
-
matrixFile.data.records.splice(recordIndex, 1);
|
|
82
|
-
|
|
83
|
-
// Auto-update @index statistics
|
|
84
|
-
if (matrixFile.index.n !== undefined) {
|
|
85
|
-
matrixFile.index.n = matrixFile.data.records.length;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (matrixFile.index.upd !== undefined) {
|
|
89
|
-
matrixFile.index.upd = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '');
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Serialize and write
|
|
93
|
-
const serializer = new MatrixSerializer();
|
|
94
|
-
const newContent = serializer.serialize(matrixFile);
|
|
95
|
-
|
|
96
|
-
if (FileHandler.write(targetPath, newContent)) {
|
|
97
|
-
Logger.success(`Deleted rule: ${ruleId}`);
|
|
98
|
-
Logger.field('Remaining rules', matrixFile.data.records.length);
|
|
99
|
-
} else {
|
|
100
|
-
process.exit(1);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* list-tags command - List all tags and their usage counts
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import chalk from 'chalk';
|
|
6
|
-
import { resolveFilePath } from '../utils/file-handler';
|
|
7
|
-
import { parseMatrixFile } from '../../parser';
|
|
8
|
-
import { TagIndexer } from '../../indexer/tag-indexer';
|
|
9
|
-
|
|
10
|
-
interface ListTagsOptions {
|
|
11
|
-
type?: string; // Filter by tag type: source, category, priority, domain, custom
|
|
12
|
-
sort?: 'name' | 'count'; // Sort by name or count
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export async function listTagsCommand(
|
|
16
|
-
file: string,
|
|
17
|
-
options: ListTagsOptions = {}
|
|
18
|
-
): Promise<void> {
|
|
19
|
-
const filePath = resolveFilePath(file);
|
|
20
|
-
|
|
21
|
-
// Parse file
|
|
22
|
-
const matrixFile = parseMatrixFile(filePath);
|
|
23
|
-
|
|
24
|
-
// Build tag index
|
|
25
|
-
const indexer = new TagIndexer();
|
|
26
|
-
indexer.buildIndex(matrixFile);
|
|
27
|
-
|
|
28
|
-
// Get tag statistics
|
|
29
|
-
let tagStats = indexer.getTagStats();
|
|
30
|
-
|
|
31
|
-
// Filter by type if specified
|
|
32
|
-
if (options.type) {
|
|
33
|
-
const typePrefix = options.type.toLowerCase();
|
|
34
|
-
tagStats = tagStats.filter((stat) => {
|
|
35
|
-
if (typePrefix === 'custom') {
|
|
36
|
-
return stat.tag.startsWith('#');
|
|
37
|
-
}
|
|
38
|
-
return stat.tag.startsWith(`${typePrefix}:`);
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Sort by name if specified
|
|
43
|
-
if (options.sort === 'name') {
|
|
44
|
-
tagStats.sort((a, b) => a.tag.localeCompare(b.tag));
|
|
45
|
-
}
|
|
46
|
-
// Default: already sorted by count descending
|
|
47
|
-
|
|
48
|
-
console.log(chalk.bold.cyan('\n🏷️ Tags\n'));
|
|
49
|
-
console.log(chalk.gray(`File: ${filePath}\n`));
|
|
50
|
-
|
|
51
|
-
if (tagStats.length === 0) {
|
|
52
|
-
console.log(chalk.yellow('No tags found.'));
|
|
53
|
-
console.log();
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Group tags by type
|
|
58
|
-
const groupedTags = groupTagsByType(tagStats);
|
|
59
|
-
|
|
60
|
-
// Display each group
|
|
61
|
-
for (const [type, tags] of Object.entries(groupedTags)) {
|
|
62
|
-
if (!options.type || options.type.toLowerCase() === type.toLowerCase()) {
|
|
63
|
-
console.log(chalk.bold(`${getTypeEmoji(type)} ${type}:`));
|
|
64
|
-
|
|
65
|
-
for (const stat of tags) {
|
|
66
|
-
const tagDisplay = stat.tag.replace(`${type.toLowerCase()}:`, '');
|
|
67
|
-
const bar = '█'.repeat(Math.min(stat.count, 20));
|
|
68
|
-
console.log(
|
|
69
|
-
` ${chalk.cyan(tagDisplay.padEnd(20))} ${chalk.green(bar)} ${chalk.yellow(stat.count)}`
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
console.log();
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Summary
|
|
78
|
-
console.log(chalk.bold('Summary:'));
|
|
79
|
-
console.log(` Total tags: ${chalk.cyan(tagStats.length)}`);
|
|
80
|
-
console.log(` Total tag types: ${chalk.cyan(Object.keys(groupedTags).length)}`);
|
|
81
|
-
console.log();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Group tags by type
|
|
86
|
-
*/
|
|
87
|
-
function groupTagsByType(
|
|
88
|
-
tagStats: { tag: string; count: number; ruleIds: string[] }[]
|
|
89
|
-
): Record<string, typeof tagStats> {
|
|
90
|
-
const grouped: Record<string, typeof tagStats> = {
|
|
91
|
-
Source: [],
|
|
92
|
-
Category: [],
|
|
93
|
-
Priority: [],
|
|
94
|
-
Domain: [],
|
|
95
|
-
Custom: [],
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
for (const stat of tagStats) {
|
|
99
|
-
if (stat.tag.startsWith('source:')) {
|
|
100
|
-
grouped.Source.push(stat);
|
|
101
|
-
} else if (stat.tag.startsWith('category:')) {
|
|
102
|
-
grouped.Category.push(stat);
|
|
103
|
-
} else if (stat.tag.startsWith('priority:')) {
|
|
104
|
-
grouped.Priority.push(stat);
|
|
105
|
-
} else if (stat.tag.startsWith('domain:')) {
|
|
106
|
-
grouped.Domain.push(stat);
|
|
107
|
-
} else {
|
|
108
|
-
grouped.Custom.push(stat);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Remove empty groups
|
|
113
|
-
for (const key of Object.keys(grouped)) {
|
|
114
|
-
if (grouped[key].length === 0) {
|
|
115
|
-
delete grouped[key];
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return grouped;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Get emoji for tag type
|
|
124
|
-
*/
|
|
125
|
-
function getTypeEmoji(type: string): string {
|
|
126
|
-
switch (type.toLowerCase()) {
|
|
127
|
-
case 'source':
|
|
128
|
-
return '📦';
|
|
129
|
-
case 'category':
|
|
130
|
-
return '📂';
|
|
131
|
-
case 'priority':
|
|
132
|
-
return '⚡';
|
|
133
|
-
case 'domain':
|
|
134
|
-
return '🌐';
|
|
135
|
-
case 'custom':
|
|
136
|
-
return '🏷️';
|
|
137
|
-
default:
|
|
138
|
-
return '•';
|
|
139
|
-
}
|
|
140
|
-
}
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* add-tag / remove-tag commands - Manage rule tags
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import chalk from 'chalk';
|
|
6
|
-
import * as fs from 'fs';
|
|
7
|
-
import { resolveFilePath } from '../utils/file-handler';
|
|
8
|
-
import { parseMatrixFile } from '../../parser';
|
|
9
|
-
import { serializeMatrixFile } from '../../serializer';
|
|
10
|
-
import { TagIndexer, buildTagIndex } from '../../indexer/tag-indexer';
|
|
11
|
-
|
|
12
|
-
interface ManageTagsOptions {
|
|
13
|
-
force?: boolean;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Add tag to a rule
|
|
18
|
-
*/
|
|
19
|
-
export async function addTagCommand(
|
|
20
|
-
file: string,
|
|
21
|
-
ruleId: string,
|
|
22
|
-
tag: string,
|
|
23
|
-
options: ManageTagsOptions = {}
|
|
24
|
-
): Promise<void> {
|
|
25
|
-
const filePath = resolveFilePath(file);
|
|
26
|
-
|
|
27
|
-
// Parse file
|
|
28
|
-
const matrixFile = parseMatrixFile(filePath);
|
|
29
|
-
|
|
30
|
-
// Find rule
|
|
31
|
-
const rule = matrixFile.rules.find((r) => r.id === ruleId);
|
|
32
|
-
if (!rule) {
|
|
33
|
-
throw new Error(`Rule not found: ${ruleId}`);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Ensure tag starts with # (for custom tags)
|
|
37
|
-
const normalizedTag = tag.startsWith('#') ? tag : `#${tag}`;
|
|
38
|
-
|
|
39
|
-
// Initialize tags array if not exists
|
|
40
|
-
if (!rule.tags) {
|
|
41
|
-
rule.tags = [];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Check if tag already exists
|
|
45
|
-
if (Array.isArray(rule.tags) && rule.tags.includes(normalizedTag)) {
|
|
46
|
-
console.log(chalk.yellow(`Tag already exists: ${normalizedTag}`));
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Add tag
|
|
51
|
-
if (Array.isArray(rule.tags)) {
|
|
52
|
-
rule.tags.push(normalizedTag);
|
|
53
|
-
} else {
|
|
54
|
-
rule.tags = [normalizedTag];
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Save file
|
|
58
|
-
const serialized = serializeMatrixFile(matrixFile);
|
|
59
|
-
fs.writeFileSync(filePath, serialized, 'utf-8');
|
|
60
|
-
|
|
61
|
-
// Rebuild tag index
|
|
62
|
-
const indexPath = filePath.replace('.matrix', '.tags.json');
|
|
63
|
-
buildTagIndex(matrixFile, indexPath);
|
|
64
|
-
|
|
65
|
-
console.log(chalk.green(`✓ Tag added: ${normalizedTag} to ${ruleId}`));
|
|
66
|
-
console.log(chalk.gray(` Index updated: ${indexPath}`));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Remove tag from a rule
|
|
71
|
-
*/
|
|
72
|
-
export async function removeTagCommand(
|
|
73
|
-
file: string,
|
|
74
|
-
ruleId: string,
|
|
75
|
-
tag: string,
|
|
76
|
-
options: ManageTagsOptions = {}
|
|
77
|
-
): Promise<void> {
|
|
78
|
-
const filePath = resolveFilePath(file);
|
|
79
|
-
|
|
80
|
-
// Parse file
|
|
81
|
-
const matrixFile = parseMatrixFile(filePath);
|
|
82
|
-
|
|
83
|
-
// Find rule
|
|
84
|
-
const rule = matrixFile.rules.find((r) => r.id === ruleId);
|
|
85
|
-
if (!rule) {
|
|
86
|
-
throw new Error(`Rule not found: ${ruleId}`);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Ensure tag starts with # (for custom tags)
|
|
90
|
-
const normalizedTag = tag.startsWith('#') ? tag : `#${tag}`;
|
|
91
|
-
|
|
92
|
-
// Check if rule has tags
|
|
93
|
-
if (!rule.tags || !Array.isArray(rule.tags)) {
|
|
94
|
-
console.log(chalk.yellow(`Rule has no tags: ${ruleId}`));
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Find tag index
|
|
99
|
-
const tagIndex = rule.tags.indexOf(normalizedTag);
|
|
100
|
-
if (tagIndex === -1) {
|
|
101
|
-
console.log(chalk.yellow(`Tag not found: ${normalizedTag}`));
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Remove tag
|
|
106
|
-
rule.tags.splice(tagIndex, 1);
|
|
107
|
-
|
|
108
|
-
// Save file
|
|
109
|
-
const serialized = serializeMatrixFile(matrixFile);
|
|
110
|
-
fs.writeFileSync(filePath, serialized, 'utf-8');
|
|
111
|
-
|
|
112
|
-
// Rebuild tag index
|
|
113
|
-
const indexPath = filePath.replace('.matrix', '.tags.json');
|
|
114
|
-
buildTagIndex(matrixFile, indexPath);
|
|
115
|
-
|
|
116
|
-
console.log(chalk.green(`✓ Tag removed: ${normalizedTag} from ${ruleId}`));
|
|
117
|
-
console.log(chalk.gray(` Index updated: ${indexPath}`));
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* List tags for a specific rule
|
|
122
|
-
*/
|
|
123
|
-
export async function showRuleTagsCommand(
|
|
124
|
-
file: string,
|
|
125
|
-
ruleId: string
|
|
126
|
-
): Promise<void> {
|
|
127
|
-
const filePath = resolveFilePath(file);
|
|
128
|
-
|
|
129
|
-
// Parse file
|
|
130
|
-
const matrixFile = parseMatrixFile(filePath);
|
|
131
|
-
|
|
132
|
-
// Find rule
|
|
133
|
-
const rule = matrixFile.rules.find((r) => r.id === ruleId);
|
|
134
|
-
if (!rule) {
|
|
135
|
-
throw new Error(`Rule not found: ${ruleId}`);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
console.log(chalk.bold.cyan(`\n🏷️ Tags for ${ruleId}\n`));
|
|
139
|
-
|
|
140
|
-
// Collect all tags
|
|
141
|
-
const allTags: string[] = [];
|
|
142
|
-
|
|
143
|
-
if (rule.source) allTags.push(`source:${rule.source}`);
|
|
144
|
-
if (rule.category) allTags.push(`category:${rule.category}`);
|
|
145
|
-
if (rule.priority) allTags.push(`priority:${rule.priority}`);
|
|
146
|
-
if (rule.domain) allTags.push(`domain:${rule.domain}`);
|
|
147
|
-
if (rule.tags && Array.isArray(rule.tags)) {
|
|
148
|
-
allTags.push(...rule.tags);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (allTags.length === 0) {
|
|
152
|
-
console.log(chalk.yellow('No tags found.'));
|
|
153
|
-
} else {
|
|
154
|
-
for (const tag of allTags) {
|
|
155
|
-
const emoji = getTagEmoji(tag);
|
|
156
|
-
console.log(` ${emoji} ${chalk.cyan(tag)}`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
console.log();
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Get emoji for tag
|
|
165
|
-
*/
|
|
166
|
-
function getTagEmoji(tag: string): string {
|
|
167
|
-
if (tag.startsWith('source:')) return '📦';
|
|
168
|
-
if (tag.startsWith('category:')) return '📂';
|
|
169
|
-
if (tag.startsWith('priority:')) return '⚡';
|
|
170
|
-
if (tag.startsWith('domain:')) return '🌐';
|
|
171
|
-
if (tag.startsWith('#')) return '🏷️';
|
|
172
|
-
return '•';
|
|
173
|
-
}
|
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Query command - 查詢 .matrix 文件
|
|
3
|
-
* 支援標籤過濾、優先級過濾、狀態過濾、關鍵字搜尋
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import * as fs from 'fs';
|
|
7
|
-
import { MatrixParser } from '../../parser';
|
|
8
|
-
import { log, error as logError } from '../utils/logger';
|
|
9
|
-
import type { MatrixFile, DataRecord } from '../../../types';
|
|
10
|
-
|
|
11
|
-
interface QueryOptions {
|
|
12
|
-
tag?: string;
|
|
13
|
-
priority?: string;
|
|
14
|
-
status?: string;
|
|
15
|
-
keyword?: string;
|
|
16
|
-
format?: 'table' | 'json' | 'compact';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface QueryResult {
|
|
20
|
-
records: DataRecord[];
|
|
21
|
-
total: number;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Query command handler
|
|
26
|
-
*/
|
|
27
|
-
export async function queryCommand(file: string, options: QueryOptions): Promise<void> {
|
|
28
|
-
try {
|
|
29
|
-
// 確保文件有 .matrix 擴展名
|
|
30
|
-
const filePath = file.endsWith('.matrix') ? file : `${file}.matrix`;
|
|
31
|
-
|
|
32
|
-
// 檢查文件是否存在
|
|
33
|
-
if (!fs.existsSync(filePath)) {
|
|
34
|
-
throw new Error(`File not found: ${filePath}`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// 讀取並解析文件
|
|
38
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
39
|
-
const parser = new MatrixParser();
|
|
40
|
-
const parseResult = parser.parse(content);
|
|
41
|
-
|
|
42
|
-
if (!parseResult.success || !parseResult.data) {
|
|
43
|
-
throw new Error(`Failed to parse file: ${parseResult.error?.message || 'Unknown error'}`);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const matrixFile = parseResult.data;
|
|
47
|
-
|
|
48
|
-
// 執行查詢
|
|
49
|
-
const result = executeQuery(matrixFile, options);
|
|
50
|
-
|
|
51
|
-
// 輸出結果
|
|
52
|
-
displayResults(result, options.format || 'table', matrixFile);
|
|
53
|
-
|
|
54
|
-
} catch (err) {
|
|
55
|
-
logError((err as Error).message);
|
|
56
|
-
throw err;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* 執行查詢邏輯(支援索引優化)
|
|
62
|
-
*/
|
|
63
|
-
function executeQuery(matrixFile: MatrixFile, options: QueryOptions): QueryResult {
|
|
64
|
-
// 如果沒有 data section,返回空結果
|
|
65
|
-
if (!matrixFile.data || !matrixFile.data.records) {
|
|
66
|
-
return { records: [], total: 0 };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
let filteredRecords = matrixFile.data.records;
|
|
70
|
-
|
|
71
|
-
// 索引優化:先檢查 @index 層是否有快速過濾機會
|
|
72
|
-
// 例如:如果查詢 status=completed,先查看 index 的 completed 計數
|
|
73
|
-
if (options.status && matrixFile.index) {
|
|
74
|
-
const statusKey = getStatusKey(options.status);
|
|
75
|
-
const countInIndex = matrixFile.index[statusKey];
|
|
76
|
-
|
|
77
|
-
// 如果 index 顯示該狀態為 0,直接返回空結果
|
|
78
|
-
if (countInIndex === 0) {
|
|
79
|
-
return { records: [], total: 0 };
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// 過濾:tag
|
|
84
|
-
if (options.tag) {
|
|
85
|
-
filteredRecords = filteredRecords.filter(record => {
|
|
86
|
-
const tags = record.fields.tags || record.fields.tag;
|
|
87
|
-
if (Array.isArray(tags)) {
|
|
88
|
-
return tags.includes(options.tag);
|
|
89
|
-
}
|
|
90
|
-
if (typeof tags === 'string') {
|
|
91
|
-
return tags.split(',').map(t => t.trim()).includes(options.tag);
|
|
92
|
-
}
|
|
93
|
-
return false;
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// 過濾:priority
|
|
98
|
-
if (options.priority) {
|
|
99
|
-
filteredRecords = filteredRecords.filter(record => {
|
|
100
|
-
const priority = record.fields.priority || record.fields.pri || record.fields.p;
|
|
101
|
-
return priority === options.priority || priority === options.priority.toUpperCase();
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// 過濾:status
|
|
106
|
-
if (options.status) {
|
|
107
|
-
filteredRecords = filteredRecords.filter(record => {
|
|
108
|
-
const status = record.fields.status || record.fields.st;
|
|
109
|
-
return matchStatus(status, options.status!);
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// 關鍵字搜尋(在所有字段中搜尋)
|
|
114
|
-
if (options.keyword) {
|
|
115
|
-
const keyword = options.keyword.toLowerCase();
|
|
116
|
-
filteredRecords = filteredRecords.filter(record => {
|
|
117
|
-
// 搜尋 ID
|
|
118
|
-
if (record.id.toLowerCase().includes(keyword)) {
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// 搜尋所有欄位值
|
|
123
|
-
for (const value of Object.values(record.fields)) {
|
|
124
|
-
const valueStr = String(value).toLowerCase();
|
|
125
|
-
if (valueStr.includes(keyword)) {
|
|
126
|
-
return true;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return false;
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return {
|
|
135
|
-
records: filteredRecords,
|
|
136
|
-
total: filteredRecords.length,
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* 狀態匹配(支援縮寫)
|
|
142
|
-
*/
|
|
143
|
-
function matchStatus(recordStatus: any, queryStatus: string): boolean {
|
|
144
|
-
const status = String(recordStatus).toLowerCase();
|
|
145
|
-
const query = queryStatus.toLowerCase();
|
|
146
|
-
|
|
147
|
-
// 完全匹配
|
|
148
|
-
if (status === query) return true;
|
|
149
|
-
|
|
150
|
-
// 縮寫匹配
|
|
151
|
-
const statusMap: Record<string, string[]> = {
|
|
152
|
-
'p': ['pending', 'p'],
|
|
153
|
-
'pending': ['pending', 'p'],
|
|
154
|
-
'i': ['in_progress', 'i', 'inprogress'],
|
|
155
|
-
'in_progress': ['in_progress', 'i', 'inprogress'],
|
|
156
|
-
'c': ['completed', 'c', 'done'],
|
|
157
|
-
'completed': ['completed', 'c', 'done'],
|
|
158
|
-
'b': ['blocked', 'b'],
|
|
159
|
-
'blocked': ['blocked', 'b'],
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
return statusMap[query]?.includes(status) || statusMap[status]?.includes(query) || false;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* 取得狀態在 index 中的鍵名
|
|
167
|
-
*/
|
|
168
|
-
function getStatusKey(status: string): string {
|
|
169
|
-
const query = status.toLowerCase();
|
|
170
|
-
|
|
171
|
-
const keyMap: Record<string, string> = {
|
|
172
|
-
'p': 'pending',
|
|
173
|
-
'pending': 'pending',
|
|
174
|
-
'i': 'inprogress',
|
|
175
|
-
'in_progress': 'inprogress',
|
|
176
|
-
'inprogress': 'inprogress',
|
|
177
|
-
'c': 'completed',
|
|
178
|
-
'completed': 'completed',
|
|
179
|
-
'done': 'completed',
|
|
180
|
-
'b': 'blocked',
|
|
181
|
-
'blocked': 'blocked',
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
return keyMap[query] || query;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* 顯示查詢結果
|
|
189
|
-
*/
|
|
190
|
-
function displayResults(result: QueryResult, format: 'table' | 'json' | 'compact', matrixFile: MatrixFile): void {
|
|
191
|
-
if (result.total === 0) {
|
|
192
|
-
log('No records found matching the query.');
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
switch (format) {
|
|
197
|
-
case 'json':
|
|
198
|
-
displayJSON(result);
|
|
199
|
-
break;
|
|
200
|
-
case 'compact':
|
|
201
|
-
displayCompact(result);
|
|
202
|
-
break;
|
|
203
|
-
case 'table':
|
|
204
|
-
default:
|
|
205
|
-
displayTable(result, matrixFile);
|
|
206
|
-
break;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* 表格格式顯示
|
|
212
|
-
*/
|
|
213
|
-
function displayTable(result: QueryResult, matrixFile: MatrixFile): void {
|
|
214
|
-
log(`\n📊 Query Results (${result.total} records found)\n`);
|
|
215
|
-
log('─'.repeat(80));
|
|
216
|
-
|
|
217
|
-
// 表頭
|
|
218
|
-
const headerRow = ['ID', 'Status', 'Priority', 'Title/Description'];
|
|
219
|
-
const colWidths = [15, 12, 10, 40];
|
|
220
|
-
|
|
221
|
-
const header = headerRow.map((h, i) => h.padEnd(colWidths[i])).join(' | ');
|
|
222
|
-
log(header);
|
|
223
|
-
log('─'.repeat(80));
|
|
224
|
-
|
|
225
|
-
// 數據行
|
|
226
|
-
for (const record of result.records) {
|
|
227
|
-
const id = record.id.padEnd(colWidths[0]);
|
|
228
|
-
const status = (record.fields.status || record.fields.st || '-').toString().padEnd(colWidths[1]);
|
|
229
|
-
const priority = (record.fields.priority || record.fields.pri || record.fields.p || '-').toString().padEnd(colWidths[2]);
|
|
230
|
-
const title = (record.fields.title || record.fields.desc || record.fields.description || '-').toString().substring(0, colWidths[3]).padEnd(colWidths[3]);
|
|
231
|
-
|
|
232
|
-
log(`${id} | ${status} | ${priority} | ${title}`);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
log('─'.repeat(80));
|
|
236
|
-
log(`Total: ${result.total} record(s)\n`);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* JSON 格式顯示
|
|
241
|
-
*/
|
|
242
|
-
function displayJSON(result: QueryResult): void {
|
|
243
|
-
console.log(JSON.stringify(result.records, null, 2));
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* 精簡格式顯示
|
|
248
|
-
*/
|
|
249
|
-
function displayCompact(result: QueryResult): void {
|
|
250
|
-
log(`Found ${result.total} record(s):\n`);
|
|
251
|
-
|
|
252
|
-
for (const record of result.records) {
|
|
253
|
-
const status = record.fields.status || record.fields.st || '-';
|
|
254
|
-
const priority = record.fields.priority || record.fields.pri || record.fields.p || '-';
|
|
255
|
-
const title = record.fields.title || record.fields.desc || '-';
|
|
256
|
-
|
|
257
|
-
log(` • ${record.id} [${status}] [P${priority}] ${title}`);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
log('');
|
|
261
|
-
}
|