avorelo 0.3.4 → 0.3.5

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 (3) hide show
  1. package/README.md +77 -95
  2. package/dist/avorelo.mjs +1160 -39
  3. package/package.json +7 -3
package/dist/avorelo.mjs CHANGED
@@ -1702,8 +1702,8 @@ var init_generic = __esm({
1702
1702
  const promptPath = join8(dir, ".avorelo", "prompt.md");
1703
1703
  if (existsSync9(promptPath)) {
1704
1704
  try {
1705
- const { unlinkSync: unlinkSync5 } = __require("node:fs");
1706
- unlinkSync5(promptPath);
1705
+ const { unlinkSync: unlinkSync6 } = __require("node:fs");
1706
+ unlinkSync6(promptPath);
1707
1707
  return { removed: [promptPath], preserved: [] };
1708
1708
  } catch {
1709
1709
  return { removed: [], preserved: [promptPath] };
@@ -5510,6 +5510,15 @@ var init_plan_contract = __esm({
5510
5510
  status: "implemented",
5511
5511
  enforcementSurfaces: ["cli", "local-dashboard"],
5512
5512
  privacyBoundary: "local_only"
5513
+ },
5514
+ {
5515
+ key: "artifact_guard_basic",
5516
+ pricingLabel: "Agent artifacts scanned for risks before AI touches the project",
5517
+ tier: "free",
5518
+ included: true,
5519
+ status: "implemented",
5520
+ enforcementSurfaces: ["cli"],
5521
+ privacyBoundary: "local_only"
5513
5522
  }
5514
5523
  ];
5515
5524
  PRO_CAPABILITIES = [
@@ -7325,8 +7334,8 @@ init_run();
7325
7334
  init_work_contract();
7326
7335
  init_receipts();
7327
7336
  init_state_ledger();
7328
- import { writeFileSync as writeFileSync37, mkdirSync as mkdirSync39, existsSync as existsSync62, readFileSync as readFileSync46, appendFileSync as appendFileSync11, rmSync as rmSync3, unlinkSync as unlinkSync4 } from "node:fs";
7329
- import { join as join61 } from "node:path";
7337
+ import { writeFileSync as writeFileSync38, mkdirSync as mkdirSync40, existsSync as existsSync64, readFileSync as readFileSync50, appendFileSync as appendFileSync11, rmSync as rmSync3, unlinkSync as unlinkSync5 } from "node:fs";
7338
+ import { join as join66 } from "node:path";
7330
7339
 
7331
7340
  // src/avorelo/capabilities/activation/index.ts
7332
7341
  import { mkdtempSync, writeFileSync as writeFileSync11, mkdirSync as mkdirSync11, rmSync as rmSync2, existsSync as existsSync24 } from "node:fs";
@@ -17608,6 +17617,1070 @@ async function sendDueTelemetry(dir, opts) {
17608
17617
  }
17609
17618
  }
17610
17619
 
17620
+ // src/avorelo/kernel/agent-artifact-guard/guard-handler.ts
17621
+ import { existsSync as existsSync63, readFileSync as readFileSync49 } from "node:fs";
17622
+ import { join as join65 } from "node:path";
17623
+
17624
+ // src/avorelo/kernel/agent-artifact-guard/index.ts
17625
+ import { join as join64 } from "node:path";
17626
+ import { readFileSync as readFileSync48 } from "node:fs";
17627
+
17628
+ // src/avorelo/kernel/agent-artifact-guard/artifact-discovery.ts
17629
+ import { readdirSync as readdirSync18, statSync as statSync13, existsSync as existsSync62 } from "node:fs";
17630
+ import { join as join61, relative as relative3, extname as extname2 } from "node:path";
17631
+ var SCANNED_EXTENSIONS = /* @__PURE__ */ new Set([
17632
+ ".md",
17633
+ ".js",
17634
+ ".ts",
17635
+ ".json",
17636
+ ".yml",
17637
+ ".yaml",
17638
+ ".sh",
17639
+ ".py",
17640
+ ".txt",
17641
+ ".toml"
17642
+ ]);
17643
+ var IGNORED_DIRS = /* @__PURE__ */ new Set([
17644
+ "node_modules",
17645
+ "dist",
17646
+ ".git",
17647
+ "build",
17648
+ "out",
17649
+ ".next",
17650
+ "__pycache__"
17651
+ ]);
17652
+ var ARTIFACT_PATTERNS = [
17653
+ { kind: "claude-skill", paths: [".claude/skills"], recursive: true },
17654
+ { kind: "claude-instructions", paths: ["CLAUDE.md", ".claude/CLAUDE.md"] },
17655
+ { kind: "codex-instructions", paths: ["AGENTS.md", "CODEX.md", ".codex/CODEX.md"] },
17656
+ { kind: "cursor-rules", paths: [".cursor/rules", ".cursorrules"], recursive: true },
17657
+ { kind: "mcp-config", paths: [".claude/mcp.json", ".cursor/mcp.json", "mcp.json", ".mcp.json"] },
17658
+ { kind: "github-actions", paths: [".github/workflows"], recursive: true },
17659
+ { kind: "package-scripts", paths: ["package.json"] },
17660
+ { kind: "hooks", paths: [".husky", ".git/hooks", ".claude/hooks"], recursive: true }
17661
+ ];
17662
+ function walkDir2(dir, results) {
17663
+ let entries;
17664
+ try {
17665
+ entries = readdirSync18(dir);
17666
+ } catch {
17667
+ return;
17668
+ }
17669
+ for (const entry of entries) {
17670
+ if (IGNORED_DIRS.has(entry)) continue;
17671
+ const full = join61(dir, entry);
17672
+ let st;
17673
+ try {
17674
+ st = statSync13(full);
17675
+ } catch {
17676
+ continue;
17677
+ }
17678
+ if (st.isDirectory()) {
17679
+ walkDir2(full, results);
17680
+ } else if (st.isFile() && SCANNED_EXTENSIONS.has(extname2(full).toLowerCase())) {
17681
+ results.push(full);
17682
+ }
17683
+ }
17684
+ }
17685
+ function discoverArtifacts(projectRoot) {
17686
+ const artifacts = [];
17687
+ for (const pattern of ARTIFACT_PATTERNS) {
17688
+ for (const p of pattern.paths) {
17689
+ const fullPath = join61(projectRoot, p);
17690
+ if (!existsSync62(fullPath)) continue;
17691
+ let st;
17692
+ try {
17693
+ st = statSync13(fullPath);
17694
+ } catch {
17695
+ continue;
17696
+ }
17697
+ if (st.isFile()) {
17698
+ if (SCANNED_EXTENSIONS.has(extname2(fullPath).toLowerCase()) || fullPath.endsWith(".json")) {
17699
+ artifacts.push({
17700
+ kind: pattern.kind,
17701
+ path: relative3(projectRoot, fullPath)
17702
+ });
17703
+ }
17704
+ } else if (st.isDirectory() && pattern.recursive) {
17705
+ const files = [];
17706
+ walkDir2(fullPath, files);
17707
+ for (const f of files) {
17708
+ artifacts.push({
17709
+ kind: pattern.kind,
17710
+ path: relative3(projectRoot, f)
17711
+ });
17712
+ }
17713
+ }
17714
+ }
17715
+ }
17716
+ return artifacts;
17717
+ }
17718
+ function classifyArtifact(filePath) {
17719
+ const lower = filePath.toLowerCase().replace(/\\/g, "/");
17720
+ if (lower.includes(".claude/skills/")) return "claude-skill";
17721
+ if (lower.includes("claude.md")) return "claude-instructions";
17722
+ if (lower.includes("agents.md") || lower.includes("codex.md")) return "codex-instructions";
17723
+ if (lower.includes(".cursor/rules") || lower.includes(".cursorrules")) return "cursor-rules";
17724
+ if (lower.includes("mcp.json") || lower.includes("mcp.yaml")) return "mcp-config";
17725
+ if (lower.includes(".github/workflows/")) return "github-actions";
17726
+ if (lower === "package.json" || lower.endsWith("/package.json")) return "package-scripts";
17727
+ if (lower.includes(".husky/") || lower.includes("/hooks/")) return "hooks";
17728
+ if (lower.endsWith(".sh")) return "shell-script";
17729
+ return "unknown";
17730
+ }
17731
+
17732
+ // src/avorelo/kernel/agent-artifact-guard/rules.ts
17733
+ var RULES2 = [
17734
+ // --- Destructive commands ---
17735
+ {
17736
+ id: "destructive-rm-rf",
17737
+ title: "Recursive force delete",
17738
+ severity: "critical",
17739
+ category: "destructive-command",
17740
+ pattern: /rm\s+-(r|f|rf|fr)\s/,
17741
+ artifactKinds: "all",
17742
+ description: "Recursively and forcibly deletes files or directories. Can cause irreversible data loss.",
17743
+ recommendedAction: "Remove or scope the delete command. Use safe alternatives with explicit paths."
17744
+ },
17745
+ {
17746
+ id: "destructive-drop-table",
17747
+ title: "SQL DROP TABLE",
17748
+ severity: "critical",
17749
+ category: "destructive-command",
17750
+ pattern: /DROP\s+TABLE/i,
17751
+ artifactKinds: "all",
17752
+ description: "Drops a database table, causing irreversible data loss.",
17753
+ recommendedAction: "Remove DROP TABLE or add explicit safety guards."
17754
+ },
17755
+ {
17756
+ id: "destructive-format-disk",
17757
+ title: "Disk format command",
17758
+ severity: "critical",
17759
+ category: "destructive-command",
17760
+ pattern: /(?:mkfs|format\s+[A-Z]:)/i,
17761
+ artifactKinds: "all",
17762
+ description: "Formats a disk or partition, destroying all data.",
17763
+ recommendedAction: "Remove disk formatting commands from agent artifacts."
17764
+ },
17765
+ // --- Remote code execution ---
17766
+ {
17767
+ id: "rce-curl-pipe-bash",
17768
+ title: "Pipe curl to shell",
17769
+ severity: "critical",
17770
+ category: "remote-code-execution",
17771
+ pattern: /curl\s[^|]*\|\s*(?:bash|sh|zsh)/,
17772
+ artifactKinds: "all",
17773
+ description: "Downloads and immediately executes a remote script. Classic remote code execution vector.",
17774
+ recommendedAction: "Download the script first, review it, then execute separately."
17775
+ },
17776
+ {
17777
+ id: "rce-wget-pipe-sh",
17778
+ title: "Pipe wget to shell",
17779
+ severity: "critical",
17780
+ category: "remote-code-execution",
17781
+ pattern: /wget\s[^|]*\|\s*(?:bash|sh|zsh)/,
17782
+ artifactKinds: "all",
17783
+ description: "Downloads and immediately executes a remote script via wget.",
17784
+ recommendedAction: "Download the script first, review it, then execute separately."
17785
+ },
17786
+ {
17787
+ id: "rce-base64-decode-exec",
17788
+ title: "Base64 decode and execute",
17789
+ severity: "critical",
17790
+ category: "remote-code-execution",
17791
+ pattern: /base64\s+-d\s*\|\s*(?:bash|sh)/,
17792
+ artifactKinds: "all",
17793
+ description: "Decodes an obfuscated payload and pipes it to a shell.",
17794
+ recommendedAction: "Remove obfuscated execution. Use plain, reviewable scripts."
17795
+ },
17796
+ {
17797
+ id: "rce-reverse-shell",
17798
+ title: "Reverse or bind shell",
17799
+ severity: "critical",
17800
+ category: "remote-code-execution",
17801
+ pattern: /(?:nc\s+-e|\/dev\/tcp\/|ncat\s.*-e|socat\s.*exec)/,
17802
+ artifactKinds: "all",
17803
+ description: "Opens an interactive shell to a remote host. Post-exploitation technique.",
17804
+ recommendedAction: "Remove reverse/bind shell commands."
17805
+ },
17806
+ // --- Secret exposure ---
17807
+ {
17808
+ id: "secret-private-key",
17809
+ title: "Private key material",
17810
+ severity: "critical",
17811
+ category: "secret-exposure",
17812
+ pattern: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,
17813
+ artifactKinds: "all",
17814
+ description: "Contains or references private key material.",
17815
+ recommendedAction: "Remove private key content. Use secret management instead."
17816
+ },
17817
+ {
17818
+ id: "secret-id-rsa",
17819
+ title: "SSH private key file reference",
17820
+ severity: "critical",
17821
+ category: "secret-exposure",
17822
+ pattern: /(?:id_rsa|id_ed25519|id_ecdsa)(?:\s|$|")/,
17823
+ artifactKinds: "all",
17824
+ description: "References an SSH private key file directly.",
17825
+ recommendedAction: "Use SSH agent or key references instead of direct file paths."
17826
+ },
17827
+ {
17828
+ id: "secret-ssh-dir",
17829
+ title: "SSH directory access",
17830
+ severity: "high",
17831
+ category: "secret-exposure",
17832
+ pattern: /~\/\.ssh\//,
17833
+ artifactKinds: "all",
17834
+ description: "Accesses the SSH directory containing private keys and host configurations.",
17835
+ recommendedAction: "Avoid direct SSH directory access in agent artifacts."
17836
+ },
17837
+ {
17838
+ id: "secret-env-file",
17839
+ title: ".env file reference",
17840
+ severity: "medium",
17841
+ category: "secret-exposure",
17842
+ pattern: /(?:\.env(?:\.\w+)?)\b/,
17843
+ artifactKinds: "all",
17844
+ description: "References a .env file which typically contains secrets and API keys.",
17845
+ recommendedAction: "Review whether the .env reference exposes sensitive values."
17846
+ },
17847
+ {
17848
+ id: "secret-process-env",
17849
+ title: "Environment variable access",
17850
+ severity: "low",
17851
+ category: "secret-exposure",
17852
+ pattern: /process\.env\b/,
17853
+ artifactKinds: "all",
17854
+ description: "Accesses environment variables which may contain secrets.",
17855
+ recommendedAction: "Verify that accessed environment variables do not contain sensitive values."
17856
+ },
17857
+ {
17858
+ id: "secret-api-key-pattern",
17859
+ title: "API key or token pattern",
17860
+ severity: "high",
17861
+ category: "secret-exposure",
17862
+ pattern: /(?:api[_-]?key|api[_-]?secret|auth[_-]?token)\s*[:=]/i,
17863
+ artifactKinds: "all",
17864
+ description: "Contains an API key or authentication token assignment.",
17865
+ recommendedAction: "Move credentials to environment variables or a secret manager."
17866
+ },
17867
+ // --- Privilege escalation ---
17868
+ {
17869
+ id: "priv-sudo",
17870
+ title: "Privilege escalation via sudo",
17871
+ severity: "high",
17872
+ category: "privilege-escalation",
17873
+ pattern: /\bsudo\b/,
17874
+ artifactKinds: "all",
17875
+ description: "Runs commands with elevated privileges.",
17876
+ recommendedAction: "Review whether elevated privileges are necessary. Scope to specific commands."
17877
+ },
17878
+ {
17879
+ id: "priv-chmod-exec",
17880
+ title: "Make file executable",
17881
+ severity: "medium",
17882
+ category: "privilege-escalation",
17883
+ pattern: /chmod\s+\+x/,
17884
+ artifactKinds: "all",
17885
+ description: "Marks a file as executable, often preceding execution of a downloaded binary.",
17886
+ recommendedAction: "Verify the file being made executable is trusted."
17887
+ },
17888
+ // --- Prompt injection / hidden instructions ---
17889
+ {
17890
+ id: "injection-prompt-override",
17891
+ title: "Prompt injection phrasing",
17892
+ severity: "high",
17893
+ category: "prompt-injection",
17894
+ pattern: /(?:ignore\s+(?:all\s+)?(?:previous|prior|above)\s+instructions|you\s+are\s+now\s+a|forget\s+(?:all\s+)?(?:your|previous)\s+instructions|disregard\s+(?:all\s+)?(?:previous|prior)\s+instructions|override\s+(?:all\s+)?(?:safety|security)\s+(?:rules|policies|instructions))/i,
17895
+ artifactKinds: "all",
17896
+ description: "Contains phrasing commonly used in prompt injection attacks to override agent instructions.",
17897
+ recommendedAction: "Remove instruction-override phrasing. Rewrite as direct task instructions."
17898
+ },
17899
+ {
17900
+ id: "injection-do-not-tell",
17901
+ title: "Concealment instruction",
17902
+ severity: "high",
17903
+ category: "hidden-instruction",
17904
+ pattern: /(?:do\s+not\s+tell\s+(?:the\s+)?user|hide\s+(?:this|these)\s+(?:changes?|from)|don'?t\s+(?:mention|reveal|show|disclose)\s+(?:this|these))/i,
17905
+ artifactKinds: "all",
17906
+ description: "Instructs the agent to conceal actions or information from the user.",
17907
+ recommendedAction: "Remove concealment instructions. Agent actions should be transparent."
17908
+ },
17909
+ {
17910
+ id: "injection-skip-tests",
17911
+ title: "Test bypass instruction",
17912
+ severity: "high",
17913
+ category: "hidden-instruction",
17914
+ pattern: /(?:skip\s+(?:all\s+)?tests?|don'?t\s+run\s+tests?|ignore\s+(?:test|lint|check)\s+(?:failures?|errors?|results?)|bypass\s+(?:ci|checks?|validation))/i,
17915
+ artifactKinds: "all",
17916
+ description: "Instructs the agent to skip testing, linting, or validation checks.",
17917
+ recommendedAction: "Remove test-bypass instructions. All changes should be validated."
17918
+ },
17919
+ {
17920
+ id: "injection-ignore-policy",
17921
+ title: "Policy bypass instruction",
17922
+ severity: "high",
17923
+ category: "hidden-instruction",
17924
+ pattern: /(?:ignore\s+(?:all\s+)?polic(?:y|ies)|always\s+approve|auto[_-]?approve\s+(?:all|everything)|skip\s+(?:review|approval))/i,
17925
+ artifactKinds: "all",
17926
+ description: "Instructs the agent to bypass approval policies or auto-approve actions.",
17927
+ recommendedAction: "Remove policy-bypass instructions. Approval workflows should be respected."
17928
+ },
17929
+ {
17930
+ id: "injection-push-to-main",
17931
+ title: "Direct push to main branch",
17932
+ severity: "high",
17933
+ category: "hidden-instruction",
17934
+ pattern: /(?:push\s+(?:directly\s+)?to\s+(?:main|master|production)|force[_-]?push|--force\s+(?:origin\s+)?(?:main|master))/i,
17935
+ artifactKinds: "all",
17936
+ description: "Instructs the agent to push directly to a protected branch or force-push.",
17937
+ recommendedAction: "Use pull requests and standard review workflows instead of direct pushes."
17938
+ },
17939
+ // --- Self-modifying ---
17940
+ {
17941
+ id: "selfmod-modify-own-skill",
17942
+ title: "Self-modifying skill instruction",
17943
+ severity: "high",
17944
+ category: "self-modifying",
17945
+ pattern: /(?:modify\s+(?:this|your\s+own)\s+(?:skill|instructions?|rules?|config)|write\s+(?:to|back\s+to)\s+(?:SKILL|CLAUDE|AGENTS)\.md|update\s+(?:your\s+own|this)\s+(?:skill|config)\s+file)/i,
17946
+ artifactKinds: "all",
17947
+ description: "Instructs the agent to modify its own skill, instructions, or configuration files.",
17948
+ recommendedAction: "Remove self-modification instructions. Agent artifacts should be immutable during execution."
17949
+ },
17950
+ // --- MCP tool poisoning ---
17951
+ {
17952
+ id: "mcp-hidden-instruction",
17953
+ title: "Hidden instruction in MCP tool description",
17954
+ severity: "high",
17955
+ category: "mcp-tool-poisoning",
17956
+ pattern: /(?:when\s+(?:the\s+)?user\s+(?:asks?|requests?)|secretly|silently\s+(?:send|post|upload|forward))/i,
17957
+ artifactKinds: ["mcp-config"],
17958
+ description: "MCP tool description contains instruction-like content that could manipulate agent behavior.",
17959
+ recommendedAction: "Review MCP tool descriptions for hidden instructions. Descriptions should only describe functionality."
17960
+ },
17961
+ // --- Package lifecycle risk ---
17962
+ {
17963
+ id: "pkg-lifecycle-exec",
17964
+ title: "Process execution in package lifecycle script",
17965
+ severity: "high",
17966
+ category: "package-lifecycle-risk",
17967
+ pattern: /(?:child_process|exec|spawn|execSync|spawnSync)\s*\(/,
17968
+ artifactKinds: ["package-scripts", "shell-script"],
17969
+ description: "Package lifecycle script or hook executes arbitrary processes.",
17970
+ recommendedAction: "Review lifecycle scripts for unexpected process execution."
17971
+ },
17972
+ {
17973
+ id: "pkg-lifecycle-network",
17974
+ title: "Network access in package lifecycle script",
17975
+ severity: "high",
17976
+ category: "package-lifecycle-risk",
17977
+ pattern: /(?:fetch|axios|https?\.(?:get|post|request)|XMLHttpRequest|node-fetch)\s*\(/,
17978
+ artifactKinds: ["package-scripts", "shell-script"],
17979
+ description: "Package lifecycle script makes network requests, potentially exfiltrating data.",
17980
+ recommendedAction: "Review lifecycle scripts for unexpected network access."
17981
+ },
17982
+ // --- Network exfiltration ---
17983
+ {
17984
+ id: "network-external-call",
17985
+ title: "External network request",
17986
+ severity: "medium",
17987
+ category: "network-exfiltration",
17988
+ pattern: /\b(?:curl|wget|fetch|axios|requests\.(?:get|post))\b/,
17989
+ artifactKinds: ["claude-skill", "claude-instructions", "cursor-rules", "codex-instructions", "hooks"],
17990
+ description: "Makes an external network request which could exfiltrate data or retrieve malicious payloads.",
17991
+ recommendedAction: "Verify the network call is necessary and targets a trusted endpoint."
17992
+ },
17993
+ // --- Eval / dynamic execution ---
17994
+ {
17995
+ id: "exec-eval",
17996
+ title: "Dynamic code execution",
17997
+ severity: "high",
17998
+ category: "remote-code-execution",
17999
+ pattern: /\b(?:eval|Invoke-Expression|Function\s*\()\s*\(/,
18000
+ artifactKinds: "all",
18001
+ description: "Executes a string as code, enabling injection and obfuscation attacks.",
18002
+ recommendedAction: "Remove eval/dynamic execution. Use explicit, reviewable code paths."
18003
+ },
18004
+ {
18005
+ id: "exec-child-process",
18006
+ title: "Node child_process import",
18007
+ severity: "high",
18008
+ category: "remote-code-execution",
18009
+ pattern: /require\s*\(\s*['"]child_process['"]\s*\)|from\s+['"]child_process['"]/,
18010
+ artifactKinds: "all",
18011
+ description: "Imports Node.js child_process module for arbitrary command execution.",
18012
+ recommendedAction: "Review whether child_process usage is necessary and scoped."
18013
+ },
18014
+ // --- Filesystem access ---
18015
+ {
18016
+ id: "fs-broad-access",
18017
+ title: "Broad filesystem access",
18018
+ severity: "medium",
18019
+ category: "filesystem-access",
18020
+ pattern: /(?:fs\.(?:readdir|rmdir|unlink)Sync|glob\s*\(\s*['"]\/|readdir\s*\(\s*['"]\/)/,
18021
+ artifactKinds: "all",
18022
+ description: "Performs broad filesystem operations that could access or delete files outside the project.",
18023
+ recommendedAction: "Scope filesystem operations to the project directory."
18024
+ },
18025
+ // --- Deploy / publish ---
18026
+ {
18027
+ id: "deploy-npm-publish",
18028
+ title: "npm publish command",
18029
+ severity: "high",
18030
+ category: "deploy-publish",
18031
+ pattern: /npm\s+publish/,
18032
+ artifactKinds: "all",
18033
+ description: "Publishes a package to npm, which is an irreversible public action.",
18034
+ recommendedAction: "Remove automatic publish commands. Publishing should be a deliberate human action."
18035
+ },
18036
+ {
18037
+ id: "deploy-dangerous-action",
18038
+ title: "Dangerous deploy/publish in CI",
18039
+ severity: "high",
18040
+ category: "deploy-publish",
18041
+ pattern: /(?:deploy\s+(?:--)?(?:prod|production)|push\s+(?:to\s+)?(?:prod|production|live))/i,
18042
+ artifactKinds: ["github-actions", "shell-script", "hooks"],
18043
+ description: "Deploys directly to production without explicit approval gates.",
18044
+ recommendedAction: "Add approval gates before production deployments."
18045
+ }
18046
+ ];
18047
+ function getRule(id) {
18048
+ return RULES2.find((r2) => r2.id === id);
18049
+ }
18050
+
18051
+ // src/avorelo/kernel/agent-artifact-guard/scanner.ts
18052
+ var MAX_PREVIEW_LENGTH = 80;
18053
+ function safeMatchPreview(match) {
18054
+ let preview = match.trim();
18055
+ if (preview.length > MAX_PREVIEW_LENGTH) {
18056
+ preview = preview.slice(0, MAX_PREVIEW_LENGTH) + "...";
18057
+ }
18058
+ preview = preview.replace(
18059
+ /(?:key|secret|token|password|passwd|pwd)\s*[:=]\s*['"]?[a-zA-Z0-9_\-./+=]{8,}/gi,
18060
+ (m) => {
18061
+ const eqIdx = m.search(/[:=]/);
18062
+ if (eqIdx >= 0) return m.slice(0, eqIdx + 1) + " [REDACTED]";
18063
+ return "[REDACTED]";
18064
+ }
18065
+ );
18066
+ return preview;
18067
+ }
18068
+ function rulesForArtifact(kind) {
18069
+ return RULES2.filter(
18070
+ (r2) => r2.artifactKinds === "all" || r2.artifactKinds.includes(kind)
18071
+ );
18072
+ }
18073
+ function scanContent2(content, filePath, artifactKind) {
18074
+ const kind = artifactKind ?? classifyArtifact(filePath);
18075
+ const applicable = rulesForArtifact(kind);
18076
+ const findings = [];
18077
+ const lines = content.split("\n");
18078
+ for (const rule of applicable) {
18079
+ if (rule.multiline) continue;
18080
+ for (let i = 0; i < lines.length; i++) {
18081
+ const match = rule.pattern.exec(lines[i]);
18082
+ if (match) {
18083
+ findings.push({
18084
+ ruleId: rule.id,
18085
+ title: rule.title,
18086
+ severity: rule.severity,
18087
+ category: rule.category,
18088
+ file: filePath,
18089
+ line: i + 1,
18090
+ matchPreview: safeMatchPreview(match[0]),
18091
+ description: rule.description,
18092
+ recommendedAction: rule.recommendedAction,
18093
+ artifactKind: kind
18094
+ });
18095
+ }
18096
+ }
18097
+ }
18098
+ for (const rule of applicable) {
18099
+ if (!rule.multiline) continue;
18100
+ const match = rule.pattern.exec(content);
18101
+ if (match) {
18102
+ const lineNumber = content.slice(0, match.index).split("\n").length;
18103
+ findings.push({
18104
+ ruleId: rule.id,
18105
+ title: rule.title,
18106
+ severity: rule.severity,
18107
+ category: rule.category,
18108
+ file: filePath,
18109
+ line: lineNumber,
18110
+ matchPreview: safeMatchPreview(match[0]),
18111
+ description: rule.description,
18112
+ recommendedAction: rule.recommendedAction,
18113
+ artifactKind: kind
18114
+ });
18115
+ }
18116
+ }
18117
+ findings.sort((a, b) => a.line - b.line);
18118
+ return findings;
18119
+ }
18120
+
18121
+ // src/avorelo/kernel/agent-artifact-guard/scorer.ts
18122
+ var SEVERITY_WEIGHTS = {
18123
+ low: 1,
18124
+ medium: 3,
18125
+ high: 6,
18126
+ critical: 10
18127
+ };
18128
+ function countBySeverity(findings) {
18129
+ const counts = { critical: 0, high: 0, medium: 0, low: 0 };
18130
+ for (const f of findings) {
18131
+ counts[f.severity]++;
18132
+ }
18133
+ return counts;
18134
+ }
18135
+ function computeRiskScore(counts) {
18136
+ if (counts.critical > 0) return 10;
18137
+ const weighted = counts.high * SEVERITY_WEIGHTS.high + counts.medium * SEVERITY_WEIGHTS.medium + counts.low * SEVERITY_WEIGHTS.low;
18138
+ if (weighted === 0) return 0;
18139
+ const score = Math.min(9.9, Math.log2(weighted + 1) * 1.5);
18140
+ return Math.round(score * 10) / 10;
18141
+ }
18142
+
18143
+ // src/avorelo/kernel/agent-artifact-guard/policy.ts
18144
+ function evaluatePolicy2(counts, riskScore) {
18145
+ let findingOutcome = "pass";
18146
+ if (counts.critical > 0) findingOutcome = "blocked";
18147
+ else if (counts.high > 0) findingOutcome = "requires_approval";
18148
+ else if (counts.medium > 0) findingOutcome = "warn";
18149
+ let scoreOutcome = "pass";
18150
+ if (riskScore >= 9) scoreOutcome = "blocked";
18151
+ else if (riskScore >= 6) scoreOutcome = "requires_approval";
18152
+ else if (riskScore >= 3) scoreOutcome = "warn";
18153
+ const ORDER = ["pass", "warn", "requires_approval", "blocked"];
18154
+ const findingRank = ORDER.indexOf(findingOutcome);
18155
+ const scoreRank = ORDER.indexOf(scoreOutcome);
18156
+ return ORDER[Math.max(findingRank, scoreRank)];
18157
+ }
18158
+ function computeNextBestAction(outcome, counts) {
18159
+ switch (outcome) {
18160
+ case "blocked":
18161
+ return `${counts.critical} critical finding(s) must be resolved before proceeding.`;
18162
+ case "requires_approval":
18163
+ return `${counts.high} high-severity finding(s) require review and approval before proceeding.`;
18164
+ case "warn":
18165
+ return `${counts.medium} medium-severity finding(s) detected. Review recommended.`;
18166
+ case "pass":
18167
+ return "No blocking findings. Artifacts are ready for use.";
18168
+ }
18169
+ }
18170
+
18171
+ // src/avorelo/kernel/agent-artifact-guard/receipt.ts
18172
+ import { createHash as createHash18 } from "node:crypto";
18173
+ function createReceipt(result3) {
18174
+ return {
18175
+ receiptType: "agent-artifact-guard-v1",
18176
+ scannedAt: result3.scannedAt,
18177
+ projectRootHash: createHash18("sha256").update(result3.projectRoot).digest("hex"),
18178
+ filesScanned: result3.filesScanned,
18179
+ artifacts: result3.artifacts.map((a) => ({ kind: a.kind, path: a.path })),
18180
+ findingSummary: {
18181
+ total: result3.findings.length,
18182
+ ...result3.counts
18183
+ },
18184
+ findings: result3.findings.map((f) => ({
18185
+ ruleId: f.ruleId,
18186
+ severity: f.severity,
18187
+ file: f.file,
18188
+ line: f.line,
18189
+ title: f.title,
18190
+ matchPreview: f.matchPreview,
18191
+ recommendedAction: f.recommendedAction
18192
+ })),
18193
+ riskScore: result3.riskScore,
18194
+ policyOutcome: result3.policyOutcome,
18195
+ nextBestAction: result3.nextBestAction,
18196
+ containsRawPrompt: false,
18197
+ containsRawSource: false,
18198
+ containsRawSecret: false,
18199
+ contentStored: false
18200
+ };
18201
+ }
18202
+
18203
+ // src/avorelo/kernel/agent-artifact-guard/reporter.ts
18204
+ var SEVERITY_BADGE = {
18205
+ critical: "[CRITICAL]",
18206
+ high: "[HIGH]",
18207
+ medium: "[MEDIUM]",
18208
+ low: "[LOW]"
18209
+ };
18210
+ var OUTCOME_LABEL = {
18211
+ pass: "PASS",
18212
+ warn: "WARN",
18213
+ requires_approval: "REQUIRES APPROVAL",
18214
+ blocked: "BLOCKED"
18215
+ };
18216
+ function riskBar(score) {
18217
+ const filled = Math.round(score / 10 * 20);
18218
+ return "\u2588".repeat(filled) + "\u2591".repeat(20 - filled);
18219
+ }
18220
+ function groupByFile(findings) {
18221
+ const map = /* @__PURE__ */ new Map();
18222
+ for (const f of findings) {
18223
+ const group = map.get(f.file) ?? [];
18224
+ group.push(f);
18225
+ map.set(f.file, group);
18226
+ }
18227
+ return map;
18228
+ }
18229
+ function renderReport(result3) {
18230
+ const lines = [];
18231
+ lines.push(`Artifact Guard Scan`);
18232
+ lines.push(`Path: ${result3.projectRoot}`);
18233
+ lines.push(`Files scanned: ${result3.filesScanned}`);
18234
+ lines.push(`Artifacts found: ${result3.artifacts.length}`);
18235
+ lines.push(`Findings: ${result3.findings.length}`);
18236
+ lines.push("");
18237
+ if (result3.findings.length > 0) {
18238
+ const grouped = groupByFile(result3.findings);
18239
+ for (const [file, findings] of grouped) {
18240
+ lines.push(` ${file}`);
18241
+ for (const f of findings) {
18242
+ lines.push(` L${f.line} ${SEVERITY_BADGE[f.severity]} ${f.title} (${f.ruleId})`);
18243
+ lines.push(` ${f.matchPreview}`);
18244
+ lines.push(` ${f.description}`);
18245
+ }
18246
+ lines.push("");
18247
+ }
18248
+ }
18249
+ lines.push(`Risk: ${riskBar(result3.riskScore)} ${result3.riskScore}/10`);
18250
+ lines.push(`Outcome: ${OUTCOME_LABEL[result3.policyOutcome]}`);
18251
+ lines.push("");
18252
+ lines.push(`Next: ${result3.nextBestAction}`);
18253
+ return lines.join("\n");
18254
+ }
18255
+ function renderSummary(result3) {
18256
+ const parts = [];
18257
+ parts.push(`Artifact Guard: ${OUTCOME_LABEL[result3.policyOutcome]}`);
18258
+ if (result3.findings.length > 0) {
18259
+ const counts = [];
18260
+ if (result3.counts.critical > 0) counts.push(`${result3.counts.critical} critical`);
18261
+ if (result3.counts.high > 0) counts.push(`${result3.counts.high} high`);
18262
+ if (result3.counts.medium > 0) counts.push(`${result3.counts.medium} medium`);
18263
+ if (result3.counts.low > 0) counts.push(`${result3.counts.low} low`);
18264
+ parts.push(`(${counts.join(", ")})`);
18265
+ } else {
18266
+ parts.push("(clean)");
18267
+ }
18268
+ return parts.join(" ");
18269
+ }
18270
+ function toJson(result3) {
18271
+ return JSON.stringify(result3, null, 2);
18272
+ }
18273
+
18274
+ // src/avorelo/kernel/agent-artifact-guard/allowlist.ts
18275
+ import { readFileSync as readFileSync46 } from "node:fs";
18276
+ import { join as join62 } from "node:path";
18277
+ function loadAllowlist(projectRoot) {
18278
+ const filePath = join62(projectRoot, ".avorelo", "artifact-guard", "allowlist.json");
18279
+ try {
18280
+ const raw = readFileSync46(filePath, "utf-8");
18281
+ const parsed = JSON.parse(raw);
18282
+ if (!Array.isArray(parsed.entries)) return [];
18283
+ return parsed.entries.filter((e) => typeof e.reason === "string" && e.reason.length > 0);
18284
+ } catch {
18285
+ return [];
18286
+ }
18287
+ }
18288
+ function isAllowlisted(finding, allowlist) {
18289
+ if (finding.severity === "critical") return false;
18290
+ for (const entry of allowlist) {
18291
+ if (!entry.reason) continue;
18292
+ const ruleMatch = !entry.ruleId || entry.ruleId === finding.ruleId;
18293
+ const fileMatch = !entry.file || finding.file === entry.file || finding.file.startsWith(entry.file);
18294
+ if (ruleMatch && fileMatch) return true;
18295
+ }
18296
+ return false;
18297
+ }
18298
+
18299
+ // src/avorelo/kernel/agent-artifact-guard/receipt-store.ts
18300
+ import { mkdirSync as mkdirSync39, writeFileSync as writeFileSync37, readdirSync as readdirSync19, readFileSync as readFileSync47, unlinkSync as unlinkSync4 } from "node:fs";
18301
+ import { join as join63 } from "node:path";
18302
+ var RECEIPT_DIR = "artifact-guard";
18303
+ var MAX_STORED_RECEIPTS = 50;
18304
+ function receiptDir(projectRoot) {
18305
+ return join63(projectRoot, ".avorelo", RECEIPT_DIR);
18306
+ }
18307
+ function receiptFilename(receipt) {
18308
+ const ts = receipt.scannedAt.replace(/[:.]/g, "-");
18309
+ return `receipt-${ts}.json`;
18310
+ }
18311
+ function storeReceipt(projectRoot, receipt) {
18312
+ const dir = receiptDir(projectRoot);
18313
+ mkdirSync39(dir, { recursive: true });
18314
+ const filename = receiptFilename(receipt);
18315
+ const filePath = join63(dir, filename);
18316
+ writeFileSync37(filePath, JSON.stringify(receipt, null, 2), "utf-8");
18317
+ pruneOldReceipts(dir);
18318
+ return filePath;
18319
+ }
18320
+ function loadLatestReceipt(projectRoot) {
18321
+ const dir = receiptDir(projectRoot);
18322
+ let files;
18323
+ try {
18324
+ files = readdirSync19(dir).filter((f) => f.startsWith("receipt-") && f.endsWith(".json"));
18325
+ } catch {
18326
+ return null;
18327
+ }
18328
+ if (files.length === 0) return null;
18329
+ files.sort();
18330
+ const latest = files[files.length - 1];
18331
+ try {
18332
+ const raw = readFileSync47(join63(dir, latest), "utf-8");
18333
+ return JSON.parse(raw);
18334
+ } catch {
18335
+ return null;
18336
+ }
18337
+ }
18338
+ function pruneOldReceipts(dir) {
18339
+ try {
18340
+ const files = readdirSync19(dir).filter((f) => f.startsWith("receipt-") && f.endsWith(".json")).sort();
18341
+ while (files.length > MAX_STORED_RECEIPTS) {
18342
+ const oldest = files.shift();
18343
+ try {
18344
+ unlinkSync4(join63(dir, oldest));
18345
+ } catch {
18346
+ break;
18347
+ }
18348
+ }
18349
+ } catch {
18350
+ }
18351
+ }
18352
+
18353
+ // src/avorelo/kernel/agent-artifact-guard/index.ts
18354
+ function scan(projectRoot, options) {
18355
+ const artifacts = discoverArtifacts(projectRoot);
18356
+ const allFindings = [];
18357
+ for (const artifact of artifacts) {
18358
+ const fullPath = join64(projectRoot, artifact.path);
18359
+ let content;
18360
+ try {
18361
+ content = readFileSync48(fullPath, "utf-8");
18362
+ } catch {
18363
+ continue;
18364
+ }
18365
+ const findings = scanContent2(content, artifact.path, artifact.kind);
18366
+ allFindings.push(...findings);
18367
+ }
18368
+ let effectiveFindings = allFindings;
18369
+ let allowlistedCount = 0;
18370
+ if (options?.applyAllowlist !== false) {
18371
+ const allowlist = loadAllowlist(projectRoot);
18372
+ if (allowlist.length > 0) {
18373
+ effectiveFindings = [];
18374
+ for (const f of allFindings) {
18375
+ if (isAllowlisted(f, allowlist)) {
18376
+ allowlistedCount++;
18377
+ } else {
18378
+ effectiveFindings.push(f);
18379
+ }
18380
+ }
18381
+ }
18382
+ }
18383
+ const counts = countBySeverity(effectiveFindings);
18384
+ const riskScore = computeRiskScore(counts);
18385
+ const policyOutcome = evaluatePolicy2(counts, riskScore);
18386
+ let nextBestAction = computeNextBestAction(policyOutcome, counts);
18387
+ if (allowlistedCount > 0) {
18388
+ nextBestAction += ` (${allowlistedCount} finding(s) allowlisted)`;
18389
+ }
18390
+ return {
18391
+ scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
18392
+ projectRoot,
18393
+ filesScanned: artifacts.length,
18394
+ artifacts,
18395
+ findings: effectiveFindings,
18396
+ counts,
18397
+ riskScore,
18398
+ policyOutcome,
18399
+ nextBestAction
18400
+ };
18401
+ }
18402
+
18403
+ // src/avorelo/kernel/agent-artifact-guard/guard-handler.ts
18404
+ function checkActivation(dir) {
18405
+ const configPath = join65(dir, ".avorelo", "model-routing", "config.json");
18406
+ try {
18407
+ if (existsSync63(configPath)) {
18408
+ const raw = JSON.parse(readFileSync49(configPath, "utf-8"));
18409
+ return {
18410
+ activated: true,
18411
+ routingMode: raw.mode ?? "seamless",
18412
+ localFirst: raw.localFirst ?? true,
18413
+ registryLoaded: raw.registryLoaded ?? false,
18414
+ cloudRoutingEnabled: raw.cloudRoutingEnabled ?? false,
18415
+ defaultPolicy: raw.defaultPolicy ?? "safety_proof_privacy_before_cost"
18416
+ };
18417
+ }
18418
+ } catch {
18419
+ }
18420
+ return {
18421
+ activated: false,
18422
+ routingMode: "seamless",
18423
+ localFirst: true,
18424
+ registryLoaded: false,
18425
+ cloudRoutingEnabled: false,
18426
+ defaultPolicy: "safety_proof_privacy_before_cost"
18427
+ };
18428
+ }
18429
+ function getRoutingStatus(state) {
18430
+ return `Routing: ${state.activated ? "active" : "inactive"} (${state.routingMode})`;
18431
+ }
18432
+ function activationWarning(outcome, nextAction) {
18433
+ switch (outcome) {
18434
+ case "blocked":
18435
+ return `Activation warning: ${nextAction} Activation cannot claim safe readiness.`;
18436
+ case "requires_approval":
18437
+ return `Review needed: ${nextAction}`;
18438
+ case "warn":
18439
+ return `Note: ${nextAction}`;
18440
+ case "pass":
18441
+ return null;
18442
+ }
18443
+ }
18444
+ function handleActivate(dir, options) {
18445
+ const state = checkActivation(dir);
18446
+ const guardResult = scan(dir);
18447
+ const receipt = createReceipt(guardResult);
18448
+ const receiptPath = storeReceipt(dir, receipt);
18449
+ const guardLine = renderSummary(guardResult);
18450
+ const warning = activationWarning(guardResult.policyOutcome, guardResult.nextBestAction);
18451
+ const lines = [getRoutingStatus(state), guardLine];
18452
+ if (warning) lines.push(warning);
18453
+ lines.push(`Receipt: ${receiptPath}`);
18454
+ if (options.json) {
18455
+ return {
18456
+ exitCode: guardResult.policyOutcome === "blocked" ? 1 : 0,
18457
+ stdout: lines.join("\n"),
18458
+ json: {
18459
+ activated: state.activated,
18460
+ artifactGuard: {
18461
+ policyOutcome: guardResult.policyOutcome,
18462
+ riskScore: guardResult.riskScore,
18463
+ findingsCount: guardResult.findings.length,
18464
+ counts: guardResult.counts,
18465
+ nextBestAction: guardResult.nextBestAction
18466
+ },
18467
+ receiptPath
18468
+ }
18469
+ };
18470
+ }
18471
+ return {
18472
+ exitCode: guardResult.policyOutcome === "blocked" ? 1 : 0,
18473
+ stdout: lines.join("\n")
18474
+ };
18475
+ }
18476
+ function handleStatus(dir, options) {
18477
+ const state = checkActivation(dir);
18478
+ const status = getRoutingStatus(state);
18479
+ const guardResult = scan(dir);
18480
+ if (options.json) {
18481
+ return {
18482
+ exitCode: 0,
18483
+ stdout: `${status}
18484
+ ${renderSummary(guardResult)}`,
18485
+ json: {
18486
+ routing: {
18487
+ active: state.activated,
18488
+ mode: state.routingMode,
18489
+ localFirst: state.localFirst
18490
+ },
18491
+ artifactGuard: {
18492
+ policyOutcome: guardResult.policyOutcome,
18493
+ riskScore: guardResult.riskScore,
18494
+ findingsCount: guardResult.findings.length
18495
+ }
18496
+ }
18497
+ };
18498
+ }
18499
+ return { exitCode: 0, stdout: `${status}
18500
+ ${renderSummary(guardResult)}` };
18501
+ }
18502
+ function handleDoctor(dir, options) {
18503
+ const state = checkActivation(dir);
18504
+ const guardResult = scan(dir);
18505
+ const lines = [
18506
+ getRoutingStatus(state),
18507
+ `Registry: ${state.registryLoaded ? "loaded" : "not loaded"}`,
18508
+ `Cloud: ${state.cloudRoutingEnabled ? "enabled" : "disabled"}`,
18509
+ `Policy: ${state.defaultPolicy}`,
18510
+ renderSummary(guardResult)
18511
+ ];
18512
+ if (options.json) {
18513
+ return {
18514
+ exitCode: 0,
18515
+ stdout: lines.join("\n"),
18516
+ json: {
18517
+ ...state,
18518
+ artifactGuard: {
18519
+ policyOutcome: guardResult.policyOutcome,
18520
+ riskScore: guardResult.riskScore,
18521
+ findingsCount: guardResult.findings.length,
18522
+ counts: guardResult.counts
18523
+ }
18524
+ }
18525
+ };
18526
+ }
18527
+ return { exitCode: 0, stdout: lines.join("\n") };
18528
+ }
18529
+ function handleReadiness(dir, options) {
18530
+ const state = checkActivation(dir);
18531
+ const guardResult = scan(dir);
18532
+ const receipt = createReceipt(guardResult);
18533
+ storeReceipt(dir, receipt);
18534
+ const latestReceipt = loadLatestReceipt(dir);
18535
+ const artifactKinds = [...new Set(guardResult.artifacts.map((a) => a.kind))];
18536
+ const highestSeverity = guardResult.counts.critical > 0 ? "critical" : guardResult.counts.high > 0 ? "high" : guardResult.counts.medium > 0 ? "medium" : guardResult.counts.low > 0 ? "low" : "none";
18537
+ const blockingCount = guardResult.counts.critical + guardResult.counts.high;
18538
+ const lines = [
18539
+ `Readiness Check`,
18540
+ ``,
18541
+ `Routing: ${state.activated ? "active" : "inactive"}`,
18542
+ `Artifact Guard: available`,
18543
+ ` Files scanned: ${guardResult.filesScanned}`,
18544
+ ` Artifact kinds: ${artifactKinds.length > 0 ? artifactKinds.join(", ") : "none detected"}`,
18545
+ ` Highest severity: ${highestSeverity}`,
18546
+ ` Policy outcome: ${guardResult.policyOutcome.toUpperCase()}`,
18547
+ ` Blocking findings: ${blockingCount}`,
18548
+ ` Latest receipt: ${latestReceipt ? "available" : "none"}`,
18549
+ ` Next: ${guardResult.nextBestAction}`
18550
+ ];
18551
+ if (guardResult.policyOutcome === "blocked") {
18552
+ lines.push(``);
18553
+ lines.push(`Readiness: NOT READY \u2014 critical findings must be resolved`);
18554
+ } else if (guardResult.policyOutcome === "requires_approval") {
18555
+ lines.push(``);
18556
+ lines.push(`Readiness: REVIEW NEEDED \u2014 high-severity findings require approval`);
18557
+ } else if (guardResult.policyOutcome === "warn") {
18558
+ lines.push(``);
18559
+ lines.push(`Readiness: READY (with warnings)`);
18560
+ } else {
18561
+ lines.push(``);
18562
+ lines.push(`Readiness: READY`);
18563
+ }
18564
+ if (options.json) {
18565
+ return {
18566
+ exitCode: guardResult.policyOutcome === "blocked" ? 1 : 0,
18567
+ stdout: lines.join("\n"),
18568
+ json: {
18569
+ routing: { active: state.activated },
18570
+ artifactGuard: {
18571
+ available: true,
18572
+ filesScanned: guardResult.filesScanned,
18573
+ artifactKinds,
18574
+ highestSeverity,
18575
+ policyOutcome: guardResult.policyOutcome,
18576
+ riskScore: guardResult.riskScore,
18577
+ blockingFindings: blockingCount,
18578
+ counts: guardResult.counts,
18579
+ latestReceipt: latestReceipt ? true : false,
18580
+ nextBestAction: guardResult.nextBestAction
18581
+ },
18582
+ readiness: guardResult.policyOutcome === "blocked" ? "not_ready" : guardResult.policyOutcome === "requires_approval" ? "review_needed" : "ready"
18583
+ }
18584
+ };
18585
+ }
18586
+ return {
18587
+ exitCode: guardResult.policyOutcome === "blocked" ? 1 : 0,
18588
+ stdout: lines.join("\n")
18589
+ };
18590
+ }
18591
+ function handleReceipt(dir, options) {
18592
+ const sub = options.receiptSubcommand ?? "latest";
18593
+ if (sub === "latest") {
18594
+ const receipt = loadLatestReceipt(dir);
18595
+ if (!receipt) {
18596
+ return { exitCode: 0, stdout: "No artifact guard receipt found. Run `avorelo guard scan` first." };
18597
+ }
18598
+ if (options.json) {
18599
+ return {
18600
+ exitCode: 0,
18601
+ stdout: JSON.stringify(receipt, null, 2),
18602
+ json: receipt
18603
+ };
18604
+ }
18605
+ const lines = [
18606
+ `Latest Artifact Guard Receipt`,
18607
+ ` Type: ${receipt.receiptType}`,
18608
+ ` Scanned: ${receipt.scannedAt}`,
18609
+ ` Files: ${receipt.filesScanned}`,
18610
+ ` Findings: ${receipt.findingSummary.total}`,
18611
+ ` Risk score: ${receipt.riskScore}/10`,
18612
+ ` Outcome: ${receipt.policyOutcome.toUpperCase()}`,
18613
+ ` Next: ${receipt.nextBestAction}`,
18614
+ ` Raw content stored: ${receipt.contentStored}`
18615
+ ];
18616
+ return { exitCode: 0, stdout: lines.join("\n") };
18617
+ }
18618
+ return { exitCode: 2, stdout: "Usage: avorelo receipt latest" };
18619
+ }
18620
+ function handleGuard(dir, options) {
18621
+ const sub = options.guardSubcommand ?? "scan";
18622
+ if (sub === "explain") {
18623
+ const ruleId = options.guardArgs?.[0];
18624
+ if (!ruleId) {
18625
+ return { exitCode: 2, stdout: "Usage: avorelo guard explain <ruleId>" };
18626
+ }
18627
+ const rule = getRule(ruleId);
18628
+ if (!rule) {
18629
+ return { exitCode: 2, stdout: `Unknown rule: ${ruleId}` };
18630
+ }
18631
+ const lines = [
18632
+ `Rule: ${rule.id}`,
18633
+ `Title: ${rule.title}`,
18634
+ `Severity: ${rule.severity}`,
18635
+ `Category: ${rule.category}`,
18636
+ `Description: ${rule.description}`,
18637
+ `Recommended action: ${rule.recommendedAction}`,
18638
+ `Applies to: ${rule.artifactKinds === "all" ? "all artifact kinds" : rule.artifactKinds.join(", ")}`
18639
+ ];
18640
+ return { exitCode: 0, stdout: lines.join("\n") };
18641
+ }
18642
+ const result3 = scan(dir);
18643
+ const receipt = createReceipt(result3);
18644
+ const receiptPath = storeReceipt(dir, receipt);
18645
+ if (options.json) {
18646
+ return {
18647
+ exitCode: result3.policyOutcome === "blocked" ? 1 : 0,
18648
+ stdout: toJson(result3),
18649
+ json: result3
18650
+ };
18651
+ }
18652
+ if (options.ci) {
18653
+ return {
18654
+ exitCode: result3.policyOutcome === "blocked" ? 1 : 0,
18655
+ stdout: toJson(result3)
18656
+ };
18657
+ }
18658
+ const report = renderReport(result3);
18659
+ return {
18660
+ exitCode: result3.policyOutcome === "blocked" ? 1 : 0,
18661
+ stdout: `${report}
18662
+ Receipt: ${receiptPath}`
18663
+ };
18664
+ }
18665
+ function handleCli(options, dir) {
18666
+ switch (options.command) {
18667
+ case "activate":
18668
+ return handleActivate(dir, options);
18669
+ case "status":
18670
+ return handleStatus(dir, options);
18671
+ case "doctor":
18672
+ return handleDoctor(dir, options);
18673
+ case "guard":
18674
+ return handleGuard(dir, options);
18675
+ case "readiness":
18676
+ return handleReadiness(dir, options);
18677
+ case "receipt":
18678
+ return handleReceipt(dir, options);
18679
+ case "run":
18680
+ return { exitCode: 0, stdout: "run not implemented in guard handler" };
18681
+ }
18682
+ }
18683
+
17611
18684
  // src/avorelo/surfaces/cli/avorelo.ts
17612
18685
  function arg(args, name, dflt) {
17613
18686
  const i = args.indexOf(name);
@@ -17695,6 +18768,10 @@ function help() {
17695
18768
  "",
17696
18769
  " context check [--target <dir>] [--json] [--strict] [--ci] Agent context integrity check",
17697
18770
  "",
18771
+ " guard scan [--target <dir>] [--json] [--ci] Scan agent artifacts for risks",
18772
+ " guard explain <rule-id> Explain a detection rule",
18773
+ " receipt latest [--target <dir>] [--json] Show latest artifact guard receipt",
18774
+ "",
17698
18775
  " dogfood-learning status [--target <dir>] [--json] Show learning status",
17699
18776
  " dogfood-learning preview [--target <dir>] [--json] Preview sanitized learning payload",
17700
18777
  " dogfood-learning send [--target <dir>] [--dry-run] [--json] Send or queue learning signal",
@@ -17738,8 +18815,8 @@ async function cmdActivate(args) {
17738
18815
  const r2 = activate(target, { approve: true });
17739
18816
  persistReceipt(target, r2.receipt);
17740
18817
  const state2 = runFullActivation(target);
17741
- state2.setupSteps.push({ id: "hooks_installed", label: "Claude Code hooks installed", status: r2.ok ? "passed" : "blocked", evidencePath: join61(target, ".claude", "settings.json") });
17742
- if (r2.ok) state2.receipts.push({ id: r2.receipt.receiptId, path: join61(target, ".avorelo", "receipts", `${r2.receipt.receiptId}.json`), type: "activation_with_hooks" });
18818
+ state2.setupSteps.push({ id: "hooks_installed", label: "Claude Code hooks installed", status: r2.ok ? "passed" : "blocked", evidencePath: join66(target, ".claude", "settings.json") });
18819
+ if (r2.ok) state2.receipts.push({ id: r2.receipt.receiptId, path: join66(target, ".avorelo", "receipts", `${r2.receipt.receiptId}.json`), type: "activation_with_hooks" });
17743
18820
  persistActivationV2(target, state2);
17744
18821
  process.stdout.write(JSON.stringify({ ok: r2.ok, mode: "activate-with-hooks", target, installed: r2.validate.wellFormed, receipt: r2.receipt.receiptId }, null, 2) + "\n");
17745
18822
  return r2.ok ? 0 : 1;
@@ -17762,7 +18839,7 @@ Activation taxonomy: ${preflight.taxonomy}
17762
18839
  process.stderr.write("Continuing with activation despite warnings...\n\n");
17763
18840
  }
17764
18841
  const state = runFullActivation(target);
17765
- const contract = createWorkContract({ contractId: "canonical-activate", objective: "full local-first activation", allowedPaths: [join61(target, ".avorelo")], planTier: "Free" });
18842
+ const contract = createWorkContract({ contractId: "canonical-activate", objective: "full local-first activation", allowedPaths: [join66(target, ".avorelo")], planTier: "Free" });
17766
18843
  const ledger = new StateLedger();
17767
18844
  const receipt = writeReceipt(ledger, {
17768
18845
  contractId: contract.contractId,
@@ -17777,7 +18854,7 @@ Activation taxonomy: ${preflight.taxonomy}
17777
18854
  redactionClasses: [],
17778
18855
  receiptId: "rcpt_canonical_activation"
17779
18856
  });
17780
- state.receipts.push({ id: receipt.receiptId, path: join61(target, ".avorelo", "receipts", `${receipt.receiptId}.json`), type: "canonical_activation" });
18857
+ state.receipts.push({ id: receipt.receiptId, path: join66(target, ".avorelo", "receipts", `${receipt.receiptId}.json`), type: "canonical_activation" });
17781
18858
  persistActivationV2(target, state);
17782
18859
  persistReceipt(target, receipt);
17783
18860
  const detection = runFullDetection(target);
@@ -17790,7 +18867,7 @@ Activation taxonomy: ${preflight.taxonomy}
17790
18867
  ` Mode: ${state.activationMode}`,
17791
18868
  ` Scope: ${scope}`,
17792
18869
  ` Status: ${state.activationStatus}`,
17793
- ` State: ${join61(target, ACTIVATION_STATE_DIR, ACTIVATION_STATE_FILE)}`
18870
+ ` State: ${join66(target, ACTIVATION_STATE_DIR, ACTIVATION_STATE_FILE)}`
17794
18871
  ];
17795
18872
  if (fv.found.length > 0) {
17796
18873
  lines.push("", " Found:");
@@ -18071,7 +19148,7 @@ function cmdStatus(args) {
18071
19148
  ` activation: ${state.activationStatus}`,
18072
19149
  ` mode: ${state.activationMode}`,
18073
19150
  ` contract: ${state.contract}`,
18074
- ` state: ${join61(target, ACTIVATION_STATE_DIR, ACTIVATION_STATE_FILE)}`,
19151
+ ` state: ${join66(target, ACTIVATION_STATE_DIR, ACTIVATION_STATE_FILE)}`,
18075
19152
  ` hooks: ${v.installed ? v.wellFormed ? "installed (6/6)" : `partial` : "not installed"}`
18076
19153
  ];
18077
19154
  if (isV2) {
@@ -18144,7 +19221,7 @@ function cmdVerify(args) {
18144
19221
  }
18145
19222
  const input = loadProofInput(target);
18146
19223
  if (input) {
18147
- const contract = createWorkContract({ contractId: "verify", objective: input.objective ?? "verify real-workflow proof", allowedPaths: [join61(target, "src")], planTier: "Free" });
19224
+ const contract = createWorkContract({ contractId: "verify", objective: input.objective ?? "verify real-workflow proof", allowedPaths: [join66(target, "src")], planTier: "Free" });
18148
19225
  const r2 = evaluateProof({ contract, artifacts: input.artifacts, readbacks: input.readbacks, dir: target, sampleSize: input.sampleSize, receiptId: "rcpt_verify" });
18149
19226
  process.stdout.write(JSON.stringify({ scope: "full", activationState: { valid: true }, proof: { decision: r2.decision, confidence: r2.confidence } }, null, 2) + "\n");
18150
19227
  return r2.decision === "STOP_DONE" ? 0 : 1;
@@ -18153,13 +19230,13 @@ function cmdVerify(args) {
18153
19230
  return 0;
18154
19231
  }
18155
19232
  function cmdSite(args) {
18156
- const outDir = arg(args, "--out", join61(process.cwd(), ".avorelo", "site"));
19233
+ const outDir = arg(args, "--out", join66(process.cwd(), ".avorelo", "site"));
18157
19234
  const r2 = buildSite(outDir);
18158
19235
  process.stdout.write(JSON.stringify({ ok: r2.ok, outDir: r2.outDir, indexPath: r2.indexPath, pages: r2.pages }, null, 2) + "\n");
18159
19236
  return r2.ok ? 0 : 1;
18160
19237
  }
18161
19238
  function cmdServe(args) {
18162
- const outDir = arg(args, "--out", join61(process.cwd(), ".avorelo", "site"));
19239
+ const outDir = arg(args, "--out", join66(process.cwd(), ".avorelo", "site"));
18163
19240
  const port = Number(arg(args, "--port", "0"));
18164
19241
  buildSite(outDir);
18165
19242
  serve(outDir, { port: Number.isFinite(port) ? port : 0 }).then((h) => {
@@ -18191,7 +19268,7 @@ function cmdServe(args) {
18191
19268
  }
18192
19269
  function readStdinJson() {
18193
19270
  try {
18194
- const raw = readFileSync46(0, "utf8");
19271
+ const raw = readFileSync50(0, "utf8");
18195
19272
  return raw.trim() ? JSON.parse(raw) : {};
18196
19273
  } catch {
18197
19274
  return {};
@@ -18215,9 +19292,9 @@ function cmdLifecycleHook(args) {
18215
19292
  const sourceLabel = String(ccEvent.tool_name ?? "").toLowerCase().includes("mcp") ? "tool_output" : "tool_output";
18216
19293
  const r3 = scanContent({ content: toolOutput, sourceKind: sourceLabel });
18217
19294
  try {
18218
- const dir = join61(cwd, ".avorelo", "secret-boundary");
18219
- mkdirSync39(dir, { recursive: true });
18220
- appendFileSync11(join61(dir, "receipts.jsonl"), JSON.stringify(redact(r3.receipt).value) + "\n");
19295
+ const dir = join66(cwd, ".avorelo", "secret-boundary");
19296
+ mkdirSync40(dir, { recursive: true });
19297
+ appendFileSync11(join66(dir, "receipts.jsonl"), JSON.stringify(redact(r3.receipt).value) + "\n");
18221
19298
  } catch {
18222
19299
  }
18223
19300
  process.stdout.write(JSON.stringify({
@@ -18234,13 +19311,13 @@ function cmdLifecycleHook(args) {
18234
19311
  }
18235
19312
  const looksLikeToolRequest = payloadRaw && ccEvent.tool !== void 0 && ccEvent.workingDir !== void 0;
18236
19313
  const req = looksLikeToolRequest ? ccEvent : mapClaudeCodeEvent(ccEvent, cwd);
18237
- const contract = createWorkContract({ contractId: "hook", objective: "lifecycle hook", allowedPaths: [join61(cwd, "src")], planTier: "Free" });
19314
+ const contract = createWorkContract({ contractId: "hook", objective: "lifecycle hook", allowedPaths: [join66(cwd, "src")], planTier: "Free" });
18238
19315
  const r2 = handleLifecycleHook(event, req, { contract });
18239
19316
  try {
18240
- const dir = join61(cwd, ".avorelo", "events");
18241
- mkdirSync39(dir, { recursive: true });
19317
+ const dir = join66(cwd, ".avorelo", "events");
19318
+ mkdirSync40(dir, { recursive: true });
18242
19319
  const entry = redact({ ts: Date.now(), event: r2.event, tool: req.tool, verdict: r2.verdict, reasonCodes: r2.reasonCodes, redactionClasses: r2.redactionClasses, latencyMs: r2.latencyMs }).value;
18243
- appendFileSync11(join61(dir, "hook-fires.jsonl"), JSON.stringify(entry) + "\n");
19320
+ appendFileSync11(join66(dir, "hook-fires.jsonl"), JSON.stringify(entry) + "\n");
18244
19321
  } catch {
18245
19322
  }
18246
19323
  if (r2.exitCode === 2) {
@@ -18647,13 +19724,13 @@ function cmdTokenCost(args) {
18647
19724
  }
18648
19725
  if (sub === "import" || sub === "validate") {
18649
19726
  const file = arg(args, "--file");
18650
- if (!file || !existsSync62(file)) {
19727
+ if (!file || !existsSync64(file)) {
18651
19728
  process.stderr.write("Usage: avorelo token-cost " + sub + " --file <path> [--json]\n");
18652
19729
  return 2;
18653
19730
  }
18654
19731
  let parsed;
18655
19732
  try {
18656
- parsed = JSON.parse(readFileSync46(file, "utf8"));
19733
+ parsed = JSON.parse(readFileSync50(file, "utf8"));
18657
19734
  } catch {
18658
19735
  process.stderr.write("Invalid JSON file.\n");
18659
19736
  return 1;
@@ -18794,9 +19871,9 @@ function cmdContextCheck(args) {
18794
19871
  const ci = args.includes("--ci");
18795
19872
  const wcPath = arg(args, "--work-contract");
18796
19873
  let workContract;
18797
- if (wcPath && existsSync62(wcPath)) {
19874
+ if (wcPath && existsSync64(wcPath)) {
18798
19875
  try {
18799
- workContract = JSON.parse(readFileSync46(wcPath, "utf8"));
19876
+ workContract = JSON.parse(readFileSync50(wcPath, "utf8"));
18800
19877
  } catch {
18801
19878
  }
18802
19879
  }
@@ -18854,12 +19931,52 @@ function cmdContext(args) {
18854
19931
  ].filter(Boolean).join("\n"));
18855
19932
  return 0;
18856
19933
  }
19934
+ function cmdGuard(args) {
19935
+ const sub = args[0];
19936
+ const target = arg(args, "--target", process.cwd());
19937
+ const asJson = args.includes("--json");
19938
+ const ci = args.includes("--ci");
19939
+ if (sub === "scan") {
19940
+ const output = handleCli(
19941
+ { command: "guard", json: asJson, verbose: false, guardSubcommand: "scan", ci },
19942
+ target
19943
+ );
19944
+ process.stdout.write(output.stdout + "\n");
19945
+ return output.exitCode;
19946
+ }
19947
+ if (sub === "explain") {
19948
+ const ruleId = args[1];
19949
+ const output = handleCli(
19950
+ { command: "guard", json: false, verbose: false, guardSubcommand: "explain", guardArgs: [ruleId] },
19951
+ target
19952
+ );
19953
+ process.stdout.write(output.stdout + "\n");
19954
+ return output.exitCode;
19955
+ }
19956
+ process.stderr.write("Usage: avorelo guard <scan|explain> [args]\n");
19957
+ return 2;
19958
+ }
19959
+ function cmdReceipt(args) {
19960
+ const sub = args[0];
19961
+ const target = arg(args, "--target", process.cwd());
19962
+ const asJson = args.includes("--json");
19963
+ if (sub === "latest" || sub === void 0) {
19964
+ const output = handleCli(
19965
+ { command: "receipt", json: asJson, verbose: false, receiptSubcommand: "latest" },
19966
+ target
19967
+ );
19968
+ process.stdout.write(output.stdout + "\n");
19969
+ return output.exitCode;
19970
+ }
19971
+ process.stderr.write("Usage: avorelo receipt latest [--target <dir>] [--json]\n");
19972
+ return 2;
19973
+ }
18857
19974
  function cmdSecretBoundary(args) {
18858
19975
  const sub = args[0];
18859
19976
  const asJson = args.includes("--json");
18860
19977
  let content = arg(args, "--content");
18861
19978
  const file = arg(args, "--file");
18862
- if (file && existsSync62(file)) content = readFileSync46(file, "utf8");
19979
+ if (file && existsSync64(file)) content = readFileSync50(file, "utf8");
18863
19980
  if (content === void 0) content = "";
18864
19981
  if (sub === "scan" || sub === void 0) {
18865
19982
  const r2 = scanContent({ content, sourceKind: file ? "file" : "tool_output" });
@@ -18926,7 +20043,7 @@ function cmdExplain(args) {
18926
20043
  const changedFiles = [];
18927
20044
  for (const { adapter } of detected) {
18928
20045
  const surface = adapter.getInstructionSurface(target);
18929
- if (surface && existsSync62(surface)) changedFiles.push(surface);
20046
+ if (surface && existsSync64(surface)) changedFiles.push(surface);
18930
20047
  }
18931
20048
  if (changedFiles.length > 0) {
18932
20049
  lines.push(" Changed:");
@@ -18940,7 +20057,7 @@ function cmdExplain(args) {
18940
20057
  lines.push("");
18941
20058
  lines.push(` Hooks: ${hookValidation.installed ? "installed" : "not installed"}`);
18942
20059
  lines.push(" Cloud: off (local-first)");
18943
- lines.push(` Receipts: ${join61(target, ".avorelo", "receipts")}`);
20060
+ lines.push(` Receipts: ${join66(target, ".avorelo", "receipts")}`);
18944
20061
  const sessionStatus = getSessionStatus(target);
18945
20062
  if (sessionStatus) {
18946
20063
  lines.push("");
@@ -19100,7 +20217,7 @@ function cmdFeedback(args) {
19100
20217
  }
19101
20218
  if (sub === "share") {
19102
20219
  const file = arg(args, "--file");
19103
- if (!file || !existsSync62(file)) {
20220
+ if (!file || !existsSync64(file)) {
19104
20221
  process.stderr.write("Usage: avorelo feedback share --file <bundle-path>\n");
19105
20222
  return 2;
19106
20223
  }
@@ -19404,7 +20521,7 @@ No loop metadata found for ${loopId}.
19404
20521
  if (sub === "doctor") {
19405
20522
  const issues = [];
19406
20523
  const ok = [];
19407
- if (existsSync62(join61(target, ".git"))) {
20524
+ if (existsSync64(join66(target, ".git"))) {
19408
20525
  ok.push("Git repository detected");
19409
20526
  } else {
19410
20527
  issues.push("Not a git repository \u2014 loop needs git for drift detection");
@@ -19414,12 +20531,12 @@ No loop metadata found for ${loopId}.
19414
20531
  } else {
19415
20532
  issues.push("Claude Code CLI not found \u2014 install it or ensure `claude` is on PATH");
19416
20533
  }
19417
- const testDir = join61(target, ".avorelo", "loops");
20534
+ const testDir = join66(target, ".avorelo", "loops");
19418
20535
  try {
19419
- mkdirSync39(testDir, { recursive: true });
19420
- const testFile = join61(testDir, ".doctor_probe");
19421
- writeFileSync37(testFile, "probe");
19422
- unlinkSync4(testFile);
20536
+ mkdirSync40(testDir, { recursive: true });
20537
+ const testFile = join66(testDir, ".doctor_probe");
20538
+ writeFileSync38(testFile, "probe");
20539
+ unlinkSync5(testFile);
19423
20540
  ok.push("Storage writable (.avorelo/loops/)");
19424
20541
  } catch {
19425
20542
  issues.push("Cannot write to .avorelo/loops/ \u2014 check permissions");
@@ -19460,11 +20577,11 @@ function cmdUninstallAll(args) {
19460
20577
  const target = arg(args, "--target", process.cwd());
19461
20578
  const adapterResult = uninstallAll(target);
19462
20579
  const hookResult = uninstall(target);
19463
- const removed = [...adapterResult.removed, ...hookResult.restored ? [join61(target, ".claude", "settings.json")] : []];
20580
+ const removed = [...adapterResult.removed, ...hookResult.restored ? [join66(target, ".claude", "settings.json")] : []];
19464
20581
  const preserved = adapterResult.preserved;
19465
- const avoreloDir2 = join61(target, ".avorelo");
20582
+ const avoreloDir2 = join66(target, ".avorelo");
19466
20583
  try {
19467
- if (existsSync62(avoreloDir2)) {
20584
+ if (existsSync64(avoreloDir2)) {
19468
20585
  rmSync3(avoreloDir2, { recursive: true, force: true });
19469
20586
  removed.push(avoreloDir2);
19470
20587
  }
@@ -20057,7 +21174,7 @@ function cmdConfig(args) {
20057
21174
  function avoreloVersion() {
20058
21175
  for (const rel of ["../package.json", "../../package.json", "../../../package.json", "../../../../package.json"]) {
20059
21176
  try {
20060
- const pkg = JSON.parse(readFileSync46(join61(import.meta.dirname, rel), "utf8"));
21177
+ const pkg = JSON.parse(readFileSync50(join66(import.meta.dirname, rel), "utf8"));
20061
21178
  if (pkg.name === "avorelo" && pkg.version) return pkg.version;
20062
21179
  } catch {
20063
21180
  }
@@ -20168,6 +21285,10 @@ function main(argv) {
20168
21285
  return cmdReadiness(rest);
20169
21286
  case "loop":
20170
21287
  return cmdLoop(rest);
21288
+ case "guard":
21289
+ return cmdGuard(rest);
21290
+ case "receipt":
21291
+ return cmdReceipt(rest);
20171
21292
  default:
20172
21293
  return help();
20173
21294
  }