cognitive-core 0.2.0 → 0.2.2
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/.claude/settings.json +111 -2
- package/.sessionlog/settings.json +4 -0
- package/dist/atlas.d.ts +10 -0
- package/dist/atlas.d.ts.map +1 -1
- package/dist/atlas.js +65 -0
- package/dist/atlas.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/learning/index.d.ts +1 -1
- package/dist/learning/index.d.ts.map +1 -1
- package/dist/learning/index.js.map +1 -1
- package/dist/learning/pipeline.d.ts +4 -31
- package/dist/learning/pipeline.d.ts.map +1 -1
- package/dist/learning/pipeline.js +12 -64
- package/dist/learning/pipeline.js.map +1 -1
- package/dist/learning/unified-pipeline.d.ts +30 -0
- package/dist/learning/unified-pipeline.d.ts.map +1 -1
- package/dist/learning/unified-pipeline.js +207 -0
- package/dist/learning/unified-pipeline.js.map +1 -1
- package/dist/memory/candidate-retrieval.d.ts.map +1 -1
- package/dist/memory/candidate-retrieval.js +3 -1
- package/dist/memory/candidate-retrieval.js.map +1 -1
- package/dist/memory/curated-loader.d.ts +21 -4
- package/dist/memory/curated-loader.d.ts.map +1 -1
- package/dist/memory/curated-loader.js +53 -16
- package/dist/memory/curated-loader.js.map +1 -1
- package/dist/memory/index.d.ts +2 -1
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +3 -1
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/playbook.d.ts +6 -0
- package/dist/memory/playbook.d.ts.map +1 -1
- package/dist/memory/playbook.js +15 -0
- package/dist/memory/playbook.js.map +1 -1
- package/dist/memory/source-resolver.d.ts +120 -0
- package/dist/memory/source-resolver.d.ts.map +1 -0
- package/dist/memory/source-resolver.js +300 -0
- package/dist/memory/source-resolver.js.map +1 -0
- package/dist/types/config.d.ts +141 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +40 -0
- package/dist/types/config.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/error-classifier.js +8 -8
- package/dist/utils/error-classifier.js.map +1 -1
- package/dist/workspace/efficacy-toolkit.d.ts +164 -0
- package/dist/workspace/efficacy-toolkit.d.ts.map +1 -0
- package/dist/workspace/efficacy-toolkit.js +281 -0
- package/dist/workspace/efficacy-toolkit.js.map +1 -0
- package/dist/workspace/index.d.ts +2 -1
- package/dist/workspace/index.d.ts.map +1 -1
- package/dist/workspace/index.js +3 -1
- package/dist/workspace/index.js.map +1 -1
- package/dist/workspace/templates/index.d.ts +3 -0
- package/dist/workspace/templates/index.d.ts.map +1 -1
- package/dist/workspace/templates/index.js +6 -0
- package/dist/workspace/templates/index.js.map +1 -1
- package/dist/workspace/templates/playbook-decay-detection.d.ts +46 -0
- package/dist/workspace/templates/playbook-decay-detection.d.ts.map +1 -0
- package/dist/workspace/templates/playbook-decay-detection.js +197 -0
- package/dist/workspace/templates/playbook-decay-detection.js.map +1 -0
- package/dist/workspace/templates/playbook-efficacy-audit.d.ts +46 -0
- package/dist/workspace/templates/playbook-efficacy-audit.d.ts.map +1 -0
- package/dist/workspace/templates/playbook-efficacy-audit.js +160 -0
- package/dist/workspace/templates/playbook-efficacy-audit.js.map +1 -0
- package/dist/workspace/templates/playbook-lifecycle-review.d.ts +51 -0
- package/dist/workspace/templates/playbook-lifecycle-review.d.ts.map +1 -0
- package/dist/workspace/templates/playbook-lifecycle-review.js +187 -0
- package/dist/workspace/templates/playbook-lifecycle-review.js.map +1 -0
- package/dist/workspace/types.d.ts +12 -54
- package/dist/workspace/types.d.ts.map +1 -1
- package/dist/workspace/types.js.map +1 -1
- package/package.json +8 -2
- package/playbooks/compound-engineering/adversarial-review.json +51 -0
- package/playbooks/compound-engineering/agent-native-architecture.json +59 -0
- package/playbooks/compound-engineering/agent-native-review.json +54 -0
- package/playbooks/compound-engineering/api-contract-review.json +52 -0
- package/playbooks/compound-engineering/brainstorm-requirements.json +55 -0
- package/playbooks/compound-engineering/bug-reproduction.json +62 -0
- package/playbooks/compound-engineering/confidence-calibration.json +49 -0
- package/playbooks/compound-engineering/correctness-review.json +49 -0
- package/playbooks/compound-engineering/data-migration-safety.json +59 -0
- package/playbooks/compound-engineering/deployment-verification.json +63 -0
- package/playbooks/compound-engineering/error-recovery-patterns.json +53 -0
- package/playbooks/compound-engineering/implementation-planning.json +64 -0
- package/playbooks/compound-engineering/issue-pattern-analysis.json +53 -0
- package/playbooks/compound-engineering/knowledge-compounding.json +63 -0
- package/playbooks/compound-engineering/learnings-research.json +54 -0
- package/playbooks/compound-engineering/maintainability-review.json +49 -0
- package/playbooks/compound-engineering/performance-review.json +54 -0
- package/playbooks/compound-engineering/plan-adversarial-review.json +56 -0
- package/playbooks/compound-engineering/plan-feasibility-review.json +56 -0
- package/playbooks/compound-engineering/project-standards-review.json +52 -0
- package/playbooks/compound-engineering/reliability-review.json +53 -0
- package/playbooks/compound-engineering/review-orchestration.json +64 -0
- package/playbooks/compound-engineering/security-review.json +54 -0
- package/playbooks/compound-engineering/systematic-execution.json +64 -0
- package/playbooks/compound-engineering/testing-review.json +50 -0
- package/src/atlas.ts +96 -0
- package/src/index.ts +27 -0
- package/src/learning/index.ts +1 -0
- package/src/learning/unified-pipeline.ts +271 -1
- package/src/memory/candidate-retrieval.ts +2 -1
- package/src/memory/curated-loader.ts +69 -16
- package/src/memory/index.ts +16 -0
- package/src/memory/playbook.ts +19 -0
- package/src/memory/source-resolver.ts +422 -0
- package/src/types/config.ts +46 -0
- package/src/types/index.ts +4 -0
- package/src/utils/error-classifier.ts +8 -8
- package/src/workspace/efficacy-toolkit.ts +496 -0
- package/src/workspace/index.ts +29 -0
- package/src/workspace/templates/index.ts +24 -0
- package/src/workspace/templates/playbook-decay-detection.ts +272 -0
- package/src/workspace/templates/playbook-efficacy-audit.ts +246 -0
- package/src/workspace/templates/playbook-lifecycle-review.ts +274 -0
- package/src/workspace/types.ts +22 -78
- package/tests/fixtures/behavioral-trajectories.ts +210 -0
- package/tests/integration/curated-sources-e2e.test.ts +502 -0
- package/tests/integration/pipeline-data-correctness.test.ts +794 -0
- package/tests/learning/meta-learner.test.ts +418 -0
- package/tests/learning/pipeline-memory-updates.test.ts +721 -0
- package/tests/learning/unified-pipeline-efficacy.test.ts +232 -0
- package/tests/memory/candidate-retrieval.test.ts +167 -0
- package/tests/memory/compound-engineering-seed.test.ts +338 -0
- package/tests/memory/curated-loader-extended.test.ts +225 -0
- package/tests/memory/meta.test.ts +399 -0
- package/tests/memory/playbook-quality-validation.test.ts +430 -0
- package/tests/memory/source-resolver.test.ts +700 -0
- package/tests/search/evaluator.test.ts +257 -0
- package/tests/search/verification-runner.test.ts +357 -0
- package/tests/utils/error-classifier.test.ts +149 -0
- package/tests/utils/trajectory-helpers.test.ts +163 -0
- package/tests/workspace/efficacy-toolkit.test.ts +404 -0
- package/tests/workspace/templates/playbook-efficacy.test.ts +377 -0
- package/.claude/settings.local.json +0 -11
- package/dist/learning/llm-extractor.d.ts +0 -88
- package/dist/learning/llm-extractor.d.ts.map +0 -1
- package/dist/learning/llm-extractor.js +0 -372
- package/dist/learning/llm-extractor.js.map +0 -1
- package/dist/learning/loop-coordinator.d.ts +0 -61
- package/dist/learning/loop-coordinator.d.ts.map +0 -1
- package/dist/learning/loop-coordinator.js +0 -96
- package/dist/learning/loop-coordinator.js.map +0 -1
- package/references/agent-workspace/CLAUDE.md +0 -74
- package/references/agent-workspace/README.md +0 -587
- package/references/agent-workspace/media/banner.png +0 -0
- package/references/agent-workspace/package-lock.json +0 -2061
- package/references/agent-workspace/package.json +0 -54
- package/references/agent-workspace/src/handle.ts +0 -122
- package/references/agent-workspace/src/index.ts +0 -32
- package/references/agent-workspace/src/manager.ts +0 -102
- package/references/agent-workspace/src/readers/json.ts +0 -71
- package/references/agent-workspace/src/readers/markdown.ts +0 -37
- package/references/agent-workspace/src/readers/raw.ts +0 -27
- package/references/agent-workspace/src/types.ts +0 -68
- package/references/agent-workspace/src/validation.ts +0 -93
- package/references/agent-workspace/src/writers/json.ts +0 -17
- package/references/agent-workspace/src/writers/markdown.ts +0 -27
- package/references/agent-workspace/src/writers/raw.ts +0 -22
- package/references/agent-workspace/tests/errors.test.ts +0 -652
- package/references/agent-workspace/tests/handle.test.ts +0 -144
- package/references/agent-workspace/tests/manager.test.ts +0 -124
- package/references/agent-workspace/tests/readers.test.ts +0 -205
- package/references/agent-workspace/tests/validation.test.ts +0 -196
- package/references/agent-workspace/tests/writers.test.ts +0 -108
- package/references/agent-workspace/tsconfig.json +0 -20
- package/references/agent-workspace/tsup.config.ts +0 -9
- package/references/minimem/.claude/settings.json +0 -7
- package/references/minimem/.sudocode/issues.jsonl +0 -18
- package/references/minimem/.sudocode/specs.jsonl +0 -1
- package/references/minimem/CLAUDE.md +0 -310
- package/references/minimem/README.md +0 -556
- package/references/minimem/claude-plugin/.claude-plugin/plugin.json +0 -10
- package/references/minimem/claude-plugin/.mcp.json +0 -7
- package/references/minimem/claude-plugin/README.md +0 -158
- package/references/minimem/claude-plugin/commands/recall.md +0 -47
- package/references/minimem/claude-plugin/commands/remember.md +0 -41
- package/references/minimem/claude-plugin/hooks/__tests__/hooks.test.ts +0 -272
- package/references/minimem/claude-plugin/hooks/hooks.json +0 -27
- package/references/minimem/claude-plugin/hooks/session-end.sh +0 -86
- package/references/minimem/claude-plugin/hooks/session-start.sh +0 -85
- package/references/minimem/claude-plugin/skills/memory/SKILL.md +0 -108
- package/references/minimem/package-lock.json +0 -5373
- package/references/minimem/package.json +0 -60
- package/references/minimem/scripts/postbuild.js +0 -35
- package/references/minimem/src/__tests__/edge-cases.test.ts +0 -371
- package/references/minimem/src/__tests__/errors.test.ts +0 -265
- package/references/minimem/src/__tests__/helpers.ts +0 -199
- package/references/minimem/src/__tests__/internal.test.ts +0 -407
- package/references/minimem/src/__tests__/knowledge.test.ts +0 -287
- package/references/minimem/src/__tests__/minimem.integration.test.ts +0 -1127
- package/references/minimem/src/__tests__/session.test.ts +0 -190
- package/references/minimem/src/cli/__tests__/commands.test.ts +0 -759
- package/references/minimem/src/cli/commands/__tests__/conflicts.test.ts +0 -141
- package/references/minimem/src/cli/commands/append.ts +0 -76
- package/references/minimem/src/cli/commands/config.ts +0 -262
- package/references/minimem/src/cli/commands/conflicts.ts +0 -413
- package/references/minimem/src/cli/commands/daemon.ts +0 -169
- package/references/minimem/src/cli/commands/index.ts +0 -12
- package/references/minimem/src/cli/commands/init.ts +0 -88
- package/references/minimem/src/cli/commands/mcp.ts +0 -177
- package/references/minimem/src/cli/commands/push-pull.ts +0 -213
- package/references/minimem/src/cli/commands/search.ts +0 -158
- package/references/minimem/src/cli/commands/status.ts +0 -84
- package/references/minimem/src/cli/commands/sync-init.ts +0 -290
- package/references/minimem/src/cli/commands/sync.ts +0 -70
- package/references/minimem/src/cli/commands/upsert.ts +0 -197
- package/references/minimem/src/cli/config.ts +0 -584
- package/references/minimem/src/cli/index.ts +0 -264
- package/references/minimem/src/cli/shared.ts +0 -161
- package/references/minimem/src/cli/sync/__tests__/central.test.ts +0 -152
- package/references/minimem/src/cli/sync/__tests__/conflicts.test.ts +0 -209
- package/references/minimem/src/cli/sync/__tests__/daemon.test.ts +0 -118
- package/references/minimem/src/cli/sync/__tests__/detection.test.ts +0 -207
- package/references/minimem/src/cli/sync/__tests__/integration.test.ts +0 -476
- package/references/minimem/src/cli/sync/__tests__/registry.test.ts +0 -363
- package/references/minimem/src/cli/sync/__tests__/state.test.ts +0 -255
- package/references/minimem/src/cli/sync/__tests__/validation.test.ts +0 -193
- package/references/minimem/src/cli/sync/__tests__/watcher.test.ts +0 -178
- package/references/minimem/src/cli/sync/central.ts +0 -292
- package/references/minimem/src/cli/sync/conflicts.ts +0 -204
- package/references/minimem/src/cli/sync/daemon.ts +0 -407
- package/references/minimem/src/cli/sync/detection.ts +0 -138
- package/references/minimem/src/cli/sync/index.ts +0 -107
- package/references/minimem/src/cli/sync/operations.ts +0 -373
- package/references/minimem/src/cli/sync/registry.ts +0 -279
- package/references/minimem/src/cli/sync/state.ts +0 -355
- package/references/minimem/src/cli/sync/validation.ts +0 -206
- package/references/minimem/src/cli/sync/watcher.ts +0 -234
- package/references/minimem/src/cli/version.ts +0 -34
- package/references/minimem/src/core/index.ts +0 -9
- package/references/minimem/src/core/indexer.ts +0 -628
- package/references/minimem/src/core/searcher.ts +0 -221
- package/references/minimem/src/db/schema.ts +0 -183
- package/references/minimem/src/db/sqlite-vec.ts +0 -24
- package/references/minimem/src/embeddings/__tests__/embeddings.test.ts +0 -431
- package/references/minimem/src/embeddings/batch-gemini.ts +0 -392
- package/references/minimem/src/embeddings/batch-openai.ts +0 -409
- package/references/minimem/src/embeddings/embeddings.ts +0 -434
- package/references/minimem/src/index.ts +0 -109
- package/references/minimem/src/internal.ts +0 -299
- package/references/minimem/src/minimem.ts +0 -1276
- package/references/minimem/src/search/__tests__/hybrid.test.ts +0 -247
- package/references/minimem/src/search/graph.ts +0 -234
- package/references/minimem/src/search/hybrid.ts +0 -151
- package/references/minimem/src/search/search.ts +0 -256
- package/references/minimem/src/server/__tests__/mcp.test.ts +0 -341
- package/references/minimem/src/server/__tests__/tools.test.ts +0 -364
- package/references/minimem/src/server/mcp.ts +0 -326
- package/references/minimem/src/server/tools.ts +0 -720
- package/references/minimem/src/session.ts +0 -460
- package/references/minimem/tsconfig.json +0 -19
- package/references/minimem/tsup.config.ts +0 -26
- package/references/minimem/vitest.config.ts +0 -24
- package/references/sessionlog/.husky/pre-commit +0 -1
- package/references/sessionlog/.lintstagedrc.json +0 -4
- package/references/sessionlog/.prettierignore +0 -4
- package/references/sessionlog/.prettierrc.json +0 -11
- package/references/sessionlog/LICENSE +0 -21
- package/references/sessionlog/README.md +0 -453
- package/references/sessionlog/eslint.config.js +0 -58
- package/references/sessionlog/package-lock.json +0 -3672
- package/references/sessionlog/package.json +0 -65
- package/references/sessionlog/src/__tests__/agent-hooks.test.ts +0 -570
- package/references/sessionlog/src/__tests__/agent-registry.test.ts +0 -127
- package/references/sessionlog/src/__tests__/claude-code-hooks.test.ts +0 -225
- package/references/sessionlog/src/__tests__/claude-generator.test.ts +0 -46
- package/references/sessionlog/src/__tests__/commit-msg.test.ts +0 -86
- package/references/sessionlog/src/__tests__/cursor-agent.test.ts +0 -224
- package/references/sessionlog/src/__tests__/e2e-live.test.ts +0 -890
- package/references/sessionlog/src/__tests__/event-log.test.ts +0 -183
- package/references/sessionlog/src/__tests__/flush-sentinel.test.ts +0 -105
- package/references/sessionlog/src/__tests__/gemini-agent.test.ts +0 -375
- package/references/sessionlog/src/__tests__/git-hooks.test.ts +0 -78
- package/references/sessionlog/src/__tests__/hook-managers.test.ts +0 -121
- package/references/sessionlog/src/__tests__/lifecycle-tasks.test.ts +0 -759
- package/references/sessionlog/src/__tests__/opencode-agent.test.ts +0 -338
- package/references/sessionlog/src/__tests__/redaction.test.ts +0 -136
- package/references/sessionlog/src/__tests__/session-repo.test.ts +0 -353
- package/references/sessionlog/src/__tests__/session-store.test.ts +0 -166
- package/references/sessionlog/src/__tests__/setup-ccweb.test.ts +0 -466
- package/references/sessionlog/src/__tests__/skill-live.test.ts +0 -461
- package/references/sessionlog/src/__tests__/summarize.test.ts +0 -348
- package/references/sessionlog/src/__tests__/task-plan-e2e.test.ts +0 -610
- package/references/sessionlog/src/__tests__/task-plan-live.test.ts +0 -632
- package/references/sessionlog/src/__tests__/transcript-timestamp.test.ts +0 -121
- package/references/sessionlog/src/__tests__/types.test.ts +0 -166
- package/references/sessionlog/src/__tests__/utils.test.ts +0 -333
- package/references/sessionlog/src/__tests__/validation.test.ts +0 -103
- package/references/sessionlog/src/__tests__/worktree.test.ts +0 -57
- package/references/sessionlog/src/agent/agents/claude-code.ts +0 -1089
- package/references/sessionlog/src/agent/agents/cursor.ts +0 -361
- package/references/sessionlog/src/agent/agents/gemini-cli.ts +0 -632
- package/references/sessionlog/src/agent/agents/opencode.ts +0 -540
- package/references/sessionlog/src/agent/registry.ts +0 -143
- package/references/sessionlog/src/agent/session-types.ts +0 -113
- package/references/sessionlog/src/agent/types.ts +0 -220
- package/references/sessionlog/src/cli.ts +0 -597
- package/references/sessionlog/src/commands/clean.ts +0 -133
- package/references/sessionlog/src/commands/disable.ts +0 -84
- package/references/sessionlog/src/commands/doctor.ts +0 -145
- package/references/sessionlog/src/commands/enable.ts +0 -202
- package/references/sessionlog/src/commands/explain.ts +0 -261
- package/references/sessionlog/src/commands/reset.ts +0 -105
- package/references/sessionlog/src/commands/resume.ts +0 -180
- package/references/sessionlog/src/commands/rewind.ts +0 -195
- package/references/sessionlog/src/commands/setup-ccweb.ts +0 -275
- package/references/sessionlog/src/commands/status.ts +0 -172
- package/references/sessionlog/src/config.ts +0 -165
- package/references/sessionlog/src/events/event-log.ts +0 -126
- package/references/sessionlog/src/git-operations.ts +0 -558
- package/references/sessionlog/src/hooks/git-hooks.ts +0 -165
- package/references/sessionlog/src/hooks/lifecycle.ts +0 -391
- package/references/sessionlog/src/index.ts +0 -650
- package/references/sessionlog/src/security/redaction.ts +0 -283
- package/references/sessionlog/src/session/state-machine.ts +0 -452
- package/references/sessionlog/src/store/checkpoint-store.ts +0 -509
- package/references/sessionlog/src/store/native-store.ts +0 -173
- package/references/sessionlog/src/store/provider-types.ts +0 -99
- package/references/sessionlog/src/store/session-store.ts +0 -266
- package/references/sessionlog/src/strategy/attribution.ts +0 -296
- package/references/sessionlog/src/strategy/common.ts +0 -207
- package/references/sessionlog/src/strategy/content-overlap.ts +0 -228
- package/references/sessionlog/src/strategy/manual-commit.ts +0 -988
- package/references/sessionlog/src/strategy/types.ts +0 -279
- package/references/sessionlog/src/summarize/claude-generator.ts +0 -115
- package/references/sessionlog/src/summarize/summarize.ts +0 -432
- package/references/sessionlog/src/types.ts +0 -508
- package/references/sessionlog/src/utils/chunk-files.ts +0 -49
- package/references/sessionlog/src/utils/commit-message.ts +0 -65
- package/references/sessionlog/src/utils/detect-agent.ts +0 -36
- package/references/sessionlog/src/utils/hook-managers.ts +0 -125
- package/references/sessionlog/src/utils/ide-tags.ts +0 -32
- package/references/sessionlog/src/utils/paths.ts +0 -79
- package/references/sessionlog/src/utils/preview-rewind.ts +0 -80
- package/references/sessionlog/src/utils/rewind-conflict.ts +0 -121
- package/references/sessionlog/src/utils/shadow-branch.ts +0 -109
- package/references/sessionlog/src/utils/string-utils.ts +0 -46
- package/references/sessionlog/src/utils/todo-extract.ts +0 -188
- package/references/sessionlog/src/utils/trailers.ts +0 -187
- package/references/sessionlog/src/utils/transcript-parse.ts +0 -177
- package/references/sessionlog/src/utils/transcript-timestamp.ts +0 -59
- package/references/sessionlog/src/utils/tree-ops.ts +0 -219
- package/references/sessionlog/src/utils/tty.ts +0 -72
- package/references/sessionlog/src/utils/validation.ts +0 -65
- package/references/sessionlog/src/utils/worktree.ts +0 -58
- package/references/sessionlog/src/wire-types.ts +0 -59
- package/references/sessionlog/templates/setup-env.sh +0 -153
- package/references/sessionlog/tsconfig.json +0 -18
- package/references/sessionlog/vitest.config.ts +0 -12
- package/references/skill-tree/.claude/settings.json +0 -6
- package/references/skill-tree/.sudocode/issues.jsonl +0 -19
- package/references/skill-tree/.sudocode/specs.jsonl +0 -3
- package/references/skill-tree/CLAUDE.md +0 -126
- package/references/skill-tree/README.md +0 -372
- package/references/skill-tree/docs/GAPS_v1.md +0 -221
- package/references/skill-tree/docs/INTEGRATION_PLAN.md +0 -467
- package/references/skill-tree/docs/TODOS.md +0 -91
- package/references/skill-tree/docs/anthropic_skill_guide.md +0 -1364
- package/references/skill-tree/docs/design/federated-skill-trees.md +0 -524
- package/references/skill-tree/docs/design/multi-agent-sync.md +0 -759
- package/references/skill-tree/docs/scraper/BRAINSTORM.md +0 -583
- package/references/skill-tree/docs/scraper/POC_PLAN.md +0 -420
- package/references/skill-tree/docs/scraper/README.md +0 -170
- package/references/skill-tree/examples/basic-usage.ts +0 -164
- package/references/skill-tree/package-lock.json +0 -1852
- package/references/skill-tree/package.json +0 -66
- package/references/skill-tree/scraper/README.md +0 -123
- package/references/skill-tree/scraper/docs/DESIGN.md +0 -683
- package/references/skill-tree/scraper/docs/PLAN.md +0 -336
- package/references/skill-tree/scraper/drizzle.config.ts +0 -10
- package/references/skill-tree/scraper/package-lock.json +0 -6329
- package/references/skill-tree/scraper/package.json +0 -68
- package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-description.md +0 -7
- package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-name.md +0 -7
- package/references/skill-tree/scraper/test/fixtures/minimal-skill/SKILL.md +0 -27
- package/references/skill-tree/scraper/test/fixtures/skill-json/SKILL.json +0 -21
- package/references/skill-tree/scraper/test/fixtures/skill-with-meta/SKILL.md +0 -54
- package/references/skill-tree/scraper/test/fixtures/skill-with-meta/_meta.json +0 -24
- package/references/skill-tree/scraper/test/fixtures/valid-skill/SKILL.md +0 -93
- package/references/skill-tree/scraper/test/fixtures/valid-skill/_meta.json +0 -22
- package/references/skill-tree/scraper/tsup.config.ts +0 -14
- package/references/skill-tree/scraper/vitest.config.ts +0 -17
- package/references/skill-tree/scripts/convert-to-vitest.ts +0 -166
- package/references/skill-tree/skills/skill-writer/SKILL.md +0 -339
- package/references/skill-tree/skills/skill-writer/references/examples.md +0 -326
- package/references/skill-tree/skills/skill-writer/references/patterns.md +0 -210
- package/references/skill-tree/skills/skill-writer/references/quality-checklist.md +0 -123
- package/references/skill-tree/test/run-all.ts +0 -106
- package/references/skill-tree/test/utils.ts +0 -128
- package/references/skill-tree/vitest.config.ts +0 -16
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
isGitSource,
|
|
4
|
+
parseGitSource,
|
|
5
|
+
discoverPlaybookDir,
|
|
6
|
+
resolvePlaybookSource,
|
|
7
|
+
sourceStateKey,
|
|
8
|
+
type SourceState,
|
|
9
|
+
} from '../../src/memory/source-resolver.js';
|
|
10
|
+
import { loadCuratedPlaybooks } from '../../src/memory/curated-loader.js';
|
|
11
|
+
import { PlaybookLibrary, createPlaybookLibrary } from '../../src/memory/playbook.js';
|
|
12
|
+
import { createSqlitePersistence } from '../../src/persistence/index.js';
|
|
13
|
+
import { mkdtemp, rm, mkdir, writeFile } from 'node:fs/promises';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
import { tmpdir } from 'node:os';
|
|
16
|
+
import { execFile as execFileCb } from 'node:child_process';
|
|
17
|
+
import { promisify } from 'node:util';
|
|
18
|
+
|
|
19
|
+
const execFile = promisify(execFileCb);
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Unit: isGitSource
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
describe('isGitSource', () => {
|
|
26
|
+
it('should recognize git: prefix', () => {
|
|
27
|
+
expect(isGitSource('git:https://github.com/org/repo')).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should recognize github: shorthand', () => {
|
|
31
|
+
expect(isGitSource('github:org/repo')).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should recognize git@ SSH URLs', () => {
|
|
35
|
+
expect(isGitSource('git@github.com:org/repo.git')).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should recognize git+ prefix', () => {
|
|
39
|
+
expect(isGitSource('git+https://github.com/org/repo')).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should not match local paths', () => {
|
|
43
|
+
expect(isGitSource('/home/user/playbooks')).toBe(false);
|
|
44
|
+
expect(isGitSource('./playbooks')).toBe(false);
|
|
45
|
+
expect(isGitSource('playbooks/')).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// Unit: parseGitSource
|
|
51
|
+
// ============================================================================
|
|
52
|
+
|
|
53
|
+
describe('parseGitSource', () => {
|
|
54
|
+
it('should parse git:https URL', () => {
|
|
55
|
+
const result = parseGitSource('git:https://github.com/org/repo');
|
|
56
|
+
expect(result.url).toBe('https://github.com/org/repo.git');
|
|
57
|
+
expect(result.ref).toBeUndefined();
|
|
58
|
+
expect(result.cacheKey).toBe('github.com/org/repo');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should parse git:https URL with ref', () => {
|
|
62
|
+
const result = parseGitSource('git:https://github.com/org/repo#v1.0.0');
|
|
63
|
+
expect(result.url).toBe('https://github.com/org/repo.git');
|
|
64
|
+
expect(result.ref).toBe('v1.0.0');
|
|
65
|
+
expect(result.cacheKey).toBe('github.com/org/repo');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should parse github: shorthand', () => {
|
|
69
|
+
const result = parseGitSource('github:EveryInc/compound-engineering-plugin');
|
|
70
|
+
expect(result.url).toBe('https://github.com/EveryInc/compound-engineering-plugin.git');
|
|
71
|
+
expect(result.ref).toBeUndefined();
|
|
72
|
+
expect(result.cacheKey).toBe('github.com/EveryInc/compound-engineering-plugin');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should parse github: shorthand with ref', () => {
|
|
76
|
+
const result = parseGitSource('github:org/repo#main');
|
|
77
|
+
expect(result.url).toBe('https://github.com/org/repo.git');
|
|
78
|
+
expect(result.ref).toBe('main');
|
|
79
|
+
expect(result.cacheKey).toBe('github.com/org/repo');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should parse git+ prefix', () => {
|
|
83
|
+
const result = parseGitSource('git+https://github.com/org/repo');
|
|
84
|
+
expect(result.url).toBe('https://github.com/org/repo.git');
|
|
85
|
+
expect(result.ref).toBeUndefined();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should not double-append .git suffix', () => {
|
|
89
|
+
const result = parseGitSource('git:https://github.com/org/repo.git');
|
|
90
|
+
expect(result.url).toBe('https://github.com/org/repo.git');
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// ============================================================================
|
|
95
|
+
// Unit: discoverPlaybookDir
|
|
96
|
+
// ============================================================================
|
|
97
|
+
|
|
98
|
+
describe('discoverPlaybookDir', () => {
|
|
99
|
+
let tempDir: string;
|
|
100
|
+
|
|
101
|
+
beforeEach(async () => {
|
|
102
|
+
tempDir = await mkdtemp(join(tmpdir(), 'resolver-discover-'));
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
afterEach(async () => {
|
|
106
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should find playbooks from manifest', async () => {
|
|
110
|
+
const pbDir = join(tempDir, 'my-playbooks');
|
|
111
|
+
await mkdir(pbDir, { recursive: true });
|
|
112
|
+
await writeFile(
|
|
113
|
+
join(tempDir, 'cognitive-core.json'),
|
|
114
|
+
JSON.stringify({ playbooks: 'my-playbooks' }),
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const result = await discoverPlaybookDir(tempDir);
|
|
118
|
+
expect(result).toBe(pbDir);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should find conventional playbooks/ directory', async () => {
|
|
122
|
+
const pbDir = join(tempDir, 'playbooks');
|
|
123
|
+
await mkdir(pbDir);
|
|
124
|
+
|
|
125
|
+
const result = await discoverPlaybookDir(tempDir);
|
|
126
|
+
expect(result).toBe(pbDir);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should find conventional curated-playbooks/ directory', async () => {
|
|
130
|
+
const pbDir = join(tempDir, 'curated-playbooks');
|
|
131
|
+
await mkdir(pbDir);
|
|
132
|
+
|
|
133
|
+
const result = await discoverPlaybookDir(tempDir);
|
|
134
|
+
expect(result).toBe(pbDir);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should prefer manifest over convention', async () => {
|
|
138
|
+
const manifestDir = join(tempDir, 'custom');
|
|
139
|
+
const conventionDir = join(tempDir, 'playbooks');
|
|
140
|
+
await mkdir(manifestDir);
|
|
141
|
+
await mkdir(conventionDir);
|
|
142
|
+
await writeFile(
|
|
143
|
+
join(tempDir, 'cognitive-core.json'),
|
|
144
|
+
JSON.stringify({ playbooks: 'custom' }),
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const result = await discoverPlaybookDir(tempDir);
|
|
148
|
+
expect(result).toBe(manifestDir);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should detect root dir with playbook JSON files', async () => {
|
|
152
|
+
await writeFile(
|
|
153
|
+
join(tempDir, 'my-playbook.json'),
|
|
154
|
+
JSON.stringify({
|
|
155
|
+
name: 'test',
|
|
156
|
+
applicability: { situations: ['test'], domains: ['test'] },
|
|
157
|
+
guidance: { strategy: 'test', tactics: ['test'] },
|
|
158
|
+
}),
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const result = await discoverPlaybookDir(tempDir);
|
|
162
|
+
expect(result).toBe(tempDir);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should return null for empty directory', async () => {
|
|
166
|
+
const result = await discoverPlaybookDir(tempDir);
|
|
167
|
+
expect(result).toBeNull();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should fall through when manifest points to non-existent directory', async () => {
|
|
171
|
+
const conventionDir = join(tempDir, 'playbooks');
|
|
172
|
+
await mkdir(conventionDir);
|
|
173
|
+
// Manifest points to a dir that doesn't exist
|
|
174
|
+
await writeFile(
|
|
175
|
+
join(tempDir, 'cognitive-core.json'),
|
|
176
|
+
JSON.stringify({ playbooks: 'does-not-exist' }),
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const result = await discoverPlaybookDir(tempDir);
|
|
180
|
+
// Should fall through to convention: playbooks/
|
|
181
|
+
expect(result).toBe(conventionDir);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('should fall through when manifest is malformed JSON', async () => {
|
|
185
|
+
const conventionDir = join(tempDir, 'playbooks');
|
|
186
|
+
await mkdir(conventionDir);
|
|
187
|
+
await writeFile(join(tempDir, 'cognitive-core.json'), '{ not valid json');
|
|
188
|
+
|
|
189
|
+
const result = await discoverPlaybookDir(tempDir);
|
|
190
|
+
expect(result).toBe(conventionDir);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should not match root dir with JSON files that lack playbook fields', async () => {
|
|
194
|
+
await writeFile(
|
|
195
|
+
join(tempDir, 'package.json'),
|
|
196
|
+
JSON.stringify({ name: 'some-package', version: '1.0.0' }),
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const result = await discoverPlaybookDir(tempDir);
|
|
200
|
+
expect(result).toBeNull();
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// ============================================================================
|
|
205
|
+
// Integration: resolvePlaybookSource — local paths
|
|
206
|
+
// ============================================================================
|
|
207
|
+
|
|
208
|
+
describe('resolvePlaybookSource — local paths', () => {
|
|
209
|
+
let tempDir: string;
|
|
210
|
+
let cacheDir: string;
|
|
211
|
+
|
|
212
|
+
beforeEach(async () => {
|
|
213
|
+
tempDir = await mkdtemp(join(tmpdir(), 'resolver-local-'));
|
|
214
|
+
cacheDir = join(tempDir, 'cache');
|
|
215
|
+
await mkdir(cacheDir);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
afterEach(async () => {
|
|
219
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('should pass through existing local paths', async () => {
|
|
223
|
+
const pbDir = join(tempDir, 'playbooks');
|
|
224
|
+
await mkdir(pbDir);
|
|
225
|
+
|
|
226
|
+
const result = await resolvePlaybookSource(pbDir, {
|
|
227
|
+
cacheDir,
|
|
228
|
+
maxAgeMs: 86400_000,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
expect(result).not.toBeNull();
|
|
232
|
+
expect(result!.playbookDir).toBe(pbDir);
|
|
233
|
+
expect(result!.isGit).toBe(false);
|
|
234
|
+
expect(result!.updated).toBe(false);
|
|
235
|
+
expect(result!.sourceId).toBe(pbDir);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should return null for non-existent local paths', async () => {
|
|
239
|
+
const result = await resolvePlaybookSource('/nonexistent/path', {
|
|
240
|
+
cacheDir,
|
|
241
|
+
maxAgeMs: 86400_000,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
expect(result).toBeNull();
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// ============================================================================
|
|
249
|
+
// Integration: resolvePlaybookSource — git sources
|
|
250
|
+
// ============================================================================
|
|
251
|
+
|
|
252
|
+
describe('resolvePlaybookSource — git sources (integration)', () => {
|
|
253
|
+
let tempDir: string;
|
|
254
|
+
let cacheDir: string;
|
|
255
|
+
let sourceStates: Map<string, SourceState>;
|
|
256
|
+
|
|
257
|
+
beforeEach(async () => {
|
|
258
|
+
tempDir = await mkdtemp(join(tmpdir(), 'resolver-git-'));
|
|
259
|
+
cacheDir = join(tempDir, 'cache');
|
|
260
|
+
await mkdir(cacheDir);
|
|
261
|
+
sourceStates = new Map();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
afterEach(async () => {
|
|
265
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Creates a local bare git repo with a playbooks/ directory containing
|
|
270
|
+
* test playbook JSON files. Returns the bare repo path (acts as "remote").
|
|
271
|
+
*/
|
|
272
|
+
async function createLocalGitRepo(opts?: {
|
|
273
|
+
playbookNames?: string[];
|
|
274
|
+
useManifest?: boolean;
|
|
275
|
+
playbookSubdir?: string;
|
|
276
|
+
createTag?: string;
|
|
277
|
+
}): Promise<string> {
|
|
278
|
+
const repoDir = join(tempDir, `fake-remote-${Date.now()}`);
|
|
279
|
+
await mkdir(repoDir);
|
|
280
|
+
await execFile('git', ['init', '--bare', repoDir]);
|
|
281
|
+
|
|
282
|
+
const workDir = join(tempDir, `work-${Date.now()}`);
|
|
283
|
+
await mkdir(workDir);
|
|
284
|
+
await execFile('git', ['clone', repoDir, workDir]);
|
|
285
|
+
await execFile('git', ['config', 'commit.gpgSign', 'false'], { cwd: workDir });
|
|
286
|
+
|
|
287
|
+
const subdir = opts?.playbookSubdir ?? 'playbooks';
|
|
288
|
+
const pbDir = join(workDir, subdir);
|
|
289
|
+
await mkdir(pbDir, { recursive: true });
|
|
290
|
+
|
|
291
|
+
const names = opts?.playbookNames ?? ['test-from-git'];
|
|
292
|
+
for (const name of names) {
|
|
293
|
+
await writeFile(
|
|
294
|
+
join(pbDir, `${name}.json`),
|
|
295
|
+
JSON.stringify({
|
|
296
|
+
name,
|
|
297
|
+
applicability: { situations: [`${name} situation`], domains: ['testing'] },
|
|
298
|
+
guidance: { strategy: `${name} strategy`, tactics: [`${name} tactic`] },
|
|
299
|
+
verification: {
|
|
300
|
+
successIndicators: ['Tests pass'],
|
|
301
|
+
failureIndicators: ['Tests fail'],
|
|
302
|
+
},
|
|
303
|
+
confidence: 0.8,
|
|
304
|
+
curatedBy: 'test-suite',
|
|
305
|
+
}),
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Optional manifest
|
|
310
|
+
if (opts?.useManifest) {
|
|
311
|
+
await writeFile(
|
|
312
|
+
join(workDir, 'cognitive-core.json'),
|
|
313
|
+
JSON.stringify({ playbooks: subdir }),
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
await execFile('git', ['add', '.'], { cwd: workDir });
|
|
318
|
+
await execFile('git', ['commit', '-m', 'Initial playbooks'], {
|
|
319
|
+
cwd: workDir,
|
|
320
|
+
env: {
|
|
321
|
+
...process.env,
|
|
322
|
+
GIT_AUTHOR_NAME: 'Test',
|
|
323
|
+
GIT_AUTHOR_EMAIL: 'test@test.com',
|
|
324
|
+
GIT_COMMITTER_NAME: 'Test',
|
|
325
|
+
GIT_COMMITTER_EMAIL: 'test@test.com',
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
await execFile('git', ['push', 'origin', 'HEAD'], { cwd: workDir });
|
|
329
|
+
|
|
330
|
+
if (opts?.createTag) {
|
|
331
|
+
await execFile('git', ['tag', opts.createTag], { cwd: workDir });
|
|
332
|
+
await execFile('git', ['push', 'origin', opts.createTag], { cwd: workDir });
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return repoDir;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function makeResolveOpts(overrides?: Partial<Parameters<typeof resolvePlaybookSource>[1]>) {
|
|
339
|
+
return {
|
|
340
|
+
cacheDir,
|
|
341
|
+
maxAgeMs: 86400_000,
|
|
342
|
+
getSourceState: (s: string) => sourceStates.get(s),
|
|
343
|
+
setSourceState: (s: string, state: SourceState) => sourceStates.set(s, state),
|
|
344
|
+
...overrides,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
it('should clone and discover playbooks from a local git repo', async () => {
|
|
349
|
+
const repoDir = await createLocalGitRepo();
|
|
350
|
+
|
|
351
|
+
const result = await resolvePlaybookSource(`git:${repoDir}`, makeResolveOpts());
|
|
352
|
+
|
|
353
|
+
expect(result).not.toBeNull();
|
|
354
|
+
expect(result!.isGit).toBe(true);
|
|
355
|
+
expect(result!.updated).toBe(true);
|
|
356
|
+
expect(result!.commitSha).toBeDefined();
|
|
357
|
+
expect(result!.commitSha!.length).toBe(40);
|
|
358
|
+
expect(result!.playbookDir).toContain('playbooks');
|
|
359
|
+
|
|
360
|
+
// Source state should be persisted
|
|
361
|
+
const stateKey = `git:${repoDir}`;
|
|
362
|
+
expect(sourceStates.has(stateKey)).toBe(true);
|
|
363
|
+
const state = sourceStates.get(stateKey)!;
|
|
364
|
+
expect(state.commitSha).toBe(result!.commitSha);
|
|
365
|
+
expect(state.url).toBe(`${repoDir}`);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it('should use cached version on second resolve when not stale', async () => {
|
|
369
|
+
const repoDir = await createLocalGitRepo();
|
|
370
|
+
const source = `git:${repoDir}`;
|
|
371
|
+
|
|
372
|
+
// First resolve
|
|
373
|
+
const first = await resolvePlaybookSource(source, makeResolveOpts());
|
|
374
|
+
expect(first!.updated).toBe(true);
|
|
375
|
+
|
|
376
|
+
// Second resolve — should not update (same SHA, not stale)
|
|
377
|
+
const second = await resolvePlaybookSource(source, makeResolveOpts());
|
|
378
|
+
expect(second!.updated).toBe(false);
|
|
379
|
+
expect(second!.commitSha).toBe(first!.commitSha);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('should force update when forceUpdate is true', async () => {
|
|
383
|
+
const repoDir = await createLocalGitRepo();
|
|
384
|
+
const source = `git:${repoDir}`;
|
|
385
|
+
|
|
386
|
+
// First resolve
|
|
387
|
+
await resolvePlaybookSource(source, makeResolveOpts());
|
|
388
|
+
|
|
389
|
+
// Force update resolve — should report updated even though SHA is same
|
|
390
|
+
const result = await resolvePlaybookSource(
|
|
391
|
+
source,
|
|
392
|
+
makeResolveOpts({ forceUpdate: true }),
|
|
393
|
+
);
|
|
394
|
+
expect(result!.updated).toBe(true);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('should fetch when cache is stale but return updated:false if SHA unchanged', async () => {
|
|
398
|
+
const repoDir = await createLocalGitRepo();
|
|
399
|
+
const source = `git:${repoDir}`;
|
|
400
|
+
|
|
401
|
+
// First resolve
|
|
402
|
+
const first = await resolvePlaybookSource(source, makeResolveOpts());
|
|
403
|
+
expect(first!.updated).toBe(true);
|
|
404
|
+
|
|
405
|
+
// Backdate the source state's loadedAt so it appears stale
|
|
406
|
+
const state = sourceStates.get(source)!;
|
|
407
|
+
state.loadedAt = new Date(Date.now() - 2 * 86400_000).toISOString(); // 2 days ago
|
|
408
|
+
sourceStates.set(source, state);
|
|
409
|
+
|
|
410
|
+
// Resolve again with 1-day maxAge — should trigger a fetch,
|
|
411
|
+
// but since SHA is unchanged, resolver returns updated:false
|
|
412
|
+
const result = await resolvePlaybookSource(
|
|
413
|
+
source,
|
|
414
|
+
makeResolveOpts({ maxAgeMs: 86400_000 }),
|
|
415
|
+
);
|
|
416
|
+
expect(result).not.toBeNull();
|
|
417
|
+
expect(result!.isGit).toBe(true);
|
|
418
|
+
expect(result!.commitSha).toBe(first!.commitSha);
|
|
419
|
+
// SHA matched cached state → no re-load needed
|
|
420
|
+
expect(result!.updated).toBe(false);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('should detect new commits after stale cache fetch', async () => {
|
|
424
|
+
const repoDir = await createLocalGitRepo({ playbookNames: ['original-pb'] });
|
|
425
|
+
const source = `git:${repoDir}`;
|
|
426
|
+
|
|
427
|
+
// First resolve
|
|
428
|
+
const first = await resolvePlaybookSource(source, makeResolveOpts());
|
|
429
|
+
expect(first!.updated).toBe(true);
|
|
430
|
+
const originalSha = first!.commitSha;
|
|
431
|
+
|
|
432
|
+
// Push a new commit to the "remote"
|
|
433
|
+
const workDir = join(tempDir, `work-update-${Date.now()}`);
|
|
434
|
+
await mkdir(workDir);
|
|
435
|
+
await execFile('git', ['clone', repoDir, workDir]);
|
|
436
|
+
await execFile('git', ['config', 'commit.gpgSign', 'false'], { cwd: workDir });
|
|
437
|
+
await writeFile(
|
|
438
|
+
join(workDir, 'playbooks', 'new-pb.json'),
|
|
439
|
+
JSON.stringify({
|
|
440
|
+
name: 'new-pb',
|
|
441
|
+
applicability: { situations: ['new situation'], domains: ['testing'] },
|
|
442
|
+
guidance: { strategy: 'new strategy', tactics: ['new tactic'] },
|
|
443
|
+
}),
|
|
444
|
+
);
|
|
445
|
+
await execFile('git', ['add', '.'], { cwd: workDir });
|
|
446
|
+
await execFile('git', ['commit', '-m', 'Add new playbook'], {
|
|
447
|
+
cwd: workDir,
|
|
448
|
+
env: {
|
|
449
|
+
...process.env,
|
|
450
|
+
GIT_AUTHOR_NAME: 'Test',
|
|
451
|
+
GIT_AUTHOR_EMAIL: 'test@test.com',
|
|
452
|
+
GIT_COMMITTER_NAME: 'Test',
|
|
453
|
+
GIT_COMMITTER_EMAIL: 'test@test.com',
|
|
454
|
+
},
|
|
455
|
+
});
|
|
456
|
+
await execFile('git', ['push', 'origin', 'HEAD'], { cwd: workDir });
|
|
457
|
+
|
|
458
|
+
// Backdate state to trigger fetch
|
|
459
|
+
const state = sourceStates.get(source)!;
|
|
460
|
+
state.loadedAt = new Date(Date.now() - 2 * 86400_000).toISOString();
|
|
461
|
+
sourceStates.set(source, state);
|
|
462
|
+
|
|
463
|
+
// Resolve again — should detect new SHA
|
|
464
|
+
const result = await resolvePlaybookSource(
|
|
465
|
+
source,
|
|
466
|
+
makeResolveOpts({ maxAgeMs: 86400_000 }),
|
|
467
|
+
);
|
|
468
|
+
expect(result).not.toBeNull();
|
|
469
|
+
expect(result!.updated).toBe(true);
|
|
470
|
+
expect(result!.commitSha).not.toBe(originalSha);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('should resolve repo with manifest-based discovery', async () => {
|
|
474
|
+
const repoDir = await createLocalGitRepo({
|
|
475
|
+
playbookNames: ['manifest-pb'],
|
|
476
|
+
useManifest: true,
|
|
477
|
+
playbookSubdir: 'custom-dir',
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
const result = await resolvePlaybookSource(`git:${repoDir}`, makeResolveOpts());
|
|
481
|
+
expect(result).not.toBeNull();
|
|
482
|
+
expect(result!.playbookDir).toContain('custom-dir');
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
it('should resolve repo using a tag ref', async () => {
|
|
486
|
+
const repoDir = await createLocalGitRepo({
|
|
487
|
+
playbookNames: ['tagged-pb'],
|
|
488
|
+
createTag: 'v1.0.0',
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
const result = await resolvePlaybookSource(
|
|
492
|
+
`git:${repoDir}#v1.0.0`,
|
|
493
|
+
makeResolveOpts(),
|
|
494
|
+
);
|
|
495
|
+
expect(result).not.toBeNull();
|
|
496
|
+
expect(result!.isGit).toBe(true);
|
|
497
|
+
expect(result!.commitSha).toBeDefined();
|
|
498
|
+
expect(result!.commitSha!.length).toBe(40);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it('should return null for git repo with no discoverable playbook dir', async () => {
|
|
502
|
+
// Create a bare repo with no playbooks directory
|
|
503
|
+
const repoDir = join(tempDir, 'empty-remote');
|
|
504
|
+
await mkdir(repoDir);
|
|
505
|
+
await execFile('git', ['init', '--bare', repoDir]);
|
|
506
|
+
|
|
507
|
+
const workDir = join(tempDir, 'empty-work');
|
|
508
|
+
await mkdir(workDir);
|
|
509
|
+
await execFile('git', ['clone', repoDir, workDir]);
|
|
510
|
+
await execFile('git', ['config', 'commit.gpgSign', 'false'], { cwd: workDir });
|
|
511
|
+
await writeFile(join(workDir, 'README.md'), '# Empty repo');
|
|
512
|
+
await execFile('git', ['add', '.'], { cwd: workDir });
|
|
513
|
+
await execFile('git', ['commit', '-m', 'No playbooks'], {
|
|
514
|
+
cwd: workDir,
|
|
515
|
+
env: {
|
|
516
|
+
...process.env,
|
|
517
|
+
GIT_AUTHOR_NAME: 'Test',
|
|
518
|
+
GIT_AUTHOR_EMAIL: 'test@test.com',
|
|
519
|
+
GIT_COMMITTER_NAME: 'Test',
|
|
520
|
+
GIT_COMMITTER_EMAIL: 'test@test.com',
|
|
521
|
+
},
|
|
522
|
+
});
|
|
523
|
+
await execFile('git', ['push', 'origin', 'HEAD'], { cwd: workDir });
|
|
524
|
+
|
|
525
|
+
const result = await resolvePlaybookSource(`git:${repoDir}`, makeResolveOpts());
|
|
526
|
+
expect(result).toBeNull();
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
// ============================================================================
|
|
531
|
+
// End-to-end: git source → resolve → load into PlaybookLibrary
|
|
532
|
+
// ============================================================================
|
|
533
|
+
|
|
534
|
+
describe('End-to-end: git source → PlaybookLibrary', () => {
|
|
535
|
+
let tempDir: string;
|
|
536
|
+
let cacheDir: string;
|
|
537
|
+
let library: PlaybookLibrary;
|
|
538
|
+
let persistence: ReturnType<typeof createSqlitePersistence>;
|
|
539
|
+
let sourceStates: Map<string, SourceState>;
|
|
540
|
+
|
|
541
|
+
beforeEach(async () => {
|
|
542
|
+
tempDir = await mkdtemp(join(tmpdir(), 'resolver-e2e-'));
|
|
543
|
+
cacheDir = join(tempDir, 'cache');
|
|
544
|
+
await mkdir(cacheDir);
|
|
545
|
+
persistence = createSqlitePersistence({ baseDir: tempDir });
|
|
546
|
+
await persistence.init();
|
|
547
|
+
library = createPlaybookLibrary(persistence);
|
|
548
|
+
await library.init();
|
|
549
|
+
sourceStates = new Map();
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
afterEach(async () => {
|
|
553
|
+
await library.close();
|
|
554
|
+
persistence.close();
|
|
555
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
async function createLocalGitRepo(names: string[]): Promise<string> {
|
|
559
|
+
const repoDir = join(tempDir, `e2e-remote-${Date.now()}`);
|
|
560
|
+
await mkdir(repoDir);
|
|
561
|
+
await execFile('git', ['init', '--bare', repoDir]);
|
|
562
|
+
|
|
563
|
+
const workDir = join(tempDir, `e2e-work-${Date.now()}`);
|
|
564
|
+
await mkdir(workDir);
|
|
565
|
+
await execFile('git', ['clone', repoDir, workDir]);
|
|
566
|
+
await execFile('git', ['config', 'commit.gpgSign', 'false'], { cwd: workDir });
|
|
567
|
+
|
|
568
|
+
const pbDir = join(workDir, 'playbooks');
|
|
569
|
+
await mkdir(pbDir);
|
|
570
|
+
|
|
571
|
+
for (const name of names) {
|
|
572
|
+
await writeFile(
|
|
573
|
+
join(pbDir, `${name}.json`),
|
|
574
|
+
JSON.stringify({
|
|
575
|
+
name,
|
|
576
|
+
applicability: {
|
|
577
|
+
situations: [`${name} situation`],
|
|
578
|
+
triggers: [`${name} trigger`],
|
|
579
|
+
domains: ['testing'],
|
|
580
|
+
},
|
|
581
|
+
guidance: {
|
|
582
|
+
strategy: `${name} strategy`,
|
|
583
|
+
tactics: [`${name} tactic 1`, `${name} tactic 2`],
|
|
584
|
+
},
|
|
585
|
+
verification: {
|
|
586
|
+
successIndicators: ['Tests pass'],
|
|
587
|
+
failureIndicators: ['Tests fail'],
|
|
588
|
+
},
|
|
589
|
+
confidence: 0.85,
|
|
590
|
+
curatedBy: 'e2e-test',
|
|
591
|
+
}),
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
await execFile('git', ['add', '.'], { cwd: workDir });
|
|
596
|
+
await execFile('git', ['commit', '-m', 'Add playbooks'], {
|
|
597
|
+
cwd: workDir,
|
|
598
|
+
env: {
|
|
599
|
+
...process.env,
|
|
600
|
+
GIT_AUTHOR_NAME: 'Test',
|
|
601
|
+
GIT_AUTHOR_EMAIL: 'test@test.com',
|
|
602
|
+
GIT_COMMITTER_NAME: 'Test',
|
|
603
|
+
GIT_COMMITTER_EMAIL: 'test@test.com',
|
|
604
|
+
},
|
|
605
|
+
});
|
|
606
|
+
await execFile('git', ['push', 'origin', 'HEAD'], { cwd: workDir });
|
|
607
|
+
return repoDir;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
it('should resolve a git source and load playbooks into the library', async () => {
|
|
611
|
+
const repoDir = await createLocalGitRepo(['alpha-pb', 'beta-pb', 'gamma-pb']);
|
|
612
|
+
const source = `git:${repoDir}`;
|
|
613
|
+
|
|
614
|
+
// Resolve
|
|
615
|
+
const resolved = await resolvePlaybookSource(source, {
|
|
616
|
+
cacheDir,
|
|
617
|
+
maxAgeMs: 86400_000,
|
|
618
|
+
getSourceState: (s) => sourceStates.get(s),
|
|
619
|
+
setSourceState: (s, state) => sourceStates.set(s, state),
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
expect(resolved).not.toBeNull();
|
|
623
|
+
expect(resolved!.updated).toBe(true);
|
|
624
|
+
|
|
625
|
+
// Load into library
|
|
626
|
+
const loadResult = await loadCuratedPlaybooks(
|
|
627
|
+
resolved!.playbookDir,
|
|
628
|
+
library,
|
|
629
|
+
{ sourceId: resolved!.sourceId },
|
|
630
|
+
);
|
|
631
|
+
|
|
632
|
+
expect(loadResult.loaded).toBe(3);
|
|
633
|
+
expect(loadResult.errors).toEqual([]);
|
|
634
|
+
|
|
635
|
+
// Verify playbooks are in the library with correct provenance
|
|
636
|
+
const alpha = await library.getByName('alpha-pb');
|
|
637
|
+
expect(alpha).toBeDefined();
|
|
638
|
+
expect(alpha!.provenance?.origin).toBe('curated');
|
|
639
|
+
expect(alpha!.provenance?.sourceFile).toMatch(new RegExp(`^${source.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}:`));
|
|
640
|
+
expect(alpha!.provenance?.curatedBy).toBe('e2e-test');
|
|
641
|
+
expect(alpha!.confidence).toBe(0.85);
|
|
642
|
+
|
|
643
|
+
const beta = await library.getByName('beta-pb');
|
|
644
|
+
expect(beta).toBeDefined();
|
|
645
|
+
|
|
646
|
+
const gamma = await library.getByName('gamma-pb');
|
|
647
|
+
expect(gamma).toBeDefined();
|
|
648
|
+
|
|
649
|
+
// Verify searchable
|
|
650
|
+
const matches = await library.findMatching('alpha situation');
|
|
651
|
+
expect(matches.length).toBeGreaterThan(0);
|
|
652
|
+
expect(matches.some(m => m.playbook.name === 'alpha-pb')).toBe(true);
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
it('should support per-source recreate in e2e flow', async () => {
|
|
656
|
+
const repoA = await createLocalGitRepo(['pb-from-a']);
|
|
657
|
+
const repoB = await createLocalGitRepo(['pb-from-b']);
|
|
658
|
+
const sourceA = `git:${repoA}`;
|
|
659
|
+
const sourceB = `git:${repoB}`;
|
|
660
|
+
|
|
661
|
+
// Resolve and load both
|
|
662
|
+
const resolvedA = await resolvePlaybookSource(sourceA, {
|
|
663
|
+
cacheDir,
|
|
664
|
+
maxAgeMs: 86400_000,
|
|
665
|
+
getSourceState: (s) => sourceStates.get(s),
|
|
666
|
+
setSourceState: (s, state) => sourceStates.set(s, state),
|
|
667
|
+
});
|
|
668
|
+
await loadCuratedPlaybooks(resolvedA!.playbookDir, library, { sourceId: sourceA });
|
|
669
|
+
|
|
670
|
+
const resolvedB = await resolvePlaybookSource(sourceB, {
|
|
671
|
+
cacheDir,
|
|
672
|
+
maxAgeMs: 86400_000,
|
|
673
|
+
getSourceState: (s) => sourceStates.get(s),
|
|
674
|
+
setSourceState: (s, state) => sourceStates.set(s, state),
|
|
675
|
+
});
|
|
676
|
+
await loadCuratedPlaybooks(resolvedB!.playbookDir, library, { sourceId: sourceB });
|
|
677
|
+
|
|
678
|
+
expect(await library.count()).toBe(2);
|
|
679
|
+
|
|
680
|
+
// Recreate only source A — source B's playbook should survive
|
|
681
|
+
await loadCuratedPlaybooks(resolvedA!.playbookDir, library, {
|
|
682
|
+
sourceId: sourceA,
|
|
683
|
+
recreate: true,
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
expect(await library.count()).toBe(2); // 1 from A (reloaded) + 1 from B (untouched)
|
|
687
|
+
expect(await library.getByName('pb-from-a')).toBeDefined();
|
|
688
|
+
expect(await library.getByName('pb-from-b')).toBeDefined();
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
// ============================================================================
|
|
693
|
+
// Unit: sourceStateKey
|
|
694
|
+
// ============================================================================
|
|
695
|
+
|
|
696
|
+
describe('sourceStateKey', () => {
|
|
697
|
+
it('should generate prefixed key', () => {
|
|
698
|
+
expect(sourceStateKey('github:org/repo')).toBe('curated-source:github:org/repo');
|
|
699
|
+
});
|
|
700
|
+
});
|