aios-core 4.2.14 → 4.3.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/.aios-core/cli/commands/validate/index.js +1 -1
- package/.aios-core/core/code-intel/helpers/creation-helper.js +183 -0
- package/.aios-core/core/code-intel/helpers/devops-helper.js +166 -0
- package/.aios-core/core/code-intel/helpers/planning-helper.js +248 -0
- package/.aios-core/core/code-intel/helpers/qa-helper.js +187 -0
- package/.aios-core/core/code-intel/helpers/story-helper.js +146 -0
- package/.aios-core/core/config/schemas/framework-config.schema.json +155 -7
- package/.aios-core/core/config/schemas/project-config.schema.json +329 -15
- package/.aios-core/core/config/template-overrides.js +84 -0
- package/.aios-core/core/docs/troubleshooting-guide.md +1 -1
- package/.aios-core/core/doctor/checks/agent-memory.js +63 -0
- package/.aios-core/core/doctor/checks/claude-md.js +56 -0
- package/.aios-core/core/doctor/checks/code-intel.js +57 -0
- package/.aios-core/core/doctor/checks/commands-count.js +81 -0
- package/.aios-core/core/doctor/checks/core-config.js +53 -0
- package/.aios-core/core/doctor/checks/entity-registry.js +53 -0
- package/.aios-core/core/doctor/checks/git-hooks.js +50 -0
- package/.aios-core/core/doctor/checks/graph-dashboard.js +48 -0
- package/.aios-core/core/doctor/checks/hooks-claude-count.js +107 -0
- package/.aios-core/core/doctor/checks/ide-sync.js +68 -0
- package/.aios-core/core/doctor/checks/index.js +46 -0
- package/.aios-core/core/doctor/checks/node-version.js +33 -0
- package/.aios-core/core/doctor/checks/npm-packages.js +35 -0
- package/.aios-core/core/doctor/checks/rules-files.js +61 -0
- package/.aios-core/core/doctor/checks/settings-json.js +121 -0
- package/.aios-core/core/doctor/checks/skills-count.js +72 -0
- package/.aios-core/core/doctor/fix-handler.js +165 -0
- package/.aios-core/core/doctor/formatters/json.js +14 -0
- package/.aios-core/core/doctor/formatters/text.js +59 -0
- package/.aios-core/core/doctor/index.js +94 -0
- package/.aios-core/core/graph-dashboard/cli.js +361 -0
- package/.aios-core/core/graph-dashboard/data-sources/code-intel-source.js +234 -0
- package/.aios-core/core/graph-dashboard/data-sources/metrics-source.js +95 -0
- package/.aios-core/core/graph-dashboard/data-sources/registry-source.js +106 -0
- package/.aios-core/core/graph-dashboard/formatters/dot-formatter.js +45 -0
- package/.aios-core/core/graph-dashboard/formatters/html-formatter.js +1437 -0
- package/.aios-core/core/graph-dashboard/formatters/json-formatter.js +13 -0
- package/.aios-core/core/graph-dashboard/formatters/mermaid-formatter.js +59 -0
- package/.aios-core/core/graph-dashboard/index.js +21 -0
- package/.aios-core/core/graph-dashboard/renderers/stats-renderer.js +217 -0
- package/.aios-core/core/graph-dashboard/renderers/status-renderer.js +125 -0
- package/.aios-core/core/graph-dashboard/renderers/tree-renderer.js +119 -0
- package/.aios-core/core/health-check/base-check.js +1 -1
- package/.aios-core/core/health-check/check-registry.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/build-config.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/ci-config.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/deployment-readiness.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/docker-config.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/env-file.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/index.js +1 -1
- package/.aios-core/core/health-check/checks/index.js +1 -1
- package/.aios-core/core/health-check/checks/local/disk-space.js +1 -1
- package/.aios-core/core/health-check/checks/local/environment-vars.js +1 -1
- package/.aios-core/core/health-check/checks/local/git-install.js +1 -1
- package/.aios-core/core/health-check/checks/local/ide-detection.js +1 -1
- package/.aios-core/core/health-check/checks/local/index.js +1 -1
- package/.aios-core/core/health-check/checks/local/memory.js +1 -1
- package/.aios-core/core/health-check/checks/local/network.js +1 -1
- package/.aios-core/core/health-check/checks/local/npm-install.js +1 -1
- package/.aios-core/core/health-check/checks/local/shell-environment.js +1 -1
- package/.aios-core/core/health-check/checks/project/agent-config.js +1 -1
- package/.aios-core/core/health-check/checks/project/aios-directory.js +1 -1
- package/.aios-core/core/health-check/checks/project/dependencies.js +1 -1
- package/.aios-core/core/health-check/checks/project/framework-config.js +1 -1
- package/.aios-core/core/health-check/checks/project/index.js +1 -1
- package/.aios-core/core/health-check/checks/project/node-version.js +1 -1
- package/.aios-core/core/health-check/checks/project/package-json.js +1 -1
- package/.aios-core/core/health-check/checks/project/task-definitions.js +1 -1
- package/.aios-core/core/health-check/checks/project/workflow-dependencies.js +1 -1
- package/.aios-core/core/health-check/checks/repository/branch-protection.js +1 -1
- package/.aios-core/core/health-check/checks/repository/commit-history.js +1 -1
- package/.aios-core/core/health-check/checks/repository/conflicts.js +1 -1
- package/.aios-core/core/health-check/checks/repository/git-repo.js +1 -1
- package/.aios-core/core/health-check/checks/repository/git-status.js +1 -1
- package/.aios-core/core/health-check/checks/repository/gitignore.js +1 -1
- package/.aios-core/core/health-check/checks/repository/index.js +1 -1
- package/.aios-core/core/health-check/checks/repository/large-files.js +1 -1
- package/.aios-core/core/health-check/checks/repository/lockfile-integrity.js +1 -1
- package/.aios-core/core/health-check/checks/services/api-endpoints.js +1 -1
- package/.aios-core/core/health-check/checks/services/claude-code.js +1 -1
- package/.aios-core/core/health-check/checks/services/gemini-cli.js +1 -1
- package/.aios-core/core/health-check/checks/services/github-cli.js +1 -1
- package/.aios-core/core/health-check/checks/services/index.js +1 -1
- package/.aios-core/core/health-check/checks/services/mcp-integration.js +1 -1
- package/.aios-core/core/health-check/engine.js +1 -1
- package/.aios-core/core/health-check/healers/backup-manager.js +1 -1
- package/.aios-core/core/health-check/healers/index.js +1 -1
- package/.aios-core/core/health-check/index.js +9 -2
- package/.aios-core/core/health-check/reporters/console.js +1 -1
- package/.aios-core/core/health-check/reporters/index.js +1 -1
- package/.aios-core/core/health-check/reporters/json.js +1 -1
- package/.aios-core/core/health-check/reporters/markdown.js +1 -1
- package/.aios-core/core/ids/layer-classifier.js +65 -0
- package/.aios-core/core/ids/registry-updater.js +49 -0
- package/.aios-core/core/index.esm.js +1 -1
- package/.aios-core/core/index.js +1 -1
- package/.aios-core/core/session/context-detector.js +2 -7
- package/.aios-core/core/synapse/context/context-tracker.js +9 -1
- package/.aios-core/core/synapse/engine.js +33 -13
- package/.aios-core/core/synapse/runtime/hook-runtime.js +40 -2
- package/.aios-core/core/synapse/session/session-manager.js +3 -2
- package/.aios-core/core/synapse/utils/atomic-write.js +79 -0
- package/.aios-core/core-config.yaml +34 -1
- package/.aios-core/data/aios-kb.md +2 -2
- package/.aios-core/data/capability-detection.js +290 -0
- package/.aios-core/data/entity-registry.yaml +10424 -2127
- package/.aios-core/data/mcp-discipline.js +166 -0
- package/.aios-core/data/mcp-tool-examples.yaml +215 -0
- package/.aios-core/data/tok2-validation.js +168 -0
- package/.aios-core/data/tok3-token-comparison.js +123 -0
- package/.aios-core/data/tool-registry.yaml +648 -0
- package/.aios-core/data/tool-search-validation.js +174 -0
- package/.aios-core/development/agents/analyst/MEMORY.md +33 -0
- package/.aios-core/development/agents/architect/MEMORY.md +39 -0
- package/.aios-core/development/agents/data-engineer/MEMORY.md +32 -0
- package/.aios-core/development/agents/dev/MEMORY.md +46 -0
- package/.aios-core/development/agents/dev.md +1 -1
- package/.aios-core/development/agents/devops/MEMORY.md +39 -0
- package/.aios-core/development/agents/devops.md +22 -0
- package/.aios-core/development/agents/pm/MEMORY.md +38 -0
- package/.aios-core/development/agents/po/MEMORY.md +45 -0
- package/.aios-core/development/agents/qa/MEMORY.md +42 -0
- package/.aios-core/development/agents/qa.md +1 -1
- package/.aios-core/development/agents/sm/MEMORY.md +31 -0
- package/.aios-core/development/agents/ux/MEMORY.md +31 -0
- package/.aios-core/development/checklists/issue-triage-checklist.md +35 -0
- package/.aios-core/development/checklists/memory-audit-checklist.md +53 -0
- package/.aios-core/development/scripts/issue-triage.js +171 -0
- package/.aios-core/development/scripts/populate-entity-registry.js +412 -19
- package/.aios-core/development/scripts/unified-activation-pipeline.js +31 -10
- package/.aios-core/development/tasks/analyze-project-structure.md +48 -0
- package/.aios-core/development/tasks/brownfield-create-epic.md +41 -0
- package/.aios-core/development/tasks/create-doc.md +44 -0
- package/.aios-core/development/tasks/create-next-story.md +10 -0
- package/.aios-core/development/tasks/dev-develop-story.md +1 -1
- package/.aios-core/development/tasks/github-devops-github-pr-automation.md +49 -0
- package/.aios-core/development/tasks/github-devops-pre-push-quality-gate.md +63 -0
- package/.aios-core/development/tasks/github-issue-triage.md +118 -0
- package/.aios-core/development/tasks/health-check.yaml +206 -171
- package/.aios-core/development/tasks/kb-mode-interaction.md +3 -3
- package/.aios-core/development/tasks/plan-create-context.md +47 -1
- package/.aios-core/development/tasks/plan-create-implementation.md +55 -0
- package/.aios-core/development/tasks/pr-automation.md +5 -5
- package/.aios-core/development/tasks/qa-gate.md +48 -0
- package/.aios-core/development/tasks/qa-review-story.md +24 -1
- package/.aios-core/development/tasks/resolve-github-issue.md +608 -0
- package/.aios-core/development/tasks/review-contributor-pr.md +152 -0
- package/.aios-core/development/tasks/setup-llm-routing.md +1 -1
- package/.aios-core/development/tasks/spec-research-dependencies.md +4 -0
- package/.aios-core/development/tasks/triage-github-issues.md +356 -0
- package/.aios-core/development/tasks/validate-agents.md +4 -0
- package/.aios-core/development/tasks/validate-next-story.md +10 -0
- package/.aios-core/development/templates/agent-handoff-tmpl.yaml +48 -0
- package/.aios-core/development/templates/code-intel-integration-pattern.md +199 -0
- package/.aios-core/development/templates/ptc-entity-validation.md +113 -0
- package/.aios-core/development/templates/ptc-qa-gate.md +100 -0
- package/.aios-core/development/templates/ptc-research-aggregation.md +94 -0
- package/.aios-core/development/templates/service-template/README.md.hbs +158 -158
- package/.aios-core/development/templates/service-template/__tests__/index.test.ts.hbs +237 -237
- package/.aios-core/development/templates/service-template/client.ts.hbs +403 -403
- package/.aios-core/development/templates/service-template/errors.ts.hbs +182 -182
- package/.aios-core/development/templates/service-template/index.ts.hbs +120 -120
- package/.aios-core/development/templates/service-template/package.json.hbs +87 -87
- package/.aios-core/development/templates/service-template/types.ts.hbs +145 -145
- package/.aios-core/development/templates/squad/agent-template.md +11 -0
- package/.aios-core/development/templates/squad/task-template.md +21 -0
- package/.aios-core/development/templates/squad-template/LICENSE +21 -21
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-COMPLETE.md +1 -1
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.2-SUMMARY.md +1 -1
- package/.aios-core/framework-config.yaml +8 -0
- package/.aios-core/index.esm.js +1 -1
- package/.aios-core/index.js +1 -1
- package/.aios-core/infrastructure/integrations/ai-providers/index.js +1 -1
- package/.aios-core/infrastructure/schemas/task-v3-schema.json +6 -0
- package/.aios-core/infrastructure/scripts/collect-tool-usage.js +311 -0
- package/.aios-core/infrastructure/scripts/generate-optimization-report.js +497 -0
- package/.aios-core/infrastructure/scripts/generate-settings-json.js +300 -0
- package/.aios-core/infrastructure/scripts/git-config-detector.js +65 -9
- package/.aios-core/infrastructure/scripts/ide-sync/index.js +3 -1
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/github-copilot.js +184 -0
- package/.aios-core/infrastructure/scripts/repository-detector.js +3 -3
- package/.aios-core/infrastructure/templates/aios-sync.yaml.template +182 -182
- package/.aios-core/infrastructure/templates/coderabbit.yaml.template +279 -279
- package/.aios-core/infrastructure/templates/github-workflows/ci.yml.template +169 -169
- package/.aios-core/infrastructure/templates/github-workflows/pr-automation.yml.template +330 -330
- package/.aios-core/infrastructure/templates/github-workflows/release.yml.template +196 -196
- package/.aios-core/infrastructure/templates/gitignore/gitignore-aios-base.tmpl +63 -63
- package/.aios-core/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +18 -18
- package/.aios-core/infrastructure/templates/gitignore/gitignore-node.tmpl +85 -85
- package/.aios-core/infrastructure/templates/gitignore/gitignore-python.tmpl +145 -145
- package/.aios-core/install-manifest.yaml +541 -249
- package/.aios-core/lib/build.json +1 -0
- package/.aios-core/local-config.yaml.template +71 -71
- package/.aios-core/monitor/hooks/lib/__init__.py +1 -1
- package/.aios-core/monitor/hooks/lib/enrich.py +58 -58
- package/.aios-core/monitor/hooks/lib/send_event.py +47 -47
- package/.aios-core/monitor/hooks/notification.py +29 -29
- package/.aios-core/monitor/hooks/post_tool_use.py +45 -45
- package/.aios-core/monitor/hooks/pre_compact.py +29 -29
- package/.aios-core/monitor/hooks/pre_tool_use.py +40 -40
- package/.aios-core/monitor/hooks/stop.py +29 -29
- package/.aios-core/monitor/hooks/subagent_stop.py +29 -29
- package/.aios-core/monitor/hooks/user_prompt_submit.py +38 -38
- package/.aios-core/product/templates/adr.hbs +125 -125
- package/.aios-core/product/templates/dbdr.hbs +241 -241
- package/.aios-core/product/templates/epic.hbs +212 -212
- package/.aios-core/product/templates/ide-rules/claude-rules.md +77 -0
- package/.aios-core/product/templates/pmdr.hbs +186 -186
- package/.aios-core/product/templates/prd-v2.0.hbs +216 -216
- package/.aios-core/product/templates/prd.hbs +201 -201
- package/.aios-core/product/templates/story.hbs +263 -263
- package/.aios-core/product/templates/task.hbs +170 -170
- package/.aios-core/product/templates/tmpl-comment-on-examples.sql +158 -158
- package/.aios-core/product/templates/tmpl-migration-script.sql +91 -91
- package/.aios-core/product/templates/tmpl-rls-granular-policies.sql +104 -104
- package/.aios-core/product/templates/tmpl-rls-kiss-policy.sql +10 -10
- package/.aios-core/product/templates/tmpl-rls-roles.sql +135 -135
- package/.aios-core/product/templates/tmpl-rls-simple.sql +77 -77
- package/.aios-core/product/templates/tmpl-rls-tenant.sql +152 -152
- package/.aios-core/product/templates/tmpl-rollback-script.sql +77 -77
- package/.aios-core/product/templates/tmpl-seed-data.sql +140 -140
- package/.aios-core/product/templates/tmpl-smoke-test.sql +16 -16
- package/.aios-core/product/templates/tmpl-staging-copy-merge.sql +139 -139
- package/.aios-core/product/templates/tmpl-stored-proc.sql +140 -140
- package/.aios-core/product/templates/tmpl-trigger.sql +152 -152
- package/.aios-core/product/templates/tmpl-view-materialized.sql +133 -133
- package/.aios-core/product/templates/tmpl-view.sql +177 -177
- package/.aios-core/scripts/pm.sh +0 -0
- package/.aios-core/user-guide.md +15 -15
- package/.aios-core/utils/filters/constants.js +10 -0
- package/.aios-core/utils/filters/content-filter.js +223 -0
- package/.aios-core/utils/filters/field-filter.js +126 -0
- package/.aios-core/utils/filters/index.js +180 -0
- package/.aios-core/utils/filters/schema-filter.js +157 -0
- package/.claude/CLAUDE.md +62 -0
- package/.claude/hooks/enforce-architecture-first.py +196 -196
- package/.claude/hooks/enforce-git-push-authority.sh +33 -0
- package/.claude/hooks/mind-clone-governance.py +192 -192
- package/.claude/hooks/read-protection.py +151 -151
- package/.claude/hooks/slug-validation.py +176 -176
- package/.claude/hooks/sql-governance.py +182 -182
- package/.claude/hooks/synapse-engine.cjs +28 -5
- package/.claude/hooks/write-path-validation.py +194 -194
- package/.claude/rules/agent-authority.md +105 -0
- package/.claude/rules/agent-handoff.md +97 -0
- package/.claude/rules/agent-memory-imports.md +15 -0
- package/.claude/rules/coderabbit-integration.md +101 -0
- package/.claude/rules/ids-principles.md +119 -0
- package/.claude/rules/story-lifecycle.md +145 -0
- package/.claude/rules/tool-examples.md +64 -0
- package/.claude/rules/tool-response-filtering.md +57 -0
- package/.claude/rules/workflow-execution.md +150 -0
- package/LICENSE +33 -33
- package/bin/aios-graph.js +9 -0
- package/bin/aios-init.js +2 -2
- package/bin/aios-minimal.js +0 -0
- package/bin/aios.js +17 -221
- package/bin/utils/detect-fsmonitor.js +70 -0
- package/bin/utils/framework-guard.js +238 -0
- package/bin/utils/validate-publish.js +108 -0
- package/package.json +6 -3
- package/packages/aios-install/bin/aios-install.js +0 -0
- package/packages/aios-install/bin/edmcp.js +0 -0
- package/packages/aios-pro-cli/bin/aios-pro.js +2 -0
- package/packages/installer/src/installer/brownfield-upgrader.js +68 -5
- package/packages/installer/src/merger/index.js +3 -0
- package/packages/installer/src/merger/strategies/index.js +6 -0
- package/packages/installer/src/merger/strategies/yaml-merger.js +181 -0
- package/packages/installer/src/updater/index.js +4 -4
- package/packages/installer/src/wizard/i18n.js +321 -3
- package/packages/installer/src/wizard/ide-config-generator.js +152 -25
- package/packages/installer/src/wizard/index.js +119 -1
- package/packages/installer/src/wizard/pro-setup.js +137 -121
- package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +261 -0
- package/packages/installer/tests/unit/claude-md-template-v5/claude-md-template-v5.test.js +192 -0
- package/packages/installer/tests/unit/doctor/doctor-checks.test.js +551 -0
- package/packages/installer/tests/unit/doctor/doctor-orchestrator.test.js +134 -0
- package/packages/installer/tests/unit/entity-registry-bootstrap.test.js +186 -0
- package/packages/installer/tests/unit/generate-settings-json/generate-settings-json.test.js +309 -0
- package/packages/installer/tests/unit/ide-sync-integration/ide-sync-integration.test.js +230 -0
- package/packages/installer/tests/unit/merger/strategies.test.js +2 -2
- package/packages/installer/tests/unit/merger/yaml-merger.test.js +327 -0
- package/scripts/check-markdown-links.py +352 -352
- package/scripts/dashboard-parallel-dev.sh +0 -0
- package/scripts/dashboard-parallel-phase3.sh +0 -0
- package/scripts/dashboard-parallel-phase4.sh +0 -0
- package/scripts/install-monitor-hooks.sh +0 -0
- package/scripts/package-synapse.js +2 -1
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// =============================================================================
|
|
3
|
+
// generate-optimization-report.js — Optimization Report & Recommendations
|
|
4
|
+
// =============================================================================
|
|
5
|
+
// Story: TOK-5 (Tool Usage Analytics Pipeline)
|
|
6
|
+
// Layer: L2 (.aios-core/infrastructure/scripts/)
|
|
7
|
+
// Purpose:
|
|
8
|
+
// - Compare post-optimization metrics against TOK-1.5 baseline (ACs 4-6)
|
|
9
|
+
// - Generate promote/demote recommendations (ACs 7-9, 13-15)
|
|
10
|
+
// - Produce summary optimization report (ACs 16-17)
|
|
11
|
+
//
|
|
12
|
+
// Usage:
|
|
13
|
+
// node .aios-core/infrastructure/scripts/generate-optimization-report.js [options]
|
|
14
|
+
//
|
|
15
|
+
// Options:
|
|
16
|
+
// --dry-run Show results without writing files
|
|
17
|
+
// --json Output results as JSON to stdout
|
|
18
|
+
// --verbose Show detailed per-tool breakdown
|
|
19
|
+
// =============================================================================
|
|
20
|
+
|
|
21
|
+
'use strict';
|
|
22
|
+
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
const yaml = require('js-yaml');
|
|
26
|
+
|
|
27
|
+
// --- Paths ---
|
|
28
|
+
const ROOT = process.cwd();
|
|
29
|
+
const ANALYTICS_DIR = path.resolve(ROOT, '.aios', 'analytics');
|
|
30
|
+
const BASELINE_FILE = path.join(ANALYTICS_DIR, 'token-baseline.json');
|
|
31
|
+
const USAGE_FILE = path.join(ANALYTICS_DIR, 'tool-usage.json');
|
|
32
|
+
const REPORT_FILE = path.join(ANALYTICS_DIR, 'optimization-report.json');
|
|
33
|
+
const RECOMMENDATIONS_FILE = path.join(ANALYTICS_DIR, 'recommendations.yaml');
|
|
34
|
+
const TOOL_REGISTRY_FILE = path.resolve(ROOT, '.aios-core', 'data', 'tool-registry.yaml');
|
|
35
|
+
|
|
36
|
+
// --- Default Thresholds (overridden by tool-registry.yaml) ---
|
|
37
|
+
const DEFAULT_THRESHOLDS = {
|
|
38
|
+
promote: { minUsesPerSession: 10, minSessions: 5 },
|
|
39
|
+
demote: { maxUsesPerNSessions: 1, sessionWindow: 5 }
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// --- Load Thresholds from tool-registry.yaml (AC 15) ---
|
|
43
|
+
function loadThresholds() {
|
|
44
|
+
try {
|
|
45
|
+
const raw = fs.readFileSync(TOOL_REGISTRY_FILE, 'utf8');
|
|
46
|
+
const registry = yaml.load(raw);
|
|
47
|
+
if (registry && registry.analytics && registry.analytics.thresholds) {
|
|
48
|
+
const t = registry.analytics.thresholds;
|
|
49
|
+
return {
|
|
50
|
+
promote: {
|
|
51
|
+
minUsesPerSession: t.promote?.minUsesPerSession ?? DEFAULT_THRESHOLDS.promote.minUsesPerSession,
|
|
52
|
+
minSessions: t.promote?.minSessions ?? DEFAULT_THRESHOLDS.promote.minSessions
|
|
53
|
+
},
|
|
54
|
+
demote: {
|
|
55
|
+
maxUsesPerNSessions: t.demote?.maxUsesPerNSessions ?? DEFAULT_THRESHOLDS.demote.maxUsesPerNSessions,
|
|
56
|
+
sessionWindow: t.demote?.sessionWindow ?? DEFAULT_THRESHOLDS.demote.sessionWindow
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
// Fall back to defaults
|
|
62
|
+
}
|
|
63
|
+
return DEFAULT_THRESHOLDS;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// --- Load Baseline (TOK-1.5) ---
|
|
67
|
+
function loadBaseline() {
|
|
68
|
+
try {
|
|
69
|
+
const raw = fs.readFileSync(BASELINE_FILE, 'utf8');
|
|
70
|
+
return JSON.parse(raw);
|
|
71
|
+
} catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// --- Load Usage Data ---
|
|
77
|
+
function loadUsageData() {
|
|
78
|
+
try {
|
|
79
|
+
const raw = fs.readFileSync(USAGE_FILE, 'utf8');
|
|
80
|
+
const data = JSON.parse(raw);
|
|
81
|
+
return data && Array.isArray(data.sessions) ? data : null;
|
|
82
|
+
} catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// --- Load Tool Registry ---
|
|
88
|
+
function loadToolRegistry() {
|
|
89
|
+
try {
|
|
90
|
+
const raw = fs.readFileSync(TOOL_REGISTRY_FILE, 'utf8');
|
|
91
|
+
return yaml.load(raw);
|
|
92
|
+
} catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// --- Get tool tier from registry ---
|
|
98
|
+
function getToolTier(registry, toolName) {
|
|
99
|
+
if (!registry || !registry.tools) return 'unknown';
|
|
100
|
+
const tool = registry.tools[toolName];
|
|
101
|
+
return tool ? `tier_${tool.tier}` : 'unknown';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// --- Aggregate tool usage across sessions ---
|
|
105
|
+
function aggregateUsage(sessions) {
|
|
106
|
+
const toolStats = {};
|
|
107
|
+
const sessionCount = sessions.length;
|
|
108
|
+
|
|
109
|
+
for (const session of sessions) {
|
|
110
|
+
if (!session.events) continue;
|
|
111
|
+
for (const event of session.events) {
|
|
112
|
+
if (!toolStats[event.tool_name]) {
|
|
113
|
+
toolStats[event.tool_name] = {
|
|
114
|
+
tool_name: event.tool_name,
|
|
115
|
+
total_invocations: 0,
|
|
116
|
+
total_token_cost_input: 0,
|
|
117
|
+
total_token_cost_output: 0,
|
|
118
|
+
sessions_used: 0,
|
|
119
|
+
invocations_per_session: []
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const stat = toolStats[event.tool_name];
|
|
123
|
+
stat.total_invocations += event.invocation_count;
|
|
124
|
+
stat.total_token_cost_input += event.token_cost_input;
|
|
125
|
+
stat.total_token_cost_output += event.token_cost_output;
|
|
126
|
+
stat.sessions_used += 1;
|
|
127
|
+
stat.invocations_per_session.push(event.invocation_count);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Calculate averages
|
|
132
|
+
for (const stat of Object.values(toolStats)) {
|
|
133
|
+
stat.avg_invocations_per_session = stat.invocations_per_session.length > 0
|
|
134
|
+
? stat.invocations_per_session.reduce((a, b) => a + b, 0) / stat.invocations_per_session.length
|
|
135
|
+
: 0;
|
|
136
|
+
stat.avg_tokens_per_session = sessionCount > 0
|
|
137
|
+
? (stat.total_token_cost_input + stat.total_token_cost_output) / sessionCount
|
|
138
|
+
: 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return { toolStats, sessionCount };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// --- Baseline Comparison (ACs 4-6) ---
|
|
145
|
+
// Compares two dimensions:
|
|
146
|
+
// 1. Static overhead: framework tokens loaded at session start (schemas, rules, agents)
|
|
147
|
+
// Baseline source: frameworkOverhead.totalEstimatedTokens
|
|
148
|
+
// Post-opt source: sum of tool schema tokenCost for tools actually used (from registry)
|
|
149
|
+
// 2. Dynamic usage: total invocation token costs per session
|
|
150
|
+
// Baseline source: workflow median totalTokens
|
|
151
|
+
// Post-opt source: actual usage data from collect-tool-usage.js
|
|
152
|
+
function compareBaseline(baseline, usageData, registry) {
|
|
153
|
+
if (!baseline || !usageData) {
|
|
154
|
+
return { available: false, reason: 'Baseline or usage data not found' };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const sessions = usageData.sessions;
|
|
158
|
+
if (sessions.length === 0) {
|
|
159
|
+
return { available: false, reason: 'No sessions to compare' };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const { toolStats, sessionCount } = aggregateUsage(sessions);
|
|
163
|
+
|
|
164
|
+
// --- Static overhead comparison (C1 fix: apples-to-apples) ---
|
|
165
|
+
// Baseline: total framework overhead loaded at session start
|
|
166
|
+
const baselineOverhead = baseline.frameworkOverhead?.totalEstimatedTokens || 0;
|
|
167
|
+
|
|
168
|
+
// Post-opt: estimate schema overhead for tools actually used in sessions
|
|
169
|
+
// Uses tokenCost from tool-registry.yaml for tools that appeared in usage data
|
|
170
|
+
let postOptSchemaOverhead = 0;
|
|
171
|
+
if (registry && registry.tools) {
|
|
172
|
+
for (const toolName of Object.keys(toolStats)) {
|
|
173
|
+
const toolDef = registry.tools[toolName];
|
|
174
|
+
if (toolDef && toolDef.tokenCost) {
|
|
175
|
+
postOptSchemaOverhead += toolDef.tokenCost;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
// Fallback: estimate from usage data (avg tokens / avg invocations = per-invocation cost)
|
|
180
|
+
postOptSchemaOverhead = Object.values(toolStats).reduce((sum, t) => {
|
|
181
|
+
return sum + (t.avg_invocations_per_session > 0
|
|
182
|
+
? Math.round((t.total_token_cost_input + t.total_token_cost_output) / t.total_invocations)
|
|
183
|
+
: 0);
|
|
184
|
+
}, 0);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// --- Dynamic usage comparison ---
|
|
188
|
+
const postOptTokensInput = Object.values(toolStats).reduce((s, t) => s + t.total_token_cost_input, 0);
|
|
189
|
+
const postOptTokensOutput = Object.values(toolStats).reduce((s, t) => s + t.total_token_cost_output, 0);
|
|
190
|
+
const postOptTotal = postOptTokensInput + postOptTokensOutput;
|
|
191
|
+
const avgPostOptPerSession = sessionCount > 0 ? postOptTotal / sessionCount : 0;
|
|
192
|
+
|
|
193
|
+
const baselineWorkflows = baseline.workflows || {};
|
|
194
|
+
|
|
195
|
+
// Per-workflow comparison (AC 5)
|
|
196
|
+
const workflowComparison = {};
|
|
197
|
+
for (const [wfName, wfData] of Object.entries(baselineWorkflows)) {
|
|
198
|
+
const baselineMedian = wfData.median?.totalTokens || 0;
|
|
199
|
+
const baselineOverheadPct = baseline.comparison?.aiosActual?.overheadPercentOfTypicalSession?.[wfName] || 0;
|
|
200
|
+
const baselineOverheadTokens = Math.round(baselineMedian * baselineOverheadPct / 100);
|
|
201
|
+
|
|
202
|
+
workflowComparison[wfName] = {
|
|
203
|
+
baseline_median_total: baselineMedian,
|
|
204
|
+
baseline_overhead_tokens: baselineOverheadTokens,
|
|
205
|
+
baseline_overhead_pct: baselineOverheadPct,
|
|
206
|
+
post_optimization_schema_overhead: postOptSchemaOverhead,
|
|
207
|
+
post_optimization_avg_usage_per_session: Math.round(avgPostOptPerSession),
|
|
208
|
+
sessions_analyzed: sessionCount
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Total reduction: compares static overhead (schema tokens loaded)
|
|
213
|
+
const absoluteReduction = baselineOverhead - postOptSchemaOverhead;
|
|
214
|
+
const percentageReduction = baselineOverhead > 0
|
|
215
|
+
? Math.round((absoluteReduction / baselineOverhead) * 1000) / 10
|
|
216
|
+
: 0;
|
|
217
|
+
|
|
218
|
+
// Target assessment (AC 6)
|
|
219
|
+
let targetStatus;
|
|
220
|
+
if (percentageReduction >= 25) {
|
|
221
|
+
targetStatus = 'ACHIEVED';
|
|
222
|
+
} else if (percentageReduction >= 15) {
|
|
223
|
+
targetStatus = 'PARTIALLY_ACHIEVED';
|
|
224
|
+
} else {
|
|
225
|
+
targetStatus = 'NOT_ACHIEVED';
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
available: true,
|
|
230
|
+
comparison_methodology: 'Static schema overhead: baseline frameworkOverhead vs post-opt tool schema costs from registry. Dynamic usage tracked separately.',
|
|
231
|
+
baseline_overhead_tokens: baselineOverhead,
|
|
232
|
+
post_optimization_overhead_tokens: postOptSchemaOverhead,
|
|
233
|
+
absolute_reduction_tokens: absoluteReduction,
|
|
234
|
+
percentage_reduction: percentageReduction,
|
|
235
|
+
target_25_45_pct: targetStatus,
|
|
236
|
+
target_description: `25-45% reduction target is ${targetStatus}. Measured: ${percentageReduction}%`,
|
|
237
|
+
dynamic_usage: {
|
|
238
|
+
avg_invocation_tokens_per_session: Math.round(avgPostOptPerSession),
|
|
239
|
+
total_invocation_tokens: postOptTotal,
|
|
240
|
+
sessions_analyzed: sessionCount
|
|
241
|
+
},
|
|
242
|
+
workflow_comparison: workflowComparison,
|
|
243
|
+
per_tool_breakdown: Object.values(toolStats).map(t => ({
|
|
244
|
+
tool_name: t.tool_name,
|
|
245
|
+
total_invocations: t.total_invocations,
|
|
246
|
+
avg_invocations_per_session: Math.round(t.avg_invocations_per_session * 10) / 10,
|
|
247
|
+
total_tokens: t.total_token_cost_input + t.total_token_cost_output,
|
|
248
|
+
avg_tokens_per_session: Math.round(t.avg_tokens_per_session)
|
|
249
|
+
})).sort((a, b) => b.total_tokens - a.total_tokens)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// --- Promote/Demote Recommendations (ACs 7-9, 13-14) ---
|
|
254
|
+
function generateRecommendations(usageData, registry, thresholds) {
|
|
255
|
+
if (!usageData || usageData.sessions.length === 0) {
|
|
256
|
+
return [];
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const { toolStats, sessionCount } = aggregateUsage(usageData.sessions);
|
|
260
|
+
const recommendations = [];
|
|
261
|
+
|
|
262
|
+
for (const [toolName, stat] of Object.entries(toolStats)) {
|
|
263
|
+
const currentTier = getToolTier(registry, toolName);
|
|
264
|
+
|
|
265
|
+
// Promote: tool used >10 times per session average across 5+ sessions (AC 7, 13)
|
|
266
|
+
if (
|
|
267
|
+
stat.avg_invocations_per_session > thresholds.promote.minUsesPerSession &&
|
|
268
|
+
stat.sessions_used >= thresholds.promote.minSessions &&
|
|
269
|
+
currentTier !== 'tier_1' // Don't promote if already Tier 1
|
|
270
|
+
) {
|
|
271
|
+
recommendations.push({
|
|
272
|
+
tool_name: toolName,
|
|
273
|
+
action: 'promote',
|
|
274
|
+
current_tier: currentTier,
|
|
275
|
+
recommended_tier: currentTier === 'tier_3' ? 'tier_2' : 'tier_1',
|
|
276
|
+
evidence: {
|
|
277
|
+
avg_invocations_per_session: Math.round(stat.avg_invocations_per_session * 10) / 10,
|
|
278
|
+
sessions_used: stat.sessions_used,
|
|
279
|
+
total_sessions: sessionCount,
|
|
280
|
+
threshold_invocations: thresholds.promote.minUsesPerSession,
|
|
281
|
+
threshold_sessions: thresholds.promote.minSessions
|
|
282
|
+
},
|
|
283
|
+
rationale: `Tool used ${Math.round(stat.avg_invocations_per_session * 10) / 10} times/session across ${stat.sessions_used} sessions (threshold: >${thresholds.promote.minUsesPerSession}/session, ${thresholds.promote.minSessions}+ sessions)`
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Demote: tool used <1 time per N sessions (AC 8, 14)
|
|
288
|
+
// C2 fix: precise calculation — sessions_used/sessionCount gives the fraction
|
|
289
|
+
// of sessions where tool appeared. Compare against maxUsesPerNSessions/sessionWindow
|
|
290
|
+
// meaning "less than 1 use per 5 sessions" = usage rate < 1/5 = 0.2
|
|
291
|
+
const usageRate = sessionCount > 0 ? stat.sessions_used / sessionCount : 0;
|
|
292
|
+
const demoteThresholdRate = thresholds.demote.maxUsesPerNSessions / thresholds.demote.sessionWindow;
|
|
293
|
+
|
|
294
|
+
if (
|
|
295
|
+
usageRate < demoteThresholdRate &&
|
|
296
|
+
sessionCount >= thresholds.demote.sessionWindow &&
|
|
297
|
+
currentTier !== 'tier_3' // Don't demote if already Tier 3
|
|
298
|
+
) {
|
|
299
|
+
recommendations.push({
|
|
300
|
+
tool_name: toolName,
|
|
301
|
+
action: 'demote',
|
|
302
|
+
current_tier: currentTier,
|
|
303
|
+
recommended_tier: currentTier === 'tier_1' ? 'tier_2' : 'tier_3',
|
|
304
|
+
evidence: {
|
|
305
|
+
usage_rate: Math.round(usageRate * 1000) / 1000,
|
|
306
|
+
demote_threshold_rate: demoteThresholdRate,
|
|
307
|
+
sessions_used: stat.sessions_used,
|
|
308
|
+
total_sessions: sessionCount,
|
|
309
|
+
threshold_max_uses: thresholds.demote.maxUsesPerNSessions,
|
|
310
|
+
threshold_session_window: thresholds.demote.sessionWindow
|
|
311
|
+
},
|
|
312
|
+
rationale: `Tool used in ${stat.sessions_used}/${sessionCount} sessions (rate: ${Math.round(usageRate * 100)}%). Threshold: <${thresholds.demote.maxUsesPerNSessions} per ${thresholds.demote.sessionWindow} sessions (${Math.round(demoteThresholdRate * 100)}%)`
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Check for tools in registry that are never used
|
|
318
|
+
if (registry && registry.tools && sessionCount >= thresholds.demote.sessionWindow) {
|
|
319
|
+
for (const [toolName, toolDef] of Object.entries(registry.tools)) {
|
|
320
|
+
if (!toolStats[toolName] && toolDef.tier < 3) {
|
|
321
|
+
recommendations.push({
|
|
322
|
+
tool_name: toolName,
|
|
323
|
+
action: 'demote',
|
|
324
|
+
current_tier: `tier_${toolDef.tier}`,
|
|
325
|
+
recommended_tier: 'tier_3',
|
|
326
|
+
evidence: {
|
|
327
|
+
usage_rate: 0,
|
|
328
|
+
sessions_used: 0,
|
|
329
|
+
total_sessions: sessionCount
|
|
330
|
+
},
|
|
331
|
+
rationale: `Tool never used in ${sessionCount} analyzed sessions. Consider demotion to Tier 3 (deferred).`
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return recommendations;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// --- Generate Summary Report (ACs 16-17) ---
|
|
341
|
+
function generateReport(comparison, recommendations, usageData, thresholds) {
|
|
342
|
+
const sessions = usageData ? usageData.sessions : [];
|
|
343
|
+
const sessionCount = sessions.length;
|
|
344
|
+
|
|
345
|
+
// Measurement period
|
|
346
|
+
let periodStart = null;
|
|
347
|
+
let periodEnd = null;
|
|
348
|
+
if (sessionCount > 0) {
|
|
349
|
+
const timestamps = sessions.map(s => s.timestamp).filter(Boolean).sort();
|
|
350
|
+
periodStart = timestamps[0];
|
|
351
|
+
periodEnd = timestamps[timestamps.length - 1];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return {
|
|
355
|
+
version: '1.0.0',
|
|
356
|
+
generated_at: new Date().toISOString(),
|
|
357
|
+
story: 'TOK-5',
|
|
358
|
+
epic: 'Token Optimization — Intelligent Tool Loading',
|
|
359
|
+
measurement_period: {
|
|
360
|
+
start: periodStart,
|
|
361
|
+
end: periodEnd,
|
|
362
|
+
sessions_analyzed: sessionCount
|
|
363
|
+
},
|
|
364
|
+
baseline_comparison: comparison,
|
|
365
|
+
total_tokens_saved: comparison.available ? comparison.absolute_reduction_tokens : 0,
|
|
366
|
+
percentage_reduction: comparison.available ? comparison.percentage_reduction : 0,
|
|
367
|
+
target_status: comparison.available ? comparison.target_25_45_pct : 'NO_DATA',
|
|
368
|
+
recommendations_count: recommendations.length,
|
|
369
|
+
promote_count: recommendations.filter(r => r.action === 'promote').length,
|
|
370
|
+
demote_count: recommendations.filter(r => r.action === 'demote').length,
|
|
371
|
+
thresholds_used: thresholds
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// --- Save Recommendations as YAML (AC 9) ---
|
|
376
|
+
function saveRecommendations(recommendations, dryRun) {
|
|
377
|
+
const yamlContent = yaml.dump({
|
|
378
|
+
version: '1.0.0',
|
|
379
|
+
generated_at: new Date().toISOString(),
|
|
380
|
+
story: 'TOK-5',
|
|
381
|
+
recommendations_count: recommendations.length,
|
|
382
|
+
recommendations: recommendations
|
|
383
|
+
}, { lineWidth: 120, noRefs: true });
|
|
384
|
+
|
|
385
|
+
if (!dryRun) {
|
|
386
|
+
if (!fs.existsSync(ANALYTICS_DIR)) {
|
|
387
|
+
fs.mkdirSync(ANALYTICS_DIR, { recursive: true });
|
|
388
|
+
}
|
|
389
|
+
fs.writeFileSync(RECOMMENDATIONS_FILE, yamlContent, 'utf8');
|
|
390
|
+
}
|
|
391
|
+
return yamlContent;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// --- Save Report as JSON (AC 16) ---
|
|
395
|
+
function saveReport(report, dryRun) {
|
|
396
|
+
const content = JSON.stringify(report, null, 2);
|
|
397
|
+
if (!dryRun) {
|
|
398
|
+
if (!fs.existsSync(ANALYTICS_DIR)) {
|
|
399
|
+
fs.mkdirSync(ANALYTICS_DIR, { recursive: true });
|
|
400
|
+
}
|
|
401
|
+
fs.writeFileSync(REPORT_FILE, content, 'utf8');
|
|
402
|
+
}
|
|
403
|
+
return content;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// --- Main ---
|
|
407
|
+
function main() {
|
|
408
|
+
const args = process.argv.slice(2);
|
|
409
|
+
const dryRun = args.includes('--dry-run');
|
|
410
|
+
const jsonOutput = args.includes('--json');
|
|
411
|
+
const verbose = args.includes('--verbose');
|
|
412
|
+
|
|
413
|
+
// Load data
|
|
414
|
+
const baseline = loadBaseline();
|
|
415
|
+
const usageData = loadUsageData();
|
|
416
|
+
const registry = loadToolRegistry();
|
|
417
|
+
const thresholds = loadThresholds();
|
|
418
|
+
|
|
419
|
+
if (!baseline) {
|
|
420
|
+
console.error('[TOK-5] Error: Baseline not found at', BASELINE_FILE);
|
|
421
|
+
console.error('[TOK-5] Run TOK-1.5 baseline collection first.');
|
|
422
|
+
process.exit(1);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (!usageData || usageData.sessions.length === 0) {
|
|
426
|
+
console.error('[TOK-5] Error: No usage data found at', USAGE_FILE);
|
|
427
|
+
console.error('[TOK-5] Run collect-tool-usage.js to collect session data first.');
|
|
428
|
+
process.exit(1);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Baseline comparison (ACs 4-6)
|
|
432
|
+
const comparison = compareBaseline(baseline, usageData, registry);
|
|
433
|
+
|
|
434
|
+
// Promote/demote recommendations (ACs 7-9, 13-14)
|
|
435
|
+
const recommendations = generateRecommendations(usageData, registry, thresholds);
|
|
436
|
+
|
|
437
|
+
// Summary report (ACs 16-17)
|
|
438
|
+
const report = generateReport(comparison, recommendations, usageData, thresholds);
|
|
439
|
+
|
|
440
|
+
// Save outputs
|
|
441
|
+
saveReport(report, dryRun);
|
|
442
|
+
saveRecommendations(recommendations, dryRun);
|
|
443
|
+
|
|
444
|
+
// Console output
|
|
445
|
+
if (jsonOutput) {
|
|
446
|
+
console.log(JSON.stringify({ report, recommendations }, null, 2));
|
|
447
|
+
} else {
|
|
448
|
+
console.log('=== TOK-5 Optimization Report ===');
|
|
449
|
+
console.log(`Sessions analyzed: ${report.measurement_period.sessions_analyzed}`);
|
|
450
|
+
console.log(`Period: ${report.measurement_period.start || 'N/A'} → ${report.measurement_period.end || 'N/A'}`);
|
|
451
|
+
console.log('');
|
|
452
|
+
|
|
453
|
+
if (comparison.available) {
|
|
454
|
+
console.log('--- Baseline Comparison ---');
|
|
455
|
+
console.log(`Baseline overhead: ${comparison.baseline_overhead_tokens} tokens`);
|
|
456
|
+
console.log(`Post-optimization: ${comparison.post_optimization_overhead_tokens} tokens`);
|
|
457
|
+
console.log(`Reduction: ${comparison.absolute_reduction_tokens} tokens (${comparison.percentage_reduction}%)`);
|
|
458
|
+
console.log(`Target (25-45%): ${comparison.target_25_45_pct}`);
|
|
459
|
+
} else {
|
|
460
|
+
console.log(`Baseline comparison: ${comparison.reason}`);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
console.log('');
|
|
464
|
+
console.log('--- Recommendations ---');
|
|
465
|
+
console.log(`Total: ${recommendations.length} (${report.promote_count} promote, ${report.demote_count} demote)`);
|
|
466
|
+
|
|
467
|
+
if (verbose && recommendations.length > 0) {
|
|
468
|
+
for (const rec of recommendations) {
|
|
469
|
+
console.log(` ${rec.action.toUpperCase()}: ${rec.tool_name} (${rec.current_tier} → ${rec.recommended_tier})`);
|
|
470
|
+
console.log(` Rationale: ${rec.rationale}`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
console.log('');
|
|
475
|
+
if (dryRun) {
|
|
476
|
+
console.log('[TOK-5] Dry run — no files written.');
|
|
477
|
+
} else {
|
|
478
|
+
console.log(`[TOK-5] Report: ${REPORT_FILE}`);
|
|
479
|
+
console.log(`[TOK-5] Recommendations: ${RECOMMENDATIONS_FILE}`);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Run main only when executed directly (not when required as module)
|
|
485
|
+
if (require.main === module) {
|
|
486
|
+
main();
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// --- Exports for testing ---
|
|
490
|
+
module.exports = {
|
|
491
|
+
loadThresholds,
|
|
492
|
+
compareBaseline,
|
|
493
|
+
generateRecommendations,
|
|
494
|
+
generateReport,
|
|
495
|
+
aggregateUsage,
|
|
496
|
+
DEFAULT_THRESHOLDS
|
|
497
|
+
};
|