@xaccefy/pi-casefile 0.2.3 → 0.2.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 +54 -35
  2. package/package.json +1 -1
  3. package/src/index.ts +81 -2
package/README.md CHANGED
@@ -1,51 +1,70 @@
1
1
  # pi-casefile
2
2
 
3
- `pi-casefile` is a local-first case ledger and validation harness designed to track offensive security investigations, bug bounty findings, and CTF progress. It features a SQLite storage engine and an isolated PoC execution runner.
3
+ Local-first security case ledger for Pi Agent. Tracks hypotheses confirmed findings with a hard PoC gate, SQLite storage, and an isolated PoC runner.
4
4
 
5
- ## Features
6
-
7
- - **State Management**: Enforces strict lifecycle transitions across states (`hypothesis`, `investigating`, `confirmed`, `blocked`, `killed`, `reported`).
8
- - **PoC Runner**: Validates vulnerability reachability in an isolated Docker sandbox with `--network none` or locally on the host.
9
- - **SQL Backend**: Syncs case information to SQLite via Node's `node:sqlite` or Bun's `bun:sqlite` compat layers.
10
- - **Reporting**: Automatically compiles markdown vulnerability reports for confirmed or reported findings.
11
-
12
- ## Installation
5
+ ## Install
13
6
 
14
7
  ```bash
15
- pi install npm:pi-casefile
8
+ pi install npm:@xaccefy/pi-casefile
16
9
  ```
17
10
 
18
- ## Environment Variables
11
+ Or via the XPI umbrella package: `pi install npm:@xaccefy/pi-xpi`
12
+
13
+ ## XP mode (default OFF)
14
+
15
+ The cyber-workflow context injection is **quiet by default** so normal dev work is not flooded with security process text.
16
+
17
+ | Control | Effect |
18
+ |---------|--------|
19
+ | `/xp` | Toggle ON/OFF |
20
+ | `/xp on` / `/xp off` | Set explicitly |
21
+ | `PI_XP_MODE=on` | Force ON for this process (overrides file) |
22
+ | `PI_XP_MODE=off` | Force OFF |
23
+
24
+ When **ON**, every prompt injects the attacker-oriented cyber workflow plus any active (non-killed/non-reported) cases. When **OFF**, nothing is injected; tools remain available.
25
+
26
+ State is persisted next to the ledger as `xp-mode` (e.g. `.pi/xp-mode`).
27
+
28
+ ## Environment
29
+
30
+ | Variable | Purpose |
31
+ |----------|---------|
32
+ | `PI_CASEFILE_PATH` | Absolute path to the SQLite ledger file |
33
+ | `CASEFILE_WORKSPACE_ROOT` / `PI_WORKSPACE_ROOT` | Override workspace root used to place `.pi/casefile.db` |
34
+
35
+ Default DB path: `<workspace>/.pi/casefile.db`
36
+
37
+ ## State machine
38
+
39
+ ```
40
+ hypothesis → investigating → confirmed → reported
41
+ ↓ ↓
42
+ blocked killed (terminal)
43
+ ```
19
44
 
20
- - `CASEFILE_PATH`: Set explicit absolute path to the SQLite database file.
21
- - `CASEFILE_SCOPE`: Scope database storage. Options: `project` (default, stores in `.pi/casefile.db` or `.casefile/casefile.db` relative to workspace root) or `global` (stores in `~/.pi/casefile/casefile.db` or `~/.casefile/casefile.db`).
45
+ - **investigating** requires `evidence` + `confidence`
46
+ - **confirmed** only via `PromoteFinding` (PoC exit 0) `CaseUpdate(status:"confirmed")` is rejected
47
+ - **reported** requires `CaseReport` first
48
+ - **killed** / **reported** are terminal (no further field edits)
22
49
 
23
- ## Core Workflow & State Transitions
50
+ There is **no** `impact_proof` tool field. Put proof text in `impact` or `evidence`.
24
51
 
25
- The ledger acts as a state gate to ensure finding validity:
26
- 1. **Hypothesis**: Initial lead tracking (`CaseAdd`).
27
- 2. **Investigation**: Move to `investigating` when code paths or reachability trace are identified (`CaseUpdate`).
28
- 3. **Confirmation**: Validate with a working PoC script (`PromoteFinding`). Requires the PoC script to exit with code `0`.
29
- 4. **Chaining**: Link low-impact primitives to build high-impact chains (`CaseLink`).
30
- 5. **Reporting**: Compile markdown report (`CaseReport`) and transition finding to `reported` (`CaseUpdate`).
52
+ ## Tools
31
53
 
32
- ## Tools Reference
54
+ | Tool | Use |
55
+ |------|-----|
56
+ | `CaseAdd` | Open a case (`title` required; start as `hypothesis` or `investigating`) |
57
+ | `CaseUpdate` | Evidence, impact, severity, status (not direct confirm) |
58
+ | `PromoteFinding` | Run on-disk PoC (Docker sandbox by default; `local:true` for host) → confirm on exit 0 |
59
+ | `CaseGet` / `CaseList` / `CaseSearch` | Read / filter / search |
60
+ | `CaseLink` / `CaseUnlink` | Bidirectional exploit chains |
61
+ | `CaseReport` | Markdown report for confirmed/reported cases |
33
62
 
34
- | Tool | Action | Requirements |
35
- |---|---|---|
36
- | `CaseAdd` | Create a new hypothesis or investigation case | Title, target (scope) |
37
- | `CaseUpdate` | Modify case details (remediation, impact, status) | ID, field updates |
38
- | `PromoteFinding` | Run on-disk PoC script to transition status to `confirmed` | ID, `poc_path`, `local` flag |
39
- | `CaseGet` | Retrieve full JSON details of a case | ID |
40
- | `CaseList` | Query and filter cases by status/confidence/tags | Filter flags, limit, offset |
41
- | `CaseSearch` | Perform full-text search across specific case fields | Query string, optional field |
42
- | `CaseLink` | Create a bidirectional link between two cases | Source ID, Target ID |
43
- | `CaseUnlink` | Remove link between two cases | Source ID, Target ID |
44
- | `CaseReport` | Write markdown report to disk | ID (must be `confirmed` or `reported`) |
63
+ Commands: `/casefile` (dashboard), `/xp` (XP mode).
45
64
 
46
- ## Development and Testing
65
+ ## Development
47
66
 
48
- Run tests via Bun:
49
67
  ```bash
50
- bun test
68
+ bun test packages/pi-casefile
69
+ bun run typecheck
51
70
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xaccefy/pi-casefile",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Offensive security case tracker for Pi Agent — bug bounties, CTFs, security audits",
5
5
  "keywords": [
6
6
  "pi-package",
package/src/index.ts CHANGED
@@ -9,6 +9,9 @@
9
9
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
10
10
  import { matchesKey, Text, truncateToWidth } from "@earendil-works/pi-tui";
11
11
  import { Type } from "@sinclair/typebox";
12
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
13
+ import { dirname, join } from "node:path";
14
+ import { homedir } from "node:os";
12
15
 
13
16
  import {
14
17
  addCaseResult,
@@ -542,6 +545,58 @@ function buildAgentInjection(active: CaseRecord[]): string {
542
545
  return caseList ? `${caseList}\n\n${STATIC_CYBER_WORKFLOW}` : STATIC_CYBER_WORKFLOW;
543
546
  }
544
547
 
548
+ // ── XP (offensive / exploit) mode toggle ─────────────────────────────
549
+ // Casefile historically injected the cyber workflow into every prompt.
550
+ // For normal dev work that is just noise, so XP mode defaults OFF. Enable
551
+ // it for offensive/audit sessions to get the full attacker discipline back.
552
+ // Toggle with /xp (or /xp on|off); override per-session with PI_XP_MODE.
553
+ // Pure helpers exported for unit tests.
554
+
555
+ export const XP_MODE_ENV = "PI_XP_MODE";
556
+ export type XpMode = "on" | "off";
557
+
558
+ export function getXpModeStatePath(): string {
559
+ try {
560
+ return join(dirname(getCasefilePath()), "xp-mode");
561
+ } catch {
562
+ return join(homedir(), ".pi", "xp-mode");
563
+ }
564
+ }
565
+
566
+ export function readXpMode(
567
+ envValue: string | undefined = process.env[XP_MODE_ENV],
568
+ statePath: string = getXpModeStatePath(),
569
+ ): XpMode {
570
+ const env = (envValue ?? "").trim().toLowerCase();
571
+ if (env === "on" || env === "1" || env === "true") return "on";
572
+ if (env === "off" || env === "0" || env === "false") return "off";
573
+ try {
574
+ if (existsSync(statePath)) {
575
+ const v = readFileSync(statePath, "utf8").trim().toLowerCase();
576
+ if (v === "on") return "on";
577
+ if (v === "off") return "off";
578
+ }
579
+ } catch {
580
+ // ignore and fall through to default
581
+ }
582
+ return "off";
583
+ }
584
+
585
+ export function writeXpMode(state: XpMode, statePath: string = getXpModeStatePath()): void {
586
+ try {
587
+ writeFileSync(statePath, state, "utf8");
588
+ } catch {
589
+ // best-effort; env var can still override at runtime
590
+ }
591
+ }
592
+
593
+ export function parseXpModeArg(args: string, current: XpMode): XpMode {
594
+ const arg = (args ?? "").trim().toLowerCase();
595
+ if (arg === "on") return "on";
596
+ if (arg === "off") return "off";
597
+ return current === "on" ? "off" : "on";
598
+ }
599
+
545
600
  // ── Main extension ────────────────────────────────────────────────────
546
601
 
547
602
  export default function casefileExtension(pi: ExtensionAPI) {
@@ -976,6 +1031,10 @@ export default function casefileExtension(pi: ExtensionAPI) {
976
1031
  name: "CaseUnlink",
977
1032
  label: "Unlink Cases",
978
1033
  description: "Remove a bidirectional link between two cases.",
1034
+ promptSnippet: "Remove a link between two cases",
1035
+ promptGuidelines: [
1036
+ "Use CaseUnlink to detach two cases that were previously linked with CaseLink (e.g. when a chain step is disproven or no longer relevant).",
1037
+ ],
979
1038
  parameters: UnlinkSchema,
980
1039
 
981
1040
  async execute(_id, params, _signal, _onUpdate, _ctx) {
@@ -1068,6 +1127,21 @@ export default function casefileExtension(pi: ExtensionAPI) {
1068
1127
  },
1069
1128
  });
1070
1129
 
1130
+ // ── Command: /xp (toggle offensive XP mode) ──
1131
+
1132
+ pi.registerCommand("xp", {
1133
+ description:
1134
+ "Toggle casefile XP (offensive) mode. ON injects the full cyber workflow each prompt; OFF (default) keeps context quiet for normal dev work. Usage: /xp [on|off]",
1135
+ handler: async (args, ctx) => {
1136
+ const next = parseXpModeArg(args ?? "", readXpMode());
1137
+ writeXpMode(next);
1138
+ ctx.ui.notify(
1139
+ `Casefile XP mode: ${next.toUpperCase()} (takes effect on the next prompt)`,
1140
+ next === "on" ? "info" : "warning",
1141
+ );
1142
+ },
1143
+ });
1144
+
1071
1145
  // ── Command: /casefile ──
1072
1146
 
1073
1147
  pi.registerCommand("casefile", {
@@ -1103,10 +1177,15 @@ export default function casefileExtension(pi: ExtensionAPI) {
1103
1177
  });
1104
1178
 
1105
1179
  // ── Event: Inject context into system prompt ──
1180
+ // XP (offensive) mode is OFF by default so normal dev work stays quiet.
1181
+ // Only when enabled do we inject the cyber workflow (and case list) each
1182
+ // prompt. This keeps the agent focused during everyday development while
1183
+ // still allowing the full attacker discipline to be switched on for
1184
+ // offensive/audit/bounty sessions.
1106
1185
 
1107
1186
  pi.on("before_agent_start", async () => {
1108
- // Once per user prompt (not every tool turn). Always inject workflow so empty
1109
- // ledgers still get attacker discipline; attach case list only when useful.
1187
+ if (readXpMode() === "off") return;
1188
+
1110
1189
  let active: CaseRecord[] = [];
1111
1190
  try {
1112
1191
  const records = readCasefile();