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,180 @@
1
+ "use strict";
2
+
3
+ function numberOrNull(value) {
4
+ const number = Number(value);
5
+ return Number.isFinite(number) ? number : null;
6
+ }
7
+
8
+ function preferred(value, fallback = null) {
9
+ if (value === undefined || value === null || value === "") return fallback;
10
+ return value;
11
+ }
12
+
13
+ function inferExecutionPath(input = {}) {
14
+ const existing = objectOrNull(input.attribution) || {};
15
+ const explicit = preferred(input.executionPath ?? existing.executionPath ?? input.meta?.executionPath, null);
16
+ if (explicit) return String(explicit);
17
+
18
+ const tool = String(input.tool || "");
19
+ const event = String(input.event || "");
20
+ const meta = input.meta || {};
21
+ const hasModelEvidence =
22
+ preferred(input.provider ?? existing.provider ?? meta.provider, null)
23
+ || preferred(input.model ?? existing.model ?? meta.model, null)
24
+ || numberOrNull(input.tokenUsage ?? existing.tokenUsage ?? meta.totalTokens ?? meta.tokenUsage) !== null
25
+ || numberOrNull(input.cost ?? existing.cost ?? meta.totalCost ?? meta.cost) !== null;
26
+ if (tool === "ccusage" || event === "CcusageSync" || event === "CloudSync") return "deterministic";
27
+ if (tool === "Bash" || tool === "Read" || tool === "Grep" || tool === "Glob" || tool === "Write" || tool === "Edit") return "deterministic";
28
+ if (event === "BatchJob") return "llm";
29
+ if (tool === "Agent" || hasModelEvidence) return "llm";
30
+ return null;
31
+ }
32
+
33
+ function objectOrNull(value) {
34
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
35
+ }
36
+
37
+ function pickFromCandidates(candidates, keys, transform = (value) => value) {
38
+ for (const candidate of candidates) {
39
+ const obj = objectOrNull(candidate);
40
+ if (!obj) continue;
41
+ for (const key of keys) {
42
+ if (!(key in obj)) continue;
43
+ const transformed = transform(obj[key]);
44
+ if (transformed !== null && transformed !== undefined && transformed !== "") return transformed;
45
+ }
46
+ }
47
+ return null;
48
+ }
49
+
50
+ function sumTokenParts(candidates) {
51
+ let sawAny = false;
52
+ let total = 0;
53
+ for (const candidate of candidates) {
54
+ const obj = objectOrNull(candidate);
55
+ if (!obj) continue;
56
+ const inputTokens = numberOrNull(obj.inputTokens ?? obj.input_tokens ?? obj.promptTokens ?? obj.prompt_tokens);
57
+ const outputTokens = numberOrNull(obj.outputTokens ?? obj.output_tokens ?? obj.completionTokens ?? obj.completion_tokens);
58
+ if (inputTokens !== null) {
59
+ sawAny = true;
60
+ total += inputTokens;
61
+ }
62
+ if (outputTokens !== null) {
63
+ sawAny = true;
64
+ total += outputTokens;
65
+ }
66
+ }
67
+ return sawAny ? total : null;
68
+ }
69
+
70
+ function extractRuntimeEvidence(params = {}) {
71
+ const toolInput = objectOrNull(params.toolInput) || {};
72
+ const toolResponse = objectOrNull(params.toolResponse) || {};
73
+ const candidates = [
74
+ params,
75
+ toolInput,
76
+ toolInput.metadata,
77
+ toolInput.usage,
78
+ toolResponse,
79
+ toolResponse.metadata,
80
+ toolResponse.metrics,
81
+ toolResponse.usage,
82
+ toolResponse.result,
83
+ objectOrNull(toolResponse.result)?.metadata,
84
+ objectOrNull(toolResponse.result)?.metrics,
85
+ objectOrNull(toolResponse.result)?.usage,
86
+ toolResponse.response,
87
+ objectOrNull(toolResponse.response)?.metadata,
88
+ objectOrNull(toolResponse.response)?.metrics,
89
+ objectOrNull(toolResponse.response)?.usage,
90
+ toolResponse.data,
91
+ objectOrNull(toolResponse.data)?.metadata,
92
+ objectOrNull(toolResponse.data)?.metrics,
93
+ objectOrNull(toolResponse.data)?.usage,
94
+ ].filter(Boolean);
95
+
96
+ const provider = pickFromCandidates(candidates, ["provider", "providerName", "provider_name"], (value) => preferred(value, null));
97
+ const model = pickFromCandidates(candidates, ["model", "modelName", "model_name"], (value) => preferred(value, null));
98
+ const tokenUsage =
99
+ pickFromCandidates(
100
+ candidates,
101
+ ["tokenUsage", "token_usage", "totalTokens", "total_tokens", "tokens"],
102
+ (value) => numberOrNull(value)
103
+ ) ?? sumTokenParts(candidates);
104
+ const cost = pickFromCandidates(
105
+ candidates,
106
+ ["cost", "totalCost", "total_cost", "measuredCost", "measured_cost"],
107
+ (value) => numberOrNull(value)
108
+ );
109
+ const latencyMs = pickFromCandidates(
110
+ candidates,
111
+ ["latencyMs", "latency_ms", "durationMs", "duration_ms", "elapsedMs", "elapsed_ms"],
112
+ (value) => numberOrNull(value)
113
+ );
114
+
115
+ if ([provider, model, tokenUsage, cost, latencyMs].every((value) => value === null)) return null;
116
+
117
+ return {
118
+ provider: provider ? String(provider) : null,
119
+ model: model ? String(model) : null,
120
+ tokenUsage,
121
+ cost,
122
+ costSource: cost !== null && cost > 0 ? "measured" : "unavailable",
123
+ latencyMs,
124
+ };
125
+ }
126
+
127
+ function normalizeAttribution(input = {}) {
128
+ const existing = objectOrNull(input.attribution) || {};
129
+ const meta = input.meta || {};
130
+ const provider = preferred(input.provider ?? existing.provider ?? meta.provider, null);
131
+ const model = preferred(input.model ?? existing.model ?? meta.model, null);
132
+ const tokenUsage = numberOrNull(input.tokenUsage ?? existing.tokenUsage ?? meta.totalTokens ?? meta.tokenUsage);
133
+ const cost = numberOrNull(input.cost ?? existing.cost ?? meta.totalCost ?? meta.cost);
134
+ const latencyMs = numberOrNull(input.latencyMs ?? existing.latencyMs ?? meta.latencyMs);
135
+
136
+ const attribution = {
137
+ sessionId: preferred(input.sessionId ?? existing.sessionId, "unknown"),
138
+ featureId: preferred(input.featureId ?? existing.featureId ?? meta.featureId, null),
139
+ componentId: preferred(input.componentId ?? existing.componentId ?? meta.componentId ?? input.event, null),
140
+ workerId: preferred(input.workerId ?? existing.workerId ?? meta.workerId, null),
141
+ routeId: preferred(input.routeId ?? existing.routeId ?? meta.routeId, null),
142
+ stepId: preferred(input.stepId ?? existing.stepId ?? meta.stepId, null),
143
+ actionType: preferred(input.actionType ?? existing.actionType ?? meta.actionType ?? input.action, null),
144
+ executionPath: inferExecutionPath(input),
145
+ provider: provider ? String(provider) : null,
146
+ model: model ? String(model) : null,
147
+ tokenUsage,
148
+ cost,
149
+ costSource: cost !== null && cost > 0 ? "measured" : preferred(input.costSource ?? existing.costSource ?? meta.costSource, "unavailable"),
150
+ latencyMs,
151
+ status: preferred(input.status ?? existing.status ?? meta.status ?? input.action, null),
152
+ timestamp: preferred(input.timestamp ?? existing.timestamp ?? input.ts, null),
153
+ };
154
+
155
+ return Object.values(attribution).some((value) => value !== null && value !== undefined && value !== "")
156
+ ? attribution
157
+ : null;
158
+ }
159
+
160
+ function inferValueUnit(metric = {}, attribution = null, measured, counterfactual) {
161
+ if (attribution?.cost !== null && attribution?.cost !== undefined) return "currency";
162
+ if (attribution?.tokenUsage !== null && attribution?.tokenUsage !== undefined) return "tokens";
163
+
164
+ const meta = metric.meta || {};
165
+ if (numberOrNull(meta.totalCost) !== null) return "currency";
166
+ if (numberOrNull(meta.totalTokens) !== null) return "tokens";
167
+ if (numberOrNull(meta.bytes) !== null) return "bytes";
168
+
169
+ const numericMeasured = numberOrNull(measured);
170
+ const numericCounterfactual = numberOrNull(counterfactual);
171
+ if (metric.category === "loop_detected" && (numericMeasured !== null || numericCounterfactual !== null)) return "minutes";
172
+ if (numericMeasured !== null || numericCounterfactual !== null) return "count";
173
+ return "unknown";
174
+ }
175
+
176
+ module.exports = {
177
+ normalizeAttribution,
178
+ inferValueUnit,
179
+ extractRuntimeEvidence,
180
+ };
@@ -0,0 +1,289 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+
6
+ const SEVERITY_WEIGHT = {
7
+ high: 3,
8
+ medium: 2,
9
+ low: 1,
10
+ };
11
+
12
+ const REASON_MAP = {
13
+ SEC_PRIVATE_KEY: {
14
+ title: "Remove embedded private key material",
15
+ suggestedFix: "Delete key material from repository files, rotate the key, and move secrets to secure storage.",
16
+ actionPath: "security -> secret rotation -> /cco-audit",
17
+ },
18
+ SEC_GH_TOKEN: {
19
+ title: "Rotate exposed GitHub token",
20
+ suggestedFix: "Remove token-like values, rotate credentials, and enforce local secret scanning before commit.",
21
+ actionPath: "security -> credential hygiene -> /cco-audit",
22
+ },
23
+ SEC_CURL_PIPE_SH: {
24
+ title: "Eliminate remote shell piping",
25
+ suggestedFix: "Download scripts first, verify checksum/signature, then run from reviewed local files.",
26
+ actionPath: "security -> safe execution defaults -> /cco-remediate",
27
+ },
28
+ SEC_REMOTE_FETCH: {
29
+ title: "Pin and verify remote fetch sources",
30
+ suggestedFix: "Restrict remote fetches to trusted pinned sources and verify checksums before use.",
31
+ actionPath: "security -> fetch controls -> /cco-remediate",
32
+ },
33
+ SEC_REMOTE_INCLUDE: {
34
+ title: "Replace remote includes with local pinned artifacts",
35
+ suggestedFix: "Vendor includes locally or pin immutable references and require explicit review.",
36
+ actionPath: "security -> include controls -> /cco-remediate",
37
+ },
38
+ SEC_UNPINNED_GIT_DEP: {
39
+ title: "Pin git dependencies",
40
+ suggestedFix: "Use immutable commit SHAs instead of floating refs (main/master/latest).",
41
+ actionPath: "security -> dependency pinning -> /cco-remediate",
42
+ },
43
+ SEC_OVERBROAD_PERMISSION: {
44
+ title: "Tighten permission budget",
45
+ suggestedFix: "Replace broad permissions with least-privilege scope for skills and agents.",
46
+ actionPath: "security -> permission budget -> /cco-settings scope=team",
47
+ },
48
+ SEC_BIDI_UNICODE: {
49
+ title: "Remove bidirectional unicode control characters",
50
+ suggestedFix: "Replace hidden directional characters and review suspicious text blocks.",
51
+ actionPath: "security -> content integrity -> /cco-audit",
52
+ },
53
+ };
54
+
55
+ function clamp(n, min, max) {
56
+ return Math.max(min, Math.min(max, n));
57
+ }
58
+
59
+ function severityRank(sev) {
60
+ if (sev === "high") return 3;
61
+ if (sev === "medium") return 2;
62
+ return 1;
63
+ }
64
+
65
+ function scoreFinding(finding, recencyWeight) {
66
+ const sevWeight = SEVERITY_WEIGHT[finding.severity] || 1;
67
+ const confidence = Number.isFinite(Number(finding.confidence)) ? Number(finding.confidence) : 0.5;
68
+ const recency = Number.isFinite(Number(recencyWeight)) ? Number(recencyWeight) : 1;
69
+ return Number((sevWeight * confidence * recency).toFixed(6));
70
+ }
71
+
72
+ function loadJson(absPath, fallback = null) {
73
+ try {
74
+ return JSON.parse(fs.readFileSync(absPath, "utf8"));
75
+ } catch {
76
+ return fallback;
77
+ }
78
+ }
79
+
80
+ function listJsonArtifacts(absDir, filterFn = null) {
81
+ try {
82
+ return fs
83
+ .readdirSync(absDir)
84
+ .filter((name) => name.endsWith(".json"))
85
+ .filter((name) => (typeof filterFn === "function" ? filterFn(name) : true))
86
+ .map((name) => {
87
+ const absPath = path.join(absDir, name);
88
+ const st = fs.statSync(absPath);
89
+ return {
90
+ name,
91
+ absPath,
92
+ relPath: path.posix.join(path.relative(process.cwd(), absDir).replace(/\\/g, "/"), name),
93
+ mtimeMs: st.mtimeMs,
94
+ data: loadJson(absPath, {}),
95
+ };
96
+ })
97
+ .sort((a, b) => b.mtimeMs - a.mtimeMs);
98
+ } catch {
99
+ return [];
100
+ }
101
+ }
102
+
103
+ function reasonDetails(reasonCode) {
104
+ return (
105
+ REASON_MAP[reasonCode] || {
106
+ title: `Review finding ${reasonCode}`,
107
+ suggestedFix: "Review the highlighted line, apply least-privilege changes, and re-run /cco-audit.",
108
+ actionPath: "security -> /cco-audit",
109
+ }
110
+ );
111
+ }
112
+
113
+ function remediationJobByReason(cwd) {
114
+ const jobsDir = path.join(cwd, ".claude", "cco", "patches", "jobs");
115
+ const jobs = listJsonArtifacts(jobsDir);
116
+ const map = new Map();
117
+
118
+ jobs.forEach((jobArt) => {
119
+ const patches = Array.isArray(jobArt.data?.patches) ? jobArt.data.patches : [];
120
+ patches.forEach((patch) => {
121
+ const reasonCode = String(patch.reasonCode || "");
122
+ if (!reasonCode) return;
123
+ if (!map.has(reasonCode)) {
124
+ map.set(reasonCode, patch.path || jobArt.relPath);
125
+ }
126
+ });
127
+ });
128
+
129
+ return map;
130
+ }
131
+
132
+ function normalizeHighlightsFromArtifacts(artifacts, cwd) {
133
+ const jobMap = remediationJobByReason(cwd);
134
+ const normalized = [];
135
+
136
+ artifacts.forEach((artifact, idx) => {
137
+ const highlights = Array.isArray(artifact.data?.highlights) ? artifact.data.highlights : [];
138
+ const artifactRecencyWeight = clamp(1 - idx * 0.03, 0.7, 1);
139
+
140
+ highlights.forEach((h, hIdx) => {
141
+ const reasonCode = String(h.id || "SEC_UNKNOWN");
142
+ const severity = ["high", "medium", "low"].includes(String(h.severity || "")) ? String(h.severity) : "low";
143
+ const confidence = Number.isFinite(Number(h.confidence)) ? Number(h.confidence) : 0.5;
144
+ const suppressed = h.suppressed === true;
145
+ const details = reasonDetails(reasonCode);
146
+ const normalizedFinding = {
147
+ findingId: `${artifact.name}:${hIdx + 1}:${reasonCode}`,
148
+ reasonCode,
149
+ severity,
150
+ confidence,
151
+ rationale: String(h.rationale || "No rationale provided."),
152
+ path: String(h.path || artifact.relPath),
153
+ line: Number.isFinite(Number(h.line)) ? Number(h.line) : 1,
154
+ snippet: String(h.snippet || ""),
155
+ suppressed,
156
+ sourceArtifact: artifact.relPath,
157
+ suggestedFix: details.suggestedFix,
158
+ actionPath: details.actionPath,
159
+ jobPath: jobMap.get(reasonCode) || null,
160
+ recencyWeight: artifactRecencyWeight,
161
+ };
162
+ normalizedFinding.rankScore = scoreFinding(normalizedFinding, artifactRecencyWeight);
163
+ normalized.push(normalizedFinding);
164
+ });
165
+ });
166
+
167
+ return normalized;
168
+ }
169
+
170
+ function compareFindings(a, b) {
171
+ if (b.rankScore !== a.rankScore) return b.rankScore - a.rankScore;
172
+ if (severityRank(b.severity) !== severityRank(a.severity)) return severityRank(b.severity) - severityRank(a.severity);
173
+ if (b.confidence !== a.confidence) return b.confidence - a.confidence;
174
+ if (a.reasonCode !== b.reasonCode) return a.reasonCode.localeCompare(b.reasonCode);
175
+ if (a.path !== b.path) return a.path.localeCompare(b.path);
176
+ return a.line - b.line;
177
+ }
178
+
179
+ function topReasonCodesFromFindings(findings, limit = 5) {
180
+ const counts = {};
181
+ findings.forEach((f) => {
182
+ counts[f.reasonCode] = (counts[f.reasonCode] || 0) + 1;
183
+ });
184
+
185
+ return Object.keys(counts)
186
+ .sort((a, b) => {
187
+ if (counts[b] !== counts[a]) return counts[b] - counts[a];
188
+ return a.localeCompare(b);
189
+ })
190
+ .slice(0, limit);
191
+ }
192
+
193
+ function riskLevelFromFindings(activeFindings) {
194
+ if (!activeFindings.length) return "low";
195
+ if (activeFindings.some((f) => f.severity === "high")) return "high";
196
+ if (activeFindings.some((f) => f.severity === "medium")) return "medium";
197
+ return "low";
198
+ }
199
+
200
+ function toTopFindingView(finding) {
201
+ return {
202
+ reasonCode: finding.reasonCode,
203
+ severity: finding.severity,
204
+ confidence: finding.confidence,
205
+ rationale: finding.rationale,
206
+ path: finding.path,
207
+ line: finding.line,
208
+ suggestedFix: finding.suggestedFix,
209
+ actionPath: finding.actionPath,
210
+ jobPath: finding.jobPath,
211
+ };
212
+ }
213
+
214
+ function toTopRecommendedFixes(activeFindings, limit = 5) {
215
+ const seen = new Set();
216
+ const out = [];
217
+
218
+ activeFindings.forEach((f) => {
219
+ if (seen.has(f.reasonCode)) return;
220
+ seen.add(f.reasonCode);
221
+ out.push({
222
+ reasonCode: f.reasonCode,
223
+ title: reasonDetails(f.reasonCode).title,
224
+ text: f.suggestedFix,
225
+ confidence: f.confidence,
226
+ actionPath: f.actionPath,
227
+ jobPath: f.jobPath,
228
+ });
229
+ });
230
+
231
+ return out.slice(0, limit);
232
+ }
233
+
234
+ function computeNextBestAction(activeFindings) {
235
+ if (!activeFindings.length) {
236
+ return "No active security findings. Keep current defaults and run /cco-audit after meaningful code or skill changes.";
237
+ }
238
+
239
+ const top = activeFindings[0];
240
+ if (top.severity === "high") {
241
+ return `Address high-risk finding ${top.reasonCode} first, apply manual remediation, then re-run /cco-audit.`;
242
+ }
243
+ return `Address ${top.reasonCode} next, then run /cco-audit to verify reduced risk.`;
244
+ }
245
+
246
+ function buildAuditSummary(cwd) {
247
+ const securityDir = path.join(cwd, ".claude", "cco", "security");
248
+ const securityArtifacts = listJsonArtifacts(
249
+ securityDir,
250
+ (name) => name.endsWith("-file-scan.json") || name.endsWith("-prerun-scan.json") || name.endsWith("-scan.json")
251
+ );
252
+
253
+ const normalized = normalizeHighlightsFromArtifacts(securityArtifacts, cwd).sort(compareFindings);
254
+ const activeFindings = normalized.filter((f) => !f.suppressed);
255
+ const suppressedFindings = normalized.filter((f) => f.suppressed);
256
+
257
+ const topFindings = activeFindings.slice(0, 5).map(toTopFindingView);
258
+ const topRecommendedFixes = toTopRecommendedFixes(activeFindings, 5);
259
+ const topReasonCodes = topReasonCodesFromFindings(activeFindings, 5);
260
+
261
+ const summary = {
262
+ riskLevel: riskLevelFromFindings(activeFindings),
263
+ activeFindings: activeFindings.length,
264
+ suppressedFindings: suppressedFindings.length,
265
+ topReasonCodes,
266
+ };
267
+
268
+ return {
269
+ auditVersion: 1,
270
+ what: "Security audit completed.",
271
+ why: "Shows deterministic high-signal security findings and manual remediation suggestions for this repo.",
272
+ summary,
273
+ topFindings,
274
+ topRecommendedFixes,
275
+ nextBestAction: computeNextBestAction(activeFindings),
276
+ attribution: {
277
+ foundBy: "Skill Security Scout",
278
+ builtBy: "Security Engineer",
279
+ designedBy: "UX Lead",
280
+ },
281
+ };
282
+ }
283
+
284
+ module.exports = {
285
+ buildAuditSummary,
286
+ topReasonCodesFromFindings,
287
+ riskLevelFromFindings,
288
+ compareFindings,
289
+ };