shraga 0.0.3 → 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 +82 -27
- package/defaults/agents/summarizer.md +16 -0
- package/defaults/agents/trace-extractor.md +84 -0
- package/defaults/bin/claude +45 -0
- package/defaults/bin/claude-revive +17 -0
- package/defaults/extensions/README.md +70 -0
- package/defaults/extensions/selftest.ext.ts +43 -0
- package/defaults/extensions/stripe-webhook.ext.ts +58 -0
- package/defaults/gmail-triage-prompt.md +42 -0
- package/defaults/scripts/README +4 -0
- package/defaults/scripts/agent-once.ts +67 -0
- package/defaults/scripts/backfill-slack-usernames.ts +82 -0
- package/defaults/scripts/notifier-throttle.ts +44 -0
- package/defaults/scripts/summarize-conversations.ts +5 -0
- package/defaults/shraga.config.ts +29 -0
- package/defaults/skills/add-skill.md +14 -0
- package/defaults/skills/artifacts.md +116 -0
- package/defaults/skills/code-review.md +26 -0
- package/defaults/skills/communications.md +54 -0
- package/defaults/skills/context-audit.md +87 -0
- package/defaults/skills/debug.md +10 -0
- package/defaults/skills/garden.md +179 -0
- package/defaults/skills/github-contributor.md +35 -0
- package/defaults/skills/identity.md +30 -0
- package/defaults/skills/mcp-server.md +62 -0
- package/defaults/skills/mcps-sync.md +105 -0
- package/defaults/skills/plan.md +9 -0
- package/defaults/skills/platform.md +177 -0
- package/defaults/skills/reconcile.md +239 -0
- package/defaults/skills/scheduler.md +192 -0
- package/defaults/skills/self-aware.md +136 -0
- package/defaults/skills/shraga-know.md +333 -0
- package/defaults/skills/stripe.md +55 -0
- package/defaults/skills/write-tests.md +10 -0
- package/defaults/skills-defaults.json +1 -0
- package/defaults/system-prompt.md +46 -0
- package/defaults/workspace/context.md +28 -0
- package/defaults/workspace.md +50 -0
- package/defaults/zdotdir/.gitignore +8 -0
- package/defaults/zdotdir/.zlogin +3 -0
- package/defaults/zdotdir/.zprofile +1 -0
- package/defaults/zdotdir/.zshenv +4 -0
- package/defaults/zdotdir/.zshrc +3 -0
- package/dist/client/assets/index-BoHttkMt.js +1940 -0
- package/dist/client/assets/index-DdibEb2O.css +10 -0
- package/dist/client/index.html +22 -0
- package/package.json +59 -14
- package/src/cli.ts +71 -46
- package/src/client/App.tsx +510 -0
- package/src/client/components/ArtifactCard.tsx +26 -0
- package/src/client/components/ArtifactPanel.tsx +138 -0
- package/src/client/components/AuthedImage.tsx +85 -0
- package/src/client/components/AutocompleteTextarea.tsx +149 -0
- package/src/client/components/ChatView.tsx +866 -0
- package/src/client/components/CliAuthConsent.tsx +98 -0
- package/src/client/components/ConfigPanel.tsx +328 -0
- package/src/client/components/ConversationHeader.tsx +156 -0
- package/src/client/components/ConversationPane.tsx +277 -0
- package/src/client/components/LoginPage.tsx +81 -0
- package/src/client/components/MachineStats.tsx +77 -0
- package/src/client/components/McpManager.tsx +209 -0
- package/src/client/components/MessageInput.tsx +263 -0
- package/src/client/components/OAuthConsent.tsx +103 -0
- package/src/client/components/SchedulesManager.tsx +99 -0
- package/src/client/components/Sidebar.tsx +235 -0
- package/src/client/components/SkillsManager.tsx +280 -0
- package/src/client/components/SmartChart.tsx +167 -0
- package/src/client/components/Toast.tsx +54 -0
- package/src/client/components/WorkspaceTree.tsx +313 -0
- package/src/client/components/ZoomableImage.tsx +123 -0
- package/src/client/components/artifact-presets.ts +10 -0
- package/src/client/components/schedules/ScheduleEditor.tsx +264 -0
- package/src/client/components/schedules/ScheduleList.tsx +271 -0
- package/src/client/components/ui/accordion.tsx +50 -0
- package/src/client/components/ui/button.tsx +43 -0
- package/src/client/components/ui/dialog.tsx +82 -0
- package/src/client/components/ui/input.tsx +19 -0
- package/src/client/components/ui/scroll-area.tsx +39 -0
- package/src/client/components/ui/textarea.tsx +18 -0
- package/src/client/globals.css +51 -0
- package/src/client/hooks/useAgentSocket.ts +79 -0
- package/src/client/hooks/useArtifacts.ts +89 -0
- package/src/client/hooks/useAuth.ts +127 -0
- package/src/client/hooks/useConversation.ts +412 -0
- package/src/client/hooks/useDarkMode.ts +57 -0
- package/src/client/hooks/useIsMobile.ts +23 -0
- package/src/client/hooks/usePush.ts +127 -0
- package/src/client/hooks/useSchedules.ts +73 -0
- package/src/client/hooks/useUnread.ts +238 -0
- package/src/client/lib/desktopAttention.ts +75 -0
- package/src/client/lib/firebase.ts +32 -0
- package/src/client/lib/googleAuthNative.ts +94 -0
- package/src/client/lib/native.ts +43 -0
- package/src/client/lib/schedule-types.ts +34 -0
- package/src/client/lib/sessionApi.ts +58 -0
- package/src/client/lib/slots.tsx +79 -0
- package/src/client/lib/storage.ts +39 -0
- package/src/client/lib/utils.ts +26 -0
- package/src/client/lib/workspaceContext.tsx +54 -0
- package/src/client/lib/ws.ts +203 -0
- package/src/client/main.tsx +14 -0
- package/src/mcp-stdio-bridge.ts +70 -0
- package/src/scripts/summarize-conversations.ts +5 -0
- package/src/scripts/typecheck.ts +43 -0
- package/src/server/agents.ts +54 -0
- package/src/server/api-keys.ts +63 -0
- package/src/server/artifacts/artifacts.export.ts +85 -0
- package/src/server/artifacts/artifacts.handler.ts +93 -0
- package/src/server/artifacts/artifacts.routes.ts +43 -0
- package/src/server/artifacts/artifacts.service.ts +100 -0
- package/src/server/artifacts/artifacts.types.ts +31 -0
- package/src/server/auth.ts +262 -0
- package/src/server/claude.ts +394 -0
- package/src/server/commands.ts +21 -0
- package/src/server/contacts.ts +177 -0
- package/src/server/conversation-summarizer.ts +204 -0
- package/src/server/data-sync.ts +664 -0
- package/src/server/directives.ts +91 -0
- package/src/server/engine/claude-code.ts +514 -0
- package/src/server/engine/index.ts +41 -0
- package/src/server/engine/registry.ts +21 -0
- package/src/server/engine/shared.ts +47 -0
- package/src/server/engine/types.ts +48 -0
- package/src/server/env-resolve.ts +71 -0
- package/src/server/env-sanitize.ts +9 -0
- package/src/server/events/bus.ts +29 -0
- package/src/server/events/dispatcher.ts +48 -0
- package/src/server/events/routes.ts +19 -0
- package/src/server/events/types.ts +9 -0
- package/src/server/extensions.ts +101 -0
- package/src/server/features.ts +109 -0
- package/src/server/file-inject.ts +45 -0
- package/src/server/hooks.ts +142 -0
- package/src/server/idempotency.ts +25 -0
- package/src/server/index.ts +1715 -0
- package/src/server/integrity-audit.ts +132 -0
- package/src/server/mcp-catalog.ts +70 -0
- package/src/server/mcp-oauth.ts +198 -0
- package/src/server/mcp-progress.ts +45 -0
- package/src/server/mcp-server.ts +456 -0
- package/src/server/mcp-sidecar.ts +87 -0
- package/src/server/mcp.ts +291 -0
- package/src/server/model-aliases.ts +76 -0
- package/src/server/paths.ts +24 -0
- package/src/server/polls.ts +175 -0
- package/src/server/push/apns.ts +113 -0
- package/src/server/push/fcm.ts +108 -0
- package/src/server/push/push.ts +66 -0
- package/src/server/push/store.ts +84 -0
- package/src/server/push/triggers.ts +99 -0
- package/src/server/scheduler/builtins.ts +157 -0
- package/src/server/scheduler/engine.ts +432 -0
- package/src/server/scheduler/index.ts +4 -0
- package/src/server/scheduler/runner.ts +334 -0
- package/src/server/scheduler/storage.ts +98 -0
- package/src/server/scheduler/timing.ts +70 -0
- package/src/server/scheduler/types.ts +62 -0
- package/src/server/sdk-utils.ts +45 -0
- package/src/server/seed.ts +174 -0
- package/src/server/session-bus.ts +18 -0
- package/src/server/sessions.ts +559 -0
- package/src/server/shraga-config.ts +167 -0
- package/src/server/skills.ts +372 -0
- package/src/server/slack/api.ts +37 -0
- package/src/server/slack/bot.ts +391 -0
- package/src/server/slack/context-cache.ts +42 -0
- package/src/server/slack/feature.ts +59 -0
- package/src/server/slack/mention-rewrite.ts +59 -0
- package/src/server/slack/oauth.ts +102 -0
- package/src/server/slack/questions.ts +112 -0
- package/src/server/slack/sessions.ts +139 -0
- package/src/server/stats.ts +106 -0
- package/src/server/summarize.ts +11 -0
- package/src/server/turn-context.ts +61 -0
- package/src/server/unclaw-config.ts +19 -0
- package/src/server/unread.ts +79 -0
- package/src/server/user-context.ts +33 -0
- package/src/server/vendor-sync.ts +52 -0
- package/src/server/voice-provider.ts +74 -0
- package/src/server/workspace.ts +249 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Create/manage scheduled jobs (cron/interval/once) and event-triggered automations via the internal API — incl. the event bus, built-in event sources (schedule.finished), per-trigger throttling, and the failure-notifier builtin.
|
|
3
|
+
triggers:
|
|
4
|
+
- schedule a job
|
|
5
|
+
- cron job
|
|
6
|
+
- recurring task
|
|
7
|
+
- event trigger
|
|
8
|
+
- react to an event
|
|
9
|
+
- event-based job
|
|
10
|
+
- emit an event
|
|
11
|
+
- event bus
|
|
12
|
+
- schedule.finished
|
|
13
|
+
- notify on failure
|
|
14
|
+
- failure notifier
|
|
15
|
+
- throttle
|
|
16
|
+
- how do i schedule
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
You can manage scheduled jobs and event-triggered automations via the internal REST API. Requests are authenticated as the current user (your uid/email are injected into env automatically).
|
|
20
|
+
|
|
21
|
+
## Auth Header
|
|
22
|
+
|
|
23
|
+
All requests need: `-H "x-internal-token: $INTERNAL_API_TOKEN"`
|
|
24
|
+
|
|
25
|
+
This authenticates you as the current session user — you'll have the same permissions they have (can only edit/delete your own schedules, system schedules are read-only).
|
|
26
|
+
|
|
27
|
+
Base URL: `http://localhost:$PORT`
|
|
28
|
+
|
|
29
|
+
## Endpoints
|
|
30
|
+
|
|
31
|
+
### List all schedules
|
|
32
|
+
```bash
|
|
33
|
+
curl -s -H "x-internal-token: $INTERNAL_API_TOKEN" http://localhost:$PORT/api/schedules | jq .
|
|
34
|
+
```
|
|
35
|
+
Returns `{ schedules: [...], runningIds: [...] }`.
|
|
36
|
+
|
|
37
|
+
### Get one schedule
|
|
38
|
+
```bash
|
|
39
|
+
curl -s -H "x-internal-token: $INTERNAL_API_TOKEN" http://localhost:$PORT/api/schedules/{id} | jq .
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Create a schedule
|
|
43
|
+
```bash
|
|
44
|
+
curl -s -X POST -H "Content-Type: application/json" -H "x-internal-token: $INTERNAL_API_TOKEN" \
|
|
45
|
+
http://localhost:$PORT/api/schedules \
|
|
46
|
+
-d '{
|
|
47
|
+
"name": "My task",
|
|
48
|
+
"enabled": true,
|
|
49
|
+
"trigger": { "kind": "cron", "expr": "0 9 * * 1-5", "tz": "Asia/Jerusalem" },
|
|
50
|
+
"task": { "kind": "prompt", "prompt": "Do the thing" }
|
|
51
|
+
}' | jq .
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Create an event-triggered schedule
|
|
55
|
+
|
|
56
|
+
Instead of a time, an `event` trigger fires when a matching external event arrives (a webhook, a signal from another system, an internal watcher). The event payload is injected into the run.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
curl -s -X POST -H "Content-Type: application/json" -H "x-internal-token: $INTERNAL_API_TOKEN" \
|
|
60
|
+
http://localhost:$PORT/api/schedules \
|
|
61
|
+
-d '{
|
|
62
|
+
"name": "Handle paid invoice",
|
|
63
|
+
"enabled": true,
|
|
64
|
+
"trigger": { "kind": "event", "source": "stripe", "match": { "type": "invoice.paid" } },
|
|
65
|
+
"task": { "kind": "prompt", "prompt": "An invoice was paid — thank the customer and log it." }
|
|
66
|
+
}' | jq .
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- `source` (required) routes the event. `match` (optional) is an AND-filter of payload **dot-paths** → expected values (case-insensitive, e.g. `{"data.amount": "42"}`). No `match` → fires on any event with that source.
|
|
70
|
+
- The event reaches the agent: for `prompt` tasks it's appended as a framed `Event data` JSON block; for `job` tasks it's passed as the `SHRAGA_EVENT` env var (never into the command string). `bash` tasks run their command as-is (no payload injected) — use a `prompt` or `job` task when the run needs the event data.
|
|
71
|
+
- "Run now" works on event schedules too (fires without event data — handy for testing).
|
|
72
|
+
|
|
73
|
+
### Create a one-time future schedule
|
|
74
|
+
```bash
|
|
75
|
+
curl -s -X POST -H "Content-Type: application/json" -H "x-internal-token: $INTERNAL_API_TOKEN" \
|
|
76
|
+
http://localhost:$PORT/api/schedules \
|
|
77
|
+
-d '{
|
|
78
|
+
"name": "Send weekly report",
|
|
79
|
+
"enabled": true,
|
|
80
|
+
"trigger": { "kind": "once", "at": 1748000000000 },
|
|
81
|
+
"task": { "kind": "prompt", "prompt": "Compile and send the weekly report" }
|
|
82
|
+
}' | jq .
|
|
83
|
+
```
|
|
84
|
+
Compute `at` as epoch milliseconds (e.g. `Date.now() + 3600000` for 1 hour from now). Must be in the future. Once-schedules auto-delete after completing.
|
|
85
|
+
|
|
86
|
+
### Update a schedule
|
|
87
|
+
```bash
|
|
88
|
+
curl -s -X PUT -H "Content-Type: application/json" -H "x-internal-token: $INTERNAL_API_TOKEN" \
|
|
89
|
+
http://localhost:$PORT/api/schedules/{id} \
|
|
90
|
+
-d '{ "name": "New name", "trigger": { "kind": "cron", "expr": "0 8 * * *", "tz": "UTC" } }' | jq .
|
|
91
|
+
```
|
|
92
|
+
Partial update — only include fields to change. System schedules cannot be edited.
|
|
93
|
+
|
|
94
|
+
### Delete a schedule
|
|
95
|
+
```bash
|
|
96
|
+
curl -s -X DELETE -H "x-internal-token: $INTERNAL_API_TOKEN" http://localhost:$PORT/api/schedules/{id} | jq .
|
|
97
|
+
```
|
|
98
|
+
System schedules cannot be deleted.
|
|
99
|
+
|
|
100
|
+
### Enable/disable a schedule
|
|
101
|
+
```bash
|
|
102
|
+
curl -s -X POST -H "Content-Type: application/json" -H "x-internal-token: $INTERNAL_API_TOKEN" \
|
|
103
|
+
http://localhost:$PORT/api/schedules/{id}/toggle \
|
|
104
|
+
-d '{ "enabled": true }' | jq .
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Run a schedule now
|
|
108
|
+
```bash
|
|
109
|
+
curl -s -X POST -H "Content-Type: application/json" -H "x-internal-token: $INTERNAL_API_TOKEN" \
|
|
110
|
+
http://localhost:$PORT/api/schedules/{id}/run \
|
|
111
|
+
-d '{ "override": "optional override prompt" }' | jq .
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Cancel a running schedule
|
|
115
|
+
```bash
|
|
116
|
+
curl -s -X POST -H "x-internal-token: $INTERNAL_API_TOKEN" http://localhost:$PORT/api/schedules/{id}/cancel | jq .
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Get run history for a schedule
|
|
120
|
+
```bash
|
|
121
|
+
curl -s -H "x-internal-token: $INTERNAL_API_TOKEN" http://localhost:$PORT/api/schedules/{id}/runs | jq .
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Schedule Shape
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
Trigger:
|
|
128
|
+
{ kind: "once", at: <epoch_ms> }
|
|
129
|
+
{ kind: "interval", everyMs: <ms> } // min 1000
|
|
130
|
+
{ kind: "cron", expr: "<cron>", tz: "<IANA_tz>" }
|
|
131
|
+
{ kind: "event", source: "<name>", match?: { "<dot.path>": "<value>" } }
|
|
132
|
+
|
|
133
|
+
Task:
|
|
134
|
+
{ kind: "prompt", prompt: "<text>" }
|
|
135
|
+
{ kind: "bash", command: "<cmd>" }
|
|
136
|
+
|
|
137
|
+
Schedule: { id, name, enabled, trigger, task, scope, createdBy, nextRun?, lastRun?, runCount }
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Emitting events (to fire event-triggered schedules)
|
|
141
|
+
|
|
142
|
+
Any caller that can present shraga auth can push an event onto the bus:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
curl -s -X POST -H "Content-Type: application/json" -H "x-internal-token: $INTERNAL_API_TOKEN" \
|
|
146
|
+
-H "X-Event-Id: optional-dedupe-key" \
|
|
147
|
+
http://localhost:$PORT/api/events/stripe \
|
|
148
|
+
-d '{ "type": "invoice.paid", "data": { "amount": 42 } }' | jq .
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The `:source` path segment is the event source; the JSON body is the payload. `X-Event-Id` (optional) dedupes retried deliveries for ~5 min.
|
|
152
|
+
|
|
153
|
+
For **vendor webhooks** that can't send shraga auth (Stripe, GitHub, …), add a data-side extension (`data/extensions/<name>.ext.ts`) that verifies the vendor's own signature, then calls `ctx.emitEvent(source, payload, { id })`. See `defaults/extensions/README.md` and the `stripe-webhook.ext.ts` example.
|
|
154
|
+
|
|
155
|
+
## Built-in event sources
|
|
156
|
+
|
|
157
|
+
The system emits these onto the bus automatically — use them as the `source` of an event trigger to react to the agent's own lifecycle:
|
|
158
|
+
|
|
159
|
+
- **`schedule.finished`** — fired when any time/manual schedule run completes. Payload: `{ scheduleId, name, status, sessionId, error? }`. `status` is `ok` | `error` | `aborted`. Chain automations off it, e.g.:
|
|
160
|
+
```json
|
|
161
|
+
{ "trigger": { "kind": "event", "source": "schedule.finished", "match": { "status": "error" } },
|
|
162
|
+
"task": { "kind": "prompt", "prompt": "A scheduled run failed — investigate and post a summary." } }
|
|
163
|
+
```
|
|
164
|
+
Runs that were *themselves* event-triggered do NOT emit `schedule.finished` — this prevents feedback loops, so you can't chain `schedule.finished` → event run → `schedule.finished` infinitely.
|
|
165
|
+
|
|
166
|
+
## Throttling event triggers
|
|
167
|
+
|
|
168
|
+
Event triggers accept an optional `throttle` that suppresses duplicate fires **before** a run is spawned:
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{ "trigger": { "kind": "event", "source": "schedule.finished", "match": { "status": "error" },
|
|
172
|
+
"throttle": { "byFields": ["name", "error"], "windowSec": 21600 } } }
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The dedup key is built from the named payload fields (dot-paths), string-normalized (lowercased, digits→`#`, whitespace-collapsed) so values differing only by timestamps/ids collapse together. A fire is dropped if an identical key fired within `windowSec`. State lives in `data/state/trigger-throttle.json` and self-prunes. Empty `byFields` throttles on the source alone. For throttling inside a `job`/`bash` task (no trigger), use the `data/scripts/notifier-throttle.ts` helper instead.
|
|
176
|
+
|
|
177
|
+
## Failure notification (built-in)
|
|
178
|
+
|
|
179
|
+
`builtin-failure-notifier` is a shipped, **disabled-by-default** schedule that reacts to `schedule.finished` / `status:error`, triages the error (credential-expiry / rate-limit / data-issue / generic), and DMs the deployment owner — throttled to one alert per job+error per 6h. To use it:
|
|
180
|
+
|
|
181
|
+
1. Enable it (toggle the schedule).
|
|
182
|
+
2. Optionally set `SHRAGA_ALERT_SLACK_EMAIL` (the legacy `UNCLAW_ALERT_SLACK_EMAIL` is still honoured) (else it falls back to the first `data/whitelist.json` entry).
|
|
183
|
+
3. Optionally edit its `task.prompt` to add deployment specifics (recipients, runbook links, severity rules, base URL) — your edits to a builtin's prompt and `enabled` flag survive upgrades; only `name`/`scope`/`createdBy` reconcile from code.
|
|
184
|
+
|
|
185
|
+
## Notes
|
|
186
|
+
|
|
187
|
+
- Event triggers never fire on a timer — they have no `nextRun` and wait for the bus. Per-trigger fires serialize (queue cap 5).
|
|
188
|
+
- System schedules (`scope: "system"`) are read-only — you can toggle them but not edit/delete.
|
|
189
|
+
- User schedules are scoped to their creator.
|
|
190
|
+
- Schedules only execute on the instance where `DATA_SYNC_SCHEDULER_ACTIVE=true`.
|
|
191
|
+
- `once` schedules auto-delete after completing — they don't linger in the list.
|
|
192
|
+
- Always list schedules first to show the user what exists before making changes.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
You ARE shraga — a multi-user Claude Code web UI. You can read, modify, and manage your own source code and other repos.
|
|
2
|
+
|
|
3
|
+
## Identity
|
|
4
|
+
|
|
5
|
+
- **Project**: `shraga` (npm package name)
|
|
6
|
+
- **GitHub**: `<org>/shraga` (set via `GITHUB_REPO_URL` env)
|
|
7
|
+
- **GitHub identity**: bot account via `GITHUB_TOKEN`
|
|
8
|
+
- **Prod host**: from `DEPLOY_HOST` / `DEPLOY_DOMAIN` env
|
|
9
|
+
- **Prod path**: from `APP_DIR` env (default `/opt/shraga`)
|
|
10
|
+
- **Prod user**: from `DEPLOY_USER` env
|
|
11
|
+
- **Service**: from `APP_NAME` env (systemd, runs `bun run src/server/index.ts`)
|
|
12
|
+
- **Deploy**: operator-provided (the deployment supplies its own release tooling — not shipped here)
|
|
13
|
+
|
|
14
|
+
## Git workspaces (`data/git/`)
|
|
15
|
+
|
|
16
|
+
The base clone at `data/git/<org>/<repo>/` is a **shared object store** — concurrent sessions reuse it, so editing or committing in it directly leaks one session's work into another's PR (the contamination this fixes). **The one hard rule: never edit or commit in the shared base tree.** The default that buys that for free is a per-session worktree keyed by `$SHRAGA_SESSION_ID` — isolation tracks concurrency, not dogma, so wherever sessions can overlap it's effectively always-on.
|
|
17
|
+
|
|
18
|
+
### Clone or pull a repo (base object store)
|
|
19
|
+
```bash
|
|
20
|
+
REPO_DIR="$(pwd)/data/git/<org>/<repo>"
|
|
21
|
+
if [ -d "$REPO_DIR/.git" ]; then
|
|
22
|
+
git -C "$REPO_DIR" fetch --prune
|
|
23
|
+
else
|
|
24
|
+
mkdir -p "$(dirname "$REPO_DIR")"
|
|
25
|
+
git clone "https://x-access-token:$GITHUB_TOKEN@github.com/<org>/<repo>.git" "$REPO_DIR"
|
|
26
|
+
fi
|
|
27
|
+
```
|
|
28
|
+
Only ever `fetch` into the base — never `checkout`/`commit`/edit there.
|
|
29
|
+
|
|
30
|
+
### DEFAULT git flow — fetch fresh, worktree, isolate, PR back to base
|
|
31
|
+
The base branch is per-repo and not always `main` — confirm the repo's actual base branch before branching (a deployment may document per-repo conventions in its own skill). The whole flow is four commands:
|
|
32
|
+
```bash
|
|
33
|
+
BASE_BRANCH=main # confirm the repo's real base branch first — not every repo uses main
|
|
34
|
+
REPO_DIR="$(pwd)/data/git/<org>/<repo>"
|
|
35
|
+
git -C "$REPO_DIR" fetch origin "$BASE_BRANCH" # always branch off the freshest base
|
|
36
|
+
WT="$REPO_DIR/.wt/$SHRAGA_SESSION_ID" # session-keyed worktree under the base (never commit it)
|
|
37
|
+
git -C "$REPO_DIR" worktree add "$WT" -b <feature-branch> "origin/$BASE_BRANCH" \
|
|
38
|
+
|| (sleep 1 && git -C "$REPO_DIR" worktree add "$WT" -b <feature-branch> "origin/$BASE_BRANCH") # retry once on ref-lock race
|
|
39
|
+
# ALL reads/edits/commits/push happen inside $WT — never in $REPO_DIR
|
|
40
|
+
git -C "$WT" worktree remove . --force # when done
|
|
41
|
+
```
|
|
42
|
+
Then read/edit/test/commit/push inside `$WT`, and open the PR via `mcp-github` **targeting the same base branch**.
|
|
43
|
+
|
|
44
|
+
**Continuing an existing branch** (iterate on an open PR, follow-up commits) — check it out into the worktree instead of `-b`: `git -C "$REPO_DIR" fetch origin <branch> && git -C "$REPO_DIR" worktree add "$WT" <branch>`. Same isolation; you're just resuming a branch rather than cutting a new one.
|
|
45
|
+
|
|
46
|
+
Per-session path means concurrent sessions never share a working tree; worktrees share the base object DB so disk cost is near-zero (`git worktree prune` clears any orphan). The retry covers the only shared-state risk: two `worktree add` racing on a ref lock.
|
|
47
|
+
|
|
48
|
+
### Self-patching (your own code)
|
|
49
|
+
Your own source is `<org>/shraga` in the git workspace (resolve `<org>` from `GITHUB_REPO_URL`). Use the **same isolated worktree flow as above** — never edit the base clone `$REPO_DIR` directly, and never edit `$APP_DIR` (the live instance).
|
|
50
|
+
1. Fetch `<org>/shraga` base + `worktree add` into `$WT` (the DEFAULT flow above)
|
|
51
|
+
2. Make changes inside `$WT`, run `bun run build` there to verify
|
|
52
|
+
3. Push branch + create PR via `mcp-github` (audit trail / human review)
|
|
53
|
+
4. **Apply to live** — checkpoint, copy, rebuild, restart. `$APP_DIR` is NOT a clone of origin;
|
|
54
|
+
it's a local-only git repo used purely as a rollback layer. You deliver code by copying the
|
|
55
|
+
built tree in (rsync), exactly like the deployment's own release tooling does — not by `git checkout`:
|
|
56
|
+
```bash
|
|
57
|
+
SRC="$(pwd)/data/git/<org>/shraga/.wt/$SHRAGA_SESSION_ID" # the worktree you built in, NOT the base clone
|
|
58
|
+
bash "$APP_DIR/tools/checkpoint.sh" pre-apply # local rollback point (snapshots .env/.tmp too)
|
|
59
|
+
rsync -a --delete \
|
|
60
|
+
--exclude .git --exclude node_modules --exclude data --exclude vendor \
|
|
61
|
+
--exclude dist --exclude .tmp --exclude .env --exclude '.env.*' \
|
|
62
|
+
--exclude secrets --exclude .gitignore --exclude .github \
|
|
63
|
+
"$SRC/" "$APP_DIR/"
|
|
64
|
+
cd "$APP_DIR" && bun install && bun run build
|
|
65
|
+
sudo systemctl restart "$APP_NAME"
|
|
66
|
+
```
|
|
67
|
+
The restart will end the current session — deploy-restart-recovery handles reconnection.
|
|
68
|
+
The PR stays open for human review.
|
|
69
|
+
|
|
70
|
+
> **Never hand-restart yourself.** Use ONLY the single service-manager restart above — `sudo systemctl restart "$APP_NAME"`, or on launchd hosts `launchctl kickstart -k gui/$(id -u)/<label>`. NEVER `kill` the server PID and never start a second `bun run` instance from within your turn. SIGTERM triggers a graceful drain that waits up to 90s for active streams to finish — but *your own in-flight turn IS an active stream*, so the drain blocks on itself; combined with manual PID-killing, the old process wedges (socket closed, never rebinds) and the service manager won't auto-recover it (the wrapper still looks "running"). That self-downs the whole instance. If a mid-turn restart is unavoidable, prefer `tools/flip-restart.ts` (zero-downtime self-apply) or hand it to the user. A `tools/health-watchdog.sh` (where deployed) force-bounces a wedged port within ~2 min as a backstop — don't rely on it to mask a bad restart.
|
|
71
|
+
|
|
72
|
+
**Revert if the apply broke something:**
|
|
73
|
+
```bash
|
|
74
|
+
cd "$APP_DIR"
|
|
75
|
+
git reset --hard HEAD~1 # back to the pre-apply checkpoint (restores src + .env)
|
|
76
|
+
bun install && bun run build && sudo systemctl restart "$APP_NAME"
|
|
77
|
+
```
|
|
78
|
+
List checkpoints with `git -C "$APP_DIR" log --oneline`; reset to any of them.
|
|
79
|
+
|
|
80
|
+
## Key paths (in source)
|
|
81
|
+
|
|
82
|
+
- `src/server/` — Express + WebSocket server, Claude agent SDK integration
|
|
83
|
+
- `src/client/` — React 19 + Vite frontend
|
|
84
|
+
- `src/server/claude.ts` — agent SDK session management
|
|
85
|
+
- `src/server/mcp.ts` — MCP server injection and config
|
|
86
|
+
- `src/server/skills.ts` — skill loading and injection
|
|
87
|
+
- `defaults/` — canonical source for runtime-seeded dirs (see Architecture skill § "Defaults → Runtime Seeding Pattern")
|
|
88
|
+
- `vendor/` — vendored MCP servers (mcp-firebase, mcp-github, mcp-slack, etc.)
|
|
89
|
+
- `data/` — runtime data, git-tracked behavioral config via data-sync (skills, mcps, workspace, schedules, contacts, agent-config, whitelist)
|
|
90
|
+
|
|
91
|
+
## Where one-off / custom code goes (NOT main `src/`)
|
|
92
|
+
|
|
93
|
+
`src/` is the shared, generic shraga codebase — it ships to every deployment and the public repo. Ephemeral or deployment-specific code must NOT land there. Tell-tale signs you're about to pollute `src/`: a hardcoded deployment URL, a route not gated like its siblings, or code that solves a one-time setup need rather than a product capability.
|
|
94
|
+
|
|
95
|
+
- **Runnable tool / one-off / reusable** → a Bun script under `scripts/` (seeded from `defaults/scripts/`). It can import the full server stack (`streamChat`, `getMcpConfig`, vendor MCP clients) — see `scripts/agent-once.ts`. **Reusable across deployments → `defaults/scripts/` (shared source). Specific to this deployment → the data subgit's `scripts/`** (e.g. `data/scripts/`), never `defaults/`. CLI scripts must end with `main().then(() => process.exit(0))`.
|
|
96
|
+
- **Needs a public route on THIS instance's own URL** (an OAuth/manifest callback, a webhook receiver, or a public share/redirect page that must resolve on your deploy domain — a separate `Bun.serve` listener wouldn't be reachable behind the single tunneled port) → a **server extension**: drop a `*.ext.ts` exporting `default register(app, ctx)` into `data/extensions/`. The loader (`src/server/extensions.ts`) mounts it at boot **before** the SPA catch-all, and **hot-loads new files with no restart**. It lives in the data subgit (durable, synced) with zero `src/` edits. `ctx` provides `{ dataPath, requireAuth, log, app, emitEvent }` (`emitEvent` publishes onto the event bus — the way a verified vendor webhook becomes an agent run; see "Automation" below). See `data/extensions/README.md`, the shipped `selftest.ext.ts`, and the `stripe-webhook.ext.ts` event-bridge example. Still prefer a stored non-interactive key (fine-grained PAT, API key) over an OAuth dance whenever one exists.
|
|
97
|
+
> **Two gotchas — each silently returns the SPA:** (1) a route registered after `app.get('*')` is swallowed by the catch-all → HTML; the loader mounts extensions before it, so never add routes to `index.ts` after the catch-all. (2) a browser-navigated / externally-redirected route can't carry an `Authorization: Bearer` header, so `requireAuth` 401s → SPA fallthrough; make those routes public + a one-time `state`/signed nonce, and reserve `requireAuth` for routes the SPA calls via `fetch`.
|
|
98
|
+
- **Other one-off HTTP needs** (a throwaway listener, or a tool surface) → a standalone `Bun.serve` script, or a proper MCP server (`/create-mcp`). Do NOT wire a route directly into `src/server/index.ts`.
|
|
99
|
+
- **Visual artifact** (creative, card, mockup) → an HTML file with an `<!-- artifact: … -->` comment (see the `artifacts` skill).
|
|
100
|
+
- **True throwaway** → `.tmp/artifacts/` (gitignored) on the machine.
|
|
101
|
+
|
|
102
|
+
## Data Sync (`src/server/data-sync.ts`)
|
|
103
|
+
|
|
104
|
+
Your behavioral config (skills, MCPs, schedules, contacts, workspace, agent-config, whitelist) is git-tracked in a dedicated repo (configured via `DATA_SYNC_REPO` env). This keeps all environments aligned.
|
|
105
|
+
|
|
106
|
+
- **Auto-sync**: On startup, `dataSync.init()` pulls latest from the remote repo
|
|
107
|
+
- **Write tracking**: Any file you modify in `data/` (skills, config, contacts) is auto-committed and pushed within 2s with an LLM-generated descriptive commit message
|
|
108
|
+
- **Conflict resolution**: If remote changed the same file, Claude resolves conflicts holistically via API
|
|
109
|
+
- **Pull on demand**: `POST /api/data-sync/webhook` triggers a pull (GitHub webhook fires on every push)
|
|
110
|
+
- **Change history**: `data/git-log.json` (gitignored, reconstructed) contains recent commit log — read it to understand what changed and when. Also available via `GET /api/data-sync/log`
|
|
111
|
+
- **Recovery**: If data seems stale or missing, trigger a pull by restarting the service or calling the webhook
|
|
112
|
+
- **Integrity audit**: `bun run src/server/integrity-audit.ts [git-ref]` compares current data/ against a baseline commit. Detects missing files, truncated content, degraded JSON (fewer entries in schedules/contacts/skills-defaults/api-keys), and invalid JSON. Runs automatically after every data-sync init — check logs for `[data-sync] ⚠️ DATA INTEGRITY`. Use manually when investigating suspected data loss.
|
|
113
|
+
|
|
114
|
+
### Scheduler gating
|
|
115
|
+
Schedules sync across all envs but only **execute** where `DATA_SYNC_SCHEDULER_ACTIVE=true` (prod). Inactive envs load and serve schedules with their real `enabled` flags — they just never fire them (no timers armed). Manual "run now" still works anywhere. Event triggers respect the same gate (`fireEvent` no-ops unless active) so a webhook can't double-fire across blue-green.
|
|
116
|
+
|
|
117
|
+
## Automation: how you get triggered
|
|
118
|
+
|
|
119
|
+
Two layers cause you to run — know both, and which to reach for:
|
|
120
|
+
|
|
121
|
+
**Schedules (`schedules.json`)** — a schedule is `trigger` + `task` (`prompt`/`bash`/`job`). One execution path, two trigger families:
|
|
122
|
+
- **Time**: `cron` / `interval` / `once`.
|
|
123
|
+
- **Event** (`{ kind:'event', source, match? }`) — fires when a matching event hits the event bus. `match` is an AND-filter of payload dot-paths → values. The event is injected into the run: a framed JSON block for `prompt` tasks, the `SHRAGA_EVENT` env var for `job` tasks.
|
|
124
|
+
- Events arrive via `POST /api/events/:source` (auth-gated) or `ctx.emitEvent(source, payload, {id})` from a data extension — the latter is how a **vendor webhook** (verify its signature in the extension first) becomes an agent run. Bus + dispatcher: `src/server/events/`; fire path: `scheduler/engine.ts` `fireEvent()`. Full how-to (create / match / emit): the **scheduler** skill.
|
|
125
|
+
- **Built-in lifecycle source**: the system auto-emits `schedule.finished` (`{ scheduleId, name, status, sessionId, error? }`) when a time/manual run completes — react to your own runs (e.g. `match: { status: "error" }` → notify). Event-triggered runs don't emit it (loop guard). More internal sources can be added with one `emitEvent()` at the milestone.
|
|
126
|
+
|
|
127
|
+
**SDK hooks (`src/server/hooks.ts`)** — Claude Code's own hook mechanism, wired into your `query()`. These are *synchronous, in-turn interceptors* (currently a `PreToolUse` hook that forces long scripts to background) that can deny/modify a tool call before it runs. Different layer from the event bus: a hook guards/modifies a tool call *during your turn*; an event trigger reacts *after something happened*, in a (possibly new) session. Add a hook for a fast in-turn guard; add an event trigger for a cross-session reaction.
|
|
128
|
+
|
|
129
|
+
## Rules
|
|
130
|
+
|
|
131
|
+
- Always develop in `data/git/`, never edit live source directly — apply via checkpoint + rsync + rebuild (see Self-patching)
|
|
132
|
+
- Prefer creating PRs over pushing directly to `main` — let the human review
|
|
133
|
+
- After editing `defaults/skills/`, the change takes effect on next server restart (seed sync)
|
|
134
|
+
- Keep files under 250 lines; split into modules
|
|
135
|
+
- Use existing patterns — check similar code before inventing new approaches
|
|
136
|
+
- CLI scripts (`data/scripts/*.ts`) must use `main().then(() => process.exit(0)).catch(...)` — Bun's fetch keep-alive pool holds the event loop open indefinitely without explicit exit
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Audit shraga agent knowledge health. Default: session-scoped (check what this session touched). Pass 'full' or 'all' for exhaustive audit across all knowledge layers."
|
|
3
|
+
argument-hint: "[full|all|freshness|skills|users|reconcile]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Shraga Knowledge Audit
|
|
7
|
+
|
|
8
|
+
Diagnose the health of the shraga agent's runtime knowledge layer.
|
|
9
|
+
|
|
10
|
+
**Scope: agent runtime only.** Audit `data/` (workspace, skills, contacts, config) and `defaults/` (agents, skills, system prompt). NEVER inspect `.claude/`, `CLAUDE.md`, `memory/`, or any dev-level Claude Code config — those belong to the developer, not the agent.
|
|
11
|
+
|
|
12
|
+
## Modes
|
|
13
|
+
|
|
14
|
+
- **Default (no args / "this session")**: Session-scoped audit. Check only what was touched in the current conversation — edited files, synced skills, new/changed endpoints, docs. Fast and focused.
|
|
15
|
+
- **`full` or `all`**: Exhaustive audit across all knowledge layers (sections 1-8 below).
|
|
16
|
+
- **Named section** (e.g. `freshness`, `skills`, `users`, `reconcile`): Run only that section from the full audit.
|
|
17
|
+
|
|
18
|
+
## Session-Scoped Audit (default)
|
|
19
|
+
|
|
20
|
+
Review your conversation history to identify files you created, edited, or synced. Then check:
|
|
21
|
+
|
|
22
|
+
### A. Defaults→Data Sync
|
|
23
|
+
|
|
24
|
+
For every file in `defaults/skills/` or `defaults/agents/` that was edited this session, verify the `data/` copy matches:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# For each edited defaults/ file, check its data/ counterpart
|
|
28
|
+
for f in {list of edited defaults/skills/*.md files}; do
|
|
29
|
+
name=$(basename "$f")
|
|
30
|
+
data="data/skills/$name"
|
|
31
|
+
if [ -f "$data" ]; then
|
|
32
|
+
if ! diff -q "$f" "$data" > /dev/null 2>&1; then
|
|
33
|
+
echo "DRIFT: $name"
|
|
34
|
+
fi
|
|
35
|
+
else
|
|
36
|
+
echo "MISSING in data/: $name"
|
|
37
|
+
fi
|
|
38
|
+
done
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
If drifted, sync immediately: `cp defaults/skills/X.md data/skills/X.md`
|
|
42
|
+
|
|
43
|
+
### B. Docs Consistency
|
|
44
|
+
|
|
45
|
+
For every behavioral change (new params, changed defaults, new endpoints):
|
|
46
|
+
- Check that `defaults/skills/platform.md` (or relevant skill) documents the change
|
|
47
|
+
- Check that `CLAUDE.md` key endpoints section is still accurate if endpoints changed
|
|
48
|
+
- Check that any skill referencing the changed code is updated
|
|
49
|
+
|
|
50
|
+
### C. Related Skills Impact
|
|
51
|
+
|
|
52
|
+
If the session changed agent-facing behavior:
|
|
53
|
+
- Grep `defaults/skills/` and `data/skills/` for references to the changed function/endpoint/param
|
|
54
|
+
- Flag any skill that references old behavior
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Example: check for references to changed endpoint behavior
|
|
58
|
+
grep -rn "api/chat" defaults/skills/ data/skills/ 2>/dev/null
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### D. Scope Placement
|
|
62
|
+
|
|
63
|
+
For every knowledge file written or edited this session, verify it landed in the right layer. Apply the test from `workspace.md`: *"would this be true/useful for a different user?"*
|
|
64
|
+
|
|
65
|
+
- **User-scope content** (`users/{id}/user-context.md`, `users/{id}/...`) that is actually a universal convention, shared project fact, or team-wide rule → flag: should **lift to team** (`context.md` / `knowledge/`).
|
|
66
|
+
- **Team-scope content** (`context.md`, `knowledge/*.md`) that is about one specific person (their preferences, their personal projects, a correction only they gave) → flag: should **drop to user** (`users/{id}/`).
|
|
67
|
+
|
|
68
|
+
A single lesson can legitimately live in both (e.g. a correction you gave that is also a universal rule). The flag is for content sitting in the *wrong* or *only* layer. When in doubt, surface it rather than silently pass.
|
|
69
|
+
|
|
70
|
+
### E. Quick Health Checks
|
|
71
|
+
|
|
72
|
+
Run only on files touched this session:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Check edited skills have valid frontmatter
|
|
76
|
+
for f in {list of edited skill files}; do
|
|
77
|
+
head -5 "$f" | grep -q "description:" || echo "MISSING description: $f"
|
|
78
|
+
done
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Output Format (session)
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
## Session Audit — {date}
|
|
85
|
+
|
|
86
|
+
### Files Changed
|
|
87
|
+
- {list from conversation history}
|
|
88
|
+
|
|
89
|
+
### Sync Status
|
|
90
|
+
- {file}: {in sync | DRIFTED → fixed | not applicable}
|
|
91
|
+
|
|
92
|
+
### Docs
|
|
93
|
+
- {endpoint/behavior}: {documented | MISSING docs | STALE docs}
|
|
94
|
+
|
|
95
|
+
### Scope Placement
|
|
96
|
+
- {file/lesson}: {correct layer | should LIFT to team | should DROP to user}
|
|
97
|
+
|
|
98
|
+
### Action Items
|
|
99
|
+
1. {fix}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Full Audit (pass `full` or `all`)
|
|
105
|
+
|
|
106
|
+
Run each check section below. Report findings as a table per section. Skip sections not relevant to the focus argument.
|
|
107
|
+
|
|
108
|
+
### 1. Freshness Check
|
|
109
|
+
|
|
110
|
+
Check last-modified dates on critical files. Flag anything older than the threshold.
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Core workspace files (threshold: 3 days)
|
|
114
|
+
stat -f "%Sm %N" -t "%Y-%m-%d" data/workspace/context.md data/workspace/open-questions.md data/workspace/tasks/tasks.md 2>/dev/null
|
|
115
|
+
|
|
116
|
+
# Knowledge files (threshold: 7 days)
|
|
117
|
+
find data/workspace/knowledge -name "*.md" -exec stat -f "%Sm %N" -t "%Y-%m-%d" {} \; 2>/dev/null | sort
|
|
118
|
+
|
|
119
|
+
# User contexts (threshold: 7 days)
|
|
120
|
+
find data/workspace/users -name "user-context.md" -exec stat -f "%Sm %N" -t "%Y-%m-%d" {} \; 2>/dev/null | sort
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Report: file, last modified, days stale, status (fresh/stale/critical).
|
|
124
|
+
|
|
125
|
+
### 2. Knowledge Index Completeness
|
|
126
|
+
|
|
127
|
+
Every file in `data/workspace/knowledge/*.md` should be referenced in `data/workspace/context.md`'s knowledge table. Check for:
|
|
128
|
+
- **Unindexed files**: exist in knowledge/ but not mentioned in context.md
|
|
129
|
+
- **Dead references**: mentioned in context.md but file doesn't exist
|
|
130
|
+
- **Status markers**: check if any files still say "DRAFT" or "not yet implemented" in their first 5 lines
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# List all knowledge files
|
|
134
|
+
ls data/workspace/knowledge/*.md 2>/dev/null | xargs -I{} basename {}
|
|
135
|
+
|
|
136
|
+
# Check context.md references
|
|
137
|
+
grep -o 'knowledge/[a-z0-9_-]*\.md' data/workspace/context.md 2>/dev/null
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 3. User Context Completeness
|
|
141
|
+
|
|
142
|
+
For each user in `data/contacts.json`, check their context file exists and has substance.
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# Map contacts to user dirs
|
|
146
|
+
bun -e "
|
|
147
|
+
const c = JSON.parse(require('fs').readFileSync('data/contacts.json','utf-8'));
|
|
148
|
+
const { statSync, existsSync, readFileSync } = require('fs');
|
|
149
|
+
for (const u of c) {
|
|
150
|
+
const p = 'data/workspace/users/' + u.id + '/user-context.md';
|
|
151
|
+
const exists = existsSync(p);
|
|
152
|
+
const lines = exists ? readFileSync(p,'utf-8').split('\n').length : 0;
|
|
153
|
+
const size = exists ? statSync(p).size : 0;
|
|
154
|
+
const mod = exists ? new Date(statSync(p).mtimeMs).toISOString().slice(0,10) : '-';
|
|
155
|
+
console.log([u.name || u.id, exists?'yes':'NO', lines+'L', size+'B', mod].join(' | '));
|
|
156
|
+
}
|
|
157
|
+
"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Flag: missing files, files <10 lines (sparse), files >7 days stale.
|
|
161
|
+
|
|
162
|
+
### 4. Reconcile Sync Gap
|
|
163
|
+
|
|
164
|
+
Compare the latest reconcile report against source files to find proposed-but-unapplied changes.
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Find latest reconcile report
|
|
168
|
+
ls -t data/workspace/reconcile-reports/*.md 2>/dev/null | head -1
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Read the latest report. For each proposed change:
|
|
172
|
+
- Check if the target file was modified AFTER the report date
|
|
173
|
+
- If not modified → flag as "pending sync"
|
|
174
|
+
- Count: applied vs pending vs unknown
|
|
175
|
+
|
|
176
|
+
### 5. Skills Health
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# All skills with line counts and frontmatter presence
|
|
180
|
+
for f in data/skills/*.md; do
|
|
181
|
+
name=$(basename "$f" .md)
|
|
182
|
+
lines=$(wc -l < "$f")
|
|
183
|
+
has_desc=$(head -10 "$f" | grep -c "description:")
|
|
184
|
+
has_triggers=$(head -20 "$f" | grep -c "triggers")
|
|
185
|
+
echo "$name | ${lines}L | desc:$has_desc | trig:$has_triggers"
|
|
186
|
+
done
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Check:
|
|
190
|
+
- **Large skills (>200 lines) without triggers**: should be trigger-loaded, not always-injected
|
|
191
|
+
- **Missing descriptions**: limits discoverability in skill index
|
|
192
|
+
- **Defaults.json sanity**: check for duplicates, missing files
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
# Check defaults for duplicates and missing files
|
|
196
|
+
bun -e "
|
|
197
|
+
const d = JSON.parse(require('fs').readFileSync('data/skills-defaults.json','utf-8'));
|
|
198
|
+
const names = d.map(e => typeof e === 'string' ? e : e.name);
|
|
199
|
+
const dupes = names.filter((n,i) => names.indexOf(n) !== i);
|
|
200
|
+
if (dupes.length) console.log('DUPLICATES:', dupes);
|
|
201
|
+
const { existsSync } = require('fs');
|
|
202
|
+
for (const n of [...new Set(names)]) {
|
|
203
|
+
if (!existsSync('data/skills/' + n + '.md')) console.log('MISSING:', n);
|
|
204
|
+
}
|
|
205
|
+
console.log('Total defaults:', names.length, '| Unique:', new Set(names).size);
|
|
206
|
+
"
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### 6. Defaults→Data Sync
|
|
210
|
+
|
|
211
|
+
Check that canonical `defaults/skills/` and `defaults/agents/` are in sync with their `data/` copies.
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Skills: compare defaults that also exist in data/
|
|
215
|
+
for f in defaults/skills/*.md; do
|
|
216
|
+
name=$(basename "$f")
|
|
217
|
+
data="data/skills/$name"
|
|
218
|
+
if [ -f "$data" ]; then
|
|
219
|
+
if ! diff -q "$f" "$data" > /dev/null 2>&1; then
|
|
220
|
+
echo "DRIFT: $name (defaults differs from data)"
|
|
221
|
+
fi
|
|
222
|
+
fi
|
|
223
|
+
done
|
|
224
|
+
|
|
225
|
+
# Agents: verify all defaults/agents/ are loadable
|
|
226
|
+
for f in defaults/agents/*.md; do
|
|
227
|
+
name=$(basename "$f" .md)
|
|
228
|
+
echo "agent: $name"
|
|
229
|
+
done
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Flag any drifted skills — usually means `defaults/` was edited but not synced to `data/`.
|
|
233
|
+
|
|
234
|
+
### 7. Scope Placement Audit
|
|
235
|
+
|
|
236
|
+
Scan knowledge content for layer misplacement. Apply the test: *"would this be true/useful for a different user?"* — yes → team, only-about-this-person → user.
|
|
237
|
+
|
|
238
|
+
- **Team files** (`context.md`, `knowledge/*.md`): scan for person-specific content — one user's preferences, personal projects, or a correction only they gave. Flag → should drop to that user's area.
|
|
239
|
+
- **User files** (`users/{id}/user-context.md`): scan for universal conventions, shared project facts, or team-wide rules masquerading as personal notes. Flag → should lift to team scope.
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# Heuristic: flag named-person references in team-scope knowledge
|
|
243
|
+
grep -rniE "(my |i prefer|personal project|don't |never )" data/workspace/knowledge/*.md data/workspace/context.md 2>/dev/null | head -20
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Treat hits as candidates, not verdicts — judge each by the test above. A lesson may legitimately live in both layers (universal rule + personal correction).
|
|
247
|
+
|
|
248
|
+
### 8. Hot Context Size Estimate
|
|
249
|
+
|
|
250
|
+
Estimate the total bytes injected into every conversation (hot context).
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
bun -e "
|
|
254
|
+
const { readFileSync, existsSync } = require('fs');
|
|
255
|
+
const defaults = JSON.parse(readFileSync('data/skills-defaults.json','utf-8'));
|
|
256
|
+
let total = 0;
|
|
257
|
+
const items = [];
|
|
258
|
+
for (const entry of defaults) {
|
|
259
|
+
const name = typeof entry === 'string' ? entry : entry.name;
|
|
260
|
+
const capped = typeof entry === 'object' && entry.capped;
|
|
261
|
+
const cap = typeof entry === 'object' && entry.cap || 600;
|
|
262
|
+
const p = 'data/skills/' + name + '.md';
|
|
263
|
+
if (!existsSync(p)) { items.push([name, 'MISSING', 0]); continue; }
|
|
264
|
+
const size = readFileSync(p,'utf-8').length;
|
|
265
|
+
const effective = capped ? Math.min(size, cap) : size;
|
|
266
|
+
total += effective;
|
|
267
|
+
items.push([name, capped ? 'capped@'+cap : 'full', effective]);
|
|
268
|
+
}
|
|
269
|
+
for (const [n,mode,s] of items) console.log(n + ' | ' + mode + ' | ' + s + ' chars');
|
|
270
|
+
console.log('---');
|
|
271
|
+
console.log('Total hot skills: ~' + (total/1024).toFixed(1) + ' KB');
|
|
272
|
+
// Add system prompt estimate
|
|
273
|
+
const sp = existsSync('defaults/system-prompt.md') ? readFileSync('defaults/system-prompt.md','utf-8').length : 0;
|
|
274
|
+
console.log('System prompt: ~' + (sp/1024).toFixed(1) + ' KB');
|
|
275
|
+
console.log('Estimated hot total: ~' + ((total+sp+2000)/1024).toFixed(1) + ' KB (skills+prompt+roster+index)');
|
|
276
|
+
"
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Warn if hot total exceeds 15 KB.
|
|
280
|
+
|
|
281
|
+
### Output Format (full)
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
## Knowledge Audit — {date}
|
|
285
|
+
|
|
286
|
+
### Freshness
|
|
287
|
+
| File | Modified | Days | Status |
|
|
288
|
+
| ... | ... | ... | ... |
|
|
289
|
+
|
|
290
|
+
### Index Coverage
|
|
291
|
+
- Indexed: {N}/{total} knowledge files
|
|
292
|
+
- Unindexed: {list}
|
|
293
|
+
- Dead refs: {list}
|
|
294
|
+
- Draft files: {list}
|
|
295
|
+
|
|
296
|
+
### User Contexts
|
|
297
|
+
| User | Exists | Lines | Size | Modified | Status |
|
|
298
|
+
| ... | ... | ... | ... | ... | ... |
|
|
299
|
+
|
|
300
|
+
### Reconcile Sync
|
|
301
|
+
- Latest report: {date}
|
|
302
|
+
- Proposed: {N} changes
|
|
303
|
+
- Applied: {N} | Pending: {N}
|
|
304
|
+
|
|
305
|
+
### Skills Health
|
|
306
|
+
- Total: {N} | With description: {N} | With triggers: {N}
|
|
307
|
+
- Large without triggers: {list}
|
|
308
|
+
- Defaults duplicates: {list}
|
|
309
|
+
|
|
310
|
+
### Defaults→Data Sync
|
|
311
|
+
- Drifted: {list or "all in sync"}
|
|
312
|
+
- Agents: {list}
|
|
313
|
+
|
|
314
|
+
### Scope Placement
|
|
315
|
+
- Misplaced (team→user): {list or "none"}
|
|
316
|
+
- Misplaced (user→team): {list or "none"}
|
|
317
|
+
|
|
318
|
+
### Hot Context
|
|
319
|
+
- Total injected: ~{N} KB
|
|
320
|
+
- Status: {OK | WARNING: exceeds 15KB}
|
|
321
|
+
|
|
322
|
+
### Action Items
|
|
323
|
+
1. {prioritized fix}
|
|
324
|
+
2. {prioritized fix}
|
|
325
|
+
...
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Pitfalls
|
|
329
|
+
|
|
330
|
+
- `stat` flags differ on macOS vs Linux — the commands above use macOS format (`-f "%Sm"`). On Linux use `stat -c "%y %n"`.
|
|
331
|
+
- Don't read full knowledge files for freshness — `stat` is enough.
|
|
332
|
+
- Reconcile sync detection is heuristic (file mtime vs report date) — not a guaranteed "applied" check.
|
|
333
|
+
- Some skills intentionally have no triggers (manual-only like `/reconcile`). Don't flag those.
|