superclawd 0.1.0 β 0.1.2
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/README.md +71 -29
- package/dist/hook-helper.mjs +61 -4
- package/dist/index.js +154 -112
- package/dist/mcp-server.mjs +45 -37
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ One command. Your standards, every session.
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install -g superclawd
|
|
9
|
-
superclawd
|
|
9
|
+
superclawd login # connect this machine in your browser (one time)
|
|
10
10
|
superclawd # launch Claude Code, supercharged
|
|
11
11
|
```
|
|
12
12
|
|
|
@@ -17,7 +17,9 @@ superclawd # launch Claude Code, supercharged
|
|
|
17
17
|
- π§ **Your skills, on tap** β workspace skills and instructions load into every session and apply automatically when they're relevant.
|
|
18
18
|
- π€ **Your agents** β the specialized agents you've defined are available as Claude Code subagents.
|
|
19
19
|
- β‘ **Commands & workflows** β your reusable commands and processes, ready to run.
|
|
20
|
+
- π **Your team's MCP servers** β MCP servers your workspace curates (with their secrets) are injected into every session automatically; no per-machine `.mcp.json` to maintain.
|
|
20
21
|
- π **Alwaysβon rules** β your foundational standards stay in effect for the whole session, even after `/clear` or a compaction.
|
|
22
|
+
- π‘οΈ **Guardrails** β your workspace's enforced rules intercept risky tool calls _before_ they run (block, require approval, or warn) β applied to every session automatically, even under `--dangerously-skip-permissions`.
|
|
21
23
|
- π **Notifications** β get pinged the moment Claude needs your input or finishes a task, so you can step away and come back at the right time.
|
|
22
24
|
|
|
23
25
|
You manage all of it from your dashboard at **[superclawd.com](https://superclawd.com)** β update a skill once and every teammate's next session picks it up.
|
|
@@ -38,18 +40,15 @@ npm install -g superclawd
|
|
|
38
40
|
|
|
39
41
|
## Set up (one time)
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
Connect this machine to your account in the browser β no copyβpasting:
|
|
42
44
|
|
|
43
45
|
```bash
|
|
44
|
-
superclawd
|
|
46
|
+
superclawd login
|
|
45
47
|
```
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
This opens your browser. Approve the request under **Settings β Devices**, and the CLI receives a credential for this machine automatically. You only do this once per machine.
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
superclawd configure --show # show your current configuration
|
|
51
|
-
superclawd configure --reset # clear it and start over
|
|
52
|
-
```
|
|
51
|
+
Manage or revoke your connected machines any time under **Settings β Devices** in the dashboard. Reβrunning `superclawd login` rotates this machine's credential.
|
|
53
52
|
|
|
54
53
|
## Use it
|
|
55
54
|
|
|
@@ -63,17 +62,53 @@ That launches Claude Code with your workspace's skills, agents, commands, workfl
|
|
|
63
62
|
|
|
64
63
|
> Prefer plain Claude Code for a oneβoff? Just run `claude` directly. `superclawd` is the supercharged way in.
|
|
65
64
|
|
|
66
|
-
|
|
65
|
+
### Nonβinteractive (print) mode
|
|
67
66
|
|
|
68
|
-
|
|
67
|
+
Run a single prompt and print the result β handy for scripts or testing how your workspace config behaves:
|
|
69
68
|
|
|
70
69
|
```bash
|
|
71
|
-
superclawd
|
|
70
|
+
superclawd -p "review this diff and apply our code standards"
|
|
72
71
|
```
|
|
73
72
|
|
|
74
|
-
|
|
73
|
+
This boots the same plugin (skills/agents/teams/workflows/MCP) and runs the prompt via `claude -p`, then exits. It never prompts for a workspace β it resolves one in order: `--workspace <id>` flag β `SUPERCLAWD_MCP_WORKSPACE` env var β your stored default. In non-interactive mode (`-p`, `--pipelines`, or no TTY) with none of those set, the CLI prints a clear error and exits β it never prompts or hangs. Add `--no-capture` to skip writing learned memories for the run.
|
|
74
|
+
|
|
75
|
+
## Your default workspace
|
|
76
|
+
|
|
77
|
+
On more than one team or project? A connected machine works across **all** your workspaces. Your **default workspace** is the one future `superclawd` launches boot into, and you set it right from the startup picker.
|
|
78
|
+
|
|
79
|
+
When you run `superclawd` interactively, a workspace picker appears:
|
|
80
|
+
|
|
81
|
+
- **β/β** β highlight a workspace
|
|
82
|
+
- **Enter** β launch into the highlighted workspace for this session
|
|
83
|
+
- **`d`** β mark the highlighted workspace as your **default** (shown with a β
)
|
|
75
84
|
|
|
76
|
-
|
|
85
|
+
For a given run, the workspace is resolved in this order: `--workspace <id>` flag β `SUPERCLAWD_MCP_WORKSPACE` env var β your stored default β (interactive only) the startup picker.
|
|
86
|
+
|
|
87
|
+
By default, every `superclawd` shows the picker first and boots into whatever you highlight and Enter (just for that session; your default β the β
β stays put unless you press `d`). Prefer to skip the picker and boot straight into your stored default? Turn off **Choose workspace at startup** in `superclawd config`. If your stored default ever becomes inaccessible (you're removed from it, it's deleted, or its slug changes), the CLI clears it automatically and shows the picker on the next launch.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Memory
|
|
92
|
+
|
|
93
|
+
SuperClawd carries durable context across sessions through four memory tiers. The CLI participates in all of them:
|
|
94
|
+
|
|
95
|
+
| Tier | Scope | Source | How it's surfaced |
|
|
96
|
+
| ----------- | ----------- | ----------------------- | ------------------------------------------------------ |
|
|
97
|
+
| **Team** | `workspace` | Human-authored | Injected at SessionStart for everyone in the workspace |
|
|
98
|
+
| **Repo** | `repo` | AI-captured | Scoped to the current repo (git-remote fingerprint) |
|
|
99
|
+
| **Agent** | `agent` | AI-captured | Scoped to the active agent |
|
|
100
|
+
| **Private** | `private` | AI-captured or self-authored, per-member | Account-scoped β injected for the author only, across every workspace they belong to |
|
|
101
|
+
|
|
102
|
+
When a tier is enabled for your workspace, the CLI registers the memory MCP tools so Claude can curate it in-session:
|
|
103
|
+
|
|
104
|
+
- `record_memory` β capture a new memory
|
|
105
|
+
- `list_memories` β list existing memories
|
|
106
|
+
- `update_memory` β refine an existing memory
|
|
107
|
+
- `remove_memory` β delete a memory
|
|
108
|
+
- `search_memories` β hybrid recall (semantic + keyword) across the visible tiers; returns index hits (titles + ids)
|
|
109
|
+
- `get_memories` β fetch the full bodies for index ids on demand
|
|
110
|
+
|
|
111
|
+
AI-tier memories are served at SessionStart as a small **CORE** (full bodies) plus a compact **INDEX** (titles + ids) whose bodies are pulled on demand, plus mid-session auto-retrieve via the `UserPromptSubmit` / `PreToolUse:Read` hooks (disable with `SUPERCLAWD_AUTO_RETRIEVE=0`).
|
|
77
112
|
|
|
78
113
|
---
|
|
79
114
|
|
|
@@ -83,7 +118,10 @@ Run `superclawd config` for an interactive menu (β/β to move, **Space/Enter*
|
|
|
83
118
|
|
|
84
119
|
- **Notifications** β your default for new sessions: get pinged when Claude needs you or finishes.
|
|
85
120
|
- **Dangerously skip permissions** β launch Claude Code without approval prompts. Off by default; turn it on only if you understand the tradeβoff.
|
|
86
|
-
- **Choose workspace at startup** β show the workspace picker each time you run `superclawd
|
|
121
|
+
- **Choose workspace at startup** β show the workspace picker each time you run `superclawd`. Highlight with β/β and press **Enter** to boot into a workspace for that session (doesn't change your default), or press **`d`** to make the highlighted workspace your default (β
). On by default; turn it off to boot straight into your stored default workspace. A stored default that becomes inaccessible self-heals β the CLI clears it and shows the picker again.
|
|
122
|
+
- **Private Memory** β let the AI remember your personal preferences across all your sessions (private to you, never shared). On by default; turn it off and no private memories are captured or injected for you.
|
|
123
|
+
- **Experimental mode** β serve the **live draft** of any entity flagged experimental (skills **and** agents), instead of its stable release. Off by default; only entities explicitly marked experimental are affected (everything else still serves its stable release).
|
|
124
|
+
- **Keep Awake** β prevent your computer from sleeping while a session is running. Off by default.
|
|
87
125
|
|
|
88
126
|
### Notifications
|
|
89
127
|
|
|
@@ -97,15 +135,19 @@ Your default lives in `superclawd config`. Inside a session you can flip notific
|
|
|
97
135
|
|
|
98
136
|
Once Claude Code is running, these slash commands are always available:
|
|
99
137
|
|
|
100
|
-
| Command
|
|
101
|
-
|
|
|
102
|
-
| `/superclawd:notifications` | Toggle notifications for the current session
|
|
103
|
-
| `/superclawd:status`
|
|
104
|
-
| `/superclawd:audit-work`
|
|
105
|
-
| `/superclawd:ultrathink`
|
|
138
|
+
| Command | What it does |
|
|
139
|
+
| --------------------------- | ------------------------------------------------------- |
|
|
140
|
+
| `/superclawd:notifications` | Toggle notifications for the current session |
|
|
141
|
+
| `/superclawd:status` | Show service status, your workspace, and credit balance |
|
|
142
|
+
| `/superclawd:audit-work` | Check the work so far against your active standards |
|
|
143
|
+
| `/superclawd:ultrathink` | Apply maximum extended thinking to the task at hand |
|
|
144
|
+
|
|
145
|
+
> The plugin's slash commands are namespaced as `/superclawd:<command>` β the bare form (e.g. `/status`) is unavailable.
|
|
106
146
|
|
|
107
147
|
## Keep it up to date
|
|
108
148
|
|
|
149
|
+
`superclawd` checks for a newer version on launch and updates itself automatically before starting, so you're never on an outdated CLI. To update manually any time:
|
|
150
|
+
|
|
109
151
|
```bash
|
|
110
152
|
superclawd update
|
|
111
153
|
```
|
|
@@ -114,15 +156,15 @@ superclawd update
|
|
|
114
156
|
|
|
115
157
|
## Command reference
|
|
116
158
|
|
|
117
|
-
| Command
|
|
118
|
-
|
|
|
119
|
-
| `superclawd`
|
|
120
|
-
| `superclawd
|
|
121
|
-
| `superclawd
|
|
122
|
-
| `superclawd config`
|
|
123
|
-
| `superclawd update`
|
|
124
|
-
| `superclawd version`
|
|
125
|
-
| `superclawd help`
|
|
159
|
+
| Command | Description |
|
|
160
|
+
| ---------------------------------- | ------------------------------------------------------------------------------------- |
|
|
161
|
+
| `superclawd` | Launch Claude Code with your workspace loaded |
|
|
162
|
+
| `superclawd --resume [session-id]` | Resume a prior Claude Code conversation (alias `-r`; omit the id to pick from a list) |
|
|
163
|
+
| `superclawd login` | Connect this machine in your browser (one time per machine) |
|
|
164
|
+
| `superclawd config` | Toggle notifications + launch options |
|
|
165
|
+
| `superclawd update` | Update to the latest version |
|
|
166
|
+
| `superclawd version` | Print the installed version |
|
|
167
|
+
| `superclawd help` | Show help |
|
|
126
168
|
|
|
127
169
|
---
|
|
128
170
|
|
package/dist/hook-helper.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire as __cr } from 'module'; const require = __cr(import.meta.url);
|
|
2
|
-
import{readFileSync as m,writeFileSync as $,existsSync as F}from"node:fs";import{basename as Y}from"node:path";import{randomUUID as B}from"node:crypto";import{spawn as b}from"node:child_process";var R=`SUPERCLAWD \u2014 operating manual (always in effect):
|
|
2
|
+
import{readFileSync as y,writeFileSync as K,appendFileSync as Ue,existsSync as L,readdirSync as $e,statSync as X,unlinkSync as We}from"node:fs";import{basename as Ye}from"node:path";import{homedir as Je}from"node:os";import{pathToFileURL as je}from"node:url";var Q=["You maintain the durable memory for ONE repository. You review a FINISHED coding session and RECONCILE what it taught against the memories ALREADY SAVED (shown below with ids), keeping the set accurate, current, and non-redundant for the NEXT session. ","SAVE or UPDATE a fact when it would save a newcomer real time or prevent a wrong turn \u2014 for example: the build/test/run command (especially anything non-default, e.g. a subdirectory or required env flags); where key code or config lives when the layout is non-obvious (e.g. the app is in a subdirectory, or content is data-driven from one file); a setup step or arrangement needed to make things work (e.g. ESM-only deps must be mocked for tests to run); a correction to a reasonable wrong assumption (e.g. the repo uses npm despite a pnpm convention); or a non-obvious gotcha and its fix. A fact STILL qualifies even if it 'could be figured out by reading the files'. ","NEVER save: secrets or credentials; code dumps; transient state (a test that merely happened to pass or fail this run); one-off task details; or truly trivial facts (e.g. 'this is a React project', or that a file exists). ","NEVER bake VOLATILE SuperClawd identifiers into a fact: a workflow/skill/command/agent SLUG or id, or a tool-invocation recipe like 'call get_workflow with <slug>'. These break the moment the entity is renamed. Refer to an entity by its human NAME (e.g. the 'Code Review Process' workflow), or omit the identifier entirely. ","CRITICAL \u2014 the ALREADY-SAVED list is the ONLY knowledge future sessions have. If it is NONE, this repo has ZERO saved knowledge: nobody knows the build command, the package manager, or where the app lives \u2014 so the session's basic orientation facts (build/test command, package manager, where the app actually lives, key setup steps) are exactly what the first memories should record. Never call a fact 'already known' or 'standard' \u2014 judge only against the literal list. ","Emit OPERATIONS against the existing set: ",'\u2022 "create" \u2014 a genuinely NEW fact not represented by any existing memory. ','\u2022 "update" \u2014 the session REFINES, SHARPENS, or CORRECTS an existing memory: cite its id and rewrite its COMPLETE body to the now-correct version. Prefer update over create whenever an existing memory is about the same thing (the package manager, the app location, the build command, \u2026), even if the wording differs. A correction (e.g. the app moved from apps/web to apps/app) is an UPDATE to that memory, NOT a new one. When you correct a fact, also UPDATE any OTHER memory that still references the now-stale version. ',"\u2022 Omit any fact already captured accurately (no op). ","A memory tagged [LOCKED] is read-only: never update it; you may only create a complementary fact. ","Keep the set MINIMAL and CURRENT: never leave two memories about the same thing, and never leave an outdated fact beside its correction. ",'CRITICAL \u2014 within THIS single response, every fact must end up in EXACTLY ONE memory: never emit a "create" whose content is already covered by (or overlaps) a memory you keep or "update" in the same response. If the session adds a fact related to an existing memory, FOLD it into that memory with ONE "update" \u2014 do NOT also create a separate memory for the same fact. ',"This is a ONE-SHOT reconciliation of an already-finished session \u2014 do NOT continue the work, ask questions, or take any action. ",`Every op MUST include a "title": a distilled, human one-line SUMMARY of its body (<=120 chars) \u2014 a real summary, NOT the body's first words copied verbatim. `,'Respond with ONLY a strict JSON object {"ops":[ ... ],"reason":"<short overall why>"} and nothing else. Each op is {"op":"create","title":"<=120-char summary","body":"<self-contained 1-4 sentence note with concrete commands/paths + the gotcha & its fix>"} or {"op":"update","id":"<existing id>","title":"<=120-char summary","body":"<rewritten complete note>"}. If the session taught nothing durable, return {"ops":[],"reason":"..."}.'].join(""),ee=["You review a FINISHED coding session and RECONCILE what it taught against the memories ALREADY SAVED (shown below with ids), keeping each memory set accurate, current, and non-redundant for the NEXT session. You maintain TWO memory sets and route every op to the correct one: ",'\u2022 "repo" \u2014 durable knowledge about THIS repository, SHARED with the whole team: the build/test/run command (especially non-default ones, e.g. a subdirectory or required env flags); where key code/config lives when non-obvious; a required setup step (e.g. an ESM-only dep must be mocked for tests); a correction to a wrong assumption; or a non-obvious gotcha and its fix. A rule about the codebase, the product, or how the TEAM works is "repo" EVEN IF the user phrases it as "I want\u2026", "always\u2026", or "going forward\u2026". A fact STILL qualifies even if it could be figured out by reading the files. ',`\u2022 "private" \u2014 a preference PRIVATE to THIS individual user about how THEY themselves want to work or be treated: tone/verbosity, what to call them, their experience level, editor/workflow habits, how the assistant should behave toward them specifically. NEVER shared with the team. ONLY for preferences about the person themselves \u2014 never about the code or the team's process. `,'Route each fact by what it is ABOUT \u2014 the WORK/team (repo) or the PERSON (private). Do NOT require the user to label something "private"; infer it. When genuinely unsure whether a stated preference is a team convention or a private one, treat it as "repo" (shared): never leak a team rule into a private memory, and never put a genuinely private preference into the shared repo set. ',"NEVER save (either set): secrets or credentials; code dumps; transient state (a test that merely passed/failed this run); one-off task details; trivia. ","NEVER bake VOLATILE SuperClawd identifiers into a fact: a workflow/skill/command/agent SLUG or id, or a tool-invocation recipe like 'call get_workflow with <slug>'. These break the moment the entity is renamed. Refer to an entity by its human NAME, or omit the identifier entirely. ","CRITICAL \u2014 the ALREADY-SAVED lists are the ONLY knowledge future sessions have. If a set is NONE it has ZERO saved knowledge (nobody knows the build command, package manager, or where the app lives), so the session's basic orientation facts are exactly what its first memories should record. Judge 'already known' only against the literal lists, never against what is 'standard'. ","Emit OPERATIONS, each tagged with its scope: ",'\u2022 {"op":"create","scope":"repo"|"private","body":"<self-contained note>"} \u2014 a genuinely NEW fact/preference not represented by any existing memory in that set. ','\u2022 {"op":"update","scope":"repo","id":"<existing repo id>","body":"<rewritten COMPLETE body>"} \u2014 the session REFINES, SHARPENS, or CORRECTS an existing REPO memory: prefer update over create whenever an existing repo memory is about the same thing (the package manager, the app location, \u2026); a correction (e.g. the app moved from apps/web to apps/app) is an UPDATE, not a new memory; also update any other repo memory that still references a now-stale fact. UPDATE applies to the repo set ONLY. ','\u2022 For the PRIVATE set, emit ONLY "create" for a genuinely new preference \u2014 never update or restate one already saved (the user curates their private memories themselves); omit (no op) any private preference already captured. ',"\u2022 Omit any fact already captured accurately (no op). ","A memory tagged [LOCKED] is read-only: never update it; you may only create a complementary fact. ","Keep each set MINIMAL and CURRENT: never leave two memories about the same thing, and never leave an outdated fact beside its correction. ",'CRITICAL \u2014 within THIS single response, every fact must end up in EXACTLY ONE memory of its set: never emit a "create" whose content is already covered by (or overlaps) a memory you keep or "update" in the same response. If the session adds a fact related to an existing memory, FOLD it into that memory with ONE "update" \u2014 do NOT also create a separate memory for the same fact. ',"This is a ONE-SHOT reconciliation of an already-finished session \u2014 do NOT continue the work, ask questions, or take any action. ",`Every op MUST include a "title": a distilled, human one-line SUMMARY of its body (<=120 chars) \u2014 a real summary, NOT the body's first words copied verbatim. `,'Respond with ONLY a strict JSON object {"ops":[ ... ],"reason":"<short overall why>"} and nothing else. Each op is {"op":"create","scope":"repo"|"private","title":"<=120-char summary","body":"..."} or {"op":"update","scope":"repo","id":"<existing id>","title":"<=120-char summary","body":"..."}. If the session taught nothing durable, return {"ops":[],"reason":"..."}.'].join(""),te=["You maintain the durable lessons for a single reusable AGENT (a specialist invoked to do a job). You review a FINISHED run and RECONCILE what it learned about HOW TO PERFORM ITS ROLE against the lessons ALREADY SAVED (shown below with ids), keeping the set accurate, current, and non-redundant for the agent's NEXT invocation (in ANY repository). ","SAVE or UPDATE a lesson when it would make this agent do its job better next time: a standing preference or convention the user/team established for its work, a correction to how it should operate, or a recurring gotcha in its domain and how to handle it. Lessons are about the ROLE, not a codebase \u2014 they qualify regardless of which repo this run happened in. ","NEVER save: secrets or credentials; code dumps; one-off task details specific only to THIS single run; transient state; or truly trivial facts. ","NEVER record a lesson about TOOL AVAILABILITY \u2014 e.g. that a tool 'does not exist', 'is unavailable', 'is not in the toolset', or that a call failed because the tool wasn't callable. Which tools a run can call is an environment/config detail set per-invocation (this agent runs with a RESTRICTED toolset that omits tools the main session has), NOT a durable fact about the role: the tool may well exist and be available in another context, so such a lesson is misleading and goes stale. Treat a 'no such tool' / 'tool not found' error as a transient environment artifact, never a lesson. ","The ALREADY-SAVED list is the ONLY knowledge the agent carries between runs \u2014 treat anything NOT listed as new; never claim a lesson is 'already known' unless it literally appears. ","IMPORTANT \u2014 the agent runs under workspace SKILLS, instructions, and guardrails that you CANNOT see in this transcript. So: do NOT treat an asserted convention, rule, or guideline as fabricated or 'hallucinated' merely because the transcript shows no basis for it \u2014 it may come from the agent's hidden instructions. NEVER record a lesson that tells the agent to stop asserting conventions, to second-guess its guidance, or that is based on YOUR OWN judgment that the agent made a mistake this run. Capture a lesson ONLY when the run itself shows the USER establishing or correcting a standing preference, or a concrete reproducible domain gotcha the agent itself confirmed. ","Emit OPERATIONS against the existing set: ",'\u2022 "create" \u2014 a genuinely NEW lesson not represented by any existing one. ','\u2022 "update" \u2014 the run REFINES or CORRECTS an existing lesson: cite its id and rewrite its COMPLETE body. Prefer update over create whenever an existing lesson is about the same thing, even if the wording differs. ',"\u2022 Omit any lesson already captured accurately (no op). ","A lesson tagged [LOCKED] is read-only: never update it; you may only create a complementary lesson. ","Keep the set MINIMAL and CURRENT: never leave two lessons about the same thing, and never leave an outdated lesson beside its correction. ",'CRITICAL \u2014 within THIS single response, every lesson must end up exactly ONCE: never emit a "create" whose content is already covered by (or overlaps) a lesson you keep or "update" in the same response. If the run adds something related to an existing lesson, FOLD it in with ONE "update" \u2014 do NOT also create a separate lesson for the same thing. ',"This is a ONE-SHOT reconciliation of an already-finished run \u2014 do NOT continue the work, ask questions, or take any action. ",`Every op MUST include a "title": a distilled, human one-line SUMMARY of its lesson (<=120 chars) \u2014 a real summary, NOT the lesson's first words copied verbatim. `,'Respond with ONLY a strict JSON object {"ops":[ ... ],"reason":"<short overall why>"} and nothing else. Each op is {"op":"create","title":"<=120-char summary","body":"<self-contained instruction the agent can apply next time>"} or {"op":"update","id":"<existing id>","title":"<=120-char summary","body":"<rewritten complete lesson>"}. If the run taught nothing durable, return {"ops":[],"reason":"..."}.'].join("");var ne=["You decide whether a code change makes a remembered lesson STALE (no longer true).",'Respond ONLY strict JSON {"verdict":"stale"|"valid","reason":"<one line>","confidence":<0..1>}.'].join(" ");import{randomUUID as ge}from"node:crypto";import{spawn as x,execSync as D}from"node:child_process";var Re=`SUPERCLAWD \u2014 operating manual (always in effect):
|
|
3
3
|
- The ALWAYS rules below are ALREADY LOADED and active for this entire session.
|
|
4
4
|
Apply them directly to all work. Do NOT call any tool to fetch them \u2014 you
|
|
5
5
|
already have them in full.
|
|
@@ -8,8 +8,65 @@ import{readFileSync as m,writeFileSync as $,existsSync as F}from"node:fs";import
|
|
|
8
8
|
the \`get_directives\` tool with that file's skillId (a UUID) BEFORE acting,
|
|
9
9
|
and apply what it returns. Those skill files are pointers, not the rules.
|
|
10
10
|
- Only call \`get_directives\` with a UUID skillId taken from an activated skill
|
|
11
|
-
file. Never call it for the ALWAYS rules, and never pass a skill name
|
|
11
|
+
file. Never call it for the ALWAYS rules, and never pass a skill name.
|
|
12
|
+
- MEMORY: when the user states something durable \u2014 a fact, preference, or standing
|
|
13
|
+
rule, ESPECIALLY phrased as "remember\u2026", "from now on", "always", or "going
|
|
14
|
+
forward" \u2014 call \`record_memory\` RIGHT AWAY to persist it (silently; it picks the
|
|
15
|
+
scope on its own: the user's PRIVATE memory, the TEAM's, or the REPO's). Acknowledging
|
|
16
|
+
a durable instruction is NOT persisting it \u2014 you must actually record it. But do NOT
|
|
17
|
+
record one-off chit-chat or personal trivia that won't shape future work (e.g. "I love
|
|
18
|
+
candies") \u2014 just acknowledge those and move on. Still NEVER
|
|
19
|
+
ask where to store it, present a "how should I remember this?" menu, or offer to edit
|
|
20
|
+
an agent definition to save a preference \u2014 just record it silently. Be ACCURATE about
|
|
21
|
+
what you saved: only say something is saved/remembered AFTER you've actually called
|
|
22
|
+
\`record_memory\` for it; never claim a preference is "already" a saved convention, or
|
|
23
|
+
that it "will persist", unless you just recorded it (or confirmed it via
|
|
24
|
+
\`list_memories\`). A background pass also captures durable facts at session end as a
|
|
25
|
+
backstop \u2014 but never rely on it for an explicit request; record that now.`;function be(n){let e=Re,t=(n||"").trim();return t&&(e+=`
|
|
12
26
|
|
|
13
27
|
ALWAYS \u2014 foundational rules (already loaded & active for this whole session; apply directly, never fetch):
|
|
14
|
-
${
|
|
15
|
-
`)
|
|
28
|
+
${t}`),e}function U(n,e){return JSON.stringify({hookSpecificOutput:{hookEventName:n,additionalContext:be(e)}})}var re="superclawd",Ae="core",ke=`mcp__plugin_${re}_${Ae}__`;var _t=`${ke}*`,oe=`${re}:`;import{homedir as Oe}from"node:os";import{join as k}from"node:path";import{readFileSync as se,writeFileSync as Mt,mkdirSync as Ut,chmodSync as $t,existsSync as Wt}from"node:fs";var Yt=(process.env.SUPERCLAWD_BACKEND||"real").toLowerCase();var Jt=process.env.SUPERCLAWD_MCP_API_URL||"https://mcp-api.superclawd.com",jt=process.env.SUPERCLAWD_API_URL||"https://api.superclawd.com",ie=process.env.SUPERCLAWD_HOME||k(Oe(),".superclawd"),Pe=k(ie,"sessions"),Ce=k(ie,"config.json");function Ne(n){if(!n||typeof n!="object")return{};let e=n,t=e.credentials??{...typeof e.keyId=="string"?{keyId:e.keyId}:{},...typeof e.secret=="string"?{secret:e.secret}:{},...typeof e.workspace=="string"?{workspace:e.workspace}:{}},r=e.preferences??{...typeof e.skipPermissions=="boolean"?{skipPermissions:e.skipPermissions}:{},...typeof e.notifications=="boolean"?{notifications:e.notifications}:{},...typeof e.chooseWorkspaceAtStartup=="boolean"?{chooseWorkspaceAtStartup:e.chooseWorkspaceAtStartup}:{},...typeof e.experimental=="boolean"?{experimental:e.experimental}:{}},o={};return Object.keys(t).length&&(o.credentials=t),Object.keys(r).length&&(o.preferences=r),typeof e.machineId=="string"&&(o.machineId=e.machineId),o}function $(){try{return Ne(JSON.parse(se(Ce,"utf8")))}catch{return null}}function Te(){return $()?.preferences?.notifications===!0}function P(n){return k(Pe,n)}function _e(n){return k(n,"config.json")}function W(n){try{return JSON.parse(se(_e(n),"utf8"))}catch{return null}}function C(n){let e=n?W(P(n)):null;return e&&typeof e.notifications=="boolean"?e.notifications:Te()}var Ft=1e3*60*60*24*3;import{existsSync as Y}from"node:fs";import{homedir as Ie}from"node:os";import{spawn as Le,execSync as xe}from"node:child_process";var N="sonnet",De=2e4;function O(){let n=process.env.SUPERCLAWD_CLAUDE_BIN;if(n&&Y(n))return n;try{let t=process.platform==="win32"?"where claude":"command -v claude",r=xe(t,{encoding:"utf8"}).trim().split(`
|
|
29
|
+
`)[0];if(r&&Y(r))return r}catch{}let e=[`${Ie()}/.local/bin/claude`,"/opt/homebrew/bin/claude","/usr/local/bin/claude","/usr/bin/claude"];for(let t of e)if(Y(t))return t;return null}function ae(n){let e=n.indexOf("{"),t=n.lastIndexOf("}");return e>=0&&t>e?n.slice(e,t+1):n}function Me(n){try{let e=n;try{let r=JSON.parse(n);typeof r.result=="string"&&(e=r.result)}catch{}let t=JSON.parse(ae(e));return typeof t.verdict!="string"||!t.verdict?null:{verdict:t.verdict,reason:typeof t.reason=="string"?t.reason:"",confidence:typeof t.confidence=="number"?t.confidence:1}}catch{return null}}function ce(n){let{system:e,user:t}=n,r=n.model||N,o=n.timeoutMs??De,i=O();return i?new Promise(s=>{let a="",c=!1,u=f=>{c||(c=!0,s(f))},l;try{l=Le(i,["-p","--model",r,"--output-format","json","--strict-mcp-config","--setting-sources","user","--tools","","--append-system-prompt",e],{stdio:["pipe","pipe","ignore"],env:{...process.env,SUPERCLAWD_POLICY_JUDGE:"1"}})}catch{return u(null)}let p=setTimeout(()=>{try{l.kill("SIGKILL")}catch{}u(null)},o);l.stdout?.on("data",f=>a+=f),l.on("error",()=>{clearTimeout(p),u(null)}),l.on("close",()=>{if(clearTimeout(p),n.onRaw)try{n.onRaw(a)}catch{}u(a)});try{l.stdin?.write(t),l.stdin?.end()}catch{}}):Promise.resolve(null)}function J(n){return ce(n).then(e=>e?Me(e):null)}function j(n){return ce(n).then(e=>{if(!e)return null;try{let t=e;try{let r=JSON.parse(e);typeof r.result=="string"&&(t=r.result)}catch{}return JSON.parse(ae(t))}catch{return null}})}var m=process.env.SUPERCLAWD_MCP_API_URL||"https://mcp-api.superclawd.com",h=(process.env.SUPERCLAWD_BACKEND||"real").toLowerCase(),S=process.env.SUPERCLAWD_MCP_KEY||"",w=process.env.SUPERCLAWD_MCP_SECRET||"",R=process.env.SUPERCLAWD_MCP_WORKSPACE||"";function _(){let n={"content-type":"application/json","x-mcp-key-id":S,"x-mcp-secret":w,"x-workspace-slug":R,"x-request-id":ge()};return process.env.SUPERCLAWD_EXPERIMENTAL==="true"&&(n["x-experimental"]="true"),process.env.SUPERCLAWD_PRIVATE_MEMORY==="0"&&(n["x-private-memory"]="false"),n}async function M(n,e){let t=new AbortController,r=setTimeout(()=>t.abort(),e);try{return await fetch(n,{headers:_(),signal:t.signal})}finally{clearTimeout(r)}}async function Fe(n,e="SessionStart"){let t="";if(n)try{t=y(n,"utf8")}catch{}if(h!=="mock"&&S&&w&&R)try{let r=process.env.SUPERCLAWD_REPO_FINGERPRINT,o=`${m}/api/prompt/foundational`+(r?`?repoFingerprint=${encodeURIComponent(r)}`:""),i=await M(o,3e3);if(i.ok){let s=await i.json();typeof s.text=="string"&&(t=s.text)}}catch{}process.stdout.write(U(e,t))}async function Ge(){let n=()=>process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}})),e=()=>process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:"SuperClawd: out of credits. Top up at app.superclawd.com to load directives."}}));if(h==="mock"||!S||!w||!R)return void n();try{let t=await M(`${m}/api/prompt/balance`,2e3);if(!t.ok)return void n();let o=(await t.json()).content,s=(Array.isArray(o)?o.map(a=>a.text??"").join(""):"").match(/<current_balance>([\d.]+)<\/current_balance>/);return s&&Number(s[1])<=0?void e():void n()}catch{return void n()}}var Ke=process.env.SUPERCLAWD_POLICY_MODEL||N,Ve=Number(process.env.SUPERCLAWD_POLICY_TIMEOUT_MS||6e4),le=6e4,He=4,Be=["You are a code-policy judge. Decide whether a staged git diff VIOLATES a single team rule.","Reason briefly about control flow, indirection, and scope \u2014 judge whether the rule is actually","broken, not whether the diff merely touches a related area. If the rule does not apply to this",'diff, the verdict is "ok".','Respond with ONLY strict JSON: {"verdict":"violation"|"ok","reason":"<one line>","confidence":<0..1>}.',"Output nothing else."].join(" ");function qe(n,e){let t=(e.evaluation.examples??[]).map((r,o)=>`Example ${o+1} (verdict: ${r.verdict}):
|
|
30
|
+
${r.diff}`).join(`
|
|
31
|
+
|
|
32
|
+
`);return[`RULE: ${e.rule}`,`CRITERIA: ${e.evaluation.criteria}`,t?`
|
|
33
|
+
LABELED EXAMPLES:
|
|
34
|
+
${t}
|
|
35
|
+
`:"","STAGED DIFF:",n].join(`
|
|
36
|
+
`)}async function Xe(n,e){let t=await J({system:Be,user:qe(n,e),model:Ke,timeoutMs:Ve});return!t||t.verdict!=="violation"&&t.verdict!=="ok"?null:{verdict:t.verdict,reason:t.reason,confidence:t.confidence}}async function ze(n,e){let t=Math.max(1,Math.min(5,e.evaluation.voteN??1)),r=(await Promise.all(Array.from({length:t},()=>Xe(n,e)))).filter(a=>a!==null);if(r.length===0)return null;let o=r.filter(a=>a.verdict==="violation");if(o.length<=r.length/2)return null;let i=o.reduce((a,c)=>a+c.confidence,0)/o.length,s=e.action;return s==="deny"&&i<.66&&(s="ask"),{name:e.name,action:s,reason:o[0].reason}}var I=60,Ze=new Set([".git","node_modules","dist","build",".next","coverage",".turbo","vendor"]);function me(n,e){if(e.includes("\0"))return"";let t=e.split(`
|
|
37
|
+
`).map(r=>`+${r}`).join(`
|
|
38
|
+
`);return`diff --git a/${n} b/${n}
|
|
39
|
+
new file
|
|
40
|
+
--- /dev/null
|
|
41
|
+
+++ b/${n}
|
|
42
|
+
${t}`}function Qe(n){let e=n.match(/(?:^|&&|;)\s*cd\s+(?:"([^"]+)"|'([^']+)'|(\S+))/),t=e?e[1]||e[2]||e[3]:"";if(!t)return process.cwd();let r=t.startsWith("/")?t:`${process.cwd()}/${t}`;try{return L(r)?r:process.cwd()}catch{return process.cwd()}}function et(n){let e=[],t=(r,o)=>{if(e.length>=I)return;let i;try{i=$e(r)}catch{return}for(let s of i){if(e.length>=I)return;if(s.startsWith("."))continue;let a=`${r}/${s}`,c=o?`${o}/${s}`:s,u=!1;try{u=X(a).isDirectory()}catch{continue}u?Ze.has(s)||t(a,c):e.push(c)}};return t(n,""),e}function tt(n,e){let t=n.match(/git\s+add\s+([^&;|]+)/),r=[],o=!1;if(t)for(let a of t[1].trim().split(/\s+/))a==="-A"||a==="."||a==="--all"||a==="-a"?o=!0:a.startsWith("-")||r.push(a.replace(/^["']|["']$/g,""));else o=!0;let i=o||r.length===0?et(e):r,s=[];for(let a of i.slice(0,I))try{let c=me(a,y(`${e}/${a}`,"utf8"));c&&s.push(c)}catch{}return s.join(`
|
|
43
|
+
`)}function nt(n){let e=Qe(n),t=o=>{try{return D(o,{cwd:e,encoding:"utf8",maxBuffer:10*1024*1024})}catch{return""}};if(t("git rev-parse --is-inside-work-tree").trim()==="true"){let o=t("git diff --cached");if(o.trim())return o;let i=[],s=t("git diff");s.trim()&&i.push(s);let a=t("git ls-files --others --exclude-standard").split(`
|
|
44
|
+
`).map(c=>c.trim()).filter(Boolean).slice(0,I);for(let c of a)try{let u=me(c,y(`${e}/${c}`,"utf8"));u&&i.push(u)}catch{}return i.join("").trim()?i.join(`
|
|
45
|
+
`):""}return tt(n,e)}async function rt(n,e){let t=()=>process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}));if(process.env.SUPERCLAWD_POLICY_JUDGE==="1")return void t();let r="",o="";try{let c=JSON.parse(e);o=c.tool_name??"",r=c.tool_input?.command??""}catch{return void t()}if(!(o==="Bash"&&/\bgit\s+commit\b/.test(r)))return void t();let s=[];try{if(n){let c=JSON.parse(y(n,"utf8"));Array.isArray(c)&&(s=c)}}catch{return void t()}if(s.length===0||!O())return void t();let a=nt(r);if(!a.trim())return void t();a.length>le&&(a=a.slice(0,le));try{let c=(await he(s,He,v=>ze(a,v))).filter(v=>v!==null);if(c.length===0)return void t();let u={deny:3,ask:2,warn:1},l=c.reduce((v,g)=>u[g.action]>u[v.action]?g:v),p=`SuperClawd policy "${l.name}": ${l.reason||l.action}`,f=l.action==="deny"?"deny":l.action==="ask"?"ask":"allow",b=f!=="allow";process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:f,...b?{permissionDecisionReason:p}:{},...f==="allow"?{additionalContext:p}:{}}}))}catch{return void t()}}var ue=6e4,ot=4;async function he(n,e,t){let r=new Array(n.length).fill(null),o=0,i=Array.from({length:Math.max(1,Math.min(e,n.length))},async()=>{for(;;){let s=o++;if(s>=n.length)return;try{r[s]=await t(n[s],s)}catch{r[s]=null}}});return await Promise.all(i),r}async function st(){let n=process.env.SUPERCLAWD_REPO_FINGERPRINT||"";if(h==="mock"||!S||!w||!R||!n)return;let e=c=>{try{return D(c,{cwd:process.cwd(),encoding:"utf8",maxBuffer:10*1024*1024,stdio:["ignore","pipe","ignore"]})}catch{return""}},t=e("git rev-parse HEAD").trim();if(!t)return;let r=e("git show --format= -p HEAD");if(!r.trim())return;r.length>ue&&(r=r.slice(0,ue));let o=[];try{let c=`${m}/api/prompt/memories?scope=repo&scopeKey=${encodeURIComponent(n)}`,u=await M(c,4e3);if(!u.ok)return;o=(await u.json().catch(()=>({}))).memories??[]}catch{return}if(o.length===0)return;let i=await he(o,ot,async c=>{let u=c.body??c.title??"";if(!u.trim())return null;let l=await J({system:ne,user:`LESSON: ${u}
|
|
46
|
+
|
|
47
|
+
COMMIT DIFF:
|
|
48
|
+
${r}`});return l?{id:c.id,verdict:l.verdict,reason:l.reason}:null}),s=[],a={};for(let c of i)c&&c.verdict==="stale"&&(s.push(c.id),c.reason&&(a[c.id]=c.reason));try{await A(`${m}/api/prompt/commit-event`,4e3,{method:"POST",headers:_(),body:JSON.stringify({hash:t,repoFingerprint:n,staleIds:s,reasons:a})})}catch{}}var it=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function at(n){let e="";for(let t=0;t<n.length;t++){let r=n[t];r==="*"?n[t+1]==="*"?(e+=".*",t++,n[t+1]==="/"&&t++):e+="[^/]*":".+^${}()|[]\\".includes(r)?e+="\\"+r:r==="?"?e+=".":e+=r}return new RegExp("^"+e+"$")}function F(n,e){try{return n().test(e)}catch{return!1}}function ct(n,e,t){let r={deny:3,ask:2,warn:1,allow:0},o={decision:"allow",message:null};for(let i of n){if(!i||!i.match||!Array.isArray(i.match.tools)||!(i.match.tools.includes("*")||i.match.tools.includes(e)))continue;let s=!1;if(i.match.pathGlobs&&it.has(e)){let a=t.file_path??t.path??"";s=i.match.pathGlobs.some(c=>F(()=>at(c),a))}if(!s&&i.match.commandPatterns&&e==="Bash"){let a=t.command??"";s=i.match.commandPatterns.some(c=>F(()=>new RegExp(c),a))}if(!s&&i.match.contentPatterns){let a=`${t.content??""}
|
|
49
|
+
${t.new_string??""}`;s=i.match.contentPatterns.some(c=>F(()=>new RegExp(c),a))}s&&r[i.action]>r[o.decision]&&(o.decision=i.action,o.message=i.message)}return o}function lt(n){let{decision:e,message:t}=n;return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:e==="deny"?"deny":e==="ask"?"ask":"allow",...!!t&&e!=="allow"?{permissionDecisionReason:t}:{}}}}function ut(n,e){let t=[];try{if(n){let s=JSON.parse(y(n,"utf8"));Array.isArray(s)&&(t=s)}}catch{t=[]}let r="",o={};try{let s=JSON.parse(e);r=s.tool_name??"",o=s.tool_input??{}}catch{}let i=ct(t,r,o);process.stdout.write(JSON.stringify(lt(i)))}function E(){return new Promise(n=>{let e="";try{process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>e+=t),process.stdin.on("end",()=>n(e)),process.stdin.on("error",()=>n(e)),setTimeout(()=>n(e),1500)}catch{n(e)}})}function dt(n){if(!n||!L(n))return"";try{let e=y(n,"utf8").trim().split(`
|
|
50
|
+
`).filter(Boolean);for(let t=e.length-1;t>=0;t--){let r=JSON.parse(e[t]),o=r.message??r;if((o.role??r.type)==="assistant"){let s=o.content,a=Array.isArray(s)?s.map(c=>c.text??"").join(" "):typeof s=="string"?s:"";if(a.trim())return a.trim().slice(0,300)}}}catch{}return""}function pt(n,e,t){if(!(!C(e??"")||h==="mock"||!S||!w||!R))try{let r=process.argv[1];if(!r)return;let o=Buffer.from(t,"utf8").toString("base64");x(process.execPath,[r,"notify-bg",n,e??"",o],{detached:!0,stdio:"ignore"}).unref()}catch{}}async function ft(n,e,t){if(!C(e??"")||h==="mock"||!S||!w||!R)return;let r={};try{r=JSON.parse(t)}catch{}let o=Ye(process.cwd()),i=n==="attention",s=i?r.message||"Claude needs your input":dt(r.transcript_path)||"Session finished",a=`[${o}] ${s}`.slice(0,500),c=e||process.env.SUPERCLAWD_SESSION_ID||"",u={type:i?"attention_needed":"completion",summary:a,x_session_request_id:c},l=()=>A(`${m}/api/prompt/notify-checkpoint`,4e3,{method:"POST",headers:{..._(),"x-session-request-id":c},body:JSON.stringify(u)});try{let p=await l();p.status===404&&c&&(await A(`${m}/api/prompt/inspector`,4e3,{method:"POST",headers:{..._(),"x-session-request-id":c},body:JSON.stringify({ide:!0,platform:"claude-code",llm_model:"claude",x_session_request_id:c,notify_on_checkpoint:!0,enable_session_files:!1})}).catch(()=>{}),p=await l())}catch{}}async function A(n,e,t){let r=new AbortController,o=setTimeout(()=>r.abort(),e);try{return await fetch(n,{...t,signal:r.signal})}finally{clearTimeout(o)}}function de(n,e,t){try{x(n,e,{detached:!0,stdio:"ignore",env:t??process.env}).unref()}catch{}}function pe(n,e,t){if(process.platform==="darwin"){let r=t?`display notification m with title ${G(n)} subtitle ${G(t)} sound name "Glass"`:`display notification m with title ${G(n)} sound name "Glass"`;de("osascript",["-e","on run {m}","-e",r,"-e","end run",e]);return}process.platform!=="win32"&&de("notify-send",[n,e])}function G(n){return`"${n.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function gt(n,e,t){if(C(t??"")){if(n==="attention"){let r="";try{r=JSON.parse(e).message??""}catch{}if(r.includes("waiting for your input"))return;pe("Claude Code",r||"Claude needs you","Needs you");return}pe("Claude Code","Done \u2014 your turn.")}}function mt(n,e){let t=Number(process.env.SUPERCLAWD_REMIND_EVERY??8);if(!Number.isFinite(t)||t<=0||!n||!e)return;let r=0;try{r=parseInt(y(e,"utf8").trim(),10)||0}catch{}r+=1;try{K(e,String(r))}catch{}if(r%t!==0)return;let o="";try{o=y(n,"utf8")}catch{return}o&&process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:o}}))}var ht=5;async function yt(n,e){if(h==="mock"||!S||!w||!R)return;let t="",r=n==="prompt"?"UserPromptSubmit":"PreToolUse";try{let o=JSON.parse(e||"{}");if(n==="prompt")t=String(o.prompt??"");else{let i=o.tool_input??{};t=String(i.file_path??i.path??"")}}catch{return}if(t=t.trim(),!(t.length<4))try{let o=new URLSearchParams({query:t.slice(0,500)}),i=process.env.SUPERCLAWD_REPO_FINGERPRINT;i&&o.set("repoFingerprint",i);let s=await M(`${m}/api/prompt/memories/search?${o.toString()}`,n==="prompt"?2500:1500);if(!s.ok)return;let c=((await s.json()).memories??[]).slice(0,ht);if(c.length===0)return;let u=c.map(p=>` [${p.scope}] ${p.id} ${(p.title??"").trim()}`).join(`
|
|
51
|
+
`),l=n==="prompt"?"RELEVANT PROJECT MEMORIES for this request (titles only \u2014 fetch the full text with get_memories([id]) before relying on one):":"PROJECT MEMORIES that may relate to this file (titles only \u2014 fetch with get_memories([id]) if relevant):";process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:r,additionalContext:`${l}
|
|
52
|
+
${u}`}}))}catch{}}var Et=3e4,ye=process.env.SUPERCLAWD_CAPTURE_MODEL||N;function d(n,e){if(process.env.SUPERCLAWD_DEBUG==="1")try{let t=D("date -u +%Y-%m-%dT%H:%M:%SZ",{encoding:"utf8"}).trim();Ue(`${Je()}/.superclawd/capture-debug.log`,JSON.stringify({ts:t,stage:n,...e})+`
|
|
53
|
+
`)}catch{}}function V(n,e=Et){if(!n||!L(n))return"";let t;try{t=y(n,"utf8").trim().split(`
|
|
54
|
+
`).filter(Boolean)}catch{return""}let r=[];for(let i of t){let s;try{s=JSON.parse(i)}catch{continue}let a=s.message??s,c=a.role??s.type;if(c!=="user"&&c!=="assistant")continue;let u=a.content;if(typeof u=="string"){u.trim()&&r.push(`${c.toUpperCase()}: ${u.trim()}`);continue}if(Array.isArray(u)){for(let l of u)if(l?.type==="text"&&typeof l.text=="string"&&l.text.trim())r.push(`${c.toUpperCase()}: ${l.text.trim()}`);else if(l?.type==="tool_use"){let p=JSON.stringify(l.input??{}).slice(0,200);r.push(`[tool ${l.name}: ${p}]`)}else if(l?.type==="tool_result"){let p=l.content,f=typeof p=="string"?p:Array.isArray(p)?p.map(b=>b?.text??"").join(" "):"";f.trim()&&r.push(`[result: ${f.trim().slice(0,200)}]`)}}}let o=r.join(`
|
|
55
|
+
`);return o.length>e&&(o=`\u2026
|
|
56
|
+
`+o.slice(o.length-e)),o}function z(n){let e=$()?.credentials??{},t=n?W(P(n)):null;return{keyId:process.env.SUPERCLAWD_MCP_KEY||e.keyId||"",secret:process.env.SUPERCLAWD_MCP_SECRET||e.secret||"",workspace:process.env.SUPERCLAWD_MCP_WORKSPACE||t?.workspace||e.workspace||""}}function H(n){return{"content-type":"application/json","x-mcp-key-id":n.keyId,"x-mcp-secret":n.secret,"x-workspace-slug":n.workspace,"x-request-id":ge()}}async function B(n,e,t){try{let r=n==="private"?`${m}/api/prompt/memories?scope=private`:`${m}/api/prompt/memories?scope=${n}&scopeKey=${encodeURIComponent(e)}`,o=await A(r,4e3,{headers:H(t)});return o.ok?((await o.json().catch(()=>({}))).memories??[]).filter(s=>s.id).map(s=>({id:String(s.id),body:(s.body??s.title??"").trim(),locked:s.lockedAt!=null})):[]}catch{return[]}}function T(n,e){return n.length?n.map(t=>`[${t.id}]${t.locked?" [LOCKED]":""} ${t.body}`).join(`
|
|
57
|
+
`):e}async function Ee(n,e,t,r){for(let o of n){let i=t(o.scope);if(!i){d(`${r}-skip-unrouted`,{scope:o.scope??null});continue}let s=(o.body||"").trim();if(o.op==="create"){if(s.length<12)continue;try{let a={scope:i.scope,content:s,learnedAgainst:i.learnedAgainst};i.scopeKey!=null&&(a.scopeKey=i.scopeKey),o.title&&o.title.trim()&&(a.title=o.title.trim());let c=await A(`${m}/api/prompt/memories`,8e3,{method:"POST",headers:H(e),body:JSON.stringify(a)});d(`${r}-create`,{scope:i.scope,status:c.status,ok:c.ok})}catch(a){d(`${r}-create-error`,{error:String(a)})}}else if(o.op==="update"&&o.id){if(!i.allowUpdate){d(`${r}-skip-update`,{scope:i.scope,id:o.id});continue}if(s.length<12)continue;try{let a=await A(`${m}/api/prompt/memories/${encodeURIComponent(o.id)}`,8e3,{method:"PUT",headers:H(e),body:JSON.stringify({content:s,learnedAgainst:i.learnedAgainst,...o.title&&o.title.trim()?{title:o.title.trim()}:{}})});d(`${r}-update`,{id:o.id,status:a.status,ok:a.ok})}catch(a){d(`${r}-update-error`,{error:String(a)})}}}}var vt=18e4;function St(n){if(!n)return null;let e;try{e=P(n)}catch{return null}let t=`${e}/capture.lock`;try{return K(t,String(process.pid),{flag:"wx"}),t}catch{try{return Date.now()-X(t).mtimeMs<vt?null:(K(t,String(process.pid)),t)}catch{return null}}}function wt(n){if(n)try{We(n)}catch{}}function Rt(n,e){let t=process.env.SUPERCLAWD_REPO_FINGERPRINT||"";if(process.env.SUPERCLAWD_POLICY_JUDGE==="1")return;let r=z(e);if(h==="mock"||!r.keyId||!r.secret||!r.workspace||!t){d("detached-skip",{backend:h,hasKey:!!r.keyId,hasSecret:!!r.secret,hasWorkspace:!!r.workspace,hasFp:!!t,launchId:e});return}try{let o=process.argv[1];if(!o)return;let i=Buffer.from(n,"utf8").toString("base64"),s=x(process.execPath,[o,"capture-bg",i,e],{detached:!0,stdio:"ignore"});s.unref(),d("detached-spawn",{pid:s.pid??null,launchId:e})}catch(o){d("detached-error",{error:String(o)})}}async function bt(n,e){let t=process.env.SUPERCLAWD_REPO_FINGERPRINT||"",r=z(e);if(d("bg-start",{hasFp:!!t,hasKey:!!r.keyId,hasWorkspace:!!r.workspace}),h==="mock"||!r.keyId||!r.secret||!r.workspace||!t)return;if(!O()){d("bg-no-claude");return}let o={};try{o=JSON.parse(n)}catch{d("bg-bad-payload");return}let i=V(o.transcript_path);if(!i){d("bg-no-digest",{transcript:o.transcript_path??null});return}let s=(i.match(/\n?\[tool /g)||[]).length,a=Number(process.env.SUPERCLAWD_CAPTURE_MIN_TOOLS??3),c=process.env.SUPERCLAWD_PRIVATE_MEMORY==="1",u=Number(process.env.SUPERCLAWD_PRIVATE_MIN_DIGEST??280),l=Number.isFinite(a)&&a>0&&s<a,p=c&&i.length>=u;if(d("bg-digest",{digestLen:i.length,toolUses:s,minTools:a,privateOn:c}),l&&!p)return;let f=St(e);if(!f){d("bg-locked");return}let b=g=>{try{return g&&L(g)?X(g).mtimeMs:0}catch{return 0}},v=b(o.transcript_path);try{if(await fe(t,r,i,c),b(o.transcript_path)>v){let g=V(o.transcript_path),ve=(g.match(/\n?\[tool /g)||[]).length,Se=Number.isFinite(a)&&a>0&&ve<a,we=c&&g.length>=u,Z=!!g&&!(Se&&!we);d("bg-rerun",{digestLen:g.length,willRun:Z}),Z&&await fe(t,r,g,c)}}finally{wt(f)}}async function fe(n,e,t,r){let o=process.env.SUPERCLAWD_REPO_MEMORY!=="0",i=[];try{let l=D("git rev-parse HEAD",{cwd:process.cwd(),encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim();l&&(i=[l])}catch{}let s=await B("repo",n,e),a=r?await B("private","",e):[];d("bg-existing",{repo:s.length,private:a.length});let c=await j({system:r?ee:Q,model:ye,user:r?`ALREADY-SAVED REPO MEMORIES (reconcile against these; ids in brackets):
|
|
58
|
+
${T(s,"NONE \u2014 this repo has no saved memories yet; future sessions start with zero knowledge of it.")}
|
|
59
|
+
|
|
60
|
+
ALREADY-SAVED PRIVATE MEMORIES for this user (create only genuinely new ones; never update):
|
|
61
|
+
${T(a,"NONE \u2014 this user has no saved private preferences yet.")}
|
|
62
|
+
|
|
63
|
+
FINISHED SESSION (condensed transcript; most recent at the end) \u2014 emit scope-tagged ops to keep both sets accurate & minimal:
|
|
64
|
+
${t}`:`ALREADY-SAVED MEMORIES FOR THIS REPO (reconcile against these; ids in brackets):
|
|
65
|
+
${T(s,"NONE \u2014 this repo has no saved memories yet; future sessions start with zero knowledge of it.")}
|
|
66
|
+
|
|
67
|
+
FINISHED SESSION (condensed transcript; most recent at the end) \u2014 emit ops to keep the repo's memory accurate & minimal:
|
|
68
|
+
${t}`,timeoutMs:6e4,onRaw:process.env.SUPERCLAWD_DEBUG==="1"?l=>d("bg-judge-raw",{raw:l.slice(0,2e3)}):void 0}),u=Array.isArray(c?.ops)?c.ops:[];d("bg-ops",{ops:u.map(l=>`${l.scope??"repo"}:${l.op}`)}),await Ee(u,e,l=>r&&l==="private"?{scope:"private",scopeKey:null,learnedAgainst:[],allowUpdate:!1}:o?{scope:"repo",scopeKey:n,learnedAgainst:i,allowUpdate:!0}:null,"bg")}var q=oe;function At(n,e,t){if(process.env.SUPERCLAWD_POLICY_JUDGE==="1"||h==="mock")return;let r="";try{r=JSON.parse(n).agent_type??""}catch{return}if(d("agent-detached",{agentType:r}),!!r.startsWith(q))try{let o=process.argv[1];if(!o)return;let i=Buffer.from(n,"utf8").toString("base64"),s=x(process.execPath,[o,"capture-agent-bg",i,e,t],{detached:!0,stdio:"ignore"});s.unref(),d("agent-detached-spawn",{pid:s.pid??null})}catch(o){d("agent-detached-error",{error:String(o)})}}async function kt(n,e,t){let r;try{r=JSON.parse(n)}catch{return}let o=r.agent_type??"";if(!o.startsWith(q))return;let i=o.slice(q.length),s="";try{let f=JSON.parse(y(e,"utf8"));typeof f[i]=="string"&&(s=f[i])}catch{}if(d("agent-bg-start",{slug:i,hasAgentId:!!s}),!s)return;let a=z(t);if(h==="mock"||!a.keyId||!a.secret||!a.workspace)return;if(!O()){d("agent-bg-no-claude");return}let c=V(r.agent_transcript_path||r.transcript_path);if(d("agent-bg-digest",{digestLen:c.length}),c.length<200)return;let u=await B("agent",s,a);d("agent-bg-existing",{count:u.length});let l=await j({system:te,model:ye,user:`ALREADY-SAVED LESSONS FOR THIS AGENT (reconcile against these; ids in brackets):
|
|
69
|
+
${T(u,"NONE \u2014 this agent has no saved lessons yet; it starts each run with only its base prompt.")}
|
|
70
|
+
|
|
71
|
+
FINISHED AGENT RUN (condensed transcript; most recent at the end) \u2014 emit ops to keep the agent's lessons accurate & minimal:
|
|
72
|
+
${c}`,timeoutMs:6e4,onRaw:process.env.SUPERCLAWD_DEBUG==="1"?f=>d("agent-bg-judge-raw",{raw:f.slice(0,2e3)}):void 0}),p=Array.isArray(l?.ops)?l.ops:[];d("agent-bg-ops",{ops:p.map(f=>f.op)}),await Ee(p,a,()=>({scope:"agent",scopeKey:s,learnedAgainst:[],allowUpdate:!0}),"agent-bg")}async function Ot(){let n=process.argv[2];if(n==="gate")return await E(),Ge();if(n==="guardrails"){let e=process.argv[3],t=await E();return ut(e,t)}if(n==="policy"){let e=process.argv[3],t=await E();return rt(e,t)}if(n==="report-commit")return st();if(n==="notify"){let e=process.argv[3],t=process.argv[4],r=await E();pt(e,t,r);return}if(n==="notify-bg"){let e=process.argv[3],t=process.argv[4]||void 0,r=Buffer.from(process.argv[5]||"","base64").toString("utf8");return ft(e,t,r)}if(n==="notify-local"){let e=process.argv[3],t=process.argv[4]||void 0,r=await E();gt(e,r,t);return}if(n==="retrieve"){let e=process.argv[3]==="file"?"file":"prompt",t=await E();return yt(e,t)}if(n==="remind"){mt(process.argv[3],process.argv[4]);return}if(n==="capture-agent"){let e=process.argv[3]||"",t=process.argv[4]||"",r=await E();At(r,e,t);return}if(n==="capture-agent-bg"){let e=Buffer.from(process.argv[3]||"","base64").toString("utf8"),t=process.argv[4]||"",r=process.argv[5]||"";await kt(e,t,r);return}if(n==="capture"){let e=process.argv[3]||"",t=await E();Rt(t,e);return}if(n==="capture-bg"){let e=Buffer.from(process.argv[3]||"","base64").toString("utf8"),t=process.argv[4]||"";await bt(e,t);return}return await E(),Fe(process.argv[3],process.argv[4]||"SessionStart")}var Pt=new Set(["gate","guardrails","policy","report-commit","notify","notify-bg","notify-local","remind","retrieve","capture","capture-bg","capture-agent","capture-agent-bg"]),Ct=!!process.argv[1]&&import.meta.url===je(process.argv[1]).href;Ct&&Ot().catch(()=>{let n=process.argv[2];Pt.has(n)||process.stdout.write(U(process.argv[4]||"SessionStart",""))});export{ct as evaluate,lt as guardrailDecision,yt as retrieve};
|