@vigolium/piolium 0.0.1

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 (271) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +117 -0
  3. package/agents/access-auditor.md +300 -0
  4. package/agents/assumption-breaker.md +154 -0
  5. package/agents/attack-designer.md +116 -0
  6. package/agents/code-scanner.md +139 -0
  7. package/agents/concurrency-auditor.md +238 -0
  8. package/agents/confirm-writer.md +257 -0
  9. package/agents/context-reviewer.md +274 -0
  10. package/agents/cross-verifier.md +165 -0
  11. package/agents/cve-scout.md +381 -0
  12. package/agents/env-builder.md +282 -0
  13. package/agents/env-profiler.md +205 -0
  14. package/agents/evidence-collector.md +140 -0
  15. package/agents/finding-grader.md +142 -0
  16. package/agents/finding-writer.md +148 -0
  17. package/agents/flow-tracer.md +106 -0
  18. package/agents/goal-backtracer.md +146 -0
  19. package/agents/history-miner.md +467 -0
  20. package/agents/independent-verifier.md +118 -0
  21. package/agents/intent-mapper.md +183 -0
  22. package/agents/longshot-collector.md +128 -0
  23. package/agents/longshot-prober.md +126 -0
  24. package/agents/patch-auditor.md +73 -0
  25. package/agents/poc-author.md +124 -0
  26. package/agents/poc-runner.md +194 -0
  27. package/agents/probe-lead.md +269 -0
  28. package/agents/red-challenger.md +101 -0
  29. package/agents/report-composer.md +208 -0
  30. package/agents/review-adjudicator.md +216 -0
  31. package/agents/spec-auditor.md +155 -0
  32. package/agents/taint-tracer.md +265 -0
  33. package/agents/test-locator.md +209 -0
  34. package/agents/threat-modeler.md +132 -0
  35. package/agents/variant-scanner.md +108 -0
  36. package/agents/variant-spotter.md +110 -0
  37. package/bin/piolium.mjs +376 -0
  38. package/extensions/piolium/_vendor/yaml.bundle.d.mts +6 -0
  39. package/extensions/piolium/_vendor/yaml.bundle.mjs +139 -0
  40. package/extensions/piolium/agent-runner.ts +322 -0
  41. package/extensions/piolium/agents.ts +266 -0
  42. package/extensions/piolium/audit-state.ts +522 -0
  43. package/extensions/piolium/bundled-resources.ts +97 -0
  44. package/extensions/piolium/candidate-scan.ts +966 -0
  45. package/extensions/piolium/command-target.ts +177 -0
  46. package/extensions/piolium/console-stream.ts +57 -0
  47. package/extensions/piolium/export-results.ts +380 -0
  48. package/extensions/piolium/findings.ts +448 -0
  49. package/extensions/piolium/heartbeat.ts +182 -0
  50. package/extensions/piolium/help.ts +234 -0
  51. package/extensions/piolium/index.ts +1865 -0
  52. package/extensions/piolium/longshot.ts +530 -0
  53. package/extensions/piolium/matcher-suggestions.ts +196 -0
  54. package/extensions/piolium/matcher-utils.ts +83 -0
  55. package/extensions/piolium/modes/balanced.ts +750 -0
  56. package/extensions/piolium/modes/confirm-bootstrap.ts +186 -0
  57. package/extensions/piolium/modes/confirm.ts +697 -0
  58. package/extensions/piolium/modes/deep.ts +917 -0
  59. package/extensions/piolium/modes/diff.ts +177 -0
  60. package/extensions/piolium/modes/lite.ts +540 -0
  61. package/extensions/piolium/modes/longshot.ts +595 -0
  62. package/extensions/piolium/modes/merge.ts +204 -0
  63. package/extensions/piolium/modes/phase-runner.ts +267 -0
  64. package/extensions/piolium/modes/reinvest.ts +546 -0
  65. package/extensions/piolium/modes/revisit.ts +279 -0
  66. package/extensions/piolium/modes.ts +48 -0
  67. package/extensions/piolium/phase-labels.ts +123 -0
  68. package/extensions/piolium/phase-status-strip.ts +92 -0
  69. package/extensions/piolium/prompt-prefix-editor.ts +39 -0
  70. package/extensions/piolium/providers/anthropic-vertex.ts +836 -0
  71. package/extensions/piolium/recon.ts +409 -0
  72. package/extensions/piolium/result-stats.ts +105 -0
  73. package/extensions/piolium/retry.ts +120 -0
  74. package/extensions/piolium/scheduler.ts +212 -0
  75. package/extensions/piolium/secrets.ts +368 -0
  76. package/extensions/piolium/tools/web-tools.ts +148 -0
  77. package/package.json +77 -0
  78. package/skills/agentic-actions-auditor/SKILL.md +327 -0
  79. package/skills/agentic-actions-auditor/references/action-profiles.md +186 -0
  80. package/skills/agentic-actions-auditor/references/cross-file-resolution.md +209 -0
  81. package/skills/agentic-actions-auditor/references/foundations.md +94 -0
  82. package/skills/agentic-actions-auditor/references/vector-a-env-var-intermediary.md +77 -0
  83. package/skills/agentic-actions-auditor/references/vector-b-direct-expression-injection.md +83 -0
  84. package/skills/agentic-actions-auditor/references/vector-c-cli-data-fetch.md +83 -0
  85. package/skills/agentic-actions-auditor/references/vector-d-pr-target-checkout.md +88 -0
  86. package/skills/agentic-actions-auditor/references/vector-e-error-log-injection.md +88 -0
  87. package/skills/agentic-actions-auditor/references/vector-f-subshell-expansion.md +82 -0
  88. package/skills/agentic-actions-auditor/references/vector-g-eval-of-ai-output.md +91 -0
  89. package/skills/agentic-actions-auditor/references/vector-h-dangerous-sandbox-configs.md +102 -0
  90. package/skills/agentic-actions-auditor/references/vector-i-wildcard-allowlists.md +88 -0
  91. package/skills/audit/SKILL.md +562 -0
  92. package/skills/audit/assets/icon.svg +7 -0
  93. package/skills/audit/hooks/scripts/validate_phase_output.py +550 -0
  94. package/skills/audit/references/adversarial-review.md +148 -0
  95. package/skills/audit/references/architecture-aware-sast.md +306 -0
  96. package/skills/audit/references/audit-workflow.md +737 -0
  97. package/skills/audit/references/chamber-protocol.md +384 -0
  98. package/skills/audit/references/creative-attack-modes.md +221 -0
  99. package/skills/audit/references/deep-analysis.md +273 -0
  100. package/skills/audit/references/domain-attack-playbooks.md +1129 -0
  101. package/skills/audit/references/knowledge-base-template.md +513 -0
  102. package/skills/audit/references/real-env-validation.md +191 -0
  103. package/skills/audit/references/report-templates.md +417 -0
  104. package/skills/audit/references/triage-and-prereqs.md +134 -0
  105. package/skills/audit/scripts/consolidate_drafts.py +554 -0
  106. package/skills/audit/scripts/partition_findings.py +152 -0
  107. package/skills/audit/scripts/rg-hotspots.sh +121 -0
  108. package/skills/audit/scripts/stamp_file_state.py +349 -0
  109. package/skills/code-reviewer/SKILL.md +65 -0
  110. package/skills/codeql/SKILL.md +281 -0
  111. package/skills/codeql/references/build-fixes.md +90 -0
  112. package/skills/codeql/references/diagnostic-query-templates.md +339 -0
  113. package/skills/codeql/references/extension-yaml-format.md +209 -0
  114. package/skills/codeql/references/important-only-suite.md +153 -0
  115. package/skills/codeql/references/language-details.md +207 -0
  116. package/skills/codeql/references/macos-arm64e-workaround.md +179 -0
  117. package/skills/codeql/references/performance-tuning.md +111 -0
  118. package/skills/codeql/references/quality-assessment.md +172 -0
  119. package/skills/codeql/references/ruleset-catalog.md +63 -0
  120. package/skills/codeql/references/run-all-suite.md +92 -0
  121. package/skills/codeql/references/sarif-processing.md +79 -0
  122. package/skills/codeql/references/threat-models.md +51 -0
  123. package/skills/codeql/workflows/build-database.md +280 -0
  124. package/skills/codeql/workflows/create-data-extensions.md +261 -0
  125. package/skills/codeql/workflows/run-analysis.md +301 -0
  126. package/skills/differential-review/SKILL.md +220 -0
  127. package/skills/differential-review/adversarial.md +203 -0
  128. package/skills/differential-review/methodology.md +234 -0
  129. package/skills/differential-review/patterns.md +300 -0
  130. package/skills/differential-review/reporting.md +369 -0
  131. package/skills/fp-check/SKILL.md +125 -0
  132. package/skills/fp-check/references/bug-class-verification.md +114 -0
  133. package/skills/fp-check/references/deep-verification.md +143 -0
  134. package/skills/fp-check/references/evidence-templates.md +91 -0
  135. package/skills/fp-check/references/false-positive-patterns.md +115 -0
  136. package/skills/fp-check/references/gate-reviews.md +27 -0
  137. package/skills/fp-check/references/standard-verification.md +78 -0
  138. package/skills/insecure-defaults/SKILL.md +117 -0
  139. package/skills/insecure-defaults/references/examples.md +409 -0
  140. package/skills/last30days/SKILL.md +444 -0
  141. package/skills/sarif-parsing/SKILL.md +483 -0
  142. package/skills/sarif-parsing/resources/jq-queries.md +162 -0
  143. package/skills/sarif-parsing/resources/sarif_helpers.py +331 -0
  144. package/skills/security-threat-model/LICENSE.txt +201 -0
  145. package/skills/security-threat-model/SKILL.md +81 -0
  146. package/skills/security-threat-model/agents/openai.yaml +4 -0
  147. package/skills/security-threat-model/references/prompt-template.md +255 -0
  148. package/skills/security-threat-model/references/security-controls-and-assets.md +32 -0
  149. package/skills/semgrep/SKILL.md +212 -0
  150. package/skills/semgrep/references/rulesets.md +162 -0
  151. package/skills/semgrep/references/scan-modes.md +110 -0
  152. package/skills/semgrep/references/scanner-task-prompt.md +140 -0
  153. package/skills/semgrep/scripts/merge_sarif.py +203 -0
  154. package/skills/semgrep/workflows/scan-workflow.md +311 -0
  155. package/skills/semgrep-rule-creator/SKILL.md +168 -0
  156. package/skills/semgrep-rule-creator/references/quick-reference.md +202 -0
  157. package/skills/semgrep-rule-creator/references/workflow.md +240 -0
  158. package/skills/semgrep-rule-variant-creator/SKILL.md +205 -0
  159. package/skills/semgrep-rule-variant-creator/references/applicability-analysis.md +250 -0
  160. package/skills/semgrep-rule-variant-creator/references/language-syntax-guide.md +324 -0
  161. package/skills/semgrep-rule-variant-creator/references/workflow.md +518 -0
  162. package/skills/sharp-edges/SKILL.md +292 -0
  163. package/skills/sharp-edges/references/auth-patterns.md +252 -0
  164. package/skills/sharp-edges/references/case-studies.md +274 -0
  165. package/skills/sharp-edges/references/config-patterns.md +333 -0
  166. package/skills/sharp-edges/references/crypto-apis.md +190 -0
  167. package/skills/sharp-edges/references/lang-c.md +205 -0
  168. package/skills/sharp-edges/references/lang-csharp.md +285 -0
  169. package/skills/sharp-edges/references/lang-go.md +270 -0
  170. package/skills/sharp-edges/references/lang-java.md +263 -0
  171. package/skills/sharp-edges/references/lang-javascript.md +269 -0
  172. package/skills/sharp-edges/references/lang-kotlin.md +265 -0
  173. package/skills/sharp-edges/references/lang-php.md +245 -0
  174. package/skills/sharp-edges/references/lang-python.md +274 -0
  175. package/skills/sharp-edges/references/lang-ruby.md +273 -0
  176. package/skills/sharp-edges/references/lang-rust.md +272 -0
  177. package/skills/sharp-edges/references/lang-swift.md +287 -0
  178. package/skills/sharp-edges/references/language-specific.md +588 -0
  179. package/skills/spec-to-code-compliance/SKILL.md +357 -0
  180. package/skills/spec-to-code-compliance/resources/COMPLETENESS_CHECKLIST.md +69 -0
  181. package/skills/spec-to-code-compliance/resources/IR_EXAMPLES.md +417 -0
  182. package/skills/spec-to-code-compliance/resources/OUTPUT_REQUIREMENTS.md +105 -0
  183. package/skills/supply-chain-risk-auditor/SKILL.md +67 -0
  184. package/skills/supply-chain-risk-auditor/resources/results-template.md +41 -0
  185. package/skills/variant-analysis/METHODOLOGY.md +327 -0
  186. package/skills/variant-analysis/SKILL.md +142 -0
  187. package/skills/variant-analysis/resources/codeql/cpp.ql +119 -0
  188. package/skills/variant-analysis/resources/codeql/go.ql +69 -0
  189. package/skills/variant-analysis/resources/codeql/java.ql +71 -0
  190. package/skills/variant-analysis/resources/codeql/javascript.ql +63 -0
  191. package/skills/variant-analysis/resources/codeql/python.ql +80 -0
  192. package/skills/variant-analysis/resources/semgrep/cpp.yaml +98 -0
  193. package/skills/variant-analysis/resources/semgrep/go.yaml +63 -0
  194. package/skills/variant-analysis/resources/semgrep/java.yaml +61 -0
  195. package/skills/variant-analysis/resources/semgrep/javascript.yaml +60 -0
  196. package/skills/variant-analysis/resources/semgrep/python.yaml +72 -0
  197. package/skills/variant-analysis/resources/variant-report-template.md +75 -0
  198. package/skills/vuln-report/SKILL.md +137 -0
  199. package/skills/vuln-report/agents/openai.yaml +4 -0
  200. package/skills/vuln-report/references/report-template.md +135 -0
  201. package/skills/wooyun-legacy/SKILL.md +367 -0
  202. package/skills/wooyun-legacy/references/bank-penetration.md +222 -0
  203. package/skills/wooyun-legacy/references/checklists/command-execution-checklist.md +119 -0
  204. package/skills/wooyun-legacy/references/checklists/csrf-checklist.md +74 -0
  205. package/skills/wooyun-legacy/references/checklists/file-upload-checklist.md +108 -0
  206. package/skills/wooyun-legacy/references/checklists/info-disclosure-checklist.md +114 -0
  207. package/skills/wooyun-legacy/references/checklists/logic-flaws-checklist.md +95 -0
  208. package/skills/wooyun-legacy/references/checklists/misconfig-checklist.md +124 -0
  209. package/skills/wooyun-legacy/references/checklists/path-traversal-checklist.md +87 -0
  210. package/skills/wooyun-legacy/references/checklists/rce-checklist.md +93 -0
  211. package/skills/wooyun-legacy/references/checklists/sql-injection-checklist.md +97 -0
  212. package/skills/wooyun-legacy/references/checklists/ssrf-checklist.md +99 -0
  213. package/skills/wooyun-legacy/references/checklists/unauthorized-access-checklist.md +89 -0
  214. package/skills/wooyun-legacy/references/checklists/weak-password-checklist.md +115 -0
  215. package/skills/wooyun-legacy/references/checklists/xss-checklist.md +103 -0
  216. package/skills/wooyun-legacy/references/checklists/xxe-checklist.md +130 -0
  217. package/skills/wooyun-legacy/references/info-disclosure.md +975 -0
  218. package/skills/wooyun-legacy/references/logic-flaws.md +721 -0
  219. package/skills/wooyun-legacy/references/path-traversal.md +1191 -0
  220. package/skills/wooyun-legacy/references/telecom-penetration.md +156 -0
  221. package/skills/wooyun-legacy/references/unauthorized-access.md +980 -0
  222. package/skills/wooyun-legacy/references/xss.md +746 -0
  223. package/skills/zeroize-audit/SKILL.md +371 -0
  224. package/skills/zeroize-audit/configs/c.yaml +21 -0
  225. package/skills/zeroize-audit/configs/default.yaml +128 -0
  226. package/skills/zeroize-audit/configs/rust.yaml +83 -0
  227. package/skills/zeroize-audit/prompts/report_template.md +238 -0
  228. package/skills/zeroize-audit/prompts/system.md +163 -0
  229. package/skills/zeroize-audit/prompts/task.md +97 -0
  230. package/skills/zeroize-audit/references/compile-commands.md +231 -0
  231. package/skills/zeroize-audit/references/detection-strategy.md +191 -0
  232. package/skills/zeroize-audit/references/ir-analysis.md +252 -0
  233. package/skills/zeroize-audit/references/mcp-analysis.md +221 -0
  234. package/skills/zeroize-audit/references/poc-generation.md +470 -0
  235. package/skills/zeroize-audit/references/rust-zeroization-patterns.md +867 -0
  236. package/skills/zeroize-audit/schemas/input.json +83 -0
  237. package/skills/zeroize-audit/schemas/output.json +140 -0
  238. package/skills/zeroize-audit/tools/analyze_asm.sh +202 -0
  239. package/skills/zeroize-audit/tools/analyze_cfg.py +381 -0
  240. package/skills/zeroize-audit/tools/analyze_heap.sh +211 -0
  241. package/skills/zeroize-audit/tools/analyze_ir_semantic.py +429 -0
  242. package/skills/zeroize-audit/tools/diff_ir.sh +135 -0
  243. package/skills/zeroize-audit/tools/diff_rust_mir.sh +189 -0
  244. package/skills/zeroize-audit/tools/emit_asm.sh +67 -0
  245. package/skills/zeroize-audit/tools/emit_ir.sh +77 -0
  246. package/skills/zeroize-audit/tools/emit_rust_asm.sh +178 -0
  247. package/skills/zeroize-audit/tools/emit_rust_ir.sh +150 -0
  248. package/skills/zeroize-audit/tools/emit_rust_mir.sh +158 -0
  249. package/skills/zeroize-audit/tools/extract_compile_flags.py +284 -0
  250. package/skills/zeroize-audit/tools/generate_poc.py +1329 -0
  251. package/skills/zeroize-audit/tools/mcp/apply_confidence_gates.py +113 -0
  252. package/skills/zeroize-audit/tools/mcp/check_mcp.sh +68 -0
  253. package/skills/zeroize-audit/tools/mcp/normalize_mcp_evidence.py +125 -0
  254. package/skills/zeroize-audit/tools/scripts/check_llvm_patterns.py +481 -0
  255. package/skills/zeroize-audit/tools/scripts/check_mir_patterns.py +554 -0
  256. package/skills/zeroize-audit/tools/scripts/check_rust_asm.py +424 -0
  257. package/skills/zeroize-audit/tools/scripts/check_rust_asm_aarch64.py +300 -0
  258. package/skills/zeroize-audit/tools/scripts/check_rust_asm_x86.py +283 -0
  259. package/skills/zeroize-audit/tools/scripts/find_dangerous_apis.py +375 -0
  260. package/skills/zeroize-audit/tools/scripts/semantic_audit.py +923 -0
  261. package/skills/zeroize-audit/tools/track_dataflow.sh +196 -0
  262. package/skills/zeroize-audit/tools/validate_rust_toolchain.sh +298 -0
  263. package/skills/zeroize-audit/workflows/phase-0-preflight.md +150 -0
  264. package/skills/zeroize-audit/workflows/phase-1-source-analysis.md +144 -0
  265. package/skills/zeroize-audit/workflows/phase-2-compiler-analysis.md +139 -0
  266. package/skills/zeroize-audit/workflows/phase-3-interim-report.md +46 -0
  267. package/skills/zeroize-audit/workflows/phase-4-poc-generation.md +46 -0
  268. package/skills/zeroize-audit/workflows/phase-5-poc-validation.md +136 -0
  269. package/skills/zeroize-audit/workflows/phase-6-final-report.md +44 -0
  270. package/skills/zeroize-audit/workflows/phase-7-test-generation.md +42 -0
  271. package/themes/piolium-srcery.json +94 -0
@@ -0,0 +1,177 @@
1
+ import { existsSync, statSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { isAbsolute, resolve } from "node:path";
4
+
5
+ export interface ParsedArchonCommandArgs {
6
+ cwd: string;
7
+ args: string;
8
+ tokens: string[];
9
+ targetCwd?: string;
10
+ error?: string;
11
+ }
12
+
13
+ export interface ParseArchonCommandOptions {
14
+ defaultTarget?: string;
15
+ }
16
+
17
+ const PHASE_ARG =
18
+ /^(?:P\d+[A-Za-z]*|Q\d+[A-Za-z]*|L\d+[A-Za-z]*|V\d+[A-Za-z]*|R\d+[A-Za-z]*|M\d+[A-Za-z]*|X\d+[A-Za-z]*)$/;
19
+
20
+ export function parseArchonCommandArgs(
21
+ args: string,
22
+ baseCwd: string,
23
+ options: ParseArchonCommandOptions = {},
24
+ ): ParsedArchonCommandArgs {
25
+ const tokens = tokenizeCommandArgs(args);
26
+ const defaultTarget = readOptionValue(tokens, "--plm-dir") ?? options.defaultTarget;
27
+ const useDefaultTarget = (): ParsedArchonCommandArgs => {
28
+ if (!defaultTarget) return { cwd: baseCwd, args: joinCommandArgs(tokens), tokens };
29
+ const resolvedDefault = resolveTargetPath(defaultTarget, baseCwd);
30
+ if (isDirectory(resolvedDefault)) {
31
+ return {
32
+ cwd: resolvedDefault,
33
+ args: joinCommandArgs(tokens),
34
+ tokens,
35
+ targetCwd: resolvedDefault,
36
+ };
37
+ }
38
+ return {
39
+ cwd: baseCwd,
40
+ args: joinCommandArgs(tokens),
41
+ tokens,
42
+ error: `${defaultTarget} is not a readable directory.`,
43
+ };
44
+ };
45
+ if (tokens.length === 0) return useDefaultTarget();
46
+
47
+ const [first, ...rest] = tokens;
48
+ if (!first || isKnownNonPathArg(first)) return useDefaultTarget();
49
+
50
+ const resolved = resolveTargetPath(first, baseCwd);
51
+ if (isDirectory(resolved)) {
52
+ return {
53
+ cwd: resolved,
54
+ args: joinCommandArgs(rest),
55
+ tokens: rest,
56
+ targetCwd: resolved,
57
+ };
58
+ }
59
+
60
+ if (looksLikePath(first) || existsSync(resolved)) {
61
+ return {
62
+ cwd: baseCwd,
63
+ args: joinCommandArgs(rest),
64
+ tokens: rest,
65
+ error: `${first} is not a readable directory.`,
66
+ };
67
+ }
68
+
69
+ return useDefaultTarget();
70
+ }
71
+
72
+ export function tokenizeCommandArgs(args: string): string[] {
73
+ const tokens: string[] = [];
74
+ let current = "";
75
+ let quote: "'" | '"' | undefined;
76
+ let escaped = false;
77
+
78
+ const push = () => {
79
+ if (current.length > 0) {
80
+ tokens.push(current);
81
+ current = "";
82
+ }
83
+ };
84
+
85
+ for (const char of args) {
86
+ if (escaped) {
87
+ current += char;
88
+ escaped = false;
89
+ continue;
90
+ }
91
+ if (char === "\\" && quote !== "'") {
92
+ escaped = true;
93
+ continue;
94
+ }
95
+ if (quote) {
96
+ if (char === quote) quote = undefined;
97
+ else current += char;
98
+ continue;
99
+ }
100
+ if (char === "'" || char === '"') {
101
+ quote = char;
102
+ continue;
103
+ }
104
+ if (/\s/.test(char)) {
105
+ push();
106
+ continue;
107
+ }
108
+ current += char;
109
+ }
110
+ if (escaped) current += "\\";
111
+ push();
112
+ return tokens;
113
+ }
114
+
115
+ export function readOptionValue(tokens: readonly string[], name: string): string | undefined {
116
+ for (let i = 0; i < tokens.length; i++) {
117
+ const token = tokens[i];
118
+ if (token === name) return tokens[i + 1];
119
+ if (token?.startsWith(`${name}=`)) return token.slice(name.length + 1);
120
+ }
121
+ return undefined;
122
+ }
123
+
124
+ export function readRepeatedOptionValues(tokens: readonly string[], name: string): string[] {
125
+ const values: string[] = [];
126
+ for (let i = 0; i < tokens.length; i++) {
127
+ const token = tokens[i];
128
+ if (token === name) {
129
+ const value = tokens[i + 1];
130
+ if (value) {
131
+ values.push(value);
132
+ i++;
133
+ }
134
+ } else if (token?.startsWith(`${name}=`)) {
135
+ values.push(token.slice(name.length + 1));
136
+ }
137
+ }
138
+ return values;
139
+ }
140
+
141
+ function isKnownNonPathArg(arg: string): boolean {
142
+ return arg.startsWith("-") || PHASE_ARG.test(arg) || /^https?:\/\//.test(arg);
143
+ }
144
+
145
+ function resolveTargetPath(input: string, baseCwd: string): string {
146
+ const expanded =
147
+ input === "~" ? homedir() : input.startsWith("~/") ? resolve(homedir(), input.slice(2)) : input;
148
+ return isAbsolute(expanded) ? expanded : resolve(baseCwd, expanded);
149
+ }
150
+
151
+ function isDirectory(path: string): boolean {
152
+ try {
153
+ return statSync(path).isDirectory();
154
+ } catch {
155
+ return false;
156
+ }
157
+ }
158
+
159
+ function looksLikePath(arg: string): boolean {
160
+ return (
161
+ arg === "~" ||
162
+ arg.startsWith("~/") ||
163
+ arg.startsWith("./") ||
164
+ arg.startsWith("../") ||
165
+ arg.includes("/")
166
+ );
167
+ }
168
+
169
+ function joinCommandArgs(tokens: readonly string[]): string {
170
+ return tokens.map(quoteCommandArg).join(" ");
171
+ }
172
+
173
+ function quoteCommandArg(token: string): string {
174
+ if (token.length === 0) return '""';
175
+ if (!/\s|["'\\]/.test(token)) return token;
176
+ return `"${token.replace(/(["\\])/g, "\\$1")}"`;
177
+ }
@@ -0,0 +1,57 @@
1
+ export interface ArchonConsoleStream {
2
+ enabled: boolean;
3
+ writeLine(line: string): void;
4
+ writeBlock(title: string, lines: readonly string[]): void;
5
+ }
6
+
7
+ export interface ArchonConsoleStreamOptions {
8
+ argv?: readonly string[];
9
+ env?: Record<string, string | undefined>;
10
+ write?: (text: string) => void;
11
+ }
12
+
13
+ export function isPiStreamingOutputMode(argv: readonly string[] = process.argv): boolean {
14
+ for (let i = 0; i < argv.length; i++) {
15
+ const arg = argv[i];
16
+ if (arg === "--mode" && argv[i + 1] === "streaming") return true;
17
+ if (arg === "--mode=streaming") return true;
18
+ }
19
+ return false;
20
+ }
21
+
22
+ function envFlagEnabled(value: string | undefined): boolean | undefined {
23
+ if (value === undefined) return undefined;
24
+ const normalized = value.trim().toLowerCase();
25
+ if (["0", "false", "no", "off"].includes(normalized)) return false;
26
+ return normalized.length > 0;
27
+ }
28
+
29
+ export function isPioliumConsoleStreamEnabled(
30
+ argv: readonly string[] = process.argv,
31
+ env: Record<string, string | undefined> = process.env,
32
+ ): boolean {
33
+ const envEnabled = envFlagEnabled(env.PIOLIUM_CONSOLE_STREAM);
34
+ if (envEnabled !== undefined) return envEnabled;
35
+ return isPiStreamingOutputMode(argv);
36
+ }
37
+
38
+ export function createArchonConsoleStream(
39
+ options: ArchonConsoleStreamOptions = {},
40
+ ): ArchonConsoleStream {
41
+ const enabled = isPioliumConsoleStreamEnabled(
42
+ options.argv ?? process.argv,
43
+ options.env ?? process.env,
44
+ );
45
+ const write = options.write ?? ((text: string) => process.stderr.write(text));
46
+ return {
47
+ enabled,
48
+ writeLine: (line) => {
49
+ if (enabled) write(`${line}\n`);
50
+ },
51
+ writeBlock: (title, lines) => {
52
+ if (!enabled) return;
53
+ write(`\n=== ${title} ===\n`);
54
+ for (const line of lines) write(`${line}\n`);
55
+ },
56
+ };
57
+ }
@@ -0,0 +1,380 @@
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
2
+ import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
3
+ import { splitFrontmatter } from "./agents.ts";
4
+ import { type FindingDraft, listFindingDirs, readFindingFrontmatter } from "./findings.ts";
5
+
6
+ export type ExportFormat = "json" | "md-dir";
7
+
8
+ export interface ExportOptions {
9
+ format?: ExportFormat;
10
+ outPath?: string;
11
+ minSeverity?: FindingDraft["severity"];
12
+ onlySeverity?: FindingDraft["severity"][];
13
+ confirmedOnly?: boolean;
14
+ excludeFp?: boolean;
15
+ since?: string;
16
+ requireOwner?: boolean;
17
+ }
18
+
19
+ export interface ExportedFinding {
20
+ id: string;
21
+ slug: string;
22
+ title: string;
23
+ severity: FindingDraft["severity"];
24
+ status?: string;
25
+ confirmStatus?: string;
26
+ owners: string[];
27
+ labels: string[];
28
+ files: string[];
29
+ findingDir: string;
30
+ draftPath?: string;
31
+ reportPath?: string;
32
+ updatedAt: string;
33
+ frontmatter: Record<string, unknown>;
34
+ }
35
+
36
+ export interface ExportResult {
37
+ findings: ExportedFinding[];
38
+ outPath: string;
39
+ format: ExportFormat;
40
+ lines: string[];
41
+ }
42
+
43
+ interface CodeownerRule {
44
+ pattern: string;
45
+ owners: string[];
46
+ }
47
+
48
+ const SEVERITY_ORDER: Record<FindingDraft["severity"], number> = {
49
+ critical: 0,
50
+ high: 1,
51
+ medium: 2,
52
+ low: 3,
53
+ info: 4,
54
+ };
55
+
56
+ const CODEOWNER_PATHS = ["CODEOWNERS", ".github/CODEOWNERS", "docs/CODEOWNERS"];
57
+
58
+ export function runExport(cwd: string, options: ExportOptions = {}): ExportResult {
59
+ const format = options.format ?? "json";
60
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
61
+ const outPath = resolveOutputPath(
62
+ cwd,
63
+ options.outPath ??
64
+ join("piolium", "exports", `findings-${timestamp}${format === "json" ? ".json" : ""}`),
65
+ );
66
+ const owners = loadCodeowners(cwd);
67
+ const sinceMs = options.since ? Date.parse(options.since) : undefined;
68
+ const findings = listFindingDirs(cwd)
69
+ .map((dir) => readExportedFinding(cwd, dir.path, owners))
70
+ .filter((finding): finding is ExportedFinding => !!finding)
71
+ .filter((finding) => includeFinding(finding, options, sinceMs))
72
+ .sort((a, b) => {
73
+ const sev = SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity];
74
+ if (sev !== 0) return sev;
75
+ return a.id.localeCompare(b.id, undefined, { numeric: true });
76
+ });
77
+
78
+ if (format === "json") {
79
+ mkdirSync(dirname(outPath), { recursive: true });
80
+ writeFileSync(
81
+ outPath,
82
+ `${JSON.stringify({ generated_at: new Date().toISOString(), findings }, null, 2)}\n`,
83
+ );
84
+ } else {
85
+ mkdirSync(outPath, { recursive: true });
86
+ for (const finding of findings) {
87
+ writeFileSync(
88
+ join(outPath, `${safeName(`${finding.id}-${finding.slug}`)}.md`),
89
+ renderMarkdownExport(finding),
90
+ );
91
+ }
92
+ }
93
+
94
+ return {
95
+ findings,
96
+ outPath,
97
+ format,
98
+ lines: [
99
+ `Directory: ${cwd}`,
100
+ `Format: ${format}`,
101
+ `Findings: ${findings.length}`,
102
+ `Output: ${outPath}`,
103
+ "",
104
+ ...findings.slice(0, 30).map((finding) => {
105
+ const ownerText = finding.owners.length > 0 ? ` owners=${finding.owners.join(",")}` : "";
106
+ return `- ${finding.id} ${finding.severity} ${finding.title}${ownerText}`;
107
+ }),
108
+ ...(findings.length > 30 ? [`... ${findings.length - 30} more`] : []),
109
+ ],
110
+ };
111
+ }
112
+
113
+ export function normalizeExportSeverity(
114
+ value: string | undefined,
115
+ ): FindingDraft["severity"] | undefined {
116
+ if (!value) return undefined;
117
+ const lower = value.toLowerCase().trim();
118
+ if (
119
+ lower === "critical" ||
120
+ lower === "high" ||
121
+ lower === "medium" ||
122
+ lower === "low" ||
123
+ lower === "info"
124
+ ) {
125
+ return lower;
126
+ }
127
+ return undefined;
128
+ }
129
+
130
+ function readExportedFinding(
131
+ cwd: string,
132
+ findingDir: string,
133
+ codeowners: CodeownerRule[],
134
+ ): ExportedFinding | undefined {
135
+ const frontmatter = readFindingFrontmatter(findingDir);
136
+ if (!frontmatter) return undefined;
137
+ const draftPath = join(findingDir, "draft.md");
138
+ const reportPath = join(findingDir, "report.md");
139
+ const draftRaw = existsSync(draftPath) ? readFileSync(draftPath, "utf8") : "";
140
+ const reportRaw = existsSync(reportPath) ? readFileSync(reportPath, "utf8") : "";
141
+ const combined = `${draftRaw}\n${reportRaw}`;
142
+ const title = readTitle(frontmatter, combined) ?? frontmatter.slug;
143
+ const files = collectFindingFiles(cwd, frontmatter, combined);
144
+ const owners = resolveOwners(files, codeowners);
145
+ const status = readOptionalString(frontmatter.status) ?? readOptionalString(frontmatter.verdict);
146
+ const confirmStatus = readConfirmStatus(combined);
147
+ const updatedAt = new Date(maxMtime([draftPath, reportPath, findingDir])).toISOString();
148
+ const labels = [
149
+ `severity:${frontmatter.severity}`,
150
+ ...owners.map((owner) => `owner:${owner.replace(/^@/, "")}`),
151
+ ...(confirmStatus ? [`confirm:${confirmStatus}`] : []),
152
+ ];
153
+ return {
154
+ id: frontmatter.id,
155
+ slug: frontmatter.slug,
156
+ title,
157
+ severity: frontmatter.severity,
158
+ ...(status ? { status } : {}),
159
+ ...(confirmStatus ? { confirmStatus } : {}),
160
+ owners,
161
+ labels,
162
+ files,
163
+ findingDir,
164
+ ...(existsSync(draftPath) ? { draftPath } : {}),
165
+ ...(existsSync(reportPath) ? { reportPath } : {}),
166
+ updatedAt,
167
+ frontmatter,
168
+ };
169
+ }
170
+
171
+ function includeFinding(
172
+ finding: ExportedFinding,
173
+ options: ExportOptions,
174
+ sinceMs: number | undefined,
175
+ ): boolean {
176
+ if (
177
+ options.onlySeverity &&
178
+ options.onlySeverity.length > 0 &&
179
+ !options.onlySeverity.includes(finding.severity)
180
+ ) {
181
+ return false;
182
+ }
183
+ if (
184
+ options.minSeverity &&
185
+ SEVERITY_ORDER[finding.severity] > SEVERITY_ORDER[options.minSeverity]
186
+ ) {
187
+ return false;
188
+ }
189
+ if (options.confirmedOnly && !isConfirmed(finding.confirmStatus)) return false;
190
+ if (options.excludeFp && isFalsePositiveLike(finding)) return false;
191
+ if (options.requireOwner && finding.owners.length === 0) return false;
192
+ if (sinceMs !== undefined && !Number.isNaN(sinceMs) && Date.parse(finding.updatedAt) < sinceMs) {
193
+ return false;
194
+ }
195
+ return true;
196
+ }
197
+
198
+ function isConfirmed(confirmStatus: string | undefined): boolean {
199
+ return (
200
+ confirmStatus === "confirmed-live" ||
201
+ confirmStatus === "confirmed-test" ||
202
+ confirmStatus === "confirmed"
203
+ );
204
+ }
205
+
206
+ function isFalsePositiveLike(finding: ExportedFinding): boolean {
207
+ const id = finding.id.toLowerCase();
208
+ const status = `${finding.status ?? ""} ${finding.confirmStatus ?? ""}`.toLowerCase();
209
+ return (
210
+ id.startsWith("fp-") ||
211
+ status.includes("false-positive") ||
212
+ status.includes("rejected") ||
213
+ status === "fp" ||
214
+ status.includes("invalid")
215
+ );
216
+ }
217
+
218
+ function readTitle(frontmatter: Record<string, unknown>, content: string): string | undefined {
219
+ const fmTitle =
220
+ readOptionalString(frontmatter.title) ??
221
+ readOptionalString(frontmatter.name) ??
222
+ readOptionalString(frontmatter.summary);
223
+ if (fmTitle) return fmTitle;
224
+ const heading = content.match(/^#\s+(.+)$/m)?.[1]?.trim();
225
+ if (heading) return heading;
226
+ return undefined;
227
+ }
228
+
229
+ function readConfirmStatus(content: string): string | undefined {
230
+ return content
231
+ .match(/^Confirm-Status:\s*([^\n\r]+)/im)?.[1]
232
+ ?.trim()
233
+ .toLowerCase();
234
+ }
235
+
236
+ function collectFindingFiles(
237
+ cwd: string,
238
+ frontmatter: Record<string, unknown>,
239
+ content: string,
240
+ ): string[] {
241
+ const files = new Set<string>();
242
+ for (const key of ["file", "path", "location", "source", "sink"]) {
243
+ addFileValue(cwd, files, frontmatter[key]);
244
+ }
245
+ for (const value of Object.values(frontmatter)) {
246
+ if (Array.isArray(value)) {
247
+ for (const item of value) addFileValue(cwd, files, item);
248
+ }
249
+ }
250
+ for (const match of content.matchAll(
251
+ /(?:`|\b)([A-Za-z0-9_./-]+\.(?:ts|tsx|js|jsx|py|go|rs|rb|java|kt|swift|c|h|cpp|cc|hpp|cs|php|scala|clj|sh|sql|lua|tf|ya?ml))(?:[:#]\d+)?`?/g,
252
+ )) {
253
+ addFileValue(cwd, files, match[1]);
254
+ }
255
+ return [...files].sort();
256
+ }
257
+
258
+ function addFileValue(cwd: string, files: Set<string>, value: unknown): void {
259
+ if (typeof value !== "string") return;
260
+ const cleaned = value
261
+ .replace(/^file:\/\//, "")
262
+ .replace(/[:#]\d+(?::\d+)?$/, "")
263
+ .trim();
264
+ if (!cleaned || /^https?:\/\//.test(cleaned)) return;
265
+ const abs = isAbsolute(cleaned) ? cleaned : resolve(cwd, cleaned);
266
+ if (!abs.startsWith(resolve(cwd))) return;
267
+ if (!existsSync(abs)) return;
268
+ const rel = relative(cwd, abs).split("\\").join("/");
269
+ if (!rel.startsWith("piolium/")) files.add(rel);
270
+ }
271
+
272
+ function loadCodeowners(cwd: string): CodeownerRule[] {
273
+ const rules: CodeownerRule[] = [];
274
+ for (const path of CODEOWNER_PATHS.map((p) => join(cwd, p))) {
275
+ if (!existsSync(path)) continue;
276
+ for (const rawLine of readFileSync(path, "utf8").split(/\r?\n/)) {
277
+ const line = rawLine.replace(/\s+#.*$/, "").trim();
278
+ if (!line || line.startsWith("#")) continue;
279
+ const parts = line.split(/\s+/);
280
+ const pattern = parts.shift();
281
+ if (!pattern || parts.length === 0) continue;
282
+ rules.push({ pattern, owners: parts });
283
+ }
284
+ }
285
+ return rules;
286
+ }
287
+
288
+ function resolveOwners(files: string[], rules: CodeownerRule[]): string[] {
289
+ const owners = new Set<string>();
290
+ for (const file of files) {
291
+ let matched: string[] | undefined;
292
+ for (const rule of rules) {
293
+ if (codeownerMatches(rule.pattern, file)) matched = rule.owners;
294
+ }
295
+ for (const owner of matched ?? []) owners.add(owner);
296
+ }
297
+ return [...owners].sort();
298
+ }
299
+
300
+ function codeownerMatches(pattern: string, filePath: string): boolean {
301
+ let pat = pattern.trim();
302
+ if (!pat || pat.startsWith("#")) return false;
303
+ pat = pat.replace(/^!/, "");
304
+ const file = filePath.split("\\").join("/");
305
+ if (pat.endsWith("/")) {
306
+ const prefix = pat.replace(/^\/+/, "");
307
+ return file.startsWith(prefix);
308
+ }
309
+ const anchored = pat.startsWith("/");
310
+ pat = pat.replace(/^\/+/, "");
311
+ if (!anchored && !pat.includes("/")) {
312
+ return file.split("/").some((part) => minimatchSegment(pat, part));
313
+ }
314
+ if (anchored) return wildcardRegex(pat).test(file);
315
+ return wildcardRegex(pat).test(file) || wildcardRegex(`**/${pat}`).test(file);
316
+ }
317
+
318
+ function minimatchSegment(pattern: string, segment: string): boolean {
319
+ return wildcardRegex(pattern).test(segment);
320
+ }
321
+
322
+ function wildcardRegex(pattern: string): RegExp {
323
+ const escaped = pattern
324
+ .split("**")
325
+ .map((part) => part.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*"))
326
+ .join(".*");
327
+ return new RegExp(`^${escaped}$`);
328
+ }
329
+
330
+ function renderMarkdownExport(finding: ExportedFinding): string {
331
+ const lines: string[] = [];
332
+ lines.push("---");
333
+ lines.push(`id: ${finding.id}`);
334
+ lines.push(`slug: ${finding.slug}`);
335
+ lines.push(`severity: ${finding.severity}`);
336
+ if (finding.status) lines.push(`status: ${finding.status}`);
337
+ if (finding.confirmStatus) lines.push(`confirm_status: ${finding.confirmStatus}`);
338
+ if (finding.owners.length > 0) lines.push(`owners: [${finding.owners.join(", ")}]`);
339
+ lines.push("---");
340
+ lines.push("");
341
+ lines.push(`# ${finding.title}`);
342
+ lines.push("");
343
+ lines.push(`- Directory: \`${finding.findingDir}\``);
344
+ if (finding.files.length > 0)
345
+ lines.push(`- Files: ${finding.files.map((file) => `\`${file}\``).join(", ")}`);
346
+ if (finding.labels.length > 0) lines.push(`- Labels: ${finding.labels.join(", ")}`);
347
+ lines.push("");
348
+ if (finding.reportPath && existsSync(finding.reportPath)) {
349
+ lines.push(readFileSync(finding.reportPath, "utf8").trimEnd());
350
+ } else if (finding.draftPath && existsSync(finding.draftPath)) {
351
+ const { body } = splitFrontmatter(readFileSync(finding.draftPath, "utf8"));
352
+ lines.push(body.trimEnd());
353
+ }
354
+ lines.push("");
355
+ return lines.join("\n");
356
+ }
357
+
358
+ function resolveOutputPath(cwd: string, outPath: string): string {
359
+ return isAbsolute(outPath) ? outPath : resolve(cwd, outPath);
360
+ }
361
+
362
+ function maxMtime(paths: string[]): number {
363
+ let max = 0;
364
+ for (const path of paths) {
365
+ try {
366
+ max = Math.max(max, statSync(path).mtimeMs);
367
+ } catch {}
368
+ }
369
+ return max || Date.now();
370
+ }
371
+
372
+ function readOptionalString(value: unknown): string | undefined {
373
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
374
+ }
375
+
376
+ function safeName(value: string): string {
377
+ const ext = extname(value);
378
+ const base = ext ? basename(value, ext) : value;
379
+ return base.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "finding";
380
+ }