@dewtech/dare-cli 3.10.0 → 3.12.0
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/README.md +7 -1
- package/dist/__tests__/ci-pr-regression.test.d.ts +2 -0
- package/dist/__tests__/ci-pr-regression.test.d.ts.map +1 -0
- package/dist/__tests__/ci-pr-regression.test.js +76 -0
- package/dist/__tests__/ci-pr-regression.test.js.map +1 -0
- package/dist/__tests__/dashboard-front.test.d.ts +2 -0
- package/dist/__tests__/dashboard-front.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-front.test.js +37 -0
- package/dist/__tests__/dashboard-front.test.js.map +1 -0
- package/dist/__tests__/dashboard-regression.test.d.ts +2 -0
- package/dist/__tests__/dashboard-regression.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-regression.test.js +68 -0
- package/dist/__tests__/dashboard-regression.test.js.map +1 -0
- package/dist/__tests__/dynamic-dag-regression.test.d.ts +2 -0
- package/dist/__tests__/dynamic-dag-regression.test.d.ts.map +1 -0
- package/dist/__tests__/dynamic-dag-regression.test.js +464 -0
- package/dist/__tests__/dynamic-dag-regression.test.js.map +1 -0
- package/dist/__tests__/ensure-skills.test.js +5 -0
- package/dist/__tests__/ensure-skills.test.js.map +1 -1
- package/dist/__tests__/ide-command-parity.test.js +2 -0
- package/dist/__tests__/ide-command-parity.test.js.map +1 -1
- package/dist/__tests__/project-generator.test.js +17 -0
- package/dist/__tests__/project-generator.test.js.map +1 -1
- package/dist/__tests__/reverse-facts.test.js +1 -0
- package/dist/__tests__/reverse-facts.test.js.map +1 -1
- package/dist/__tests__/terminal-parity-regression.test.d.ts +2 -0
- package/dist/__tests__/terminal-parity-regression.test.d.ts.map +1 -0
- package/dist/__tests__/terminal-parity-regression.test.js +116 -0
- package/dist/__tests__/terminal-parity-regression.test.js.map +1 -0
- package/dist/__tests__/terminal-parity.test.d.ts +2 -0
- package/dist/__tests__/terminal-parity.test.d.ts.map +1 -0
- package/dist/__tests__/terminal-parity.test.js +81 -0
- package/dist/__tests__/terminal-parity.test.js.map +1 -0
- package/dist/agent/__tests__/antigravity-driver.test.d.ts +2 -0
- package/dist/agent/__tests__/antigravity-driver.test.d.ts.map +1 -0
- package/dist/agent/__tests__/antigravity-driver.test.js +52 -0
- package/dist/agent/__tests__/antigravity-driver.test.js.map +1 -0
- package/dist/agent/__tests__/codex-driver.test.d.ts +2 -0
- package/dist/agent/__tests__/codex-driver.test.d.ts.map +1 -0
- package/dist/agent/__tests__/codex-driver.test.js +68 -0
- package/dist/agent/__tests__/codex-driver.test.js.map +1 -0
- package/dist/agent/__tests__/cursor-driver.test.d.ts +2 -0
- package/dist/agent/__tests__/cursor-driver.test.d.ts.map +1 -0
- package/dist/agent/__tests__/cursor-driver.test.js +52 -0
- package/dist/agent/__tests__/cursor-driver.test.js.map +1 -0
- package/dist/agent/driver.d.ts +1 -1
- package/dist/agent/driver.d.ts.map +1 -1
- package/dist/agent/drivers/antigravity.d.ts +8 -0
- package/dist/agent/drivers/antigravity.d.ts.map +1 -0
- package/dist/agent/drivers/antigravity.js +99 -0
- package/dist/agent/drivers/antigravity.js.map +1 -0
- package/dist/agent/drivers/codex.d.ts +12 -0
- package/dist/agent/drivers/codex.d.ts.map +1 -0
- package/dist/agent/drivers/codex.js +137 -0
- package/dist/agent/drivers/codex.js.map +1 -0
- package/dist/agent/drivers/cursor.d.ts +8 -0
- package/dist/agent/drivers/cursor.d.ts.map +1 -0
- package/dist/agent/drivers/cursor.js +99 -0
- package/dist/agent/drivers/cursor.js.map +1 -0
- package/dist/ai/__tests__/ai-core.test.d.ts +2 -0
- package/dist/ai/__tests__/ai-core.test.d.ts.map +1 -0
- package/dist/ai/__tests__/ai-core.test.js +41 -0
- package/dist/ai/__tests__/ai-core.test.js.map +1 -0
- package/dist/ai/__tests__/parity.test.d.ts +2 -0
- package/dist/ai/__tests__/parity.test.d.ts.map +1 -0
- package/dist/ai/__tests__/parity.test.js +36 -0
- package/dist/ai/__tests__/parity.test.js.map +1 -0
- package/dist/ai/__tests__/pipeline.test.d.ts +2 -0
- package/dist/ai/__tests__/pipeline.test.d.ts.map +1 -0
- package/dist/ai/__tests__/pipeline.test.js +147 -0
- package/dist/ai/__tests__/pipeline.test.js.map +1 -0
- package/dist/ai/__tests__/refine-bridge.test.d.ts +2 -0
- package/dist/ai/__tests__/refine-bridge.test.d.ts.map +1 -0
- package/dist/ai/__tests__/refine-bridge.test.js +17 -0
- package/dist/ai/__tests__/refine-bridge.test.js.map +1 -0
- package/dist/ai/__tests__/resolve.test.d.ts +2 -0
- package/dist/ai/__tests__/resolve.test.d.ts.map +1 -0
- package/dist/ai/__tests__/resolve.test.js +42 -0
- package/dist/ai/__tests__/resolve.test.js.map +1 -0
- package/dist/ai/capabilities.d.ts +3 -0
- package/dist/ai/capabilities.d.ts.map +1 -0
- package/dist/ai/capabilities.js +11 -0
- package/dist/ai/capabilities.js.map +1 -0
- package/dist/ai/command-options.d.ts +10 -0
- package/dist/ai/command-options.d.ts.map +1 -0
- package/dist/ai/command-options.js +15 -0
- package/dist/ai/command-options.js.map +1 -0
- package/dist/ai/config.d.ts +27 -0
- package/dist/ai/config.d.ts.map +1 -0
- package/dist/ai/config.js +89 -0
- package/dist/ai/config.js.map +1 -0
- package/dist/ai/parity.d.ts +13 -0
- package/dist/ai/parity.d.ts.map +1 -0
- package/dist/ai/parity.js +87 -0
- package/dist/ai/parity.js.map +1 -0
- package/dist/ai/parse-json-output.d.ts +5 -0
- package/dist/ai/parse-json-output.d.ts.map +1 -0
- package/dist/ai/parse-json-output.js +25 -0
- package/dist/ai/parse-json-output.js.map +1 -0
- package/dist/ai/pipeline.d.ts +20 -0
- package/dist/ai/pipeline.d.ts.map +1 -0
- package/dist/ai/pipeline.js +303 -0
- package/dist/ai/pipeline.js.map +1 -0
- package/dist/ai/prompts.d.ts +6 -0
- package/dist/ai/prompts.d.ts.map +1 -0
- package/dist/ai/prompts.js +49 -0
- package/dist/ai/prompts.js.map +1 -0
- package/dist/ai/providers.d.ts +63 -0
- package/dist/ai/providers.d.ts.map +1 -0
- package/dist/ai/providers.js +297 -0
- package/dist/ai/providers.js.map +1 -0
- package/dist/ai/refine-bridge.d.ts +5 -0
- package/dist/ai/refine-bridge.d.ts.map +1 -0
- package/dist/ai/refine-bridge.js +14 -0
- package/dist/ai/refine-bridge.js.map +1 -0
- package/dist/ai/registry.d.ts +12 -0
- package/dist/ai/registry.d.ts.map +1 -0
- package/dist/ai/registry.js +43 -0
- package/dist/ai/registry.js.map +1 -0
- package/dist/ai/resolve.d.ts +28 -0
- package/dist/ai/resolve.d.ts.map +1 -0
- package/dist/ai/resolve.js +83 -0
- package/dist/ai/resolve.js.map +1 -0
- package/dist/ai/schemas.d.ts +175 -0
- package/dist/ai/schemas.d.ts.map +1 -0
- package/dist/ai/schemas.js +199 -0
- package/dist/ai/schemas.js.map +1 -0
- package/dist/ai/types.d.ts +52 -0
- package/dist/ai/types.d.ts.map +1 -0
- package/dist/ai/types.js +8 -0
- package/dist/ai/types.js.map +1 -0
- package/dist/bin/dare.js +4 -0
- package/dist/bin/dare.js.map +1 -1
- package/dist/commands/__tests__/ai-command.test.d.ts +2 -0
- package/dist/commands/__tests__/ai-command.test.d.ts.map +1 -0
- package/dist/commands/__tests__/ai-command.test.js +68 -0
- package/dist/commands/__tests__/ai-command.test.js.map +1 -0
- package/dist/commands/__tests__/dashboard-command.test.d.ts +2 -0
- package/dist/commands/__tests__/dashboard-command.test.d.ts.map +1 -0
- package/dist/commands/__tests__/dashboard-command.test.js +62 -0
- package/dist/commands/__tests__/dashboard-command.test.js.map +1 -0
- package/dist/commands/__tests__/execute-agent.test.js +82 -0
- package/dist/commands/__tests__/execute-agent.test.js.map +1 -1
- package/dist/commands/__tests__/gate-flags.test.d.ts +2 -0
- package/dist/commands/__tests__/gate-flags.test.d.ts.map +1 -0
- package/dist/commands/__tests__/gate-flags.test.js +58 -0
- package/dist/commands/__tests__/gate-flags.test.js.map +1 -0
- package/dist/commands/__tests__/persist-viz.test.d.ts +2 -0
- package/dist/commands/__tests__/persist-viz.test.d.ts.map +1 -0
- package/dist/commands/__tests__/persist-viz.test.js +178 -0
- package/dist/commands/__tests__/persist-viz.test.js.map +1 -0
- package/dist/commands/__tests__/refine-apply.test.d.ts +2 -0
- package/dist/commands/__tests__/refine-apply.test.d.ts.map +1 -0
- package/dist/commands/__tests__/refine-apply.test.js +118 -0
- package/dist/commands/__tests__/refine-apply.test.js.map +1 -0
- package/dist/commands/__tests__/replan-splice.test.d.ts +2 -0
- package/dist/commands/__tests__/replan-splice.test.d.ts.map +1 -0
- package/dist/commands/__tests__/replan-splice.test.js +295 -0
- package/dist/commands/__tests__/replan-splice.test.js.map +1 -0
- package/dist/commands/ai.d.ts +3 -0
- package/dist/commands/ai.d.ts.map +1 -0
- package/dist/commands/ai.js +141 -0
- package/dist/commands/ai.js.map +1 -0
- package/dist/commands/blueprint.d.ts.map +1 -1
- package/dist/commands/blueprint.js +17 -3
- package/dist/commands/blueprint.js.map +1 -1
- package/dist/commands/dag.d.ts.map +1 -1
- package/dist/commands/dag.js +2 -0
- package/dist/commands/dag.js.map +1 -1
- package/dist/commands/dashboard.d.ts +17 -0
- package/dist/commands/dashboard.d.ts.map +1 -0
- package/dist/commands/dashboard.js +77 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/design.d.ts.map +1 -1
- package/dist/commands/design.js +21 -2
- package/dist/commands/design.js.map +1 -1
- package/dist/commands/discover.d.ts.map +1 -1
- package/dist/commands/discover.js +9 -1
- package/dist/commands/discover.js.map +1 -1
- package/dist/commands/dna.d.ts.map +1 -1
- package/dist/commands/dna.js +23 -3
- package/dist/commands/dna.js.map +1 -1
- package/dist/commands/execute.d.ts +11 -0
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +219 -6
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/graph.d.ts.map +1 -1
- package/dist/commands/graph.js +33 -5
- package/dist/commands/graph.js.map +1 -1
- package/dist/commands/guard.d.ts.map +1 -1
- package/dist/commands/guard.js +27 -3
- package/dist/commands/guard.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +1 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +14 -2
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/patterns.d.ts.map +1 -1
- package/dist/commands/patterns.js +14 -2
- package/dist/commands/patterns.js.map +1 -1
- package/dist/commands/refine.d.ts +2 -0
- package/dist/commands/refine.d.ts.map +1 -1
- package/dist/commands/refine.js +94 -22
- package/dist/commands/refine.js.map +1 -1
- package/dist/commands/reverse.d.ts.map +1 -1
- package/dist/commands/reverse.js +28 -3
- package/dist/commands/reverse.js.map +1 -1
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +60 -7
- package/dist/commands/review.js.map +1 -1
- package/dist/core/types/project.d.ts +1 -1
- package/dist/core/types/project.d.ts.map +1 -1
- package/dist/dag-runner/__tests__/maxdepth-config.test.d.ts +2 -0
- package/dist/dag-runner/__tests__/maxdepth-config.test.d.ts.map +1 -0
- package/dist/dag-runner/__tests__/maxdepth-config.test.js +28 -0
- package/dist/dag-runner/__tests__/maxdepth-config.test.js.map +1 -0
- package/dist/dag-runner/__tests__/sub-dag.test.d.ts +2 -0
- package/dist/dag-runner/__tests__/sub-dag.test.d.ts.map +1 -0
- package/dist/dag-runner/__tests__/sub-dag.test.js +127 -0
- package/dist/dag-runner/__tests__/sub-dag.test.js.map +1 -0
- package/dist/dag-runner/run_dag.d.ts +3 -1
- package/dist/dag-runner/run_dag.d.ts.map +1 -1
- package/dist/dag-runner/run_dag.js.map +1 -1
- package/dist/dag-runner/state-store.d.ts +2 -0
- package/dist/dag-runner/state-store.d.ts.map +1 -1
- package/dist/dag-runner/state-store.js +44 -8
- package/dist/dag-runner/state-store.js.map +1 -1
- package/dist/dag-runner/sub-dag.d.ts +23 -0
- package/dist/dag-runner/sub-dag.d.ts.map +1 -0
- package/dist/dag-runner/sub-dag.js +117 -0
- package/dist/dag-runner/sub-dag.js.map +1 -0
- package/dist/dashboard/__tests__/dashboard-routes.test.d.ts +2 -0
- package/dist/dashboard/__tests__/dashboard-routes.test.d.ts.map +1 -0
- package/dist/dashboard/__tests__/dashboard-routes.test.js +102 -0
- package/dist/dashboard/__tests__/dashboard-routes.test.js.map +1 -0
- package/dist/dashboard/routes.d.ts +8 -0
- package/dist/dashboard/routes.d.ts.map +1 -0
- package/dist/dashboard/routes.js +70 -0
- package/dist/dashboard/routes.js.map +1 -0
- package/dist/exec/safe-spawn.d.ts.map +1 -1
- package/dist/exec/safe-spawn.js +6 -1
- package/dist/exec/safe-spawn.js.map +1 -1
- package/dist/graphrag/__tests__/hybrid-integration.test.js +1 -1
- package/dist/graphrag/__tests__/hybrid-integration.test.js.map +1 -1
- package/dist/graphrag/__tests__/no-heavy-dep-in-core.test.js +2 -2
- package/dist/graphrag/__tests__/no-heavy-dep-in-core.test.js.map +1 -1
- package/dist/graphrag/embeddings.js +1 -1
- package/dist/graphrag/embeddings.js.map +1 -1
- package/dist/http/__tests__/shared-app.test.d.ts +2 -0
- package/dist/http/__tests__/shared-app.test.d.ts.map +1 -0
- package/dist/http/__tests__/shared-app.test.js +57 -0
- package/dist/http/__tests__/shared-app.test.js.map +1 -0
- package/dist/http/app.d.ts +15 -0
- package/dist/http/app.d.ts.map +1 -0
- package/dist/http/app.js +32 -0
- package/dist/http/app.js.map +1 -0
- package/dist/mcp-server/server.d.ts.map +1 -1
- package/dist/mcp-server/server.js +5 -15
- package/dist/mcp-server/server.js.map +1 -1
- package/dist/reporters/__tests__/github-reporter.test.d.ts +2 -0
- package/dist/reporters/__tests__/github-reporter.test.d.ts.map +1 -0
- package/dist/reporters/__tests__/github-reporter.test.js +93 -0
- package/dist/reporters/__tests__/github-reporter.test.js.map +1 -0
- package/dist/reporters/ci-gate.d.ts +27 -0
- package/dist/reporters/ci-gate.d.ts.map +1 -0
- package/dist/reporters/ci-gate.js +100 -0
- package/dist/reporters/ci-gate.js.map +1 -0
- package/dist/reporters/github.d.ts +36 -0
- package/dist/reporters/github.d.ts.map +1 -0
- package/dist/reporters/github.js +142 -0
- package/dist/reporters/github.js.map +1 -0
- package/dist/skills/bundled.d.ts +5 -0
- package/dist/skills/bundled.d.ts.map +1 -0
- package/dist/skills/bundled.js +34 -0
- package/dist/skills/bundled.js.map +1 -0
- package/dist/skills/commands/add.d.ts +1 -3
- package/dist/skills/commands/add.d.ts.map +1 -1
- package/dist/skills/commands/add.js +20 -3
- package/dist/skills/commands/add.js.map +1 -1
- package/dist/skills/tests/bundled.spec.d.ts +2 -0
- package/dist/skills/tests/bundled.spec.d.ts.map +1 -0
- package/dist/skills/tests/bundled.spec.js +24 -0
- package/dist/skills/tests/bundled.spec.js.map +1 -0
- package/dist/telemetry/__tests__/aggregator.test.d.ts +2 -0
- package/dist/telemetry/__tests__/aggregator.test.d.ts.map +1 -0
- package/dist/telemetry/__tests__/aggregator.test.js +164 -0
- package/dist/telemetry/__tests__/aggregator.test.js.map +1 -0
- package/dist/telemetry/aggregator.d.ts +43 -0
- package/dist/telemetry/aggregator.d.ts.map +1 -0
- package/dist/telemetry/aggregator.js +282 -0
- package/dist/telemetry/aggregator.js.map +1 -0
- package/dist/types/UpdateManifest.types.d.ts +1 -1
- package/dist/types/UpdateManifest.types.d.ts.map +1 -1
- package/dist/utils/dag-converter.js +1 -1
- package/dist/utils/dag-converter.js.map +1 -1
- package/dist/utils/excalidraw-renderer.d.ts.map +1 -1
- package/dist/utils/excalidraw-renderer.js +1 -0
- package/dist/utils/excalidraw-renderer.js.map +1 -1
- package/dist/utils/graph-renderer.d.ts +2 -0
- package/dist/utils/graph-renderer.d.ts.map +1 -1
- package/dist/utils/graph-renderer.js +188 -14
- package/dist/utils/graph-renderer.js.map +1 -1
- package/dist/utils/project-detector.d.ts +1 -0
- package/dist/utils/project-detector.d.ts.map +1 -1
- package/dist/utils/project-detector.js +8 -0
- package/dist/utils/project-detector.js.map +1 -1
- package/dist/utils/project-generator.d.ts +1 -1
- package/dist/utils/project-generator.d.ts.map +1 -1
- package/dist/utils/project-generator.js +23 -2
- package/dist/utils/project-generator.js.map +1 -1
- package/dist/utils/templates.d.ts +2 -0
- package/dist/utils/templates.d.ts.map +1 -1
- package/dist/utils/templates.js +74 -0
- package/dist/utils/templates.js.map +1 -1
- package/dist/verification/__tests__/decay-policy.test.js +1 -0
- package/dist/verification/__tests__/decay-policy.test.js.map +1 -1
- package/dist/verification/__tests__/safe-spawn.test.js +12 -0
- package/dist/verification/__tests__/safe-spawn.test.js.map +1 -1
- package/dist/verification/config.d.ts.map +1 -1
- package/dist/verification/config.js +2 -0
- package/dist/verification/config.js.map +1 -1
- package/dist/verification/types.d.ts +2 -0
- package/dist/verification/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/skills/dare-ax/generator.ts +325 -0
- package/skills/dare-ax/index.ts +19 -0
- package/skills/dare-ax/metrics.ts +352 -0
- package/skills/dare-ax/package-lock.json +1855 -0
- package/skills/dare-ax/package.json +50 -0
- package/skills/dare-ax/secret-detector.ts +123 -0
- package/skills/dare-ax/skill.yml +19 -0
- package/skills/dare-ax/templates/llms.txt.jinja2 +80 -0
- package/skills/dare-ax/tests/generator.spec.ts +193 -0
- package/skills/dare-ax/tests/metrics.spec.ts +394 -0
- package/skills/dare-ax/tests/validator.spec.ts +298 -0
- package/skills/dare-ax/tsconfig.json +18 -0
- package/skills/dare-ax/types.ts +79 -0
- package/skills/dare-ax/validator.ts +238 -0
- package/skills/dare-frontend-design/generator.ts +616 -0
- package/skills/dare-frontend-design/index.ts +25 -0
- package/skills/dare-frontend-design/linter.ts +227 -0
- package/skills/dare-frontend-design/metrics.ts +82 -0
- package/skills/dare-frontend-design/package-lock.json +1855 -0
- package/skills/dare-frontend-design/package.json +43 -0
- package/skills/dare-frontend-design/skill.yml +20 -0
- package/skills/dare-frontend-design/tests/frontend_design.spec.ts +435 -0
- package/skills/dare-frontend-design/tsconfig.json +18 -0
- package/skills/dare-frontend-design/types.ts +62 -0
- package/skills/dare-layered-design/generator.ts +740 -0
- package/skills/dare-layered-design/index.ts +17 -0
- package/skills/dare-layered-design/linter.ts +462 -0
- package/skills/dare-layered-design/metrics.ts +409 -0
- package/skills/dare-layered-design/package-lock.json +1855 -0
- package/skills/dare-layered-design/package.json +50 -0
- package/skills/dare-layered-design/skill.yml +35 -0
- package/skills/dare-layered-design/tests/generator.spec.ts +156 -0
- package/skills/dare-layered-design/tests/linter.spec.ts +255 -0
- package/skills/dare-layered-design/tests/metrics.spec.ts +286 -0
- package/skills/dare-layered-design/tsconfig.json +18 -0
- package/skills/dare-layered-design/types.ts +48 -0
- package/skills/dare-llm-integration/cache/llm_cache.ts +122 -0
- package/skills/dare-llm-integration/index.ts +49 -0
- package/skills/dare-llm-integration/metrics.ts +107 -0
- package/skills/dare-llm-integration/package-lock.json +1855 -0
- package/skills/dare-llm-integration/package.json +49 -0
- package/skills/dare-llm-integration/prompts/prompt_loader.ts +258 -0
- package/skills/dare-llm-integration/providers/anthropic_provider.ts +159 -0
- package/skills/dare-llm-integration/providers/dummy_provider.ts +113 -0
- package/skills/dare-llm-integration/providers/llm_provider.ts +6 -0
- package/skills/dare-llm-integration/providers/openai_provider.ts +215 -0
- package/skills/dare-llm-integration/rate_limit/token_bucket.ts +86 -0
- package/skills/dare-llm-integration/skill.yml +23 -0
- package/skills/dare-llm-integration/tests/fixtures/greet_v1.jinja2 +1 -0
- package/skills/dare-llm-integration/tests/fixtures/summarize_v1.jinja2 +1 -0
- package/skills/dare-llm-integration/tests/fixtures/summarize_v2.jinja2 +3 -0
- package/skills/dare-llm-integration/tests/llm_integration.spec.ts +657 -0
- package/skills/dare-llm-integration/tsconfig.json +23 -0
- package/skills/dare-llm-integration/types.ts +91 -0
- package/skills/dare-llm-integration/validators/output_validator.ts +200 -0
- package/skills/dare-quality-telemetry/collect.ts +134 -0
- package/skills/dare-quality-telemetry/collectors/dare_ax_collector.ts +301 -0
- package/skills/dare-quality-telemetry/collectors/dare_layered_design_collector.ts +406 -0
- package/skills/dare-quality-telemetry/collectors/index.ts +24 -0
- package/skills/dare-quality-telemetry/github_actions_template.ts +25 -0
- package/skills/dare-quality-telemetry/index.ts +18 -0
- package/skills/dare-quality-telemetry/metrics.ts +137 -0
- package/skills/dare-quality-telemetry/package-lock.json +1855 -0
- package/skills/dare-quality-telemetry/package.json +48 -0
- package/skills/dare-quality-telemetry/regression.ts +60 -0
- package/skills/dare-quality-telemetry/reporter.ts +132 -0
- package/skills/dare-quality-telemetry/skill.yml +18 -0
- package/skills/dare-quality-telemetry/tests/quality_telemetry.spec.ts +885 -0
- package/skills/dare-quality-telemetry/tsconfig.json +19 -0
- package/skills/dare-quality-telemetry/types.ts +41 -0
- package/skills/dare-realtime/event_registry.ts +101 -0
- package/skills/dare-realtime/index.ts +30 -0
- package/skills/dare-realtime/metrics.ts +84 -0
- package/skills/dare-realtime/package-lock.json +1855 -0
- package/skills/dare-realtime/package.json +43 -0
- package/skills/dare-realtime/reconnect_strategy.ts +85 -0
- package/skills/dare-realtime/schema_validator.ts +80 -0
- package/skills/dare-realtime/skill.yml +21 -0
- package/skills/dare-realtime/subscription_manager.ts +106 -0
- package/skills/dare-realtime/tests/realtime.spec.ts +482 -0
- package/skills/dare-realtime/tsconfig.json +18 -0
- package/skills/dare-realtime/types.ts +51 -0
- package/templates/.github/workflows/dare-pr.yml +26 -0
- package/templates/dashboard/app.js +184 -0
- package/templates/dashboard/index.html +63 -0
- package/templates/dashboard/style.css +134 -0
- package/templates/ide/antigravity/.agents/skills/dare-ai/SKILL.md +17 -0
- package/templates/ide/antigravity/.agents/skills/dare-blueprint/SKILL.md +2 -0
- package/templates/ide/antigravity/.agents/skills/dare-dashboard/SKILL.md +18 -0
- package/templates/ide/antigravity/.agents/skills/dare-design/SKILL.md +2 -0
- package/templates/ide/antigravity/.agents/skills/dare-dna/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-migrate/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-patterns/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-refine/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-reverse/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-review/SKILL.md +3 -0
- package/templates/ide/claude/.claude/commands/dare-ai.md +17 -0
- package/templates/ide/claude/.claude/commands/dare-blueprint.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-dashboard.md +18 -0
- package/templates/ide/claude/.claude/commands/dare-design.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-dna.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-migrate.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-patterns.md +3 -0
- package/templates/ide/claude/.claude/commands/dare-refine.md +3 -0
- package/templates/ide/claude/.claude/commands/dare-reverse.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-review.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-ai.md +17 -0
- package/templates/ide/cursor/.cursor/commands/dare-blueprint.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-dashboard.md +18 -0
- package/templates/ide/cursor/.cursor/commands/dare-design.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-dna.md +2 -0
- package/templates/ide/cursor/.cursor/commands/dare-migrate.md +2 -0
- package/templates/ide/cursor/.cursor/commands/dare-patterns.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-refine.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-reverse.md +2 -0
- package/templates/ide/cursor/.cursor/commands/dare-review.md +3 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dare-ax — metrics tests (M-01 to M-04)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import os from 'os';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { DareAxMetrics } from '../metrics.js';
|
|
10
|
+
|
|
11
|
+
/** A valid llms.txt for M-01 tests */
|
|
12
|
+
const VALID_LLMS_TXT = `# llms.txt — Project Context for AI Agents
|
|
13
|
+
|
|
14
|
+
## Project Overview
|
|
15
|
+
A test project demonstrating DARE Method layered architecture.
|
|
16
|
+
|
|
17
|
+
## Tech Stack
|
|
18
|
+
- Language: TypeScript
|
|
19
|
+
- Framework: NestJS
|
|
20
|
+
- Database: Postgres
|
|
21
|
+
|
|
22
|
+
## Architecture
|
|
23
|
+
4-layer architecture: Handlers, Services, Repositories, Models.
|
|
24
|
+
Dependency flows downward only: Handler → Service → Repository → Model.
|
|
25
|
+
|
|
26
|
+
## Directory Structure
|
|
27
|
+
\`\`\`
|
|
28
|
+
src/
|
|
29
|
+
├── handlers/
|
|
30
|
+
├── services/
|
|
31
|
+
├── repositories/
|
|
32
|
+
└── models/
|
|
33
|
+
\`\`\`
|
|
34
|
+
|
|
35
|
+
## Key Endpoints
|
|
36
|
+
- GET /health — Health check
|
|
37
|
+
- GET /api/v1/users — List users
|
|
38
|
+
|
|
39
|
+
## Important Files
|
|
40
|
+
- config.json — Configuration
|
|
41
|
+
|
|
42
|
+
## Getting Started
|
|
43
|
+
\`\`\`bash
|
|
44
|
+
make dev
|
|
45
|
+
\`\`\`
|
|
46
|
+
|
|
47
|
+
## Rate Limits
|
|
48
|
+
- Public endpoints: 100 req/min per IP
|
|
49
|
+
|
|
50
|
+
## Security Notes
|
|
51
|
+
- Input validated in handlers
|
|
52
|
+
|
|
53
|
+
## For AI Agents
|
|
54
|
+
- OpenAPI: GET /openapi.json
|
|
55
|
+
- CLI: ./app --json
|
|
56
|
+
- No secrets here
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
describe('DareAxMetrics', () => {
|
|
60
|
+
let tmpDir: string;
|
|
61
|
+
let metrics: DareAxMetrics;
|
|
62
|
+
|
|
63
|
+
beforeEach(() => {
|
|
64
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'dare-ax-metrics-test-'));
|
|
65
|
+
metrics = new DareAxMetrics();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
afterEach(() => {
|
|
69
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// ── M-01 ──────────────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
describe('M-01: llms.txt valid', () => {
|
|
75
|
+
it('passes when llms.txt exists and is valid', () => {
|
|
76
|
+
fs.writeFileSync(path.join(tmpDir, 'llms.txt'), VALID_LLMS_TXT, 'utf-8');
|
|
77
|
+
|
|
78
|
+
const result = metrics.collectM01(tmpDir);
|
|
79
|
+
|
|
80
|
+
expect(result.id).toBe('M-01');
|
|
81
|
+
expect(result.pass).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('fails when llms.txt does not exist', () => {
|
|
85
|
+
const result = metrics.collectM01(tmpDir);
|
|
86
|
+
|
|
87
|
+
expect(result.id).toBe('M-01');
|
|
88
|
+
expect(result.pass).toBe(false);
|
|
89
|
+
expect(result.detail).toContain('not found');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('fails when llms.txt is missing required sections', () => {
|
|
93
|
+
fs.writeFileSync(
|
|
94
|
+
path.join(tmpDir, 'llms.txt'),
|
|
95
|
+
'# llms.txt\n\nJust a comment, no sections.\n',
|
|
96
|
+
'utf-8'
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const result = metrics.collectM01(tmpDir);
|
|
100
|
+
|
|
101
|
+
expect(result.pass).toBe(false);
|
|
102
|
+
expect(result.detail).toContain('Validation failed');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('fails when llms.txt contains a secret', () => {
|
|
106
|
+
const content = VALID_LLMS_TXT + '\naws_key=' + 'AKIA' + 'IOSFODNN7EXAMPLE\n';
|
|
107
|
+
fs.writeFileSync(path.join(tmpDir, 'llms.txt'), content, 'utf-8');
|
|
108
|
+
|
|
109
|
+
const result = metrics.collectM01(tmpDir);
|
|
110
|
+
|
|
111
|
+
expect(result.pass).toBe(false);
|
|
112
|
+
expect(result.detail!.toLowerCase()).toContain('secret');
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// ── M-02 ──────────────────────────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
describe('M-02: openapi.json exists', () => {
|
|
119
|
+
it('passes when openapi.json is at project root', () => {
|
|
120
|
+
fs.writeFileSync(
|
|
121
|
+
path.join(tmpDir, 'openapi.json'),
|
|
122
|
+
'{"openapi":"3.1.0"}',
|
|
123
|
+
'utf-8'
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const result = metrics.collectM02(tmpDir);
|
|
127
|
+
|
|
128
|
+
expect(result.id).toBe('M-02');
|
|
129
|
+
expect(result.pass).toBe(true);
|
|
130
|
+
expect(result.detail).toContain('openapi.json');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('passes when openapi.json is in public/', () => {
|
|
134
|
+
const publicDir = path.join(tmpDir, 'public');
|
|
135
|
+
fs.mkdirSync(publicDir);
|
|
136
|
+
fs.writeFileSync(
|
|
137
|
+
path.join(publicDir, 'openapi.json'),
|
|
138
|
+
'{"openapi":"3.1.0"}',
|
|
139
|
+
'utf-8'
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const result = metrics.collectM02(tmpDir);
|
|
143
|
+
|
|
144
|
+
expect(result.pass).toBe(true);
|
|
145
|
+
// Normalize path separators for Windows/Unix compatibility
|
|
146
|
+
expect(result.detail?.replace(/\\/g, '/')).toContain('public/openapi.json');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('passes when openapi.yaml is at project root', () => {
|
|
150
|
+
fs.writeFileSync(path.join(tmpDir, 'openapi.yaml'), 'openapi: "3.1.0"', 'utf-8');
|
|
151
|
+
|
|
152
|
+
const result = metrics.collectM02(tmpDir);
|
|
153
|
+
|
|
154
|
+
expect(result.pass).toBe(true);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('fails when no openapi file found', () => {
|
|
158
|
+
const result = metrics.collectM02(tmpDir);
|
|
159
|
+
|
|
160
|
+
expect(result.id).toBe('M-02');
|
|
161
|
+
expect(result.pass).toBe(false);
|
|
162
|
+
expect(result.detail).toContain('No openapi');
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// ── M-03 ──────────────────────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
describe('M-03: CLI supports --json', () => {
|
|
169
|
+
it('passes when --json flag found in src/', () => {
|
|
170
|
+
const srcDir = path.join(tmpDir, 'src');
|
|
171
|
+
fs.mkdirSync(srcDir);
|
|
172
|
+
fs.writeFileSync(
|
|
173
|
+
path.join(srcDir, 'cli.ts'),
|
|
174
|
+
`
|
|
175
|
+
import { Command } from 'commander';
|
|
176
|
+
const cmd = new Command();
|
|
177
|
+
cmd.option('--json', 'Output as JSON');
|
|
178
|
+
cmd.parse(process.argv);
|
|
179
|
+
`,
|
|
180
|
+
'utf-8'
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
const result = metrics.collectM03(tmpDir);
|
|
184
|
+
|
|
185
|
+
expect(result.id).toBe('M-03');
|
|
186
|
+
expect(result.pass).toBe(true);
|
|
187
|
+
expect(result.detail).toContain('--json');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('passes when --json found in bin/', () => {
|
|
191
|
+
const binDir = path.join(tmpDir, 'bin');
|
|
192
|
+
fs.mkdirSync(binDir);
|
|
193
|
+
fs.writeFileSync(
|
|
194
|
+
path.join(binDir, 'dare.ts'),
|
|
195
|
+
'program.option("--json", "JSON output");\n',
|
|
196
|
+
'utf-8'
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const result = metrics.collectM03(tmpDir);
|
|
200
|
+
|
|
201
|
+
expect(result.pass).toBe(true);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('passes when --json found nested in src/commands/', () => {
|
|
205
|
+
const dir = path.join(tmpDir, 'src', 'commands');
|
|
206
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
207
|
+
fs.writeFileSync(
|
|
208
|
+
path.join(dir, 'init.ts'),
|
|
209
|
+
'.option("--json", "Output results as JSON")\n',
|
|
210
|
+
'utf-8'
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const result = metrics.collectM03(tmpDir);
|
|
214
|
+
|
|
215
|
+
expect(result.pass).toBe(true);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('fails when no --json flag in any source file', () => {
|
|
219
|
+
const srcDir = path.join(tmpDir, 'src');
|
|
220
|
+
fs.mkdirSync(srcDir);
|
|
221
|
+
fs.writeFileSync(
|
|
222
|
+
path.join(srcDir, 'cli.ts'),
|
|
223
|
+
'program.option("--verbose", "Verbose output");\n',
|
|
224
|
+
'utf-8'
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
const result = metrics.collectM03(tmpDir);
|
|
228
|
+
|
|
229
|
+
expect(result.id).toBe('M-03');
|
|
230
|
+
expect(result.pass).toBe(false);
|
|
231
|
+
expect(result.detail).toContain('--json');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('fails when project has no src/ or bin/ directories', () => {
|
|
235
|
+
const result = metrics.collectM03(tmpDir);
|
|
236
|
+
|
|
237
|
+
expect(result.pass).toBe(false);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// ── M-04 ──────────────────────────────────────────────────────────────
|
|
242
|
+
|
|
243
|
+
describe('M-04: Rate limit configuration detected', () => {
|
|
244
|
+
it('passes when express-rate-limit in package.json dependencies', () => {
|
|
245
|
+
const pkg = {
|
|
246
|
+
name: 'test',
|
|
247
|
+
dependencies: { 'express-rate-limit': '^7.0.0' },
|
|
248
|
+
};
|
|
249
|
+
fs.writeFileSync(
|
|
250
|
+
path.join(tmpDir, 'package.json'),
|
|
251
|
+
JSON.stringify(pkg),
|
|
252
|
+
'utf-8'
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
const result = metrics.collectM04(tmpDir);
|
|
256
|
+
|
|
257
|
+
expect(result.id).toBe('M-04');
|
|
258
|
+
expect(result.pass).toBe(true);
|
|
259
|
+
expect(result.detail).toContain('express-rate-limit');
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('passes when @nestjs/throttler in package.json', () => {
|
|
263
|
+
const pkg = {
|
|
264
|
+
name: 'test',
|
|
265
|
+
dependencies: { '@nestjs/throttler': '^5.0.0' },
|
|
266
|
+
};
|
|
267
|
+
fs.writeFileSync(
|
|
268
|
+
path.join(tmpDir, 'package.json'),
|
|
269
|
+
JSON.stringify(pkg),
|
|
270
|
+
'utf-8'
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
const result = metrics.collectM04(tmpDir);
|
|
274
|
+
|
|
275
|
+
expect(result.pass).toBe(true);
|
|
276
|
+
expect(result.detail).toContain('nestjs/throttler');
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('passes when rack-attack in Gemfile', () => {
|
|
280
|
+
fs.writeFileSync(
|
|
281
|
+
path.join(tmpDir, 'Gemfile'),
|
|
282
|
+
'gem "rack-attack"\n',
|
|
283
|
+
'utf-8'
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
const result = metrics.collectM04(tmpDir);
|
|
287
|
+
|
|
288
|
+
expect(result.pass).toBe(true);
|
|
289
|
+
expect(result.detail).toContain('rack-attack');
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('passes when tower-governor in Cargo.toml', () => {
|
|
293
|
+
fs.writeFileSync(
|
|
294
|
+
path.join(tmpDir, 'Cargo.toml'),
|
|
295
|
+
'[dependencies]\ntower-governor = "0.2"\n',
|
|
296
|
+
'utf-8'
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
const result = metrics.collectM04(tmpDir);
|
|
300
|
+
|
|
301
|
+
expect(result.pass).toBe(true);
|
|
302
|
+
expect(result.detail).toContain('tower-governor');
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('passes when rate_limit pattern found in source', () => {
|
|
306
|
+
const srcDir = path.join(tmpDir, 'src');
|
|
307
|
+
fs.mkdirSync(srcDir);
|
|
308
|
+
fs.writeFileSync(
|
|
309
|
+
path.join(srcDir, 'middleware.ts'),
|
|
310
|
+
'app.use(rateLimitMiddleware({ windowMs: 60000, max: 100 }));\n',
|
|
311
|
+
'utf-8'
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
const result = metrics.collectM04(tmpDir);
|
|
315
|
+
|
|
316
|
+
expect(result.pass).toBe(true);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('fails when no rate limit library or pattern found', () => {
|
|
320
|
+
const pkg = {
|
|
321
|
+
name: 'test',
|
|
322
|
+
dependencies: { express: '^4.0.0', nestjs: '^10.0.0' },
|
|
323
|
+
};
|
|
324
|
+
fs.writeFileSync(
|
|
325
|
+
path.join(tmpDir, 'package.json'),
|
|
326
|
+
JSON.stringify(pkg),
|
|
327
|
+
'utf-8'
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
const result = metrics.collectM04(tmpDir);
|
|
331
|
+
|
|
332
|
+
expect(result.id).toBe('M-04');
|
|
333
|
+
expect(result.pass).toBe(false);
|
|
334
|
+
expect(result.detail).toContain('No rate limit');
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('fails when no manifests exist at all', () => {
|
|
338
|
+
const result = metrics.collectM04(tmpDir);
|
|
339
|
+
|
|
340
|
+
expect(result.pass).toBe(false);
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// ── collect() (all metrics) ────────────────────────────────────────────
|
|
345
|
+
|
|
346
|
+
describe('collect()', () => {
|
|
347
|
+
it('returns array of 4 MetricResults', () => {
|
|
348
|
+
const results = metrics.collect(tmpDir);
|
|
349
|
+
|
|
350
|
+
expect(results).toHaveLength(4);
|
|
351
|
+
expect(results.map((r) => r.id)).toEqual(['M-01', 'M-02', 'M-03', 'M-04']);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('all pass when project has all required assets', () => {
|
|
355
|
+
// Setup a project that satisfies all metrics
|
|
356
|
+
fs.writeFileSync(path.join(tmpDir, 'llms.txt'), VALID_LLMS_TXT, 'utf-8');
|
|
357
|
+
fs.writeFileSync(
|
|
358
|
+
path.join(tmpDir, 'openapi.json'),
|
|
359
|
+
'{"openapi":"3.1.0"}',
|
|
360
|
+
'utf-8'
|
|
361
|
+
);
|
|
362
|
+
const srcDir = path.join(tmpDir, 'src');
|
|
363
|
+
fs.mkdirSync(srcDir);
|
|
364
|
+
fs.writeFileSync(
|
|
365
|
+
path.join(srcDir, 'cli.ts'),
|
|
366
|
+
'cmd.option("--json", "JSON output");\n',
|
|
367
|
+
'utf-8'
|
|
368
|
+
);
|
|
369
|
+
const pkg = {
|
|
370
|
+
name: 'test',
|
|
371
|
+
dependencies: { 'express-rate-limit': '^7.0.0' },
|
|
372
|
+
};
|
|
373
|
+
fs.writeFileSync(
|
|
374
|
+
path.join(tmpDir, 'package.json'),
|
|
375
|
+
JSON.stringify(pkg),
|
|
376
|
+
'utf-8'
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
const results = metrics.collect(tmpDir);
|
|
380
|
+
|
|
381
|
+
for (const r of results) {
|
|
382
|
+
expect(r.pass, `Expected ${r.id} to pass, got: ${r.detail}`).toBe(true);
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('all fail when project directory is empty', () => {
|
|
387
|
+
const results = metrics.collect(tmpDir);
|
|
388
|
+
|
|
389
|
+
for (const r of results) {
|
|
390
|
+
expect(r.pass, `Expected ${r.id} to fail`).toBe(false);
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
});
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dare-ax — validator tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import os from 'os';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { DareAxValidator } from '../validator.js';
|
|
10
|
+
|
|
11
|
+
/** A complete, valid llms.txt for use as baseline in tests */
|
|
12
|
+
const VALID_LLMS_TXT = `# llms.txt — Project Context for AI Agents
|
|
13
|
+
|
|
14
|
+
## Project Overview
|
|
15
|
+
This is a test API project that provides user management endpoints.
|
|
16
|
+
It follows the DARE Method layered architecture pattern.
|
|
17
|
+
|
|
18
|
+
## Tech Stack
|
|
19
|
+
- Language: TypeScript
|
|
20
|
+
- Framework: NestJS
|
|
21
|
+
- Database: Postgres
|
|
22
|
+
- Key Dependencies: nestjs, typeorm, pg, class-validator, rxjs
|
|
23
|
+
|
|
24
|
+
## Architecture
|
|
25
|
+
The project uses a 4-layer architecture: Handlers (controllers), Services,
|
|
26
|
+
Repositories, and Models. Each layer has a single responsibility. Handlers
|
|
27
|
+
never call Repositories directly — always through Services.
|
|
28
|
+
|
|
29
|
+
## Directory Structure
|
|
30
|
+
\`\`\`
|
|
31
|
+
src/
|
|
32
|
+
├── handlers/
|
|
33
|
+
├── services/
|
|
34
|
+
├── repositories/
|
|
35
|
+
└── models/
|
|
36
|
+
\`\`\`
|
|
37
|
+
|
|
38
|
+
## Key Endpoints
|
|
39
|
+
- GET /health — Health check
|
|
40
|
+
- GET /api/v1/users — List users
|
|
41
|
+
- POST /api/v1/users — Create user
|
|
42
|
+
|
|
43
|
+
## Important Files
|
|
44
|
+
- config.json — Application configuration
|
|
45
|
+
- docker-compose.yml — Local dev environment
|
|
46
|
+
|
|
47
|
+
## Getting Started
|
|
48
|
+
\`\`\`bash
|
|
49
|
+
make dev
|
|
50
|
+
\`\`\`
|
|
51
|
+
|
|
52
|
+
## Rate Limits
|
|
53
|
+
- Public endpoints: 100 req/min per IP
|
|
54
|
+
- Auth endpoints: 10 req/min per IP
|
|
55
|
+
|
|
56
|
+
## Security Notes
|
|
57
|
+
- All input validated in handlers
|
|
58
|
+
- SQL via parameterized queries
|
|
59
|
+
- See .env.example
|
|
60
|
+
|
|
61
|
+
## For AI Agents
|
|
62
|
+
- OpenAPI: GET /openapi.json
|
|
63
|
+
- CLI: ./project-cli --help, ./project-cli --json
|
|
64
|
+
- Architecture: Handler → Service → Repository → Model
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
describe('DareAxValidator', () => {
|
|
68
|
+
let tmpDir: string;
|
|
69
|
+
let validator: DareAxValidator;
|
|
70
|
+
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'dare-ax-validator-test-'));
|
|
73
|
+
validator = new DareAxValidator();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
afterEach(() => {
|
|
77
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// ── File-based validate() ───────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
describe('validate(path)', () => {
|
|
83
|
+
it('returns valid=false when file does not exist', () => {
|
|
84
|
+
const result = validator.validate(path.join(tmpDir, 'non-existent.txt'));
|
|
85
|
+
|
|
86
|
+
expect(result.valid).toBe(false);
|
|
87
|
+
expect(result.errors.some((e) => e.code === 'E_FILE_NOT_FOUND')).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('returns valid=false for empty file', () => {
|
|
91
|
+
const filePath = path.join(tmpDir, 'llms.txt');
|
|
92
|
+
fs.writeFileSync(filePath, '', 'utf-8');
|
|
93
|
+
|
|
94
|
+
const result = validator.validate(filePath);
|
|
95
|
+
|
|
96
|
+
expect(result.valid).toBe(false);
|
|
97
|
+
expect(result.errors.some((e) => e.code === 'E_EMPTY_FILE')).toBe(true);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('returns valid=true for a complete llms.txt', () => {
|
|
101
|
+
const filePath = path.join(tmpDir, 'llms.txt');
|
|
102
|
+
fs.writeFileSync(filePath, VALID_LLMS_TXT, 'utf-8');
|
|
103
|
+
|
|
104
|
+
const result = validator.validate(filePath);
|
|
105
|
+
|
|
106
|
+
expect(result.valid).toBe(true);
|
|
107
|
+
expect(result.errors).toHaveLength(0);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('returns error for missing "Project Overview" section', () => {
|
|
111
|
+
const content = VALID_LLMS_TXT.replace('## Project Overview', '## Old Overview');
|
|
112
|
+
const filePath = path.join(tmpDir, 'llms.txt');
|
|
113
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
114
|
+
|
|
115
|
+
const result = validator.validate(filePath);
|
|
116
|
+
|
|
117
|
+
expect(result.valid).toBe(false);
|
|
118
|
+
expect(result.errors.some((e) => e.message.includes('Project Overview'))).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('returns error for missing "Tech Stack" section', () => {
|
|
122
|
+
const content = VALID_LLMS_TXT.replace('## Tech Stack', '## Stack');
|
|
123
|
+
const filePath = path.join(tmpDir, 'llms.txt');
|
|
124
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
125
|
+
|
|
126
|
+
const result = validator.validate(filePath);
|
|
127
|
+
|
|
128
|
+
expect(result.valid).toBe(false);
|
|
129
|
+
expect(result.errors.some((e) => e.message.includes('Tech Stack'))).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('returns error for missing "Architecture" section', () => {
|
|
133
|
+
const content = VALID_LLMS_TXT.replace('## Architecture', '## Arch');
|
|
134
|
+
const filePath = path.join(tmpDir, 'llms.txt');
|
|
135
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
136
|
+
|
|
137
|
+
const result = validator.validate(filePath);
|
|
138
|
+
|
|
139
|
+
expect(result.valid).toBe(false);
|
|
140
|
+
expect(result.errors.some((e) => e.message.includes('Architecture'))).toBe(true);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('returns error for missing "Key Endpoints" section', () => {
|
|
144
|
+
const content = VALID_LLMS_TXT.replace('## Key Endpoints', '## Endpoints');
|
|
145
|
+
const filePath = path.join(tmpDir, 'llms.txt');
|
|
146
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
147
|
+
|
|
148
|
+
const result = validator.validate(filePath);
|
|
149
|
+
|
|
150
|
+
expect(result.valid).toBe(false);
|
|
151
|
+
expect(result.errors.some((e) => e.message.includes('Key Endpoints'))).toBe(true);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('returns error for missing "For AI Agents" section', () => {
|
|
155
|
+
const content = VALID_LLMS_TXT.replace('## For AI Agents', '## For Agents');
|
|
156
|
+
const filePath = path.join(tmpDir, 'llms.txt');
|
|
157
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
158
|
+
|
|
159
|
+
const result = validator.validate(filePath);
|
|
160
|
+
|
|
161
|
+
expect(result.valid).toBe(false);
|
|
162
|
+
expect(result.errors.some((e) => e.message.includes('For AI Agents'))).toBe(true);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('returns error for all 5 required sections missing', () => {
|
|
166
|
+
const filePath = path.join(tmpDir, 'llms.txt');
|
|
167
|
+
fs.writeFileSync(filePath, '# Just a comment\n\nNo sections here at all.\n', 'utf-8');
|
|
168
|
+
|
|
169
|
+
const result = validator.validate(filePath);
|
|
170
|
+
|
|
171
|
+
expect(result.valid).toBe(false);
|
|
172
|
+
// Should have errors for all 5 required sections
|
|
173
|
+
const missingErrors = result.errors.filter((e) => e.code === 'E_MISSING_SECTION');
|
|
174
|
+
expect(missingErrors).toHaveLength(5);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('returns error when AWS key detected', () => {
|
|
178
|
+
const content = VALID_LLMS_TXT + '\n' + 'AKIA' + 'IOSFODNN7EXAMPLE' + '\n';
|
|
179
|
+
const filePath = path.join(tmpDir, 'llms.txt');
|
|
180
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
181
|
+
|
|
182
|
+
const result = validator.validate(filePath);
|
|
183
|
+
|
|
184
|
+
expect(result.valid).toBe(false);
|
|
185
|
+
expect(result.errors.some((e) => e.code === 'E_SECRET_DETECTED')).toBe(true);
|
|
186
|
+
expect(result.errors.some((e) => e.message.includes('AWS'))).toBe(true);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('returns error when Stripe secret key detected', () => {
|
|
190
|
+
const content =
|
|
191
|
+
VALID_LLMS_TXT + '\nstripe_key=' + 'sk_li' + 've_abcdefghijklmnopqrstuvwx\n';
|
|
192
|
+
const filePath = path.join(tmpDir, 'llms.txt');
|
|
193
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
194
|
+
|
|
195
|
+
const result = validator.validate(filePath);
|
|
196
|
+
|
|
197
|
+
expect(result.valid).toBe(false);
|
|
198
|
+
expect(result.errors.some((e) => e.code === 'E_SECRET_DETECTED')).toBe(true);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('returns error when password assignment detected', () => {
|
|
202
|
+
const content = VALID_LLMS_TXT + '\ndatabase_password="s3cr3tP4ss"\n';
|
|
203
|
+
const filePath = path.join(tmpDir, 'llms.txt');
|
|
204
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
205
|
+
|
|
206
|
+
const result = validator.validate(filePath);
|
|
207
|
+
|
|
208
|
+
expect(result.valid).toBe(false);
|
|
209
|
+
expect(result.errors.some((e) => e.code === 'E_SECRET_DETECTED')).toBe(true);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('returns warning for missing recommended sections', () => {
|
|
213
|
+
// Remove recommended sections but keep required
|
|
214
|
+
const contentWithoutRecommended = `# llms.txt
|
|
215
|
+
|
|
216
|
+
## Project Overview
|
|
217
|
+
This is a test project with only required sections.
|
|
218
|
+
|
|
219
|
+
## Tech Stack
|
|
220
|
+
- Language: TypeScript
|
|
221
|
+
- Framework: NestJS
|
|
222
|
+
- Database: Postgres
|
|
223
|
+
|
|
224
|
+
## Architecture
|
|
225
|
+
3-layer architecture with handlers, services, and repositories.
|
|
226
|
+
|
|
227
|
+
## Key Endpoints
|
|
228
|
+
- GET /health — Health check
|
|
229
|
+
- GET /api/v1/users — List users
|
|
230
|
+
|
|
231
|
+
## For AI Agents
|
|
232
|
+
- OpenAPI: GET /openapi.json
|
|
233
|
+
- CLI: ./app --json
|
|
234
|
+
`;
|
|
235
|
+
const filePath = path.join(tmpDir, 'llms.txt');
|
|
236
|
+
fs.writeFileSync(filePath, contentWithoutRecommended, 'utf-8');
|
|
237
|
+
|
|
238
|
+
const result = validator.validate(filePath);
|
|
239
|
+
|
|
240
|
+
expect(result.valid).toBe(true); // warnings don't make it invalid
|
|
241
|
+
expect(result.warnings.length).toBeGreaterThan(0);
|
|
242
|
+
expect(
|
|
243
|
+
result.warnings.some((w) => w.code === 'W_MISSING_RECOMMENDED_SECTION')
|
|
244
|
+
).toBe(true);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('warns when "For AI Agents" section does not mention OpenAPI', () => {
|
|
248
|
+
const content = VALID_LLMS_TXT.replace(
|
|
249
|
+
'- OpenAPI: GET /openapi.json\n- CLI: ./project-cli --help, ./project-cli --json',
|
|
250
|
+
'- CLI: ./project-cli --json'
|
|
251
|
+
);
|
|
252
|
+
const filePath = path.join(tmpDir, 'llms.txt');
|
|
253
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
254
|
+
|
|
255
|
+
const result = validator.validate(filePath);
|
|
256
|
+
|
|
257
|
+
expect(
|
|
258
|
+
result.warnings.some((w) => w.code === 'W_MISSING_OPENAPI_REFERENCE')
|
|
259
|
+
).toBe(true);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// ── Content-based validateContent() ────────────────────────────────────
|
|
264
|
+
|
|
265
|
+
describe('validateContent(content)', () => {
|
|
266
|
+
it('validates string content without writing to disk', () => {
|
|
267
|
+
const result = validator.validateContent(VALID_LLMS_TXT);
|
|
268
|
+
|
|
269
|
+
expect(result.valid).toBe(true);
|
|
270
|
+
expect(result.errors).toHaveLength(0);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it('returns error for empty string', () => {
|
|
274
|
+
const result = validator.validateContent('');
|
|
275
|
+
|
|
276
|
+
expect(result.valid).toBe(false);
|
|
277
|
+
expect(result.errors.some((e) => e.code === 'E_EMPTY_FILE')).toBe(true);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('returns error for OpenAI API key', () => {
|
|
281
|
+
const content =
|
|
282
|
+
VALID_LLMS_TXT + '\nopenai_key=sk-' + 'a'.repeat(48) + '\n';
|
|
283
|
+
const result = validator.validateContent(content);
|
|
284
|
+
|
|
285
|
+
expect(result.valid).toBe(false);
|
|
286
|
+
expect(result.errors.some((e) => e.code === 'E_SECRET_DETECTED')).toBe(true);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('ignores Jinja2 template placeholders as secrets', () => {
|
|
290
|
+
// Template variables like {{ api_key }} should not be flagged as secrets
|
|
291
|
+
const content = VALID_LLMS_TXT + '\n- API key: {{ api_key }}\n';
|
|
292
|
+
const result = validator.validateContent(content);
|
|
293
|
+
|
|
294
|
+
// Template placeholders are not actual secrets
|
|
295
|
+
expect(result.errors.filter((e) => e.code === 'E_SECRET_DETECTED')).toHaveLength(0);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "./dist",
|
|
5
|
+
"rootDir": ".",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"sourceMap": true
|
|
9
|
+
},
|
|
10
|
+
"include": [
|
|
11
|
+
"*.ts",
|
|
12
|
+
"tests/**/*.ts"
|
|
13
|
+
],
|
|
14
|
+
"exclude": [
|
|
15
|
+
"node_modules",
|
|
16
|
+
"dist"
|
|
17
|
+
]
|
|
18
|
+
}
|