avorelo 0.1.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.
Files changed (258) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -0
  3. package/bin/avorelo +9 -0
  4. package/package.json +135 -0
  5. package/scripts/README.md +40 -0
  6. package/scripts/cco-dashboard.js +252 -0
  7. package/scripts/cco-status.js +430 -0
  8. package/scripts/lib/activation/account-state.js +37 -0
  9. package/scripts/lib/activation/activation-runner.js +546 -0
  10. package/scripts/lib/activation/activation-self-healing.js +480 -0
  11. package/scripts/lib/activation/activation-state.js +83 -0
  12. package/scripts/lib/activation/activation-summary.js +191 -0
  13. package/scripts/lib/activation/adapters/claude-code.js +77 -0
  14. package/scripts/lib/activation/adapters/codex-cli.js +52 -0
  15. package/scripts/lib/activation/adapters/cursor.js +37 -0
  16. package/scripts/lib/activation/adapters/github-agent.js +39 -0
  17. package/scripts/lib/activation/adapters/terminal.js +42 -0
  18. package/scripts/lib/activation/adapters/vscode.js +39 -0
  19. package/scripts/lib/activation/adapters/windsurf.js +37 -0
  20. package/scripts/lib/activation/ai-surface-detector.js +151 -0
  21. package/scripts/lib/activation/connect-account.js +145 -0
  22. package/scripts/lib/activation/detect-environment.js +75 -0
  23. package/scripts/lib/activation/detect-hosts.js +62 -0
  24. package/scripts/lib/activation/format-activation-output.js +109 -0
  25. package/scripts/lib/activation/next-action.js +43 -0
  26. package/scripts/lib/activation/repair-engine.js +219 -0
  27. package/scripts/lib/activation-distribution-readiness.js +507 -0
  28. package/scripts/lib/adapter-conformance.js +176 -0
  29. package/scripts/lib/adapter-readiness.js +417 -0
  30. package/scripts/lib/adapter-safety-boundaries.js +335 -0
  31. package/scripts/lib/adapter-technical-readiness-gate.js +205 -0
  32. package/scripts/lib/agent-access-governance.js +455 -0
  33. package/scripts/lib/agent-enforcement.js +765 -0
  34. package/scripts/lib/agent-policy-profile.js +210 -0
  35. package/scripts/lib/agent-security/action-evaluator.js +507 -0
  36. package/scripts/lib/agent-security/adapter-registry.js +98 -0
  37. package/scripts/lib/agent-security/auto-policy.js +139 -0
  38. package/scripts/lib/agent-security/bounded-scan.js +93 -0
  39. package/scripts/lib/agent-security/enforcement-adapter.js +174 -0
  40. package/scripts/lib/agent-security/enforcement-engine.js +1129 -0
  41. package/scripts/lib/agent-security/file-write-adapter.js +183 -0
  42. package/scripts/lib/agent-security/file-write-rules.js +178 -0
  43. package/scripts/lib/agent-security/index.js +3342 -0
  44. package/scripts/lib/agent-security/instruction-risk.js +181 -0
  45. package/scripts/lib/agent-security/mcp-action-adapter.js +185 -0
  46. package/scripts/lib/agent-security/mcp-action-rules.js +184 -0
  47. package/scripts/lib/agent-security/package-action-adapter.js +175 -0
  48. package/scripts/lib/agent-security/package-action-rules.js +233 -0
  49. package/scripts/lib/agent-security/performance.js +148 -0
  50. package/scripts/lib/agent-security/permission-minimizer.js +403 -0
  51. package/scripts/lib/agent-security/scan-cache.js +74 -0
  52. package/scripts/lib/agent-security/source-trust.js +146 -0
  53. package/scripts/lib/ai-install-prompt.js +288 -0
  54. package/scripts/lib/ai-workspace-hygiene.js +1499 -0
  55. package/scripts/lib/alpha-activation.js +520 -0
  56. package/scripts/lib/alpha-feedback.js +263 -0
  57. package/scripts/lib/alpha-readiness-gate.js +332 -0
  58. package/scripts/lib/anti-gaming.js +169 -0
  59. package/scripts/lib/artifact-health.js +431 -0
  60. package/scripts/lib/attribution.js +180 -0
  61. package/scripts/lib/audit.js +289 -0
  62. package/scripts/lib/avorelo-skill-registry.js +810 -0
  63. package/scripts/lib/batch-jobs.js +71 -0
  64. package/scripts/lib/brain-pack.js +578 -0
  65. package/scripts/lib/brand-boundary.js +424 -0
  66. package/scripts/lib/brand.js +74 -0
  67. package/scripts/lib/browser-capability.js +1048 -0
  68. package/scripts/lib/browser-proof-preflight.js +321 -0
  69. package/scripts/lib/cache-readiness.js +187 -0
  70. package/scripts/lib/canonical-reentry.js +162 -0
  71. package/scripts/lib/capability-packs.js +314 -0
  72. package/scripts/lib/capability-recommender.js +512 -0
  73. package/scripts/lib/capability-registry.js +1059 -0
  74. package/scripts/lib/carry-forward-surfacing.js +194 -0
  75. package/scripts/lib/ccusage-adapter.js +188 -0
  76. package/scripts/lib/company-loop.js +1149 -0
  77. package/scripts/lib/config.js +637 -0
  78. package/scripts/lib/context-acquisition-plan.js +287 -0
  79. package/scripts/lib/context-budget-guard.js +170 -0
  80. package/scripts/lib/context-budget-scanner.js +257 -0
  81. package/scripts/lib/context-optimizer.js +715 -0
  82. package/scripts/lib/context-reduction-plan.js +178 -0
  83. package/scripts/lib/context-safety.js +88 -0
  84. package/scripts/lib/context-savings-engine.js +158 -0
  85. package/scripts/lib/cost-evidence.js +254 -0
  86. package/scripts/lib/cross-host-install-plan.js +308 -0
  87. package/scripts/lib/cross-host-install-readiness.js +237 -0
  88. package/scripts/lib/cross-host-value-flow.js +268 -0
  89. package/scripts/lib/dashboard.js +900 -0
  90. package/scripts/lib/design-partner-feedback.js +346 -0
  91. package/scripts/lib/entitlements.js +100 -0
  92. package/scripts/lib/execution-packet.js +559 -0
  93. package/scripts/lib/experimentation-events.js +547 -0
  94. package/scripts/lib/external-capability-compliance.js +107 -0
  95. package/scripts/lib/external-user-simulation.js +166 -0
  96. package/scripts/lib/failure-recovery-readiness.js +81 -0
  97. package/scripts/lib/failure-recovery.js +419 -0
  98. package/scripts/lib/feedback-intelligence.js +537 -0
  99. package/scripts/lib/feedback-signals.js +205 -0
  100. package/scripts/lib/file-integrity.js +68 -0
  101. package/scripts/lib/fsx.js +127 -0
  102. package/scripts/lib/full-readiness-gate.js +451 -0
  103. package/scripts/lib/guidance-builder.js +174 -0
  104. package/scripts/lib/hook-apply.js +1019 -0
  105. package/scripts/lib/hook-baseline.js +310 -0
  106. package/scripts/lib/hook-config-preview.js +275 -0
  107. package/scripts/lib/hook-contracts.js +290 -0
  108. package/scripts/lib/hook-safety-boundary-readiness.js +80 -0
  109. package/scripts/lib/host-capability-matrix.js +351 -0
  110. package/scripts/lib/host-support-context.js +254 -0
  111. package/scripts/lib/http-hook-action.js +538 -0
  112. package/scripts/lib/install-ai-readiness.js +84 -0
  113. package/scripts/lib/install-intake-risk.js +1037 -0
  114. package/scripts/lib/install-journey-intelligence.js +329 -0
  115. package/scripts/lib/intervention-guidance.js +57 -0
  116. package/scripts/lib/known-limitations.js +115 -0
  117. package/scripts/lib/l8-path-truth.js +146 -0
  118. package/scripts/lib/launch-hardening-gate.js +436 -0
  119. package/scripts/lib/launch-readiness.js +628 -0
  120. package/scripts/lib/learning-memory.js +686 -0
  121. package/scripts/lib/lifecycle-hooks.js +802 -0
  122. package/scripts/lib/local-package-smoke.js +423 -0
  123. package/scripts/lib/local-pricing.js +299 -0
  124. package/scripts/lib/mcp-enforcement.js +311 -0
  125. package/scripts/lib/mcp-least-privilege-policy.js +303 -0
  126. package/scripts/lib/mcp-tool-inventory.js +388 -0
  127. package/scripts/lib/mcp-tool-risk.js +0 -0
  128. package/scripts/lib/memory.js +335 -0
  129. package/scripts/lib/metrics.js +699 -0
  130. package/scripts/lib/micro-proof.js +133 -0
  131. package/scripts/lib/next-run-context.js +436 -0
  132. package/scripts/lib/operating-value.js +1648 -0
  133. package/scripts/lib/optimization-v3.js +122 -0
  134. package/scripts/lib/orchestration/adapters/_shared.js +49 -0
  135. package/scripts/lib/orchestration/adapters/aider.js +18 -0
  136. package/scripts/lib/orchestration/adapters/claude-code.js +35 -0
  137. package/scripts/lib/orchestration/adapters/codex.js +35 -0
  138. package/scripts/lib/orchestration/adapters/gemini-cli.js +18 -0
  139. package/scripts/lib/orchestration/adapters/git.js +25 -0
  140. package/scripts/lib/orchestration/adapters/index.js +31 -0
  141. package/scripts/lib/orchestration/adapters/lm-studio.js +18 -0
  142. package/scripts/lib/orchestration/adapters/ollama.js +18 -0
  143. package/scripts/lib/orchestration/adapters/opencode.js +18 -0
  144. package/scripts/lib/orchestration/adapters/openrouter.js +18 -0
  145. package/scripts/lib/orchestration/adapters/test-runner.js +25 -0
  146. package/scripts/lib/orchestration/cli.js +438 -0
  147. package/scripts/lib/orchestration/execution-manager.js +279 -0
  148. package/scripts/lib/orchestration/handoff.js +314 -0
  149. package/scripts/lib/orchestration/index.js +456 -0
  150. package/scripts/lib/orchestration/inventory.js +47 -0
  151. package/scripts/lib/orchestration/model-discovery.js +498 -0
  152. package/scripts/lib/orchestration/model-profiler.js +170 -0
  153. package/scripts/lib/orchestration/model-profiles.js +252 -0
  154. package/scripts/lib/orchestration/model-refresh-policy.js +72 -0
  155. package/scripts/lib/orchestration/proof-writer.js +349 -0
  156. package/scripts/lib/orchestration/provider-discovery/aider.js +49 -0
  157. package/scripts/lib/orchestration/provider-discovery/claude-code.js +56 -0
  158. package/scripts/lib/orchestration/provider-discovery/codex.js +49 -0
  159. package/scripts/lib/orchestration/provider-discovery/common.js +186 -0
  160. package/scripts/lib/orchestration/provider-discovery/gemini.js +106 -0
  161. package/scripts/lib/orchestration/provider-discovery/lm-studio.js +118 -0
  162. package/scripts/lib/orchestration/provider-discovery/models-dev.js +12 -0
  163. package/scripts/lib/orchestration/provider-discovery/ollama.js +100 -0
  164. package/scripts/lib/orchestration/provider-discovery/opencode.js +47 -0
  165. package/scripts/lib/orchestration/provider-discovery/openrouter.js +44 -0
  166. package/scripts/lib/orchestration/risk-classifier.js +130 -0
  167. package/scripts/lib/orchestration/routing-policy.js +486 -0
  168. package/scripts/lib/orchestration/settings.js +112 -0
  169. package/scripts/lib/orchestration/state.js +165 -0
  170. package/scripts/lib/orchestration/verification-manager.js +138 -0
  171. package/scripts/lib/output-profiles.js +146 -0
  172. package/scripts/lib/package-content-audit.js +368 -0
  173. package/scripts/lib/package-runtime.js +278 -0
  174. package/scripts/lib/plan-surface.js +53 -0
  175. package/scripts/lib/plans.js +2318 -0
  176. package/scripts/lib/policy-provider.js +27 -0
  177. package/scripts/lib/prelaunch-activation-readiness.js +409 -0
  178. package/scripts/lib/prelaunch-evidence-store.js +816 -0
  179. package/scripts/lib/prelaunch-intelligence.js +869 -0
  180. package/scripts/lib/pricing-experiment.js +118 -0
  181. package/scripts/lib/pro-moment-events.js +77 -0
  182. package/scripts/lib/pro-moment-state.js +227 -0
  183. package/scripts/lib/pro-moments.js +1216 -0
  184. package/scripts/lib/product-learning-events.js +629 -0
  185. package/scripts/lib/project-profile.js +555 -0
  186. package/scripts/lib/prompt-compiler.js +280 -0
  187. package/scripts/lib/prompt-lint.js +32 -0
  188. package/scripts/lib/prompt-suggestions.js +52 -0
  189. package/scripts/lib/proof-canonical.js +398 -0
  190. package/scripts/lib/proof-drilldown.js +383 -0
  191. package/scripts/lib/proof-events.js +342 -0
  192. package/scripts/lib/proof-history.js +243 -0
  193. package/scripts/lib/proof-metrics.js +296 -0
  194. package/scripts/lib/proof-outcome-evidence.js +134 -0
  195. package/scripts/lib/proof-receipt.js +335 -0
  196. package/scripts/lib/proof-record.js +461 -0
  197. package/scripts/lib/public-activation-distribution-gate.js +258 -0
  198. package/scripts/lib/public-cli.js +3891 -0
  199. package/scripts/lib/public-distribution-truth.js +211 -0
  200. package/scripts/lib/public-install-claim-checker.js +294 -0
  201. package/scripts/lib/publish-provenance-readiness.js +283 -0
  202. package/scripts/lib/readiness-delta.js +218 -0
  203. package/scripts/lib/readiness-evidence-closure.js +196 -0
  204. package/scripts/lib/reentry-memory-capture.js +241 -0
  205. package/scripts/lib/reentry-memory-retrieval.js +302 -0
  206. package/scripts/lib/reentry-memory-status.js +146 -0
  207. package/scripts/lib/reentry-memory-store.js +178 -0
  208. package/scripts/lib/reentry-state.js +66 -0
  209. package/scripts/lib/release-candidate-bundle.js +166 -0
  210. package/scripts/lib/remediation.js +81 -0
  211. package/scripts/lib/repo-map.js +391 -0
  212. package/scripts/lib/run-improvements-lifecycle.js +330 -0
  213. package/scripts/lib/run-improvements.js +789 -0
  214. package/scripts/lib/runtime-decision-policy.js +387 -0
  215. package/scripts/lib/safe-path-engine.js +705 -0
  216. package/scripts/lib/safe-run-controller.js +887 -0
  217. package/scripts/lib/score.js +262 -0
  218. package/scripts/lib/seamless-enforcement.js +329 -0
  219. package/scripts/lib/seamless-outcome.js +689 -0
  220. package/scripts/lib/seamless-reality-gate.js +5043 -0
  221. package/scripts/lib/security-risk-classifier.js +511 -0
  222. package/scripts/lib/security-scan.js +384 -0
  223. package/scripts/lib/session-context-optimizer.js +1211 -0
  224. package/scripts/lib/session-timing.js +315 -0
  225. package/scripts/lib/skill-hygiene.js +805 -0
  226. package/scripts/lib/skill-packs.js +161 -0
  227. package/scripts/lib/skills-operating-layer.js +580 -0
  228. package/scripts/lib/smart-work-routing.js +768 -0
  229. package/scripts/lib/source-catalog.js +700 -0
  230. package/scripts/lib/status-value-summary.js +32 -0
  231. package/scripts/lib/support-bundle.js +578 -0
  232. package/scripts/lib/task-continuation.js +440 -0
  233. package/scripts/lib/test-helpers.js +15 -0
  234. package/scripts/lib/tier.js +38 -0
  235. package/scripts/lib/token-context-quality-gate.js +370 -0
  236. package/scripts/lib/token-cost-capture.js +187 -0
  237. package/scripts/lib/token-cost-intelligence.js +358 -0
  238. package/scripts/lib/token-efficiency-evidence.js +213 -0
  239. package/scripts/lib/token-evidence.js +699 -0
  240. package/scripts/lib/tokenish.js +17 -0
  241. package/scripts/lib/tool-output-sandbox.js +304 -0
  242. package/scripts/lib/trust-audit.js +136 -0
  243. package/scripts/lib/unified-events.js +396 -0
  244. package/scripts/lib/upgrade-interruption-recovery.js +407 -0
  245. package/scripts/lib/usage-ledger.js +201 -0
  246. package/scripts/lib/value-ledger.js +130 -0
  247. package/scripts/lib/value-proof-calibration.js +531 -0
  248. package/scripts/lib/visual-qa.js +231 -0
  249. package/scripts/lib/voice-alpha.js +29 -0
  250. package/scripts/lib/work-aware-orchestration.js +976 -0
  251. package/scripts/lib/work-control-receipts.js +577 -0
  252. package/scripts/lib/work-ledger.js +1123 -0
  253. package/scripts/lib/work-panel-preview.js +352 -0
  254. package/scripts/lib/workflow-discipline.js +280 -0
  255. package/scripts/lib/workflow-signals.js +419 -0
  256. package/scripts/lib/workspace-map.js +281 -0
  257. package/scripts/lib/workspace-registry.js +1367 -0
  258. package/scripts/lib/workspace-resolver.js +480 -0
@@ -0,0 +1,287 @@
1
+ "use strict";
2
+
3
+ // ── Context Acquisition Plan ──────────────────────────────────────────────────
4
+ //
5
+ // Determines what context to acquire for a task — without loading it all.
6
+ // Selects the minimal sufficient context strategy, budget, and fallback.
7
+ //
8
+ // Contract: avorelo.contextAcquisitionPlan.v1
9
+ //
10
+ // Strategy order:
11
+ // 1. project profile / activation facts
12
+ // 2. repo map / symbol map
13
+ // 3. selected package scope
14
+ // 4. git diff summary (if relevant)
15
+ // 5. file slices
16
+ // 6. full file (only if necessary and within budget)
17
+ // 7. fallback + warn if confidence low
18
+ //
19
+ // Non-goals: loading full repo, selecting secrets/sensitive files,
20
+ // automatic full-file fallback without reason.
21
+
22
+ const fs = require("node:fs");
23
+ const path = require("node:path");
24
+ const { nowIso } = require("./fsx");
25
+ const { appendProductLearningEvent } = require("./product-learning-events");
26
+
27
+ const CONTRACT = "avorelo.contextAcquisitionPlan.v1";
28
+ const SCHEMA_VERSION = 1;
29
+
30
+ const PLAN_DIR_REL = ".claude/cco/orchestration/context-acquisition";
31
+ const LATEST_PLAN_REL = `${PLAN_DIR_REL}/latest-plan.json`;
32
+
33
+ const DEFAULT_CONTEXT_BUDGET_TOKENS = 8000;
34
+ const MAX_CONTEXT_BUDGET_TOKENS = 16384;
35
+
36
+ // ── Task classification ───────────────────────────────────────────────────────
37
+
38
+ const TASK_PATTERNS = {
39
+ docs: /\b(docs?|documentation|readme|changelog|guide|tutorial|write up|update docs?)\b/i,
40
+ test: /\b(test|spec|jest|vitest|mocha|pytest|unit test|integration test)\b/i,
41
+ // security checked before bugfix so "fix auth vulnerability" classifies as security
42
+ security: /\b(auth(?:entication|orization)?|security|secret|password|rbac|csrf|xss|sql injection|vulnerability|privilege|injection)\b/i,
43
+ bugfix: /\b(fix|bug|broken|regression|error|crash|fail|patch)\b/i,
44
+ refactor: /\b(refactor|cleanup|clean up|restructure|rename|reorganize|simplify)\b/i,
45
+ feature: /\b(add|implement|build|create|new feature|support)\b/i,
46
+ explore: /\b(understand|explore|survey|overview|what does|explain|analyze)\b/i,
47
+ };
48
+
49
+ function classifyTask(taskText) {
50
+ if (!taskText) return "unknown";
51
+ for (const [kind, re] of Object.entries(TASK_PATTERNS)) {
52
+ if (re.test(taskText)) return kind;
53
+ }
54
+ return "unknown";
55
+ }
56
+
57
+ // ── Strategy selection ────────────────────────────────────────────────────────
58
+
59
+ function selectContextStrategy(cwd, taskText, repoMap, executionPacket, options = {}) {
60
+ const taskKind = classifyTask(taskText);
61
+ const budget = options.contextBudget || DEFAULT_CONTEXT_BUDGET_TOKENS;
62
+
63
+ const strategy = {
64
+ taskKind,
65
+ useProjectProfile: true,
66
+ useRepoMap: true,
67
+ usePackageScope: false,
68
+ useGitDiff: false,
69
+ useFileSlices: false,
70
+ useFullFiles: false,
71
+ requiresApproval: false,
72
+ conservative: false,
73
+ confidence: "medium",
74
+ qualityRisks: [],
75
+ nextAction: null,
76
+ };
77
+
78
+ // Docs task: profile + repo map only, no source
79
+ if (taskKind === "docs") {
80
+ strategy.useFileSlices = false;
81
+ strategy.useFullFiles = false;
82
+ strategy.confidence = "high";
83
+ strategy.nextAction = "Load project profile and repo map; read only doc files.";
84
+ return strategy;
85
+ }
86
+
87
+ // Security task: caution + approval
88
+ if (taskKind === "security") {
89
+ strategy.requiresApproval = true;
90
+ strategy.useFileSlices = true;
91
+ strategy.qualityRisks.push("security_sensitive: do not compress security-critical config");
92
+ strategy.confidence = "low";
93
+ strategy.nextAction = "Review selected files carefully. Approval required before proceeding.";
94
+ return strategy;
95
+ }
96
+
97
+ // Explore task: repo map first, no full dump
98
+ if (taskKind === "explore") {
99
+ strategy.useRepoMap = true;
100
+ strategy.useFileSlices = false;
101
+ strategy.useFullFiles = false;
102
+ strategy.confidence = "medium";
103
+ strategy.nextAction = "Use repo map for overview. Load slices only if specific question requires it.";
104
+ return strategy;
105
+ }
106
+
107
+ // Bug fix: repo map + slices + git diff
108
+ if (taskKind === "bugfix") {
109
+ strategy.useGitDiff = true;
110
+ strategy.useFileSlices = true;
111
+ strategy.confidence = "medium";
112
+ strategy.nextAction = "Load git diff first. Select likely relevant file slices.";
113
+ return strategy;
114
+ }
115
+
116
+ // Test / refactor / feature: slices with optional full file fallback
117
+ if (["test", "refactor", "feature"].includes(taskKind)) {
118
+ strategy.useFileSlices = true;
119
+ strategy.useFullFiles = false; // only if slice insufficient
120
+ strategy.confidence = "medium";
121
+ strategy.nextAction = "Select relevant file slices via repo map symbols.";
122
+ return strategy;
123
+ }
124
+
125
+ // Unknown: conservative
126
+ strategy.conservative = true;
127
+ strategy.useFileSlices = false;
128
+ strategy.confidence = "low";
129
+ strategy.qualityRisks.push("task_unclear: conservative context to avoid quality regression");
130
+ strategy.nextAction = "Task unclear. Use repo map only. Escalate if more context needed.";
131
+ return strategy;
132
+ }
133
+
134
+ // ── Plan builder ──────────────────────────────────────────────────────────────
135
+
136
+ function buildContextAcquisitionPlan(cwd, taskText, options = {}) {
137
+ const budget = options.contextBudget || DEFAULT_CONTEXT_BUDGET_TOKENS;
138
+
139
+ // Try to read repo map
140
+ let repoMap = null;
141
+ try {
142
+ const mapPath = path.join(cwd, ".claude/cco/orchestration/repo-map/latest-map.json");
143
+ if (fs.existsSync(mapPath)) {
144
+ repoMap = JSON.parse(fs.readFileSync(mapPath, "utf8"));
145
+ }
146
+ } catch {}
147
+
148
+ // Try to read execution packet
149
+ let executionPacket = null;
150
+ try {
151
+ const packetPath = path.join(cwd, ".claude/cco/orchestration/execution-packet/latest-packet.json");
152
+ if (fs.existsSync(packetPath)) {
153
+ executionPacket = JSON.parse(fs.readFileSync(packetPath, "utf8"));
154
+ }
155
+ } catch {}
156
+
157
+ const strategy = selectContextStrategy(cwd, taskText, repoMap, executionPacket, options);
158
+ const taskKind = strategy.taskKind;
159
+
160
+ // Select relevant files via repo map
161
+ let selectedFiles = [];
162
+ let selectedSymbols = [];
163
+ let fullFileFallbacks = [];
164
+
165
+ if (repoMap && strategy.useFileSlices) {
166
+ try {
167
+ const { selectLikelyRelevantFiles } = require("./repo-map");
168
+ const relevant = selectLikelyRelevantFiles(cwd, taskText, repoMap, options);
169
+ selectedFiles = relevant.slice(0, 10).map((f) => ({ path: f.path, reason: "relevance_score", estimatedTokens: f.estimatedTokens }));
170
+ selectedSymbols = relevant.flatMap((f) => (f.symbols || []).slice(0, 3)).slice(0, 20);
171
+ } catch {}
172
+ }
173
+
174
+ // Slices are symbolic; actual slicing happens in execution
175
+ const slices = strategy.useFileSlices ? selectedFiles.map((f) => ({ path: f.path, type: "slice" })) : [];
176
+
177
+ // Full file fallbacks: only listed if necessary
178
+ if (strategy.useFullFiles) {
179
+ for (const f of selectedFiles.slice(0, 3)) {
180
+ if (f.estimatedTokens && f.estimatedTokens < 2000) {
181
+ fullFileFallbacks.push({ path: f.path, reason: "small_file_full_read_safe" });
182
+ }
183
+ }
184
+ }
185
+
186
+ const contextBudget = {
187
+ targetTokens: budget,
188
+ maxTokens: MAX_CONTEXT_BUDGET_TOKENS,
189
+ estimatedSelected: selectedFiles.reduce((s, f) => s + (f.estimatedTokens || 0), 0),
190
+ withinBudget: selectedFiles.reduce((s, f) => s + (f.estimatedTokens || 0), 0) < budget,
191
+ };
192
+
193
+ const excluded = [];
194
+ if (taskKind === "docs") excluded.push({ reason: "docs_task_no_source_context_needed" });
195
+ if (strategy.conservative) excluded.push({ reason: "unknown_task_conservative" });
196
+ if (repoMap && repoMap.sensitiveExcluded > 0) excluded.push({ reason: "sensitive_files_excluded", count: repoMap.sensitiveExcluded });
197
+
198
+ return {
199
+ contract: CONTRACT,
200
+ schemaVersion: SCHEMA_VERSION,
201
+ createdAt: nowIso(),
202
+ taskSummary: taskText ? taskText.slice(0, 200) : null,
203
+ taskKind,
204
+ strategy: {
205
+ useProjectProfile: strategy.useProjectProfile,
206
+ useRepoMap: strategy.useRepoMap,
207
+ useGitDiff: strategy.useGitDiff,
208
+ useFileSlices: strategy.useFileSlices,
209
+ useFullFiles: strategy.useFullFiles,
210
+ requiresApproval: strategy.requiresApproval,
211
+ conservative: strategy.conservative,
212
+ },
213
+ selectedFiles,
214
+ selectedSymbols,
215
+ slices,
216
+ excluded,
217
+ fullFileFallbacks,
218
+ contextBudget,
219
+ confidence: strategy.confidence,
220
+ qualityRisks: strategy.qualityRisks,
221
+ nextAction: strategy.nextAction,
222
+ redacted: true,
223
+ };
224
+ }
225
+
226
+ function writeContextAcquisitionPlan(cwd, plan) {
227
+ const dir = path.join(cwd, PLAN_DIR_REL);
228
+ fs.mkdirSync(dir, { recursive: true });
229
+ const abs = path.join(cwd, LATEST_PLAN_REL);
230
+ fs.writeFileSync(abs, JSON.stringify(plan, null, 2), "utf8");
231
+ try {
232
+ appendProductLearningEvent(cwd, "context_acquisition_plan_generated", {
233
+ taskKind: plan.taskKind,
234
+ confidence: plan.confidence,
235
+ selectedFiles: plan.selectedFiles.length,
236
+ withinBudget: plan.contextBudget.withinBudget,
237
+ requiresApproval: plan.strategy.requiresApproval,
238
+ });
239
+ } catch {}
240
+ return abs;
241
+ }
242
+
243
+ function buildContextPlanSurface(cwd, options = {}) {
244
+ const abs = path.join(cwd, LATEST_PLAN_REL);
245
+ if (!fs.existsSync(abs)) {
246
+ return { status: "not_available", artifactPath: LATEST_PLAN_REL };
247
+ }
248
+ try {
249
+ const plan = JSON.parse(fs.readFileSync(abs, "utf8"));
250
+ return {
251
+ status: "available",
252
+ taskKind: plan.taskKind,
253
+ confidence: plan.confidence,
254
+ selectedFiles: plan.selectedFiles.length,
255
+ withinBudget: plan.contextBudget.withinBudget,
256
+ artifactPath: LATEST_PLAN_REL,
257
+ };
258
+ } catch {
259
+ return { status: "error", artifactPath: LATEST_PLAN_REL };
260
+ }
261
+ }
262
+
263
+ function formatContextPlanText(plan, options = {}) {
264
+ const lines = [`Context Acquisition Plan [${plan.taskKind}]`];
265
+ lines.push(` Confidence: ${plan.confidence} | Approval required: ${plan.strategy.requiresApproval}`);
266
+ lines.push(` Strategy: profile=${plan.strategy.useProjectProfile} repoMap=${plan.strategy.useRepoMap} gitDiff=${plan.strategy.useGitDiff} slices=${plan.strategy.useFileSlices} fullFile=${plan.strategy.useFullFiles}`);
267
+ if (plan.selectedFiles.length) {
268
+ lines.push(` Selected files (${plan.selectedFiles.length}): ${plan.selectedFiles.slice(0, 5).map((f) => f.path).join(", ")}`);
269
+ }
270
+ lines.push(` Budget: ~${plan.contextBudget.estimatedSelected}/${plan.contextBudget.targetTokens} tokens (${plan.contextBudget.withinBudget ? "within" : "over"} budget)`);
271
+ if (plan.qualityRisks.length) lines.push(` Quality risks: ${plan.qualityRisks.join("; ")}`);
272
+ if (plan.nextAction) lines.push(` Next: ${plan.nextAction}`);
273
+ return lines.join("\n");
274
+ }
275
+
276
+ module.exports = {
277
+ CONTRACT,
278
+ SCHEMA_VERSION,
279
+ PLAN_DIR_REL,
280
+ LATEST_PLAN_REL,
281
+ classifyTask,
282
+ selectContextStrategy,
283
+ buildContextAcquisitionPlan,
284
+ writeContextAcquisitionPlan,
285
+ buildContextPlanSurface,
286
+ formatContextPlanText,
287
+ };
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+
6
+ const BUDGET_REL = ".claude/cco/state/context-budget.json";
7
+
8
+ const DEFAULT_BUDGETS = {
9
+ "cost-saver": {
10
+ maxContextChars: 40000,
11
+ warnAtPercent: 70,
12
+ blockAtPercent: 90,
13
+ maxToolOutputBytes: 12000,
14
+ maxSingleFileBytes: 20000,
15
+ },
16
+ balanced: {
17
+ maxContextChars: 80000,
18
+ warnAtPercent: 70,
19
+ blockAtPercent: 90,
20
+ maxToolOutputBytes: 24000,
21
+ maxSingleFileBytes: 40000,
22
+ },
23
+ quality: {
24
+ maxContextChars: 160000,
25
+ warnAtPercent: 75,
26
+ blockAtPercent: 92,
27
+ maxToolOutputBytes: 48000,
28
+ maxSingleFileBytes: 80000,
29
+ },
30
+ };
31
+
32
+ const DEFAULT_PROFILE = "balanced";
33
+
34
+ function getBudgetForProfile(profile) {
35
+ return DEFAULT_BUDGETS[profile] || DEFAULT_BUDGETS[DEFAULT_PROFILE];
36
+ }
37
+
38
+ function normalizeBudgetConfig(raw) {
39
+ if (!raw || raw.enabled === false) {
40
+ return { enabled: false };
41
+ }
42
+ const rawProfile = String(raw.profile || DEFAULT_PROFILE);
43
+ const profile = DEFAULT_BUDGETS[rawProfile] ? rawProfile : DEFAULT_PROFILE;
44
+ const base = DEFAULT_BUDGETS[profile];
45
+ return {
46
+ enabled: raw.enabled !== false,
47
+ profile,
48
+ maxContextChars: Number.isFinite(Number(raw.maxContextChars)) ? Math.max(8000, Number(raw.maxContextChars)) : base.maxContextChars,
49
+ warnAtPercent: Number.isFinite(Number(raw.warnAtPercent)) ? Math.min(99, Math.max(10, Number(raw.warnAtPercent))) : base.warnAtPercent,
50
+ blockAtPercent: Number.isFinite(Number(raw.blockAtPercent)) ? Math.min(100, Math.max(50, Number(raw.blockAtPercent))) : base.blockAtPercent,
51
+ maxToolOutputBytes: Number.isFinite(Number(raw.maxToolOutputBytes)) ? Number(raw.maxToolOutputBytes) : base.maxToolOutputBytes,
52
+ maxSingleFileBytes: Number.isFinite(Number(raw.maxSingleFileBytes)) ? Number(raw.maxSingleFileBytes) : base.maxSingleFileBytes,
53
+ };
54
+ }
55
+
56
+ function estimateChars(text) {
57
+ return typeof text === "string" ? text.length : String(text || "").length;
58
+ }
59
+
60
+ function computeBudgetState(usedChars, budget) {
61
+ const { maxContextChars, warnAtPercent, blockAtPercent } = budget;
62
+ const usedPercent = maxContextChars > 0 ? (usedChars / maxContextChars) * 100 : 0;
63
+ const isOverWarn = usedPercent >= warnAtPercent;
64
+ const isOverBlock = usedPercent >= blockAtPercent;
65
+ const remainingChars = Math.max(0, maxContextChars - usedChars);
66
+
67
+ let status = "ok";
68
+ if (isOverBlock) status = "critical";
69
+ else if (isOverWarn) status = "warn";
70
+
71
+ return {
72
+ usedChars,
73
+ maxContextChars,
74
+ usedPercent: Math.round(usedPercent * 10) / 10,
75
+ remainingChars,
76
+ status,
77
+ warnAtPercent,
78
+ blockAtPercent,
79
+ };
80
+ }
81
+
82
+ function checkToolOutputSize(outputText, budget) {
83
+ const bytes = Buffer.byteLength(String(outputText || ""), "utf8");
84
+ const isOversized = bytes > budget.maxToolOutputBytes;
85
+ return {
86
+ bytes,
87
+ maxBytes: budget.maxToolOutputBytes,
88
+ isOversized,
89
+ truncateHint: isOversized ? `Output exceeds ${budget.maxToolOutputBytes} bytes. Store out-of-band and reference by path.` : null,
90
+ };
91
+ }
92
+
93
+ function checkSingleFileSize(fileSizeBytes, budget) {
94
+ const isOversized = fileSizeBytes > budget.maxSingleFileBytes;
95
+ return {
96
+ bytes: fileSizeBytes,
97
+ maxBytes: budget.maxSingleFileBytes,
98
+ isOversized,
99
+ truncateHint: isOversized ? `File exceeds ${budget.maxSingleFileBytes} bytes. Read in chunks or summarize.` : null,
100
+ };
101
+ }
102
+
103
+ function loadBudgetState(cwd) {
104
+ try {
105
+ const p = path.join(cwd, BUDGET_REL);
106
+ return JSON.parse(fs.readFileSync(p, "utf8"));
107
+ } catch {
108
+ return null;
109
+ }
110
+ }
111
+
112
+ function saveBudgetState(cwd, state) {
113
+ const p = path.join(cwd, BUDGET_REL);
114
+ fs.mkdirSync(path.dirname(p), { recursive: true });
115
+ fs.writeFileSync(p, JSON.stringify(state, null, 2), "utf8");
116
+ }
117
+
118
+ function buildBudgetStatus(cwd, rawBudgetConfig) {
119
+ const budget = normalizeBudgetConfig(rawBudgetConfig);
120
+ if (!budget.enabled) {
121
+ return { enabled: false, status: "off" };
122
+ }
123
+
124
+ const stored = loadBudgetState(cwd);
125
+ const usedChars = stored?.usedChars || 0;
126
+ const budgetState = computeBudgetState(usedChars, budget);
127
+
128
+ const warnings = [];
129
+ if (budgetState.status === "critical") {
130
+ warnings.push(`Context at ${budgetState.usedPercent}% of limit — consider /cco-optimize or summarize large outputs.`);
131
+ } else if (budgetState.status === "warn") {
132
+ warnings.push(`Context at ${budgetState.usedPercent}% of limit — approaching budget.`);
133
+ }
134
+
135
+ return {
136
+ enabled: true,
137
+ status: budgetState.status,
138
+ usedChars: budgetState.usedChars,
139
+ maxContextChars: budgetState.maxContextChars,
140
+ usedPercent: budgetState.usedPercent,
141
+ remainingChars: budgetState.remainingChars,
142
+ warnAtPercent: budgetState.warnAtPercent,
143
+ blockAtPercent: budgetState.blockAtPercent,
144
+ warnings,
145
+ budgetPath: BUDGET_REL,
146
+ };
147
+ }
148
+
149
+ function formatBudgetStatusText(budgetStatus) {
150
+ if (!budgetStatus || !budgetStatus.enabled || budgetStatus.status === "off") return null;
151
+ const { status, usedPercent, remainingChars, warnings } = budgetStatus;
152
+ if (status === "ok" && warnings.length === 0) return null;
153
+ const lines = [`Context budget: ${usedPercent}% used, ${remainingChars} chars remaining`];
154
+ warnings.forEach((w) => lines.push(` ⚠ ${w}`));
155
+ return lines.join("\n");
156
+ }
157
+
158
+ module.exports = {
159
+ DEFAULT_BUDGETS,
160
+ getBudgetForProfile,
161
+ normalizeBudgetConfig,
162
+ estimateChars,
163
+ computeBudgetState,
164
+ checkToolOutputSize,
165
+ checkSingleFileSize,
166
+ loadBudgetState,
167
+ saveBudgetState,
168
+ buildBudgetStatus,
169
+ formatBudgetStatusText,
170
+ };