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,403 @@
1
+ "use strict";
2
+
3
+ const { isSafeTestCommand } = require("./action-evaluator");
4
+ const { classifyFilesystemWriteTarget } = require("./file-write-rules");
5
+ const { classifyPackageAction } = require("./package-action-rules");
6
+ const { classifyMcpAction } = require("./mcp-action-rules");
7
+
8
+ function normalizePath(value) {
9
+ return String(value || "").replace(/\\/g, "/");
10
+ }
11
+
12
+ function isSafeLimitedShellCommand(command) {
13
+ const normalized = String(command || "").trim().toLowerCase();
14
+ if (!normalized) return false;
15
+ if (/[|;&><]/.test(normalized)) return false;
16
+ if (isSafeTestCommand(normalized)) return true;
17
+ return /^(npm run lint\b|pnpm run lint\b|yarn lint\b|node (?:scripts|tools|tests)[\\/][a-z0-9._/-]+)\s*$/i.test(normalized);
18
+ }
19
+
20
+ function buildPermissionLimitPlan(input, options = {}) {
21
+ const evaluation = input?.evaluation || {};
22
+ const action = input?.action || {};
23
+ const componentCard = options.componentCard || null;
24
+ const target = normalizePath(action?.target || evaluation?.target || "");
25
+ const command = String(action?.command || evaluation?.command || "");
26
+
27
+ const plan = {
28
+ recommendedMode: "limited",
29
+ wouldAllow: [],
30
+ wouldLimit: [],
31
+ wouldDeny: [],
32
+ ttlRecommendation: "once",
33
+ scope: {
34
+ componentId: evaluation.componentId || componentCard?.id || null,
35
+ actionType: evaluation.actionType || action?.actionType || null,
36
+ target: target || null,
37
+ command: command || null,
38
+ },
39
+ reason: "Wuz will allow only the minimum needed for this task.",
40
+ };
41
+
42
+ if (/\b(curl|wget)\b[^\n]*\|\s*(bash|sh|pwsh|powershell)\b|\b(invoke-webrequest|invoke-restmethod|downloadstring)\b/i.test(command)) {
43
+ plan.recommendedMode = "deny";
44
+ plan.wouldDeny.push("remote script execution");
45
+ plan.ttlRecommendation = "none";
46
+ plan.reason = "Remote script execution should be denied.";
47
+ return plan;
48
+ }
49
+
50
+ if ((evaluation.actionType || action?.actionType).startsWith("mcp.")) {
51
+ const mcpProfile = classifyMcpAction({
52
+ ...action,
53
+ actionType: evaluation.actionType || action?.actionType || "mcp.unknown",
54
+ target,
55
+ mcpServer: action?.mcpServer || evaluation?.mcpServer || null,
56
+ toolName: action?.toolName || evaluation?.toolName || null,
57
+ args: action?.args,
58
+ resource: action?.resource,
59
+ });
60
+
61
+ plan.scope.mcpServer = mcpProfile.mcpServer;
62
+ plan.scope.toolName = mcpProfile.toolName;
63
+ plan.scope.repo = mcpProfile.repoScope || "current";
64
+ plan.scope.branchPrefix = mcpProfile.safeBranchPrefix ? "wuz/" : null;
65
+ plan.scope.allowedTargets = [mcpProfile.target || "requested-mcp-action"];
66
+
67
+ if (mcpProfile.deleteLike || mcpProfile.adminLike) {
68
+ plan.recommendedMode = "deny";
69
+ plan.wouldDeny.push("delete actions");
70
+ plan.wouldDeny.push("admin actions");
71
+ plan.ttlRecommendation = "none";
72
+ plan.reason = "Destructive external actions should be denied.";
73
+ return plan;
74
+ }
75
+
76
+ if (mcpProfile.secretLike) {
77
+ plan.recommendedMode = "deny";
78
+ plan.wouldDeny.push("secrets modification");
79
+ plan.ttlRecommendation = "none";
80
+ plan.reason = "Secrets modification should be denied.";
81
+ return plan;
82
+ }
83
+
84
+ if (mcpProfile.workflowLike) {
85
+ plan.recommendedMode = "deny";
86
+ plan.wouldDeny.push("workflow modification");
87
+ plan.ttlRecommendation = "none";
88
+ plan.reason = "Workflow changes should not be auto-limited through MCP.";
89
+ return plan;
90
+ }
91
+
92
+ if (mcpProfile.deployLike || mcpProfile.pushMainLike) {
93
+ plan.recommendedMode = "deny";
94
+ plan.wouldDeny.push("push to main or publish/deploy actions");
95
+ plan.ttlRecommendation = "none";
96
+ plan.reason = "Push-to-main, deploy, or publish actions should be denied by default.";
97
+ return plan;
98
+ }
99
+
100
+ if (mcpProfile.createPullRequest) {
101
+ plan.recommendedMode = "limited";
102
+ plan.wouldAllow.push("create pull request");
103
+ plan.wouldLimit.push("current repo only");
104
+ plan.wouldLimit.push("safe branch scope only");
105
+ plan.wouldDeny.push("delete actions");
106
+ plan.wouldDeny.push("push to main");
107
+ plan.wouldDeny.push("workflow or secrets modification");
108
+ plan.wouldDeny.push("workflow modification");
109
+ plan.wouldDeny.push("secrets modification");
110
+ plan.wouldDeny.push("admin actions");
111
+ plan.ttlRecommendation = "30 minutes";
112
+ plan.reason = "Wuz limited this MCP action to the current task.";
113
+ return plan;
114
+ }
115
+
116
+ if (mcpProfile.createIssue) {
117
+ plan.recommendedMode = "limited";
118
+ plan.wouldAllow.push("create or update issue");
119
+ plan.wouldLimit.push("current repo only");
120
+ plan.wouldDeny.push("delete actions");
121
+ plan.wouldDeny.push("workflow or secrets modification");
122
+ plan.wouldDeny.push("workflow modification");
123
+ plan.wouldDeny.push("secrets modification");
124
+ plan.wouldDeny.push("admin actions");
125
+ plan.ttlRecommendation = "30 minutes";
126
+ plan.reason = "Wuz limited this MCP action to the current task.";
127
+ return plan;
128
+ }
129
+
130
+ if (mcpProfile.readOnly) {
131
+ plan.recommendedMode = "limited";
132
+ plan.wouldAllow.push("read requested metadata");
133
+ plan.wouldLimit.push("current repo only");
134
+ plan.wouldDeny.push("external mutation");
135
+ plan.ttlRecommendation = "30 minutes";
136
+ plan.reason = "The action can stay within a small read-only MCP scope.";
137
+ return plan;
138
+ }
139
+
140
+ plan.recommendedMode = "approval-required";
141
+ plan.wouldAllow.push("run the exact requested MCP action if explicitly approved");
142
+ plan.wouldLimit.push("current repo only");
143
+ plan.wouldDeny.push("delete actions");
144
+ plan.wouldDeny.push("workflow modification");
145
+ plan.wouldDeny.push("secrets modification");
146
+ plan.ttlRecommendation = "once";
147
+ plan.reason = "Unknown external mutation should be reviewed before it is allowed.";
148
+ return plan;
149
+ }
150
+
151
+ if ((evaluation.actionType || action?.actionType) === "workflow.modify" || /(?:^|[\\/])\.github[\\/]workflows[\\/].+/i.test(target)) {
152
+ plan.recommendedMode = "approval-required";
153
+ plan.wouldAllow.push("modify only the requested workflow file if explicitly approved");
154
+ plan.wouldLimit.push("no broad .github write");
155
+ plan.wouldDeny.push("secrets changes");
156
+ plan.ttlRecommendation = "this session";
157
+ plan.scope.allowedPaths = [target || ".github/workflows/*"];
158
+ plan.reason = "Workflow changes should stay tightly scoped and session-bound.";
159
+ return plan;
160
+ }
161
+
162
+ if ((evaluation.actionType || action?.actionType) === "filesystem.write") {
163
+ const writeProfile = classifyFilesystemWriteTarget(action?.context?.cwd || process.cwd(), target);
164
+
165
+ if (!writeProfile.insideProject || writeProfile.pathTraversal) {
166
+ plan.recommendedMode = "deny";
167
+ plan.wouldDeny.push("path traversal or outside project root");
168
+ plan.ttlRecommendation = "none";
169
+ plan.reason = "Path outside project root is not allowed.";
170
+ return plan;
171
+ }
172
+
173
+ if (writeProfile.broadWrite) {
174
+ plan.recommendedMode = "deny";
175
+ plan.wouldDeny.push("broad or wildcard file writes");
176
+ plan.ttlRecommendation = "none";
177
+ plan.reason = "Broad project-wide writes are not allowed.";
178
+ return plan;
179
+ }
180
+
181
+ if (writeProfile.sensitive) {
182
+ plan.recommendedMode = "deny";
183
+ plan.wouldDeny.push("sensitive file access");
184
+ plan.ttlRecommendation = "none";
185
+ plan.reason = "Sensitive environment or credential files should not be modified through a limited plan.";
186
+ return plan;
187
+ }
188
+
189
+ if (writeProfile.workflow) {
190
+ plan.recommendedMode = "approval-required";
191
+ plan.wouldAllow.push("modify only the requested workflow file if explicitly approved");
192
+ plan.wouldLimit.push("no broad .github write");
193
+ plan.wouldDeny.push("workflow secrets changes");
194
+ plan.ttlRecommendation = "this session";
195
+ plan.scope.allowedPaths = [writeProfile.normalizedTarget || ".github/workflows/*"];
196
+ plan.reason = "Workflow files can execute code in CI and should require explicit approval.";
197
+ return plan;
198
+ }
199
+
200
+ if (writeProfile.packageFile || writeProfile.dockerFile || writeProfile.configFile || writeProfile.scriptFile) {
201
+ plan.recommendedMode = "approval-required";
202
+ plan.wouldAllow.push("write only the requested project file if explicitly approved");
203
+ plan.wouldLimit.push("no broad config or script changes");
204
+ plan.wouldDeny.push("sensitive file access");
205
+ plan.ttlRecommendation = "once";
206
+ plan.scope.allowedPaths = [writeProfile.normalizedTarget || "requested-path"];
207
+ plan.reason = "Config, package, Docker, and script writes need explicit review.";
208
+ return plan;
209
+ }
210
+
211
+ if (writeProfile.safeLimited) {
212
+ plan.recommendedMode = "limited";
213
+ plan.wouldAllow.push("write the exact requested file once");
214
+ plan.wouldLimit.push("current project only");
215
+ plan.wouldDeny.push("sensitive file access");
216
+ plan.wouldDeny.push("workflow modification");
217
+ plan.wouldDeny.push("path traversal");
218
+ plan.wouldDeny.push("outside project root");
219
+ plan.ttlRecommendation = writeProfile.testFile ? "this session" : "30 minutes";
220
+ plan.scope.allowedPaths = [writeProfile.normalizedTarget];
221
+ plan.reason = writeProfile.testFile
222
+ ? "Wuz limited this action to the current task."
223
+ : "Wuz limited this action to the current task.";
224
+ return plan;
225
+ }
226
+
227
+ plan.recommendedMode = "approval-required";
228
+ plan.wouldAllow.push("write only the requested project path if explicitly approved");
229
+ plan.wouldDeny.push("sensitive file access");
230
+ plan.wouldDeny.push("workflow modification");
231
+ plan.ttlRecommendation = "once";
232
+ plan.scope.allowedPaths = [writeProfile.normalizedTarget || "requested-path"];
233
+ plan.reason = "This file write should be reviewed before it is allowed.";
234
+ return plan;
235
+ }
236
+
237
+ if (
238
+ (evaluation.actionType || action?.actionType) === "package.install" ||
239
+ (evaluation.actionType || action?.actionType) === "package.run" ||
240
+ (evaluation.actionType || action?.actionType) === "package.script"
241
+ ) {
242
+ const packageProfile = classifyPackageAction({
243
+ ...action,
244
+ actionType: evaluation.actionType || action?.actionType || null,
245
+ command,
246
+ target,
247
+ });
248
+
249
+ plan.scope.packageManager = packageProfile.packageManager;
250
+ plan.scope.scriptName = packageProfile.scriptName !== "unknown" ? packageProfile.scriptName : null;
251
+ plan.scope.packageName = packageProfile.packageName || null;
252
+
253
+ if (
254
+ packageProfile.remoteScript ||
255
+ packageProfile.destructiveChain ||
256
+ packageProfile.globalInstall ||
257
+ packageProfile.unsafeFlags ||
258
+ packageProfile.installFromUrlOrPath ||
259
+ packageProfile.sensitiveTarget ||
260
+ packageProfile.workflowTarget
261
+ ) {
262
+ plan.recommendedMode = "deny";
263
+ plan.wouldDeny.push(packageProfile.reasons[0] || "unsafe package action");
264
+ plan.ttlRecommendation = "none";
265
+ plan.scope.allowedCommands = [];
266
+ plan.reason = packageProfile.reasons[0] || "No safe limited version found. Recommended: deny.";
267
+ return plan;
268
+ }
269
+
270
+ if (packageProfile.lifecycleRisk) {
271
+ plan.recommendedMode = "deny";
272
+ plan.wouldDeny.push("lifecycle script execution");
273
+ plan.ttlRecommendation = "none";
274
+ plan.scope.allowedCommands = [];
275
+ plan.reason = "Lifecycle scripts like preinstall, postinstall, or prepare require explicit approval.";
276
+ return plan;
277
+ }
278
+
279
+ if (packageProfile.safeTestOrLint) {
280
+ plan.recommendedMode = "limited";
281
+ plan.wouldAllow.push(
282
+ packageProfile.scriptName === "lint"
283
+ ? "run the exact lint command once"
284
+ : "run the exact test command once",
285
+ );
286
+ plan.wouldLimit.push("current project only");
287
+ plan.wouldDeny.push("remote script execution");
288
+ plan.wouldDeny.push("global install");
289
+ plan.wouldDeny.push("lifecycle script execution");
290
+ plan.wouldDeny.push("sensitive file access");
291
+ plan.ttlRecommendation = packageProfile.scriptName === "test" ? "this session" : "30 minutes";
292
+ plan.scope.allowedCommands = [command];
293
+ plan.reason = "Wuz limited this action to the current task.";
294
+ return plan;
295
+ }
296
+
297
+ if (packageProfile.specificPackageInstall && packageProfile.ignoreScripts) {
298
+ plan.recommendedMode = "limited";
299
+ plan.wouldAllow.push("install the exact requested package once");
300
+ plan.wouldLimit.push("current project only");
301
+ plan.wouldDeny.push("remote script execution");
302
+ plan.wouldDeny.push("global install");
303
+ plan.wouldDeny.push("lifecycle script execution");
304
+ plan.wouldDeny.push("sensitive file access");
305
+ plan.ttlRecommendation = "30 minutes";
306
+ plan.scope.allowedCommands = [command];
307
+ plan.reason = "Wuz limited this package install to the current project and exact requested dependency.";
308
+ return plan;
309
+ }
310
+
311
+ if (packageProfile.specificPackageInstall) {
312
+ plan.recommendedMode = "approval-required";
313
+ plan.wouldAllow.push("install the exact requested package if explicitly approved");
314
+ plan.wouldLimit.push("current project only");
315
+ plan.wouldDeny.push("global install");
316
+ plan.wouldDeny.push("lifecycle script execution");
317
+ plan.ttlRecommendation = "once";
318
+ plan.scope.allowedCommands = [command];
319
+ plan.reason = "Specific dependency installs should be reviewed before they are allowed.";
320
+ return plan;
321
+ }
322
+
323
+ plan.recommendedMode = "approval-required";
324
+ plan.wouldAllow.push("run the exact requested package command if explicitly approved");
325
+ plan.wouldLimit.push("current project only");
326
+ plan.wouldDeny.push("remote script execution");
327
+ plan.wouldDeny.push("global install");
328
+ plan.wouldDeny.push("lifecycle script execution");
329
+ plan.ttlRecommendation = "once";
330
+ plan.scope.allowedCommands = [command];
331
+ plan.reason = "This package action should be reviewed before it is allowed.";
332
+ return plan;
333
+ }
334
+
335
+ if ((evaluation.actionType || action?.actionType) === "shell.run") {
336
+ if (isSafeLimitedShellCommand(command)) {
337
+ plan.recommendedMode = "limited";
338
+ plan.wouldAllow.push(command.startsWith("npm run lint") || command.startsWith("pnpm run lint") || command.startsWith("yarn lint")
339
+ ? "run the requested lint command once"
340
+ : "run the requested command once");
341
+ plan.wouldLimit.push("current project only");
342
+ plan.wouldLimit.push("no network assumption");
343
+ plan.wouldDeny.push("remote script execution");
344
+ plan.wouldDeny.push("workflow modification");
345
+ plan.wouldDeny.push("sensitive file access");
346
+ plan.ttlRecommendation = "30 minutes";
347
+ plan.scope.allowedCommands = [command];
348
+ plan.scope.allowedPaths = ["current-project"];
349
+ plan.reason = "Wuz will limit this command to the current project and current task.";
350
+ return plan;
351
+ }
352
+ plan.recommendedMode = "deny";
353
+ plan.wouldDeny.push("no safe limited version for this shell command");
354
+ plan.ttlRecommendation = "none";
355
+ plan.scope.allowedCommands = [];
356
+ plan.reason = "No safe limited version found. Recommended: deny.";
357
+ return plan;
358
+ }
359
+
360
+ if ((evaluation.actionType || action?.actionType) === "network.call") {
361
+ plan.recommendedMode = "approval-required";
362
+ plan.wouldAllow.push("call only the requested remote target");
363
+ plan.wouldLimit.push("no credential export");
364
+ plan.wouldLimit.push("no follow-up write actions");
365
+ plan.ttlRecommendation = "once";
366
+ plan.scope.allowedTargets = [target || "requested-network-target"];
367
+ plan.reason = "Remote network access should be reviewed and narrowed to the requested target.";
368
+ return plan;
369
+ }
370
+
371
+ if ((evaluation.actionType || action?.actionType) === "package.install") {
372
+ plan.recommendedMode = "approval-required";
373
+ plan.wouldAllow.push("run the requested package install once if approved");
374
+ plan.wouldLimit.push("no remote script execution");
375
+ plan.wouldDeny.push("postinstall or preinstall expansion");
376
+ plan.ttlRecommendation = "once";
377
+ plan.scope.allowedCommands = [command || "requested-package-install"];
378
+ plan.reason = "Package install actions can execute code and should stay bounded.";
379
+ return plan;
380
+ }
381
+
382
+ if ((evaluation.actionType || action?.actionType) === "filesystem.read" && Array.isArray(evaluation.sensitiveTargets) && evaluation.sensitiveTargets.length > 0) {
383
+ plan.recommendedMode = "approval-required";
384
+ plan.wouldAllow.push("inspect only the requested sensitive target");
385
+ plan.wouldDeny.push("secret value reads");
386
+ plan.wouldDeny.push("credential export or copying");
387
+ plan.ttlRecommendation = "once";
388
+ plan.scope.allowedPaths = evaluation.sensitiveTargets.slice(0, 3);
389
+ plan.reason = "Sensitive reads should be explicit, narrow, and temporary.";
390
+ return plan;
391
+ }
392
+
393
+ plan.wouldAllow.push("read local project state");
394
+ plan.wouldLimit.push("no write or destructive actions");
395
+ plan.ttlRecommendation = "once";
396
+ plan.reason = "The action can stay within a small read-oriented scope.";
397
+ return plan;
398
+ }
399
+
400
+ module.exports = {
401
+ buildPermissionLimitPlan,
402
+ isSafeLimitedShellCommand,
403
+ };
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+
3
+ const crypto = require("crypto");
4
+ const { safeReadJson, safeWriteJson, nowIso } = require("../fsx");
5
+
6
+ const SCAN_CACHE_REL_PATH = ".claude/cco/security/scan-cache.json";
7
+ const SCAN_CACHE_VERSION = 1;
8
+
9
+ function sha256(value) {
10
+ return crypto.createHash("sha256").update(String(value || "")).digest("hex");
11
+ }
12
+
13
+ function stableStringify(value) {
14
+ if (Array.isArray(value)) return `[${value.map((item) => stableStringify(item)).join(",")}]`;
15
+ if (value && typeof value === "object") {
16
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`).join(",")}}`;
17
+ }
18
+ return JSON.stringify(value);
19
+ }
20
+
21
+ function loadScanCache(cwd) {
22
+ const doc = safeReadJson(cwd, SCAN_CACHE_REL_PATH, null);
23
+ if (!doc || typeof doc !== "object" || !Array.isArray(doc.records)) {
24
+ return {
25
+ cacheVersion: SCAN_CACHE_VERSION,
26
+ scannerVersion: "unknown",
27
+ records: [],
28
+ };
29
+ }
30
+ return doc;
31
+ }
32
+
33
+ function cacheLookup(cwd, payload) {
34
+ const cache = loadScanCache(cwd);
35
+ const record = cache.records.find((item) =>
36
+ item &&
37
+ item.cacheKey === payload.cacheKey &&
38
+ item.path === payload.path &&
39
+ item.mtimeMs === payload.mtimeMs &&
40
+ item.size === payload.size &&
41
+ item.contentHash === payload.contentHash &&
42
+ item.scannerVersion === payload.scannerVersion
43
+ );
44
+ return record || null;
45
+ }
46
+
47
+ function writeScanCache(cwd, scannerVersion, records) {
48
+ safeWriteJson(cwd, SCAN_CACHE_REL_PATH, {
49
+ cacheVersion: SCAN_CACHE_VERSION,
50
+ scannerVersion,
51
+ updatedAt: nowIso(),
52
+ records: records.slice(-500),
53
+ });
54
+ }
55
+
56
+ function updateScanCache(cwd, scannerVersion, nextRecord) {
57
+ const current = loadScanCache(cwd);
58
+ const records = (Array.isArray(current.records) ? current.records : []).filter((item) => item.cacheKey !== nextRecord.cacheKey);
59
+ records.push(nextRecord);
60
+ writeScanCache(cwd, scannerVersion, records);
61
+ }
62
+
63
+ function buildCacheKey(parts) {
64
+ return sha256(stableStringify(parts)).slice(0, 24);
65
+ }
66
+
67
+ module.exports = {
68
+ SCAN_CACHE_REL_PATH,
69
+ SCAN_CACHE_VERSION,
70
+ loadScanCache,
71
+ cacheLookup,
72
+ updateScanCache,
73
+ buildCacheKey,
74
+ };
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+
3
+ const crypto = require("crypto");
4
+
5
+ const SOURCE_TYPE_TO_TRUST_LEVEL = {
6
+ trusted_system: "trusted",
7
+ user_prompt: "user_supplied",
8
+ local_project_file: "project_local",
9
+ skill_instruction: "project_local",
10
+ claude_command: "project_local",
11
+ cursor_rule: "project_local",
12
+ mcp_config: "project_local",
13
+ mcp_response: "tool_returned_untrusted",
14
+ rag_chunk: "external_untrusted",
15
+ web_content: "external_untrusted",
16
+ issue_ticket: "external_untrusted",
17
+ generated_file: "project_local",
18
+ unknown_external: "unknown",
19
+ };
20
+
21
+ function sha256(value) {
22
+ return crypto.createHash("sha256").update(String(value || "")).digest("hex");
23
+ }
24
+
25
+ function boundText(value, max = 4000) {
26
+ const text = String(value || "");
27
+ return text.length > max ? `${text.slice(0, max)}...` : text;
28
+ }
29
+
30
+ function redactInlineSecrets(text) {
31
+ return String(text || "")
32
+ .replace(/((?:secret|token|password|private[_ -]?key|api[_ -]?key)\s*[:=]\s*)([^\r\n]+)/ig, "$1[redacted]")
33
+ .replace(/(-----BEGIN [A-Z ]+-----)[\s\S]{0,800}?(-----END [A-Z ]+-----)/g, "$1 [redacted] $2");
34
+ }
35
+
36
+ function sanitizeStoredContent(text, max = 4000) {
37
+ return boundText(redactInlineSecrets(text), max);
38
+ }
39
+
40
+ function normalizeSourceType(sourceType) {
41
+ const value = String(sourceType || "").trim();
42
+ return Object.prototype.hasOwnProperty.call(SOURCE_TYPE_TO_TRUST_LEVEL, value) ? value : "unknown_external";
43
+ }
44
+
45
+ function deriveTrustLevel(sourceType) {
46
+ return SOURCE_TYPE_TO_TRUST_LEVEL[normalizeSourceType(sourceType)] || "unknown";
47
+ }
48
+
49
+ function deriveUsedAs(sourceType, intendedUse) {
50
+ const normalizedSourceType = normalizeSourceType(sourceType);
51
+ const normalizedIntendedUse = String(intendedUse || "").toLowerCase();
52
+ if (["mcp_response", "rag_chunk", "web_content", "issue_ticket", "unknown_external"].includes(normalizedSourceType)) {
53
+ return normalizedIntendedUse === "instruction" ? "data_only" : "evidence_only";
54
+ }
55
+ if (normalizedIntendedUse === "instruction") return "instruction";
56
+ if (normalizedIntendedUse === "evidence") return "evidence_only";
57
+ if (normalizedIntendedUse === "data") return "data_only";
58
+ return "unknown";
59
+ }
60
+
61
+ function deriveAllowedToInfluenceTools(trustLevel, usedAs, instructionRisk, remediations = []) {
62
+ if (["external_untrusted", "tool_returned_untrusted", "unknown"].includes(trustLevel)) return false;
63
+ if (usedAs === "data_only" || usedAs === "evidence_only") return false;
64
+ if (instructionRisk === "high") return false;
65
+ if (remediations.includes("treat_as_data_only") || remediations.includes("quarantine_source")) return false;
66
+ return true;
67
+ }
68
+
69
+ function deriveRecommendation(sourceType, trustLevel, instructionRisk, findings = []) {
70
+ const normalizedSourceType = normalizeSourceType(sourceType);
71
+ if (["external_untrusted", "tool_returned_untrusted", "unknown"].includes(trustLevel)) {
72
+ return instructionRisk === "low" ? "treat_as_data_only" : "quarantine_source";
73
+ }
74
+ if (instructionRisk === "high") {
75
+ return ["skill_instruction", "claude_command", "cursor_rule", "mcp_config"].includes(normalizedSourceType)
76
+ ? "require_approval"
77
+ : "quarantine_source";
78
+ }
79
+ if (instructionRisk === "medium" || findings.length > 0) return "require_approval";
80
+ return "trust_source";
81
+ }
82
+
83
+ function buildSourceId(input = {}) {
84
+ if (input.sourceId) return String(input.sourceId);
85
+ const seed = [
86
+ normalizeSourceType(input.sourceType),
87
+ input.origin || "",
88
+ input.path || "",
89
+ input.contentHash || sha256(input.content || ""),
90
+ ].join(":");
91
+ return `source-${sha256(seed).slice(0, 12)}`;
92
+ }
93
+
94
+ function buildSourceRecord(input = {}) {
95
+ const sourceType = normalizeSourceType(input.sourceType);
96
+ const trustLevel = deriveTrustLevel(sourceType);
97
+ const instructionRisk = input.instructionRisk || "low";
98
+ const findings = Array.isArray(input.findings) ? input.findings : [];
99
+ const usedAs = input.usedAs || deriveUsedAs(sourceType, input.intendedUse);
100
+ const remediationActions = Array.isArray(input.remediationActions) ? input.remediationActions : [];
101
+ return {
102
+ sourceId: buildSourceId(input),
103
+ sourceType,
104
+ trustLevel,
105
+ origin: input.origin || null,
106
+ path: input.path || null,
107
+ contentHash: input.contentHash || sha256(input.content || ""),
108
+ contentSample: sanitizeStoredContent(input.content || input.contentSample || ""),
109
+ instructionRisk,
110
+ usedAs,
111
+ allowedToInfluenceTools: deriveAllowedToInfluenceTools(trustLevel, usedAs, instructionRisk, remediationActions),
112
+ findings,
113
+ recommendation: input.recommendation || deriveRecommendation(sourceType, trustLevel, instructionRisk, findings),
114
+ remediationActions,
115
+ createdAt: input.createdAt || null,
116
+ updatedAt: input.updatedAt || null,
117
+ };
118
+ }
119
+
120
+ function sourceTypeForComponentType(componentType) {
121
+ switch (String(componentType || "")) {
122
+ case "skill":
123
+ return "skill_instruction";
124
+ case "command":
125
+ return "claude_command";
126
+ case "cursor-rule":
127
+ return "cursor_rule";
128
+ case "mcp":
129
+ return "mcp_config";
130
+ default:
131
+ return "local_project_file";
132
+ }
133
+ }
134
+
135
+ module.exports = {
136
+ boundText,
137
+ sanitizeStoredContent,
138
+ normalizeSourceType,
139
+ deriveTrustLevel,
140
+ deriveUsedAs,
141
+ deriveAllowedToInfluenceTools,
142
+ deriveRecommendation,
143
+ buildSourceId,
144
+ buildSourceRecord,
145
+ sourceTypeForComponentType,
146
+ };