@shadowclone/cli 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.
- package/LICENSE +21 -0
- package/README.md +123 -0
- package/bin/shadowclone.mjs +52 -0
- package/dist/shadowclone.js +103 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Atchyut Pulavarthi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# shadowclone
|
|
2
|
+
|
|
3
|
+
shadowclone reads the AI coding sessions already on your disk, builds a profile of how you work, and runs copies of you inside the agent you already use.
|
|
4
|
+
|
|
5
|
+
Claude Code, Codex, and Cursor write every session to a file. Those files hold every time you stopped the agent, refused a tool, or picked one option over another. shadowclone turns those moments into a profile, loads it into your live sessions, and compiles it into a subagent. The subagent is you. Spawn ten of them on ten tasks, or let one run in a worktree while you're away.
|
|
6
|
+
|
|
7
|
+
The name is the Naruto reference. A shadow clone does your work while you do something else, and what it learned comes back when it dissolves.
|
|
8
|
+
|
|
9
|
+
## How it works
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
observe -> index -> signal -> distill -> profile -> dispatch
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
| Stage | Module | Does |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| observe | `src/observe/` | reads transcripts into one event stream, incrementally |
|
|
18
|
+
| index | `src/index/` | SQLite cache of pointers into those files, never the text |
|
|
19
|
+
| signal | `src/signal/` | finds where you overrode the agent, no model involved |
|
|
20
|
+
| distill | `src/distill/` | turns those moments into rules, through your own agent CLI |
|
|
21
|
+
| profile | `src/profile/` | markdown you can read, plus a subagent that is you |
|
|
22
|
+
| dispatch | `src/dispatch/` | runs a clone in a worktree and leaves a receipt |
|
|
23
|
+
|
|
24
|
+
There is no API key and no server. Model calls go through `claude`, `codex`, or `cursor-agent`, already installed and logged in, on your own plan. `docs/architecture/` has the reasoning behind each piece.
|
|
25
|
+
|
|
26
|
+
## Status
|
|
27
|
+
|
|
28
|
+
Early.
|
|
29
|
+
|
|
30
|
+
- **Built.** Phases 0 through 6. `shadowclone learn` reads enabled Claude Code, Codex, Cursor, and Antigravity sessions into one offline mirror, `learn --deep` selects an authenticated CLI by enforceable capabilities, the plugin provides a live `shadowclone` subagent, and `shadowclone run` leaves unattended work on a local branch with a receipt.
|
|
31
|
+
- **Manual checks.** Real authenticated provider runs, plugin installation, and provider-specific corpus tuning still need to be exercised outside recorded fixtures. API and local endpoint engines remain later work.
|
|
32
|
+
|
|
33
|
+
`docs/design/001-agent-transcript-pivot.md` is the spec. `docs/architecture/06-roadmap.md` is the order.
|
|
34
|
+
|
|
35
|
+
### Provider coverage
|
|
36
|
+
|
|
37
|
+
Observe, distill, and dispatch are separate claims. An installed CLI is never capture consent, and an engine is not called for a purpose whose policy it cannot enforce.
|
|
38
|
+
|
|
39
|
+
| Provider | Observe | Distill | Dispatch |
|
|
40
|
+
| --- | --- | --- | --- |
|
|
41
|
+
| Claude Code | built | built | built |
|
|
42
|
+
| Codex | built | built | blocked on granular tool and budget controls |
|
|
43
|
+
| Cursor | built | built | blocked on granular tool and budget controls |
|
|
44
|
+
| Antigravity CLI | built | blocked on a per-run deny-all tool policy | blocked on granular tool and budget controls |
|
|
45
|
+
| GitHub Copilot CLI, OpenCode, Aider, Amp | planned, one reviewed provider at a time | capability dependent | capability dependent |
|
|
46
|
+
|
|
47
|
+
`docs/design/003-provider-expansion.md` defines the qualification gate and the registry that keeps these claims honest.
|
|
48
|
+
|
|
49
|
+
## Privacy
|
|
50
|
+
|
|
51
|
+
This is the first question to ask about a program that reads your agent transcripts, so it goes here rather than at the bottom.
|
|
52
|
+
|
|
53
|
+
**What it reads.** Every source is opt-in and off by default. The list grows only when a release note says it grew.
|
|
54
|
+
|
|
55
|
+
| Source | Path | Default | Built today |
|
|
56
|
+
| --- | --- | --- | --- |
|
|
57
|
+
| `antigravity` | `~/.gemini/antigravity-cli/brain/*/.system_generated/logs/transcript_full.jsonl` | off | read only when enabled |
|
|
58
|
+
| `claude-code` | `~/.claude/projects/**/*.jsonl` | off | read only when enabled |
|
|
59
|
+
| `claude-prompts` | `~/.claude/history.jsonl` | off | read only when enabled |
|
|
60
|
+
| `codex` | `~/.codex/sessions/**/*.jsonl` | off | read only when enabled |
|
|
61
|
+
| `cursor` | `~/.cursor/chats/**/{store.db,meta.json}` | off | read only when enabled |
|
|
62
|
+
| `git-metadata` | observed repositories' local `remote.origin.url` | off | read only when enabled |
|
|
63
|
+
| `shell` | `~/.zsh_history`, `~/.bash_history` | off | read only when enabled |
|
|
64
|
+
|
|
65
|
+
**What leaves your machine.** Only what your own agent CLI sends, under your own account. `shadowclone learn` makes no network call. `shadowclone learn --deep` sends only redacted, allowlisted correction excerpts after separate consent. shadowclone has no server, account, key, telemetry, analytics, or crash reporting.
|
|
66
|
+
|
|
67
|
+
**What gets scrubbed.** Secrets, private paths, internal hosts, emails, cloud resources, and database URLs. `src/redact/index.test.ts` is the list. It is over-eager on purpose.
|
|
68
|
+
|
|
69
|
+
**What is never read.** Tool results, file contents, thinking blocks, and anything from a data-access tool. Those hold other people's data, so they are excluded outright rather than redacted. `docs/architecture/07-enterprise.md` has the list.
|
|
70
|
+
|
|
71
|
+
**What is stored.** Everything lives under `~/.shadowclone/`. The SQLite index contains pointers, event kinds, and tool metadata, never transcript text. The profile is plain markdown plus a generated-rule manifest containing only rule ids and relative profile paths. shadowclone never makes a second copy of your transcripts. Nothing is synced or uploaded. `shadowclone forget --all` removes all of it in one step.
|
|
72
|
+
|
|
73
|
+
**What stays in your org.** Every rule is scoped to the git remote it came from when `git-metadata` is enabled. Without that consent, each working directory is an isolated origin that never promotes a rule to global. An admin can disable shadowclone fleet-wide with a root-owned file.
|
|
74
|
+
|
|
75
|
+
**What it does on your behalf.** The live subagent runs inside your current Claude Code session and its permission mode. `shadowclone run "<task>"` explicitly approves one local worktree, branch, and commit for that task. A repo allowlist is only a ceiling for remote actions, and each run must also name an action with `--approve`. Learned denials stay advisory until observation can identify the denied action without storing raw tool input. Merge, force push, `bypassPermissions`, and `--dangerously-skip-permissions` are never allowed.
|
|
76
|
+
|
|
77
|
+
If a secret gets past the redaction, that is the highest-value bug report this project can get. Open an issue with the shape of the string, not the string itself.
|
|
78
|
+
|
|
79
|
+
## Quickstart
|
|
80
|
+
|
|
81
|
+
Needs one of `claude`, `codex`, or `cursor-agent` installed and logged in for anything that calls a model. No API key.
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npm i -g @shadowclone/cli
|
|
85
|
+
shadowclone doctor
|
|
86
|
+
shadowclone init
|
|
87
|
+
shadowclone learn
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
That is the whole install. The package brings its own runtime, so nothing else is required.
|
|
91
|
+
|
|
92
|
+
Working on shadowclone itself:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
git clone https://github.com/theonly1me/shadowclone.git
|
|
96
|
+
cd shadowclone
|
|
97
|
+
bun install
|
|
98
|
+
bun run check # typecheck, lint, and tests
|
|
99
|
+
bun run cli doctor
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Add this checkout as a local Claude Code marketplace, then install the plugin:
|
|
103
|
+
|
|
104
|
+
```text
|
|
105
|
+
/plugin marketplace add /path/to/shadowclone
|
|
106
|
+
/plugin install shadowclone@shadowclone
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Run `shadowclone install` inside a repository before its next Claude Code session. It writes the scoped `.claude/agents/shadowclone.md`. The plugin injects the same profile at session start, refreshes the offline profile at session end, and exposes it through MCP.
|
|
110
|
+
|
|
111
|
+
The default run creates a worktree and local commit, writes a receipt under `~/.shadowclone/runs/`, and pushes nothing. Remote actions need both a matching `[repo."<host>/<owner>/<repo>"]` allowlist and an explicit per-run `--approve`.
|
|
112
|
+
|
|
113
|
+
## Contributing
|
|
114
|
+
|
|
115
|
+
`CONTRIBUTING.md` has the rules. Short version: small diffs, `bun run check`, PR body under 250 words.
|
|
116
|
+
|
|
117
|
+
`SECURITY.md` says what to report privately and how to verify a release download.
|
|
118
|
+
|
|
119
|
+
Anything touching capture, storage, or egress gets a closer read. `.claude/skills/data-handling/SKILL.md` says what a reviewer checks.
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
|
|
123
|
+
MIT. See `LICENSE`.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const packageDirectory = path.dirname(
|
|
10
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
11
|
+
);
|
|
12
|
+
const bundle = path.join(packageDirectory, "dist", "shadowclone.js");
|
|
13
|
+
|
|
14
|
+
function bundledBunPath() {
|
|
15
|
+
try {
|
|
16
|
+
const manifestPath = require.resolve("bun/package.json");
|
|
17
|
+
const manifest = require(manifestPath);
|
|
18
|
+
const relative =
|
|
19
|
+
typeof manifest.bin === "string" ? manifest.bin : manifest.bin?.bun;
|
|
20
|
+
if (typeof relative !== "string") {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const candidate = path.join(path.dirname(manifestPath), relative);
|
|
24
|
+
return existsSync(candidate) ? candidate : null;
|
|
25
|
+
} catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function bunOnPath() {
|
|
31
|
+
return spawnSync("bun", ["--version"], { stdio: "ignore" }).status === 0
|
|
32
|
+
? "bun"
|
|
33
|
+
: null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const runtime = bundledBunPath() ?? bunOnPath();
|
|
37
|
+
|
|
38
|
+
if (runtime === null) {
|
|
39
|
+
console.error(
|
|
40
|
+
"shadowclone: bun was not found. Reinstall with npm i -g @shadowclone/cli, or install bun from https://bun.sh",
|
|
41
|
+
);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const result = spawnSync(runtime, [bundle, ...process.argv.slice(2)], {
|
|
46
|
+
stdio: "inherit",
|
|
47
|
+
});
|
|
48
|
+
if (result.error) {
|
|
49
|
+
console.error("shadowclone: could not start bun");
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
process.exit(result.status ?? 1);
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
3
|
+
var sr={name:"@shadowclone/cli",version:"0.0.1",module:"src/cli/index.ts",type:"module",packageManager:"bun@1.3.3",bin:{shadowclone:"bin/shadowclone.mjs"},scripts:{start:"bun run src/cli/index.ts",cli:"bun run src/cli/index.ts",check:"bun run typecheck && bun run lint && bun test",test:"bun test",typecheck:"tsc --noEmit",lint:"biome lint --error-on-warnings --diagnostic-level=warn . && bun run scripts/conventions.ts","lint:fix":"biome lint --write .",build:"bun run scripts/build.ts",prepack:"bun run build"},devDependencies:{"@biomejs/biome":"2.5.12","@types/bun":"latest",typescript:"~5.9"},description:"Learns how you work from the AI coding sessions already on your disk, then runs copies of you inside the agent you already use.",license:"MIT",repository:{type:"git",url:"git+https://github.com/theonly1me/shadowclone.git"},homepage:"https://shadowclone.co",bugs:{url:"https://github.com/theonly1me/shadowclone/issues"},keywords:["claude-code","codex","cursor","agent","subagent","local-first"],files:["bin","dist","README.md","LICENSE"],dependencies:{bun:"^1.4.2"}};function X(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function R(e,r){let t=e[r];return typeof t==="string"?t:null}function he(e,r){let t=e[r];return typeof t==="number"?t:null}function zt(e){let r=e.message;if(!X(r)||!Array.isArray(r.content))return"";return r.content.flatMap((t)=>X(t)&&R(t,"type")==="text"&&R(t,"text")!==null?[R(t,"text")??""]:[]).join("")}function Gt(e){if(!Array.isArray(e))return[];return e.flatMap((r)=>{if(!X(r))return[];let t=R(r,"tool_name")??R(r,"toolName");return t?[{toolName:t,toolUseId:R(r,"tool_use_id")??R(r,"toolUseId")}]:[]})}function be(e){let r=[],t=null;for(let i of e.stream.split(`
|
|
4
|
+
`)){if(i.trim().length===0)continue;try{let s=JSON.parse(i);if(!X(s))continue;if(R(s,"type")==="assistant")r.push(zt(s));if(R(s,"type")==="result")t=s}catch{continue}}let n=t?R(t,"result"):null,o=r.join("");return{engine:"claude-code",sessionId:(t?R(t,"session_id"):null)??e.fallbackSessionId,transcriptPath:null,text:o.length>0?o:n??"",structured:t?.structured_output??null,costUsd:t?he(t,"total_cost_usd"):null,durationMs:t?he(t,"duration_ms")??0:0,turns:t?he(t,"num_turns")??0:0,isError:t?.is_error===!0||t===null,permissionDenials:Gt(t?.permission_denials)}}function ar(e){if(e.values===void 0)return;e.arguments_.push(e.flag),e.arguments_.push(...e.values.length===0?[""]:e.values)}function lr(e){let r=["claude","-p","--output-format","stream-json","--verbose","--session-id",e.sessionId];if(e.run.systemPromptFile)r.push("--append-system-prompt-file",e.run.systemPromptFile);if(e.run.model)r.push("--model",e.run.model);if(e.run.permissionMode)r.push("--permission-mode",e.run.permissionMode);if(e.run.maxBudgetUsd!==void 0)r.push("--max-budget-usd",e.run.maxBudgetUsd.toString());if(e.run.outputSchema!==void 0)r.push("--json-schema",JSON.stringify(e.run.outputSchema));return ar({arguments_:r,flag:"--allowedTools",values:e.run.allowedTools}),ar({arguments_:r,flag:"--disallowedTools",values:e.run.disallowedTools}),r}async function we(e){let r=e.sessionId??crypto.randomUUID(),t=Bun.spawn({cmd:[...lr({run:e,sessionId:r})],cwd:e.cwd,stdin:"pipe",stdout:"pipe",stderr:"ignore",signal:e.signal});t.stdin.write(e.prompt),t.stdin.end();let[n,o]=await Promise.all([t.exited,new Response(t.stdout).text()]),i=be({stream:o,fallbackSessionId:r});return n===0?i:{...i,isError:!0}}import{mkdtemp as Jt,rm as Wt}from"fs/promises";import Kt from"os";import cr from"path";function dr(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function N(e,r){let t=e[r];return typeof t==="string"?t:null}function Ht(e){try{return JSON.parse(e)}catch{return null}}function xe(e){let r=e.fallbackSessionId,t="",n=0,o=!1;for(let i of e.stream.split(`
|
|
5
|
+
`)){if(i.trim().length===0)continue;let s;try{s=JSON.parse(i)}catch{o=!0;continue}if(!dr(s))continue;let a=N(s,"type");if(a==="thread.started")r=N(s,"thread_id")??r;if(a==="turn.completed")n+=1;if(a==="turn.failed"||a==="error")o=!0;let d=dr(s.item)?s.item:null,y=d?N(d,"item_type")??N(d,"type"):null;if(a==="item.completed"&&d!==null&&(y==="assistant_message"||y==="agent_message"))t=N(d,"text")??t}return{engine:"codex",sessionId:r,transcriptPath:null,text:t,structured:Ht(t),costUsd:null,durationMs:e.durationMs,turns:n,isError:o,permissionDenials:[]}}async function Z(e){let r=[];if(e.run.systemPromptFile)r.push("Follow this shadowclone profile:",await Bun.file(e.run.systemPromptFile).text());if(r.push("Complete this task:",e.run.prompt),e.outputSchemaInPrompt&&e.run.outputSchema!==void 0)r.push("Return only JSON matching this schema:",JSON.stringify(e.run.outputSchema));return r.join(`
|
|
6
|
+
|
|
7
|
+
`)}function pr(e){if(e.sessionId!==void 0)throw Error("Codex cannot set a caller-provided session id");if(e.maxBudgetUsd!==void 0)throw Error("Codex cannot enforce a per-run dollar budget");if(e.disallowedTools&&e.disallowedTools.length>0)throw Error("Codex cannot enforce a granular tool denylist");if(e.allowedTools&&e.allowedTools.length>0)throw Error("Codex cannot enforce a granular tool allowlist");if(![void 0,"dontAsk","plan"].includes(e.permissionMode))throw Error("Codex cannot honor this permission mode")}function fr(e){pr(e.run);let r=["codex","exec","-","--json","--sandbox","read-only","-C",e.run.cwd,"--skip-git-repo-check","-c",'approval_policy="never"',"-c","mcp_servers={}"];if(e.run.allowedTools?.length===0)r.push("--disable","shell_tool");if(e.run.model)r.push("--model",e.run.model);if(e.outputSchemaPath)r.push("--output-schema",e.outputSchemaPath);return r}async function ur(e){let r=await Z({run:e.run,outputSchemaInPrompt:!1}),t=crypto.randomUUID(),n=Date.now(),o=Bun.spawn({cmd:[...fr(e)],cwd:e.run.cwd,stdin:"pipe",stdout:"pipe",stderr:"ignore",signal:e.run.signal});o.stdin.write(r),o.stdin.end();let[i,s]=await Promise.all([o.exited,new Response(o.stdout).text()]),a=xe({stream:s,fallbackSessionId:t,durationMs:Date.now()-n});return i===0?a:{...a,isError:!0}}async function Pe(e){if(pr(e),e.outputSchema===void 0)return ur({run:e});let r=await Jt(cr.join(Kt.tmpdir(),"shadowclone-codex-")),t=cr.join(r,"schema.json");await Bun.write(t,JSON.stringify(e.outputSchema));try{return await ur({run:e,outputSchemaPath:t})}finally{await Wt(r,{recursive:!0,force:!0})}}import{mkdir as Yt,mkdtemp as Qt,rm as en}from"fs/promises";import rn from"os";import ve from"path";function Xt(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function V(e,r){let t=e[r];return typeof t==="string"?t:null}function Zt(e,r){let t=e[r];return typeof t==="number"?t:null}function Vt(e){try{return JSON.parse(e)}catch{return null}}function Re(e){let r=e.fallbackSessionId,t=null,n=0;for(let i of e.stream.split(`
|
|
8
|
+
`)){if(i.trim().length===0)continue;let s;try{s=JSON.parse(i)}catch{continue}if(!Xt(s))continue;if(r=V(s,"session_id")??r,V(s,"type")==="assistant")n+=1;if(V(s,"type")==="result")t=s}let o=t?V(t,"result")??"":"";return{engine:"cursor-agent",sessionId:r,transcriptPath:null,text:o,structured:Vt(o),costUsd:null,durationMs:t?Zt(t,"duration_ms")??0:0,turns:n,isError:t===null||t.is_error===!0,permissionDenials:[]}}function mr(e){if(e.sessionId!==void 0)throw Error("Cursor cannot set a caller-provided session id");if(e.maxBudgetUsd!==void 0)throw Error("Cursor cannot enforce a per-run dollar budget");if(e.disallowedTools&&e.disallowedTools.length>0)throw Error("Cursor cannot enforce a granular tool denylist");if(e.allowedTools&&e.allowedTools.length>0)throw Error("Cursor cannot enforce a granular tool allowlist");if(![void 0,"dontAsk","plan"].includes(e.permissionMode))throw Error("Cursor cannot honor this permission mode")}function gr(e){mr(e);let r=["cursor-agent","--print","--output-format","stream-json","--sandbox","enabled","--mode",e.permissionMode==="plan"?"plan":"ask","--workspace",e.cwd];if(e.model)r.push("--model",e.model);return r}async function yr(e){let r=await Z({run:e.run,outputSchemaInPrompt:!0}),t=crypto.randomUUID(),n=Bun.spawn({cmd:[...gr({...e.run,cwd:e.workspace}),"--trust"],cwd:e.workspace,stdin:"pipe",stdout:"pipe",stderr:"ignore",signal:e.run.signal});n.stdin.write(r),n.stdin.end();let[o,i]=await Promise.all([n.exited,new Response(n.stdout).text()]),s=Re({stream:i,fallbackSessionId:t});return o===0?s:{...s,isError:!0}}async function Ee(e){if(mr(e),e.allowedTools?.length!==0)return yr({run:e,workspace:e.cwd});let r=await Qt(ve.join(rn.tmpdir(),"shadowclone-cursor-")),t=ve.join(r,".cursor");await Yt(t,{recursive:!0}),await Bun.write(ve.join(t,"cli.json"),JSON.stringify({version:1,permissions:{allow:[],deny:["Shell(*)","Read(*)","Write(*)","WebFetch(*)","Mcp(*:*)"]}}));try{return await yr({run:e,workspace:r})}finally{await en(r,{recursive:!0,force:!0})}}var Y=[{id:"claude-code",captureSource:"claude-code",transcriptFormat:"jsonl",engine:{id:"claude-code",implemented:!0,capabilities:{structuredOutput:"native",callerSessionId:!0,maxBudgetUsd:!0,granularToolPolicy:!0,isolatedNoTools:!0}}},{id:"codex",captureSource:"codex",transcriptFormat:"jsonl",engine:{id:"codex",implemented:!0,capabilities:{structuredOutput:"native",callerSessionId:!1,maxBudgetUsd:!1,granularToolPolicy:!1,isolatedNoTools:!0}}},{id:"cursor",captureSource:"cursor",transcriptFormat:"sqlite",engine:{id:"cursor-agent",implemented:!0,capabilities:{structuredOutput:"prompted",callerSessionId:!1,maxBudgetUsd:!1,granularToolPolicy:!1,isolatedNoTools:!0}}},{id:"antigravity",captureSource:"antigravity",transcriptFormat:"jsonl",engine:{id:"antigravity",implemented:!1,capabilities:{structuredOutput:"native",callerSessionId:!1,maxBudgetUsd:!1,granularToolPolicy:!1,isolatedNoTools:!1}}}];function Ce(e){return Y.find((r)=>r.engine?.id===e)??null}function tn(e){return e?.implemented===!0&&e.capabilities.structuredOutput!=="none"&&e.capabilities.isolatedNoTools}function Q(e){let r=tn(e.engine),t=e.engine?.implemented===!0,n=e.engine?.capabilities;return{observe:e.captureSource!==null,distill:r,dispatch:t&&n?.callerSessionId===!0&&n.maxBudgetUsd&&n.granularToolPolicy}}function ke(e){let r=Q(e.definition);return e.purpose==="distill"?r.distill:r.dispatch}async function ee(e){try{return await Bun.spawn({cmd:[...e],stdout:"ignore",stderr:"ignore"}).exited===0}catch{return!1}}async function hr(e={}){let r=e.probe??ee,t=await r(["claude","--version"]),n=t&&await r(["claude","auth","status"]);return{engine:"claude-code",installed:t,authenticated:n}}async function br(e={}){let r=e.probe??ee,t=await r(["codex","--version"]),n=t&&await r(["codex","login","status"]);return{engine:"codex",installed:t,authenticated:n}}async function wr(e={}){let r=e.probe??ee,t=await r(["cursor-agent","--version"]),n=t&&await r(["cursor-agent","status"]);return{engine:"cursor-agent",installed:t,authenticated:n}}function nn(e){if(e==="claude-code")return we;if(e==="codex")return Pe;if(e==="cursor-agent")return Ee;return null}function on(e){let r=Ce(e.engineId);return r!==null&&ke({definition:r,purpose:e.purpose})}async function T(e){let r=await hr(e),t=await br(e),n=await wr(e),o=e.allowedEngines??["claude-code","codex","cursor-agent"],i=[r,t,n],s=i.find((d)=>d.authenticated&&o.includes(d.engine)&&on({engineId:d.engine,purpose:e.purpose})),a=s?nn(s.engine):null;return{availability:i,runner:a,selectedEngine:a?s?.engine??null:null}}import{mkdir as bn}from"fs/promises";import wn from"path";import sn from"os";import g from"path";function an(e){if(e==="darwin")return"/Library/Application Support/shadowclone/managed.json";if(e==="linux")return"/etc/shadowclone/managed.json";return null}function ln(e){let r=g.join(e.homeDirectory,".shadowclone"),t=g.join(r,"profile");return{shadowcloneDirectory:r,configFile:g.join(r,"config.toml"),indexDatabase:g.join(r,"index.db"),profileDirectory:t,rejectedProfileFile:g.join(t,".rejected"),profileManifestFile:g.join(t,".generated"),compiledProfileFile:g.join(t,".compiled.md"),distillDirectory:g.join(r,"distill"),worktreesDirectory:g.join(r,"worktrees"),runsDirectory:g.join(r,"runs"),antigravityBrainDirectory:g.join(e.homeDirectory,".gemini","antigravity-cli","brain"),claudeProjectsDirectory:g.join(e.homeDirectory,".claude","projects"),claudePromptHistoryFile:g.join(e.homeDirectory,".claude","history.jsonl"),codexSessionsDirectory:g.join(e.homeDirectory,".codex","sessions"),cursorChatsDirectory:g.join(e.homeDirectory,".cursor","chats"),shellHistoryFiles:[g.join(e.homeDirectory,".zsh_history"),g.join(e.homeDirectory,".bash_history")],managedConfigFile:an(e.platform),runDirectory:(n)=>g.join(r,"runs",n),worktreeDirectory:(n)=>g.join(r,"worktrees",n)}}var h=ln({homeDirectory:sn.homedir(),platform:process.platform});import{stat as yn}from"fs/promises";var S=["push","pr-draft","pr-reply"];function xr(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function dn(e){if(!Array.isArray(e))return null;let r=e.flatMap((t)=>{let n=S.find((o)=>o===t);return n?[n]:[]});return r.length===e.length?r:null}function Pr(e){if(e===void 0)return{};if(!xr(e))throw Error("Config repo settings must be tables");let r={};for(let[t,n]of Object.entries(e)){if(!xr(n))throw Error("Every repo policy must be a table");let o=dn(n.allow);if(o===null||typeof n.maxBudgetUsd!=="number"||n.maxBudgetUsd<=0||typeof n.requireCleanExit!=="boolean"||Object.keys(n).some((i)=>i!=="allow"&&i!=="maxBudgetUsd"&&i!=="requireCleanExit"))throw Error("Every repo policy must contain valid action settings");r[t]={allow:o,maxBudgetUsd:n.maxBudgetUsd,requireCleanExit:n.requireCleanExit}}return r}function Rr(e){return Object.entries(e).sort(([r],[t])=>r.localeCompare(t)).flatMap(([r,t])=>["",`[repo.${JSON.stringify(r)}]`,`allow = [${t.allow.map((n)=>JSON.stringify(n)).join(", ")}]`,`maxBudgetUsd = ${t.maxBudgetUsd}`,`requireCleanExit = ${t.requireCleanExit}`])}var C=["antigravity","claude-code","claude-prompts","codex","cursor","git-metadata","shell"],cn=C.filter((e)=>e!=="antigravity"),un=C.filter((e)=>e!=="antigravity"&&e!=="git-metadata"),O={schemaVersion:1,sources:{antigravity:!1,"claude-code":!1,"claude-prompts":!1,codex:!1,cursor:!1,"git-metadata":!1,shell:!1},distillation:{deep:!1},repo:{}};function Se(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function j(e){let r=Object.keys(e.record),t=new Set(e.keys);return r.length===e.keys.length&&r.every((n)=>t.has(n))}function pn(e){if(!Se(e)||!j({record:e,keys:C})&&!j({record:e,keys:cn})&&!j({record:e,keys:un}))throw Error("Config sources must contain every supported source and no unknown sources");let r=e.antigravity??!1,t=e["claude-code"],n=e["claude-prompts"],{codex:o,cursor:i}=e,s=e["git-metadata"]??!1,a=e.shell;if(typeof r!=="boolean"||typeof t!=="boolean"||typeof n!=="boolean"||typeof o!=="boolean"||typeof i!=="boolean"||typeof s!=="boolean"||typeof a!=="boolean")throw Error("Every config source setting must be a boolean");return{antigravity:r,"claude-code":t,"claude-prompts":n,codex:o,cursor:i,"git-metadata":s,shell:a}}function fn(e){if(!Se(e)||!j({record:e,keys:["deep"]}))throw Error("Config distillation must contain only the deep setting");if(typeof e.deep!=="boolean")throw Error("Config distillation.deep must be a boolean");return{deep:e.deep}}function vr(e){let r=["schema-version","sources","distillation"],t=[...r,"repo"];if(!Se(e)||!j({record:e,keys:r})&&!j({record:e,keys:t}))throw Error("Config must contain only supported top-level settings");if(e["schema-version"]!==1)throw Error("Config schema-version must be 1");return{schemaVersion:1,sources:pn(e.sources),distillation:fn(e.distillation),repo:Pr(e.repo)}}var Er=["claude-code","codex","cursor-agent","antigravity","anthropic-api","openai-compatible"],Ie={enabled:!0,allowedSources:C,allowedEngines:Er,distillation:"allowed",originScope:"strict",blockedOrigins:[],maxActionTier:"act"};function mn(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Ae(e){return Array.isArray(e)&&e.every((r)=>typeof r==="string")?e:null}function gn(e){let r=Ae(e);if(r===null)return null;let t=r.flatMap((n)=>{let o=C.find((i)=>i===n);return o?[o]:[]});return t.length===r.length?t:null}function hn(e){let r=Ae(e);if(r===null)return null;let t=r.flatMap((n)=>{let o=Er.find((i)=>i===n);return o?[o]:[]});return t.length===r.length?t:null}function Cr(e){if(!mn(e))throw Error("Managed policy must be a JSON object");let r=gn(e.allowedSources),t=hn(e.allowedEngines),n=Ae(e.blockedOrigins),{distillation:o,maxActionTier:i}=e;if(typeof e.enabled!=="boolean"||r===null||t===null||n===null||o!=="allowed"&&o!=="local-only"&&o!=="disabled"||e.originScope!=="strict"||i!=="observe"&&i!=="draft"&&i!=="act")throw Error("Managed policy has invalid or missing fields");return{enabled:e.enabled,allowedSources:r,allowedEngines:t,distillation:o,originScope:"strict",blockedOrigins:n,maxActionTier:i}}function Te(e){let r=(t)=>e.policy.enabled&&e.policy.allowedSources.includes(t);return{...e.config,sources:{antigravity:e.config.sources.antigravity&&r("antigravity"),"claude-code":e.config.sources["claude-code"]&&r("claude-code"),"claude-prompts":e.config.sources["claude-prompts"]&&r("claude-prompts"),codex:e.config.sources.codex&&r("codex"),cursor:e.config.sources.cursor&&r("cursor"),"git-metadata":e.config.sources["git-metadata"]&&r("git-metadata"),shell:e.config.sources.shell&&r("shell")},distillation:{deep:e.config.distillation.deep&&e.policy.enabled&&e.policy.distillation!=="disabled"}}}async function L(e){if(e===null)return Ie;let r=Bun.file(e);if(!await r.exists())return Ie;if((await yn(e)).uid!==0)throw Error("Managed policy must be owned by root");let n=await r.json();return Cr(n)}async function xn(e={}){let r=e.configPath??h.configFile,t=Bun.file(r);if(!await t.exists())return O;let n=Bun.TOML.parse(await t.text());return vr(n)}async function v(e={}){let r=await L(e.managedConfigPath===void 0?h.managedConfigFile:e.managedConfigPath),t=r.enabled?await xn({configPath:e.configPath}):O;return{config:Te({config:t,policy:r}),policy:r}}function Pn(e){let r=C.map((t)=>`${t} = ${e.sources[t]}`);return[`schema-version = ${e.schemaVersion}`,"","[sources]",...r,"","[distillation]",`deep = ${e.distillation.deep}`,...Rr(e.repo),""].join(`
|
|
9
|
+
`)}async function Oe(e){let r=e.configPath??h.configFile;await bn(wn.dirname(r),{recursive:!0}),await Bun.write(r,Pn(e.config))}function De(e){return{...e.config,sources:{...e.config.sources,[e.source]:e.enabled}}}function kr(e){return{...e.config,distillation:{deep:e.enabled}}}function Rn(){return Y.map((e)=>{let r=Q(e);return`${e.id}: observe=${r.observe?"yes":"no"}, distill=${r.distill?"yes":"no"}, dispatch=${r.dispatch?"yes":"no"}`})}function vn(e){if(e.distillation==="disabled")return"Deep distillation is disabled by managed policy.";if(e.distillation==="local-only")return"Deep distillation is restricted to local engines, which are not implemented.";return e.selectedEngine?`Selected engine: ${e.selectedEngine}`:"No authenticated distillation engine is available."}async function Sr(e={}){let r=e.managedConfigPath===void 0?h.managedConfigFile:e.managedConfigPath,t=await L(r);if(r!==null&&await Bun.file(r).exists())console.log(`Managed policy: ${r}`);if(!t.enabled){console.log("Managed policy: shadowclone is disabled.");return}let n=t.distillation==="allowed"?t.allowedEngines:[],o=await T({purpose:"distill",probe:e.probe,allowedEngines:n});for(let i of o.availability){let s=i.authenticated?"authenticated":i.installed?"installed, authentication not found":"not installed";console.log(`${i.engine}: ${s}`)}console.log(vn({distillation:t.distillation,selectedEngine:o.selectedEngine})),console.log("Provider support:");for(let i of Rn())console.log(i)}import{rm as En}from"fs/promises";async function Ir(e=h.shadowcloneDirectory){await En(e,{recursive:!0,force:!0}),console.log("Removed all shadowclone data.")}import Un from"path";import{mkdir as Cn}from"fs/promises";import Ar from"path";function Tr(e){let r=e.name??"shadowclone";if(!/^[a-z0-9-]+$/.test(r))throw Error("Agent name must use lowercase letters, numbers, and hyphens");return["---",`name: ${r}`,"description: A copy of the user that follows their learned engineering profile","model: inherit","tools: Read, Grep, Glob, Bash, Edit, Write","---","",e.profile.trim(),""].join(`
|
|
10
|
+
`)}async function Be(e){let r=e.name??"shadowclone",t=Ar.join(e.targetDirectory,".claude","agents"),n=Ar.join(t,`${r}.md`);return await Cn(t,{recursive:!0}),await Bun.write(n,Tr(e)),n}import{mkdir as Sn}from"fs/promises";import ne from"path";function re(e){return new Bun.CryptoHasher("sha256").update(e).digest("hex").slice(0,16)}function _e(e){return e.scope==="global"?`global/${e.section}.md`:`org/${e.originDirectory??"isolated"}/${e.section}.md`}function te(e){let r=`## ${e.title}
|
|
11
|
+
|
|
12
|
+
${e.body}`,t=[`key=${e.key}`,`observations=${e.observations}`,`confidence=${e.confidence.toFixed(2)}`,`last-seen=${e.lastSeen}`,`sessions=${e.sessions}`,`origins=${e.origins.join(",")}`,`scope=${e.scope}`,`fingerprint=${re(r)}`].join(" ");return`${r}
|
|
13
|
+
|
|
14
|
+
<!-- shadowclone: ${t} -->`}function Or(e){let r=e.metadata.split(/\s+/),t=`${e.name}=`;return r.find((o)=>o.startsWith(t))?.slice(t.length)??null}function kn(e){let r=e.match(/\n\n<!-- shadowclone: ([^\n]+) -->\s*$/),t=r?.[1];if(!t||r.index===void 0)return{key:null,content:e.trim(),edited:!0};let n=Or({metadata:t,name:"key"}),o=Or({metadata:t,name:"fingerprint"});if(n===null||o===null)return{key:null,content:e.trim(),edited:!0};let i=e.slice(0,r.index).trim();return{key:n,fingerprint:o,content:e.trim(),edited:re(i)!==o}}function U(e){return e.trim().split(/\n(?=## )/).filter((r)=>r.trim().length>0).map(kn)}function Dr(e){let r=e.content.match(new RegExp(`\\b${e.name}=([\\d.]+)`)),t=r?.[1]?Number(r[1]):Number.NaN;return Number.isFinite(t)?t:e.fallback}function In(e){let r=!e.includes("<!-- shadowclone:");return{content:e.replace(/\n\n<!-- shadowclone: [^\n]+ -->\s*$/,"").trim(),observations:Dr({content:e,name:"observations",fallback:r?Number.MAX_SAFE_INTEGER:0}),confidence:Dr({content:e,name:"confidence",fallback:r?1:0})}}async function An(e){let r=[],t=new Bun.Glob("**/*.md");try{for await(let n of t.scan({cwd:e,absolute:!0,onlyFiles:!0}))r.push(n)}catch{return[]}return r.sort()}function Tn(e){let r=e.filePath.split(ne.sep).join("/");if(!r.includes("/projects/"))return!0;return e.targetRepo!==null&&r.endsWith(`/projects/${e.targetRepo}.md`)}async function oe(e){let r=[ne.join(e.profileDirectory,"global"),ne.join(e.profileDirectory,"org",e.origin.directoryName)],t=(await Promise.all(r.map(An))).flat(),n=[];for(let s of t){if(!Tn({filePath:s,targetRepo:e.targetRepo??null}))continue;let a=await Bun.file(s).text();n.push(...U(a).map((d)=>In(d.content)))}let o=e.confidenceThreshold??0,i=n.filter((s)=>s.confidence>=o).sort((s,a)=>a.observations-s.observations||s.content.localeCompare(a.content));return i.length===0?`# Shadowclone profile
|
|
15
|
+
`:`# Shadowclone profile
|
|
16
|
+
|
|
17
|
+
${i.map((s)=>s.content).join(`
|
|
18
|
+
|
|
19
|
+
`)}
|
|
20
|
+
`}async function q(e){let r=await oe(e);return await Sn(ne.dirname(e.outputPath),{recursive:!0}),await Bun.write(e.outputPath,r),r}function D(e,r){let t=Math.max(3,48-e.length);return` ${e} ${".".repeat(t)} ${r}`}function Br(e){return D(e.label,`${e.count} of ${e.total}`)}function Fe(e){let r=e.report,t=(r.corpus.bytes/1048576).toFixed(1),n=r.interruptions.length>0?r.interruptions.slice(0,5).map((s)=>D(s.label,s.count)):[D("no interruptions indexed",0)],o=r.denials.length>0?r.denials.slice(0,5).map((s)=>D(s.label,s.count)):[D("no tool refusals indexed",0)],i=r.structural.toolUses.length>0?r.structural.toolUses.slice(0,5).map((s)=>D(s.label,s.count)):[D("no tool calls indexed",0)];return[` Read ${r.corpus.sessions} sessions, ${t} MB, ${r.corpus.activeDays} active days.${e.networkCallsMade?"":" No network calls were made."}`,""," You stop the agent most often",...n,"",` You have refused tools ${r.denials.reduce((s,a)=>s+a.count,0)} times`,...o,""," When the agent asked, you answered",Br({label:"agent questions",count:r.answeredQuestions,total:r.askedQuestions}),Br({label:"presented plans",count:r.resolvedPlans,total:r.presentedPlans}),""," Your most used agent tools",...i,""," Profile written to ~/.shadowclone/profile/. Open it. Argue with it."].join(`
|
|
21
|
+
`)}import On from"path";function je(e){let r=new Bun.CryptoHasher("sha256").update(e).digest("hex").slice(0,16);return{id:`isolated:${r}`,directoryName:`isolated--${r}`,promotable:!1}}function _r(e){let r=e.trim(),t=r.match(/^(?:[^@]+@)?([^/:]+):([^/]+)\/(.+)$/);if(t){let[,n,o,i]=t;return n&&o&&i?{host:n,owner:o,repository:i.replace(/\.git$/,"")}:null}try{let n=new URL(r),[o,i]=n.pathname.split("/").filter(Boolean);return o&&i?{host:n.hostname,owner:decodeURIComponent(o),repository:decodeURIComponent(i).replace(/\.git$/,"")}:null}catch{return null}}function Ne(e){let r=_r(e);if(r===null)return null;let t=r.host.toLowerCase(),n=r.owner.toLowerCase(),o=`${t}/${n}`,i=o.replace(/[^a-z0-9._-]+/g,"--");return{id:o,directoryName:i,promotable:!0}}function Fr(e){let r=_r(e),t=Ne(e);if(r===null||t===null)return null;return{id:`${t.id}/${r.repository.toLowerCase()}`,origin:t}}function Dn(e,r){let t=r.split("*").map((n)=>n.replace(/[.+?^${}()|[\]\\]/g,"\\$&")).join(".*");return new RegExp(`^${t}$`).test(e)}function k(e){let r=e.cwd?On.basename(e.cwd):null,t=[e.origin.id,...r?[`${e.origin.id}/${r}`]:[]];return e.patterns.some((n)=>t.some((o)=>Dn(o,n)))}async function ie(e){let r=Bun.spawn({cmd:["git","-C",e,"config","--local","--get","remote.origin.url"],stdout:"pipe",stderr:"ignore"}),[t,n]=await Promise.all([r.exited,new Response(r.stdout).text()]),o=n.trim();return t===0&&o.length>0?o:null}function Me(e){return e.cwd.length>0?e.cwd:`${e.source}:${e.sessionId}`}async function $e(e){let r=new Map,t=e.readRemote??ie;for(let n of e.events){let o=Me(n);if(r.has(o))continue;r.set(o,await B({cwd:n.cwd,fallbackKey:o,enabled:e.enabled,readRemote:t}))}return r}async function B(e){let r=e.cwd||e.fallbackKey||"unknown",t=e.readRemote??ie,n=e.enabled&&e.cwd.length>0?await t(e.cwd):null;return n===null?je(r):Ne(n)??je(r)}async function Le(e){let r=e.readRemote??ie,t=e.enabled&&e.cwd.length>0?await r(e.cwd):null,n=t?Fr(t):null;if(n!==null)return n;let o=await B({cwd:e.cwd,enabled:!1});return{id:o.id,origin:o}}function I(e){return e.origins.get(Me(e.event))??je(Me(e.event))}function jr(e){for(let r=e.length-1;r>=0;r-=1){let t=e[r];if(t&&(t.kind==="tool-call"||t.kind==="plan-presented"||t.kind==="question-asked"||t.kind==="assistant-text"))return t}return null}function Bn(e){if(e?.tool)return{category:`tool:${e.tool.name}`,label:`while using ${e.tool.name}`};if(e?.kind==="plan-presented")return{category:"after-plan",label:"after presenting a plan"};if(e?.kind==="assistant-text")return{category:"assistant-text",label:"during an explanation"};return{category:"other",label:"before finishing a response"}}function se(e){let r=[e.relatedEvent?.textRef,e.event.textRef].filter((t)=>t!==null&&t!==void 0);return{kind:e.kind,category:e.category,label:e.label,sessionId:e.event.sessionId,timestamp:e.event.timestamp,origin:e.origin,textRefs:r}}function _n(e){let r=[],t=[],n=null,o=null;for(let i of e.events){let s=I({event:i,origins:e.origins});if(i.kind==="interruption"){let a=jr(t),d=Bn(a);r.push(se({kind:"interruption",...d,event:i,origin:s,relatedEvent:a}))}if(i.kind==="permission-denied"){let a=jr(t),d=a?.tool?.name??"an unspecified tool";r.push(se({kind:"permission-denied",category:`tool:${d}`,label:d,event:i,origin:s,relatedEvent:a}))}if(i.kind==="question-asked")n=i;if(i.kind==="plan-presented")o=i;if(i.kind==="user-prompt"||i.kind==="question-answered"){if(n!==null||i.kind==="question-answered")r.push(se({kind:"question-answered",category:"agent-question",label:"an agent question",event:i,origin:s,relatedEvent:n})),n=null}if(i.kind==="user-prompt"||i.kind==="plan-resolved"){if(o!==null||i.kind==="plan-resolved")r.push(se({kind:"plan-resolved",category:"presented-plan",label:"a presented plan",event:i,origin:s,relatedEvent:o})),o=null}t.push(i)}return r}function Mr(e){return[...Map.groupBy(e.events,(t)=>`${t.source}:${t.sessionId}`).values()].flatMap((t)=>_n({events:t,origins:e.origins}))}function Nr(e){let r=new Map;for(let t of e){let n=r.get(t.category);r.set(t.category,{...t,count:(n?.count??0)+1})}return[...r.values()].sort((t,n)=>n.count-t.count||t.label.localeCompare(n.label))}function Ue(e){return Nr(e)}function $r(e){let r=new Set(e.map((o)=>`${o.source}:${o.sessionId}`)),t=new Set(e.filter((o)=>o.kind==="plan-presented").map((o)=>`${o.source}:${o.sessionId}`));return{toolUses:Nr(e.flatMap((o)=>o.tool?[{category:o.tool.name,label:o.tool.name}]:[])),planSessions:t.size,totalSessions:r.size}}function Lr(e,r){return e.filter((t)=>t.kind===r).length}async function ae(e){let r=await $e({events:e.events,enabled:e.gitMetadataEnabled,readRemote:e.readRemote}),t=e.events.filter((s)=>!k({origin:I({event:s,origins:r}),cwd:s.cwd,patterns:e.blockedOrigins??[]})),n=Mr({events:t,origins:r}),o=n.filter((s)=>s.kind==="interruption"),i=n.filter((s)=>s.kind==="permission-denied");return{corrections:n,events:t,origins:r,report:{corpus:e.corpus,interruptions:Ue(o),denials:Ue(i),answeredQuestions:n.filter((s)=>s.kind==="question-answered").length,askedQuestions:Lr(t,"question-asked"),resolvedPlans:n.filter((s)=>s.kind==="plan-resolved").length,presentedPlans:Lr(t,"plan-presented"),structural:$r(t)}}}function qr(e){return new Bun.CryptoHasher("sha256").update(e).digest("hex").slice(0,16)}function Fn(e){if(e.kind==="interruption")return{title:`Stops the agent ${e.label}`,body:`Pause and reassess when work reaches this pattern: ${e.label}.`,section:"workflow"};if(e.kind==="permission-denied")return{title:`Requests confirmation after refusing ${e.label}`,body:`A \`${e.label}\` request was refused. Ask before repeating a similar action, but do not treat the whole tool family as blocked.`,section:"boundaries"};if(e.kind==="question-answered")return{title:"Answers agent questions before work continues",body:"Ask a focused question when a consequential choice is unresolved.",section:"workflow"};return{title:"Reviews presented plans",body:"Present the plan and wait for its resolution before implementation.",section:"workflow"}}function jn(e){let r=Map.groupBy(e,(t)=>`${t.origin.id}:${t.kind}`);return e.map((t)=>{let n=Fn(t);return{key:qr(`${t.kind}:${t.category}`),...n,origin:t.origin,timestamp:t.timestamp,sessionId:t.sessionId,opportunities:r.get(`${t.origin.id}:${t.kind}`)?.length??1}})}function Mn(e){let r=e.events.filter((n)=>n.tool!==null),t=Map.groupBy(r,(n)=>I({event:n,origins:e.origins}).id);return r.map((n)=>{let o=I({event:n,origins:e.origins}),i=n.tool?.name??"unknown";return{key:qr(`tool-use:${i}`),title:`Uses ${i} in agent sessions`,body:`The \`${i}\` tool is part of the regular workflow.`,section:"engineering",origin:o,timestamp:n.timestamp,sessionId:n.sessionId,opportunities:t.get(o.id)?.length??1}})}function Ur(e){let[r]=e.observations;if(!r)throw Error("Cannot aggregate an empty rule");let t=new Set(e.observations.map((s)=>s.sessionId)),n=[...new Set(e.observations.map((s)=>s.origin.id))].sort(),o=Math.max(...e.observations.map((s)=>s.timestamp)),i=Math.max(...e.observations.map((s)=>s.opportunities));return{key:r.key,title:r.title,body:r.body,section:r.section,scope:e.scope,originDirectory:e.scope==="org"?r.origin.directoryName:null,observations:e.observations.length,confidence:Math.min(1,e.observations.length/i),lastSeen:o>0?new Date(o).toISOString().slice(0,10):"unknown",sessions:t.size,origins:n}}function z(e){let r=[...jn(e.signals),...Mn(e)],t=[];for(let n of Map.groupBy(r,(o)=>o.key).values()){let o=new Set(n.filter((s)=>s.origin.promotable).map((s)=>s.origin.id));if(o.size>=2)t.push(Ur({observations:n.filter((s)=>s.origin.promotable),scope:"global"}));let i=Map.groupBy(n.filter((s)=>!s.origin.promotable||o.size<2),(s)=>s.origin.id);for(let s of i.values())t.push(Ur({observations:s,scope:"org"}))}return t.sort((n,o)=>o.observations-n.observations||n.title.localeCompare(o.title))}import{mkdir as zr}from"fs/promises";import Gr from"path";async function qe(e){let r=Bun.file(e);if(!await r.exists())return[];return(await r.text()).split(`
|
|
22
|
+
`).flatMap((t)=>{let[n,o]=t.split("\t");return n&&o?[{relativePath:n,key:o}]:[]})}function ze(e){return`${e.slice().sort((r,t)=>r.relativePath.localeCompare(t.relativePath)||r.key.localeCompare(t.key)).map((r)=>`${r.relativePath} ${r.key}`).join(`
|
|
23
|
+
`)}
|
|
24
|
+
`}function Nn(e){return Map.groupBy(e,_e)}async function $n(e){let r=Bun.file(e);return await r.exists()?U(await r.text()):[]}function Ge(e){return`${e.relativePath} ${e.key}`}async function G(e){await zr(e.paths.profileDirectory,{recursive:!0});let r=await qe(e.paths.profileManifestFile),t=await qe(e.paths.rejectedProfileFile),n=new Map(t.map((u)=>[Ge(u),u])),o=Nn(e.rules),i=r.map((u)=>u.relativePath),s=new Set([...o.keys(),...i]),a=[],d=0,y=0;for(let u of s){let b=Gr.join(e.paths.profileDirectory,u),m=await $n(b),E=new Map(m.flatMap((c)=>c.key===null?[]:[[c.key,c]])),P=o.get(u)??[],p=[];for(let c of r.filter((x)=>x.relativePath===u))if(P.some((x)=>x.key===c.key)&&!E.has(c.key))n.set(Ge(c),c);for(let c of m){if(c.key===null){p.push(c.content);continue}let x=P.find((Ut)=>Ut.key===c.key);p.push(x&&!c.edited?te(x):c.content),a.push({relativePath:u,key:c.key})}for(let c of P){let x={relativePath:u,key:c.key};if(!E.has(c.key)&&!n.has(Ge(x)))p.push(te(c)),a.push(x)}if(p.length>0)await zr(Gr.dirname(b),{recursive:!0}),await Bun.write(b,`${p.join(`
|
|
25
|
+
|
|
26
|
+
`)}
|
|
27
|
+
`),d+=1,y+=p.length}return await Bun.write(e.paths.profileManifestFile,ze(a)),await Bun.write(e.paths.rejectedProfileFile,ze([...n.values()])),{files:d,rules:y,rejected:n.size}}function Ln(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function le(e){let r=JSON.parse(e);if(!Ln(r))throw Error("Hook input must be a JSON object");return r}function de(e,r){let t=e[r];return typeof t==="string"?t:null}async function qn(e){let r=e.paths??h,{config:t,policy:n}=await v({configPath:e.configPath,managedConfigPath:e.managedConfigPath===void 0?r.managedConfigFile:e.managedConfigPath});if(!n.enabled)return null;let o=le(e.input),i=de(o,"cwd")??process.cwd(),s=await B({cwd:i,enabled:t.sources["git-metadata"],readRemote:e.readRemote});if(k({origin:s,cwd:i,patterns:n.blockedOrigins}))return null;return{profile:await oe({profileDirectory:r.profileDirectory,origin:s,targetRepo:Un.basename(i)})}}async function Hr(e){let r=await qn(e);return r===null?null:{hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:r.profile}}}async function He(e){let r=await Hr(e);if(r!==null)await Bun.stdout.write(`${JSON.stringify(r)}
|
|
28
|
+
`)}import{realpath as Rt}from"fs/promises";import Ke from"path";import{Database as Eo}from"bun:sqlite";import{mkdir as Co}from"fs/promises";import ko from"path";import{stat as Jn}from"fs/promises";import ce from"path";import{stat as zn}from"fs/promises";function Gn(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Hn(e){return Gn(e)&&e.code==="ENOENT"}async function Jr(e){let r=await zn(e.sourcePath).catch((m)=>{if(Hn(m))return null;throw m});if(r===null)return null;let t=r.mtimeMs,n=e.cursor;if(n!==null&&n.byteSize===r.size&&n.modifiedAt===t)return{values:[],cursor:n,rescanned:!1,bytesRead:0};let i=n!==null&&(r.size<n.byteOffset||r.size===n.byteSize&&t!==n.modifiedAt),s=n===null||i?0:n.byteOffset,a=Bun.file(e.sourcePath),d=new Uint8Array(await a.slice(s,r.size).arrayBuffer()),y=[],u=0,b=0;for(let m=0;m<d.length;m+=1){if(d[m]!==10)continue;let P=m>u&&d[m-1]===13?m-1:m,p=P-u;if(p>0)y.push({ref:{type:"file",sourcePath:e.sourcePath,byteOffset:s+u,byteLength:p},bytes:d.slice(u,P)});u=m+1,b=u}return{values:y,cursor:{sourcePath:e.sourcePath,byteSize:r.size,modifiedAt:t,byteOffset:s+b},rescanned:i,bytesRead:d.length}}async function A(e){let r=await Jr(e);if(r===null)return null;let t=new TextDecoder,n=r.values.map((o)=>{let i;try{i=JSON.parse(t.decode(o.bytes))}catch{throw Error(`Transcript record is invalid at byte offset ${o.ref.byteOffset}`)}return{value:i,ref:o.ref}});return{...r,values:n}}async function Wr(e){let r=await Jr(e);if(r===null)return null;return{...r,values:r.values.map((t)=>t.ref)}}function f(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function l(e,r){let t=e[r];return typeof t==="string"?t:null}function _(e,r){return e[r]===!0}function F(e,r){let t=e[r];return f(t)?t:null}function w(e){if(typeof e==="number"&&Number.isFinite(e))return e<10000000000?e*1000:e;if(typeof e==="string"){let r=Date.parse(e);return Number.isNaN(r)?0:r}return 0}var Wn=new Set(["CODE_ACTION","GREP_SEARCH","LIST_DIRECTORY","MCP_TOOL","REPLACE_FILE_CONTENT","RUN_COMMAND","VIEW_FILE","WRITE_TO_FILE"]);function Kr(e){let r=e.tool_calls;if(!Array.isArray(r))return null;let t=r.find(f);if(!t)return null;return{toolUseId:l(t,"id")??l(t,"tool_call_id"),name:l(t,"name")??"unknown"}}function Kn(e,r){return{toolUseId:l(e,"tool_call_id")??l(e,"call_id"),name:l(e,"tool_name")??r.toLowerCase()}}function Xn(e){let r=l(e,"type");if(r==="USER_INPUT")return l(e,"content")===null?null:"user-prompt";if(r==="PLANNER_RESPONSE"){if(Kr(e))return"tool-call";if(e.thinking!==void 0)return"thinking";return l(e,"content")===null?"thinking":"assistant-text"}if(r==="CHECKPOINT")return"session-end";return r!==null&&Wn.has(r)?"tool-result":null}function Zn(e){return ce.basename(ce.dirname(ce.dirname(ce.dirname(e))))}function Vn(e){if(!f(e.value))return null;let r=Xn(e.value),t=l(e.value,"type");if(r===null||t===null)return null;let n=e.value.step_index,o=r==="tool-call"?Kr(e.value):r==="tool-result"?Kn(e.value,t):null,i=l(e.value,"status");return{source:"antigravity",sessionId:e.sessionId,eventId:`antigravity:${typeof n==="number"?n:e.ref.byteOffset}`,parentEventId:null,timestamp:w(e.value.created_at),cwd:l(e.value,"workspace")??"",gitBranch:l(e.value,"git_branch"),kind:r,tool:o,isError:i==="ERROR"||i==="CANCELED",textRef:r==="user-prompt"||r==="assistant-text"?e.ref:null}}async function Xr(e){let r=await A(e);if(r===null)return null;let t=Zn(e.sourcePath),n=null,o=r.values.flatMap((i)=>{let s=Vn({...i,sessionId:t});if(s===null)return[];let a={...s,parentEventId:n};return n=s.eventId,[a]});return{source:"antigravity",sourcePath:e.sourcePath,events:o,cursor:r.cursor,rescanned:r.rescanned,bytesRead:r.bytesRead}}async function Zr(e){try{if(!(await Jn(e)).isDirectory())return[]}catch(n){if(f(n)&&n.code==="ENOENT")return[];throw n}let r=[],t="*/.system_generated/logs/transcript_full.jsonl";for await(let n of new Bun.Glob(t).scan({cwd:e,absolute:!0,dot:!0,onlyFiles:!0}))r.push(n);return r.sort()}import Qr from"path";import Vr from"path";function ue(e){let r=`${Vr.basename(e.ref.sourcePath)}:${w(e.record.timestamp)}`;return{source:"claude-code",sessionId:l(e.record,"sessionId")??Vr.basename(e.ref.sourcePath,".jsonl"),eventId:l(e.message,"id")??l(e.record,"uuid")??r,parentEventId:l(e.record,"parentUuid"),timestamp:w(e.record.timestamp),cwd:l(e.record,"cwd")??"",gitBranch:l(e.record,"gitBranch")}}function pe(e,r){if(typeof e==="string")return e.includes(r);if(!Array.isArray(e))return!1;return e.some((t)=>f(t)&&typeof t.content==="string"&&t.content.includes(r))}function Yn(e){if(e.interrupted)return"interruption";if(e.denied)return"permission-denied";if(e.questionAnswered)return"question-answered";if(e.planResolved)return"plan-resolved";return e.plainPrompt?"user-prompt":"tool-result"}function Yr(e){if(_(e.record,"isMeta"))return[];let r=ue(e),t=e.message.content,n=pe(t,"[Request interrupted by user"),o=pe(t,"user doesn't want to proceed with this tool use"),i=pe(t,"User has answered your questions"),s=pe(t,"The user has approved your plan"),a=typeof t==="string",d=Yn({interrupted:n,denied:o,questionAnswered:i,planResolved:s,plainPrompt:a});return[{...r,kind:d,tool:null,isError:_(e.record,"is_error"),textRef:a&&!n&&!o?e.ref:null}]}function Qn(e){if(e==="ExitPlanMode")return"plan-presented";if(e==="AskUserQuestion")return"question-asked";return"tool-call"}function eo(e){if(l(e,"type")!=="tool_use")return null;let r=l(e,"name");if(r===null)return null;return{toolUseId:l(e,"id"),name:r}}function ro(e){let r=e.content;return Array.isArray(r)?r:[r]}function to(e){return e.blocks.length===1&&(l(e.block,"type")==="text"||e.kind==="question-asked"||e.kind==="plan-presented")?e.ref:null}function no(e){let r=ue(e),t=ro(e.message),n=[];for(let o of t){if(!f(o))continue;let i=l(o,"type"),s=eo(o),a=s===null?i==="thinking"?"thinking":"assistant-text":Qn(s.name);n.push({...r,kind:a,tool:s,isError:!1,textRef:to({blocks:t,block:o,ref:e.ref,kind:a})})}return n}function oo(e){if(!f(e.value))return[];let r=l(e.value,"type");if(r==="result"){let n=w(e.value.timestamp);return[{source:"claude-code",sessionId:l(e.value,"session_id")??l(e.value,"sessionId")??Qr.basename(e.ref.sourcePath,".jsonl"),eventId:l(e.value,"uuid")??`result:${Qr.basename(e.ref.sourcePath)}:${n}`,parentEventId:l(e.value,"parentUuid"),timestamp:n,cwd:l(e.value,"cwd")??"",gitBranch:l(e.value,"gitBranch"),kind:"session-end",tool:null,isError:_(e.value,"is_error"),textRef:null}]}let t=F(e.value,"message");if(t===null)return[];if(r==="assistant")return no({record:e.value,message:t,ref:e.ref});if(r==="user")return Yr({record:e.value,message:t,ref:e.ref});return[]}async function fe(e){let r=await A(e);if(r===null)return null;return{source:"claude-code",sourcePath:e.sourcePath,events:r.values.flatMap(oo),cursor:r.cursor,rescanned:r.rescanned,bytesRead:r.bytesRead}}async function et(e){let r=new Bun.Glob("**/*.jsonl"),t=[];for await(let n of r.scan({cwd:e,absolute:!0,onlyFiles:!0}))t.push(n);return t.sort()}import io from"path";function so(e){if(!f(e.value))return null;if((l(e.value,"display")??l(e.value,"prompt"))===null)return null;let t=w(e.value.timestamp);return{source:"claude-prompts",sessionId:l(e.value,"sessionId")??"claude-prompts",eventId:l(e.value,"id")??`prompt:${t}:${e.ref.byteOffset}`,parentEventId:null,timestamp:t,cwd:l(e.value,"project")??"",gitBranch:null,kind:"user-prompt",tool:null,isError:!1,textRef:e.ref}}async function rt(e){let r=await A(e);if(r===null)return null;let t=r.values.map(so).filter((n)=>n!==null);return{source:"claude-prompts",sourcePath:io.resolve(e.sourcePath),events:t,cursor:r.cursor,rescanned:r.rescanned,bytesRead:r.bytesRead}}import ao from"path";function nt(e){return{sessionId:ao.basename(e,".jsonl"),cwd:"",gitBranch:null}}function tt(e){if(!f(e.value)||l(e.value,"type")!=="session_meta")return null;let r=F(e.value,"payload");if(r===null)return null;let t=F(r,"git");return{sessionId:l(r,"id")??nt(e.sourcePath).sessionId,cwd:l(r,"cwd")??"",gitBranch:t?l(t,"branch"):null}}async function lo(e){let r=Bun.file(e).stream().getReader(),t=[],n=0;while(!0){let s=await r.read();if(s.done)break;let a=s.value.indexOf(10),d=a<0?s.value:s.value.slice(0,a);if(t.push(d),n+=d.byteLength,a>=0){await r.cancel();break}}let o=new Uint8Array(n),i=0;for(let s of t)o.set(s,i),i+=s.byteLength;try{return JSON.parse(new TextDecoder().decode(o))}catch{throw Error("Codex transcript has an invalid session header")}}async function ot(e){for(let t of e.values){let n=tt({sourcePath:e.sourcePath,value:t.value});if(n!==null)return n}return tt({sourcePath:e.sourcePath,value:await lo(e.sourcePath)})??nt(e.sourcePath)}function st(e){let r=l(e,"type");if(r!=="function_call"&&r!=="custom_tool_call"&&r!=="local_shell_call")return null;return{toolUseId:l(e,"call_id")??l(e,"id"),name:l(e,"name")??(r==="local_shell_call"?"shell":"unknown")}}function co(e){let r=l(e,"type"),t=l(e,"role");if(r==="message"&&t==="user")return"user-prompt";if(r==="message"&&t==="assistant")return"assistant-text";if(r==="reasoning")return"thinking";if(r==="function_call_output"||r==="custom_tool_call_output"||r==="local_shell_call_output")return"tool-result";return st(e)?"tool-call":null}function it(e){return{source:"codex",sessionId:e.context.sessionId,eventId:`codex:${e.ref.byteOffset}`,parentEventId:null,timestamp:w(e.envelope.timestamp),cwd:e.context.cwd,gitBranch:e.context.gitBranch,kind:e.kind,tool:e.tool,isError:!1,textRef:e.kind==="user-prompt"||e.kind==="assistant-text"?e.ref:null}}function uo(e){if(!f(e.value))return null;let r=l(e.value,"type"),t=F(e.value,"payload");if(t===null)return null;if(r==="response_item"){let i=co(t);return i===null?null:it({context:e.context,envelope:e.value,ref:e.ref,kind:i,tool:st(t)})}let n=l(t,"type");if(r!=="event_msg"||n!=="task_complete"&&n!=="task_completed"&&n!=="turn_aborted")return null;let o=n==="turn_aborted"?"interruption":"session-end";return{...it({context:e.context,envelope:e.value,ref:e.ref,kind:o,tool:null}),isError:_(t,"is_error")}}async function at(e){let r=await A(e);if(r===null)return null;if(r.values.length===0)return{source:"codex",sourcePath:e.sourcePath,events:[],cursor:r.cursor,rescanned:r.rescanned,bytesRead:r.bytesRead};let t=await ot({sourcePath:e.sourcePath,values:r.values}),n=null,o=r.values.flatMap((i)=>{let s=uo({...i,context:t});if(s===null)return[];let a={...s,parentEventId:n};return n=s.eventId,[a]});return{source:"codex",sourcePath:e.sourcePath,events:o,cursor:r.cursor,rescanned:r.rescanned,bytesRead:r.bytesRead}}async function lt(e){let r=[];for await(let t of new Bun.Glob("**/rollout-*.jsonl").scan({cwd:e,absolute:!0,onlyFiles:!0}))r.push(t);return r.sort()}function po(e){return{type:"sqlite-blob",sourcePath:e.context.sourcePath,blobId:e.blobId,jsonPath:e.jsonPath,unwrap:e.unwrap}}function fo(e){let r=l(e,"type");if(r!=="tool-call"&&r!=="tool_use"&&r!=="tool_call")return null;return{toolUseId:l(e,"toolCallId")??l(e,"tool_call_id")??l(e,"id"),name:l(e,"toolName")??l(e,"name")??"unknown"}}function ye(e){return{source:"cursor",sessionId:e.context.sessionId,eventId:`cursor:${e.blobId}:${e.index}`,parentEventId:null,timestamp:e.context.timestamp+e.index,cwd:e.context.cwd,gitBranch:null,kind:e.kind,tool:e.tool,isError:!1,textRef:e.ref}}function dt(e){let r=e.role==="user"&&e.text.includes("<user_query>"),t=e.role==="assistant";if(!r&&!t)return null;return ye({context:e.context,blobId:e.blobId,index:e.index,kind:r?"user-prompt":"assistant-text",tool:null,ref:po({context:e.context,blobId:e.blobId,jsonPath:e.jsonPath,unwrap:r?"user-query":null})})}function ct(e){if(!f(e.blob.value))return[];let r=l(e.blob.value,"role");if(r==="tool")return[ye({context:e.context,blobId:e.blob.id,index:0,kind:"tool-result",tool:null,ref:null})];let t=e.blob.value.content;if(typeof t==="string"){let n=dt({context:e.context,blobId:e.blob.id,index:0,role:r??"",text:t,jsonPath:["content"]});return n?[n]:[]}if(!Array.isArray(t))return[];return t.flatMap((n,o)=>{if(!f(n))return[];let i=l(n,"type");if(r==="assistant"&&i==="reasoning")return[ye({context:e.context,blobId:e.blob.id,index:o,kind:"thinking",tool:null,ref:null})];let s=fo(n);if(s!==null)return[ye({context:e.context,blobId:e.blob.id,index:o,kind:"tool-call",tool:s,ref:null})];let a=l(n,"text"),d=a?dt({context:e.context,blobId:e.blob.id,index:o,role:r??"",text:a,jsonPath:["content",o,"text"]}):null;return d?[d]:[]})}import{Database as yo}from"bun:sqlite";import{stat as mo}from"fs/promises";import M from"path";async function Je(e){try{let r=await mo(e);return{size:r.size,modifiedAt:r.mtimeMs}}catch{return null}}function go(e){let r=/^(?:[0-9a-fA-F]{2})+$/.test(e)?new TextDecoder().decode(Uint8Array.from(e.match(/.{2}/g)?.map((t)=>Number.parseInt(t,16))??[])):e;try{return JSON.parse(r)}catch{return null}}function ho(e){try{return JSON.parse(new TextDecoder().decode(e))}catch{return null}}async function bo(e){let r=Bun.file(M.join(M.dirname(e),"meta.json"));if(!await r.exists())return null;try{return JSON.parse(await r.text())}catch{return null}}async function ut(e){let r=await Je(e);if(r===null)return null;let[t,n]=await Promise.all([Je(`${e}-wal`),Je(M.join(M.dirname(e),"meta.json"))]),o=[r,t,n].filter((i)=>i!==null);return{size:o.reduce((i,s)=>i+s.size,0),modifiedAt:Math.max(...o.map((i)=>i.modifiedAt))}}async function pt(e){let r=null;try{r=new yo(e.sourcePath,{readonly:!0,strict:!0});let t=r.query("SELECT id, data FROM blobs ORDER BY rowid").all().flatMap((a)=>{let d=ho(a.data);return d===null?[]:[{id:a.id,value:d}]}),n=go(r.query("SELECT value FROM meta WHERE key = '0'").get()?.value??""),o=await bo(e.sourcePath),i=f(n)?n:{},s=f(o)?o:{};return{blobs:t,sessionId:l(i,"agentId")??M.basename(M.dirname(e.sourcePath)),cwd:l(s,"cwd")??"",timestamp:w(s.createdAtMs)||w(i.createdAt)||e.signature.modifiedAt,cursor:{sourcePath:e.sourcePath,byteSize:e.signature.size,modifiedAt:e.signature.modifiedAt,byteOffset:e.signature.size},bytesRead:e.signature.size}}catch{throw Error("Cursor chat store could not be read")}finally{r?.close()}}async function ft(e){let r=await ut(e.sourcePath);if(r===null)return null;if(e.cursor!==null&&e.cursor.byteSize===r.size&&e.cursor.modifiedAt===r.modifiedAt)return{source:"cursor",sourcePath:e.sourcePath,events:[],cursor:e.cursor,rescanned:!1,bytesRead:0};let t=await pt({sourcePath:e.sourcePath,signature:r}),n=[],o=null;for(let i of t.blobs)for(let s of ct({blob:i,context:{sourcePath:e.sourcePath,sessionId:t.sessionId,cwd:t.cwd,timestamp:t.timestamp+n.length}})){let a={...s,parentEventId:o};n.push(a),o=a.eventId}return{source:"cursor",sourcePath:e.sourcePath,events:n,cursor:t.cursor,rescanned:e.cursor!==null,bytesRead:t.bytesRead}}async function yt(e){let r=[];for await(let t of new Bun.Glob("**/store.db").scan({cwd:e,absolute:!0,onlyFiles:!0}))r.push(t);return r.sort()}import wo from"path";async function mt(e){let r=await Wr(e);if(r===null)return null;return{source:"shell",sourcePath:wo.resolve(e.sourcePath),events:r.values.map((t)=>({source:"shell",sessionId:"shell-history",eventId:`shell:${t.byteOffset}`,parentEventId:null,timestamp:r.cursor.modifiedAt,cwd:"",gitBranch:null,kind:"user-prompt",tool:null,isError:!1,textRef:t})),cursor:r.cursor,rescanned:r.rescanned,bytesRead:r.bytesRead}}function xo(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function We(e){if(!xo(e)||typeof e.sourcePath!=="string")return null;if(e.type==="file"&&typeof e.byteOffset==="number"&&typeof e.byteLength==="number")return{type:"file",sourcePath:e.sourcePath,byteOffset:e.byteOffset,byteLength:e.byteLength};if(e.type!=="sqlite-blob"||typeof e.blobId!=="string"||!Array.isArray(e.jsonPath)||!e.jsonPath.every((r)=>typeof r==="string"||typeof r==="number")||e.unwrap!==null&&e.unwrap!=="user-query")return null;return{type:"sqlite-blob",sourcePath:e.sourcePath,blobId:e.blobId,jsonPath:e.jsonPath,unwrap:e.unwrap}}function H(e){return JSON.stringify(e)}async function*gt(e){if(e.config.sources.antigravity){let r=await Zr(e.paths.antigravityBrainDirectory);for(let t of r){let n=await Xr({sourcePath:t,cursor:await e.getCursor(t)});if(n!==null)yield n}}if(e.config.sources["claude-code"]){let r=await et(e.paths.claudeProjectsDirectory);for(let t of r){let n=await fe({sourcePath:t,cursor:await e.getCursor(t)});if(n!==null)yield n}}if(e.config.sources["claude-prompts"]){let r=e.paths.claudePromptHistoryFile,t=await rt({sourcePath:r,cursor:await e.getCursor(r)});if(t!==null)yield t}if(e.config.sources.codex){let r=await lt(e.paths.codexSessionsDirectory);for(let t of r){let n=await at({sourcePath:t,cursor:await e.getCursor(t)});if(n!==null)yield n}}if(e.config.sources.cursor){let r=await yt(e.paths.cursorChatsDirectory);for(let t of r){let n=await ft({sourcePath:t,cursor:await e.getCursor(t)});if(n!==null)yield n}}if(e.config.sources.shell)for(let r of e.paths.shellHistoryFiles){let t=await mt({sourcePath:r,cursor:await e.getCursor(r)});if(t!==null)yield t}}function Po(e){if(e.query("PRAGMA user_version").get()?.user_version===2)return;e.exec(`
|
|
29
|
+
DROP TABLE IF EXISTS events;
|
|
30
|
+
DROP TABLE IF EXISTS cursors;
|
|
31
|
+
`)}function ht(e){Po(e),e.exec(`
|
|
32
|
+
PRAGMA journal_mode = WAL;
|
|
33
|
+
PRAGMA foreign_keys = ON;
|
|
34
|
+
|
|
35
|
+
CREATE TABLE IF NOT EXISTS cursors (
|
|
36
|
+
source_path TEXT PRIMARY KEY,
|
|
37
|
+
source TEXT NOT NULL,
|
|
38
|
+
byte_size INTEGER NOT NULL,
|
|
39
|
+
modified_at REAL NOT NULL,
|
|
40
|
+
byte_offset INTEGER NOT NULL
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
44
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
45
|
+
source_path TEXT NOT NULL,
|
|
46
|
+
source TEXT NOT NULL,
|
|
47
|
+
session_id TEXT NOT NULL,
|
|
48
|
+
event_id TEXT NOT NULL,
|
|
49
|
+
parent_event_id TEXT,
|
|
50
|
+
timestamp INTEGER NOT NULL,
|
|
51
|
+
cwd TEXT NOT NULL,
|
|
52
|
+
git_branch TEXT,
|
|
53
|
+
kind TEXT NOT NULL,
|
|
54
|
+
tool_use_id TEXT,
|
|
55
|
+
tool_name TEXT,
|
|
56
|
+
is_error INTEGER NOT NULL,
|
|
57
|
+
text_ref TEXT
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
CREATE INDEX IF NOT EXISTS events_source_path
|
|
61
|
+
ON events(source_path);
|
|
62
|
+
CREATE INDEX IF NOT EXISTS events_session
|
|
63
|
+
ON events(source, session_id);
|
|
64
|
+
CREATE INDEX IF NOT EXISTS events_kind
|
|
65
|
+
ON events(kind);
|
|
66
|
+
|
|
67
|
+
PRAGMA user_version = 2;
|
|
68
|
+
`)}function bt(e){e.database.transaction((t)=>{if(t.rescanned)e.database.query("DELETE FROM events WHERE source_path = ?").run(t.sourcePath);let n=e.database.query(`INSERT INTO events (
|
|
69
|
+
source_path, source, session_id, event_id, parent_event_id,
|
|
70
|
+
timestamp, cwd, git_branch, kind, tool_use_id, tool_name,
|
|
71
|
+
is_error, text_ref
|
|
72
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);for(let o of t.events)n.run(t.sourcePath,o.source,o.sessionId,o.eventId,o.parentEventId,o.timestamp,o.cwd,o.gitBranch,o.kind,o.tool?.toolUseId??null,o.tool?.name??null,o.isError?1:0,o.textRef===null?null:JSON.stringify(o.textRef));e.database.query(`INSERT INTO cursors (
|
|
73
|
+
source_path, source, byte_size, modified_at, byte_offset
|
|
74
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
75
|
+
ON CONFLICT(source_path) DO UPDATE SET
|
|
76
|
+
source = excluded.source,
|
|
77
|
+
byte_size = excluded.byte_size,
|
|
78
|
+
modified_at = excluded.modified_at,
|
|
79
|
+
byte_offset = excluded.byte_offset`).run(t.sourcePath,t.source,t.cursor.byteSize,t.cursor.modifiedAt,t.cursor.byteOffset)})(e.batch)}function Ro(e){if(e===null)return null;try{return We(JSON.parse(e))}catch{return null}}function vo(e){return{id:e.id,sourcePath:e.source_path,source:e.source,sessionId:e.session_id,eventId:e.event_id,parentEventId:e.parent_event_id,timestamp:e.timestamp,cwd:e.cwd,gitBranch:e.git_branch,kind:e.kind,tool:e.tool_name===null?null:{toolUseId:e.tool_use_id,name:e.tool_name},isError:e.is_error===1,textRef:Ro(e.text_ref)}}class me{#e;constructor(e){this.#e=e}getCursor(e){let r=this.#e.query(`SELECT source_path, byte_size, modified_at, byte_offset
|
|
80
|
+
FROM cursors WHERE source_path = ?`).get(e);return r===null?null:{sourcePath:r.source_path,byteSize:r.byte_size,modifiedAt:r.modified_at,byteOffset:r.byte_offset}}saveBatch(e){bt({database:this.#e,batch:e})}listEvents(){return this.#e.query(`SELECT id, source_path, source, session_id, event_id,
|
|
81
|
+
parent_event_id, timestamp, cwd, git_branch, kind, tool_use_id,
|
|
82
|
+
tool_name, is_error, text_ref
|
|
83
|
+
FROM events ORDER BY source, session_id, id`).all().map(vo)}getCorpusSummary(){return this.#e.query(`SELECT
|
|
84
|
+
(SELECT COUNT(*) FROM (
|
|
85
|
+
SELECT DISTINCT source, session_id FROM events
|
|
86
|
+
)) AS sessions,
|
|
87
|
+
COALESCE((SELECT SUM(byte_size) FROM cursors), 0) AS bytes,
|
|
88
|
+
(SELECT COUNT(*) FROM (
|
|
89
|
+
SELECT DISTINCT date(timestamp / 1000, 'unixepoch')
|
|
90
|
+
FROM events WHERE timestamp > 0
|
|
91
|
+
)) AS activeDays`).get()??{sessions:0,bytes:0,activeDays:0}}countEvents(){return this.#e.query("SELECT COUNT(*) AS count FROM events").get()?.count??0}countSessions(){return this.getCorpusSummary().sessions}close(){this.#e.close()}}async function ge(e){await Co(ko.dirname(e),{recursive:!0});let r=new Eo(e,{create:!0});return ht(r),new me(r)}async function wt(e){let r=0,t=0,n=0,o=0;for await(let i of gt({config:e.config,paths:e.paths,getCursor:(s)=>e.index.getCursor(s)}))e.index.saveBatch(i),r+=1,t+=i.events.length,n+=i.bytesRead,o+=i.rescanned?1:0;return{files:r,events:t,sessions:e.index.countSessions(),bytesRead:n,rescannedFiles:o}}async function xt(e){let r=await fe({sourcePath:e.sourcePath,cursor:e.index.getCursor(e.sourcePath)});if(r===null)return 0;return e.index.saveBatch(r),r.events.length}async function Pt(e){let r=e.index.listEvents(),t=await ae({events:r,corpus:e.index.getCorpusSummary(),gitMetadataEnabled:e.config.sources["git-metadata"],readRemote:e.readRemote,blockedOrigins:e.blockedOrigins}),n=z({events:t.events,signals:t.corrections,origins:t.origins});await G({paths:e.paths,rules:n})}async function So(e){let r,t;try{[r,t]=await Promise.all([Rt(e.filePath),Rt(e.directory)])}catch{return!1}let n=Ke.relative(t,r);return n.length>0&&!n.startsWith(`..${Ke.sep}`)&&n!==".."&&!Ke.isAbsolute(n)}async function Xe(e){let r=e.paths??h,{config:t,policy:n}=await v({configPath:e.configPath,managedConfigPath:e.managedConfigPath===void 0?r.managedConfigFile:e.managedConfigPath});if(!n.enabled||!t.sources["claude-code"])return;let o=de(le(e.input),"transcript_path");if(o===null||!await So({filePath:o,directory:r.claudeProjectsDirectory}))throw Error("Session hook received an invalid transcript path");let i=await ge(r.indexDatabase);try{await xt({index:i,sourcePath:o}),await Pt({index:i,config:t,paths:r,readRemote:e.readRemote,blockedOrigins:n.blockedOrigins})}finally{i.close()}}var Io=[{id:"antigravity",question:"Enable Antigravity CLI transcripts?"},{id:"claude-code",question:"Enable Claude Code transcripts?"},{id:"claude-prompts",question:"Enable Claude prompt history?"},{id:"codex",question:"Enable Codex transcripts?"},{id:"cursor",question:"Enable Cursor CLI chat stores?"},{id:"shell",question:"Enable shell history?"}];function Ao(e){return prompt(`${e} [y/N]`)?.trim().toLowerCase()==="y"}async function vt(e={}){await Oe({config:O,configPath:e.configPath});let r=e.ask??Ao,t=O,n=!1;for(let d of Io){let y=await r(d.question);t=De({config:t,source:d.id,enabled:y}),n=n||y}let o=await r("Enable reading git remote origins for organization-scoped profiles?"),i=await r("Enable semantic distillation through your authenticated agent CLI?"),s=De({config:t,source:"git-metadata",enabled:o}),a=kr({config:s,enabled:i});await Oe({config:a,configPath:e.configPath}),console.log(n||o||i?"Selected sources and capabilities enabled.":"All capture sources remain disabled.")}import To from"path";async function Et(e={}){let r=e.cwd??process.cwd(),t=e.paths??h,{config:n,policy:o}=await v({configPath:e.configPath,managedConfigPath:e.managedConfigPath===void 0?t.managedConfigFile:e.managedConfigPath});if(!o.enabled)throw Error("Shadowclone is disabled by managed policy");let i=await B({cwd:r,enabled:n.sources["git-metadata"],readRemote:e.readRemote});if(k({origin:i,cwd:r,patterns:o.blockedOrigins}))throw Error("Managed policy blocks this repository");let s=await q({profileDirectory:t.profileDirectory,outputPath:t.compiledProfileFile,origin:i,targetRepo:To.basename(r)});await Be({targetDirectory:r,profile:s}),console.log("Installed .claude/agents/shadowclone.md for this repository.")}import Oo from"os";import{Database as Do}from"bun:sqlite";var Ze=[{label:"pem-block",pattern:/-----BEGIN [^-]+-----[\s\S]*?-----END [^-]+-----/g,replacement:"[redacted:pem-block]"},{label:"authorization-header",pattern:/(Authorization\s*:\s*)(?:Bearer|Basic|Token)\s+[A-Za-z0-9._~+/=-]+/gi,replacement:"$1[redacted:authorization]"},{label:"llm-api-key",pattern:/\bsk-[A-Za-z0-9_-]{12,}/g,replacement:"[redacted:llm-api-key]"},{label:"github-token",pattern:/\b(?:gh[porsu]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,})/g,replacement:"[redacted:github-token]"},{label:"slack-token",pattern:/\bxox[abprs]-[A-Za-z0-9-]{10,}/g,replacement:"[redacted:slack-token]"},{label:"aws-access-key-id",pattern:/\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g,replacement:"[redacted:aws-access-key-id]"},{label:"jwt",pattern:/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]+/g,replacement:"[redacted:jwt]"},{label:"secret-assignment",pattern:/\b([A-Za-z_][A-Za-z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|CREDENTIALS)[A-Za-z0-9_]*)(\s*=\s*)(?:"[^"\n]+"|'[^'\n]+'|[^\s"'[\n]+)/gi,replacement:"$1$2[redacted:secret-assignment]"},{label:"database-url",pattern:/\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis):\/\/[^\s"'`]+/gi,replacement:"[redacted:database-url]"},{label:"email-address",pattern:/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi,replacement:"[redacted:email-address]"},{label:"private-ip",pattern:/\b(?:10(?:\.\d{1,3}){3}|192\.168(?:\.\d{1,3}){2}|172\.(?:1[6-9]|2\d|3[01])(?:\.\d{1,3}){2})\b/g,replacement:"[redacted:private-ip]"},{label:"internal-hostname",pattern:/\b(?:[a-z0-9-]+\.)+(?:internal|corp|local)\b/gi,replacement:"[redacted:internal-hostname]"},{label:"cloud-resource",pattern:/\b(?:arn:aws:[^\s"'`]+|(?:s3|gs):\/\/[a-z0-9][a-z0-9._-]{2,}[^\s"'`]*)/gi,replacement:"[redacted:cloud-resource]"},{label:"absolute-path",pattern:/(^|[\s("'=])\/(?:Users|home|private|var|opt|etc|srv|Volumes)\/[^\s"'`),]+/gm,replacement:"$1[redacted:absolute-path]"},{label:"high-entropy-string",pattern:/\b(?=[A-Za-z0-9+/_=-]{40,}\b)(?=[A-Za-z0-9+/_=-]*[A-Z])(?=[A-Za-z0-9+/_=-]*[a-z])(?=[A-Za-z0-9+/_=-]*\d)[A-Za-z0-9+/_=-]+\b/g,replacement:"[redacted:high-entropy-string]"}];function Ct(e){let r=e.homeDirectory??Oo.homedir(),t=r?e.text.replaceAll(r,"~"):e.text;for(let n of Ze)t=t.replace(n.pattern,n.replacement);return t}var Jl=Ze.map((e)=>e.label);function Bo(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function _o(e){let r=e.value;for(let t of e.path){if(typeof t==="number"){if(!Array.isArray(r))return null;r=r[t];continue}if(!Bo(r))return null;r=r[t]}return r}function Fo(e){if(e.unwrap===null)return e.text;return e.text.match(/<user_query>\s*([\s\S]*?)\s*<\/user_query>/)?.[1]??""}async function jo(e){if(!await Bun.file(e.sourcePath).exists())return"";let r=null;try{r=new Do(e.sourcePath,{readonly:!0,strict:!0});let t=r.query("SELECT data FROM blobs WHERE id = ?").get(e.blobId);if(t===null)return"";let n=JSON.parse(new TextDecoder().decode(t.data)),o=_o({value:n,path:e.jsonPath});return typeof o==="string"?Fo({text:o,unwrap:e.unwrap}):""}catch{return""}finally{r?.close()}}async function Ve(e){if(e.ref.type==="sqlite-blob"){let n=await jo(e.ref);return Ct({text:n})}let r=Bun.file(e.ref.sourcePath);if(!await r.exists()||r.size<e.ref.byteOffset+e.ref.byteLength)return"";let t=await r.slice(e.ref.byteOffset,e.ref.byteOffset+e.ref.byteLength).text();return Ct({text:t})}function J(e){let r=e.batchSize??20;if(!Number.isInteger(r)||r<1)throw Error("Distillation batch size must be a positive integer");let t=[],n=Map.groupBy(e.signals,(o)=>o.origin.id);for(let o of n.values()){let[i]=o;if(!i)continue;for(let s=0;s<o.length;s+=r)t.push({origin:i.origin,signals:o.slice(s,s+r)})}return t}async function Ye(e){if(new Set(e.signals.map((o)=>o.origin.id)).size>1)throw Error("A distillation request must contain one origin");let t=e.maxExcerptCharacters??4000,n=[];for(let o of e.signals){let i=[];for(let s of o.textRefs){let a=await Ve({ref:s});if(a.length>0)i.push(a.slice(0,t))}n.push([`Kind: ${o.kind}`,`Pattern: ${o.label}`,...i.map((s)=>`Excerpt:
|
|
92
|
+
${s}`)].join(`
|
|
93
|
+
`))}return["Turn these correction moments into short, reusable engineering rules.","Use only the evidence shown. Do not repeat secrets or private identifiers.","Return JSON matching the supplied schema.","",n.join(`
|
|
94
|
+
|
|
95
|
+
`)].join(`
|
|
96
|
+
`)}import{mkdir as Mo}from"fs/promises";import kt from"path";function St(e){let r=e.signals.map((t)=>({kind:t.kind,sessionId:t.sessionId,timestamp:t.timestamp,refs:t.textRefs}));return new Bun.CryptoHasher("sha256").update(`${e.origin.id}:${JSON.stringify(r)}`).digest("hex").slice(0,24)}async function It(e){let r=Bun.file(kt.join(e.checkpointDirectory,`${St(e.batch)}.json`));if(!await r.exists())return null;let t=await r.json();return Array.isArray(t)?t.filter(No):null}function No(e){if(typeof e!=="object"||e===null||Array.isArray(e))return!1;let r="origins"in e&&Array.isArray(e.origins)?e.origins:null;return"key"in e&&typeof e.key==="string"&&"title"in e&&typeof e.title==="string"&&"body"in e&&typeof e.body==="string"&&"section"in e&&(e.section==="engineering"||e.section==="workflow"||e.section==="boundaries")&&"scope"in e&&(e.scope==="global"||e.scope==="org")&&"originDirectory"in e&&(typeof e.originDirectory==="string"||e.originDirectory===null)&&"observations"in e&&typeof e.observations==="number"&&"confidence"in e&&typeof e.confidence==="number"&&"lastSeen"in e&&typeof e.lastSeen==="string"&&"sessions"in e&&typeof e.sessions==="number"&&r?.every((t)=>typeof t==="string")===!0}async function At(e){await Mo(e.checkpointDirectory,{recursive:!0}),await Bun.write(kt.join(e.checkpointDirectory,`${St(e.batch)}.json`),`${JSON.stringify(e.rules,null,2)}
|
|
97
|
+
`)}var Qe={type:"object",additionalProperties:!1,required:["rules"],properties:{rules:{type:"array",maxItems:8,items:{type:"object",additionalProperties:!1,required:["title","body","section"],properties:{title:{type:"string",maxLength:120},body:{type:"string",maxLength:600},section:{type:"string",enum:["engineering","workflow","boundaries"]}}}}}};function Tt(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Ot(e,r){return e.replaceAll("<!--","").replaceAll("-->","").replace(/\s+/g," ").trim().slice(0,r)}function $o(e){return e==="engineering"||e==="workflow"||e==="boundaries"?e:null}function er(e){if(!Tt(e)||!Array.isArray(e.rules))throw Error("The engine returned an invalid distillation result");return e.rules.flatMap((r)=>{if(!Tt(r))return[];let t=$o(r.section);if(typeof r.title!=="string"||typeof r.body!=="string"||t===null)return[];let n=Ot(r.title,120),o=Ot(r.body,600);return n&&o?[{title:n,body:o,section:t}]:[]})}var Lo=new Set(["user-prompt","plan-presented","plan-resolved","question-asked","question-answered","permission-denied","interruption"]);function Dt(e){return Lo.has(e.kind)&&e.textRef!==null}function W(e){let r=new Set(e.events.flatMap((n)=>Dt(n)&&n.textRef?[H(n.textRef)]:[])),t=new Set(e.events.flatMap((n)=>n.kind==="assistant-text"&&n.textRef?[H(n.textRef)]:[]));return e.signals.map((n)=>({...n,textRefs:n.textRefs.filter((o)=>{let i=H(o);return r.has(i)||t.has(i)})}))}function qo(e){let[r]=e.signals;if(!r)return[];let t=e.signals.length,n=new Set(e.signals.map((i)=>i.sessionId)).size,o=Math.max(...e.signals.map((i)=>i.timestamp));return er(e.value).map((i)=>{let s=new Bun.CryptoHasher("sha256").update(`semantic:${i.title.toLowerCase()}`).digest("hex").slice(0,16);return{...i,key:s,scope:"org",originDirectory:r.origin.directoryName,observations:t,confidence:1,lastSeen:o>0?new Date(o).toISOString().slice(0,10):"unknown",sessions:n,origins:[r.origin.id]}})}function zo(e){if(e.structured!==null&&e.structured!==void 0)return e.structured;try{return JSON.parse(e.text)}catch{throw Error("The engine returned no structured distillation result")}}async function Bt(e){let r=[],t=0,n=W({signals:e.signals,events:e.events}).filter((o)=>o.textRefs.length>0);for(let o of J({signals:n})){let i=await It({checkpointDirectory:e.checkpointDirectory,batch:o});if(i!==null){r.push(...i);continue}let s=await Ye({signals:o.signals}),a=await e.runner({prompt:s,cwd:e.workingDirectory,allowedTools:[],permissionMode:"dontAsk",maxBudgetUsd:e.maxBudgetUsd,outputSchema:Qe});if(t+=1,a.isError)throw Error("The agent engine failed during distillation");let d=qo({value:zo(a),signals:o.signals});await At({checkpointDirectory:e.checkpointDirectory,batch:o,rules:d}),r.push(...d)}return{rules:r,engineRuns:t}}async function _t(e={}){let r=e.paths??h,{config:t,policy:n}=await v({configPath:e.configPath,managedConfigPath:e.managedConfigPath===void 0?r.managedConfigFile:e.managedConfigPath});if(!n.enabled)throw Error("Shadowclone is disabled by managed policy");let o=await ge(e.databasePath??r.indexDatabase);try{let i=await wt({index:o,config:t,paths:r}),s=o.listEvents(),a=await ae({events:s,corpus:o.getCorpusSummary(),gitMetadataEnabled:t.sources["git-metadata"],readRemote:e.readRemote,blockedOrigins:n.blockedOrigins}),d=z({events:a.events,signals:a.corrections,origins:a.origins}),y=[],u=!1;if(e.deep){if(!t.distillation.deep)throw Error("Deep distillation is disabled in config");let b=W({signals:a.corrections,events:a.events}).filter((E)=>E.textRefs.length>0),m=J({signals:b});if(console.log(`Deep distillation will run up to ${m.length} agent batches.`),m.length>0){if(n.distillation!=="allowed")throw Error("Managed policy does not allow remote distillation");let E=e.runner?null:await T({purpose:"distill",allowedEngines:n.allowedEngines}),P=e.runner??E?.runner;if(!P)throw Error("No authenticated agent engine is available");let p=await Bt({signals:b,runner:P,workingDirectory:r.shadowcloneDirectory,checkpointDirectory:r.distillDirectory,events:a.events});y=p.rules,u=p.engineRuns>0}}if(await G({paths:r,rules:[...d,...y]}),console.log(Fe({report:a.report,networkCallsMade:u})),i.rescannedFiles>0)console.log(`
|
|
98
|
+
Rescanned ${i.rescannedFiles} rewritten files.`)}finally{o.close()}}import Mt from"path";var Go=["Read","Grep","Glob","Edit","Write","Bash(git status)","Bash(git diff)","Bash(bun test)","Bash(bun run typecheck)"],Ft={push:"Bash(git push:*)","pr-draft":"Bash(gh pr create --draft:*)","pr-reply":"Bash(gh pr comment:*)"},Ho=["Bash(git add:*)","Bash(git commit:*)","Bash(git push --force:*)","Bash(git push -f:*)","Bash(gh pr merge:*)"];function rr(e){let r=e.configuredPolicy??{allow:[],maxBudgetUsd:2,requireCleanExit:!0},t=e.managedActionTier==="act"?r.allow:[],n=S.filter((a)=>t.includes(a)&&e.approvedActions.includes(a)),o=[...S.filter((a)=>!n.includes(a)),"force-push","merge"],i=[...Go,...n.map((a)=>Ft[a])],s=[...S.filter((a)=>!n.includes(a)).map((a)=>Ft[a]),...Ho];return{allowedTools:i,disallowedTools:s,permissionMode:"acceptEdits",maxBudgetUsd:r.maxBudgetUsd,requireCleanExit:r.requireCleanExit,grantedActions:n,blockedActions:o}}import{mkdir as Jo}from"fs/promises";import Wo from"path";async function tr(e){await Jo(e.runDirectory,{recursive:!0});let r=Wo.join(e.runDirectory,"receipt.json");return await Bun.write(r,`${JSON.stringify(e.receipt,null,2)}
|
|
99
|
+
`),r}import{mkdir as Ko}from"fs/promises";import Xo from"path";async function K(e){let r=Bun.spawn({cmd:[...e.command],cwd:e.cwd,stdout:"pipe",stderr:"ignore"}),[t,n]=await Promise.all([r.exited,new Response(r.stdout).text()]);return{exitCode:t,stdout:n}}async function jt(e){let r=await e.runner({command:e.command,cwd:e.cwd}),t=r.stdout.trim();if(r.exitCode!==0||t.length===0)throw Error(e.failure);return t}async function nr(e){let r=e.runner??K,t=await jt({runner:r,command:["git","rev-parse","--show-toplevel"],cwd:e.targetDirectory,failure:"Target directory is not a git repository"}),n=await jt({runner:r,command:["git","rev-parse","HEAD"],cwd:t,failure:"Target repository has no current commit"});if(await Ko(Xo.dirname(e.worktreeDirectory),{recursive:!0}),(await r({command:["git","worktree","add",e.worktreeDirectory,"-b",e.branch,n],cwd:t})).exitCode!==0)throw Error("Could not create the clone worktree");return{repoDirectory:t,worktreeDirectory:e.worktreeDirectory,baseCommit:n,branch:e.branch}}async function or(e){let r=e.runner??K,[t,n,o]=await Promise.all([r({command:["git","status","--porcelain"],cwd:e.worktree.worktreeDirectory}),r({command:["git","diff","--name-only",`${e.worktree.baseCommit}..HEAD`],cwd:e.worktree.worktreeDirectory}),r({command:["git","log","--format=%H",`${e.worktree.baseCommit}..HEAD`],cwd:e.worktree.worktreeDirectory})]),i=t.exitCode===0?t.stdout.split(`
|
|
100
|
+
`).filter(Boolean).map((a)=>a.slice(3)):[],s=n.exitCode===0?n.stdout.split(`
|
|
101
|
+
`).filter(Boolean):[];return{filesChanged:[...new Set([...s,...i])],commits:o.exitCode===0?o.stdout.split(`
|
|
102
|
+
`).filter(Boolean):[],isClean:t.exitCode===0&&i.length===0}}async function ir(e){let r=e.runner??K,t=await r({command:["git","status","--porcelain"],cwd:e.worktree.worktreeDirectory});if(t.exitCode!==0)throw Error("Could not inspect the clone worktree");if(t.stdout.trim().length===0)return!1;let o=(await r({command:["git","add","--all"],cwd:e.worktree.worktreeDirectory})).exitCode===0?await r({command:["git","commit","-m","chore: apply shadowclone task"],cwd:e.worktree.worktreeDirectory}):null;if(o===null||o.exitCode!==0)throw Error("Could not commit the clone result");return!0}function Zo(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"").slice(0,40)||"task"}function Vo(e){return e.match(/^## /gm)?.length??0}async function Nt(e){let r=e.targetDirectory??process.cwd(),t=e.paths??h,{config:n,policy:o}=await v({configPath:e.configPath,managedConfigPath:e.managedConfigPath===void 0?t.managedConfigFile:e.managedConfigPath});if(!o.enabled||o.maxActionTier==="observe")throw Error("Managed policy does not allow headless clone runs");let i=await Le({cwd:r,enabled:n.sources["git-metadata"],readRemote:e.readRemote});if(k({origin:i.origin,cwd:r,patterns:o.blockedOrigins}))throw Error("Managed policy blocks this repository");let s=rr({configuredPolicy:n.repo[i.id]??null,approvedActions:e.approvedActions??[],managedActionTier:o.maxActionTier}),a=e.runner?null:await T({purpose:"dispatch",allowedEngines:o.allowedEngines}),d=e.runner??a?.runner;if(!d)throw Error("No authenticated agent engine is available");let y=e.runId??crypto.randomUUID(),u=`shadowclone/${Zo(e.task)}-${y.slice(0,8)}`,b=await nr({targetDirectory:r,worktreeDirectory:t.worktreeDirectory(y),branch:u,runner:e.commandRunner}),m=Mt.join(t.runDirectory(y),"profile.md"),E=await q({profileDirectory:t.profileDirectory,outputPath:m,origin:i.origin,targetRepo:Mt.basename(b.repoDirectory)}),P=e.startedAt??new Date().toISOString(),p=await d({prompt:[e.task,"","Work only in this worktree. Leave the finished change uncommitted.","Do not merge or force push under any circumstance."].join(`
|
|
103
|
+
`),cwd:b.worktreeDirectory,systemPromptFile:m,sessionId:y,allowedTools:s.allowedTools,disallowedTools:s.disallowedTools,permissionMode:s.permissionMode,maxBudgetUsd:s.maxBudgetUsd});if(!p.isError)await ir({worktree:b,runner:e.commandRunner});let c=await or({worktree:b,runner:e.commandRunner}),x={runId:y,task:e.task,repo:i.id,branch:u,engine:p.engine,model:null,sessionId:p.sessionId,transcriptPath:p.transcriptPath,startedAt:P,durationMs:p.durationMs,costUsd:p.costUsd,turns:p.turns,filesChanged:c.filesChanged,commits:c.commits,actionsTaken:c.commits.length>0?["commit"]:[],actionsBlockedByPolicy:s.blockedActions,permissionDenials:p.permissionDenials,profileRulesApplied:Vo(E)};if(await tr({runDirectory:t.runDirectory(y),receipt:x}),p.isError||s.requireCleanExit&&!c.isClean)throw Error("Clone run did not finish cleanly; review its receipt");return x}function Yo(e){let r=[],t=[];for(let o=0;o<e.length;o+=1){let i=e[o];if(i!=="--approve"){if(i)r.push(i);continue}let s=e[o+1],a=S.find((d)=>d===s);if(!a)throw Error("Run approval must name a supported action");t.push(a),o+=1}let n=r.join(" ").trim();if(n.length===0)throw Error("Run requires a task");return{task:n,approvedActions:[...new Set(t)]}}async function $t(e){let r=Yo(e),t=await Nt(r);console.log(`Clone run ${t.runId} finished. Review ~/.shadowclone/runs/${t.runId}/receipt.json.`)}var Qo="Usage: shadowclone <init|learn [--deep]|doctor|install|run <task>|forget --all>";function Lt(){console.log(Qo)}function ei(){console.log(sr.version)}async function ri(e){let[r,...t]=e;if(r==="--help"||r==="-h"||r==="help"){Lt();return}if(r==="--version"||r==="-v"){ei();return}if(r==="init"){await vt();return}if(r==="learn"&&(t.length===0||t.length===1&&t[0]==="--deep")){await _t({deep:t[0]==="--deep"});return}if(r==="doctor"&&t.length===0){await Sr();return}if(r==="install"&&t.length===0){await Et();return}if(r==="run"){await $t(t);return}if(r==="hook"&&t[0]==="session-end"){await Xe({input:await Bun.stdin.text()});return}if(r==="hook"&&t[0]==="session-start"){await He({input:await Bun.stdin.text()});return}if(r==="forget"&&t[0]==="--all"){await Ir();return}if(Lt(),r!==void 0)process.exitCode=1}await ri(Bun.argv.slice(2));
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shadowclone/cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"module": "src/cli/index.ts",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"packageManager": "bun@1.3.3",
|
|
7
|
+
"bin": {
|
|
8
|
+
"shadowclone": "bin/shadowclone.mjs"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "bun run src/cli/index.ts",
|
|
12
|
+
"cli": "bun run src/cli/index.ts",
|
|
13
|
+
"check": "bun run typecheck && bun run lint && bun test",
|
|
14
|
+
"test": "bun test",
|
|
15
|
+
"typecheck": "tsc --noEmit",
|
|
16
|
+
"lint": "biome lint --error-on-warnings --diagnostic-level=warn . && bun run scripts/conventions.ts",
|
|
17
|
+
"lint:fix": "biome lint --write .",
|
|
18
|
+
"build": "bun run scripts/build.ts",
|
|
19
|
+
"prepack": "bun run build"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@biomejs/biome": "2.5.12",
|
|
23
|
+
"@types/bun": "latest",
|
|
24
|
+
"typescript": "~5.9"
|
|
25
|
+
},
|
|
26
|
+
"description": "Learns how you work from the AI coding sessions already on your disk, then runs copies of you inside the agent you already use.",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/theonly1me/shadowclone.git"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://shadowclone.co",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/theonly1me/shadowclone/issues"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"claude-code",
|
|
38
|
+
"codex",
|
|
39
|
+
"cursor",
|
|
40
|
+
"agent",
|
|
41
|
+
"subagent",
|
|
42
|
+
"local-first"
|
|
43
|
+
],
|
|
44
|
+
"files": [
|
|
45
|
+
"bin",
|
|
46
|
+
"dist",
|
|
47
|
+
"README.md",
|
|
48
|
+
"LICENSE"
|
|
49
|
+
],
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"bun": "^1.4.2"
|
|
52
|
+
}
|
|
53
|
+
}
|