@howlil/ez-agents 3.5.0 → 4.0.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 +735 -537
- package/agents/ez-architect-agent.md +267 -0
- package/agents/ez-backend-agent.md +303 -0
- package/agents/ez-chief-strategist.md +271 -0
- package/agents/ez-codebase-mapper.md +770 -770
- package/agents/ez-context-manager.md +319 -0
- package/agents/ez-debugger.md +1255 -1255
- package/agents/ez-design-expert.md +347 -0
- package/agents/ez-devops-agent.md +331 -0
- package/agents/ez-executor.md +487 -487
- package/agents/ez-frontend-agent.md +322 -0
- package/agents/ez-phase-researcher.md +553 -553
- package/agents/ez-planner.md +1307 -1307
- package/agents/ez-product-engineer.md +435 -0
- package/agents/ez-project-researcher.md +629 -629
- package/agents/ez-qa-agent.md +320 -0
- package/agents/ez-release-agent.md +333 -333
- package/agents/ez-requirements-agent.md +377 -377
- package/agents/ez-roadmapper.md +650 -650
- package/agents/ez-technical-writer.md +551 -0
- package/agents/ez-ux-expert.md +393 -0
- package/agents/ez-verifier.md +579 -579
- package/bin/guards/autonomy-guard.cjs +346 -0
- package/bin/guards/context-budget-guard.cjs +278 -0
- package/bin/guards/hallucination-guard.cjs +380 -0
- package/bin/guards/hidden-state-guard.cjs +182 -0
- package/bin/guards/team-overhead-guard.cjs +266 -0
- package/bin/guards/tool-sprawl-guard.cjs +271 -0
- package/bin/lib/analytics/analytics-collector.cjs +86 -0
- package/bin/lib/analytics/analytics-reporter.cjs +130 -0
- package/bin/lib/analytics/cohort-analyzer.cjs +138 -0
- package/bin/lib/analytics/funnel-analyzer.cjs +147 -0
- package/bin/lib/analytics/nps-tracker.cjs +147 -0
- package/bin/lib/archetype-detector.cjs +289 -0
- package/bin/lib/assistant-adapter.cjs +361 -0
- package/bin/lib/audit-exec.cjs +175 -0
- package/bin/lib/auth.cjs +176 -0
- package/bin/lib/backup-service.cjs +422 -0
- package/bin/lib/bdd-validator.cjs +622 -0
- package/bin/lib/business-flow-mapper.cjs +429 -0
- package/bin/lib/circuit-breaker.cjs +276 -0
- package/bin/lib/code-complexity-analyzer.cjs +360 -0
- package/bin/lib/codebase-analyzer.cjs +241 -0
- package/bin/lib/commands.cjs +691 -0
- package/bin/lib/config.cjs +236 -0
- package/bin/lib/constraint-extractor.cjs +526 -0
- package/bin/lib/content-scanner.cjs +238 -0
- package/bin/lib/context-cache.cjs +154 -0
- package/bin/lib/context-compressor.cjs +102 -0
- package/bin/lib/context-deduplicator.cjs +105 -0
- package/bin/lib/context-errors.cjs +78 -0
- package/bin/lib/context-manager.cjs +338 -0
- package/bin/lib/context-metadata-tracker.cjs +140 -0
- package/bin/lib/context-relevance-scorer.cjs +99 -0
- package/bin/lib/core.cjs +507 -0
- package/bin/lib/cost-alerts.cjs +174 -0
- package/bin/lib/cost-tracker.cjs +275 -0
- package/bin/lib/crash-recovery.cjs +220 -0
- package/bin/lib/dependency-graph.cjs +319 -0
- package/bin/lib/deploy/deploy-audit-log.cjs +76 -0
- package/bin/lib/deploy/deploy-detector.cjs +69 -0
- package/bin/lib/deploy/deploy-env-manager.cjs +109 -0
- package/bin/lib/deploy/deploy-health-check.cjs +88 -0
- package/bin/lib/deploy/deploy-pre-flight.cjs +57 -0
- package/bin/lib/deploy/deploy-rollback.cjs +72 -0
- package/bin/lib/deploy/deploy-runner.cjs +97 -0
- package/bin/lib/deploy/deploy-status.cjs +74 -0
- package/bin/lib/discussion-synthesizer.cjs +439 -0
- package/bin/lib/error-cache.cjs +114 -0
- package/bin/lib/error-registry.cjs +177 -0
- package/bin/lib/file-access.cjs +207 -0
- package/bin/lib/file-lock.cjs +236 -0
- package/bin/lib/finops/budget-enforcer.cjs +126 -0
- package/bin/lib/finops/cost-reporter.cjs +132 -0
- package/bin/lib/finops/finops-analyzer.cjs +112 -0
- package/bin/lib/finops/spot-manager.cjs +118 -0
- package/bin/lib/framework-detector.cjs +396 -0
- package/bin/lib/frontmatter.cjs +313 -0
- package/bin/lib/fs-utils.cjs +153 -0
- package/bin/lib/gate-executor.cjs +272 -0
- package/bin/lib/gates/README.md +374 -0
- package/bin/lib/gates/gate-01-requirement.cjs +303 -0
- package/bin/lib/gates/gate-02-architecture.cjs +555 -0
- package/bin/lib/gates/gate-03-code.cjs +635 -0
- package/bin/lib/gates/gate-04-security.cjs +829 -0
- package/bin/lib/git-errors.cjs +83 -0
- package/bin/lib/git-utils.cjs +321 -0
- package/bin/lib/git-workflow-engine.cjs +1157 -0
- package/bin/lib/health-check.cjs +227 -0
- package/bin/lib/index.cjs +279 -0
- package/bin/lib/init.cjs +725 -0
- package/bin/lib/lock-logger.cjs +194 -0
- package/bin/lib/lock-state.cjs +263 -0
- package/bin/lib/lockfile-validator.cjs +227 -0
- package/bin/lib/log-rotation.cjs +71 -0
- package/bin/lib/logger.cjs +125 -0
- package/bin/lib/memory-compression.cjs +256 -0
- package/bin/lib/milestone.cjs +247 -0
- package/bin/lib/model-provider.cjs +241 -0
- package/bin/lib/package-manager-detector.cjs +203 -0
- package/bin/lib/package-manager-executor.cjs +385 -0
- package/bin/lib/package-manager-service.cjs +216 -0
- package/bin/lib/perf/api-monitor.cjs +88 -0
- package/bin/lib/perf/db-optimizer.cjs +78 -0
- package/bin/lib/perf/frontend-performance.cjs +56 -0
- package/bin/lib/perf/perf-analyzer.cjs +77 -0
- package/bin/lib/perf/perf-baseline.cjs +102 -0
- package/bin/lib/perf/perf-reporter.cjs +117 -0
- package/bin/lib/perf/regression-detector.cjs +92 -0
- package/bin/lib/phase.cjs +963 -0
- package/bin/lib/planning-write.cjs +123 -0
- package/bin/lib/project-reporter.cjs +565 -0
- package/bin/lib/quality-gate.cjs +332 -0
- package/bin/lib/quality-metrics.cjs +324 -0
- package/bin/lib/recovery-manager.cjs +98 -0
- package/bin/lib/release-validator.cjs +617 -0
- package/bin/lib/retry.cjs +119 -0
- package/bin/lib/roadmap.cjs +309 -0
- package/bin/lib/safe-exec.cjs +173 -0
- package/bin/lib/safe-path.cjs +130 -0
- package/bin/lib/security-errors.cjs +62 -0
- package/bin/lib/session-chain.cjs +304 -0
- package/bin/lib/session-errors.cjs +81 -0
- package/bin/lib/session-export.cjs +251 -0
- package/bin/lib/session-import.cjs +262 -0
- package/bin/lib/session-manager.cjs +280 -0
- package/bin/lib/skill-context.cjs +148 -0
- package/bin/lib/skill-matcher.cjs +236 -0
- package/bin/lib/skill-registry.cjs +360 -0
- package/bin/lib/skill-resolver.cjs +449 -0
- package/bin/lib/skill-triggers.cjs +90 -0
- package/bin/lib/skill-validator.cjs +270 -0
- package/bin/lib/skill-versioning.cjs +355 -0
- package/bin/lib/stack-detector.cjs +399 -0
- package/bin/lib/state.cjs +736 -0
- package/bin/lib/tech-debt-analyzer.cjs +309 -0
- package/bin/lib/temp-file.cjs +239 -0
- package/bin/lib/template.cjs +223 -0
- package/bin/lib/test-file-lock.cjs +112 -0
- package/bin/lib/test-graceful.cjs +93 -0
- package/bin/lib/test-logger.cjs +60 -0
- package/bin/lib/test-safe-exec.cjs +38 -0
- package/bin/lib/test-safe-path.cjs +33 -0
- package/bin/lib/test-temp-file.cjs +125 -0
- package/bin/lib/tier-manager.cjs +428 -0
- package/bin/lib/timeout-exec.cjs +63 -0
- package/bin/lib/tradeoff-analyzer.cjs +284 -0
- package/bin/lib/url-fetch.cjs +170 -0
- package/bin/lib/verify.cjs +863 -0
- package/bin/update.js +217 -214
- package/commands/deploy.cjs +53 -0
- package/commands/ez/add-tests.md +41 -41
- package/commands/ez/audit-milestone.md +36 -36
- package/commands/ez/complete-milestone.md +136 -136
- package/commands/ez/discuss-phase.md +90 -90
- package/commands/ez/execute-phase.md +52 -52
- package/commands/ez/help.md +22 -22
- package/commands/ez/map-codebase.md +71 -71
- package/commands/ez/new-milestone.md +44 -44
- package/commands/ez/new-project.md +51 -42
- package/commands/ez/plan-phase.md +53 -53
- package/commands/ez/progress.md +36 -36
- package/commands/ez/quick.md +45 -45
- package/commands/ez/resume-work.md +40 -40
- package/commands/ez/run-phase.md +580 -0
- package/commands/ez/settings.md +36 -36
- package/commands/ez/update.md +37 -37
- package/commands/ez/verify-work.md +402 -38
- package/commands/health-check.cjs +44 -0
- package/commands/rollback.cjs +47 -0
- package/ez-agents/bin/ez-tools.cjs +599 -2
- package/ez-agents/bin/guards/autonomy-guard.cjs +346 -0
- package/ez-agents/bin/guards/context-budget-guard.cjs +247 -0
- package/ez-agents/bin/guards/hallucination-guard.cjs +271 -0
- package/ez-agents/bin/guards/hidden-state-guard.cjs +182 -0
- package/ez-agents/bin/guards/team-overhead-guard.cjs +266 -0
- package/ez-agents/bin/guards/tool-sprawl-guard.cjs +271 -0
- package/ez-agents/bin/lib/analytics/analytics-collector.cjs +86 -0
- package/ez-agents/bin/lib/analytics/analytics-reporter.cjs +130 -0
- package/ez-agents/bin/lib/analytics/cohort-analyzer.cjs +138 -0
- package/ez-agents/bin/lib/analytics/funnel-analyzer.cjs +147 -0
- package/ez-agents/bin/lib/analytics/nps-tracker.cjs +147 -0
- package/ez-agents/bin/lib/archetype-detector.cjs +289 -0
- package/ez-agents/bin/lib/audit-exec.cjs +166 -167
- package/ez-agents/bin/lib/auth.cjs +176 -176
- package/ez-agents/bin/lib/backup-service.cjs +422 -0
- package/ez-agents/bin/lib/bdd-validator.cjs +622 -622
- package/ez-agents/bin/lib/business-flow-mapper.cjs +429 -0
- package/ez-agents/bin/lib/code-complexity-analyzer.cjs +360 -0
- package/ez-agents/bin/lib/codebase-analyzer.cjs +241 -0
- package/ez-agents/bin/lib/commands.cjs +685 -685
- package/ez-agents/bin/lib/config.cjs +41 -1
- package/ez-agents/bin/lib/constraint-extractor.cjs +526 -0
- package/ez-agents/bin/lib/content-scanner.cjs +238 -238
- package/ez-agents/bin/lib/context-cache.cjs +154 -154
- package/ez-agents/bin/lib/context-errors.cjs +71 -71
- package/ez-agents/bin/lib/context-manager.cjs +220 -220
- package/ez-agents/bin/lib/core.cjs +507 -512
- package/ez-agents/bin/lib/cost-tracker.cjs +243 -0
- package/ez-agents/bin/lib/crash-recovery.cjs +172 -0
- package/ez-agents/bin/lib/dependency-graph.cjs +319 -0
- package/ez-agents/bin/lib/deploy/deploy-audit-log.cjs +76 -0
- package/ez-agents/bin/lib/deploy/deploy-detector.cjs +69 -0
- package/ez-agents/bin/lib/deploy/deploy-env-manager.cjs +109 -0
- package/ez-agents/bin/lib/deploy/deploy-health-check.cjs +88 -0
- package/ez-agents/bin/lib/deploy/deploy-pre-flight.cjs +57 -0
- package/ez-agents/bin/lib/deploy/deploy-rollback.cjs +72 -0
- package/ez-agents/bin/lib/deploy/deploy-runner.cjs +97 -0
- package/ez-agents/bin/lib/deploy/deploy-status.cjs +74 -0
- package/ez-agents/bin/lib/file-access.cjs +207 -207
- package/ez-agents/bin/lib/finops/budget-enforcer.cjs +126 -0
- package/ez-agents/bin/lib/finops/cost-reporter.cjs +132 -0
- package/ez-agents/bin/lib/finops/finops-analyzer.cjs +112 -0
- package/ez-agents/bin/lib/finops/spot-manager.cjs +118 -0
- package/ez-agents/bin/lib/framework-detector.cjs +396 -0
- package/ez-agents/bin/lib/frontmatter.cjs +3 -1
- package/ez-agents/bin/lib/gates/README.md +374 -0
- package/ez-agents/bin/lib/gates/gate-01-requirement.cjs +303 -0
- package/ez-agents/bin/lib/gates/gate-02-architecture.cjs +555 -0
- package/ez-agents/bin/lib/gates/gate-03-code.cjs +635 -0
- package/ez-agents/bin/lib/gates/gate-04-security.cjs +829 -0
- package/ez-agents/bin/lib/git-errors.cjs +83 -83
- package/ez-agents/bin/lib/git-utils.cjs +321 -321
- package/ez-agents/bin/lib/git-workflow-engine.cjs +1157 -1157
- package/ez-agents/bin/lib/health-check.cjs +162 -162
- package/ez-agents/bin/lib/index.cjs +2 -8
- package/ez-agents/bin/lib/init.cjs +0 -2
- package/ez-agents/bin/lib/lockfile-validator.cjs +227 -227
- package/ez-agents/bin/lib/log-rotation.cjs +71 -0
- package/ez-agents/bin/lib/logger.cjs +22 -47
- package/ez-agents/bin/lib/memory-compression.cjs +256 -256
- package/ez-agents/bin/lib/package-manager-detector.cjs +203 -203
- package/ez-agents/bin/lib/package-manager-executor.cjs +385 -385
- package/ez-agents/bin/lib/package-manager-service.cjs +216 -216
- package/ez-agents/bin/lib/perf/api-monitor.cjs +88 -0
- package/ez-agents/bin/lib/perf/db-optimizer.cjs +78 -0
- package/ez-agents/bin/lib/perf/frontend-performance.cjs +56 -0
- package/ez-agents/bin/lib/perf/perf-analyzer.cjs +77 -0
- package/ez-agents/bin/lib/perf/perf-baseline.cjs +102 -0
- package/ez-agents/bin/lib/perf/perf-reporter.cjs +117 -0
- package/ez-agents/bin/lib/perf/regression-detector.cjs +92 -0
- package/ez-agents/bin/lib/project-reporter.cjs +502 -0
- package/ez-agents/bin/lib/quality-gate.cjs +332 -0
- package/ez-agents/bin/lib/recovery-manager.cjs +98 -0
- package/ez-agents/bin/lib/release-validator.cjs +617 -614
- package/ez-agents/bin/lib/security-errors.cjs +62 -0
- package/ez-agents/bin/lib/session-chain.cjs +304 -304
- package/ez-agents/bin/lib/session-errors.cjs +81 -81
- package/ez-agents/bin/lib/session-export.cjs +251 -251
- package/ez-agents/bin/lib/session-import.cjs +262 -262
- package/ez-agents/bin/lib/session-manager.cjs +280 -280
- package/ez-agents/bin/lib/skill-context.cjs +148 -0
- package/ez-agents/bin/lib/skill-matcher.cjs +236 -0
- package/ez-agents/bin/lib/skill-registry.cjs +341 -0
- package/ez-agents/bin/lib/skill-resolver.cjs +449 -0
- package/ez-agents/bin/lib/skill-triggers.cjs +90 -0
- package/ez-agents/bin/lib/skill-validator.cjs +270 -0
- package/ez-agents/bin/lib/skill-versioning.cjs +355 -0
- package/ez-agents/bin/lib/stack-detector.cjs +399 -0
- package/ez-agents/bin/lib/tech-debt-analyzer.cjs +309 -0
- package/ez-agents/bin/lib/tier-manager.cjs +428 -428
- package/ez-agents/bin/lib/tradeoff-analyzer.cjs +284 -0
- package/ez-agents/bin/lib/url-fetch.cjs +170 -170
- package/ez-agents/bin/lib/verify.cjs +863 -863
- package/ez-agents/references/decimal-phase-calculation.md +65 -65
- package/ez-agents/references/git-integration.md +248 -248
- package/ez-agents/references/git-planning-commit.md +38 -38
- package/ez-agents/references/metrics-schema.md +118 -118
- package/ez-agents/references/model-profile-resolution.md +34 -34
- package/ez-agents/references/model-profiles.md +93 -93
- package/ez-agents/references/phase-argument-parsing.md +61 -61
- package/ez-agents/references/planning-config.md +340 -340
- package/ez-agents/references/tier-strategy.md +103 -103
- package/ez-agents/references/ui-brand.md +160 -160
- package/ez-agents/references/verification-patterns.md +612 -612
- package/ez-agents/templates/DEBUG.md +164 -164
- package/ez-agents/templates/UAT.md +247 -247
- package/ez-agents/templates/agent-output-format.md +404 -0
- package/ez-agents/templates/bdd-feature.md +173 -173
- package/ez-agents/templates/codebase/architecture.md +255 -255
- package/ez-agents/templates/codebase/structure.md +285 -285
- package/ez-agents/templates/copilot-instructions.md +7 -7
- package/ez-agents/templates/debug-subagent-prompt.md +91 -91
- package/ez-agents/templates/discovery.md +146 -146
- package/ez-agents/templates/discussion.md +68 -68
- package/ez-agents/templates/handoff-protocol.md +294 -0
- package/ez-agents/templates/incident-runbook.md +205 -205
- package/ez-agents/templates/mode-workflow-templates.md +301 -0
- package/ez-agents/templates/phase-prompt.md +610 -610
- package/ez-agents/templates/planner-subagent-prompt.md +117 -117
- package/ez-agents/templates/project.md +184 -184
- package/ez-agents/templates/release-checklist.md +136 -133
- package/ez-agents/templates/research.md +552 -552
- package/ez-agents/templates/rollback-plan.md +201 -201
- package/ez-agents/templates/security-user-setup.md +244 -0
- package/ez-agents/templates/skill-validation-rules.md +476 -0
- package/ez-agents/templates/state.md +180 -176
- package/ez-agents/templates/summary-complex.md +59 -59
- package/ez-agents/tests/gates/gate-01-02.test.cjs +812 -0
- package/ez-agents/tests/gates/gate-03-04.test.cjs +762 -0
- package/ez-agents/tests/gates/gate-05-validator.test.cjs +145 -0
- package/ez-agents/tests/gates/gate-06-docs-validator.test.cjs +244 -0
- package/ez-agents/tests/gates/gate-07-release-validator.test.cjs +219 -0
- package/ez-agents/tests/guards/context-budget-guard.test.cjs +145 -0
- package/ez-agents/tests/guards/edge-case-guards.test.cjs +238 -0
- package/ez-agents/tests/guards/hallucination-guard.test.cjs +124 -0
- package/ez-agents/workflows/audit-milestone.md +1 -1
- package/ez-agents/workflows/autonomous.md +844 -844
- package/ez-agents/workflows/complete-milestone.md +1 -1
- package/ez-agents/workflows/discuss-phase.md +1 -1
- package/ez-agents/workflows/execute-phase.md +124 -3
- package/ez-agents/workflows/help.md +42 -181
- package/ez-agents/workflows/hotfix.md +291 -291
- package/ez-agents/workflows/new-milestone.md +713 -713
- package/ez-agents/workflows/new-project.md +1089 -1107
- package/ez-agents/workflows/plan-phase.md +0 -40
- package/ez-agents/workflows/release.md +253 -253
- package/ez-agents/workflows/resume-session.md +215 -215
- package/ez-agents/workflows/run-phase.md +531 -0
- package/ez-agents/workflows/settings.md +2 -35
- package/hooks/dist/ez-check-update.js +81 -81
- package/hooks/dist/ez-context-monitor.js +148 -141
- package/hooks/dist/ez-statusline.js +115 -115
- package/package.json +78 -71
- package/scripts/fix-qwen-installation.js +144 -144
- package/agents/ez-integration-checker.md +0 -443
- package/agents/ez-nyquist-auditor.md +0 -176
- package/agents/ez-observer-agent.md +0 -260
- package/agents/ez-plan-checker.md +0 -706
- package/agents/ez-research-synthesizer.md +0 -247
- package/agents/ez-scrum-master-agent.md +0 -242
- package/agents/ez-tech-lead-agent.md +0 -267
- package/agents/ez-ui-auditor.md +0 -439
- package/agents/ez-ui-checker.md +0 -300
- package/agents/ez-ui-researcher.md +0 -353
- package/commands/ez/add-phase.md +0 -43
- package/commands/ez/add-todo.md +0 -47
- package/commands/ez/arch-review.md +0 -102
- package/commands/ez/auth.md +0 -87
- package/commands/ez/autonomous.md +0 -41
- package/commands/ez/check-todos.md +0 -45
- package/commands/ez/cleanup.md +0 -18
- package/commands/ez/debug.md +0 -168
- package/commands/ez/export-session.md +0 -79
- package/commands/ez/gather-requirements.md +0 -117
- package/commands/ez/git-workflow.md +0 -72
- package/commands/ez/health.md +0 -22
- package/commands/ez/hotfix.md +0 -120
- package/commands/ez/import-session.md +0 -82
- package/commands/ez/insert-phase.md +0 -32
- package/commands/ez/join-discord.md +0 -18
- package/commands/ez/list-phase-assumptions.md +0 -46
- package/commands/ez/list-sessions.md +0 -96
- package/commands/ez/package-manager.md +0 -316
- package/commands/ez/pause-work.md +0 -38
- package/commands/ez/plan-milestone-gaps.md +0 -34
- package/commands/ez/preflight.md +0 -79
- package/commands/ez/reapply-patches.md +0 -124
- package/commands/ez/release.md +0 -153
- package/commands/ez/remove-phase.md +0 -31
- package/commands/ez/research-phase.md +0 -190
- package/commands/ez/resume.md +0 -107
- package/commands/ez/set-profile.md +0 -34
- package/commands/ez/standup.md +0 -85
- package/commands/ez/stats.md +0 -18
- package/commands/ez/ui-phase.md +0 -34
- package/commands/ez/ui-review.md +0 -32
- package/commands/ez/validate-phase.md +0 -35
- package/ez-agents/bin/lib/metrics-tracker.cjs +0 -406
- package/ez-agents/templates/UI-SPEC.md +0 -100
- package/ez-agents/templates/VALIDATION.md +0 -76
- package/ez-agents/templates/context.md +0 -352
- package/ez-agents/templates/verification-report.md +0 -322
- package/ez-agents/workflows/arch-review.md +0 -54
- package/ez-agents/workflows/export-session.md +0 -255
- package/ez-agents/workflows/gather-requirements.md +0 -206
- package/ez-agents/workflows/import-session.md +0 -303
- package/ez-agents/workflows/research-phase.md +0 -74
- package/ez-agents/workflows/standup.md +0 -64
- package/ez-agents/workflows/ui-phase.md +0 -290
- package/ez-agents/workflows/ui-review.md +0 -157
- package/ez-agents/workflows/validate-phase.md +0 -167
|
@@ -1,207 +1,207 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* File Access Service
|
|
5
|
-
*
|
|
6
|
-
* Provides file reading capabilities with glob pattern support.
|
|
7
|
-
* Uses micromatch for glob matching with support for negation and brace expansion.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const fs = require('fs');
|
|
11
|
-
const path = require('path');
|
|
12
|
-
const micromatch = require('micromatch');
|
|
13
|
-
const { FileAccessError } = require('./context-errors.cjs');
|
|
14
|
-
|
|
15
|
-
const MAX_FILE_COUNT = 1000;
|
|
16
|
-
|
|
17
|
-
class FileAccessService {
|
|
18
|
-
/**
|
|
19
|
-
* Create a new FileAccessService instance
|
|
20
|
-
* @param {string} cwd - Current working directory (defaults to process.cwd())
|
|
21
|
-
*/
|
|
22
|
-
constructor(cwd) {
|
|
23
|
-
this.cwd = cwd || process.cwd();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Read files matching patterns
|
|
28
|
-
* @param {string|string[]} patterns - File patterns (glob or single path)
|
|
29
|
-
* @returns {Array<{path: string, content: string}>} - Array of file objects
|
|
30
|
-
* @throws {FileAccessError} - On file access errors
|
|
31
|
-
*/
|
|
32
|
-
readFiles(patterns) {
|
|
33
|
-
const patternArray = Array.isArray(patterns) ? patterns : [patterns];
|
|
34
|
-
|
|
35
|
-
// Get all files recursively
|
|
36
|
-
const allFiles = this._getAllFiles(this.cwd);
|
|
37
|
-
|
|
38
|
-
// Convert paths to relative paths from cwd
|
|
39
|
-
const relativeFiles = allFiles.map(f => {
|
|
40
|
-
const relPath = path.relative(this.cwd, f);
|
|
41
|
-
// Convert to POSIX style paths for glob matching
|
|
42
|
-
return relPath.replace(/\\/g, '/');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
// Filter with micromatch
|
|
46
|
-
const matchedFiles = micromatch.match(relativeFiles, patternArray, {
|
|
47
|
-
dot: false, // Don't match hidden files/directories by default
|
|
48
|
-
nocase: false
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
// Check max file count
|
|
52
|
-
if (matchedFiles.length > MAX_FILE_COUNT) {
|
|
53
|
-
throw new FileAccessError(
|
|
54
|
-
matchedFiles[0],
|
|
55
|
-
`Max file count exceeded: ${matchedFiles.length} > ${MAX_FILE_COUNT}`
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Read file contents
|
|
60
|
-
const results = matchedFiles.map(filePath => {
|
|
61
|
-
const fullPath = path.join(this.cwd, filePath);
|
|
62
|
-
|
|
63
|
-
if (!fs.existsSync(fullPath)) {
|
|
64
|
-
throw new FileAccessError(filePath, 'File not found');
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
69
|
-
return {
|
|
70
|
-
path: filePath,
|
|
71
|
-
content
|
|
72
|
-
};
|
|
73
|
-
} catch (err) {
|
|
74
|
-
if (err.code === 'EACCES') {
|
|
75
|
-
throw new FileAccessError(filePath, 'Permission denied');
|
|
76
|
-
}
|
|
77
|
-
throw new FileAccessError(filePath, err.message);
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
return results;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Get all files recursively from a directory
|
|
86
|
-
* @param {string} dir - Directory to scan
|
|
87
|
-
* @returns {string[]} - Array of file paths
|
|
88
|
-
* @private
|
|
89
|
-
*/
|
|
90
|
-
_getAllFiles(dir) {
|
|
91
|
-
const files = [];
|
|
92
|
-
|
|
93
|
-
try {
|
|
94
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
95
|
-
|
|
96
|
-
for (const entry of entries) {
|
|
97
|
-
const fullPath = path.join(dir, entry.name);
|
|
98
|
-
|
|
99
|
-
// Skip hidden directories (starting with .)
|
|
100
|
-
if (entry.isDirectory() && !entry.name.startsWith('.')) {
|
|
101
|
-
const subFiles = this._getAllFiles(fullPath);
|
|
102
|
-
files.push(...subFiles);
|
|
103
|
-
} else if (entry.isFile()) {
|
|
104
|
-
files.push(fullPath);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
} catch (err) {
|
|
108
|
-
// Ignore permission errors during directory traversal
|
|
109
|
-
if (err.code !== 'EACCES') {
|
|
110
|
-
throw err;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return files;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Normalize a file path (convert Windows paths to Unix style)
|
|
119
|
-
* @param {string} filePath - The path to normalize
|
|
120
|
-
* @returns {string} - Normalized path
|
|
121
|
-
* @throws {FileAccessError} - On path traversal attempts
|
|
122
|
-
*/
|
|
123
|
-
normalizePath(filePath) {
|
|
124
|
-
// Convert Windows backslashes to forward slashes
|
|
125
|
-
const normalized = filePath.replace(/\\/g, '/');
|
|
126
|
-
|
|
127
|
-
// Check for path traversal attempts
|
|
128
|
-
if (normalized.includes('..')) {
|
|
129
|
-
throw new FileAccessError(filePath, 'Path traversal not allowed');
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return normalized;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Validate a file path
|
|
137
|
-
* @param {string} filePath - The path to validate
|
|
138
|
-
* @returns {boolean} - True if valid
|
|
139
|
-
*/
|
|
140
|
-
validatePath(filePath) {
|
|
141
|
-
// Reject paths with null bytes
|
|
142
|
-
if (filePath.includes('\x00')) {
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Resolve the path
|
|
147
|
-
const resolvedPath = path.resolve(this.cwd, filePath);
|
|
148
|
-
|
|
149
|
-
// Check if path is within cwd (prevent access outside project)
|
|
150
|
-
const normalizedCwd = path.resolve(this.cwd).replace(/\\/g, '/');
|
|
151
|
-
const normalizedResolved = resolvedPath.replace(/\\/g, '/');
|
|
152
|
-
|
|
153
|
-
// Path must be within or equal to cwd
|
|
154
|
-
if (!normalizedResolved.startsWith(normalizedCwd)) {
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return true;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Check if a file exists
|
|
163
|
-
* @param {string} filePath - Path to check
|
|
164
|
-
* @returns {boolean} - True if file exists
|
|
165
|
-
*/
|
|
166
|
-
fileExists(filePath) {
|
|
167
|
-
const fullPath = path.join(this.cwd, filePath);
|
|
168
|
-
return fs.existsSync(fullPath);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Read a single file
|
|
173
|
-
* @param {string} filePath - Path to the file
|
|
174
|
-
* @returns {{path: string, content: string}} - File object
|
|
175
|
-
* @throws {FileAccessError} - On file access errors
|
|
176
|
-
*/
|
|
177
|
-
readFile(filePath) {
|
|
178
|
-
const results = this.readFiles([filePath]);
|
|
179
|
-
return results[0] || null;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Get file info (size, modified time, etc.)
|
|
184
|
-
* @param {string} filePath - Path to the file
|
|
185
|
-
* @returns {{path: string, size: number, mtime: Date}} - File info
|
|
186
|
-
* @throws {FileAccessError} - On file access errors
|
|
187
|
-
*/
|
|
188
|
-
getFileInfo(filePath) {
|
|
189
|
-
const fullPath = path.join(this.cwd, filePath);
|
|
190
|
-
|
|
191
|
-
if (!fs.existsSync(fullPath)) {
|
|
192
|
-
throw new FileAccessError(filePath, 'File not found');
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const stats = fs.statSync(fullPath);
|
|
196
|
-
|
|
197
|
-
return {
|
|
198
|
-
path: filePath,
|
|
199
|
-
size: stats.size,
|
|
200
|
-
mtime: stats.mtime,
|
|
201
|
-
isDirectory: stats.isDirectory(),
|
|
202
|
-
isFile: stats.isFile()
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
module.exports = FileAccessService;
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* File Access Service
|
|
5
|
+
*
|
|
6
|
+
* Provides file reading capabilities with glob pattern support.
|
|
7
|
+
* Uses micromatch for glob matching with support for negation and brace expansion.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const micromatch = require('micromatch');
|
|
13
|
+
const { FileAccessError } = require('./context-errors.cjs');
|
|
14
|
+
|
|
15
|
+
const MAX_FILE_COUNT = 1000;
|
|
16
|
+
|
|
17
|
+
class FileAccessService {
|
|
18
|
+
/**
|
|
19
|
+
* Create a new FileAccessService instance
|
|
20
|
+
* @param {string} cwd - Current working directory (defaults to process.cwd())
|
|
21
|
+
*/
|
|
22
|
+
constructor(cwd) {
|
|
23
|
+
this.cwd = cwd || process.cwd();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Read files matching patterns
|
|
28
|
+
* @param {string|string[]} patterns - File patterns (glob or single path)
|
|
29
|
+
* @returns {Array<{path: string, content: string}>} - Array of file objects
|
|
30
|
+
* @throws {FileAccessError} - On file access errors
|
|
31
|
+
*/
|
|
32
|
+
readFiles(patterns) {
|
|
33
|
+
const patternArray = Array.isArray(patterns) ? patterns : [patterns];
|
|
34
|
+
|
|
35
|
+
// Get all files recursively
|
|
36
|
+
const allFiles = this._getAllFiles(this.cwd);
|
|
37
|
+
|
|
38
|
+
// Convert paths to relative paths from cwd
|
|
39
|
+
const relativeFiles = allFiles.map(f => {
|
|
40
|
+
const relPath = path.relative(this.cwd, f);
|
|
41
|
+
// Convert to POSIX style paths for glob matching
|
|
42
|
+
return relPath.replace(/\\/g, '/');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Filter with micromatch
|
|
46
|
+
const matchedFiles = micromatch.match(relativeFiles, patternArray, {
|
|
47
|
+
dot: false, // Don't match hidden files/directories by default
|
|
48
|
+
nocase: false
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Check max file count
|
|
52
|
+
if (matchedFiles.length > MAX_FILE_COUNT) {
|
|
53
|
+
throw new FileAccessError(
|
|
54
|
+
matchedFiles[0],
|
|
55
|
+
`Max file count exceeded: ${matchedFiles.length} > ${MAX_FILE_COUNT}`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Read file contents
|
|
60
|
+
const results = matchedFiles.map(filePath => {
|
|
61
|
+
const fullPath = path.join(this.cwd, filePath);
|
|
62
|
+
|
|
63
|
+
if (!fs.existsSync(fullPath)) {
|
|
64
|
+
throw new FileAccessError(filePath, 'File not found');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
69
|
+
return {
|
|
70
|
+
path: filePath,
|
|
71
|
+
content
|
|
72
|
+
};
|
|
73
|
+
} catch (err) {
|
|
74
|
+
if (err.code === 'EACCES') {
|
|
75
|
+
throw new FileAccessError(filePath, 'Permission denied');
|
|
76
|
+
}
|
|
77
|
+
throw new FileAccessError(filePath, err.message);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return results;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get all files recursively from a directory
|
|
86
|
+
* @param {string} dir - Directory to scan
|
|
87
|
+
* @returns {string[]} - Array of file paths
|
|
88
|
+
* @private
|
|
89
|
+
*/
|
|
90
|
+
_getAllFiles(dir) {
|
|
91
|
+
const files = [];
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
95
|
+
|
|
96
|
+
for (const entry of entries) {
|
|
97
|
+
const fullPath = path.join(dir, entry.name);
|
|
98
|
+
|
|
99
|
+
// Skip hidden directories (starting with .)
|
|
100
|
+
if (entry.isDirectory() && !entry.name.startsWith('.')) {
|
|
101
|
+
const subFiles = this._getAllFiles(fullPath);
|
|
102
|
+
files.push(...subFiles);
|
|
103
|
+
} else if (entry.isFile()) {
|
|
104
|
+
files.push(fullPath);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} catch (err) {
|
|
108
|
+
// Ignore permission errors during directory traversal
|
|
109
|
+
if (err.code !== 'EACCES') {
|
|
110
|
+
throw err;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return files;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Normalize a file path (convert Windows paths to Unix style)
|
|
119
|
+
* @param {string} filePath - The path to normalize
|
|
120
|
+
* @returns {string} - Normalized path
|
|
121
|
+
* @throws {FileAccessError} - On path traversal attempts
|
|
122
|
+
*/
|
|
123
|
+
normalizePath(filePath) {
|
|
124
|
+
// Convert Windows backslashes to forward slashes
|
|
125
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
126
|
+
|
|
127
|
+
// Check for path traversal attempts
|
|
128
|
+
if (normalized.includes('..')) {
|
|
129
|
+
throw new FileAccessError(filePath, 'Path traversal not allowed');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return normalized;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Validate a file path
|
|
137
|
+
* @param {string} filePath - The path to validate
|
|
138
|
+
* @returns {boolean} - True if valid
|
|
139
|
+
*/
|
|
140
|
+
validatePath(filePath) {
|
|
141
|
+
// Reject paths with null bytes
|
|
142
|
+
if (filePath.includes('\x00')) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Resolve the path
|
|
147
|
+
const resolvedPath = path.resolve(this.cwd, filePath);
|
|
148
|
+
|
|
149
|
+
// Check if path is within cwd (prevent access outside project)
|
|
150
|
+
const normalizedCwd = path.resolve(this.cwd).replace(/\\/g, '/');
|
|
151
|
+
const normalizedResolved = resolvedPath.replace(/\\/g, '/');
|
|
152
|
+
|
|
153
|
+
// Path must be within or equal to cwd
|
|
154
|
+
if (!normalizedResolved.startsWith(normalizedCwd)) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Check if a file exists
|
|
163
|
+
* @param {string} filePath - Path to check
|
|
164
|
+
* @returns {boolean} - True if file exists
|
|
165
|
+
*/
|
|
166
|
+
fileExists(filePath) {
|
|
167
|
+
const fullPath = path.join(this.cwd, filePath);
|
|
168
|
+
return fs.existsSync(fullPath);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Read a single file
|
|
173
|
+
* @param {string} filePath - Path to the file
|
|
174
|
+
* @returns {{path: string, content: string}} - File object
|
|
175
|
+
* @throws {FileAccessError} - On file access errors
|
|
176
|
+
*/
|
|
177
|
+
readFile(filePath) {
|
|
178
|
+
const results = this.readFiles([filePath]);
|
|
179
|
+
return results[0] || null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get file info (size, modified time, etc.)
|
|
184
|
+
* @param {string} filePath - Path to the file
|
|
185
|
+
* @returns {{path: string, size: number, mtime: Date}} - File info
|
|
186
|
+
* @throws {FileAccessError} - On file access errors
|
|
187
|
+
*/
|
|
188
|
+
getFileInfo(filePath) {
|
|
189
|
+
const fullPath = path.join(this.cwd, filePath);
|
|
190
|
+
|
|
191
|
+
if (!fs.existsSync(fullPath)) {
|
|
192
|
+
throw new FileAccessError(filePath, 'File not found');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const stats = fs.statSync(fullPath);
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
path: filePath,
|
|
199
|
+
size: stats.size,
|
|
200
|
+
mtime: stats.mtime,
|
|
201
|
+
isDirectory: stats.isDirectory(),
|
|
202
|
+
isFile: stats.isFile()
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
module.exports = FileAccessService;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Budget Enforcer — Enforces spending limits and auto-pauses over-budget operations
|
|
3
|
+
* Integrates with cost-tracker for budget ceiling enforcement
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
class BudgetEnforcer {
|
|
10
|
+
constructor(cwd) {
|
|
11
|
+
this.cwd = cwd || process.cwd();
|
|
12
|
+
this.configPath = path.join(this.cwd, '.planning', 'config.json');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Check if budget is exceeded
|
|
17
|
+
* @param {number} currentCost - Current total cost
|
|
18
|
+
* @returns {Object} Budget status { ok, warning, exceeded, action }
|
|
19
|
+
*/
|
|
20
|
+
checkBudget(currentCost) {
|
|
21
|
+
const config = this.loadConfig();
|
|
22
|
+
const ceiling = config.cost_tracking?.budget?.ceiling || null;
|
|
23
|
+
const warningThreshold = config.cost_tracking?.warning_threshold || 80;
|
|
24
|
+
|
|
25
|
+
if (!ceiling) {
|
|
26
|
+
return { ok: true, warning: false, exceeded: false, action: 'none' };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const percentage = (currentCost / ceiling) * 100;
|
|
30
|
+
|
|
31
|
+
if (percentage >= 100) {
|
|
32
|
+
return {
|
|
33
|
+
ok: false,
|
|
34
|
+
warning: false,
|
|
35
|
+
exceeded: true,
|
|
36
|
+
action: config.cost_tracking?.auto_pause ? 'pause' : 'alert',
|
|
37
|
+
percentage: Math.round(percentage)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (percentage >= warningThreshold) {
|
|
42
|
+
return {
|
|
43
|
+
ok: true,
|
|
44
|
+
warning: true,
|
|
45
|
+
exceeded: false,
|
|
46
|
+
action: 'warn',
|
|
47
|
+
percentage: Math.round(percentage)
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return { ok: true, warning: false, exceeded: false, action: 'none', percentage: Math.round(percentage) };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Enforce budget (exit if exceeded and auto_pause enabled)
|
|
56
|
+
* @param {number} currentCost - Current total cost
|
|
57
|
+
*/
|
|
58
|
+
enforce(currentCost) {
|
|
59
|
+
const status = this.checkBudget(currentCost);
|
|
60
|
+
|
|
61
|
+
if (status.exceeded && status.action === 'pause') {
|
|
62
|
+
console.error(`Budget ceiling exceeded (${status.percentage}%) — operations paused`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (status.warning) {
|
|
67
|
+
console.warn(`Budget warning: ${status.percentage}% of ceiling used`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return status;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Set budget ceiling
|
|
75
|
+
* @param {number} ceiling - Budget ceiling in USD
|
|
76
|
+
*/
|
|
77
|
+
setCeiling(ceiling) {
|
|
78
|
+
const config = this.loadConfig();
|
|
79
|
+
if (!config.cost_tracking) config.cost_tracking = {};
|
|
80
|
+
if (!config.cost_tracking.budget) config.cost_tracking.budget = {};
|
|
81
|
+
config.cost_tracking.budget.ceiling = ceiling;
|
|
82
|
+
this.saveConfig(config);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Load config
|
|
87
|
+
* @returns {Object} Config
|
|
88
|
+
*/
|
|
89
|
+
loadConfig() {
|
|
90
|
+
if (!fs.existsSync(this.configPath)) {
|
|
91
|
+
return { cost_tracking: { budget: {}, warning_threshold: 80 } };
|
|
92
|
+
}
|
|
93
|
+
return JSON.parse(fs.readFileSync(this.configPath, 'utf8'));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Save config
|
|
98
|
+
* @param {Object} config - Config to save
|
|
99
|
+
*/
|
|
100
|
+
saveConfig(config) {
|
|
101
|
+
fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2), 'utf8');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Check budget status
|
|
107
|
+
* @param {number} currentCost - Current cost
|
|
108
|
+
* @param {string} cwd - Working directory
|
|
109
|
+
* @returns {Object} Budget status
|
|
110
|
+
*/
|
|
111
|
+
function checkBudget(currentCost, cwd) {
|
|
112
|
+
const enforcer = new BudgetEnforcer(cwd);
|
|
113
|
+
return enforcer.checkBudget(currentCost);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Enforce budget
|
|
118
|
+
* @param {number} currentCost - Current cost
|
|
119
|
+
* @param {string} cwd - Working directory
|
|
120
|
+
*/
|
|
121
|
+
function enforce(currentCost, cwd) {
|
|
122
|
+
const enforcer = new BudgetEnforcer(cwd);
|
|
123
|
+
return enforcer.enforce(currentCost);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = { BudgetEnforcer, checkBudget, enforce };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cost Reporter — Cost breakdown by phase/operation/provider with trend analysis
|
|
3
|
+
* Generates detailed cost reports from cost-tracker data
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
class CostReporter {
|
|
10
|
+
constructor(cwd) {
|
|
11
|
+
this.cwd = cwd || process.cwd();
|
|
12
|
+
this.metricsPath = path.join(this.cwd, '.planning', 'metrics.json');
|
|
13
|
+
this.reportsDir = path.join(this.cwd, '.planning', 'finops', 'reports');
|
|
14
|
+
this.ensureDir();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Generate cost breakdown report
|
|
19
|
+
* @param {Object} options - Report options
|
|
20
|
+
* @returns {Object} Cost report
|
|
21
|
+
*/
|
|
22
|
+
generateReport(options = {}) {
|
|
23
|
+
const { CostTracker } = require('../cost-tracker.cjs');
|
|
24
|
+
const tracker = new CostTracker(this.cwd);
|
|
25
|
+
const data = tracker.aggregate();
|
|
26
|
+
|
|
27
|
+
const report = {
|
|
28
|
+
timestamp: new Date().toISOString(),
|
|
29
|
+
total: data.total,
|
|
30
|
+
byPhase: data.by_phase || {},
|
|
31
|
+
byProvider: data.by_provider || {},
|
|
32
|
+
trend: this.calculateTrend(data),
|
|
33
|
+
recommendations: this.generateRecommendations(data)
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return report;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Save report to file
|
|
41
|
+
* @param {Object} report - Report to save
|
|
42
|
+
* @param {string} filename - Optional filename
|
|
43
|
+
* @returns {string} Path to saved report
|
|
44
|
+
*/
|
|
45
|
+
saveReport(report, filename) {
|
|
46
|
+
const reportFilename = filename || `cost-${Date.now()}.json`;
|
|
47
|
+
const reportPath = path.join(this.reportsDir, reportFilename);
|
|
48
|
+
|
|
49
|
+
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2), 'utf8');
|
|
50
|
+
return reportPath;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Calculate cost trend
|
|
55
|
+
* @param {Object} data - Aggregated cost data
|
|
56
|
+
* @returns {Array} Trend data
|
|
57
|
+
*/
|
|
58
|
+
calculateTrend(data) {
|
|
59
|
+
// Placeholder - would track historical trend
|
|
60
|
+
return [{
|
|
61
|
+
timestamp: new Date().toISOString(),
|
|
62
|
+
total: data.total.cost || 0
|
|
63
|
+
}];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Generate cost optimization recommendations
|
|
68
|
+
* @param {Object} data - Cost data
|
|
69
|
+
* @returns {Array} Recommendations
|
|
70
|
+
*/
|
|
71
|
+
generateRecommendations(data) {
|
|
72
|
+
const recommendations = [];
|
|
73
|
+
|
|
74
|
+
// Check for high-cost phases
|
|
75
|
+
for (const [phase, costs] of Object.entries(data.by_phase || {})) {
|
|
76
|
+
if (costs.cost > 10) {
|
|
77
|
+
recommendations.push({
|
|
78
|
+
category: 'phase',
|
|
79
|
+
phase,
|
|
80
|
+
suggestion: `High cost phase ($${costs.cost}) — review operation efficiency`
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Check for expensive providers
|
|
86
|
+
for (const [provider, costs] of Object.entries(data.by_provider || {})) {
|
|
87
|
+
if (costs.cost > 5) {
|
|
88
|
+
recommendations.push({
|
|
89
|
+
category: 'provider',
|
|
90
|
+
provider,
|
|
91
|
+
suggestion: `Consider alternative models for ${provider}`
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return recommendations;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Ensure reports directory exists
|
|
101
|
+
*/
|
|
102
|
+
ensureDir() {
|
|
103
|
+
if (!fs.existsSync(this.reportsDir)) {
|
|
104
|
+
fs.mkdirSync(this.reportsDir, { recursive: true });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Generate cost report
|
|
111
|
+
* @param {Object} options - Report options
|
|
112
|
+
* @param {string} cwd - Working directory
|
|
113
|
+
* @returns {Object} Cost report
|
|
114
|
+
*/
|
|
115
|
+
function generateReport(options = {}, cwd) {
|
|
116
|
+
const reporter = new CostReporter(cwd);
|
|
117
|
+
return reporter.generateReport(options);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Save cost report
|
|
122
|
+
* @param {Object} report - Report to save
|
|
123
|
+
* @param {string} filename - Filename
|
|
124
|
+
* @param {string} cwd - Working directory
|
|
125
|
+
* @returns {string} Path to saved report
|
|
126
|
+
*/
|
|
127
|
+
function saveReport(report, filename, cwd) {
|
|
128
|
+
const reporter = new CostReporter(cwd);
|
|
129
|
+
return reporter.saveReport(report, filename);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = { CostReporter, generateReport, saveReport };
|